#![allow( clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names, clippy::float_cmp )] use ndarray::prelude::*; use ndarray::Zip; use itertools::{assert_equal, cloned}; use std::mem::swap; #[test] fn test_azip1() { let mut a = Array::zeros(62); let mut x = 0; azip!((a in &mut a) { *a = x; x += 1; }); assert_equal(cloned(&a), 0..a.len()); } #[test] fn test_azip2() { let mut a = Array::zeros((5, 7)); let b = Array::from_shape_fn(a.dim(), |(i, j)| 1. / (i + 2 * j) as f32); azip!((a in &mut a, &b in &b) *a = b); assert_eq!(a, b); } #[test] fn test_azip2_1() { let mut a = Array::zeros((5, 7)); let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j) as f32); let b = b.slice(s![..;-1, 3..]); azip!((a in &mut a, &b in &b) *a = b); assert_eq!(a, b); } #[test] fn test_azip2_3() { let mut b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j) as f32); let mut c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); let a = b.clone(); azip!((b in &mut b, c in &mut c) swap(b, c)); assert_eq!(a, c); assert!(a != b); } #[test] #[cfg(feature = "approx")] fn test_zip_collect() { use approx::assert_abs_diff_eq; // test Zip::map_collect and that it preserves c/f layout. let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); { let a = Zip::from(&b).and(&c).map_collect(|x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); assert_eq!(a.strides(), b.strides()); } { let b = b.t(); let c = c.t(); let a = Zip::from(&b).and(&c).map_collect(|x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); assert_eq!(a.strides(), b.strides()); } } #[test] #[cfg(feature = "approx")] fn test_zip_assign_into() { use approx::assert_abs_diff_eq; let mut a = Array::::zeros((5, 10)); let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); Zip::from(&b).and(&c).map_assign_into(&mut a, |x, y| x + y); assert_abs_diff_eq!(a, &b + &c, epsilon = 1e-6); } #[test] #[cfg(feature = "approx")] fn test_zip_assign_into_cell() { use approx::assert_abs_diff_eq; use std::cell::Cell; let a = Array::, _>::default((5, 10)); let b = Array::from_shape_fn((5, 10), |(i, j)| 1. / (i + 2 * j + 1) as f32); let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); Zip::from(&b).and(&c).map_assign_into(&a, |x, y| x + y); let a2 = a.mapv(|elt| elt.get()); assert_abs_diff_eq!(a2, &b + &c, epsilon = 1e-6); } #[test] fn test_zip_collect_drop() { use std::cell::RefCell; use std::panic; struct Recorddrop<'a>((usize, usize), &'a RefCell>); impl<'a> Drop for Recorddrop<'a> { fn drop(&mut self) { self.1.borrow_mut().push(self.0); } } #[derive(Copy, Clone)] enum Config { CC, CF, FF, } impl Config { fn a_is_f(self) -> bool { match self { Config::CC | Config::CF => false, _ => true, } } fn b_is_f(self) -> bool { match self { Config::CC => false, _ => true, } } } let test_collect_panic = |config: Config, will_panic: bool, slice: bool| { let mut inserts = RefCell::new(Vec::new()); let mut drops = RefCell::new(Vec::new()); let mut a = Array::from_shape_fn((5, 10).set_f(config.a_is_f()), |idx| idx); let mut b = Array::from_shape_fn((5, 10).set_f(config.b_is_f()), |_| 0); if slice { a = a.slice_move(s![.., ..-1]); b = b.slice_move(s![.., ..-1]); } let _result = panic::catch_unwind(panic::AssertUnwindSafe(|| { Zip::from(&a).and(&b).map_collect(|&elt, _| { if elt.0 > 3 && will_panic { panic!(); } inserts.borrow_mut().push(elt); Recorddrop(elt, &drops) }); })); println!("{:?}", inserts.get_mut()); println!("{:?}", drops.get_mut()); assert_eq!(inserts.get_mut().len(), drops.get_mut().len(), "Incorrect number of drops"); assert_eq!(inserts.get_mut(), drops.get_mut(), "Incorrect order of drops"); }; for &should_panic in &[true, false] { for &should_slice in &[false, true] { test_collect_panic(Config::CC, should_panic, should_slice); test_collect_panic(Config::CF, should_panic, should_slice); test_collect_panic(Config::FF, should_panic, should_slice); } } } #[test] fn test_azip_syntax_trailing_comma() { let mut b = Array::::zeros((5, 5)); let mut c = Array::::ones((5, 5)); let a = b.clone(); azip!((b in &mut b, c in &mut c, ) swap(b, c)); assert_eq!(a, c); assert!(a != b); } #[test] #[cfg(feature = "approx")] fn test_azip2_sum() { use approx::assert_abs_diff_eq; let c = Array::from_shape_fn((5, 10), |(i, j)| f32::exp((i + j) as f32)); for i in 0..2 { let ax = Axis(i); let mut b = Array::zeros(c.len_of(ax)); azip!((b in &mut b, c in c.axis_iter(ax)) *b = c.sum()); assert_abs_diff_eq!(b, c.sum_axis(Axis(1 - i)), epsilon = 1e-6); } } #[test] #[cfg(feature = "approx")] fn test_azip3_slices() { use approx::assert_abs_diff_eq; let mut a = [0.; 32]; let mut b = [0.; 32]; let mut c = [0.; 32]; for (i, elt) in b.iter_mut().enumerate() { *elt = i as f32; } azip!((a in &mut a[..], b in &b[..], c in &mut c[..]) { *a += b / 10.; *c = a.sin(); }); let res = Array::linspace(0., 3.1, 32).mapv_into(f32::sin); assert_abs_diff_eq!(res, ArrayView::from(&c), epsilon = 1e-4); } #[test] #[cfg(feature = "approx")] fn test_broadcast() { use approx::assert_abs_diff_eq; let n = 16; let mut a = Array::::zeros((n, n)); let mut b = Array::::from_elem((1, n), 1.); for ((i, j), elt) in b.indexed_iter_mut() { *elt /= 1. + (i + 2 * j) as f32; } let d = Array::from_elem((1, n), 1.); let e = Array::from_elem((), 2.); { let z = Zip::from(a.view_mut()) .and_broadcast(&b) .and_broadcast(&d) .and_broadcast(&e); z.for_each(|x, &y, &z, &w| *x = y + z + w); } let sum = &b + &d + &e; assert_abs_diff_eq!(a, sum.broadcast((n, n)).unwrap(), epsilon = 1e-4); } #[should_panic] #[test] fn test_zip_dim_mismatch_1() { let mut a = Array::zeros((5, 7)); let mut d = a.raw_dim(); d[0] += 1; let b = Array::from_shape_fn(d, |(i, j)| 1. / (i + 2 * j) as f32); azip!((a in &mut a, &b in &b) *a = b); } // Test that Zip handles memory layout correctly for // Zip::from(A).and(B) // where A is F-contiguous and B contiguous but neither F nor C contiguous. #[test] fn test_contiguous_but_not_c_or_f() { let a = Array::from_iter(0..27) .into_shape_with_order((3, 3, 3)) .unwrap(); // both F order let a = a.reversed_axes(); let mut b = a.clone(); assert_eq!(a.strides(), b.strides()); assert_eq!(a.strides(), &[1, 3, 9]); b.swap_axes(0, 1); // test single elem so that test keeps working if array `+` impl changes let correct = &a + &b; let correct_012 = a[[0, 1, 2]] + b[[0, 1, 2]]; let mut ans = Array::zeros(a.dim().f()); azip!((ans in &mut ans, &a in &a, &b in &b) *ans = a + b); println!("{:?}", a); println!("{:?}", b); println!("{:?}", ans); assert_eq!(ans[[0, 1, 2]], correct_012); assert_eq!(ans, correct); } #[test] fn test_clone() { let a = Array::from_iter(0..27) .into_shape_with_order((3, 3, 3)) .unwrap(); let z = Zip::from(&a).and(a.exact_chunks((1, 1, 1))); let w = z.clone(); let mut result = Vec::new(); z.for_each(|x, y| { result.push((x, y)); }); let mut i = 0; w.for_each(|x, y| { assert_eq!(result[i], (x, y)); i += 1; }); } #[test] fn test_indices_0() { let a1 = arr0(3); let mut count = 0; Zip::indexed(&a1).for_each(|i, elt| { count += 1; assert_eq!(i, ()); assert_eq!(*elt, 3); }); assert_eq!(count, 1); } #[test] fn test_indices_1() { let mut a1 = Array::default(12); for (i, elt) in a1.indexed_iter_mut() { *elt = i; } let mut count = 0; Zip::indexed(&a1).for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, a1.len()); let mut count = 0; let len = a1.len(); let (x, y) = Zip::indexed(&mut a1).split(); x.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len / 2); y.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len); } #[test] fn test_indices_2() { let mut a1 = Array::default((10, 12)); for (i, elt) in a1.indexed_iter_mut() { *elt = i; } let mut count = 0; azip!((index i, &a1 in &a1) { count += 1; assert_eq!(a1, i); }); assert_eq!(count, a1.len()); let mut count = 0; let len = a1.len(); let (x, y) = Zip::indexed(&mut a1).split(); x.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len / 2); y.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len); } #[test] fn test_indices_3() { let mut a1 = Array::default((4, 5, 6)); for (i, elt) in a1.indexed_iter_mut() { *elt = i; } let mut count = 0; Zip::indexed(&a1).for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, a1.len()); let mut count = 0; let len = a1.len(); let (x, y) = Zip::indexed(&mut a1).split(); x.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len / 2); y.for_each(|i, elt| { count += 1; assert_eq!(*elt, i); }); assert_eq!(count, len); } #[test] fn test_indices_split_1() { for m in (0..4).chain(10..12) { for n in (0..4).chain(10..12) { let a1 = Array::::default((m, n)); if a1.len() <= 1 { continue; } let (a, b) = Zip::indexed(&a1).split(); let mut seen = Vec::new(); let mut ac = 0; a.for_each(|i, _| { ac += 1; seen.push(i); }); let mut bc = 0; b.for_each(|i, _| { bc += 1; seen.push(i); }); assert_eq!(a1.len(), ac + bc); seen.sort(); assert_eq!(seen.len(), a1.len()); seen.dedup(); assert_eq!(seen.len(), a1.len()); } } } #[test] fn test_zip_all() { let a = Array::::zeros(62); let b = Array::::ones(62); let mut c = Array::::ones(62); c[5] = 0.0; assert_eq!(true, Zip::from(&a).and(&b).all(|&x, &y| x + y == 1.0)); assert_eq!(false, Zip::from(&a).and(&b).all(|&x, &y| x == y)); assert_eq!(false, Zip::from(&a).and(&c).all(|&x, &y| x + y == 1.0)); } #[test] fn test_zip_all_empty_array() { let a = Array::::zeros(0); let b = Array::::ones(0); assert_eq!(true, Zip::from(&a).and(&b).all(|&_x, &_y| true)); assert_eq!(true, Zip::from(&a).and(&b).all(|&_x, &_y| false)); }