dashu_ratio/
rbig.rs

1use dashu_base::{EstimatedLog2, Sign};
2use dashu_int::{DoubleWord, IBig, UBig};
3
4use crate::{error::panic_divide_by_0, repr::Repr};
5
6/// An arbitrary precision rational number.
7///
8/// This struct represents an rational number with arbitrarily large numerator and denominator
9/// based on [UBig] and [IBig].
10#[derive(PartialOrd, Ord)]
11#[repr(transparent)]
12pub struct RBig(pub(crate) Repr);
13
14/// An arbitrary precision rational number without strict reduction.
15///
16/// This struct is almost the same as [RBig], except for that the numerator and the
17/// denominator are allowed to have common divisors **other than a power of 2**. This allows
18/// faster computation because [Gcd][dashu_base::Gcd] is not required for each operation.
19///
20/// Since the representation is not canonicalized, [Hash] is not implemented for [Relaxed].
21/// Please use [RBig] if you want to store the rational number in a hash set, or use `num_order::NumHash`.
22///
23/// # Conversion from/to [RBig]
24///
25/// To convert from [RBig], use [RBig::relax()]. To convert to [RBig], use [Relaxed::canonicalize()].
26#[derive(PartialEq, Eq, PartialOrd, Ord)]
27#[repr(transparent)]
28pub struct Relaxed(pub(crate) Repr); // the result is not always normalized
29
30impl RBig {
31    /// [RBig] with value 0
32    pub const ZERO: Self = Self(Repr::zero());
33    /// [RBig] with value 1
34    pub const ONE: Self = Self(Repr::one());
35    /// [RBig] with value -1
36    pub const NEG_ONE: Self = Self(Repr::neg_one());
37
38    /// Create a rational number from a signed numerator and an unsigned denominator
39    ///
40    /// # Examples
41    ///
42    /// ```
43    /// # use dashu_int::{IBig, UBig};
44    /// # use dashu_ratio::RBig;
45    /// assert_eq!(RBig::from_parts(IBig::ZERO, UBig::ONE), RBig::ZERO);
46    /// assert_eq!(RBig::from_parts(IBig::ONE, UBig::ONE), RBig::ONE);
47    /// assert_eq!(RBig::from_parts(IBig::NEG_ONE, UBig::ONE), RBig::NEG_ONE);
48    /// ```
49    #[inline]
50    pub fn from_parts(numerator: IBig, denominator: UBig) -> Self {
51        if denominator.is_zero() {
52            panic_divide_by_0()
53        }
54
55        Self(
56            Repr {
57                numerator,
58                denominator,
59            }
60            .reduce(),
61        )
62    }
63
64    /// Convert the rational number into (numerator, denumerator) parts.
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// # use dashu_int::{IBig, UBig};
70    /// # use dashu_ratio::RBig;
71    /// assert_eq!(RBig::ZERO.into_parts(), (IBig::ZERO, UBig::ONE));
72    /// assert_eq!(RBig::ONE.into_parts(), (IBig::ONE, UBig::ONE));
73    /// assert_eq!(RBig::NEG_ONE.into_parts(), (IBig::NEG_ONE, UBig::ONE));
74    /// ```
75    #[inline]
76    pub fn into_parts(self) -> (IBig, UBig) {
77        (self.0.numerator, self.0.denominator)
78    }
79
80    /// Create a rational number from a signed numerator and a signed denominator
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// # use dashu_int::{IBig, UBig};
86    /// # use dashu_ratio::RBig;
87    /// assert_eq!(RBig::from_parts_signed(1.into(), 1.into()), RBig::ONE);
88    /// assert_eq!(RBig::from_parts_signed(12.into(), (-12).into()), RBig::NEG_ONE);
89    /// ```
90    #[inline]
91    pub fn from_parts_signed(numerator: IBig, denominator: IBig) -> Self {
92        let (sign, mag) = denominator.into_parts();
93        Self::from_parts(numerator * sign, mag)
94    }
95
96    /// Create a rational number in a const context
97    ///
98    /// The magnitude of the numerator and the denominator is limited to
99    /// a [DoubleWord][dashu_int::DoubleWord].
100    ///
101    /// # Examples
102    ///
103    /// ```
104    /// # use dashu_int::Sign;
105    /// # use dashu_ratio::{RBig, Relaxed};
106    /// const ONE: RBig = RBig::from_parts_const(Sign::Positive, 1, 1);
107    /// assert_eq!(ONE, RBig::ONE);
108    /// const NEG_ONE: RBig = RBig::from_parts_const(Sign::Negative, 1, 1);
109    /// assert_eq!(NEG_ONE, RBig::NEG_ONE);
110    /// ```
111    #[inline]
112    pub const fn from_parts_const(
113        sign: Sign,
114        mut numerator: DoubleWord,
115        mut denominator: DoubleWord,
116    ) -> Self {
117        if denominator == 0 {
118            panic_divide_by_0()
119        } else if numerator == 0 {
120            return Self::ZERO;
121        }
122
123        if numerator > 1 && denominator > 1 {
124            // perform a naive but const gcd
125            let (mut y, mut r) = (denominator, numerator % denominator);
126            while r > 1 {
127                let new_r = y % r;
128                y = r;
129                r = new_r;
130            }
131            if r == 0 {
132                numerator /= y;
133                denominator /= y;
134            }
135        }
136
137        Self(Repr {
138            numerator: IBig::from_parts_const(sign, numerator),
139            denominator: UBig::from_dword(denominator),
140        })
141    }
142
143    /// Get the numerator of the rational number
144    ///
145    /// # Examples
146    ///
147    /// ```
148    /// # use dashu_int::IBig;
149    /// # use dashu_ratio::RBig;
150    /// assert_eq!(RBig::ZERO.numerator(), &IBig::ZERO);
151    /// assert_eq!(RBig::ONE.numerator(), &IBig::ONE);
152    /// ```
153    #[inline]
154    pub fn numerator(&self) -> &IBig {
155        &self.0.numerator
156    }
157
158    /// Get the denominator of the rational number
159    ///
160    /// # Examples
161    ///
162    /// ```
163    /// # use dashu_int::UBig;
164    /// # use dashu_ratio::RBig;
165    /// assert_eq!(RBig::ZERO.denominator(), &UBig::ONE);
166    /// assert_eq!(RBig::ONE.denominator(), &UBig::ONE);
167    /// ```
168    #[inline]
169    pub fn denominator(&self) -> &UBig {
170        &self.0.denominator
171    }
172
173    /// Convert this rational number into a [Relaxed] version
174    ///
175    /// # Examples
176    ///
177    /// ```
178    /// # use dashu_ratio::{RBig, Relaxed};
179    /// assert_eq!(RBig::ZERO.relax(), Relaxed::ZERO);
180    /// assert_eq!(RBig::ONE.relax(), Relaxed::ONE);
181    /// ```
182    #[inline]
183    pub fn relax(self) -> Relaxed {
184        Relaxed(self.0)
185    }
186
187    /// Regard the number as a [Relaxed] number and return a reference of [Relaxed] type.
188    ///
189    /// # Examples
190    ///
191    /// ```
192    /// # use dashu_ratio::{RBig, Relaxed};
193    /// assert_eq!(RBig::ONE.as_relaxed(), &Relaxed::ONE);
194    #[inline]
195    pub const fn as_relaxed(&self) -> &Relaxed {
196        // SAFETY: RBig and Relaxed are both transparent wrapper around the Repr type.
197        //         This conversion is only available for immutable references, so that
198        //         the rational number will be kept reduced.
199        unsafe { core::mem::transmute(self) }
200    }
201
202    /// Check whether the number is 0
203    ///
204    /// # Examples
205    ///
206    /// ```
207    /// # use dashu_ratio::RBig;
208    /// assert!(RBig::ZERO.is_zero());
209    /// assert!(!RBig::ONE.is_zero());
210    /// ```
211    #[inline]
212    pub const fn is_zero(&self) -> bool {
213        self.0.numerator.is_zero()
214    }
215
216    /// Check whether the number is 1
217    ///
218    /// # Examples
219    ///
220    /// ```
221    /// # use dashu_ratio::RBig;
222    /// assert!(!RBig::ZERO.is_one());
223    /// assert!(RBig::ONE.is_one());
224    /// ```
225    #[inline]
226    pub const fn is_one(&self) -> bool {
227        self.0.numerator.is_one() && self.0.denominator.is_one()
228    }
229
230    /// Determine if the number can be regarded as an integer.
231    ///
232    /// # Examples
233    ///
234    /// ```
235    /// # use dashu_ratio::RBig;
236    /// assert!(RBig::ZERO.is_int());
237    /// assert!(RBig::ONE.is_int());
238    /// ```
239    #[inline]
240    pub const fn is_int(&self) -> bool {
241        self.0.denominator.is_one()
242    }
243}
244
245// This custom implementation is necessary due to https://github.com/rust-lang/rust/issues/98374
246impl Clone for RBig {
247    #[inline]
248    fn clone(&self) -> RBig {
249        RBig(self.0.clone())
250    }
251    #[inline]
252    fn clone_from(&mut self, source: &RBig) {
253        self.0.clone_from(&source.0)
254    }
255}
256
257impl Default for RBig {
258    #[inline]
259    fn default() -> Self {
260        Self::ZERO
261    }
262}
263
264impl EstimatedLog2 for RBig {
265    #[inline]
266    fn log2_bounds(&self) -> (f32, f32) {
267        self.0.log2_bounds()
268    }
269    #[inline]
270    fn log2_est(&self) -> f32 {
271        self.0.log2_est()
272    }
273}
274
275impl Relaxed {
276    /// [Relaxed] with value 0
277    pub const ZERO: Self = Self(Repr::zero());
278    /// [Relaxed] with value 1
279    pub const ONE: Self = Self(Repr::one());
280    /// [Relaxed] with value -1
281    pub const NEG_ONE: Self = Self(Repr::neg_one());
282
283    /// Create a rational number from a signed numerator and a signed denominator
284    ///
285    /// See [RBig::from_parts] for details.
286    #[inline]
287    pub fn from_parts(numerator: IBig, denominator: UBig) -> Self {
288        if denominator.is_zero() {
289            panic_divide_by_0();
290        }
291
292        Self(
293            Repr {
294                numerator,
295                denominator,
296            }
297            .reduce2(),
298        )
299    }
300
301    /// Convert the rational number into (numerator, denumerator) parts.
302    ///
303    /// See [RBig::into_parts] for details.
304    #[inline]
305    pub fn into_parts(self) -> (IBig, UBig) {
306        (self.0.numerator, self.0.denominator)
307    }
308
309    /// Create a rational number from a signed numerator and a signed denominator
310    ///
311    /// See [RBig::from_parts_signed] for details.
312    #[inline]
313    pub fn from_parts_signed(numerator: IBig, denominator: IBig) -> Self {
314        let (sign, mag) = denominator.into_parts();
315        Self::from_parts(numerator * sign, mag)
316    }
317
318    /// Create a rational number in a const context
319    ///
320    /// See [RBig::from_parts_const] for details.
321    #[inline]
322    pub const fn from_parts_const(
323        sign: Sign,
324        numerator: DoubleWord,
325        denominator: DoubleWord,
326    ) -> Self {
327        if denominator == 0 {
328            panic_divide_by_0()
329        } else if numerator == 0 {
330            return Self::ZERO;
331        }
332
333        let n2 = numerator.trailing_zeros();
334        let d2 = denominator.trailing_zeros();
335        let zeros = if n2 <= d2 { n2 } else { d2 };
336        Self(Repr {
337            numerator: IBig::from_parts_const(sign, numerator >> zeros),
338            denominator: UBig::from_dword(denominator >> zeros),
339        })
340    }
341
342    /// Create an Relaxed instance from two static sequences of [Word][crate::Word]s representing the
343    /// numerator and denominator.
344    ///
345    /// This method is intended for static creation macros.
346    #[doc(hidden)]
347    #[rustversion::since(1.64)]
348    #[inline]
349    pub const unsafe fn from_static_words(
350        sign: dashu_base::Sign,
351        numerator_words: &'static [dashu_int::Word],
352        denominator_words: &'static [dashu_int::Word],
353    ) -> Self {
354        Self(Repr::from_static_words(sign, numerator_words, denominator_words))
355    }
356
357    /// Get the numerator of the rational number
358    ///
359    /// See [RBig::numerator] for details.
360    #[inline]
361    pub fn numerator(&self) -> &IBig {
362        &self.0.numerator
363    }
364
365    /// Get the denominator of the rational number
366    ///
367    /// See [RBig::denominator] for details.
368    #[inline]
369    pub fn denominator(&self) -> &UBig {
370        &self.0.denominator
371    }
372
373    /// Convert this rational number into an [RBig] version
374    ///
375    /// # Examples
376    ///
377    /// ```
378    /// # use dashu_int::IBig;
379    /// # use dashu_ratio::{RBig, Relaxed};
380    /// assert_eq!(Relaxed::ONE.canonicalize(), RBig::ONE);
381    ///
382    /// let r = Relaxed::from_parts(10.into(), 5u8.into());
383    /// assert_eq!(r.canonicalize().numerator(), &IBig::from(2));
384    /// ```
385    #[inline]
386    pub fn canonicalize(self) -> RBig {
387        RBig(self.0.reduce())
388    }
389
390    /// Check whether the number is 0
391    ///
392    /// See [RBig::is_zero] for details.
393    #[inline]
394    pub const fn is_zero(&self) -> bool {
395        self.0.numerator.is_zero()
396    }
397
398    /// Check whether the number is 1
399    ///
400    /// See [RBig::is_one] for details.
401    #[inline]
402    pub fn is_one(&self) -> bool {
403        self.0.denominator.as_ibig() == &self.0.numerator
404    }
405}
406
407// This custom implementation is necessary due to https://github.com/rust-lang/rust/issues/98374
408impl Clone for Relaxed {
409    #[inline]
410    fn clone(&self) -> Relaxed {
411        Relaxed(self.0.clone())
412    }
413    #[inline]
414    fn clone_from(&mut self, source: &Relaxed) {
415        self.0.clone_from(&source.0)
416    }
417}
418
419impl Default for Relaxed {
420    #[inline]
421    fn default() -> Self {
422        Self::ZERO
423    }
424}
425
426impl EstimatedLog2 for Relaxed {
427    #[inline]
428    fn log2_bounds(&self) -> (f32, f32) {
429        self.0.log2_bounds()
430    }
431    #[inline]
432    fn log2_est(&self) -> f32 {
433        self.0.log2_est()
434    }
435}