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