Skip to main content

relp_num/rational/big/
properties.rs

1use std::cmp::Ordering;
2
3use num_traits::{One, Zero};
4
5use crate::{NonZero, NonZeroSign, Sign};
6use crate::integer::big::ops::non_zero::mul_non_zero;
7use crate::integer::big::ops::normalize::simplify_fraction_without_info;
8use crate::integer::big::properties::cmp;
9use crate::rational::big::{Big, NonZeroBig};
10
11impl<const S: usize> Big<S> {
12    #[allow(unused)]
13    pub(crate) unsafe fn is_well_formed(&self) -> bool {
14        // SAFETY: Both of these only read the last word of the value they are asked about; they are
15        // `unsafe` as a marker on the invariant they report on, and impose nothing on the caller.
16        if !unsafe { self.numerator.is_well_formed() } {
17            return false;
18        }
19        // SAFETY: As above.
20        if !unsafe { self.denominator.is_well_formed() } {
21            return false;
22        }
23
24        match self.sign {
25            Sign::Zero => {
26                self.numerator.is_zero() && self.denominator.is_one()
27            }
28            Sign::Positive | Sign::Negative => {
29                if self.numerator.is_zero() {
30                    return false;
31                }
32                if !self.denominator.is_not_zero() {
33                    return false;
34                }
35
36                let mut n_clone = self.numerator.clone();
37                let mut d_clone = self.denominator.clone();
38                // SAFETY: The two `inner_mut` calls hand out mutable access to the words of copies
39                // that are dropped at the end of this block, so nothing observes a broken invariant
40                // even though this is what those accessors are `unsafe` for. Both copies are well
41                // formed by the two checks at the top and non zero by the two just above, which is
42                // what the simplification requires.
43                unsafe {
44                    simplify_fraction_without_info(n_clone.inner_mut(), d_clone.inner_mut());
45                }
46
47                n_clone == self.numerator && d_clone == self.denominator
48            }
49        }
50    }
51}
52
53impl<const S: usize> PartialEq for Big<S> {
54    #[inline]
55    fn eq(&self, other: &Self) -> bool {
56        match (self.sign, other.sign) {
57            (Sign::Positive, Sign::Negative) |
58            (Sign::Negative, Sign::Positive) => false,
59            (Sign::Zero, Sign::Zero) => true,
60            (Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => {
61                self.numerator == other.numerator && self.denominator == other.denominator
62            }
63            (Sign::Zero, Sign::Positive | Sign::Negative) |
64            (Sign::Positive | Sign::Negative, Sign::Zero) => false,
65        }
66    }
67}
68impl<const S: usize> Eq for Big<S> {}
69
70impl<const S: usize> PartialEq for NonZeroBig<S> {
71    #[inline]
72    fn eq(&self, other: &Self) -> bool {
73        self.sign == other.sign && self.numerator == other.numerator && self.denominator == other.denominator
74    }
75}
76impl<const S: usize> Eq for NonZeroBig<S> {}
77
78macro_rules! rational {
79    ($name:ident, $sign:ident) => {
80        impl<const S: usize> PartialOrd for $name<S> {
81            #[inline]
82            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
83                Some(self.cmp(other))
84            }
85        }
86
87        impl<const S: usize> Ord for $name<S> {
88            #[inline]
89            #[allow(unreachable_patterns)]
90            fn cmp(&self, other: &Self) -> Ordering {
91                match self.sign.cmp(&other.sign) {
92                    Ordering::Less => return Ordering::Less,
93                    Ordering::Greater => return Ordering::Greater,
94                    // Both zero, or both nonzero with the same sign; compare the magnitudes.
95                    Ordering::Equal => {}
96                }
97
98                if !self.sign.is_not_zero() {
99                    // Equal signs and the sign is zero, so both values are zero.
100                    return Ordering::Equal;
101                }
102
103                // When one cross product needs at least two more limbs than the other, the
104                // comparison is decided without computing either product.
105                let left = self.numerator.len() + other.denominator.len();
106                let right = other.numerator.len() + self.denominator.len();
107                if left > right + 1 {
108                    return match self.sign {
109                        $sign::Positive => Ordering::Greater,
110                        $sign::Negative => Ordering::Less,
111                        _ => unreachable!("sign was checked to be nonzero"),
112                    };
113                }
114                if right > left + 1 {
115                    return match self.sign {
116                        $sign::Positive => Ordering::Less,
117                        $sign::Negative => Ordering::Greater,
118                        _ => unreachable!("sign was checked to be nonzero"),
119                    };
120                }
121
122                if self.numerator == other.numerator && self.denominator == other.denominator {
123                    return Ordering::Equal;
124                }
125
126                let (ad, bc) = unsafe {
127                    // SAFETY: All values are non zero
128                    (
129                        mul_non_zero::<S>(&self.numerator, &other.denominator),
130                        mul_non_zero::<S>(&other.numerator, &self.denominator),
131                    )
132                };
133
134                match (cmp(&ad, &bc), self.sign) {
135                    (Ordering::Less, $sign::Positive) | (Ordering::Greater, $sign::Negative) => Ordering::Less,
136                    (Ordering::Equal, _) => Ordering::Equal,
137                    (Ordering::Greater, $sign::Positive) | (Ordering::Less, $sign::Negative) => Ordering::Greater,
138                    _ => unreachable!("sign was checked to be nonzero"),
139                }
140            }
141        }
142    }
143}
144
145rational!(Big, Sign);
146rational!(NonZeroBig, NonZeroSign);