Skip to main content

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