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}