use num_traits::Zero;
use unitforge::quantities::{Distance, DistanceUnit, Force, ForceUnit};
use unitforge::{MAX_ABS_QUANTITY_POWER, PhysicsQuantity};
#[test]
fn optimize_on_add() {
let f_1 = Force::new(10.0, ForceUnit::N);
let f_2 = Force::new(1.0e12, ForceUnit::N);
let f = f_1 + f_2;
assert!(f.get_multiplier().log10().round() == 0.0);
}
#[test]
fn optimize_large() {
let mut x = Force::from_exponential(1e10, 0);
x.optimize();
assert!(x.get_multiplier().log10().round() == 0.0);
assert_eq!(x.to(ForceUnit::N), 1e10);
}
#[test]
fn optimize_small() {
let mut x = Force::from_exponential(1e-10, 0);
x.optimize();
assert!(x.get_multiplier().log10().round() == 0.0);
assert_eq!(x.to(ForceUnit::N), 1e-10);
}
#[test]
fn from_exponential_preserves_small_multiplier_with_large_positive_power() {
let force = Force::from_exponential(1.0e-20, 20);
assert!((force.to(ForceUnit::N) - 1.0).abs() < 1.0e-12);
}
#[test]
fn as_f64_preserves_denormalized_representation() {
let force = Force::from_exponential(1.0e-20, 20);
assert!((force.as_f64() - 1.0).abs() < 1.0e-12);
assert!((force.to(ForceUnit::N) - 1.0).abs() < 1.0e-12);
}
#[test]
fn optimize_normalizes_small_multiplier_instead_of_zeroing() {
let mut force = Force::from_exponential(1.0e-20, 20);
force.optimize();
assert!((force.to(ForceUnit::N) - 1.0).abs() < 1.0e-12);
assert!(!force.is_zero());
}
#[test]
fn optimize_zero() {
let mut force = Force::zero();
force.optimize();
assert_eq!(force, Force::zero());
}
#[test]
fn optimize_infinity_is_a_no_op() {
let mut pos = Force::INFINITY;
let mut neg = Force::NEG_INFINITY;
pos.optimize();
neg.optimize();
assert!(pos.get_multiplier().is_infinite());
assert!(neg.get_multiplier().is_infinite());
assert!(pos.get_multiplier().is_sign_positive());
assert!(neg.get_multiplier().is_sign_negative());
assert_eq!(pos.get_power(), 0);
assert_eq!(neg.get_power(), 0);
}
#[test]
fn optimize_saturates_to_infinity_when_upper_bound_is_reached() {
let mut force = Force::from_exponential(1.0, MAX_ABS_QUANTITY_POWER);
force.optimize();
assert!(force.get_multiplier().is_infinite());
assert!(force.get_multiplier().is_sign_positive());
assert_eq!(force.get_power(), 0);
}
#[test]
fn optimize_saturates_to_negative_infinity_when_upper_bound_is_reached() {
let mut force = Force::from_exponential(-1.0, MAX_ABS_QUANTITY_POWER);
force.optimize();
assert!(force.get_multiplier().is_infinite());
assert!(force.get_multiplier().is_sign_negative());
assert_eq!(force.get_power(), 0);
}
#[test]
fn optimize_saturates_to_zero_when_lower_bound_is_reached() {
let mut force = Force::from_exponential(1.0, -MAX_ABS_QUANTITY_POWER);
force.optimize();
assert!(force.is_zero());
assert_eq!(force.get_power(), 0);
}
#[test]
fn scalar_mul_saturates_when_upper_bound_is_reached() {
let force = Force::from_exponential(1.0, MAX_ABS_QUANTITY_POWER - 1) * 10.0;
assert!(force.get_multiplier().is_infinite());
assert!(force.get_multiplier().is_sign_positive());
assert_eq!(force.get_power(), 0);
}
#[test]
fn scalar_div_saturates_to_zero_when_lower_bound_is_reached() {
let force = Force::from_exponential(1.0, -(MAX_ABS_QUANTITY_POWER - 1)) / 10.0;
assert!(force.is_zero());
assert_eq!(force.get_power(), 0);
}
#[test]
fn add_and_sub_keep_normalized_power_within_bound() {
let lhs = Force::from_exponential(1.0e-20, 20);
let rhs = Force::from_exponential(5.0e-21, 20);
let sum = lhs + rhs;
assert!(sum.get_power().abs() < MAX_ABS_QUANTITY_POWER);
assert!((sum.to(ForceUnit::N) - 1.5).abs() < 1.0e-12);
let diff = lhs - rhs;
assert!(diff.get_power().abs() < MAX_ABS_QUANTITY_POWER);
assert!((diff.to(ForceUnit::N) - 0.5).abs() < 1.0e-12);
}
#[test]
fn subtraction_with_large_powers_does_not_overflow() {
let lhs = Distance::from_exponential(1.0, i32::MAX);
let rhs = Distance::from_exponential(1.0, i32::MAX);
let diff = lhs - rhs;
assert_eq!(diff.to(DistanceUnit::m), 0.0);
}