dashu-int 0.6.0-rc.4

Arbitrary-precision integer math library for Rust, balancing ergonomics and efficiency. Provides UBig and IBig (unsigned and signed), with small values inlined on the stack. Efficient arithmetic, modular arithmetic, and number theory (pow, ilog, gcd, gcd_ext); two's-complement bit operations; parsing and formatting in base 2-36; optional serde, rand, num-traits, rkyv, and zeroize.
Documentation
//! Implement num-order traits.

use crate::{ibig::IBig, ubig::UBig};
use core::{cmp::Ordering, hash::Hash};
use dashu_base::{BitTest, FloatEncoding, Sign, Signed};
use num_order::{NumHash, NumOrd};

impl NumHash for UBig {
    fn num_hash<H: core::hash::Hasher>(&self, state: &mut H) {
        let m = self % (i128::MAX as u128);
        (m as i128).hash(state)
    }
}
impl NumHash for IBig {
    fn num_hash<H: core::hash::Hasher>(&self, state: &mut H) {
        (self % i128::MAX).hash(state)
    }
}

impl NumOrd<UBig> for UBig {
    #[inline]
    fn num_cmp(&self, other: &UBig) -> Ordering {
        self.cmp(other)
    }
    #[inline]
    fn num_partial_cmp(&self, other: &UBig) -> Option<Ordering> {
        self.partial_cmp(other)
    }
}

impl NumOrd<IBig> for UBig {
    #[inline]
    fn num_cmp(&self, other: &IBig) -> Ordering {
        let (rhs_sign, rhs_mag) = other.as_sign_repr();
        match rhs_sign {
            Sign::Positive => self.repr().cmp(&rhs_mag),
            Sign::Negative => Ordering::Greater,
        }
    }
    #[inline]
    fn num_partial_cmp(&self, other: &IBig) -> Option<Ordering> {
        Some(self.num_cmp(other))
    }
}

impl NumOrd<UBig> for IBig {
    #[inline]
    fn num_cmp(&self, other: &UBig) -> Ordering {
        let (lhs_sign, lhs_mag) = self.as_sign_repr();
        match lhs_sign {
            Sign::Positive => lhs_mag.cmp(&other.repr()),
            Sign::Negative => Ordering::Less,
        }
    }
    #[inline]
    fn num_partial_cmp(&self, other: &UBig) -> Option<Ordering> {
        Some(self.num_cmp(other))
    }
}

impl NumOrd<IBig> for IBig {
    #[inline]
    fn num_cmp(&self, other: &IBig) -> Ordering {
        self.cmp(other)
    }
    #[inline]
    fn num_partial_cmp(&self, other: &IBig) -> Option<Ordering> {
        self.partial_cmp(other)
    }
}

macro_rules! impl_num_ord_ubig_with_unsigned {
    ($($t:ty)*) => {$(
        impl NumOrd<$t> for UBig {
            #[inline]
            fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
                self.partial_cmp(&UBig::from_unsigned(*other))
            }
        }
        impl NumOrd<UBig> for $t {
            #[inline]
            fn num_partial_cmp(&self, other: &UBig) -> Option<Ordering> {
                UBig::from_unsigned(*self).partial_cmp(other)
            }
        }
    )*};
}
impl_num_ord_ubig_with_unsigned!(u8 u16 u32 u64 u128 usize);

macro_rules! impl_num_ord_ubig_with_signed {
    ($($t:ty)*) => {$(
        impl NumOrd<$t> for UBig {
            #[inline]
            fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
                self.num_partial_cmp(&IBig::from_signed(*other))
            }
        }
        impl NumOrd<UBig> for $t {
            #[inline]
            fn num_partial_cmp(&self, other: &UBig) -> Option<Ordering> {
                IBig::from_signed(*self).num_partial_cmp(other)
            }
        }
    )*};
}
impl_num_ord_ubig_with_signed!(i8 i16 i32 i64 i128 isize);

macro_rules! impl_num_ord_ibig_with_unsigned {
    ($($t:ty)*) => {$(
        impl NumOrd<$t> for IBig {
            #[inline]
            fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
                self.partial_cmp(&IBig::from_unsigned(*other))
            }
        }
        impl NumOrd<IBig> for $t {
            #[inline]
            fn num_partial_cmp(&self, other: &IBig) -> Option<Ordering> {
                IBig::from_unsigned(*self).partial_cmp(other)
            }
        }
    )*};
}
impl_num_ord_ibig_with_unsigned!(u8 u16 u32 u64 u128 usize);

macro_rules! impl_num_ord_ibig_with_signed {
    ($($t:ty)*) => {$(
        impl NumOrd<$t> for IBig {
            #[inline]
            fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
                self.partial_cmp(&IBig::from_signed(*other))
            }
        }
        impl NumOrd<IBig> for $t {
            #[inline]
            fn num_partial_cmp(&self, other: &IBig) -> Option<Ordering> {
                IBig::from_signed(*self).partial_cmp(other)
            }
        }
    )*};
}
impl_num_ord_ibig_with_signed!(i8 i16 i32 i64 i128 isize);

macro_rules! impl_num_ord_ubig_with_float {
    ($($t:ty)*) => {$(
        impl NumOrd<$t> for UBig {
            fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
                // step0: compare with nan and 0
                if other.is_nan() {
                    return None;
                } else if *other == 0. {
                    return match self.is_zero() {
                        true => Some(Ordering::Equal),
                        false => Some(Ordering::Greater)
                    };
                }

                // step1: compare sign
                if other.sign() == Sign::Negative {
                    return Some(Ordering::Greater);
                }

                // step2: compare with infinity
                if other.is_infinite() {
                    return Some(Ordering::Less);
                }

                // step3: test if the integer is bigger than the max float value
                let self_bits = self.bit_len();
                if self_bits > (<$t>::MANTISSA_DIGITS as usize + <$t>::MAX_EXP as usize) {
                    return Some(Ordering::Greater);
                }

                // step4: decode the float and compare the bits
                let (man, exp) = other.decode().unwrap();
                let other_bits = man.bit_len() as isize + exp as isize;
                if other_bits < 0 {
                    return Some(Ordering::Greater);
                } else if self_bits > other_bits as usize {
                    return Some(Ordering::Greater);
                } else if self_bits < other_bits as usize {
                    return Some(Ordering::Less);
                }

                // step5: do the final comparison
                if exp >= 0 {
                    let shifted = UBig::from(man.unsigned_abs()) << exp as usize;
                    self.partial_cmp(&shifted)
                } else {
                    (self << (-exp as usize)).partial_cmp(&UBig::from(man.unsigned_abs()))
                }
            }
        }

        impl NumOrd<UBig> for $t {
            #[inline]
            fn num_partial_cmp(&self, other: &UBig) -> Option<Ordering> {
                other.num_partial_cmp(self).map(|ord| ord.reverse())
            }
        }
    )*};
}
impl_num_ord_ubig_with_float!(f32 f64);

macro_rules! impl_num_ord_ibig_with_float {
    ($($t:ty)*) => {$(
        impl NumOrd<$t> for IBig {
            fn num_partial_cmp(&self, other: &$t) -> Option<Ordering> {
                // step0: compare with nan and 0
                if other.is_nan() {
                    return None;
                } else if *other == 0. {
                    return match self.is_zero() {
                        true => Some(Ordering::Equal),
                        false => Some(self.sign() * Ordering::Greater)
                    };
                }

                // step1: compare sign
                let sign = match (self.sign(), other.sign()) {
                    (Sign::Positive, Sign::Positive) => Sign::Positive,
                    (Sign::Positive, Sign::Negative) => return Some(Ordering::Greater),
                    (Sign::Negative, Sign::Positive) => return Some(Ordering::Less),
                    (Sign::Negative, Sign::Negative) => Sign::Negative,
                };

                // step2: compare with infinity and 0
                if other.is_infinite() {
                    return Some(-sign * Ordering::Less);
                }

                // step3: test if the integer is bigger than the max float value
                let self_bits = self.bit_len();
                if self_bits > (<$t>::MANTISSA_DIGITS as usize + <$t>::MAX_EXP as usize) {
                    return Some(sign * Ordering::Greater);
                }

                // step4: decode the float and compare the bits
                let (man, exp) = other.decode().unwrap();
                let other_bits = man.bit_len() as isize + exp as isize; // i.e. log2(x) + 1
                if other_bits < 0 {
                    return Some(sign * Ordering::Greater);
                } else if self_bits > other_bits as usize {
                    return Some(sign * Ordering::Greater);
                } else if self_bits < other_bits as usize {
                    return Some(sign * Ordering::Less);
                }

                // step5: do the final comparison
                if exp >= 0 {
                    let shifted = IBig::from(man) << exp as usize;
                    self.partial_cmp(&shifted)
                } else {
                    (self << (-exp as usize)).partial_cmp(&IBig::from(man))
                }
            }
        }

        impl NumOrd<IBig> for $t {
            #[inline]
            fn num_partial_cmp(&self, other: &IBig) -> Option<Ordering> {
                other.num_partial_cmp(self).map(|ord| ord.reverse())
            }
        }
    )*};
}
impl_num_ord_ibig_with_float!(f32 f64);