use ndhistogram::{axis::Uniform, ndhistogram, HistND, Histogram};
use num_traits::Float;
use rand::{prelude::StdRng, SeedableRng};
use rand_distr::{Distribution, Normal, StandardNormal};
pub fn generate_nomal<T: Float>(mu: T, sigma: T, seed: u64) -> impl Iterator<Item = T>
where
StandardNormal: Distribution<T>,
{
let rng = StdRng::seed_from_u64(seed);
Normal::new(mu, sigma).unwrap().sample_iter(rng)
}
type Hist1D = HistND<(Uniform<f64>,), f64>;
type Hist3D = HistND<(Uniform<f64>, Uniform<f64>, Uniform<f64>), f64>;
fn generate_normal_hist_1d(seed: u64) -> Hist1D {
let mut hist = ndhistogram!(Uniform::new(10, -5.0, 5.0));
generate_nomal(-1.0, 2.0, seed)
.take(1000)
.for_each(|it| hist.fill(&it));
hist
}
fn generate_normal_hist_3d(seed: u64) -> Hist3D {
let mut hist = ndhistogram!(
Uniform::new(10, -5.0, 5.0),
Uniform::new(10, -5.0, 5.0),
Uniform::new(10, -5.0, 5.0),
);
let x = generate_nomal(-1.0, 2.0, seed + 1);
let y = generate_nomal(0.0, 2.0, seed + 2);
let z = generate_nomal(1.0, 2.0, seed + 3);
let xyz = x.zip(y).zip(z).map(|((x, y), z)| (x, y, z));
xyz.take(1000).for_each(|it| hist.fill(&it));
hist
}
#[test]
fn test_ndhistogram_add_1d_elementwise_with_copy() {
let left = generate_normal_hist_1d(1);
let right = generate_normal_hist_1d(2);
let hadd = (&left + &right).unwrap();
let actual: Vec<_> = hadd
.iter()
.map(|it| (it.index, it.bin, *it.value))
.collect();
let expected: Vec<_> = left
.iter()
.zip(right.into_iter())
.map(|(l, r)| (l.index, l.bin, l.value + r.value))
.collect();
assert_eq!(actual, expected)
}
macro_rules! impl_binary_op {
($fnname1d:tt, $fnname3d:tt, $fnnamefails:tt, $mathsymbol:tt) => {
#[test]
fn $fnname1d() {
let left = generate_normal_hist_1d(1);
let right = generate_normal_hist_1d(2);
let hadd = (&left $mathsymbol &right).unwrap();
let actual: Vec<_> = hadd
.iter()
.map(|it| (it.index, it.bin, *it.value))
.filter(|(_,_, v)| !v.is_nan())
.collect();
let expected: Vec<_> = left
.iter()
.zip(right.into_iter())
.map(|(l, r)| (l.index, l.bin, l.value $mathsymbol r.value))
.filter(|(_,_, v)| !v.is_nan())
.collect();
assert_eq!(actual, expected)
}
#[test]
fn $fnnamefails() {
let left = ndhistogram!(Uniform::new(10, -5.0, 5.0));
let right = ndhistogram!(Uniform::new(10, -5.0, 6.0));
let hadd = &left $mathsymbol &right;
assert!(hadd.is_err())
}
#[test]
fn $fnname3d() {
let left = generate_normal_hist_3d(1);
let right = generate_normal_hist_3d(2);
let hadd = (&left $mathsymbol &right).unwrap();
let actual: Vec<_> = hadd
.iter()
.map(|it| (it.index, it.bin, *it.value))
.filter(|(_,_, v)| !v.is_nan())
.collect();
let expected: Vec<_> = left
.iter()
.zip(right.into_iter())
.map(|(l, r)| (l.index, l.bin, l.value $mathsymbol r.value))
.filter(|(_,_, v)| !v.is_nan())
.collect();
assert_eq!(actual, expected)
}
}
}
impl_binary_op! {test_ndhistogram_1d_elementwise_add, test_ndhistogram_3d_elementwise_add, test_ndhistogram_binning_mismatch_add, +}
impl_binary_op! {test_ndhistogram_1d_elementwise_mul, test_ndhistogram_3d_elementwise_mul, test_ndhistogram_binning_mismatch_mul, *}
impl_binary_op! {test_ndhistogram_1d_elementwise_sub, test_ndhistogram_3d_elementwise_sub, test_ndhistogram_binning_mismatch_sub, -}
impl_binary_op! {test_ndhistogram_1d_elementwise_div, test_ndhistogram_3d_elementwise_div, test_ndhistogram_binning_mismatch_div, /}
macro_rules! impl_binary_op_with_scalar {
($fnname1d:tt, $fnname3d:tt, $mathsymbol:tt) => {
#[test]
fn $fnname1d() {
let left = generate_normal_hist_1d(1);
let right = 2.0;
let hadd = (&left $mathsymbol &right);
let actual: Vec<_> = hadd
.iter()
.map(|it| (it.index, it.bin, *it.value))
.filter(|(_,_, v)| !v.is_nan())
.collect();
let expected: Vec<_> = left
.iter()
.map(|l| (l.index, l.bin, l.value $mathsymbol right))
.filter(|(_,_, v)| !v.is_nan())
.collect();
assert_eq!(actual, expected)
}
}
}
impl_binary_op_with_scalar! {test_ndhistogram_1d_scalar_add, test_ndhistogram_3d_scalar_add, +}
impl_binary_op_with_scalar! {test_ndhistogram_1d_scalar_mul, test_ndhistogram_3d_scalar_mul, *}
impl_binary_op_with_scalar! {test_ndhistogram_1d_scalar_sub, test_ndhistogram_3d_scalar_sub, -}
impl_binary_op_with_scalar! {test_ndhistogram_1d_scalar_div, test_ndhistogram_3d_scalar_div, /}
#[test]
fn test_ndhistogram_1d_equality() {
let left = generate_normal_hist_1d(1);
let right = left.clone();
assert_eq!(left, right)
}
#[test]
fn test_ndhistogram_1d_inequality() {
let left = generate_normal_hist_1d(1);
let mut right = left.clone();
right.fill(&1.0);
assert_ne!(left, right)
}
#[test]
fn test_ndhistogram_3d_equality() {
let left = generate_normal_hist_3d(1);
let right = left.clone();
assert_eq!(left, right)
}
#[test]
fn test_ndhistogram_3d_inequality() {
let left = generate_normal_hist_3d(1);
let mut right = left.clone();
right.fill(&(1.0, 2.0, 3.0));
assert_ne!(left, right)
}
macro_rules! impl_binary_op_with_owned {
($fnname1d:tt, $mathsymbol:tt) => {
#[test]
fn $fnname1d() {
let left = generate_normal_hist_1d(1);
let left_copy = left.clone();
let right = generate_normal_hist_1d(2);
let hadd = (left $mathsymbol &right).unwrap();
let actual: Vec<_> = hadd
.iter()
.map(|it| (it.index, it.bin, *it.value))
.filter(|(_,_, v)| !v.is_nan())
.collect();
let expected: Vec<_> = left_copy
.iter()
.zip(right.into_iter())
.map(|(l, r)| (l.index, l.bin, l.value $mathsymbol r.value))
.filter(|(_,_, v)| !v.is_nan())
.collect();
assert_eq!(actual, expected)
}
}
}
impl_binary_op_with_owned! {test_ndhistogram_1d_add_owned, +}
impl_binary_op_with_owned! {test_ndhistogram_1d_mul_owned, *}
impl_binary_op_with_owned! {test_ndhistogram_1d_sub_owned, -}
impl_binary_op_with_owned! {test_ndhistogram_1d_div_owned, /}
macro_rules! impl_op_assign {
($fnname:tt, $mathsymbol:tt, $testmathsymbol:tt) => {
#[test]
fn $fnname() {
let mut left = generate_normal_hist_1d(1);
let right = generate_normal_hist_1d(2);
let left_copy = left.clone();
left $mathsymbol &right;
let actual: Vec<_> = left
.iter()
.map(|it| (it.index, it.bin, *it.value))
.collect();
let expected: Vec<_> = left_copy
.iter()
.zip(right.into_iter())
.map(|(l, r)| (l.index, l.bin, l.value $testmathsymbol r.value))
.collect();
assert_eq!(actual, expected)
}
}
}
impl_op_assign! {test_add_assign, +=, +}
impl_op_assign! {test_mul_assign, *=, *}
impl_op_assign! {test_div_assign, /=, /}
impl_op_assign! {test_sub_assign, -=, -}