use num_traits::ToPrimitive;
use crate::rational::Ratio;
use crate::rational::big::Big;
use crate::traits::Narrow;
use crate::{
Rational8, Rational16, Rational32, Rational64, Rational128, RationalUsize,
};
macro_rules! narrow_to {
($($narrow:ty => $to:ident),+ $(,)?) => {$(
impl<const S: usize> Narrow<$narrow> for Big<S> {
fn narrow(&self) -> Option<$narrow> {
Some(Ratio {
sign: self.sign,
numerator: self.numerator.$to()?,
denominator: self.denominator.$to()?,
})
}
}
)+}
}
narrow_to!(
Rational8 => to_u8,
Rational16 => to_u16,
Rational32 => to_u32,
Rational64 => to_u64,
Rational128 => to_u128,
RationalUsize => to_usize,
);
#[cfg(test)]
mod test {
use std::str::FromStr;
use crate::{
Narrow, Rational8, Rational64, Rational128, RationalBig, Sign, R32, R64, R8, RB,
};
#[test]
fn narrowing_keeps_the_value_or_declines() {
assert_eq!(RB!(3, 4).narrow(), Some(R8!(3, 4)));
assert_eq!(RB!(-3, 4).narrow(), Some(R8!(-3, 4)));
assert_eq!(RB!(0).narrow(), Some(R8!(0)));
assert_eq!(<RationalBig as Narrow<Rational8>>::narrow(&RB!(300)), None);
assert_eq!(RB!(300).narrow(), Some(R32!(300)));
assert_eq!(<RationalBig as Narrow<Rational8>>::narrow(&RB!(1, 300)), None);
}
#[test]
fn what_does_not_fit_in_any_of_them() {
let value = RationalBig::from_str("340282366920938463463374607431768211456").unwrap();
assert_eq!(<RationalBig as Narrow<Rational64>>::narrow(&value), None);
assert_eq!(<RationalBig as Narrow<Rational128>>::narrow(&value), None);
}
#[test]
fn a_multi_word_value_that_fits() {
let value = RationalBig::from_str("340282366920938463463374607431768211455").unwrap();
let narrow: Rational128 = value.narrow().unwrap();
assert_eq!(narrow, Rational128::new_signed(Sign::Positive, u128::MAX, 1).unwrap());
assert_eq!(RationalBig::from(narrow), value);
}
#[test]
fn narrowing_and_widening_are_inverse() {
for value in [RB!(0), RB!(1), RB!(-1), RB!(7, 9), RB!(-1234, 5678)] {
let narrow: Rational64 = value.narrow().unwrap();
assert_eq!(RationalBig::from(narrow), value);
}
assert_eq!(R64!(-1234, 5678), R64!(-617, 2839));
}
}