use crate::shrinks;
use crate::BoxShrink;
use num_traits::Float;
pub fn float<F>() -> BoxShrink<F>
where
F: Float + std::fmt::Debug + Clone + 'static,
{
shrinks::from_fn(move |original: F| {
let finite_original = if original.is_finite() {
original
} else {
F::min_value()
};
let finites = finite_values(finite_original);
let specials = special_values(original);
specials.clone().into_iter().chain(finites.clone())
})
}
fn special_values<F>(original: F) -> Vec<F>
where
F: Float + std::fmt::Debug + Clone + 'static,
{
let mut values = Vec::<F>::new();
if original.is_nan() {
values.push(F::neg_infinity());
values.push(F::infinity());
} else if original.is_infinite() && original.is_sign_negative() {
values.push(F::infinity());
}
values
}
fn finite_values<F>(original: F) -> impl Iterator<Item = F> + Clone
where
F: Float + std::fmt::Debug + Clone + 'static,
{
let mut subtraction = original;
let mut old_result = original;
let two = F::one() + F::one();
std::iter::from_fn(move || {
let result = original - subtraction;
if result == old_result {
None
} else {
subtraction = subtraction / two;
old_result = result;
Some(vec![-result, result])
}
})
.flatten()
}
#[cfg(test)]
mod test {
use crate::gens;
use crate::monkey_test;
use crate::testing::assert_iter_eq;
#[test]
fn should_be_able_to_shrink_from_nan() {
assert_iter_eq(
super::float().candidates(f32::NAN).take(4),
vec![f32::NEG_INFINITY, f32::INFINITY, -0.0, 0.0],
"should shrink to (-)inf and further from NaN",
)
}
#[test]
fn shrinker_should_always_terminate() {
monkey_test()
.with_generator(gens::f64::any())
.assert_true(|f| dbg!(super::float().candidates(f).count()) < 200);
}
}