dashu_ratio/
repr.rs

1use dashu_base::{EstimatedLog2, Gcd};
2use dashu_int::{IBig, UBig};
3
4pub struct Repr {
5    pub(crate) numerator: IBig,
6    pub(crate) denominator: UBig,
7}
8
9impl Repr {
10    /// Remove the common factors between numerator and denominator
11    pub fn reduce(self) -> Repr {
12        if self.numerator.is_zero() {
13            return Repr::zero();
14        }
15
16        let g = (&self.numerator).gcd(&self.denominator);
17        Repr {
18            numerator: self.numerator / &g,
19            denominator: self.denominator / g,
20        }
21    }
22
23    /// Remove the common factors with the hint, that is the factors are calculated
24    /// as `gcd(hint, gcd(numerator, denominator))`
25    pub fn reduce_with_hint(self, hint: UBig) -> Repr {
26        if self.numerator.is_zero() {
27            return Repr::zero();
28        }
29
30        let g = hint.gcd(&self.numerator).gcd(&self.denominator);
31        Repr {
32            numerator: self.numerator / &g,
33            denominator: self.denominator / g,
34        }
35    }
36
37    /// Remove only common factor of power of 2, which is cheap
38    pub fn reduce2(self) -> Repr {
39        if self.numerator.is_zero() {
40            return Repr::zero();
41        }
42
43        let n_zeros = self.numerator.trailing_zeros().unwrap_or_default();
44        let d_zeros = self.denominator.trailing_zeros().unwrap();
45        let zeros = n_zeros.min(d_zeros);
46
47        if zeros > 0 {
48            Repr {
49                numerator: self.numerator >> zeros,
50                denominator: self.denominator >> zeros,
51            }
52        } else {
53            self
54        }
55    }
56
57    #[inline]
58    pub const fn zero() -> Repr {
59        Repr {
60            numerator: IBig::ZERO,
61            denominator: UBig::ONE,
62        }
63    }
64    #[inline]
65    pub const fn one() -> Repr {
66        Repr {
67            numerator: IBig::ONE,
68            denominator: UBig::ONE,
69        }
70    }
71    #[inline]
72    pub const fn neg_one() -> Repr {
73        Repr {
74            numerator: IBig::NEG_ONE,
75            denominator: UBig::ONE,
76        }
77    }
78
79    /// This methods only check if the numerator and denominator have common factor 2.
80    /// It should be prevented to use this method to directly generate an RBig instance.
81    #[rustversion::since(1.64)]
82    pub const unsafe fn from_static_words(
83        sign: dashu_base::Sign,
84        numerator_words: &'static [dashu_int::Word],
85        denominator_words: &'static [dashu_int::Word],
86    ) -> Self {
87        let numerator = IBig::from_static_words(sign, numerator_words);
88        let denominator = UBig::from_static_words(denominator_words);
89
90        // check whether the numbers are reduced
91        let num_zeros = match numerator.trailing_zeros() {
92            Some(n) => n,
93            None => 0,
94        };
95        let den_zeros = match denominator.trailing_zeros() {
96            Some(n) => n,
97            None => 0,
98        };
99        assert!(!(num_zeros > 0 && den_zeros > 0));
100
101        Self {
102            numerator,
103            denominator,
104        }
105    }
106}
107
108// This custom implementation is necessary due to https://github.com/rust-lang/rust/issues/98374
109impl Clone for Repr {
110    #[inline]
111    fn clone(&self) -> Self {
112        Self {
113            numerator: self.numerator.clone(),
114            denominator: self.denominator.clone(),
115        }
116    }
117
118    #[inline]
119    fn clone_from(&mut self, source: &Self) {
120        self.numerator.clone_from(&source.numerator);
121        self.denominator.clone_from(&source.denominator);
122    }
123}
124
125impl EstimatedLog2 for Repr {
126    #[inline]
127    fn log2_est(&self) -> f32 {
128        self.numerator.log2_est() - self.denominator.log2_est()
129    }
130
131    fn log2_bounds(&self) -> (f32, f32) {
132        let (n_lb, n_ub) = self.numerator.log2_bounds();
133        let (d_lb, d_ub) = self.denominator.log2_bounds();
134        (n_lb - d_ub, n_ub - d_lb)
135    }
136}