relp_num/rational/small/
io.rs

1use std::convert::TryInto;
2use std::fmt;
3use std::str::FromStr;
4
5use num_traits::{One, 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::gcd_scalar;
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) => {
18        {
19            let floor = $value.numerator / $value.denominator;
20            floor.try_into().ok()
21                .map(|value: $target| match $value.sign {
22                    Sign::Zero | Sign::Positive => value,
23                    Sign::Negative => -value,
24                })
25        }
26    }
27}
28
29macro_rules! unsigned_floor {
30    ($value:expr) => {
31        {
32            match $value.sign {
33                Sign::Zero | Sign::Positive => {
34                    let floor = $value.numerator / $value.denominator;
35                    floor.try_into().ok()
36                }
37                Sign::Negative => None,
38            }
39        }
40    }
41}
42
43macro_rules! float {
44    ($value:expr, $target:ty) => {
45        {
46            let ratio = $value.numerator as $target / $value.denominator as $target;
47            let signed_ratio = match $value.sign {
48                Sign::Zero | Sign::Positive => ratio,
49                Sign::Negative => -ratio,
50            };
51
52            Some(signed_ratio)
53        }
54    }
55}
56
57macro_rules! creation {
58    ($name:ident, $ity:ty, $uty:ty, $gcd_name:ident, $simplify_name:ident) => {
59        impl $name {
60            #[must_use]
61            pub fn new(numerator: $ity, mut denominator: $uty) -> Option<Self> {
62                if denominator.is_zero() {
63                    None
64                } else {
65                    Some({
66                        let mut numerator_abs = numerator.unsigned_abs();
67                        if numerator == 0 {
68                            <Self as num_traits::Zero>::zero()
69                        } else if numerator_abs == denominator {
70                            Self {
71                                sign: Signed::signum(&numerator),
72                                numerator: 1,
73                                denominator: 1,
74                            }
75                        } else {
76                            if numerator_abs != 1 && denominator != 1 {
77                                let gcd = gcd_scalar(numerator_abs as usize, denominator as usize) as $uty;
78
79                                numerator_abs /= gcd;
80                                denominator /= gcd;
81                            }
82
83                            Self {
84                                sign: Signed::signum(&numerator),
85                                numerator: numerator_abs,
86                                denominator,
87                            }
88                        }
89                    })
90                }
91            }
92            pub fn new_signed<T: Into<Sign>>(sign: T, numerator: $uty, denominator: $uty) -> Self {
93                debug_assert_ne!(denominator, 0);
94                let sign = sign.into();
95                debug_assert!((numerator == 0) == (sign == Sign::Zero));
96
97                match sign {
98                    Sign::Positive => debug_assert_ne!(numerator, 0),
99                    Sign::Zero => {
100                        debug_assert_eq!(numerator, 0);
101                        return <Self as num_traits::Zero>::zero();
102                    },
103                    Sign::Negative => {}
104                }
105
106                let (numerator, denominator) = $simplify_name(numerator, denominator);
107
108                Self {
109                    sign,
110                    numerator,
111                    denominator,
112                }
113            }
114        }
115
116        impl Default for $name {
117            fn default() -> Self {
118                Self::zero()
119            }
120        }
121
122        impl fmt::Debug for $name {
123            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124                match self.sign {
125                    Sign::Positive => {}
126                    Sign::Zero => return f.write_str("0"),
127                    Sign::Negative => f.write_str("-")?,
128                }
129
130                fmt::Debug::fmt(&self.numerator, f)?;
131                if !self.denominator.is_one() {
132                    f.write_str(" / ")?;
133                    fmt::Debug::fmt(&self.denominator, f)?;
134                }
135
136                Ok(())
137            }
138        }
139
140        impl num_traits::FromPrimitive for $name {
141            #[must_use]
142            #[inline]
143            fn from_i64(n: i64) -> Option<Self> {
144                if n.unsigned_abs() <= <$uty>::MAX as u64 {
145                    Some(Self {
146                        sign: Signed::signum(&n),
147                        numerator: n.unsigned_abs() as $uty,
148                        denominator: 1,
149                    })
150                } else {
151                    None
152                }
153            }
154
155            #[must_use]
156            #[inline]
157            fn from_u64(n: u64) -> Option<Self> {
158                if n <= <$uty>::MAX as u64 {
159                    Some(Self {
160                        sign: Signed::signum(&n),
161                        numerator: n as $uty,
162                        denominator: 1,
163                    })
164                } else {
165                    None
166                }
167            }
168
169            #[must_use]
170            #[inline]
171            fn from_f32(n: f32) -> Option<Self> {
172                Big::<8>::from_f32(n).map(Self::from_big_if_it_fits).flatten()
173            }
174
175            #[must_use]
176            #[inline]
177            fn from_f64(n: f64) -> Option<Self> {
178                Big::<16>::from_f64(n).map(Self::from_big_if_it_fits).flatten()
179            }
180        }
181
182        impl ToPrimitive for $name {
183            fn to_isize(&self) -> Option<isize> {
184                signed_floor!(self, isize)
185            }
186
187            fn to_i8(&self) -> Option<i8> {
188                signed_floor!(self, i8)
189            }
190
191            fn to_i16(&self) -> Option<i16> {
192                signed_floor!(self, i16)
193            }
194
195            fn to_i32(&self) -> Option<i32> {
196                signed_floor!(self, i32)
197            }
198
199            fn to_i64(&self) -> Option<i64> {
200                signed_floor!(self, i64)
201            }
202
203            fn to_i128(&self) -> Option<i128> {
204                signed_floor!(self, i128)
205            }
206
207            fn to_usize(&self) -> Option<usize> {
208                unsigned_floor!(self)
209            }
210
211            fn to_u8(&self) -> Option<u8> {
212                unsigned_floor!(self)
213            }
214
215            fn to_u16(&self) -> Option<u16> {
216                unsigned_floor!(self)
217            }
218
219            fn to_u32(&self) -> Option<u32> {
220                unsigned_floor!(self)
221            }
222
223            fn to_u64(&self) -> Option<u64> {
224                unsigned_floor!(self)
225            }
226
227            fn to_u128(&self) -> Option<u128> {
228                unsigned_floor!(self)
229            }
230
231            fn to_f32(&self) -> Option<f32> {
232                float!(self, f32)
233            }
234
235            fn to_f64(&self) -> Option<f64> {
236                float!(self, f64)
237            }
238        }
239
240        impl FromStr for $name {
241            type Err = &'static str;
242
243            fn from_str(from: &str) -> Result<Self, Self::Err> {
244                Big::<8>::from_str(from)
245                    .map(|big| match Self::from_big_if_it_fits(big) {
246                        Some(value) => Ok(value),
247                        None => Err("value was too large for this type"),
248                    })
249                    .flatten()
250            }
251        }
252
253        impl $name {
254            fn from_big_if_it_fits<const S: usize>(big: Big<S>) -> Option<Self> {
255                if num_traits::Zero::is_zero(&big) {
256                    return Some(<Self as num_traits::Zero>::zero());
257                }
258
259                if big.numerator.len() == 1 && big.denominator.len() == 1 {
260                    if big.numerator[0] <= <$uty>::MAX as usize && big.denominator[0] <= <$uty>::MAX as usize {
261                        return Some(Self {
262                            sign: big.sign,
263                            numerator: big.numerator[0] as $uty,
264                            denominator: big.denominator[0] as $uty,
265                        })
266                    }
267                }
268
269                None
270            }
271        }
272
273        impl From<&$name> for $name {
274            #[must_use]
275            #[inline]
276            fn from(other: &$name) -> Self {
277                *other
278            }
279        }
280
281        impl num_traits::Zero for $name {
282            #[must_use]
283            #[inline]
284            fn zero() -> Self {
285                Self {
286                    sign: Sign::Zero,
287                    numerator: 0,
288                    denominator: 1,
289                }
290            }
291
292            #[inline]
293            fn set_zero(&mut self) {
294                self.sign = Sign::Zero;
295                self.numerator = 0;
296                self.denominator = 1;
297            }
298
299            #[must_use]
300            #[inline]
301            fn is_zero(&self) -> bool {
302                self.sign == Sign::Zero
303            }
304        }
305    }
306}
307
308creation!(Rational8, i8, u8, gcd8, simplify8);
309creation!(Rational16, i16, u16, gcd16, simplify16);
310creation!(Rational32, i32, u32, gcd32, simplify32);
311creation!(Rational64, i64, u64, gcd64, simplify64);
312creation!(Rational128, i128, u128, gcd128, simplify128);
313creation!(RationalUsize, isize, usize, gcd_usize, simplify_usize);
314
315macro_rules! impl_one {
316    ($name:ident, $sign:ident) => {
317        impl num_traits::One for $name {
318            #[must_use]
319            #[inline]
320            fn one() -> Self {
321                Self {
322                    sign: $sign::Positive,
323                    numerator: 1,
324                    denominator: 1,
325                }
326            }
327
328            #[inline]
329            fn set_one(&mut self) {
330                self.sign = $sign::Positive;
331                self.numerator = 1;
332                self.denominator = 1;
333            }
334
335            #[must_use]
336            #[inline]
337            fn is_one(&self) -> bool {
338                self.numerator == 1 && self.denominator == 1 && self.sign == $sign::Positive
339            }
340        }
341    }
342}
343impl_one!(Rational8, Sign);
344impl_one!(Rational16, Sign);
345impl_one!(Rational32, Sign);
346impl_one!(Rational64, Sign);
347impl_one!(Rational128, Sign);
348impl_one!(RationalUsize, Sign);
349impl_one!(NonZeroRational8, NonZeroSign);
350impl_one!(NonZeroRational16, NonZeroSign);
351impl_one!(NonZeroRational32, NonZeroSign);
352impl_one!(NonZeroRational64, NonZeroSign);
353impl_one!(NonZeroRational128, NonZeroSign);
354impl_one!(NonZeroRationalUsize, NonZeroSign);
355
356macro_rules! size_dependent_unsigned {
357    ($name:ty, $uty:ty, $other:ty, $simplify:ident) => {
358        impl From<$other> for $name {
359            #[must_use]
360            #[inline]
361            fn from(other: $other) -> Self {
362                Self {
363                    sign: Signed::signum(&other),
364                    numerator: other as $uty,
365                    denominator: 1,
366                }
367            }
368        }
369        impl From<&$other> for $name {
370            #[must_use]
371            #[inline]
372            fn from(other: &$other) -> Self {
373                Self {
374                    sign: Signed::signum(other),
375                    numerator: *other as $uty,
376                    denominator: 1,
377                }
378            }
379        }
380        impl From<($other, $other)> for $name {
381            #[must_use]
382            #[inline]
383            fn from(other: ($other, $other)) -> Self {
384                assert_ne!(other.1, 0, "attempt to divide by zero");
385
386                let (numerator, denominator) = $simplify(other.0, other.1);
387
388                Self {
389                    sign: Signed::signum(&other.0) * Signed::signum(&other.1),
390                    numerator: numerator as $uty,
391                    denominator: denominator as $uty,
392                }
393            }
394        }
395    }
396}
397
398size_dependent_unsigned!(Rational8, u8, u8, simplify8);
399size_dependent_unsigned!(Rational16, u16, u8, simplify8);
400size_dependent_unsigned!(Rational16, u16, u16, simplify16);
401size_dependent_unsigned!(Rational32, u32, u8, simplify8);
402size_dependent_unsigned!(Rational32, u32, u16, simplify16);
403size_dependent_unsigned!(Rational32, u32, u32, simplify32);
404size_dependent_unsigned!(Rational64, u64, u8, simplify8);
405size_dependent_unsigned!(Rational64, u64, u16, simplify16);
406size_dependent_unsigned!(Rational64, u64, u32, simplify32);
407size_dependent_unsigned!(Rational64, u64, u64, simplify64);
408size_dependent_unsigned!(Rational128, u128, u8, simplify8);
409size_dependent_unsigned!(Rational128, u128, u16, simplify16);
410size_dependent_unsigned!(Rational128, u128, u32, simplify32);
411size_dependent_unsigned!(Rational128, u128, u64, simplify64);
412size_dependent_unsigned!(Rational128, u128, u128, simplify128);
413
414macro_rules! size_dependent_signed {
415    ($name:ty, $uty:ty, $other_signed:ty, $simplify:ident) => {
416        impl From<$other_signed> for $name {
417            #[must_use]
418            #[inline]
419            fn from(other: $other_signed) -> Self {
420                Self {
421                    sign: Signed::signum(&other),
422                    numerator: other.unsigned_abs() as $uty,
423                    denominator: 1,
424                }
425            }
426        }
427        impl From<&$other_signed> for $name {
428            #[must_use]
429            #[inline]
430            fn from(other: &$other_signed) -> Self {
431                Self {
432                    sign: Signed::signum(other),
433                    numerator: other.unsigned_abs() as $uty,
434                    denominator: 1,
435                }
436            }
437        }
438        impl From<($other_signed, $other_signed)> for $name {
439            #[must_use]
440            #[inline]
441            fn from(other: ($other_signed, $other_signed)) -> Self {
442                assert_ne!(other.1, 0, "attempt to divide by zero");
443
444                let (numerator, denominator) = $simplify(other.0.unsigned_abs(), other.1.unsigned_abs());
445
446                Self {
447                    sign: Signed::signum(&other.0) * Signed::signum(&other.1),
448                    numerator: numerator as $uty,
449                    denominator: denominator as $uty,
450                }
451            }
452        }
453    }
454}
455
456size_dependent_signed!(Rational8, u8, i8, simplify8);
457size_dependent_signed!(Rational16, u16, i8, simplify8);
458size_dependent_signed!(Rational16, u16, i16, simplify16);
459size_dependent_signed!(Rational32, u32, i8, simplify8);
460size_dependent_signed!(Rational32, u32, i16, simplify16);
461size_dependent_signed!(Rational32, u32, i32, simplify32);
462size_dependent_signed!(Rational64, u64, i8, simplify8);
463size_dependent_signed!(Rational64, u64, i16, simplify16);
464size_dependent_signed!(Rational64, u64, i32, simplify32);
465size_dependent_signed!(Rational64, u64, i64, simplify64);
466size_dependent_signed!(Rational128, u128, i8, simplify8);
467size_dependent_signed!(Rational128, u128, i16, simplify16);
468size_dependent_signed!(Rational128, u128, i32, simplify32);
469size_dependent_signed!(Rational128, u128, i64, simplify64);
470size_dependent_signed!(Rational128, u128, i128, simplify128);
471
472#[cfg(test)]
473mod test {
474    use num_traits::ToPrimitive;
475
476    use crate::{R16, R32, R64, R8, Rational16, Rational32, Rational8};
477
478    #[test]
479    fn test_debug() {
480        assert_eq!(format!("{:?}", R8!(2, 3)), "2 / 3");
481        assert_eq!(format!("{:?}", R8!(0)), "0");
482        assert_eq!(format!("{:?}", R8!(-1)), "-1");
483        assert_eq!(format!("{:?}", R8!(-0)), "0");
484        assert_eq!(format!("{:?}", -R8!(2, 3)), "-2 / 3");
485    }
486
487    #[test]
488    fn test_from() {
489        assert_eq!(Rational8::from(4_u8), R8!(4));
490        assert_eq!(Rational16::from(-4_i8), R16!(-4));
491        assert_eq!(Rational8::from(&4_u8), R8!(4));
492        assert_eq!(Rational16::from(&-4_i8), R16!(-4));
493        assert_eq!(Rational16::from((-4_i8, 2_i8)), R16!(-2));
494        assert_eq!(Rational16::from((4_u8, 2_u8)), R16!(2));
495    }
496
497    #[test]
498    fn test_to_primitive() {
499        assert_eq!(R8!(0).to_u8(), Some(0));
500        assert_eq!(R8!(1, 2).to_u8(), Some(0));
501        assert_eq!(R8!(3, 4).to_i8(), Some(0));
502        assert_eq!(R8!(3, 2).to_i8(), Some(1));
503        assert_eq!(R8!(-0).to_i8(), Some(0));
504        assert_eq!(R8!(-1, 2).to_i8(), Some(0));
505        assert_eq!(R8!(-3, 4).to_i8(), Some(0));
506        assert_eq!(R8!(-3, 2).to_i8(), Some(-1));
507
508        assert_eq!(Rational8::new(1, 1).unwrap().to_i32(), Some(1));
509        assert_eq!(R8!(-10).to_i32(), Some(-10));
510        assert_eq!(R8!(-11).to_u16(), None);
511        assert_eq!(R64!(2_u64.pow(63) + 2_u64.pow(20)).to_i64(), None);
512        assert_eq!(R8!(0).to_i64(), Some(0));
513        assert_eq!(R8!(0).to_u64(), Some(0));
514        assert_eq!(R8!(1, 2).to_u64(), Some(0));
515        assert_eq!(R8!(8).to_u64(), Some(8));
516
517        assert_eq!(R8!(0).to_f64(), Some(0_f64));
518        assert_eq!(R32!(-156, 99).to_f64(), Some(-156_f64 / 99_f64));
519        assert_eq!(R8!(3, 2).to_f64(), Some(1.5_f64));
520        assert_eq!(R8!(-0).to_f64(), Some(0_f64));
521        assert_eq!(R8!(-3, 2).to_f64(), Some(-1.5_f64));
522    }
523
524    #[test]
525    #[should_panic]
526    #[allow(unused_must_use)]
527    fn test_from_div_zero() {
528        Rational32::from((4, 0));
529    }
530}