1use _num_modular::{FixedMersenneInt, ModularAbs, ModularInteger};
2use core::cmp::Ordering;
3use dashu_base::{BitTest, EstimatedLog2, FloatEncoding, Sign, Signed};
4use dashu_int::{IBig, UBig, Word};
5use num_order::{NumHash, NumOrd};
6
7use crate::{
8 cmp::{repr_cmp_ibig, repr_cmp_ubig},
9 fbig::FBig,
10 repr::Repr,
11 round::Round,
12 utils::shl_digits_in_place,
13};
14
15impl<const B1: Word, const B2: Word> NumOrd<Repr<B2>> for Repr<B1> {
16 fn num_cmp(&self, other: &Repr<B2>) -> Ordering {
17 match (self.is_infinite(), other.is_infinite()) {
19 (true, true) => return self.exponent.cmp(&other.exponent),
20 (false, true) => {
21 return match other.exponent >= 0 {
22 true => Ordering::Less,
23 false => Ordering::Greater,
24 }
25 }
26 (true, false) => {
27 return match self.exponent >= 0 {
28 true => Ordering::Greater,
29 false => Ordering::Less,
30 }
31 }
32 _ => {}
33 };
34
35 let sign = match (self.significand.sign(), other.significand.sign()) {
37 (Sign::Positive, Sign::Positive) => Sign::Positive,
38 (Sign::Positive, Sign::Negative) => return Ordering::Greater,
39 (Sign::Negative, Sign::Positive) => return Ordering::Less,
40 (Sign::Negative, Sign::Negative) => Sign::Negative,
41 };
42
43 let (self_lo, self_hi) = self.log2_bounds();
45 let (other_lo, other_hi) = other.log2_bounds();
46 if self_lo > other_hi {
47 return sign * Ordering::Greater;
48 }
49 if self_hi < other_lo {
50 return sign * Ordering::Less;
51 }
52
53 let (mut lhs, mut rhs) = (self.significand.clone(), other.significand.clone());
55 if self.exponent < 0 {
56 shl_digits_in_place::<B1>(&mut rhs, (-self.exponent) as usize);
57 } else {
58 shl_digits_in_place::<B1>(&mut lhs, self.exponent as usize);
59 }
60 if other.exponent < 0 {
61 shl_digits_in_place::<B2>(&mut lhs, (-other.exponent) as usize);
62 } else {
63 shl_digits_in_place::<B2>(&mut rhs, other.exponent as usize);
64 }
65 lhs.cmp(&rhs)
66 }
67 #[inline]
68 fn num_partial_cmp(&self, other: &Repr<B2>) -> Option<Ordering> {
69 Some(self.num_cmp(other))
70 }
71}
72
73impl<R1: Round, R2: Round, const B1: Word, const B2: Word> NumOrd<FBig<R2, B2>> for FBig<R1, B1> {
74 #[inline]
75 fn num_cmp(&self, other: &FBig<R2, B2>) -> Ordering {
76 self.repr.num_cmp(&other.repr)
77 }
78 #[inline]
79 fn num_partial_cmp(&self, other: &FBig<R2, B2>) -> Option<Ordering> {
80 self.repr.num_partial_cmp(&other.repr)
81 }
82}
83
84macro_rules! impl_num_ord_with_method {
85 ($T:ty, $method:ident) => {
86 impl<const B: Word> NumOrd<$T> for Repr<B> {
87 #[inline]
88 fn num_cmp(&self, other: &$T) -> Ordering {
89 $method::<B, false>(self, other)
90 }
91 #[inline]
92 fn num_partial_cmp(&self, other: &$T) -> Option<Ordering> {
93 Some($method::<B, false>(self, other))
94 }
95 }
96 impl<const B: Word> NumOrd<Repr<B>> for $T {
97 #[inline]
98 fn num_cmp(&self, other: &Repr<B>) -> Ordering {
99 $method::<B, false>(other, self).reverse()
100 }
101 #[inline]
102 fn num_partial_cmp(&self, other: &Repr<B>) -> Option<Ordering> {
103 Some($method::<B, false>(other, self).reverse())
104 }
105 }
106 };
107}
108impl_num_ord_with_method!(UBig, repr_cmp_ubig);
109impl_num_ord_with_method!(IBig, repr_cmp_ibig);
110
111macro_rules! forward_num_ord_to_repr {
112 ($t:ty) => {
113 impl<R: Round, const B: Word> NumOrd<$t> for FBig<R, B> {
114 #[inline]
115 fn num_cmp(&self, other: &$t) -> Ordering {
116 self.repr.num_cmp(other)
117 }
118 #[inline]
119 fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
120 self.repr.num_partial_cmp(other)
121 }
122 }
123
124 impl<R: Round, const B: Word> NumOrd<FBig<R, B>> for $t {
125 #[inline]
126 fn num_cmp(&self, other: &FBig<R, B>) -> Ordering {
127 self.num_cmp(&other.repr)
128 }
129 #[inline]
130 fn num_partial_cmp(&self, other: &FBig<R, B>) -> Option<Ordering> {
131 self.num_partial_cmp(&other.repr)
132 }
133 }
134 };
135}
136forward_num_ord_to_repr!(UBig);
137forward_num_ord_to_repr!(IBig);
138
139macro_rules! impl_num_ord_fbig_unsigned {
140 ($($t:ty)*) => {$(
141 impl<const B: Word> NumOrd<$t> for Repr<B> {
142 #[inline]
143 fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
144 Some(repr_cmp_ubig::<B, false>(self, &UBig::from(*other)))
145 }
146 }
147 impl<const B: Word> NumOrd<Repr<B>> for $t {
148 #[inline]
149 fn num_partial_cmp(&self, other: &Repr<B>) -> Option<Ordering> {
150 Some(repr_cmp_ubig::<B, false>(other, &UBig::from(*self)).reverse())
151 }
152 }
153 impl<R: Round, const B: Word> NumOrd<$t> for FBig<R, B> {
154 #[inline]
155 fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
156 Some(repr_cmp_ubig::<B, false>(&self.repr, &UBig::from(*other)))
157 }
158 }
159 impl<R: Round, const B: Word> NumOrd<FBig<R, B>> for $t {
160 #[inline]
161 fn num_partial_cmp(&self, other: &FBig<R, B>) -> Option<Ordering> {
162 Some(repr_cmp_ubig::<B, false>(&other.repr, &UBig::from(*self)).reverse())
163 }
164 }
165 )*};
166}
167impl_num_ord_fbig_unsigned!(u8 u16 u32 u64 u128 usize);
168
169macro_rules! impl_num_ord_with_signed {
170 ($($t:ty)*) => {$(
171 impl<const B: Word> NumOrd<$t> for Repr<B> {
172 #[inline]
173 fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
174 Some(repr_cmp_ibig::<B, false>(self, &IBig::from(*other)))
175 }
176 }
177 impl<const B: Word> NumOrd<Repr<B>> for $t {
178 #[inline]
179 fn num_partial_cmp(&self, other: &Repr<B>) -> Option<Ordering> {
180 Some(repr_cmp_ibig::<B, false>(other, &IBig::from(*self)).reverse())
181 }
182 }
183 impl<R: Round, const B: Word> NumOrd<$t> for FBig<R, B> {
184 #[inline]
185 fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
186 Some(repr_cmp_ibig::<B, false>(&self.repr, &IBig::from(*other)))
187 }
188 }
189 impl<R: Round, const B: Word> NumOrd<FBig<R, B>> for $t {
190 #[inline]
191 fn num_partial_cmp(&self, other: &FBig<R, B>) -> Option<Ordering> {
192 Some(repr_cmp_ibig::<B, false>(&other.repr, &IBig::from(*self)).reverse())
193 }
194 }
195 )*};
196}
197impl_num_ord_with_signed!(i8 i16 i32 i64 i128 isize);
198
199macro_rules! impl_num_ord_with_float {
200 ($($t:ty)*) => {$(
201 impl<const B: Word> NumOrd<$t> for Repr<B> {
202 #[inline]
203 fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
204 if other.is_nan() {
206 return None;
207 } else if *other == 0. {
208 return match self.is_zero() {
209 true => Some(Ordering::Equal),
210 false => Some(self.sign() * Ordering::Greater)
211 };
212 }
213
214 let sign = match (self.sign(), other.sign()) {
216 (Sign::Positive, Sign::Positive) => Sign::Positive,
217 (Sign::Positive, Sign::Negative) => return Some(Ordering::Greater),
218 (Sign::Negative, Sign::Positive) => return Some(Ordering::Less),
219 (Sign::Negative, Sign::Negative) => Sign::Negative,
220 };
221
222 match (self.is_infinite(), other.is_infinite()) {
224 (true, true) => return Some(Ordering::Equal),
225 (false, true) => return Some(sign * Ordering::Less),
226 (true, false) => return Some(sign * Ordering::Greater),
227 _ => {}
228 };
229
230 let self_signif_log2 = self.significand.bit_len() as isize;
235 let self_log2 = self_signif_log2 + B.bit_len() as isize * self.exponent;
236 let (self_log2_lb, self_log2_ub) = if self.exponent >= 0 {
237 (self_log2 - self.exponent, self_log2)
238 } else {
239 (self_log2, self_log2 - self.exponent)
240 };
241 if self_log2_lb > (<$t>::MANTISSA_DIGITS as isize + <$t>::MAX_EXP as isize) {
242 return Some(sign * Ordering::Greater);
243 }
244
245 let (other_signif, other_exp) = other.decode().unwrap();
247 let other_log2 = other_signif.bit_len() as isize + other_exp as isize;
248 if self_log2_lb > other_log2 {
249 return Some(sign * Ordering::Greater);
250 } else if self_log2_ub < other_log2 {
251 return Some(sign * Ordering::Less);
252 }
253
254 let (mut lhs, mut rhs) = (self.significand.clone(), IBig::from(other_signif));
256 if self.exponent < 0 {
257 shl_digits_in_place::<B>(&mut rhs, (-self.exponent) as usize);
258 } else {
259 shl_digits_in_place::<B>(&mut lhs, self.exponent as usize);
260 }
261 if other_exp < 0 {
262 lhs <<= (-other_exp) as usize;
263 } else {
264 rhs <<= other_exp as usize;
265 }
266 Some(lhs.cmp(&rhs))
267 }
268 }
269
270 impl<const B: Word> NumOrd<Repr<B>> for $t {
271 #[inline]
272 fn num_partial_cmp(&self, other: &Repr<B>) -> Option<Ordering> {
273 other.num_partial_cmp(self).map(|ord| ord.reverse())
274 }
275 }
276 )*};
277}
278impl_num_ord_with_float!(f32 f64);
279forward_num_ord_to_repr!(f32);
280forward_num_ord_to_repr!(f64);
281
282impl<const B: Word> NumHash for Repr<B> {
283 fn num_hash<H: core::hash::Hasher>(&self, state: &mut H) {
284 type MInt = FixedMersenneInt<127, 1>;
286 const M127: i128 = i128::MAX;
287 const M127U: u128 = M127 as u128;
288
289 let signif_residue = &self.significand % M127;
290 let signif_hash = MInt::new(signif_residue.unsigned_abs(), &M127U);
291 let exp_hash = if B == 2 {
292 signif_hash.convert(1 << self.exponent.absm(&127))
293 } else if self.exponent < 0 {
294 signif_hash
296 .convert(B as u128)
297 .pow(&(-self.exponent as u128))
298 .inv()
299 .unwrap()
300 } else {
301 signif_hash.convert(B as u128).pow(&(self.exponent as u128))
302 };
303
304 let mut hash = (signif_hash * exp_hash).residue() as i128;
305 if signif_residue < 0 {
306 hash = -hash;
307 }
308
309 hash.num_hash(state)
310 }
311}
312
313impl<R: Round, const B: Word> NumHash for FBig<R, B> {
314 #[inline]
315 fn num_hash<H: core::hash::Hasher>(&self, state: &mut H) {
316 self.repr.num_hash(state)
317 }
318}