use uninum::{Number, num};
#[test]
fn test_cross_type_equality_consistency() {
let equivalent_groups = vec![
vec![
Number::from(0_u64),
Number::from(0_i64),
Number::from(0_u64),
Number::from(0_i64),
num!(0.0),
num!(-0.0),
num!(0.0),
num!(-0.0),
],
vec![
Number::from(1_u64),
Number::from(1_i64),
Number::from(1_u64),
Number::from(1_i64),
num!(1.0),
num!(1.0),
],
vec![
Number::from(42_u64),
Number::from(42_i64),
Number::from(42_u64),
Number::from(42_i64),
num!(42.0),
num!(42.0),
],
vec![
Number::from(255_u64),
Number::from(255_u64),
Number::from(255_i64),
Number::from(255_i64),
num!(255.0),
num!(255.0),
],
vec![
Number::from(-1_i64),
Number::from(-1_i64),
num!(-1.0),
num!(-1.0),
],
];
#[cfg(feature = "decimal")]
let equivalent_groups = {
use rust_decimal::Decimal;
let mut equivalent_groups = equivalent_groups;
equivalent_groups[0].push(Number::from(Decimal::new(0, 0)));
equivalent_groups[1].push(Number::from(Decimal::new(1, 0)));
equivalent_groups[2].push(Number::from(Decimal::new(42, 0)));
equivalent_groups[3].push(Number::from(Decimal::new(255, 0)));
equivalent_groups[4].push(Number::from(Decimal::new(-1, 0)));
equivalent_groups
};
for group in equivalent_groups {
for (i, a) in group.iter().enumerate() {
for (j, b) in group.iter().enumerate() {
assert_eq!(
a, b,
"Cross-type equality failed at group item {i} and {j}: {a:?} != {b:?}"
);
}
}
}
}
#[test]
fn test_cross_type_ordering_consistency() {
let test_cases = vec![
(Number::from(10_u64), Number::from(20_u64)),
(Number::from(-10_i64), Number::from(10_u64)),
(Number::from(100_u64), num!(200.0)),
(Number::from(-50_i64), num!(0.0)),
(num!(3.12), num!(3.15)),
(Number::from(1000_u64), Number::from(2000_i64)),
(
Number::from(i64::from(i32::MIN)),
Number::from(i64::from(i32::MAX)),
),
(Number::from(0_u64), Number::from(u64::from(u32::MAX))),
(Number::from(i64::MIN), Number::from(i64::MAX)),
(Number::from(0_u64), Number::from(i64::MAX)),
];
for (smaller, larger) in test_cases {
assert!(
smaller < larger,
"Cross-type ordering failed: {smaller:?} should be < {larger:?}"
);
assert!(
smaller <= larger,
"Cross-type ordering failed: {smaller:?} should be <= {larger:?}"
);
assert!(
larger > smaller,
"Cross-type ordering failed: {larger:?} should be > {smaller:?}"
);
assert!(
larger >= smaller,
"Cross-type ordering failed: {larger:?} should be >= {smaller:?}"
);
assert_ne!(
smaller, larger,
"Cross-type ordering failed: {smaller:?} should not equal {larger:?}"
);
}
}
#[test]
fn test_cross_type_hash_consistency() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
fn calculate_hash<T: Hash>(t: &T) -> u64 {
let mut hasher = DefaultHasher::new();
t.hash(&mut hasher);
hasher.finish()
}
let equivalent_groups = vec![
vec![
Number::from(42_u64),
Number::from(42_i64),
Number::from(42_u64),
Number::from(42_i64),
num!(42.0),
num!(42.0),
],
vec![
Number::from(0_u64),
Number::from(0_i64),
Number::from(0_u64),
Number::from(0_i64),
num!(0.0),
num!(0.0),
],
];
for group in equivalent_groups {
let mut expected_hash = None;
for num in &group {
let hash = calculate_hash(num);
if let Some(expected) = expected_hash {
assert_eq!(
hash, expected,
"Cross-type hash inconsistency: {num:?} hashes to {hash} but expected \
{expected}"
);
} else {
expected_hash = Some(hash);
}
}
}
}
#[test]
fn test_cross_type_special_values() {
let nan_f64 = num!(f64::NAN);
let nan_f64_2 = num!(f64::NAN);
let pos_inf_f64 = num!(f64::INFINITY);
let pos_inf_f64_2 = num!(f64::INFINITY);
let neg_inf_f64 = num!(f64::NEG_INFINITY);
let neg_inf_f64_2 = num!(f64::NEG_INFINITY);
assert_eq!(nan_f64, nan_f64_2, "NaN should be equal to itself");
assert_eq!(
pos_inf_f64, pos_inf_f64_2,
"Positive infinity should be equal to itself"
);
assert_eq!(
neg_inf_f64, neg_inf_f64_2,
"Negative infinity should be equal to itself"
);
assert!(
neg_inf_f64 < pos_inf_f64,
"Negative infinity should be less than positive infinity"
);
assert!(
pos_inf_f64 < nan_f64,
"Positive infinity should be less than NaN"
);
assert!(
neg_inf_f64 < nan_f64,
"Negative infinity should be less than NaN"
);
}
#[test]
fn test_cross_type_boundary_values() {
let boundary_tests = vec![
(
Number::from(u64::from(u32::MAX)),
Number::from(u32::MAX as u64),
),
(
Number::from(u64::from(u32::MAX)),
Number::from(u32::MAX as i64),
),
(Number::from(u64::from(u32::MAX)), num!(u32::MAX as f64)),
(
Number::from(i64::from(i32::MAX)),
Number::from(i32::MAX as i64),
),
(Number::from(i64::from(i32::MAX)), num!(i32::MAX as f64)),
(
Number::from(i64::from(i32::MIN)),
Number::from(i32::MIN as i64),
),
(Number::from(i64::from(i32::MIN)), num!(i32::MIN as f64)),
];
for (smaller_type, larger_type) in boundary_tests {
assert_eq!(
smaller_type, larger_type,
"Boundary value cross-type equality failed: {smaller_type:?} != {larger_type:?}"
);
}
}
#[test]
fn test_cross_type_precision_limits() {
let large_int = Number::from(9007199254740992_u64); let large_float = num!(9007199254740992.0);
assert_eq!(
large_int, large_float,
"Large integer should equal its exact float representation"
);
let larger_int = Number::from(9007199254740993_u64); let larger_float = num!(9007199254740993.0);
if larger_int != larger_float {
assert!(
larger_int != larger_float,
"Different precision values should not be equal"
);
}
}
#[test]
fn test_cross_type_arithmetic_consistency() {
let a_u32 = Number::from(10_u64);
let a_i32 = Number::from(10_i64);
let a_f64 = num!(10.0);
let b_u32 = Number::from(5_u64);
let b_i32 = Number::from(5_i64);
let b_f64 = num!(5.0);
assert_eq!(a_u32, a_i32, "Cross-type operands should be equal");
assert_eq!(a_u32, a_f64, "Cross-type operands should be equal");
assert_eq!(b_u32, b_i32, "Cross-type operands should be equal");
assert_eq!(b_u32, b_f64, "Cross-type operands should be equal");
}
#[test]
fn test_cross_type_collection_behavior() {
use std::collections::{HashMap, HashSet};
let mut set = HashSet::new();
set.insert(Number::from(42_u64));
set.insert(Number::from(42_i64));
set.insert(num!(42.0));
set.insert(Number::from(42_u64));
assert_eq!(
set.len(),
1,
"HashSet should contain only one entry for equivalent cross-type values"
);
let mut map = HashMap::new();
map.insert(Number::from(100_u64), "u32");
map.insert(Number::from(100_i64), "i32");
map.insert(num!(100.0), "f64");
assert_eq!(
map.len(),
1,
"HashMap should contain only one entry for equivalent cross-type keys"
);
assert_eq!(
map.get(&Number::from(100_u64)),
Some(&"f64"),
"HashMap should find equivalent cross-type keys"
);
}
#[test]
fn test_cross_type_mixed_operations() {
let values = [
Number::from(5_u64),
Number::from(5_i64),
Number::from(5_u64),
Number::from(5_i64),
num!(5.0),
num!(5.0),
];
for i in 0..values.len() {
for j in 0..values.len() {
assert_eq!(
values[i], values[j],
"Cross-type mixed operation failed: {:?} != {:?}",
values[i], values[j]
);
}
}
}
#[test]
fn test_cross_type_edge_case_consistency() {
let zero_values = vec![
Number::from(0_u64),
Number::from(0_i64),
Number::from(0_u64),
Number::from(0_i64),
num!(0.0),
num!(-0.0),
num!(0.0),
num!(-0.0),
];
for a in &zero_values {
for b in &zero_values {
assert_eq!(a, b, "All zero values should be equal: {a:?} != {b:?}");
}
}
let max_i8_values = vec![
Number::from(i64::from(i8::MAX as i32)),
Number::from(i8::MAX as i64),
Number::from(u64::from(i8::MAX as u32)),
Number::from(i8::MAX as u64),
num!(i8::MAX as f64),
num!(i8::MAX as f64),
];
for a in &max_i8_values {
for b in &max_i8_values {
assert_eq!(a, b, "All i8::MAX values should be equal: {a:?} != {b:?}");
}
}
}
#[test]
fn test_cross_type_precision_differences() {
let f64_pi = num!(3.16);
let f64_pi_2 = num!(3.16);
assert_eq!(
f64_pi, f64_pi_2,
"F64 values with same literal should be equal"
);
assert!(f64_pi.approx_eq(&f64_pi_2, 1e-6, 0.0));
let a = num!(3.16);
let b = num!(3.16f64);
assert!(a.approx_eq(&b, 1e-6, 0.0));
}
#[test]
fn test_cross_type_large_integer_precision_loss() {
let large_int = Number::from(16777217_u64); let as_f64 = num!(16777217.0f64);
let as_f64_2 = num!(16777217.0f64);
assert_eq!(
large_int, as_f64,
"Large integer should equal its precise F64 representation"
);
assert_eq!(as_f64, as_f64_2, "F64 values should be equal");
}