use ferray_core::dimension::{Axis, Ix1, Ix2, IxDyn};
use ferray_core::{Array, error::FerrayResult};
use ferray_test_oracle::{TOL_REDUCTION_F64_ABS, assert_close_f64_slice};
#[test]
fn vectorize_satisfies_identity_and_mapv_equivalence() {
let id = ferray_window::vectorize(|x: f64| x);
let input =
Array::<f64, Ix1>::from_vec(Ix1::new([5]), vec![-2.0, -0.5, 0.0, 1.5, 3.0]).unwrap();
let out = id(&input).expect("ferray_window::vectorize identity");
assert_close_f64_slice(
out.as_slice().unwrap(),
input.as_slice().unwrap(),
0.0,
TOL_REDUCTION_F64_ABS,
);
let f = |x: f64| x.sin() + x.powi(2);
let vf = ferray_window::vectorize(f);
let via_vectorize = vf(&input).expect("ferray_window::vectorize map");
let via_mapv = input.mapv(f);
assert_close_f64_slice(
via_vectorize.as_slice().unwrap(),
via_mapv.as_slice().unwrap(),
0.0,
TOL_REDUCTION_F64_ABS,
);
let m2 =
Array::<f64, Ix2>::from_vec(Ix2::new([2, 3]), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]).unwrap();
let sq = ferray_window::vectorize(|x: f64| x * x);
let out2 = sq(&m2).expect("ferray_window::vectorize 2d");
assert_eq!(out2.shape(), &[2, 3]);
assert_close_f64_slice(
out2.as_slice().unwrap(),
&[1.0, 4.0, 9.0, 16.0, 25.0, 36.0],
0.0,
TOL_REDUCTION_F64_ABS,
);
}
#[test]
fn piecewise_matches_documented_semantics() {
let x = Array::<f64, Ix1>::from_vec(Ix1::new([5]), vec![-2.0, -0.5, 0.0, 1.5, 3.0]).unwrap();
let cond_neg =
Array::<bool, Ix1>::from_vec(Ix1::new([5]), vec![true, true, false, false, false]).unwrap();
let cond_pos =
Array::<bool, Ix1>::from_vec(Ix1::new([5]), vec![false, false, false, true, true]).unwrap();
let neg: &dyn Fn(f64) -> f64 = &|_| -1.0;
let pos: &dyn Fn(f64) -> f64 = &|_| 1.0;
let sign = ferray_window::piecewise(&x, &[cond_neg, cond_pos], &[neg, pos], 0.0)
.expect("ferray_window::piecewise sign");
assert_close_f64_slice(
sign.as_slice().unwrap(),
&[-1.0, -1.0, 0.0, 1.0, 1.0],
0.0,
TOL_REDUCTION_F64_ABS,
);
let c1 = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![true, true, true]).unwrap();
let c2 = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![true, true, true]).unwrap();
let y = Array::<f64, Ix1>::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
let f1: &dyn Fn(f64) -> f64 = &|v| v * 10.0;
let f2: &dyn Fn(f64) -> f64 = &|v| v * 100.0;
let result = ferray_window::piecewise(&y, &[c1, c2], &[f1, f2], 0.0)
.expect("ferray_window::piecewise first-match");
assert_close_f64_slice(
result.as_slice().unwrap(),
&[10.0, 20.0, 30.0],
0.0,
TOL_REDUCTION_F64_ABS,
);
let c_none = Array::<bool, Ix1>::from_vec(Ix1::new([3]), vec![false, false, false]).unwrap();
let any: &dyn Fn(f64) -> f64 = &|v| v * 10.0;
let result_default =
ferray_window::piecewise(&y, &[c_none], &[any], -42.0).expect("piecewise default");
assert_close_f64_slice(
result_default.as_slice().unwrap(),
&[-42.0, -42.0, -42.0],
0.0,
TOL_REDUCTION_F64_ABS,
);
}
#[test]
fn apply_along_axis_matches_numpy_sum_axis() {
let m =
Array::<f64, Ix2>::from_vec(Ix2::new([2, 3]), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]).unwrap();
let col_sums = ferray_window::apply_along_axis(
|col: &Array<f64, Ix1>| -> FerrayResult<f64> { Ok(col.iter().copied().sum::<f64>()) },
Axis(0),
&m,
)
.expect("ferray_window::apply_along_axis axis=0");
assert_eq!(col_sums.shape(), &[3]);
let col_data: Vec<f64> = col_sums.iter().copied().collect();
assert_close_f64_slice(&col_data, &[5.0, 7.0, 9.0], 0.0, TOL_REDUCTION_F64_ABS);
let row_sums = ferray_window::apply_along_axis(
|row: &Array<f64, Ix1>| -> FerrayResult<f64> { Ok(row.iter().copied().sum::<f64>()) },
Axis(1),
&m,
)
.expect("ferray_window::apply_along_axis axis=1");
assert_eq!(row_sums.shape(), &[2]);
let row_data: Vec<f64> = row_sums.iter().copied().collect();
assert_close_f64_slice(&row_data, &[6.0, 15.0], 0.0, TOL_REDUCTION_F64_ABS);
}
#[test]
fn apply_over_axes_matches_numpy_double_sum() {
let a = Array::<f64, IxDyn>::from_vec(IxDyn::new(&[2, 3]), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
.unwrap();
let total = ferray_window::apply_over_axes(ferray_window::sum_axis_keepdims, &a, &[0, 1])
.expect("ferray_window::apply_over_axes double sum");
assert_eq!(total.shape(), &[1, 1]);
let total_data: Vec<f64> = total.iter().copied().collect();
assert_close_f64_slice(&total_data, &[21.0], 0.0, TOL_REDUCTION_F64_ABS);
let along0 = ferray_window::apply_over_axes(ferray_window::sum_axis_keepdims, &a, &[0])
.expect("ferray_window::apply_over_axes axis 0");
assert_eq!(along0.shape(), &[1, 3]);
let along0_data: Vec<f64> = along0.iter().copied().collect();
assert_close_f64_slice(&along0_data, &[5.0, 7.0, 9.0], 0.0, TOL_REDUCTION_F64_ABS);
}
#[test]
fn sum_axis_keepdims_matches_numpy_sum_with_keepdims() {
let a = Array::<f64, IxDyn>::from_vec(IxDyn::new(&[2, 3]), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
.unwrap();
let s0 = ferray_window::sum_axis_keepdims(&a, Axis(0))
.expect("ferray_window::sum_axis_keepdims axis 0");
assert_eq!(s0.shape(), &[1, 3]);
let s0_data: Vec<f64> = s0.iter().copied().collect();
assert_close_f64_slice(&s0_data, &[5.0, 7.0, 9.0], 0.0, TOL_REDUCTION_F64_ABS);
let s1 = ferray_window::sum_axis_keepdims(&a, Axis(1))
.expect("ferray_window::sum_axis_keepdims axis 1");
assert_eq!(s1.shape(), &[2, 1]);
let s1_data: Vec<f64> = s1.iter().copied().collect();
assert_close_f64_slice(&s1_data, &[6.0, 15.0], 0.0, TOL_REDUCTION_F64_ABS);
}