1use crate::{
2 cmp::{repr_cmp_ibig, repr_cmp_ubig},
3 rbig::RBig,
4 repr::Repr,
5 Relaxed,
6};
7use _num_modular::{FixedMersenneInt, ModularInteger};
8use core::cmp::Ordering;
9use dashu_base::{BitTest, FloatEncoding, Sign, Signed};
10use dashu_int::{IBig, UBig};
11use num_order::{NumHash, NumOrd};
12
13macro_rules! impl_ord_between_ratio {
14 ($t1:ty, $t2:ty) => {
15 impl NumOrd<$t2> for $t1 {
16 #[inline]
17 fn num_eq(&self, other: &$t2) -> bool {
18 self.0.eq(&other.0)
19 }
20 #[inline]
21 fn num_partial_cmp(&self, other: &$t2) -> Option<Ordering> {
22 Some(self.0.cmp(&other.0))
23 }
24 #[inline]
25 fn num_cmp(&self, other: &$t2) -> Ordering {
26 self.0.cmp(&other.0)
27 }
28 }
29 };
30}
31impl_ord_between_ratio!(RBig, Relaxed);
32impl_ord_between_ratio!(Relaxed, RBig);
33
34impl NumOrd<UBig> for Repr {
35 #[inline]
36 fn num_cmp(&self, other: &UBig) -> Ordering {
37 repr_cmp_ubig::<false>(self, other)
38 }
39 #[inline]
40 fn num_partial_cmp(&self, other: &UBig) -> Option<Ordering> {
41 Some(repr_cmp_ubig::<false>(self, other))
42 }
43}
44
45impl NumOrd<IBig> for Repr {
46 #[inline]
47 fn num_cmp(&self, other: &IBig) -> Ordering {
48 repr_cmp_ibig::<false>(self, other)
49 }
50 #[inline]
51 fn num_partial_cmp(&self, other: &IBig) -> Option<Ordering> {
52 Some(repr_cmp_ibig::<false>(self, other))
53 }
54}
55
56macro_rules! forward_num_ord_to_repr {
57 ($R:ty, $T:ty) => {
58 impl NumOrd<$T> for $R {
59 #[inline]
60 fn num_cmp(&self, other: &$T) -> Ordering {
61 self.0.num_cmp(other)
62 }
63 #[inline]
64 fn num_partial_cmp(&self, other: &$T) -> Option<Ordering> {
65 self.0.num_partial_cmp(other)
66 }
67 }
68 impl NumOrd<$R> for $T {
69 #[inline]
70 fn num_cmp(&self, other: &$R) -> Ordering {
71 other.0.num_cmp(self).reverse()
72 }
73 #[inline]
74 fn num_partial_cmp(&self, other: &$R) -> Option<Ordering> {
75 other.0.num_partial_cmp(self).map(|ord| ord.reverse())
76 }
77 }
78 };
79}
80forward_num_ord_to_repr!(RBig, UBig);
81forward_num_ord_to_repr!(Relaxed, UBig);
82forward_num_ord_to_repr!(RBig, IBig);
83forward_num_ord_to_repr!(Relaxed, IBig);
84
85impl NumHash for Repr {
86 fn num_hash<H: core::hash::Hasher>(&self, state: &mut H) {
87 type MInt = FixedMersenneInt<127, 1>;
89 const M127: i128 = i128::MAX;
90 const M127U: u128 = M127 as u128;
91 const HASH_INF: i128 = i128::MAX;
92 const HASH_NEGINF: i128 = i128::MIN + 1;
93
94 let ub = (&self.denominator) % M127U; let binv = if ub != 0 {
96 MInt::new(ub, &M127U).inv().unwrap()
97 } else {
98 return if self.numerator.is_positive() {
100 HASH_INF.num_hash(state)
101 } else {
102 HASH_NEGINF.num_hash(state)
103 };
104 };
105
106 let ua = (&self.numerator) % M127;
107 let ua = binv.convert(ua.unsigned_abs());
108 let ab = (ua * binv).residue() as i128;
109 (self.numerator.sign() * ab).num_hash(state)
110 }
111}
112
113impl NumHash for RBig {
114 #[inline]
115 fn num_hash<H: core::hash::Hasher>(&self, state: &mut H) {
116 self.0.num_hash(state)
117 }
118}
119
120impl NumHash for Relaxed {
121 #[inline]
122 fn num_hash<H: core::hash::Hasher>(&self, state: &mut H) {
123 self.0.num_hash(state)
124 }
125}
126
127#[cfg(feature = "dashu-float")]
128mod with_float {
129 use super::*;
130 use crate::cmp::with_float::repr_cmp_fbig;
131 use dashu_float::{round::Round, FBig, Repr as FloatRepr};
132 use dashu_int::Word;
133
134 impl<const B: Word> NumOrd<FloatRepr<B>> for Repr {
135 #[inline]
136 fn num_cmp(&self, other: &FloatRepr<B>) -> Ordering {
137 repr_cmp_fbig::<B, false>(self, other)
138 }
139 #[inline]
140 fn num_partial_cmp(&self, other: &FloatRepr<B>) -> Option<Ordering> {
141 Some(repr_cmp_fbig::<B, false>(self, other))
142 }
143 }
144
145 impl<R: Round, const B: Word> NumOrd<FBig<R, B>> for RBig {
146 #[inline]
147 fn num_cmp(&self, other: &FBig<R, B>) -> Ordering {
148 self.0.num_cmp(other.repr())
149 }
150 #[inline]
151 fn num_partial_cmp(&self, other: &FBig<R, B>) -> Option<Ordering> {
152 self.0.num_partial_cmp(other.repr())
153 }
154 }
155
156 impl<R: Round, const B: Word> NumOrd<FBig<R, B>> for Relaxed {
157 #[inline]
158 fn num_cmp(&self, other: &FBig<R, B>) -> Ordering {
159 self.0.num_cmp(other.repr())
160 }
161 #[inline]
162 fn num_partial_cmp(&self, other: &FBig<R, B>) -> Option<Ordering> {
163 self.0.num_partial_cmp(other.repr())
164 }
165 }
166
167 impl<R: Round, const B: Word> NumOrd<RBig> for FBig<R, B> {
168 #[inline]
169 fn num_cmp(&self, other: &RBig) -> Ordering {
170 other.0.num_cmp(self.repr()).reverse()
171 }
172 #[inline]
173 fn num_partial_cmp(&self, other: &RBig) -> Option<Ordering> {
174 Some(self.num_cmp(other))
175 }
176 }
177
178 impl<R: Round, const B: Word> NumOrd<Relaxed> for FBig<R, B> {
179 #[inline]
180 fn num_cmp(&self, other: &Relaxed) -> Ordering {
181 other.0.num_cmp(self.repr()).reverse()
182 }
183 #[inline]
184 fn num_partial_cmp(&self, other: &Relaxed) -> Option<Ordering> {
185 Some(self.num_cmp(other))
186 }
187 }
188}
189
190macro_rules! impl_num_ord_with_unsigned {
191 ($($t:ty)*) => {$(
192 impl NumOrd<$t> for Repr {
193 #[inline]
194 fn num_cmp(&self, other: &$t) -> Ordering {
195 repr_cmp_ubig::<false>(self, &UBig::from(*other))
196 }
197 #[inline]
198 fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
199 Some(repr_cmp_ubig::<false>(self, &UBig::from(*other)))
200 }
201 }
202 forward_num_ord_to_repr!(RBig, $t);
203 forward_num_ord_to_repr!(Relaxed, $t);
204 )*};
205}
206impl_num_ord_with_unsigned!(u8 u16 u32 u64 u128 usize);
207
208macro_rules! impl_num_ord_with_signed {
209 ($($t:ty)*) => {$(
210 impl NumOrd<$t> for Repr {
211 #[inline]
212 fn num_cmp(&self, other: &$t) -> Ordering {
213 repr_cmp_ibig::<false>(self, &IBig::from(*other))
214 }
215 #[inline]
216 fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
217 Some(repr_cmp_ibig::<false>(self, &IBig::from(*other)))
218 }
219 }
220 forward_num_ord_to_repr!(RBig, $t);
221 forward_num_ord_to_repr!(Relaxed, $t);
222 )*};
223}
224impl_num_ord_with_signed!(i8 i16 i32 i64 i128 isize);
225
226macro_rules! impl_num_ord_with_float {
227 ($($t:ty)*) => {$(
228 impl NumOrd<$t> for Repr {
229 fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
230 if other.is_nan() {
232 return None;
233 } else if other.is_infinite() {
234 return match other.sign() {
235 Sign::Positive => Some(Ordering::Less),
236 Sign::Negative => Some(Ordering::Greater),
237 };
238 } else if *other == 0. {
239 return match self.numerator.is_zero() {
240 true => Some(Ordering::Equal),
241 false => Some(self.numerator.sign() * Ordering::Greater)
242 };
243 }
244
245 let sign = match (self.numerator.sign(), other.sign()) {
247 (Sign::Positive, Sign::Positive) => Sign::Positive,
248 (Sign::Positive, Sign::Negative) => return Some(Ordering::Greater),
249 (Sign::Negative, Sign::Positive) => return Some(Ordering::Less),
250 (Sign::Negative, Sign::Negative) => Sign::Negative,
251 };
252
253 let self_log2 = self.numerator.bit_len() as isize - self.denominator.bit_len() as isize;
258 let (self_log2_lb, self_log2_ub) = (self_log2 - 1, self_log2 + 1);
259 if self_log2_lb > (<$t>::MANTISSA_DIGITS as isize + <$t>::MAX_EXP as isize) {
260 return Some(sign * Ordering::Greater);
261 }
262
263 let (other_man, other_exp) = other.decode().unwrap();
265 let other_log2 = other_man.bit_len() as isize + other_exp as isize - 1;
266 if self_log2_lb > other_log2 {
267 return Some(sign * Ordering::Greater);
268 } else if self_log2_ub < other_log2 {
269 return Some(sign * Ordering::Less);
270 }
271
272 let (other_man, other_exp) = other.decode().unwrap();
274 let (mut lhs, mut rhs) = (self.numerator.clone(), IBig::from(other_man) * &self.denominator);
275 if other_exp < 0 {
276 lhs <<= -other_exp as usize;
277 } else {
278 rhs <<= other_exp as usize;
279 }
280
281 Some(lhs.cmp(&rhs))
282 }
283 }
284 )*};
285}
286impl_num_ord_with_float!(f32 f64);
287forward_num_ord_to_repr!(RBig, f32);
288forward_num_ord_to_repr!(Relaxed, f32);
289forward_num_ord_to_repr!(RBig, f64);
290forward_num_ord_to_repr!(Relaxed, f64);