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}