use g_math::fixed_point::frac_config::FRAC_BITS;
use g_math::fixed_point::{FixedMatrix, FixedPoint, FixedVector, OverflowDetected};
fn one() -> FixedPoint { FixedPoint::one() }
fn unit() -> FixedPoint {
let half = FixedPoint::from_str("0.5");
let mut u = one();
for _ in 0..FRAC_BITS { u = u.try_mul(half).unwrap(); }
u
}
fn top_power() -> FixedPoint {
let mut p = one();
while let Ok(q) = p.try_add(p) { p = q; }
p
}
fn min() -> FixedPoint { top_power().try_neg().unwrap().try_sub(top_power()).unwrap() }
fn max() -> FixedPoint { top_power().try_sub(unit()).unwrap().try_add(top_power()).unwrap() }
#[test]
fn edges_are_the_storage_edges() {
assert_eq!(min().try_add(max()).unwrap(), unit().try_neg().unwrap());
assert!(max().try_add(unit()).is_err());
assert!(min().try_sub(unit()).is_err());
}
#[test]
fn try_twins_report_overflow() {
assert_eq!(max().try_add(unit()), Err(OverflowDetected::TierOverflow));
assert_eq!(min().try_sub(unit()), Err(OverflowDetected::TierOverflow));
assert_eq!(min().try_neg(), Err(OverflowDetected::TierOverflow));
assert_eq!(max().try_mul(FixedPoint::from_str("1.5")), Err(OverflowDetected::TierOverflow));
assert_eq!(max().try_mul(max()), Err(OverflowDetected::TierOverflow));
assert_eq!(min().try_mul(one().try_neg().unwrap()), Err(OverflowDetected::TierOverflow));
assert_eq!(one().try_div(FixedPoint::ZERO), Err(OverflowDetected::DivisionByZero));
assert_eq!(min().try_div(one().try_neg().unwrap()), Err(OverflowDetected::TierOverflow));
assert_eq!(max().try_div(unit()), Err(OverflowDetected::TierOverflow));
}
#[test]
fn edges_that_fit_are_exact() {
assert_eq!(min().try_mul(one()).unwrap(), min());
assert_eq!(min().try_div(one()).unwrap(), min());
let half_min = min().try_mul(FixedPoint::from_str("0.5")).unwrap();
assert_eq!(half_min.try_add(half_min).unwrap(), min());
assert_eq!(max().try_neg().unwrap().try_sub(unit()).unwrap(), min());
assert_eq!(max().try_mul(one()).unwrap(), max());
assert_eq!(max().try_div(one()).unwrap(), max());
}
#[test]
fn operators_match_their_twins_in_range() {
let u = unit();
let mut values = vec![one(), u, u.try_neg().unwrap(), max(), min(), top_power(), FixedPoint::ZERO];
values.extend(["1.5", "-0.75", "3.25"].iter().filter_map(|s| FixedPoint::try_from_str(s).ok()));
for &a in &values {
for &b in &values {
if let Ok(s) = a.try_add(b) { assert_eq!(a + b, s); }
if let Ok(d) = a.try_sub(b) { assert_eq!(a - b, d); }
if let Ok(p) = a.try_mul(b) { assert_eq!(a * b, p); }
if let Ok(q) = a.try_div(b) { assert_eq!(a / b, q); }
}
if let Ok(n) = a.try_neg() { assert_eq!(-a, n); }
}
}
#[test]
#[should_panic(expected = "overflow")]
fn add_panics_at_the_top() { let _ = max() + unit(); }
#[test]
#[should_panic(expected = "overflow")]
fn sub_panics_at_the_bottom() { let _ = min() - unit(); }
#[test]
#[should_panic(expected = "overflow")]
fn neg_panics_at_the_minimum() { let _ = -min(); }
#[test]
#[should_panic(expected = "overflow")]
fn mul_panics_out_of_range() { let _ = max() * max(); }
#[test]
#[should_panic(expected = "division by zero")]
fn div_panics_on_zero() { let _ = one() / FixedPoint::ZERO; }
#[test]
#[should_panic(expected = "overflow")]
fn div_panics_out_of_range() { let _ = max() / unit(); }
#[test]
#[should_panic(expected = "overflow")]
fn add_assign_panics_at_the_top() { let mut x = max(); x += unit(); }
#[test]
#[should_panic(expected = "overflow")]
fn vector_add_inherits_the_check() {
let _ = FixedVector::from_slice(&[one(), max()]) + FixedVector::from_slice(&[one(), unit()]);
}
#[test]
#[should_panic(expected = "overflow")]
fn matrix_add_inherits_the_check() {
let _ = FixedMatrix::from_slice(1, 2, &[one(), max()]) + FixedMatrix::from_slice(1, 2, &[one(), unit()]);
}