1use std::cmp::Ordering;
11
12#[cfg(feature = "big-decimal")]
13use bigdecimal::BigDecimal;
14#[cfg(any(feature = "big-integer", feature = "big-decimal"))]
15use num_bigint::BigInt;
16#[cfg(any(feature = "big-integer", feature = "big-decimal"))]
17use num_rational::BigRational;
18#[cfg(any(feature = "big-integer", feature = "big-decimal"))]
19use num_traits::Signed;
20#[cfg(any(feature = "big-integer", feature = "big-decimal"))]
21use num_traits::ToPrimitive;
22#[cfg(any(feature = "big-integer", feature = "big-decimal"))]
23use num_traits::Zero;
24
25use super::NumericComparisonPolicy;
26use super::internal::NumberRepr;
27#[cfg(feature = "big-integer")]
28use super::internal::compare_big_integer_float;
29use super::internal::compare_magnitude;
30#[cfg(feature = "big-decimal")]
31use super::internal::decimal_rational;
32#[cfg(any(feature = "big-integer", feature = "big-decimal"))]
33use super::internal::f32_rational;
34#[cfg(any(feature = "big-integer", feature = "big-decimal"))]
35use super::internal::f64_rational;
36use super::internal::finite_parts;
37
38#[cfg(feature = "big-integer")]
49#[must_use]
50#[inline]
51fn compare_big_integer_signed(left: &BigInt, right: i128) -> Ordering {
52 match left.to_i128() {
53 Some(left) => left.cmp(&right),
54 None if left.is_negative() => Ordering::Less,
55 None => Ordering::Greater,
56 }
57}
58
59#[cfg(feature = "big-integer")]
70#[must_use]
71#[inline]
72fn compare_big_integer_unsigned(left: &BigInt, right: u128) -> Ordering {
73 if left.is_negative() {
74 Ordering::Less
75 } else {
76 match left.to_u128() {
77 Some(left) => left.cmp(&right),
78 None => Ordering::Greater,
79 }
80 }
81}
82
83#[cfg(feature = "big-integer")]
95fn compare_big_integer_to_fixed(left: &BigInt, right: NumberRepr<'_>) -> Option<Ordering> {
96 match right {
97 NumberRepr::Int8(right) => Some(compare_big_integer_signed(left, i128::from(right))),
98 NumberRepr::Int16(right) => Some(compare_big_integer_signed(left, i128::from(right))),
99 NumberRepr::Int32(right) => Some(compare_big_integer_signed(left, i128::from(right))),
100 NumberRepr::Int64(right) => Some(compare_big_integer_signed(left, i128::from(right))),
101 NumberRepr::Int128(right) => Some(compare_big_integer_signed(left, right)),
102 NumberRepr::UInt8(right) => Some(compare_big_integer_unsigned(left, u128::from(right))),
103 NumberRepr::UInt16(right) => Some(compare_big_integer_unsigned(left, u128::from(right))),
104 NumberRepr::UInt32(right) => Some(compare_big_integer_unsigned(left, u128::from(right))),
105 NumberRepr::UInt64(right) => Some(compare_big_integer_unsigned(left, u128::from(right))),
106 NumberRepr::UInt128(right) => Some(compare_big_integer_unsigned(left, right)),
107 _ => None,
108 }
109}
110
111#[must_use]
148#[derive(Debug, Clone, Copy)]
149pub struct NumberRef<'a> {
150 inner: NumberRepr<'a>,
152}
153
154macro_rules! impl_from_copy {
156 ($source:ty, $variant:ident) => {
157 impl From<$source> for NumberRef<'_> {
158 #[inline]
168 fn from(value: $source) -> Self {
169 Self {
170 inner: NumberRepr::$variant(value),
171 }
172 }
173 }
174 };
175}
176
177#[cfg(any(feature = "big-integer", feature = "big-decimal"))]
179macro_rules! impl_from_ref {
180 ($source:ty, $variant:ident) => {
181 impl<'a> From<&'a $source> for NumberRef<'a> {
182 #[inline]
192 fn from(value: &'a $source) -> Self {
193 Self {
194 inner: NumberRepr::$variant(value),
195 }
196 }
197 }
198 };
199}
200
201impl_from_copy!(i8, Int8);
202impl_from_copy!(i16, Int16);
203impl_from_copy!(i32, Int32);
204impl_from_copy!(i64, Int64);
205impl_from_copy!(i128, Int128);
206impl_from_copy!(u8, UInt8);
207impl_from_copy!(u16, UInt16);
208impl_from_copy!(u32, UInt32);
209impl_from_copy!(u64, UInt64);
210impl_from_copy!(u128, UInt128);
211impl_from_copy!(f32, Float32);
212impl_from_copy!(f64, Float64);
213#[cfg(feature = "big-integer")]
214impl_from_ref!(BigInt, BigInteger);
215#[cfg(feature = "big-decimal")]
216impl_from_ref!(BigDecimal, BigDecimal);
217
218impl<'a> NumberRef<'a> {
219 #[inline]
225 #[must_use]
226 pub fn is_integer(self) -> bool {
227 matches!(
228 self.inner,
229 NumberRepr::Int8(_)
230 | NumberRepr::Int16(_)
231 | NumberRepr::Int32(_)
232 | NumberRepr::Int64(_)
233 | NumberRepr::Int128(_)
234 | NumberRepr::UInt8(_)
235 | NumberRepr::UInt16(_)
236 | NumberRepr::UInt32(_)
237 | NumberRepr::UInt64(_)
238 | NumberRepr::UInt128(_)
239 )
240 }
241
242 #[inline]
248 #[must_use]
249 pub fn is_float(self) -> bool {
250 matches!(self.inner, NumberRepr::Float32(_) | NumberRepr::Float64(_))
251 }
252
253 #[inline]
260 #[must_use]
261 pub fn is_big_integer(self) -> bool {
262 #[cfg(feature = "big-integer")]
263 {
264 matches!(self.inner, NumberRepr::BigInteger(_))
265 }
266 #[cfg(not(feature = "big-integer"))]
267 {
268 false
269 }
270 }
271
272 #[inline]
279 #[must_use]
280 pub fn is_big_decimal(self) -> bool {
281 #[cfg(feature = "big-decimal")]
282 {
283 matches!(self.inner, NumberRepr::BigDecimal(_))
284 }
285 #[cfg(not(feature = "big-decimal"))]
286 {
287 false
288 }
289 }
290
291 #[inline]
297 #[must_use]
298 pub fn is_nan(self) -> bool {
299 match self.inner {
300 NumberRepr::Float32(value) => value.is_nan(),
301 NumberRepr::Float64(value) => value.is_nan(),
302 _ => false,
303 }
304 }
305
306 #[inline]
312 #[must_use]
313 pub fn is_infinite(self) -> bool {
314 match self.inner {
315 NumberRepr::Float32(value) => value.is_infinite(),
316 NumberRepr::Float64(value) => value.is_infinite(),
317 _ => false,
318 }
319 }
320
321 #[inline]
328 #[must_use]
329 pub fn is_finite(self) -> bool {
330 !self.is_nan() && !self.is_infinite()
331 }
332
333 #[must_use]
340 pub fn is_zero(self) -> bool {
341 match self.inner {
342 NumberRepr::Int8(value) => value == 0,
343 NumberRepr::Int16(value) => value == 0,
344 NumberRepr::Int32(value) => value == 0,
345 NumberRepr::Int64(value) => value == 0,
346 NumberRepr::Int128(value) => value == 0,
347 NumberRepr::UInt8(value) => value == 0,
348 NumberRepr::UInt16(value) => value == 0,
349 NumberRepr::UInt32(value) => value == 0,
350 NumberRepr::UInt64(value) => value == 0,
351 NumberRepr::UInt128(value) => value == 0,
352 NumberRepr::Float32(value) => value == 0.0,
353 NumberRepr::Float64(value) => value == 0.0,
354 #[cfg(feature = "big-integer")]
355 NumberRepr::BigInteger(value) => value.is_zero(),
356 #[cfg(feature = "big-decimal")]
357 NumberRepr::BigDecimal(value) => value.is_zero(),
358 #[cfg(not(any(feature = "big-integer", feature = "big-decimal")))]
359 NumberRepr::Lifetime(_, impossible) => match impossible {},
360 }
361 }
362
363 #[must_use]
370 pub fn is_positive(self) -> bool {
371 match self.inner {
372 NumberRepr::Int8(value) => value > 0,
373 NumberRepr::Int16(value) => value > 0,
374 NumberRepr::Int32(value) => value > 0,
375 NumberRepr::Int64(value) => value > 0,
376 NumberRepr::Int128(value) => value > 0,
377 NumberRepr::UInt8(value) => value > 0,
378 NumberRepr::UInt16(value) => value > 0,
379 NumberRepr::UInt32(value) => value > 0,
380 NumberRepr::UInt64(value) => value > 0,
381 NumberRepr::UInt128(value) => value > 0,
382 NumberRepr::Float32(value) => value > 0.0,
383 NumberRepr::Float64(value) => value > 0.0,
384 #[cfg(feature = "big-integer")]
385 NumberRepr::BigInteger(value) => value.is_positive(),
386 #[cfg(feature = "big-decimal")]
387 NumberRepr::BigDecimal(value) => value.is_positive(),
388 #[cfg(not(any(feature = "big-integer", feature = "big-decimal")))]
389 NumberRepr::Lifetime(_, impossible) => match impossible {},
390 }
391 }
392
393 #[must_use]
400 pub fn is_negative(self) -> bool {
401 match self.inner {
402 NumberRepr::Int8(value) => value < 0,
403 NumberRepr::Int16(value) => value < 0,
404 NumberRepr::Int32(value) => value < 0,
405 NumberRepr::Int64(value) => value < 0,
406 NumberRepr::Int128(value) => value < 0,
407 NumberRepr::UInt8(_) => false,
408 NumberRepr::UInt16(_) => false,
409 NumberRepr::UInt32(_) => false,
410 NumberRepr::UInt64(_) => false,
411 NumberRepr::UInt128(_) => false,
412 NumberRepr::Float32(value) => value < 0.0,
413 NumberRepr::Float64(value) => value < 0.0,
414 #[cfg(feature = "big-integer")]
415 NumberRepr::BigInteger(value) => value.is_negative(),
416 #[cfg(feature = "big-decimal")]
417 NumberRepr::BigDecimal(value) => value.is_negative(),
418 #[cfg(not(any(feature = "big-integer", feature = "big-decimal")))]
419 NumberRepr::Lifetime(_, impossible) => match impossible {},
420 }
421 }
422
423 pub fn compare(self, right: NumberRef<'_>, policy: NumericComparisonPolicy) -> Option<Ordering> {
446 if self.is_nan() || right.is_nan() {
447 return None;
448 }
449 match (self.is_infinite(), right.is_infinite()) {
450 (true, true) => {
451 return Some(if self.is_negative() == right.is_negative() {
452 Ordering::Equal
453 } else if self.is_negative() {
454 Ordering::Less
455 } else {
456 Ordering::Greater
457 });
458 }
459 (true, false) => {
460 return Some(if self.is_negative() {
461 Ordering::Less
462 } else {
463 Ordering::Greater
464 });
465 }
466 (false, true) => {
467 return Some(if right.is_negative() {
468 Ordering::Greater
469 } else {
470 Ordering::Less
471 });
472 }
473 (false, false) => {}
474 }
475 if policy == NumericComparisonPolicy::Approximate
476 && (self.is_float() || right.is_float())
477 && let (Some(left), Some(right)) = (self.to_approximate_f64(), right.to_approximate_f64())
478 && left.is_finite()
479 && right.is_finite()
480 {
481 return left.partial_cmp(&right);
482 }
483 #[cfg(any(feature = "big-integer", feature = "big-decimal"))]
484 {
485 #[cfg(feature = "big-integer")]
486 let has_big_integer = self.is_big_integer() || right.is_big_integer();
487 #[cfg(not(feature = "big-integer"))]
488 let has_big_integer = false;
489 #[cfg(feature = "big-decimal")]
490 let has_big_decimal = self.is_big_decimal() || right.is_big_decimal();
491 #[cfg(not(feature = "big-decimal"))]
492 let has_big_decimal = false;
493 if has_big_integer || has_big_decimal {
494 #[cfg(feature = "big-decimal")]
495 if policy == NumericComparisonPolicy::Exact
496 && self.is_big_decimal() != right.is_big_decimal()
497 && let Some(ordering) = self.compare_zero_or_opposite_sign(right)
498 {
499 return Some(ordering);
500 }
501 return self.compare_exact_rational(right);
502 }
503 }
504 self.compare_fixed(right)
505 }
506
507 #[cfg(feature = "big-decimal")]
519 fn compare_zero_or_opposite_sign(self, right: NumberRef<'_>) -> Option<Ordering> {
520 let left_zero = self.is_zero();
521 let right_zero = right.is_zero();
522 if left_zero {
523 return Some(if right_zero {
524 Ordering::Equal
525 } else if right.is_negative() {
526 Ordering::Greater
527 } else {
528 Ordering::Less
529 });
530 }
531 if right_zero {
532 return Some(if self.is_negative() {
533 Ordering::Less
534 } else {
535 Ordering::Greater
536 });
537 }
538 if self.is_negative() != right.is_negative() {
539 return Some(if self.is_negative() {
540 Ordering::Less
541 } else {
542 Ordering::Greater
543 });
544 }
545 None
546 }
547
548 #[inline]
554 pub(in crate::numeric) const fn inner(self) -> NumberRepr<'a> {
555 self.inner
556 }
557
558 #[cfg(any(feature = "big-integer", feature = "big-decimal"))]
571 fn compare_exact_rational(self, right: NumberRef<'_>) -> Option<Ordering> {
572 #[cfg(feature = "big-integer")]
573 if let (NumberRepr::BigInteger(left), NumberRepr::BigInteger(right)) = (self.inner, right.inner) {
574 return Some(left.cmp(right));
575 }
576 #[cfg(feature = "big-integer")]
577 if let Some(ordering) = self.compare_big_integer_fixed(right) {
578 return Some(ordering);
579 }
580 #[cfg(feature = "big-integer")]
581 if let Some(ordering) = self.compare_big_integer_float(right) {
582 return Some(ordering);
583 }
584 #[cfg(feature = "big-decimal")]
585 if let (NumberRepr::BigDecimal(left), NumberRepr::BigDecimal(right)) = (self.inner, right.inner) {
586 return Some(left.cmp(right));
587 }
588 match (self.to_exact_rational(), right.to_exact_rational()) {
589 (Some(left), Some(right)) => Some(left.cmp(&right)),
590 #[cfg(feature = "big-decimal")]
591 _ => self.compare_exact_decimal(right),
592 #[cfg(not(feature = "big-decimal"))]
593 _ => None,
594 }
595 }
596
597 #[cfg(feature = "big-integer")]
608 #[inline]
609 fn compare_big_integer_fixed(self, right: NumberRef<'_>) -> Option<Ordering> {
610 match (self.inner, right.inner) {
611 (NumberRepr::BigInteger(left), right) => compare_big_integer_to_fixed(left, right),
612 (left, NumberRepr::BigInteger(right)) => compare_big_integer_to_fixed(right, left).map(Ordering::reverse),
613 _ => None,
614 }
615 }
616
617 #[cfg(feature = "big-integer")]
624 #[inline]
625 fn compare_big_integer_float(self, right: NumberRef<'_>) -> Option<Ordering> {
626 match (self.inner, right.inner) {
627 (NumberRepr::BigInteger(left), NumberRepr::Float32(_) | NumberRepr::Float64(_)) => {
628 compare_big_integer_float(left, right)
629 }
630 (NumberRepr::Float32(_) | NumberRepr::Float64(_), NumberRepr::BigInteger(right)) => {
631 compare_big_integer_float(right, self).map(Ordering::reverse)
632 }
633 _ => None,
634 }
635 }
636
637 #[cfg(feature = "big-decimal")]
649 #[inline]
650 fn compare_exact_decimal(self, right: NumberRef<'_>) -> Option<Ordering> {
651 Some(self.to_exact_decimal()?.cmp(&right.to_exact_decimal()?))
652 }
653
654 fn compare_fixed(self, right: NumberRef<'_>) -> Option<Ordering> {
665 let (left_negative, left_significand, left_exponent) = finite_parts(self)?;
666 let (right_negative, right_significand, right_exponent) = finite_parts(right)?;
667 if left_significand == 0 && right_significand == 0 {
668 return Some(Ordering::Equal);
669 }
670 match left_negative.cmp(&right_negative) {
671 Ordering::Less => Some(Ordering::Greater),
672 Ordering::Greater => Some(Ordering::Less),
673 Ordering::Equal => {
674 let ordering = compare_magnitude(left_significand, left_exponent, right_significand, right_exponent);
675 Some(if left_negative { ordering.reverse() } else { ordering })
676 }
677 }
678 }
679
680 fn to_approximate_f64(self) -> Option<f64> {
693 match self.inner {
694 NumberRepr::Int8(value) => Some(f64::from(value)),
695 NumberRepr::Int16(value) => Some(f64::from(value)),
696 NumberRepr::Int32(value) => Some(f64::from(value)),
697 NumberRepr::Int64(value) => Some(value as f64),
698 NumberRepr::Int128(value) => Some(value as f64),
699 NumberRepr::UInt8(value) => Some(f64::from(value)),
700 NumberRepr::UInt16(value) => Some(f64::from(value)),
701 NumberRepr::UInt32(value) => Some(f64::from(value)),
702 NumberRepr::UInt64(value) => Some(value as f64),
703 NumberRepr::UInt128(value) => Some(value as f64),
704 NumberRepr::Float32(value) => Some(f64::from(value)),
705 NumberRepr::Float64(value) => Some(value),
706 #[cfg(feature = "big-integer")]
707 NumberRepr::BigInteger(value) => value.to_f64(),
708 #[cfg(feature = "big-decimal")]
709 NumberRepr::BigDecimal(value) => {
710 let (coefficient, scale) = value.as_bigint_and_scale();
711 let max_scale_reduction = i64::try_from(coefficient.bits()).ok()?;
713 scale.checked_sub(max_scale_reduction)?;
714 value.to_f64()
715 }
716 #[cfg(not(any(feature = "big-integer", feature = "big-decimal")))]
717 NumberRepr::Lifetime(_, impossible) => match impossible {},
718 }
719 }
720
721 #[cfg(any(feature = "big-integer", feature = "big-decimal"))]
728 fn to_exact_rational(self) -> Option<BigRational> {
729 match self.inner {
730 NumberRepr::Int8(value) => Some(BigRational::from_integer(BigInt::from(value))),
731 NumberRepr::Int16(value) => Some(BigRational::from_integer(BigInt::from(value))),
732 NumberRepr::Int32(value) => Some(BigRational::from_integer(BigInt::from(value))),
733 NumberRepr::Int64(value) => Some(BigRational::from_integer(BigInt::from(value))),
734 NumberRepr::Int128(value) => Some(BigRational::from_integer(BigInt::from(value))),
735 NumberRepr::UInt8(value) => Some(BigRational::from_integer(BigInt::from(value))),
736 NumberRepr::UInt16(value) => Some(BigRational::from_integer(BigInt::from(value))),
737 NumberRepr::UInt32(value) => Some(BigRational::from_integer(BigInt::from(value))),
738 NumberRepr::UInt64(value) => Some(BigRational::from_integer(BigInt::from(value))),
739 NumberRepr::UInt128(value) => Some(BigRational::from_integer(BigInt::from(value))),
740 NumberRepr::Float32(value) => Some(f32_rational(value)),
741 NumberRepr::Float64(value) => Some(f64_rational(value)),
742 #[cfg(feature = "big-integer")]
743 NumberRepr::BigInteger(value) => Some(BigRational::from_integer(value.clone())),
744 #[cfg(feature = "big-decimal")]
745 NumberRepr::BigDecimal(value) => decimal_rational(value),
746 }
747 }
748
749 #[cfg(feature = "big-decimal")]
759 fn to_exact_decimal(self) -> Option<BigDecimal> {
760 match self.inner {
761 NumberRepr::Int8(value) => Some(BigDecimal::from(value)),
762 NumberRepr::Int16(value) => Some(BigDecimal::from(value)),
763 NumberRepr::Int32(value) => Some(BigDecimal::from(value)),
764 NumberRepr::Int64(value) => Some(BigDecimal::from(value)),
765 NumberRepr::Int128(value) => Some(BigDecimal::from(value)),
766 NumberRepr::UInt8(value) => Some(BigDecimal::from(value)),
767 NumberRepr::UInt16(value) => Some(BigDecimal::from(value)),
768 NumberRepr::UInt32(value) => Some(BigDecimal::from(value)),
769 NumberRepr::UInt64(value) => Some(BigDecimal::from(value)),
770 NumberRepr::UInt128(value) => Some(BigDecimal::from(value)),
771 NumberRepr::Float32(value) => BigDecimal::try_from(value).ok(),
772 NumberRepr::Float64(value) => BigDecimal::try_from(value).ok(),
773 #[cfg(feature = "big-integer")]
774 NumberRepr::BigInteger(value) => Some(BigDecimal::from(value.clone())),
775 NumberRepr::BigDecimal(value) => Some(value.clone()),
776 }
777 }
778}