use num::Rational64; use super::Int; use crate::prelude::*; pub type Ratio = num::BigRational; impl From for Ratio { fn from(value: Int) -> Self { Ratio::from_integer(value.into()) } } impl From<&Int> for Ratio { fn from(value: &Int) -> Self { Ratio::from_integer(value.to_owned().into()) } } pub trait RatioExt: Sized { #[must_use] fn div_euclid(&self, n: &Self) -> Self; #[must_use] fn rem_euclid(&self, n: &Self) -> Self; fn to_f64_uc(&self) -> f64; #[must_use] fn approximate_float(x: f64) -> Option; } impl RatioExt for Ratio { #[inline] fn div_euclid(&self, n: &Self) -> Ratio { let (n_abs, n_sgn) = (n.abs(), n.signum()); (self / n_abs).floor() * n_sgn } #[inline] fn rem_euclid(&self, n: &Self) -> Ratio { ToPrimitive::to_f64(self); let n_abs = n.abs(); self - (self / &n_abs).floor() * &n_abs } #[inline] fn to_f64_uc(&self) -> f64 { if let Some(f) = self.to_f64() { f } else if self.is_negative() { f64::NEG_INFINITY } else { f64::INFINITY } } fn approximate_float(x: f64) -> Option { if let Some(r) = Rational64::approximate_float(x) { let r = Ratio::new((*r.numer()).into(), (*r.denom()).into()); Some(r) } else { let mut y = x; if y.abs() < 1.0 { y = 1.0 / y; } let i = Int::from_f64(y)?; let r = Ratio::from_integer(i.into()); if x.abs() < 1.0 { Some(r.recip()) } else { Some(r) } } } }