ebi/
fraction_exact.rs

1use anyhow::{Error, Result, anyhow};
2use fraction::{BigFraction, BigUint, GenericFraction, Sign};
3use num::{BigInt, One as NumOne, Zero as NumZero};
4use num_bigint::ToBigUint;
5use num_rational::Ratio;
6use std::{
7    borrow::Borrow,
8    cmp::Ordering,
9    f64,
10    hash::Hash,
11    iter::Sum,
12    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
13    str::FromStr,
14    sync::Arc,
15};
16
17use crate::{
18    exact::MaybeExact,
19    fraction::UInt,
20    traits::{One, Signed, Zero},
21};
22
23#[derive(Clone)]
24pub struct FractionExact(pub fraction::BigFraction);
25
26impl FractionExact {
27    pub fn sqrt_abs(&self, decimal_places: u32) -> FractionExact {
28        Self(self.0.sqrt_abs(decimal_places))
29    }
30
31    pub fn is_sign_negative(&self) -> bool {
32        self.0.is_sign_negative()
33    }
34
35    pub fn is_sign_positive(&self) -> bool {
36        self.0.is_sign_positive()
37    }
38
39    /// Returns true if the value is Infinity (does not matter positive or negative)
40    pub fn is_infinite(&self) -> bool {
41        self.0.is_infinite()
42    }
43
44    pub fn is_nan(&self) -> bool {
45        self.0.is_nan()
46    }
47
48    pub fn infinity() -> Self {
49        Self(BigFraction::infinity())
50    }
51
52    pub fn neg_infinity() -> Self {
53        Self(BigFraction::neg_infinity())
54    }
55
56    pub fn nan() -> Self {
57        Self(BigFraction::nan())
58    }
59
60    pub fn sign(&self) -> Option<Sign> {
61        self.0.sign()
62    }
63
64    /**
65     * 1/self
66     */
67    pub fn recip(&self) -> Self {
68        Self(self.0.recip())
69    }
70
71    pub fn one_minus(mut self) -> Self {
72        self.0 = self.0.neg();
73        self.0.add_assign(1.to_biguint().unwrap());
74        self
75    }
76
77    pub fn two() -> FractionExact {
78        Self(GenericFraction::Rational(
79            Sign::Plus,
80            Ratio::new_raw(UInt::from(2u32), UInt::from(1u32)),
81        ))
82    }
83}
84
85impl MaybeExact for FractionExact {
86    type Approximate = f64;
87    type Exact = fraction::BigFraction;
88
89    fn is_exact(&self) -> bool {
90        true
91    }
92
93    fn extract_approx(&self) -> Result<f64> {
94        Err(anyhow!("cannot extract a float from a fraction"))
95    }
96
97    /**
98     * This is a low-level function to extract an f64. Only use if you are sure that the fraction is exact.
99     * May not be available in all compilation modes.
100     */
101    fn extract_exact(&self) -> Result<&GenericFraction<BigUint>> {
102        Ok(&self.0)
103    }
104}
105
106impl One for FractionExact {
107    fn one() -> Self {
108        Self(GenericFraction::Rational(Sign::Plus, NumOne::one()))
109    }
110
111    fn is_one(&self) -> bool {
112        fraction::One::is_one(&self.0)
113    }
114}
115
116impl Zero for FractionExact {
117    fn zero() -> Self {
118        Self(GenericFraction::Rational(Sign::Plus, NumZero::zero()))
119    }
120
121    fn is_zero(&self) -> bool {
122        fraction::Zero::is_zero(&self.0)
123    }
124}
125
126impl Signed for FractionExact {
127    fn abs(&self) -> Self {
128        Self(self.0.abs())
129    }
130
131    fn is_positive(&self) -> bool {
132        !Zero::is_zero(&self.0) && fraction::Signed::is_positive(&self.0)
133    }
134
135    fn is_negative(&self) -> bool {
136        fraction::Signed::is_negative(&self.0)
137    }
138
139    fn is_not_negative(&self) -> bool {
140        !self.is_negative()
141    }
142
143    fn is_not_positive(&self) -> bool {
144        !self.is_positive()
145    }
146}
147
148impl FromStr for FractionExact {
149    type Err = Error;
150
151    fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> {
152        Ok(Self(BigFraction::from_str(s)?))
153    }
154}
155
156impl From<&FractionExact> for FractionExact {
157    fn from(value: &FractionExact) -> Self {
158        value.clone()
159    }
160}
161
162impl From<Arc<FractionExact>> for FractionExact {
163    fn from(value: Arc<FractionExact>) -> Self {
164        Self(value.0.clone())
165    }
166}
167
168impl From<&Arc<FractionExact>> for FractionExact {
169    fn from(value: &Arc<FractionExact>) -> Self {
170        match value.as_ref() {
171            FractionExact(f) => FractionExact(f.clone()),
172        }
173    }
174}
175
176impl TryFrom<BigUint> for FractionExact {
177    type Error = Error;
178
179    fn try_from(value: BigUint) -> std::prelude::v1::Result<Self, Self::Error> {
180        Ok(Self(GenericFraction::Rational(
181            Sign::Plus,
182            Ratio::new(value, UInt::from(1u32)),
183        )))
184    }
185}
186
187impl TryFrom<&BigUint> for FractionExact {
188    type Error = Error;
189
190    fn try_from(value: &BigUint) -> std::prelude::v1::Result<Self, Self::Error> {
191        Ok(Self(GenericFraction::Rational(
192            Sign::Plus,
193            Ratio::new(value.clone(), UInt::from(1u32)),
194        )))
195    }
196}
197
198impl TryFrom<BigInt> for FractionExact {
199    type Error = Error;
200
201    fn try_from(value: BigInt) -> std::prelude::v1::Result<Self, Self::Error> {
202        Ok(Self(GenericFraction::Rational(
203            if value.is_negative() {
204                Sign::Minus
205            } else {
206                Sign::Plus
207            },
208            Ratio::new(value.abs().to_biguint().unwrap(), UInt::from(1u32)),
209        )))
210    }
211}
212
213impl TryFrom<(BigUint, BigUint)> for FractionExact {
214    type Error = Error;
215
216    fn try_from(value: (BigUint, BigUint)) -> std::prelude::v1::Result<Self, Self::Error> {
217        Ok(Self(GenericFraction::Rational(
218            Sign::Plus,
219            Ratio::new(value.0, value.1),
220        )))
221    }
222}
223
224impl std::fmt::Display for FractionExact {
225    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226        std::fmt::Display::fmt(&self.0, f)
227    }
228}
229
230impl std::fmt::Debug for FractionExact {
231    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232        f.debug_tuple("Exact ").field(&self.0).finish()
233    }
234}
235
236impl Add<&FractionExact> for &FractionExact {
237    type Output = FractionExact;
238
239    fn add(self, rhs: &FractionExact) -> Self::Output {
240        match (self, rhs) {
241            (FractionExact(x), FractionExact(y)) => FractionExact(x.add(y)),
242        }
243    }
244}
245
246impl<T> AddAssign<T> for FractionExact
247where
248    T: Borrow<FractionExact>,
249{
250    fn add_assign(&mut self, rhs: T) {
251        let rhs = rhs.borrow();
252        match (self, rhs) {
253            (FractionExact(x), FractionExact(y)) => x.add_assign(y),
254        }
255    }
256}
257
258impl AddAssign<&Arc<FractionExact>> for FractionExact {
259    fn add_assign(&mut self, rhs: &Arc<FractionExact>) {
260        let rhs = rhs.borrow();
261        match (self, rhs) {
262            (FractionExact(x), FractionExact(y)) => x.add_assign(y),
263        }
264    }
265}
266
267impl Sub<&FractionExact> for &FractionExact {
268    type Output = FractionExact;
269
270    fn sub(self, rhs: &FractionExact) -> Self::Output {
271        match (self, rhs) {
272            (FractionExact(x), FractionExact(y)) => FractionExact(x.sub(y)),
273        }
274    }
275}
276
277impl<T> SubAssign<T> for FractionExact
278where
279    T: Borrow<FractionExact>,
280{
281    fn sub_assign(&mut self, rhs: T) {
282        let rhs = rhs.borrow();
283        match (self, rhs) {
284            (FractionExact(x), FractionExact(y)) => x.sub_assign(y),
285        }
286    }
287}
288
289impl Mul<&FractionExact> for &FractionExact {
290    type Output = FractionExact;
291
292    fn mul(self, rhs: &FractionExact) -> Self::Output {
293        match (self, rhs) {
294            (FractionExact(x), FractionExact(y)) => FractionExact(x.mul(y)),
295        }
296    }
297}
298
299impl<T> MulAssign<T> for FractionExact
300where
301    T: Borrow<FractionExact>,
302{
303    fn mul_assign(&mut self, rhs: T) {
304        let rhs = rhs.borrow();
305        match (self, rhs) {
306            (FractionExact(x), FractionExact(y)) => x.mul_assign(y),
307        }
308    }
309}
310
311impl Div<&FractionExact> for &FractionExact {
312    type Output = FractionExact;
313
314    fn div(self, rhs: &FractionExact) -> Self::Output {
315        match (self, rhs) {
316            (FractionExact(x), FractionExact(y)) => FractionExact(x.div(y)),
317        }
318    }
319}
320
321impl<T> DivAssign<T> for FractionExact
322where
323    T: Borrow<FractionExact>,
324{
325    fn div_assign(&mut self, rhs: T) {
326        let rhs = rhs.borrow();
327        match (self, rhs) {
328            (FractionExact(x), FractionExact(y)) => x.div_assign(y),
329        }
330    }
331}
332
333impl Neg for FractionExact {
334    type Output = FractionExact;
335
336    fn neg(self) -> Self::Output {
337        FractionExact(self.0.neg())
338    }
339}
340
341impl<'a> Neg for &'a FractionExact {
342    type Output = FractionExact;
343
344    fn neg(self) -> Self::Output {
345        match self {
346            FractionExact(f) => FractionExact(f.neg()),
347        }
348    }
349}
350
351impl PartialEq for FractionExact {
352    fn eq(&self, other: &Self) -> bool {
353        match (self, other) {
354            (FractionExact(x), FractionExact(y)) => x == y,
355        }
356    }
357}
358
359impl Eq for FractionExact {}
360
361impl PartialOrd for FractionExact {
362    /**
363     * Note that exact and approximate should not be compared.
364     */
365    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
366        match (self, other) {
367            (FractionExact(x), FractionExact(y)) => x.partial_cmp(y),
368        }
369    }
370}
371
372impl Ord for FractionExact {
373    fn cmp(&self, other: &Self) -> Ordering {
374        self.0.cmp(&other.0)
375    }
376}
377
378impl Hash for FractionExact {
379    /**
380     * For good reasons, Rust does not support hashing of doubles. However, we need it to store distributions in a hashmap.
381     * Approximate arithmetic is discouraged
382     */
383    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
384        match self {
385            FractionExact(f) => f.hash(state),
386        }
387    }
388}
389
390impl Sum for FractionExact {
391    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
392        iter.fold(Self::zero(), |sum, f| &sum + &f)
393    }
394}
395
396impl<'a> Sum<&'a FractionExact> for FractionExact {
397    fn sum<I: Iterator<Item = &'a FractionExact>>(iter: I) -> Self {
398        iter.fold(FractionExact::zero(), |sum, f| &sum + f)
399    }
400}
401
402//======================== primitive types ========================//
403
404macro_rules! from {
405    ($t:ident) => {
406        impl From<$t> for FractionExact {
407            fn from(value: $t) -> Self {
408                Self(GenericFraction::Rational(
409                    Sign::Plus,
410                    Ratio::new(value.to_biguint().unwrap(), UInt::from(1u32)),
411                ))
412            }
413        }
414    };
415}
416
417macro_rules! from_signed {
418    ($t:ident) => {
419        impl From<$t> for FractionExact {
420            fn from(value: $t) -> Self {
421                Self(GenericFraction::Rational(
422                    if value.is_negative() {
423                        Sign::Minus
424                    } else {
425                        Sign::Plus
426                    },
427                    Ratio::new(value.abs().to_biguint().unwrap(), UInt::from(1u32)),
428                ))
429            }
430        }
431    };
432}
433
434macro_rules! from_tuple_u_u {
435    ($t:ident,$tt:ident) => {
436        impl From<($t, $tt)> for FractionExact {
437            fn from(value: ($t, $tt)) -> Self {
438                FractionExact(GenericFraction::Rational(
439                    Sign::Plus,
440                    Ratio::new(UInt::from(value.0), UInt::from(value.1)),
441                ))
442            }
443        }
444    };
445}
446
447macro_rules! from_tuple_u_i {
448    ($t:ident,$tt:ident) => {
449        impl From<($t, $tt)> for FractionExact {
450            fn from(value: ($t, $tt)) -> Self {
451                let s1 = if value.1.is_negative() {
452                    Sign::Minus
453                } else {
454                    Sign::Plus
455                };
456                FractionExact(GenericFraction::Rational(
457                    s1,
458                    Ratio::new(UInt::from(value.0), UInt::from(value.1.abs() as u128)),
459                ))
460            }
461        }
462    };
463}
464
465macro_rules! from_tuple_i_u {
466    ($t:ident,$tt:ident) => {
467        impl From<($t, $tt)> for FractionExact {
468            fn from(value: ($t, $tt)) -> Self {
469                let s1 = if value.0.is_negative() {
470                    Sign::Minus
471                } else {
472                    Sign::Plus
473                };
474                Self(GenericFraction::Rational(
475                    s1,
476                    Ratio::new(UInt::from(value.0.abs() as u128), UInt::from(value.1)),
477                ))
478            }
479        }
480    };
481}
482
483macro_rules! from_tuple_i_i {
484    ($t:ident,$tt:ident) => {
485        impl From<($t, $tt)> for FractionExact {
486            fn from(value: ($t, $tt)) -> Self {
487                let s0 = if value.0.is_negative() {
488                    Sign::Minus
489                } else {
490                    Sign::Plus
491                };
492                let s1 = if value.1.is_negative() {
493                    Sign::Minus
494                } else {
495                    Sign::Plus
496                };
497                Self(GenericFraction::Rational(
498                    s0 * s1,
499                    Ratio::new(
500                        UInt::from(value.0.abs() as u128),
501                        UInt::from(value.1.abs() as u128),
502                    ),
503                ))
504            }
505        }
506    };
507}
508
509macro_rules! add {
510    ($t:ident) => {
511        impl<'a> Add<$t> for &'a FractionExact {
512            type Output = FractionExact;
513
514            fn add(self, rhs: $t) -> Self::Output {
515                let rhs = rhs.into();
516                match (self, rhs) {
517                    (FractionExact(x), FractionExact(y)) => FractionExact(x.add(y)),
518                }
519            }
520        }
521    };
522}
523
524macro_rules! add_assign {
525    ($t:ident) => {
526        impl AddAssign<$t> for FractionExact {
527            fn add_assign(&mut self, rhs: $t) {
528                let rhs = rhs.into();
529                match (self, rhs) {
530                    (FractionExact(x), FractionExact(y)) => x.add_assign(y),
531                }
532            }
533        }
534    };
535}
536
537macro_rules! sub {
538    ($t:ident) => {
539        impl<'a> Sub<$t> for &'a FractionExact {
540            type Output = FractionExact;
541
542            fn sub(self, rhs: $t) -> Self::Output {
543                let rhs = rhs.into();
544                match (self, rhs) {
545                    (FractionExact(x), FractionExact(y)) => FractionExact(x.sub(y)),
546                }
547            }
548        }
549    };
550}
551
552macro_rules! sub_assign {
553    ($t:ident) => {
554        impl SubAssign<$t> for FractionExact {
555            fn sub_assign(&mut self, rhs: $t) {
556                let rhs = rhs.into();
557                match (self, rhs) {
558                    (FractionExact(x), FractionExact(y)) => x.sub_assign(y),
559                }
560            }
561        }
562    };
563}
564
565macro_rules! mul {
566    ($t:ident) => {
567        impl<'a> Mul<$t> for &'a FractionExact {
568            type Output = FractionExact;
569
570            fn mul(self, rhs: $t) -> Self::Output {
571                let rhs = rhs.into();
572                match (self, rhs) {
573                    (FractionExact(x), FractionExact(y)) => FractionExact(x.mul(y)),
574                }
575            }
576        }
577    };
578}
579
580macro_rules! mul_assign {
581    ($t:ident) => {
582        impl MulAssign<$t> for FractionExact {
583            fn mul_assign(&mut self, rhs: $t) {
584                let rhs = rhs.into();
585                match (self, rhs) {
586                    (FractionExact(x), FractionExact(y)) => x.mul_assign(y),
587                }
588            }
589        }
590    };
591}
592
593macro_rules! div {
594    ($t:ident) => {
595        impl<'a> Div<$t> for &'a FractionExact {
596            type Output = FractionExact;
597
598            fn div(self, rhs: $t) -> Self::Output {
599                let rhs = rhs.into();
600                match (self, rhs) {
601                    (FractionExact(x), FractionExact(y)) => FractionExact(x.div(y)),
602                }
603            }
604        }
605    };
606}
607
608macro_rules! div_assign {
609    ($t:ident) => {
610        impl DivAssign<$t> for FractionExact {
611            fn div_assign(&mut self, rhs: $t) {
612                let rhs = rhs.into();
613                match (self, rhs) {
614                    (FractionExact(x), FractionExact(y)) => x.div_assign(y),
615                }
616            }
617        }
618    };
619}
620
621macro_rules! ttype_tuple {
622    ($t:ident) => {
623        from_tuple_u_u!($t, usize);
624        from_tuple_u_u!($t, u128);
625        from_tuple_u_u!($t, u64);
626        from_tuple_u_u!($t, u32);
627        from_tuple_u_u!($t, u16);
628        from_tuple_u_u!($t, u8);
629        from_tuple_u_i!($t, i128);
630        from_tuple_u_i!($t, i64);
631        from_tuple_u_i!($t, i32);
632        from_tuple_u_i!($t, i16);
633        from_tuple_u_i!($t, i8);
634    };
635}
636
637macro_rules! ttype_tuple_signed {
638    ($t:ident) => {
639        from_tuple_i_u!($t, usize);
640        from_tuple_i_u!($t, u128);
641        from_tuple_i_u!($t, u64);
642        from_tuple_i_u!($t, u32);
643        from_tuple_i_u!($t, u16);
644        from_tuple_i_u!($t, u8);
645        from_tuple_i_i!($t, i64);
646        from_tuple_i_i!($t, i32);
647        from_tuple_i_i!($t, i16);
648        from_tuple_i_i!($t, i8);
649    };
650}
651
652macro_rules! ttype {
653    ($t:ident) => {
654        from!($t);
655        ttype_tuple!($t);
656        add!($t);
657        add_assign!($t);
658        sub!($t);
659        sub_assign!($t);
660        mul!($t);
661        mul_assign!($t);
662        div!($t);
663        div_assign!($t);
664    };
665}
666
667macro_rules! ttype_signed {
668    ($t:ident) => {
669        from_signed!($t);
670        ttype_tuple_signed!($t);
671        add!($t);
672        add_assign!($t);
673        sub!($t);
674        sub_assign!($t);
675        mul!($t);
676        mul_assign!($t);
677        div!($t);
678        div_assign!($t);
679    };
680}
681
682ttype!(usize);
683ttype!(u128);
684ttype!(u64);
685ttype!(u32);
686ttype!(u16);
687ttype!(u8);
688ttype_signed!(i128);
689ttype_signed!(i64);
690ttype_signed!(i32);
691ttype_signed!(i16);
692ttype_signed!(i8);
693
694#[cfg(test)]
695mod tests {
696    use std::ops::Neg;
697
698    use crate::{fraction_exact::FractionExact, traits::{One, Signed, Zero}};
699
700    #[test]
701    fn fraction_neg() {
702        let one = FractionExact::one();
703        assert!(one.is_positive());
704        let one = one.neg();
705        assert!(one.is_negative());
706    }
707
708    #[test]
709    fn fraction_exact() {
710        let zero = FractionExact::one().one_minus();
711
712        assert!(zero.is_zero());
713    }
714}