use ariadnetor_core::Scalar;
use num_traits::{Float, NumCast};
#[inline]
pub(crate) fn try_real_from_f64<T: Scalar>(x: f64) -> Option<T::Real> {
let cast = <T::Real as NumCast>::from(x)?;
if cast.is_finite() { Some(cast) } else { None }
}
#[cfg(test)]
mod tests {
use super::try_real_from_f64;
use num_complex::Complex;
#[test]
fn f64_identity_within_range() {
assert_eq!(try_real_from_f64::<f64>(0.0), Some(0.0));
assert_eq!(try_real_from_f64::<f64>(1e-10), Some(1e-10));
assert_eq!(try_real_from_f64::<f64>(1e300), Some(1e300));
assert_eq!(try_real_from_f64::<f64>(-1e300), Some(-1e300));
}
#[test]
fn f64_rejects_non_finite() {
assert_eq!(try_real_from_f64::<f64>(f64::INFINITY), None);
assert_eq!(try_real_from_f64::<f64>(f64::NEG_INFINITY), None);
assert_eq!(try_real_from_f64::<f64>(f64::NAN), None);
}
#[test]
fn f32_within_range_round_trips() {
assert_eq!(try_real_from_f64::<f32>(0.0), Some(0.0_f32));
assert_eq!(try_real_from_f64::<f32>(1e-10), Some(1e-10_f32));
}
#[test]
fn f32_rejects_overflow_to_inf() {
assert_eq!(try_real_from_f64::<f32>(1e300_f64), None);
assert_eq!(try_real_from_f64::<f32>(-1e300_f64), None);
assert_eq!(try_real_from_f64::<f32>((f32::MAX as f64) * 2.0), None);
}
#[test]
fn f32_rejects_non_finite() {
assert_eq!(try_real_from_f64::<f32>(f64::INFINITY), None);
assert_eq!(try_real_from_f64::<f32>(f64::NEG_INFINITY), None);
assert_eq!(try_real_from_f64::<f32>(f64::NAN), None);
}
#[test]
fn complex_real_part_inherits_constraints() {
assert_eq!(try_real_from_f64::<Complex<f32>>(1e300_f64), None);
assert_eq!(try_real_from_f64::<Complex<f32>>(f64::NAN), None);
assert_eq!(try_real_from_f64::<Complex<f32>>(1e-10), Some(1e-10_f32));
assert_eq!(try_real_from_f64::<Complex<f64>>(1e300_f64), Some(1e300));
assert_eq!(try_real_from_f64::<Complex<f64>>(f64::INFINITY), None);
}
}