Skip to main content

relp_num/rational/small/
io.rs

1use std::convert::TryInto;
2use std::fmt;
3use std::str::FromStr;
4
5use num_traits::Zero;
6use num_traits::ToPrimitive;
7
8use crate::non_zero::NonZeroSign;
9use crate::rational::big::Big;
10use crate::rational::small::{Rational128, Rational16, Rational32, Rational64, Rational8, RationalUsize};
11use crate::rational::small::{NonZeroRational128, NonZeroRational16, NonZeroRational32, NonZeroRational64, NonZeroRational8, NonZeroRationalUsize};
12use crate::rational::small::ops::building_blocks::{gcd128, gcd16, gcd32, gcd64, gcd8, gcd_usize};
13use crate::rational::small::ops::building_blocks::{simplify128, simplify16, simplify32, simplify64, simplify8, simplify_usize};
14use crate::sign::{Sign, Signed};
15
16macro_rules! signed_floor {
17    ($value:expr, $target:ty, $unsigned_target:ty) => {
18        {
19            let floor = $value.numerator / $value.denominator;
20            // The magnitude is converted to the *unsigned* target, because the most negative value
21            // of the target has a magnitude one larger than its largest positive value. Converting
22            // to the signed target and negating afterwards would reject exactly that value.
23            let magnitude: Option<$unsigned_target> = floor.try_into().ok();
24
25            magnitude.and_then(|magnitude| match $value.sign {
26                Sign::Zero | Sign::Positive => <$target>::try_from(magnitude).ok(),
27                Sign::Negative => {
28                    if magnitude <= <$target>::MIN.unsigned_abs() {
29                        // Negation in the unsigned domain, where the magnitude of the most
30                        // negative value is representable; the bit pattern is the same.
31                        Some(magnitude.wrapping_neg() as $target)
32                    } else {
33                        None
34                    }
35                }
36            })
37        }
38    }
39}
40
41macro_rules! unsigned_floor {
42    ($value:expr) => {
43        {
44            match $value.sign {
45                Sign::Zero | Sign::Positive => {
46                    let floor = $value.numerator / $value.denominator;
47                    floor.try_into().ok()
48                }
49                Sign::Negative => None,
50            }
51        }
52    }
53}
54
55macro_rules! float {
56    ($value:expr, $target:ty) => {
57        {
58            let ratio = $value.numerator as $target / $value.denominator as $target;
59            let signed_ratio = match $value.sign {
60                Sign::Zero | Sign::Positive => ratio,
61                Sign::Negative => -ratio,
62            };
63
64            Some(signed_ratio)
65        }
66    }
67}
68
69macro_rules! creation {
70    ($name:ident, $ity:ty, $uty:ty, $gcd_name:ident, $simplify_name:ident) => {
71        impl $name {
72            /// A ratio in lowest terms, or `None` when the denominator is zero.
73            ///
74            /// # Range
75            ///
76            /// The magnitude of this type is stored unsigned, while this constructor takes a
77            /// signed numerator of the same width. A `Rational8` therefore represents every value
78            /// from `-255` to `255`, but this constructor only reaches `-128` to `127`; the values
79            /// in between are reached by [`FromStr`](std::str::FromStr), by
80            /// [`FromPrimitive`](num_traits::FromPrimitive) from a wider primitive, and by
81            /// arithmetic. They are representable but not convertible back:
82            /// [`to_i8`](num_traits::ToPrimitive::to_i8) returns `None` for them, because an `i8`
83            /// is what doesn't fit, not the ratio.
84            #[must_use]
85            pub fn new(numerator: $ity, mut denominator: $uty) -> Option<Self> {
86                if denominator.is_zero() {
87                    None
88                } else {
89                    Some({
90                        let mut numerator_abs = numerator.unsigned_abs();
91                        if numerator == 0 {
92                            <Self as num_traits::Zero>::zero()
93                        } else if numerator_abs == denominator {
94                            Self {
95                                sign: Signed::signum(&numerator),
96                                numerator: 1,
97                                denominator: 1,
98                            }
99                        } else {
100                            if numerator_abs != 1 && denominator != 1 {
101                                // Note that this gcd is computed at the width of this type; casting
102                                // to `usize` first would discard the high bits of a `u128`.
103                                let gcd = $gcd_name(numerator_abs, denominator);
104
105                                numerator_abs /= gcd;
106                                denominator /= gcd;
107                            }
108
109                            Self {
110                                sign: Signed::signum(&numerator),
111                                numerator: numerator_abs,
112                                denominator,
113                            }
114                        }
115                    })
116                }
117            }
118            /// A ratio in lowest terms from a sign and a magnitude.
119            ///
120            /// # Return value
121            ///
122            /// `None` when the arguments describe no number: the denominator has to be nonzero,
123            /// and the sign has to be [`Sign::Zero`] exactly when the numerator is zero. These
124            /// invariants used to be checked with `debug_assert!` only, which let a release build
125            /// construct a ratio with a zero denominator; `Big::new_signed` returns an `Option`
126            /// for the same reason.
127            #[must_use]
128            pub fn new_signed<T: Into<Sign>>(sign: T, numerator: $uty, denominator: $uty) -> Option<Self> {
129                if denominator == 0 {
130                    return None;
131                }
132
133                match (sign.into(), numerator) {
134                    (Sign::Zero, 0) => Some(<Self as num_traits::Zero>::zero()),
135                    // `$simplify_name` doesn't terminate on a zero numerator.
136                    (sign @ (Sign::Positive | Sign::Negative), numerator) if numerator != 0 => {
137                        let (numerator, denominator) = $simplify_name(numerator, denominator);
138
139                        Some(Self {
140                            sign,
141                            numerator,
142                            denominator,
143                        })
144                    }
145                    _ => None,
146                }
147            }
148        }
149
150        impl Default for $name {
151            fn default() -> Self {
152                Self::zero()
153            }
154        }
155
156        /// Renders exactly like [`Display`](fmt::Display), so the output parses back.
157        impl fmt::Debug for $name {
158            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159                fmt::Display::fmt(self, f)
160            }
161        }
162
163        impl num_traits::FromPrimitive for $name {
164            #[inline]
165            fn from_i64(n: i64) -> Option<Self> {
166                if n.unsigned_abs() <= <$uty>::MAX as u64 {
167                    Some(Self {
168                        sign: Signed::signum(&n),
169                        numerator: n.unsigned_abs() as $uty,
170                        denominator: 1,
171                    })
172                } else {
173                    None
174                }
175            }
176
177            #[inline]
178            fn from_u64(n: u64) -> Option<Self> {
179                if n <= <$uty>::MAX as u64 {
180                    Some(Self {
181                        sign: Signed::signum(&n),
182                        numerator: n as $uty,
183                        denominator: 1,
184                    })
185                } else {
186                    None
187                }
188            }
189
190            /// The default implementation goes through [`from_i64`](num_traits::FromPrimitive::from_i64),
191            /// which discards every value that only a wider magnitude type can hold.
192            #[inline]
193            fn from_i128(n: i128) -> Option<Self> {
194                let numerator = n.unsigned_abs().try_into().ok()?;
195
196                Some(Self {
197                    sign: Signed::signum(&n),
198                    numerator,
199                    denominator: 1,
200                })
201            }
202
203            /// The default implementation goes through [`from_u64`](num_traits::FromPrimitive::from_u64),
204            /// which discards every value that only a wider magnitude type can hold.
205            #[inline]
206            fn from_u128(n: u128) -> Option<Self> {
207                let numerator = n.try_into().ok()?;
208
209                Some(Self {
210                    sign: Signed::signum(&n),
211                    numerator,
212                    denominator: 1,
213                })
214            }
215
216            #[inline]
217            fn from_f32(n: f32) -> Option<Self> {
218                Big::<8>::from_f32(n).map(Self::from_big_if_it_fits).flatten()
219            }
220
221            #[inline]
222            fn from_f64(n: f64) -> Option<Self> {
223                Big::<16>::from_f64(n).map(Self::from_big_if_it_fits).flatten()
224            }
225        }
226
227        impl ToPrimitive for $name {
228            fn to_isize(&self) -> Option<isize> {
229                signed_floor!(self, isize, usize)
230            }
231
232            fn to_i8(&self) -> Option<i8> {
233                signed_floor!(self, i8, u8)
234            }
235
236            fn to_i16(&self) -> Option<i16> {
237                signed_floor!(self, i16, u16)
238            }
239
240            fn to_i32(&self) -> Option<i32> {
241                signed_floor!(self, i32, u32)
242            }
243
244            fn to_i64(&self) -> Option<i64> {
245                signed_floor!(self, i64, u64)
246            }
247
248            fn to_i128(&self) -> Option<i128> {
249                signed_floor!(self, i128, u128)
250            }
251
252            fn to_usize(&self) -> Option<usize> {
253                unsigned_floor!(self)
254            }
255
256            fn to_u8(&self) -> Option<u8> {
257                unsigned_floor!(self)
258            }
259
260            fn to_u16(&self) -> Option<u16> {
261                unsigned_floor!(self)
262            }
263
264            fn to_u32(&self) -> Option<u32> {
265                unsigned_floor!(self)
266            }
267
268            fn to_u64(&self) -> Option<u64> {
269                unsigned_floor!(self)
270            }
271
272            fn to_u128(&self) -> Option<u128> {
273                unsigned_floor!(self)
274            }
275
276            fn to_f32(&self) -> Option<f32> {
277                float!(self, f32)
278            }
279
280            fn to_f64(&self) -> Option<f64> {
281                float!(self, f64)
282            }
283        }
284
285        impl FromStr for $name {
286            type Err = &'static str;
287
288            fn from_str(from: &str) -> Result<Self, Self::Err> {
289                Big::<8>::from_str(from)
290                    .map(|big| match Self::from_big_if_it_fits(big) {
291                        Some(value) => Ok(value),
292                        None => Err("value was too large for this type"),
293                    })
294                    .flatten()
295            }
296        }
297
298        impl $name {
299            fn from_big_if_it_fits<const S: usize>(big: Big<S>) -> Option<Self> {
300                if num_traits::Zero::is_zero(&big) {
301                    return Some(<Self as num_traits::Zero>::zero());
302                }
303
304                /// The number of `usize` words of a `Big` that this magnitude type can hold.
305                ///
306                /// At least one, also when a `usize` is not narrower than the magnitude type; a
307                /// single word that is too large is rejected by the conversion below instead.
308                const WORDS: usize = {
309                    let words = std::mem::size_of::<$uty>() / std::mem::size_of::<usize>();
310                    if words > 0 { words } else { 1 }
311                };
312
313                /// Reassemble the little endian `usize` words of a `Big` magnitude.
314                fn scalar(words: &[usize]) -> Option<$uty> {
315                    if words.len() > WORDS {
316                        // More words than the magnitude type has room for, whatever they contain.
317                        return None;
318                    }
319
320                    // `WORDS` words of a `usize` are never more than the 128 bits of the
321                    // accumulator, so no shift below reaches its width.
322                    let mut value = 0_u128;
323                    for (index, &word) in words.iter().enumerate() {
324                        value |= (word as u128) << (index as u32 * usize::BITS);
325                    }
326
327                    // A single word can still be too large, when the magnitude type is narrower
328                    // than a `usize`.
329                    value.try_into().ok()
330                }
331
332                Some(Self {
333                    sign: big.sign,
334                    numerator: scalar(&big.numerator)?,
335                    denominator: scalar(&big.denominator)?,
336                })
337            }
338        }
339
340        impl From<&$name> for $name {
341            #[inline]
342            fn from(other: &$name) -> Self {
343                *other
344            }
345        }
346
347        impl num_traits::Zero for $name {
348            #[inline]
349            fn zero() -> Self {
350                Self {
351                    sign: Sign::Zero,
352                    numerator: 0,
353                    denominator: 1,
354                }
355            }
356
357            #[inline]
358            fn set_zero(&mut self) {
359                self.sign = Sign::Zero;
360                self.numerator = 0;
361                self.denominator = 1;
362            }
363
364            #[inline]
365            fn is_zero(&self) -> bool {
366                self.sign == Sign::Zero
367            }
368        }
369    }
370}
371
372creation!(Rational8, i8, u8, gcd8, simplify8);
373creation!(Rational16, i16, u16, gcd16, simplify16);
374creation!(Rational32, i32, u32, gcd32, simplify32);
375creation!(Rational64, i64, u64, gcd64, simplify64);
376creation!(Rational128, i128, u128, gcd128, simplify128);
377creation!(RationalUsize, isize, usize, gcd_usize, simplify_usize);
378
379macro_rules! impl_one {
380    ($name:ident, $sign:ident) => {
381        impl num_traits::One for $name {
382            #[inline]
383            fn one() -> Self {
384                Self {
385                    sign: $sign::Positive,
386                    numerator: 1,
387                    denominator: 1,
388                }
389            }
390
391            #[inline]
392            fn set_one(&mut self) {
393                self.sign = $sign::Positive;
394                self.numerator = 1;
395                self.denominator = 1;
396            }
397
398            #[inline]
399            fn is_one(&self) -> bool {
400                self.numerator == 1 && self.denominator == 1 && self.sign == $sign::Positive
401            }
402        }
403    }
404}
405impl_one!(Rational8, Sign);
406impl_one!(Rational16, Sign);
407impl_one!(Rational32, Sign);
408impl_one!(Rational64, Sign);
409impl_one!(Rational128, Sign);
410impl_one!(RationalUsize, Sign);
411impl_one!(NonZeroRational8, NonZeroSign);
412impl_one!(NonZeroRational16, NonZeroSign);
413impl_one!(NonZeroRational32, NonZeroSign);
414impl_one!(NonZeroRational64, NonZeroSign);
415impl_one!(NonZeroRational128, NonZeroSign);
416impl_one!(NonZeroRationalUsize, NonZeroSign);
417
418macro_rules! size_dependent_unsigned {
419    ($name:ty, $uty:ty, $other:ty, $simplify:ident) => {
420        impl From<$other> for $name {
421            #[inline]
422            fn from(other: $other) -> Self {
423                Self {
424                    sign: Signed::signum(&other),
425                    numerator: other as $uty,
426                    denominator: 1,
427                }
428            }
429        }
430        impl From<&$other> for $name {
431            #[inline]
432            fn from(other: &$other) -> Self {
433                Self {
434                    sign: Signed::signum(other),
435                    numerator: *other as $uty,
436                    denominator: 1,
437                }
438            }
439        }
440        impl From<($other, $other)> for $name {
441            #[inline]
442            fn from(other: ($other, $other)) -> Self {
443                assert_ne!(other.1, 0, "attempt to divide by zero");
444
445                // `$simplify` doesn't terminate on a zero numerator.
446                if other.0 == 0 {
447                    return <Self as num_traits::Zero>::zero();
448                }
449
450                let (numerator, denominator) = $simplify(other.0, other.1);
451
452                Self {
453                    sign: Signed::signum(&other.0) * Signed::signum(&other.1),
454                    numerator: numerator as $uty,
455                    denominator: denominator as $uty,
456                }
457            }
458        }
459    }
460}
461
462size_dependent_unsigned!(Rational8, u8, u8, simplify8);
463size_dependent_unsigned!(Rational16, u16, u8, simplify8);
464size_dependent_unsigned!(Rational16, u16, u16, simplify16);
465size_dependent_unsigned!(Rational32, u32, u8, simplify8);
466size_dependent_unsigned!(Rational32, u32, u16, simplify16);
467size_dependent_unsigned!(Rational32, u32, u32, simplify32);
468size_dependent_unsigned!(Rational64, u64, u8, simplify8);
469size_dependent_unsigned!(Rational64, u64, u16, simplify16);
470size_dependent_unsigned!(Rational64, u64, u32, simplify32);
471size_dependent_unsigned!(Rational64, u64, u64, simplify64);
472size_dependent_unsigned!(Rational128, u128, u8, simplify8);
473size_dependent_unsigned!(Rational128, u128, u16, simplify16);
474size_dependent_unsigned!(Rational128, u128, u32, simplify32);
475size_dependent_unsigned!(Rational128, u128, u64, simplify64);
476size_dependent_unsigned!(Rational128, u128, u128, simplify128);
477
478macro_rules! size_dependent_signed {
479    ($name:ty, $uty:ty, $other_signed:ty, $simplify:ident) => {
480        impl From<$other_signed> for $name {
481            #[inline]
482            fn from(other: $other_signed) -> Self {
483                Self {
484                    sign: Signed::signum(&other),
485                    numerator: other.unsigned_abs() as $uty,
486                    denominator: 1,
487                }
488            }
489        }
490        impl From<&$other_signed> for $name {
491            #[inline]
492            fn from(other: &$other_signed) -> Self {
493                Self {
494                    sign: Signed::signum(other),
495                    numerator: other.unsigned_abs() as $uty,
496                    denominator: 1,
497                }
498            }
499        }
500        impl From<($other_signed, $other_signed)> for $name {
501            #[inline]
502            fn from(other: ($other_signed, $other_signed)) -> Self {
503                assert_ne!(other.1, 0, "attempt to divide by zero");
504
505                // `$simplify` doesn't terminate on a zero numerator.
506                if other.0 == 0 {
507                    return <Self as num_traits::Zero>::zero();
508                }
509
510                let (numerator, denominator) = $simplify(other.0.unsigned_abs(), other.1.unsigned_abs());
511
512                Self {
513                    sign: Signed::signum(&other.0) * Signed::signum(&other.1),
514                    numerator: numerator as $uty,
515                    denominator: denominator as $uty,
516                }
517            }
518        }
519    }
520}
521
522size_dependent_signed!(Rational8, u8, i8, simplify8);
523size_dependent_signed!(Rational16, u16, i8, simplify8);
524size_dependent_signed!(Rational16, u16, i16, simplify16);
525size_dependent_signed!(Rational32, u32, i8, simplify8);
526size_dependent_signed!(Rational32, u32, i16, simplify16);
527size_dependent_signed!(Rational32, u32, i32, simplify32);
528size_dependent_signed!(Rational64, u64, i8, simplify8);
529size_dependent_signed!(Rational64, u64, i16, simplify16);
530size_dependent_signed!(Rational64, u64, i32, simplify32);
531size_dependent_signed!(Rational64, u64, i64, simplify64);
532size_dependent_signed!(Rational128, u128, i8, simplify8);
533size_dependent_signed!(Rational128, u128, i16, simplify16);
534size_dependent_signed!(Rational128, u128, i32, simplify32);
535size_dependent_signed!(Rational128, u128, i64, simplify64);
536size_dependent_signed!(Rational128, u128, i128, simplify128);
537
538#[cfg(test)]
539mod test {
540    use std::str::FromStr;
541
542    use num_traits::{FromPrimitive, ToPrimitive};
543
544    use crate::{R16, R32, R64, R8, Rational128, Rational16, Rational32, Rational64, Rational8, RationalUsize};
545    use crate::rational::Ratio;
546    use crate::sign::Sign;
547
548    #[test]
549    fn test_debug() {
550        assert_eq!(format!("{:?}", R8!(2, 3)), "2/3");
551        assert_eq!(format!("{:?}", R8!(0)), "0");
552        assert_eq!(format!("{:?}", R8!(-1)), "-1");
553        assert_eq!(format!("{:?}", R8!(-0)), "0");
554        assert_eq!(format!("{:?}", -R8!(2, 3)), "-2/3");
555    }
556
557    /// `Debug` output has to parse back, which means rendering it exactly like `Display`.
558    #[test]
559    fn test_debug_round_trips() {
560        for value in [R8!(2, 3), R8!(0), R8!(-1), R8!(1), R8!(-2, 3), R8!(127, 2)] {
561            let rendered = format!("{value:?}");
562            assert_eq!(rendered, format!("{value}"));
563            assert_eq!(Rational8::from_str(&rendered), Ok(value), "{rendered}");
564        }
565    }
566
567    #[test]
568    fn test_from() {
569        assert_eq!(Rational8::from(4_u8), R8!(4));
570        assert_eq!(Rational16::from(-4_i8), R16!(-4));
571        assert_eq!(Rational8::from(&4_u8), R8!(4));
572        assert_eq!(Rational16::from(&-4_i8), R16!(-4));
573        assert_eq!(Rational16::from((-4_i8, 2_i8)), R16!(-2));
574        assert_eq!(Rational16::from((4_u8, 2_u8)), R16!(2));
575    }
576
577    #[test]
578    fn test_to_primitive() {
579        assert_eq!(R8!(0).to_u8(), Some(0));
580        assert_eq!(R8!(1, 2).to_u8(), Some(0));
581        assert_eq!(R8!(3, 4).to_i8(), Some(0));
582        assert_eq!(R8!(3, 2).to_i8(), Some(1));
583        assert_eq!(R8!(-0).to_i8(), Some(0));
584        assert_eq!(R8!(-1, 2).to_i8(), Some(0));
585        assert_eq!(R8!(-3, 4).to_i8(), Some(0));
586        assert_eq!(R8!(-3, 2).to_i8(), Some(-1));
587
588        assert_eq!(Rational8::new(1, 1).unwrap().to_i32(), Some(1));
589        assert_eq!(R8!(-10).to_i32(), Some(-10));
590        assert_eq!(R8!(-11).to_u16(), None);
591        assert_eq!(R64!(2_u64.pow(63) + 2_u64.pow(20)).to_i64(), None);
592        assert_eq!(R8!(0).to_i64(), Some(0));
593        assert_eq!(R8!(0).to_u64(), Some(0));
594        assert_eq!(R8!(1, 2).to_u64(), Some(0));
595        assert_eq!(R8!(8).to_u64(), Some(8));
596
597        assert_eq!(R8!(0).to_f64(), Some(0_f64));
598        assert_eq!(R32!(-156, 99).to_f64(), Some(-156_f64 / 99_f64));
599        assert_eq!(R8!(3, 2).to_f64(), Some(1.5_f64));
600        assert_eq!(R8!(-0).to_f64(), Some(0_f64));
601        assert_eq!(R8!(-3, 2).to_f64(), Some(-1.5_f64));
602    }
603
604    /// The invariants used to be checked with `debug_assert!`, so a release build accepted them.
605    #[test]
606    fn test_new_signed_invalid() {
607        // A zero denominator used to build a ratio that divides by zero.
608        assert_eq!(Rational8::new_signed(Sign::Positive, 1, 0), None);
609        assert_eq!(Rational8::new_signed(Sign::Zero, 0, 0), None);
610        assert_eq!(Rational128::new_signed(Sign::Negative, u128::MAX, 0), None);
611
612        // A sign that disagrees with the numerator.
613        assert_eq!(Rational64::new_signed(Sign::Zero, 1, 1), None);
614        assert_eq!(Rational64::new_signed(Sign::Positive, 0, 1), None);
615        assert_eq!(Rational64::new_signed(Sign::Negative, 0, 1), None);
616
617        // Valid arguments are unchanged.
618        assert_eq!(Rational64::new_signed(Sign::Positive, 6, 18), Rational64::new(1, 3));
619        assert_eq!(Rational64::new_signed(Sign::Zero, 0, 6), Some(R64!(0)));
620        assert_eq!(Rational64::new_signed(Sign::Negative, 9, 18), Some(-R64!(1, 2)));
621        assert_eq!(Rational128::new_signed(Sign::Negative, u128::MAX, 1).unwrap().numerator, u128::MAX);
622    }
623
624    /// The magnitude of the most negative value of a target doesn't fit its positive range.
625    #[test]
626    fn test_to_primitive_most_negative() {
627        assert_eq!(Rational8::from_str("-128").unwrap().to_i8(), Some(i8::MIN));
628        assert_eq!(Rational8::from_str("-129").unwrap().to_i8(), None);
629        assert_eq!(Rational8::from_str("128").unwrap().to_i8(), None);
630        assert_eq!(Rational8::from_str("-255/2").unwrap().to_i8(), Some(-127));
631        assert_eq!(Rational16::from_str("-257/2").unwrap().to_i8(), Some(i8::MIN));
632
633        assert_eq!(Rational16::from_str("-32768").unwrap().to_i16(), Some(i16::MIN));
634        assert_eq!(Rational16::from_str("-32769").unwrap().to_i16(), None);
635
636        assert_eq!(Rational64::from_i64(i64::MIN).unwrap().to_i64(), Some(i64::MIN));
637        assert_eq!(Rational64::from_i64(i64::MIN).unwrap().to_i32(), None);
638        assert_eq!(Rational128::from_i128(i128::MIN).unwrap().to_i128(), Some(i128::MIN));
639        assert_eq!(Rational128::from_i128(i128::MIN).unwrap().to_i64(), None);
640        assert_eq!(Rational128::from_i64(i64::MIN).unwrap().to_i64(), Some(i64::MIN));
641
642        // The most negative `isize`, whose magnitude a `RationalUsize` can hold.
643        let value: RationalUsize = Ratio {
644            sign: Sign::Negative,
645            numerator: 1 << (usize::BITS - 1),
646            denominator: 1,
647        };
648        assert_eq!(value.to_isize(), Some(isize::MIN));
649        let value: RationalUsize = Ratio { sign: Sign::Negative, numerator: usize::MAX, denominator: 1 };
650        assert_eq!(value.to_isize(), None);
651
652        // Truncation towards zero and the unsigned conversions are unchanged.
653        assert_eq!(R8!(-3, 2).to_i8(), Some(-1));
654        assert_eq!(R8!(-0).to_i8(), Some(0));
655        assert_eq!(R8!(-1).to_u8(), None);
656        assert_eq!(R8!(127).to_i8(), Some(127));
657    }
658
659    /// Values that need more than a single `usize` word of the `Big` they are parsed into.
660    #[test]
661    fn test_from_str_above_64_bits() {
662        let value = Rational128::from_str("18446744073709551616").unwrap();
663        assert_eq!(value.sign, Sign::Positive);
664        assert_eq!(value.numerator, 1 << 64);
665        assert_eq!(value.denominator, 1);
666
667        let value = Rational128::from_str(&i128::MAX.to_string()).unwrap();
668        assert_eq!(value.numerator, i128::MAX as u128);
669        assert_eq!(value.denominator, 1);
670
671        let value = Rational128::from_str(&u128::MAX.to_string()).unwrap();
672        assert_eq!(value.numerator, u128::MAX);
673        assert_eq!(value.denominator, 1);
674
675        let value = Rational128::from_str(&format!("-{}", u128::MAX)).unwrap();
676        assert_eq!(value.sign, Sign::Negative);
677        assert_eq!(value.numerator, u128::MAX);
678
679        // A denominator of more than one word.
680        let value = Rational128::from_str("1/18446744073709551616").unwrap();
681        assert_eq!(value.numerator, 1);
682        assert_eq!(value.denominator, 1 << 64);
683
684        // One word too many is still rejected, at every width; this is `2 ^ 128`.
685        assert_eq!(
686            Rational128::from_str("340282366920938463463374607431768211456"),
687            Err("value was too large for this type"),
688        );
689        assert_eq!(Rational64::from_str("18446744073709551616"), Err("value was too large for this type"));
690        assert_eq!(Rational32::from_str("4294967296"), Err("value was too large for this type"));
691        assert_eq!(Rational8::from_str("256"), Err("value was too large for this type"));
692        assert_eq!(Rational8::from_str("1/256"), Err("value was too large for this type"));
693        assert_eq!(RationalUsize::from_str(&usize::MAX.to_string()).unwrap().numerator, usize::MAX);
694        assert_eq!(
695            RationalUsize::from_str(&(usize::MAX as u128 + 1).to_string()),
696            Err("value was too large for this type"),
697        );
698    }
699
700    /// Floats larger than a `u64`, which are parsed into a multi word `Big` as well.
701    #[test]
702    fn test_from_f64_above_64_bits() {
703        // `1e30` is exactly representable in an `f64`, as this many.
704        let value = Rational128::from_f64(1e30).unwrap();
705        assert_eq!(value.numerator, 1_000_000_000_000_000_019_884_624_838_656);
706        assert_eq!(value.denominator, 1);
707
708        let value = Rational128::from_f64(-(2_f64.powi(100))).unwrap();
709        assert_eq!(value.sign, Sign::Negative);
710        assert_eq!(value.numerator, 1 << 100);
711        assert_eq!(value.denominator, 1);
712
713        let value = Rational128::from_f32(2_f32.powi(80)).unwrap();
714        assert_eq!(value.numerator, 1 << 80);
715
716        // `2 ^ 128` needs one word more than a `u128` has.
717        assert_eq!(Rational128::from_f64(2_f64.powi(128)), None);
718        assert_eq!(Rational128::from_f64(u128::MAX as f64), None);
719        assert_eq!(Rational64::from_f64(1e30), None);
720    }
721
722    /// The default implementations of these two route through the 64 bit ones.
723    #[test]
724    fn test_from_128_bit_primitive() {
725        assert_eq!(Rational128::from_i128(i128::MAX).unwrap().numerator, i128::MAX as u128);
726        assert_eq!(Rational128::from_i128(i128::MIN).unwrap().numerator, 1 << 127);
727        assert_eq!(Rational128::from_i128(i128::MIN).unwrap().sign, Sign::Negative);
728        assert_eq!(Rational128::from_u128(u128::MAX).unwrap().numerator, u128::MAX);
729        assert_eq!(Rational128::from_i128(0).unwrap(), <Rational128 as num_traits::Zero>::zero());
730
731        assert_eq!(RationalUsize::from_u128(usize::MAX as u128).unwrap().numerator, usize::MAX);
732        assert_eq!(RationalUsize::from_u128(usize::MAX as u128 + 1), None);
733
734        assert_eq!(Rational64::from_i128(i128::MAX), None);
735        assert_eq!(Rational64::from_u128(u128::MAX), None);
736        assert_eq!(Rational64::from_i128(i64::MIN as i128).unwrap().numerator, 1 << 63);
737        assert_eq!(Rational8::from_i128(-128).unwrap().numerator, 128);
738        assert_eq!(Rational8::from_i128(-256), None);
739    }
740
741    #[test]
742    #[should_panic]
743    #[allow(unused_must_use)]
744    fn test_from_div_zero() {
745        Rational32::from((4, 0));
746    }
747
748    #[test]
749    fn test_from_tuple_zero_numerator() {
750        // A zero numerator used to be handed to `simplify`, which doesn't terminate on it.
751        macro_rules! assert_canonical_zero {
752            ($value:expr) => {{
753                let value = $value;
754                assert_eq!(value.sign, Sign::Zero);
755                assert_eq!(value.numerator, 0);
756                assert_eq!(value.denominator, 1);
757            }}
758        }
759
760        // Signed tuples
761        assert_canonical_zero!(Rational8::from((0_i8, 5_i8)));
762        assert_canonical_zero!(Rational32::from((0_i32, 5_i32)));
763        assert_canonical_zero!(Rational32::from((0_i16, -3_i16)));
764        assert_canonical_zero!(Rational128::from((0_i128, -7_i128)));
765
766        // Unsigned tuples
767        assert_canonical_zero!(Rational8::from((0_u8, 5_u8)));
768        assert_canonical_zero!(Rational32::from((0_u32, 5_u32)));
769        assert_canonical_zero!(Rational32::from((0_u16, 3_u16)));
770        assert_canonical_zero!(Rational128::from((0_u128, 7_u128)));
771
772        assert_eq!(Rational32::from((0_i32, 5_i32)), R32!(0));
773        assert_eq!(Rational32::from((0_u32, 5_u32)), R32!(0));
774    }
775
776    #[test]
777    fn test_new_128_bit_gcd() {
778        // The gcd used to be computed after casting both arguments to `usize`, discarding the
779        // high 64 bits on a 64-bit platform.
780        let value = Rational128::new(3 << 64, 2_u128 << 64).unwrap();
781        assert_eq!(value.sign, Sign::Positive);
782        assert_eq!(value.numerator, 3);
783        assert_eq!(value.denominator, 2);
784
785        let value = Rational128::new(6, 1_u128 << 64).unwrap();
786        assert_eq!(value.numerator, 3);
787        assert_eq!(value.denominator, 1_u128 << 63);
788
789        // Numerator and denominator both larger than 64 bits, result in lowest terms.
790        let value = Rational128::new(6_i128 << 70, 4_u128 << 70).unwrap();
791        assert_eq!(value.numerator, 3);
792        assert_eq!(value.denominator, 2);
793
794        // The low 64 bits of these are 3 and 2, coprime, while the actual gcd is 2 ^ 64 + 1.
795        let large_factor = (1_i128 << 64) + 1;
796        let value = Rational128::new(3 * large_factor, 2 * large_factor as u128).unwrap();
797        assert_eq!(value.sign, Sign::Positive);
798        assert_eq!(value.numerator, 3);
799        assert_eq!(value.denominator, 2);
800
801        let value = Rational128::new(-(5 << 100), 15_u128 << 100).unwrap();
802        assert_eq!(value.sign, Sign::Negative);
803        assert_eq!(value.numerator, 1);
804        assert_eq!(value.denominator, 3);
805    }
806}