#![cfg(test)]
use alloc::{
string::{String, ToString},
vec::Vec,
};
use crate::{
numeric::{
Abs, BoundedMax, BoundedMin, Cast, CastError, CastFrom, Infinite, IsFinite, LossyCast,
LossyCastFrom, MinMax, Nan, SaturatingCast, SaturatingCastFrom, TryCast, TryCastFrom,
TryExactCast, TryExactCastFrom,
},
tuple::{Tuple, TupleLike},
};
#[test]
fn array_roundtrip() {
let t = Tuple::from_array([1_i32, 2, 3]);
assert_eq!(t.as_array(), &[1, 2, 3]);
assert_eq!(t.into_array(), [1, 2, 3]);
}
#[test]
fn splat_surface() {
let t = Tuple::<i32, 4>::splat(7);
assert_eq!(t.as_array(), &[7, 7, 7, 7]);
}
#[test]
fn iota_surface() {
let t = Tuple::<f32, 5>::iota();
assert_eq!(t.as_array(), &[0.0, 1.0, 2.0, 3.0, 4.0]);
}
#[test]
fn slice_access_surface() {
let mut t = Tuple::from_array([1_i32, 2, 3]);
assert_eq!(t.as_slice(), &[1, 2, 3]);
t.as_mut_slice()[1] = 20;
assert_eq!(t.as_slice(), &[1, 20, 3]);
}
#[test]
fn iter_surface() {
let t = Tuple::from_array([1_i32, 2, 3]);
let collected = t.iter().copied().collect::<Vec<i32>>();
assert_eq!(collected, Vec::from([1, 2, 3]));
let mut t2 = Tuple::from_array([1_i32, 2, 3]);
for v in t2.iter_mut() {
*v *= 2;
}
assert_eq!(t2.as_array(), &[2, 4, 6]);
}
#[test]
fn map_and_zip_surface() {
let a = Tuple::from_array([1_i32, 2, 3]);
let b = Tuple::from_array([4_i32, 5, 6]);
let doubled = a.map(|x| x * 2);
assert_eq!(doubled.as_array(), &[2, 4, 6]);
let plus_one = a.map_ref(|x| x + 1);
assert_eq!(plus_one.as_array(), &[2, 3, 4]);
let summed = a.zip_with(&b, |x, y| x + y);
assert_eq!(summed.as_array(), &[5, 7, 9]);
let zipped = a.zip(&b);
assert_eq!(zipped.as_array(), &[(&1, &4), (&2, &5), (&3, &6)]);
}
#[test]
fn indexing_surface() {
let mut t = Tuple::from_array([10_i32, 20, 30]);
assert_eq!(t[0], 10);
assert_eq!(t[2], 30);
t[1] = 99;
assert_eq!(t.as_array(), &[10, 99, 30]);
}
#[test]
fn into_iterator_surface() {
let t = Tuple::from_array([1_i32, 2, 3]);
let owned = t.into_iter().collect::<Vec<i32>>();
assert_eq!(owned, Vec::from([1, 2, 3]));
let t2 = Tuple::from_array([4_i32, 5, 6]);
let by_ref = (&t2).into_iter().copied().collect::<Vec<i32>>();
assert_eq!(by_ref, Vec::from([4, 5, 6]));
let mut t3 = Tuple::from_array([7_i32, 8, 9]);
for v in &mut t3 {
*v += 1;
}
assert_eq!(t3.as_array(), &[8, 9, 10]);
}
#[test]
fn conversion_and_default_surface() {
let t: Tuple<i32, 3> = [1, 2, 3].into();
assert_eq!(t.as_array(), &[1, 2, 3]);
let arr: [i32; 3] = t.into();
assert_eq!(arr, [1, 2, 3]);
let d = Tuple::<i32, 3>::default();
assert_eq!(d.as_array(), &[0, 0, 0]);
}
#[test]
fn tuplelike_surface() {
let mut t = Tuple::from_array([1_i32, 2, 3]);
assert_eq!(<Tuple<i32, 3> as TupleLike>::LEN, 3);
assert_eq!(TupleLike::as_slice(&t), &[1, 2, 3]);
TupleLike::as_mut_slice(&mut t)[0] = 11;
assert_eq!(t.as_array(), &[11, 2, 3]);
}
#[test]
fn arithmetic_surface() {
let a = Tuple::from_array([10_i32, 20, 30]);
let b = Tuple::from_array([1_i32, 2, 3]);
assert_eq!((-a).as_array(), &[-10, -20, -30]);
assert_eq!((a + b).as_array(), &[11, 22, 33]);
assert_eq!((a - b).as_array(), &[9, 18, 27]);
assert_eq!((a * b).as_array(), &[10, 40, 90]);
assert_eq!((a / b).as_array(), &[10, 10, 10]);
assert_eq!((a % b).as_array(), &[0, 0, 0]);
assert_eq!((a + 2).as_array(), &[12, 22, 32]);
assert_eq!((a - 2).as_array(), &[8, 18, 28]);
assert_eq!((a * 2).as_array(), &[20, 40, 60]);
assert_eq!((a / 2).as_array(), &[5, 10, 15]);
assert_eq!((a % 4).as_array(), &[2, 0, 2]);
let mut c = Tuple::from_array([1_i32, 2, 3]);
c += Tuple::from_array([2, 3, 4]);
assert_eq!(c.as_array(), &[3, 5, 7]);
c -= Tuple::from_array([1, 1, 1]);
assert_eq!(c.as_array(), &[2, 4, 6]);
c *= Tuple::from_array([2, 2, 2]);
assert_eq!(c.as_array(), &[4, 8, 12]);
c /= Tuple::from_array([2, 2, 3]);
assert_eq!(c.as_array(), &[2, 4, 4]);
c %= Tuple::from_array([2, 3, 3]);
assert_eq!(c.as_array(), &[0, 1, 1]);
c += 2;
assert_eq!(c.as_array(), &[2, 3, 3]);
c -= 1;
assert_eq!(c.as_array(), &[1, 2, 2]);
c *= 3;
assert_eq!(c.as_array(), &[3, 6, 6]);
c /= 3;
assert_eq!(c.as_array(), &[1, 2, 2]);
c %= 2;
assert_eq!(c.as_array(), &[1, 0, 0]);
}
#[test]
fn numeric_predicates_surface() {
let all_finite = Tuple::from_array([1.0_f32, 2.0, 3.0]);
let has_infinite = Tuple::from_array([1.0_f32, f32::INFINITY, 3.0]);
let has_nan = Tuple::from_array([1.0_f32, f32::NAN, 3.0]);
assert!(all_finite.is_finite());
assert!(!has_infinite.is_finite());
assert!(!has_nan.is_finite());
assert!(!all_finite.is_infinite());
assert!(has_infinite.is_infinite());
assert!(!all_finite.is_nan());
assert!(has_nan.is_nan());
let inf = Tuple::<f32, 3>::INFINITY;
let neg_inf = Tuple::<f32, 3>::NEG_INFINITY;
let nan = Tuple::<f32, 3>::NAN;
assert!(inf.is_infinite());
assert!(neg_inf.is_infinite());
assert!(nan.is_nan());
let inf_trait = <Tuple<f32, 3> as Infinite>::INFINITY;
let neg_inf_trait = <Tuple<f32, 3> as Infinite>::NEG_INFINITY;
let nan_trait = <Tuple<f32, 3> as Nan>::NAN;
assert!(inf_trait.is_infinite());
assert!(neg_inf_trait.is_infinite());
assert!(nan_trait.is_nan());
assert!(<Tuple<f32, 3> as IsFinite>::is_finite(all_finite));
}
#[test]
fn bounded_and_min_max_surface() {
assert_eq!(<Tuple<i32, 3> as BoundedMin>::MIN.as_array(), &[i32::MIN; 3]);
assert_eq!(<Tuple<i32, 3> as BoundedMax>::MAX.as_array(), &[i32::MAX; 3]);
let a = Tuple::from_array([1_i32, 5, 3]);
let b = Tuple::from_array([2_i32, 4, 7]);
assert_eq!(a.min(b).as_array(), &[1, 4, 3]);
assert_eq!(a.max(b).as_array(), &[2, 5, 7]);
assert_eq!(<Tuple<i32, 3> as MinMax>::minimum(a, b).as_array(), &[1, 4, 3]);
assert_eq!(<Tuple<i32, 3> as MinMax>::maximum(a, b).as_array(), &[2, 5, 7]);
}
#[test]
fn abs_surface() {
let t = Tuple::from_array([-1_i32, 2, -3]);
assert_eq!(t.abs().as_array(), &[1, 2, 3]);
assert_eq!(<Tuple<i32, 3> as Abs>::abs(t).as_array(), &[1, 2, 3]);
let a = Tuple::from_array([1_i32, 2, 3]);
assert_eq!(a.negate().as_array(), &[-1, -2, -3]);
}
#[test]
fn approx_eq_abs_surface() {
let a = Tuple::from_array([1.0_f32, 2.0, 3.0]);
let b = Tuple::from_array([1.0_f32 + 5e-7, 2.0 - 5e-7, 3.0 + 5e-7]);
let c = Tuple::from_array([1.0_f32 + 2e-4, 2.0, 3.0]);
assert!(a.approx_eq_abs_tol(&b, 1e-6));
assert!(!a.approx_eq_abs_tol(&c, 1e-6));
}
#[test]
fn approx_eq_rel_surface() {
let a = Tuple::from_array([10.0_f32, 20.0, 30.0]);
let b = Tuple::from_array([10.0_f32 + 5e-5, 20.0 - 5e-5, 30.0 + 5e-5]);
let c = Tuple::from_array([10.0_f32 + 5e-2, 20.0, 30.0]);
assert!(a.approx_eq_rel_tol(&b, 1e-4));
assert!(!a.approx_eq_rel_tol(&c, 1e-4));
}
#[test]
fn length_squared_surface() {
let t = Tuple::from_array([2_i32, 3, 6]);
assert_eq!(t.length_squared(), 49);
}
#[cfg(feature = "bytemuck")]
#[test]
fn bytemuck_roundtrip() {
let t = Tuple::from_array([1_i32, 2, 3, 4]);
let bytes = bytemuck::bytes_of(&t);
let out = bytemuck::pod_read_unaligned::<Tuple<i32, 4>>(bytes);
assert_eq!(out, t);
}
#[cfg(feature = "zerocopy")]
#[test]
fn zerocopy_roundtrip() {
let t = Tuple::from_array([5_i32, 6, 7, 8]);
let bytes = <Tuple<i32, 4> as zerocopy::IntoBytes>::as_bytes(&t);
let out =
<Tuple<i32, 4> as zerocopy::FromBytes>::read_from_bytes(bytes).expect("valid tuple bytes");
assert_eq!(out, t);
}
#[cfg(feature = "sakka")]
#[test]
fn sakka_roundtrip() {
let t = Tuple::from_array([9_i32, 10, 11, 12]);
let mut writer = sakka::Writer::new(sakka::Endian::Little, ());
<Tuple<i32, 4> as sakka::Encode>::encode(&t, &mut writer).expect("encode tuple");
let bytes = writer.finish();
let mut reader = sakka::Reader::new(&bytes, sakka::Endian::Little, ());
let out = <Tuple<i32, 4> as sakka::Decode>::decode(&mut reader).expect("decode tuple");
assert_eq!(out, t);
assert!(reader.is_eof());
}
#[test]
fn try_map_surface() {
let t = Tuple::from_array([1_i32, 2, 3]);
let result = t.try_map(|x| -> Result<i32, String> { Ok(x * 2) });
assert_eq!(result, Ok(Tuple::from_array([2_i32, 4, 6])));
let result = t.try_map(|x| -> Result<i32, String> {
if x == 2 { Err("failed at 2".to_string()) } else { Ok(x * 10) }
});
assert_eq!(result, Err("failed at 2".to_string()));
let result = t.try_map(|x| -> Result<u8, CastError> {
if x > 255 { Err(CastError::OutOfRange) } else { Ok(x as u8) }
});
assert_eq!(result, Ok(Tuple::from_array([1_u8, 2, 3])));
}
#[test]
fn cast_variants_surface() {
let src = Tuple::from_array([1_i32, 2, 3]);
let _: Tuple<i64, 3> = src.cast();
let _: Tuple<u8, 3> = src.lossy_cast();
let _: Tuple<u8, 3> = src.saturating_cast();
let _: Result<Tuple<u8, 3>, _> = src.try_cast();
let _: Result<Tuple<i64, 3>, _> = src.try_exact_cast();
let _: Tuple<i64, 3> = Tuple::cast_from(src);
let _: Tuple<u8, 3> = Tuple::lossy_cast_from(src);
let _: Tuple<u8, 3> = Tuple::saturating_cast_from(src);
let _: Result<Tuple<u8, 3>, _> = Tuple::try_cast_from(src);
let _: Result<Tuple<i64, 3>, _> = Tuple::try_exact_cast_from(src);
let _: Tuple<i64, 3> = <Tuple<i32, 3> as Cast<Tuple<i64, 3>>>::cast(src);
let _: Tuple<u8, 3> = <Tuple<i32, 3> as LossyCast<Tuple<u8, 3>>>::lossy_cast(src);
let _: Tuple<u8, 3> = <Tuple<i32, 3> as SaturatingCast<Tuple<u8, 3>>>::saturating_cast(src);
let _: Result<Tuple<u8, 3>, _> = <Tuple<i32, 3> as TryCast<Tuple<u8, 3>>>::try_cast(src);
let _: Result<Tuple<i64, 3>, _> =
<Tuple<i32, 3> as TryExactCast<Tuple<i64, 3>>>::try_exact_cast(src);
let _: Tuple<i64, 3> = <Tuple<i64, 3> as CastFrom<Tuple<i32, 3>>>::cast_from(src);
let _: Tuple<u8, 3> = <Tuple<u8, 3> as LossyCastFrom<Tuple<i32, 3>>>::lossy_cast_from(src);
let _: Tuple<u8, 3> =
<Tuple<u8, 3> as SaturatingCastFrom<Tuple<i32, 3>>>::saturating_cast_from(src);
let _: Result<Tuple<u8, 3>, _> =
<Tuple<u8, 3> as TryCastFrom<Tuple<i32, 3>>>::try_cast_from(src);
let _: Result<Tuple<i64, 3>, _> =
<Tuple<i64, 3> as TryExactCastFrom<Tuple<i32, 3>>>::try_exact_cast_from(src);
}