dashu_int/third_party/
num_order.rs

1//! Implement num-order traits.
2
3use crate::{ibig::IBig, ubig::UBig};
4use core::{cmp::Ordering, hash::Hash};
5use dashu_base::{BitTest, FloatEncoding, Sign, Signed};
6use num_order::{NumHash, NumOrd};
7
8impl NumHash for UBig {
9    fn num_hash<H: core::hash::Hasher>(&self, state: &mut H) {
10        let m = self % (i128::MAX as u128);
11        (m as i128).hash(state)
12    }
13}
14impl NumHash for IBig {
15    fn num_hash<H: core::hash::Hasher>(&self, state: &mut H) {
16        (self % i128::MAX).hash(state)
17    }
18}
19
20impl NumOrd<UBig> for UBig {
21    #[inline]
22    fn num_cmp(&self, other: &UBig) -> Ordering {
23        self.cmp(other)
24    }
25    #[inline]
26    fn num_partial_cmp(&self, other: &UBig) -> Option<Ordering> {
27        self.partial_cmp(other)
28    }
29}
30
31impl NumOrd<IBig> for UBig {
32    #[inline]
33    fn num_cmp(&self, other: &IBig) -> Ordering {
34        let (rhs_sign, rhs_mag) = other.as_sign_repr();
35        match rhs_sign {
36            Sign::Positive => self.repr().cmp(&rhs_mag),
37            Sign::Negative => Ordering::Greater,
38        }
39    }
40    #[inline]
41    fn num_partial_cmp(&self, other: &IBig) -> Option<Ordering> {
42        Some(self.num_cmp(other))
43    }
44}
45
46impl NumOrd<UBig> for IBig {
47    #[inline]
48    fn num_cmp(&self, other: &UBig) -> Ordering {
49        let (lhs_sign, lhs_mag) = self.as_sign_repr();
50        match lhs_sign {
51            Sign::Positive => lhs_mag.cmp(&other.repr()),
52            Sign::Negative => Ordering::Less,
53        }
54    }
55    #[inline]
56    fn num_partial_cmp(&self, other: &UBig) -> Option<Ordering> {
57        Some(self.num_cmp(other))
58    }
59}
60
61impl NumOrd<IBig> for IBig {
62    #[inline]
63    fn num_cmp(&self, other: &IBig) -> Ordering {
64        self.cmp(other)
65    }
66    #[inline]
67    fn num_partial_cmp(&self, other: &IBig) -> Option<Ordering> {
68        self.partial_cmp(other)
69    }
70}
71
72macro_rules! impl_num_ord_ubig_with_unsigned {
73    ($($t:ty)*) => {$(
74        impl NumOrd<$t> for UBig {
75            #[inline]
76            fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
77                self.partial_cmp(&UBig::from_unsigned(*other))
78            }
79        }
80        impl NumOrd<UBig> for $t {
81            #[inline]
82            fn num_partial_cmp(&self, other: &UBig) -> Option<Ordering> {
83                UBig::from_unsigned(*self).partial_cmp(other)
84            }
85        }
86    )*};
87}
88impl_num_ord_ubig_with_unsigned!(u8 u16 u32 u64 u128 usize);
89
90macro_rules! impl_num_ord_ubig_with_signed {
91    ($($t:ty)*) => {$(
92        impl NumOrd<$t> for UBig {
93            #[inline]
94            fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
95                self.num_partial_cmp(&IBig::from_signed(*other))
96            }
97        }
98        impl NumOrd<UBig> for $t {
99            #[inline]
100            fn num_partial_cmp(&self, other: &UBig) -> Option<Ordering> {
101                IBig::from_signed(*self).num_partial_cmp(other)
102            }
103        }
104    )*};
105}
106impl_num_ord_ubig_with_signed!(i8 i16 i32 i64 i128 isize);
107
108macro_rules! impl_num_ord_ibig_with_unsigned {
109    ($($t:ty)*) => {$(
110        impl NumOrd<$t> for IBig {
111            #[inline]
112            fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
113                self.partial_cmp(&IBig::from_unsigned(*other))
114            }
115        }
116        impl NumOrd<IBig> for $t {
117            #[inline]
118            fn num_partial_cmp(&self, other: &IBig) -> Option<Ordering> {
119                IBig::from_unsigned(*self).partial_cmp(other)
120            }
121        }
122    )*};
123}
124impl_num_ord_ibig_with_unsigned!(u8 u16 u32 u64 u128 usize);
125
126macro_rules! impl_num_ord_ibig_with_signed {
127    ($($t:ty)*) => {$(
128        impl NumOrd<$t> for IBig {
129            #[inline]
130            fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
131                self.partial_cmp(&IBig::from_signed(*other))
132            }
133        }
134        impl NumOrd<IBig> for $t {
135            #[inline]
136            fn num_partial_cmp(&self, other: &IBig) -> Option<Ordering> {
137                IBig::from_signed(*self).partial_cmp(other)
138            }
139        }
140    )*};
141}
142impl_num_ord_ibig_with_signed!(i8 i16 i32 i64 i128 isize);
143
144macro_rules! impl_num_ord_ubig_with_float {
145    ($($t:ty)*) => {$(
146        impl NumOrd<$t> for UBig {
147            fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
148                // step0: compare with nan and 0
149                if other.is_nan() {
150                    return None;
151                } else if *other == 0. {
152                    return match self.is_zero() {
153                        true => Some(Ordering::Equal),
154                        false => Some(Ordering::Greater)
155                    };
156                }
157
158                // step1: compare sign
159                if other.sign() == Sign::Negative {
160                    return Some(Ordering::Greater);
161                }
162
163                // step2: compare with infinity
164                if other.is_infinite() {
165                    return Some(Ordering::Less);
166                }
167
168                // step3: test if the integer is bigger than the max float value
169                let self_bits = self.bit_len();
170                if self_bits > (<$t>::MANTISSA_DIGITS as usize + <$t>::MAX_EXP as usize) {
171                    return Some(Ordering::Greater);
172                }
173
174                // step4: decode the float and compare the bits
175                let (man, exp) = other.decode().unwrap();
176                let other_bits = man.bit_len() as isize + exp as isize;
177                if other_bits < 0 {
178                    return Some(Ordering::Greater);
179                } else if self_bits > other_bits as usize {
180                    return Some(Ordering::Greater);
181                } else if self_bits < other_bits as usize {
182                    return Some(Ordering::Less);
183                }
184
185                // step5: do the final comparison
186                if exp >= 0 {
187                    let shifted = UBig::from(man.unsigned_abs()) << exp as usize;
188                    self.partial_cmp(&shifted)
189                } else {
190                    (self << (-exp as usize)).partial_cmp(&UBig::from(man.unsigned_abs()))
191                }
192            }
193        }
194
195        impl NumOrd<UBig> for $t {
196            #[inline]
197            fn num_partial_cmp(&self, other: &UBig) -> Option<Ordering> {
198                other.num_partial_cmp(self).map(|ord| ord.reverse())
199            }
200        }
201    )*};
202}
203impl_num_ord_ubig_with_float!(f32 f64);
204
205macro_rules! impl_num_ord_ibig_with_float {
206    ($($t:ty)*) => {$(
207        impl NumOrd<$t> for IBig {
208            fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
209                // step0: compare with nan and 0
210                if other.is_nan() {
211                    return None;
212                } else if *other == 0. {
213                    return match self.is_zero() {
214                        true => Some(Ordering::Equal),
215                        false => Some(self.sign() * Ordering::Greater)
216                    };
217                }
218
219                // step1: compare sign
220                let sign = match (self.sign(), other.sign()) {
221                    (Sign::Positive, Sign::Positive) => Sign::Positive,
222                    (Sign::Positive, Sign::Negative) => return Some(Ordering::Greater),
223                    (Sign::Negative, Sign::Positive) => return Some(Ordering::Less),
224                    (Sign::Negative, Sign::Negative) => Sign::Negative,
225                };
226
227                // step2: compare with infinity and 0
228                if other.is_infinite() {
229                    return Some(-sign * Ordering::Less);
230                }
231
232                // step3: test if the integer is bigger than the max float value
233                let self_bits = self.bit_len();
234                if self_bits > (<$t>::MANTISSA_DIGITS as usize + <$t>::MAX_EXP as usize) {
235                    return Some(sign * Ordering::Greater);
236                }
237
238                // step4: decode the float and compare the bits
239                let (man, exp) = other.decode().unwrap();
240                let other_bits = man.bit_len() as isize + exp as isize; // i.e. log2(x) + 1
241                if other_bits < 0 {
242                    return Some(sign * Ordering::Greater);
243                } else if self_bits > other_bits as usize {
244                    return Some(sign * Ordering::Greater);
245                } else if self_bits < other_bits as usize {
246                    return Some(sign * Ordering::Less);
247                }
248
249                // step5: do the final comparison
250                if exp >= 0 {
251                    let shifted = IBig::from(man) << exp as usize;
252                    self.partial_cmp(&shifted)
253                } else {
254                    (self << (-exp as usize)).partial_cmp(&IBig::from(man))
255                }
256            }
257        }
258
259        impl NumOrd<IBig> for $t {
260            #[inline]
261            fn num_partial_cmp(&self, other: &IBig) -> Option<Ordering> {
262                other.num_partial_cmp(self).map(|ord| ord.reverse())
263            }
264        }
265    )*};
266}
267impl_num_ord_ibig_with_float!(f32 f64);