Skip to main content

relp_num/rational/small/
mod.rs

1//! # Rational types of fixed size
2use std::fmt;
3use std::fmt::Display;
4use std::ops::Neg;
5
6use crate::Negateable;
7use crate::non_zero::NonZeroSign;
8use crate::rational::Ratio;
9use crate::sign::Sign;
10
11mod io;
12pub(crate) mod ops;
13mod absorb;
14mod gcd;
15mod narrow;
16mod size;
17
18macro_rules! rational {
19    ($name:ident, $ity:ty, $uty:ty) => {
20        /// A signed ratio between two small integers.
21        pub type $name = Ratio<Sign, $uty, $uty>;
22
23        impl Neg for $name {
24            type Output = Self;
25
26            #[inline]
27            fn neg(mut self) -> Self::Output {
28                Negateable::negate(&mut self.sign);
29                self
30            }
31        }
32
33        impl Neg for &$name {
34            type Output = $name;
35
36            #[inline]
37            fn neg(self) -> Self::Output {
38                Self::Output {
39                    sign: !self.sign,
40                    numerator: self.numerator,
41                    denominator: self.denominator,
42                }
43            }
44        }
45
46        impl PartialEq for $name {
47            fn eq(&self, other: &Self) -> bool {
48                match (self.sign, other.sign) {
49                    (Sign::Positive, Sign::Negative) |
50                    (Sign::Negative, Sign::Positive) => false,
51                    (Sign::Zero, Sign::Zero) => true,
52                    (Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => {
53                        self.numerator == other.numerator && self.denominator == other.denominator
54                    }
55                    (Sign::Zero, Sign::Positive | Sign::Negative) |
56                    (Sign::Positive | Sign::Negative, Sign::Zero) => false,
57                }
58            }
59        }
60        impl Eq for $name {}
61
62        impl Display for $name {
63            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64                match self.sign {
65                    Sign::Positive => {}
66                    Sign::Zero => return write!(f, "0"),
67                    Sign::Negative => {
68                        write!(f, "-")?;
69                    }
70                }
71
72                write!(f, "{}", self.numerator)?;
73                if self.denominator != 1 {
74                    write!(f, "/")?;
75                    write!(f, "{}", self.denominator)?;
76                }
77
78                fmt::Result::Ok(())
79            }
80        }
81    }
82}
83rational!(Rational8, i8, u8);
84rational!(Rational16, i16, u16);
85rational!(Rational32, i32, u32);
86rational!(Rational64, i64, u64);
87rational!(RationalUsize, isize, usize);
88rational!(Rational128, i128, u128);
89
90macro_rules! rational_non_zero {
91    ($name:ident, $ity:ty, $uty:ty) => {
92        /// Non zero rational number.
93        pub type $name = Ratio<NonZeroSign, $uty, $uty>;
94
95        impl PartialEq for $name {
96            fn eq(&self, other: &Self) -> bool {
97                self.numerator == other.numerator &&
98                    self.denominator == other.denominator &&
99                    self.sign == other.sign
100            }
101        }
102        impl Eq for $name {}
103    }
104}
105rational_non_zero!(NonZeroRational8, i8, u8);
106rational_non_zero!(NonZeroRational16, i16, u16);
107rational_non_zero!(NonZeroRational32, i32, u32);
108rational_non_zero!(NonZeroRational64, i64, u64);
109rational_non_zero!(NonZeroRational128, i128, u128);
110rational_non_zero!(NonZeroRationalUsize, isize, usize);
111
112#[cfg(test)]
113mod test {
114    use std::cmp::Ordering;
115    use std::str::FromStr;
116
117    use num_traits::{FromPrimitive, One, Zero};
118
119    use crate::{NonZero, NonZeroSign, NonZeroSigned, Rational128};
120    use crate::{R16, R32, R64, R8};
121    use crate::rational::{Ratio, Rational16, Rational32, Rational64, Rational8, Sign};
122
123    #[test]
124    fn test_new() {
125        assert_eq!(R8!(0, 2), Ratio { sign: Sign::Zero, numerator: 0_u8, denominator: 1 });
126        assert_eq!(R8!(2, 2), Ratio { sign: Sign::Positive, numerator: 1, denominator: 1 });
127        assert_eq!(R8!(6, 2), Ratio { sign: Sign::Positive, numerator: 3, denominator: 1 });
128        assert_eq!(R8!(-6, 2), Ratio { sign: Sign::Negative, numerator: 3, denominator: 1 });
129    }
130
131    #[test]
132    fn test_from() {
133        assert_eq!(<Rational8 as From<_>>::from(1_u8), Rational8::one());
134        assert_eq!(<Rational32 as From<_>>::from(1), Rational32::one());
135
136        assert_eq!(FromPrimitive::from_u64(16), Some(R8!(16)));
137        assert_eq!(FromPrimitive::from_u16(0), Some(Rational8::zero()));
138        assert_eq!(<Rational16 as FromPrimitive>::from_u32(u32::MAX), None);
139        assert_eq!(FromPrimitive::from_i32(i32::MAX), Some(R32!(i32::MAX, 1)));
140        assert_eq!(<Rational64 as FromPrimitive>::from_i128(i128::MAX), None);
141        assert_eq!(FromPrimitive::from_i16(-1), Some(R8!(-2, 2)));
142
143        assert_eq!(<Rational128 as FromPrimitive>::from_f64(f64::NAN), None);
144        assert_eq!(<Rational64 as FromPrimitive>::from_f64(f64::INFINITY), None);
145        assert_eq!(<Rational32 as FromPrimitive>::from_f64(f64::NEG_INFINITY), None);
146        assert_eq!(FromPrimitive::from_f64(-0_f64), Some(Rational8::zero()));
147        // u128::MAX gets rounded upwards in f64 conversion
148        assert!(<Rational128 as FromPrimitive>::from_f64(u128::MAX as f64).is_none());
149
150        assert_eq!(<Rational32 as FromPrimitive>::from_i64(i64::MAX), None);
151
152        assert_eq!(Rational64::from((-1, 2)), R64!(-1, 2));
153    }
154
155    #[test]
156    fn test_nonzero() {
157        assert!(!Rational8::zero().is_not_zero());
158        assert_eq!(Rational16::zero().is_zero(), !Rational16::zero().is_not_zero());
159
160        assert_eq!(R8!(1).non_zero_signum(), NonZeroSign::Positive);
161    }
162
163    #[test]
164    #[should_panic]
165    fn test_sign_panic() {
166        R8!(0).non_zero_signum();
167    }
168
169    #[test]
170    fn test_one() {
171        assert!(Rational64::zero() < Rational64::one());
172        assert!(Rational64::one().is_one());
173        let mut x = Rational32::zero();
174        x.set_one();
175        assert_eq!(x, Rational32::one());
176    }
177
178    #[test]
179    fn test_cmp() {
180        assert!(R8!(12) < R8!(13));
181        assert!(R8!(1, 2) > R8!(1, 3));
182        assert_eq!(R8!(7, 11), R8!(7, 11));
183        assert!(R8!(3, 4) < R8!(5, 6));
184        assert!(R8!(13) > R8!(12));
185        assert_eq!(R32!(0), R32!(0));
186        assert_eq!(R32!(0).partial_cmp(&R32!(0)), Some(Ordering::Equal));
187        assert_eq!(R32!(12, 5).partial_cmp(&R32!(23, 10)), Some(Ordering::Greater));
188        assert!(!(R32!(7, 11) < R32!(7, 11)));
189        assert!(R64!(-3, 11) < R64!(3, 11));
190        assert_ne!(R64!(-9, 4), R64!(9, 4));
191
192        assert!(R8!(-3) < R8!(-2));
193        assert!(R8!(-3) < R8!(0));
194        assert!(R8!(-3) < R8!(2));
195        assert!(R8!(-3) < R8!(3));
196
197        assert!(Rational8::from_str("255/2").unwrap() < R8!(128));
198    }
199
200    #[test]
201    fn test_eq() {
202        assert_eq!(R8!(3), R8!(3));
203        assert_eq!(R8!(0), R8!(0));
204        assert_eq!(R8!(-1), R8!(-1));
205        assert_ne!(R8!(-1), R8!(0));
206        assert_ne!(R8!(-1), R8!(1));
207        assert_ne!(R8!(0), R8!(1));
208    }
209
210    #[test]
211    fn test_add() {
212        assert_eq!(Rational64::one() + Rational64::one(), R64!(2));
213        assert_eq!(R64!(3, 2) + R64!(3, 2), R64!(3));
214        assert_eq!(R64!(-3, 2) + R64!(3, 2), Rational64::zero());
215        assert_eq!(R64!(948, 64) + Rational64::zero(), R64!(948, 64));
216        assert_eq!(-Rational64::one() + Rational64::one(), Rational64::zero());
217        assert_eq!(Rational128::zero() + Rational128::one(), Rational128::one());
218        assert_eq!(Rational128::zero() + -Rational128::one(), -Rational128::one());
219
220        assert_eq!(&R32!(3, 9) + &R32!(-1, 3), R32!(0));
221        assert_eq!(R8!(4, 5) + R8!(1, 5), R8!(1));
222        assert_eq!(R8!(5, 1) + R8!(3, 2), R8!(13, 2));
223        assert_eq!(R8!(3, 4) + R8!(3), R8!(15, 4));
224        assert_eq!(R8!(3, 4) + R8!(17, 5), R8!(83, 20));
225        assert_eq!(R32!(3, 4) + R32!(3, 32), R32!(27, 32));
226        assert_eq!(R32!(-10) + R32!(9), R32!(-1));
227
228        let limit = 10;
229        for a in -limit..limit {
230            for b in 1..limit {
231                for c in -limit..limit {
232                    for d in 1..limit {
233                        assert_eq!(R32!(a, b as u32) + R32!(c, d as u32), R32!(a * d + c * b, b as u32 * d as u32), "{} / {} + {} / {}", a, b, c, d);
234                    }
235                }
236            }
237        }
238    }
239
240    #[test]
241    fn test_sum() {
242        assert_eq!((0..10).map(Rational32::from).sum::<Rational32>(), R32!(45));
243        assert_eq!((0_i16..10).map(|i| Rational16::new(i, 2).unwrap()).sum::<Rational16>(), R16!(45, 2));
244        let vs = vec![
245            (R32!(23, 31), R32!(-699, 65)),
246            (R32!(29, 31), R32!(-30736, 1885)),
247        ];
248        let product = vs.into_iter().map(|(a, b)| &a * &b).sum::<Rational32>();
249        let constant = R32!(-2826, 155);
250        assert_eq!(constant - product, R32!(5));
251    }
252
253    #[test]
254    fn test_sub() {
255        assert_eq!(Rational64::one() - Rational64::one(), R64!(0));
256        assert_eq!(R64!(3, 2) - R64!(3, 2), R64!(0));
257        assert_eq!(R64!(-3, 2) - R64!(3, 2), -R64!(3));
258        assert_eq!(R64!(948, 64) - Rational64::zero(), R64!(948, 64));
259        assert_eq!(-Rational64::one() - Rational64::one(), -R64!(2));
260        assert_eq!(Rational64::one() - Rational64::one(), -R64!(0));
261        assert_eq!(Rational128::zero() - Rational128::one(), -Rational128::one());
262        assert_eq!(Rational128::zero() - -Rational128::one(), Rational128::one());
263
264        assert_eq!(&R32!(3, 9) - &R32!(1, 3), R32!(0));
265        assert_eq!(R8!(4, 5) - R8!(1, 5), R8!(3, 5));
266        assert_eq!(R8!(5, 1) - R8!(3, 2), R8!(7, 2));
267        assert_eq!(R8!(3, 4) - R8!(3), R8!(-9, 4));
268        assert_eq!(R8!(3, 4) - R8!(17, 5), R8!(15 - 4 * 17, 20));
269        assert_eq!(R8!(17, 5) - R8!(3, 4), R8!(4 * 17 - 15, 20));
270
271        assert_eq!(R32!(3601, 155) - R32!(2826, 155), R32!(5));
272        assert_eq!(R32!(-2826, 155) - R32!(-3601, 155), R32!(5));
273
274        let limit = 10;
275        for a in -limit..limit {
276            for b in 1..limit {
277                for c in -limit..limit {
278                    for d in 1..limit {
279                        assert_eq!(R32!(a, b as u32) - R32!(c, d as u32), R32!(a * d - c * b, b as u32 * d as u32), "{} / {} - {} / {}", a, b, c, d);
280                    }
281                }
282            }
283        }
284    }
285
286    #[test]
287    fn test_mul() {
288        assert_eq!(Rational64::one() * Rational64::one(), R64!(1));
289        assert_eq!(R64!(3, 2) * R64!(3, 2), R64!(9, 4));
290        assert_eq!(R64!(-3, 2) * R64!(3, 2), -R64!(9, 4));
291        assert_eq!(R64!(948, 64) * Rational64::zero(), R64!(0));
292        assert_eq!(-Rational64::one() * Rational64::one(), -R64!(1));
293        assert_eq!(Rational64::one() * Rational64::one(), R64!(1));
294        assert_eq!(Rational128::zero() * Rational128::one(), -Rational128::zero());
295        assert_eq!(Rational128::zero() * -Rational128::one(), Rational128::zero());
296
297        assert_eq!(R8!(3, 2) * R8!(4, 9), R8!(2, 3));
298        assert_eq!(R32!(27, 32) * R32!(2), R32!(27, 16));
299
300        let limit = 10;
301        for a in -limit..limit {
302            for b in 1..limit {
303                for c in -limit..limit {
304                    for d in 1..limit {
305                        assert_eq!(R32!(a, b as u32) * R32!(c, d as u32), R32!(a * c, b as u32 * d as u32), "{} / {} * {} / {}", a, b, c, d);
306                    }
307                }
308            }
309        }
310    }
311
312    #[test]
313    fn test_div() {
314        assert_eq!(Rational64::one() / Rational64::one(), R64!(1));
315        assert_eq!(R64!(3, 2) / R64!(3, 2), R64!(1));
316        assert_eq!(R64!(-3, 2) / R64!(3, 2), -R64!(1));
317        assert_eq!(-Rational64::one() / Rational64::one(), -R64!(1));
318        assert_eq!(Rational128::zero() / Rational128::one(), -Rational128::zero());
319        assert_eq!(Rational128::zero() / -Rational128::one(), Rational128::zero());
320    }
321
322    #[test]
323    fn test_new_signed() {
324        assert_eq!(Rational64::new_signed(Sign::Positive, 6, 18), Some(R64!(1, 3)));
325        assert_eq!(Rational64::new_signed(Sign::Zero, 0, 6), Some(R64!(0)));
326        assert_eq!(Rational64::new_signed(Sign::Negative, 9, 18), Some(-R64!(1, 2)));
327    }
328
329    /// A sign that disagrees with the numerator, or a zero denominator, has no value to return.
330    ///
331    /// These used to be `debug_assert!`s, so in release they built a value that violated the
332    /// type's invariant instead.
333    #[test]
334    fn test_new_signed_invalid() {
335        assert_eq!(Rational64::new_signed(Sign::Zero, 1, 1), None);
336        assert_eq!(Rational64::new_signed(Sign::Positive, 0, 1), None);
337        assert_eq!(Rational64::new_signed(Sign::Negative, 0, 1), None);
338        assert_eq!(Rational64::new_signed(Sign::Positive, 1, 0), None);
339    }
340
341    #[test]
342    fn test_display() {
343        assert_eq!(Rational64::one().to_string(), "1");
344        assert_eq!(Rational64::zero().to_string(), "0");
345        assert_eq!(R64!(1, 2).to_string(), "1/2");
346        assert_eq!(R64!(-1, 2).to_string(), "-1/2");
347    }
348
349    #[test]
350    fn test_from_str() {
351        assert_eq!(Rational8::from_str("8"), Ok(R8!(8)));
352        assert_eq!(Rational8::from_str("-8"), Ok(-R8!(8)));
353        assert_eq!(Rational16::from_str("-16/3"), Ok(R16!(-16, 3)));
354        assert_eq!(Rational16::from_str("16/4"), Ok(R16!(16, 4)));
355        assert_eq!(Rational32::from_str("4294967297"), Err("value was too large for this type"));
356    }
357}