relp-num 0.1.14

Number types for the relp crate
Documentation
use std::fmt::Debug;
use std::hash::Hash;

use index_utils::merge_sparse_indices;
use num_traits::One;

use crate::non_zero::NonZero;
use crate::NonZeroSign;
use crate::rational::Ratio;
use crate::Sign;
use crate::traits::factorization::{NonZeroFactorizable, NonZeroFactorization};

macro_rules! impl_with_sign {
    ($sign:ty) => {
        impl<Numerator, Denominator, Factor> NonZeroFactorizable for Ratio<$sign, Numerator, Denominator>
        where
            Self: One,
            Numerator: NonZeroFactorizable<Factor=Factor, Power=u32>,
            Denominator: NonZeroFactorizable<Factor=Factor, Power=u32>,
            Factor: Ord + NonZero + Hash + Clone + Debug,
        {
            type Factor = Factor;
            type Power = i32;

            fn factorize(&self) -> NonZeroFactorization<Self::Factor, Self::Power> {
                debug_assert!(self.is_not_zero());

                let all_powers_small = |factors: &[(Factor, u32)]| {
                    factors.iter().all(|&(_, p)| p <= i32::MAX as u32)
                };

                let NonZeroFactorization { factors: n_factors, sign: n_sign } = self.numerator.factorize();
                debug_assert!(all_powers_small(&n_factors));
                let NonZeroFactorization { factors: d_factors, sign: d_sign } = self.denominator.factorize();
                debug_assert!(all_powers_small(&d_factors));

                let factors = merge_sparse_indices(
                    n_factors.into_iter(), d_factors.into_iter(),
                    |left, right| left as i32 - right as i32,
                    |x| x as i32, |x| -(x as i32),
                    NonZero::is_not_zero,
                );

                NonZeroFactorization { sign: NonZeroSign::from(self.sign) * n_sign * d_sign, factors }
            }
        }
    }
}
impl_with_sign!(Sign);
impl_with_sign!(NonZeroSign);

#[cfg(test)]
mod test {
    use num_traits::{One, Zero};

    use crate::{NonZeroRationalBig, RationalBig};
    use crate::non_zero::NonZeroSign;
    use crate::rational::Ratio;
    use crate::traits::factorization::{NonZeroFactorizable, NonZeroFactorization};

    #[test]
    fn test_factorize() {
        let ratio: Ratio<_, u64, u64> = Ratio { sign: NonZeroSign::Positive, numerator: 1, denominator: 2 };
        let expected = NonZeroFactorization { sign: NonZeroSign::Positive, factors: vec![(2, -1)] };
        assert_eq!(ratio.factorize(), expected);

        let ratio: Ratio<_, u64, u64> = Ratio { sign: NonZeroSign::Positive, numerator: 161, denominator: 3 };
        let expected = NonZeroFactorization { sign: NonZeroSign::Positive, factors: vec![(3, -1), (7, 1), (23, 1)] };
        assert_eq!(ratio.factorize(), expected);

        let ratio: Ratio<_, u64, u64> = Ratio { sign: NonZeroSign::Positive, numerator: 2, denominator: 1 };
        let expected = NonZeroFactorization { sign: NonZeroSign::Positive, factors: vec![(2, 1)] };
        assert_eq!(ratio.factorize(), expected);

        let ratio: Ratio<_, u64, u64> = Ratio { sign: NonZeroSign::Negative, numerator: 1, denominator: 1 };
        let expected = NonZeroFactorization { sign: NonZeroSign::Negative, factors: vec![] };
        assert_eq!(ratio.factorize(), expected);

        let ratio = NonZeroRationalBig::one();
        let expected = NonZeroFactorization { sign: NonZeroSign::Positive, factors: vec![] };
        assert_eq!(ratio.factorize(), expected);
        let ratio = RationalBig::one();
        let expected = NonZeroFactorization { sign: NonZeroSign::Positive, factors: vec![] };
        assert_eq!(ratio.factorize(), expected);
    }

    #[test]
    #[should_panic]
    fn test_factorize_zero() {
        RationalBig::zero().factorize();
    }
}