mod frequency;
mod chi_square;
use std::hash::Hash;
use std::fmt::Debug;
use num::{Num, ToPrimitive};
use arithmetic::UnsignedArithmetic;
pub use self::frequency::FrequencyMap;
pub use self::chi_square::chi_square;
pub use self::chi_square::chi_square_test;
pub fn histogram<K, I: IntoIterator<Item = K>>(random_numbers: I, width: u8)
where
K: Hash + Eq + Clone + Copy + Ord + Debug,
{
let freq_map: FrequencyMap<K, u64> = random_numbers.into_iter().collect();
let min = freq_map.values().min().unwrap();
let max = freq_map.values().max().unwrap();
for (item, count) in freq_map.iter() {
print!("{:?}: ", item);
let item_norm = normalize(*count, *min, *max);
let bar_width: u64 = (item_norm * width as f64) as u64;
for _ in 0..bar_width as usize {
print!("*")
}
println!("");
}
}
fn normalize<N>(value: N, min: N, max: N) -> f64
where
N: Clone + Num + ToPrimitive + UnsignedArithmetic + Debug,
{
if value < min || value > max {
panic!("Value out of min/max range");
}
let numerator = (value - min.clone())
.to_f64()
.expect("Unable to convert `N` to f64");
let denominator = (max - min).to_f64().expect("Unable to convert `N` to f64");
let result = numerator / denominator;
if result.is_nan() {
panic!("Divide by zero");
}
result
}
#[cfg(test)]
mod tests {
extern crate float_cmp;
extern crate rand;
use super::*;
use self::float_cmp::ApproxEqUlps;
#[test]
fn test_histogram() {
let test_data = [
0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8
];
histogram(test_data.iter(), 42);
}
#[test]
fn test_normalize_simple() {
assert!(normalize(0, 0, 10).approx_eq_ulps(&0_f64, 2));
assert!(normalize(1, 0, 10).approx_eq_ulps(&0.1_f64, 2));
assert!(normalize(2, 0, 10).approx_eq_ulps(&0.2_f64, 2));
assert!(normalize(3, 0, 10).approx_eq_ulps(&0.3_f64, 2));
assert!(normalize(4, 0, 10).approx_eq_ulps(&0.4_f64, 2));
assert!(normalize(5, 0, 10).approx_eq_ulps(&0.5_f64, 2));
assert!(normalize(6, 0, 10).approx_eq_ulps(&0.6_f64, 2));
assert!(normalize(7, 0, 10).approx_eq_ulps(&0.7_f64, 2));
assert!(normalize(8, 0, 10).approx_eq_ulps(&0.8_f64, 2));
assert!(normalize(9, 0, 10).approx_eq_ulps(&0.9_f64, 2));
assert!(normalize(10, 0, 10).approx_eq_ulps(&1_f64, 2));
}
#[test]
fn test_normalize_simple_offset() {
assert!(normalize(10, 10, 20).approx_eq_ulps(&0_f64, 2));
assert!(normalize(11, 10, 20).approx_eq_ulps(&0.1_f64, 2));
assert!(normalize(12, 10, 20).approx_eq_ulps(&0.2_f64, 2));
assert!(normalize(13, 10, 20).approx_eq_ulps(&0.3_f64, 2));
assert!(normalize(14, 10, 20).approx_eq_ulps(&0.4_f64, 2));
assert!(normalize(15, 10, 20).approx_eq_ulps(&0.5_f64, 2));
assert!(normalize(16, 10, 20).approx_eq_ulps(&0.6_f64, 2));
assert!(normalize(17, 10, 20).approx_eq_ulps(&0.7_f64, 2));
assert!(normalize(18, 10, 20).approx_eq_ulps(&0.8_f64, 2));
assert!(normalize(19, 10, 20).approx_eq_ulps(&0.9_f64, 2));
assert!(normalize(20, 10, 20).approx_eq_ulps(&1_f64, 2));
}
#[test]
#[should_panic]
fn test_normalize_divide_by_zero() {
normalize(0, 0, 0);
}
#[test]
#[should_panic]
fn test_normalize_value_out_of_range_low() {
normalize(9, 10, 20);
}
#[test]
#[should_panic]
fn test_normalize_value_out_of_range_high() {
normalize(21, 10, 20);
}
}