use core::convert::TryFrom;
use rand::prelude::SliceRandom;
use scientific::{Error, Precision, Scientific};
#[allow(clippy::type_complexity)]
const FUNCTIONS: [(
&str,
fn(f64, f64) -> f64,
fn(&Scientific, &Scientific) -> Result<Scientific, Error>,
); 4] = [
("add", |a, b| a + b, |a, b| Ok(a + b)),
("sub", |a, b| a - b, |a, b| Ok(a - b)),
("mul", |a, b| a * b, |a, b| Ok(a * b)),
(
"div",
|a, b| a / b,
|a, b| a.div_truncate(b, Precision::F64),
),
];
pub(crate) fn test_float<I>(numbers: I, exponents: &[i32], randomize: bool)
where
I: Iterator<Item = f64>,
{
let mut numbers = numbers
.map(|n| (n, Scientific::from_string(n.to_string()).unwrap()))
.collect::<Vec<(f64, Scientific)>>();
if randomize {
let mut rng = rand::thread_rng();
numbers.shuffle(&mut rng);
}
for (flt_a, sci_a) in numbers.iter() {
for (flt_b, sci_b) in numbers.iter() {
for (name, flt_fn, sci_fn) in FUNCTIONS.iter() {
let flt_result = flt_fn(*flt_a, *flt_b);
let sci_result = sci_fn(sci_a, sci_b);
let diff = diff(sci_a, sci_b, flt_result, &sci_result);
assert!(
diff < 2e-15_f64,
"function {name}({flt_a}, {flt_b}) -> {flt_result:?} = {sci_result:?}; diff: {diff:e}",
);
}
let flt_result = flt_a.partial_cmp(flt_b);
let sci_result = sci_a.partial_cmp(sci_b);
assert_eq!(
flt_result, sci_result,
"function {}({}, {}) -> {:?} = {:?}",
"partial_cmp", flt_a, flt_b, flt_result, sci_result,
);
}
for int_b in exponents {
let flt_result = flt_a.powi(*int_b);
let sci_result = sci_a.powi((*int_b) as usize);
let diff = diff(sci_a, sci_a, flt_result, &Ok(sci_result.clone()));
assert!(
diff < 5e-15_f64,
"function {}({}, {}) -> {:?} = {:?}; diff: {:e}",
"powi",
flt_a,
int_b,
flt_result,
sci_result,
diff,
);
}
let flt_result = flt_a.sqrt();
let sci_result = sci_a.sqrt_rpsp(Precision::F64);
let diff = diff(sci_a, sci_a, flt_result, &sci_result);
assert!(
diff < 2e-15_f64,
"function {}({}) -> {:?} = {:?}; diff: {:e}",
"sqrt",
flt_a,
flt_result,
sci_result,
diff,
);
let bytes = sci_a.to_bytes();
assert_eq!(Ok(sci_a), Scientific::from_bytes(&bytes).as_ref());
}
}
fn diff(sci_a: &Scientific, sci_b: &Scientific, flt: f64, sci: &Result<Scientific, Error>) -> f64 {
match sci {
Err(_) => {
if flt.is_finite() {
1.0
} else {
0.0
}
}
Ok(sci) => {
if flt.is_infinite() && f64::from(sci).is_infinite() {
0.0
} else {
match Scientific::try_from(flt) {
Err(_) => 1.0, Ok(flt) => {
let max_exp = sci
.exponent1()
.max(sci_a.exponent1())
.max(sci_b.exponent1());
let n1 = sci >> max_exp;
let n2 = &flt >> max_exp;
(&(&n1 - &n2).abs()).into()
}
}
}
}
}
}