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
use crate::{
    add_ops::repr::{sub_large, sub_large_dword, sub_large_ref_val},
    buffer::Buffer,
    cmp,
    div_const::{ConstDivisor, ConstDivisorRepr},
    helper_macros::debug_assert_zero,
    math,
    primitive::shrink_dword,
    repr::{Repr, TypedRepr, TypedReprRef},
    shift,
    ubig::UBig,
};
use num_modular::Reducer;

use super::{
    repr::{ReducedDword, ReducedLarge, ReducedRepr, ReducedWord},
    Reduced,
};

impl ConstDivisor {
    /// If target is larger than the normalized divisor, then subtract it once.
    fn reduce_once(&self, target: UBig) -> UBig {
        if !self.check(&target) {
            match &self.0 {
                ConstDivisorRepr::Single(d) => target - d.normalized_divisor(),
                ConstDivisorRepr::Double(d) => target - d.normalized_divisor(),
                ConstDivisorRepr::Large(d) => {
                    match target.into_repr() {
                        TypedRepr::Small(s) => UBig::from_dword(s), // no need to reduce
                        TypedRepr::Large(s) => UBig(sub_large(s, &d.normalized_divisor)),
                    }
                }
            }
        } else {
            target
        }
    }

    /// Reduce -target
    fn reduce_negate(&self, target: UBig) -> UBig {
        match &self.0 {
            ConstDivisorRepr::Single(d) => d.normalized_divisor() - target,
            ConstDivisorRepr::Double(d) => d.normalized_divisor() - target,
            ConstDivisorRepr::Large(d) => match target.into_repr() {
                TypedRepr::Small(s) => {
                    UBig(sub_large_dword(d.normalized_divisor.as_ref().into(), s))
                }
                TypedRepr::Large(s) => UBig(sub_large_ref_val(&d.normalized_divisor, s)),
            },
        }
    }

    fn convert_from_normalized(&self, target: &UBig) -> Reduced<'_> {
        match &self.0 {
            ConstDivisorRepr::Single(d) => {
                Reduced::from_single(ReducedWord(target.try_into().unwrap()), d)
            }
            ConstDivisorRepr::Double(d) => {
                Reduced::from_double(ReducedDword(target.try_into().unwrap()), d)
            }
            ConstDivisorRepr::Large(d) => {
                let mut buf = Buffer::allocate_exact(d.normalized_divisor.len());
                let words = target.as_words();
                buf.push_slice(words);
                buf.push_zeros(d.normalized_divisor.len() - words.len());
                Reduced::from_large(ReducedLarge(buf.into_boxed_slice()), d)
            }
        }
    }

    fn convert_to_normalized(&self, target: Reduced) -> UBig {
        // this function is for internal use only!
        // it's not checked whether `target` is using `self` as the ring!
        match target.repr() {
            ReducedRepr::Single(raw, _) => UBig(Repr::from_word(raw.0)),
            ReducedRepr::Double(raw, _) => UBig(Repr::from_dword(raw.0)),
            ReducedRepr::Large(raw, _) => {
                let mut buf = Buffer::from(raw.0.as_ref());
                buf.pop_zeros();
                UBig(Repr::from_buffer(buf))
            }
        }
    }
}

impl Reducer<UBig> for ConstDivisor {
    #[inline]
    fn new(m: &UBig) -> Self {
        ConstDivisor::new(m.clone())
    }

    fn transform(&self, target: UBig) -> UBig {
        UBig(match &self.0 {
            ConstDivisorRepr::Single(d) => Repr::from_word(ReducedWord::from_ubig(&target, d).0),
            ConstDivisorRepr::Double(d) => Repr::from_dword(ReducedDword::from_ubig(&target, d).0),
            ConstDivisorRepr::Large(d) => Repr::from_buffer(d.rem_repr(target.into_repr())),
        })
    }
    fn check(&self, target: &UBig) -> bool {
        // check whether target < self.divisor()
        match (&self.0, target.repr()) {
            (ConstDivisorRepr::Single(d), TypedReprRef::RefSmall(dw)) => match shrink_dword(dw) {
                Some(w) => d.0.check(&w),
                None => false,
            },
            (ConstDivisorRepr::Single(_), TypedReprRef::RefLarge(_)) => false,
            (ConstDivisorRepr::Double(d), TypedReprRef::RefSmall(dw)) => d.0.check(&dw),
            (ConstDivisorRepr::Double(_), TypedReprRef::RefLarge(_)) => false,
            (ConstDivisorRepr::Large(_), TypedReprRef::RefSmall(_)) => true,
            (ConstDivisorRepr::Large(d), TypedReprRef::RefLarge(words)) => {
                cmp::cmp_in_place(words, &d.normalized_divisor).is_le()
                    && words[0] & math::ones_word(d.shift) == 0 // must be shifted
            }
        }
    }

    #[inline]
    fn modulus(&self) -> UBig {
        self.value()
    }

    fn residue(&self, target: UBig) -> UBig {
        UBig(match target.into_repr() {
            TypedRepr::Small(dw) => match &self.0 {
                ConstDivisorRepr::Single(d) => {
                    Repr::from_word(shrink_dword(dw).unwrap() >> d.0.shift())
                }
                ConstDivisorRepr::Double(d) => Repr::from_dword(dw >> d.0.shift()),
                ConstDivisorRepr::Large(d) => Repr::from_dword(dw >> d.shift),
            },
            TypedRepr::Large(mut buffer) => {
                if let ConstDivisorRepr::Large(d) = &self.0 {
                    debug_assert_zero!(shift::shr_in_place(&mut buffer, d.shift));
                    Repr::from_buffer(buffer)
                } else {
                    unreachable!()
                }
            }
        })
    }

    #[inline(always)]
    fn is_zero(&self, target: &UBig) -> bool {
        target.is_zero()
    }

    #[inline]
    fn add(&self, lhs: &UBig, rhs: &UBig) -> UBig {
        self.reduce_once(lhs + rhs)
    }
    #[inline]
    fn dbl(&self, target: UBig) -> UBig {
        self.reduce_once(target << 1)
    }
    #[inline]
    fn sub(&self, lhs: &UBig, rhs: &UBig) -> UBig {
        if lhs >= rhs {
            lhs - rhs
        } else {
            self.reduce_negate(rhs - lhs)
        }
    }
    #[inline]
    fn neg(&self, target: UBig) -> UBig {
        if target.is_zero() {
            target
        } else {
            self.reduce_negate(target)
        }
    }

    // for the following operations, copying is relatively cheap and the implementations of
    // the `Reduced` type are relied on.

    #[inline]
    fn mul(&self, lhs: &UBig, rhs: &UBig) -> UBig {
        let lhs = self.convert_from_normalized(lhs);
        let rhs = self.convert_from_normalized(rhs);
        self.convert_to_normalized(lhs * rhs)
    }
    #[inline]
    fn sqr(&self, target: UBig) -> UBig {
        self.convert_to_normalized(self.convert_from_normalized(&target).sqr())
    }
    #[inline]
    fn inv(&self, target: UBig) -> Option<UBig> {
        self.convert_from_normalized(&target)
            .inv()
            .map(|v| self.convert_to_normalized(v))
    }
    #[inline]
    fn pow(&self, base: UBig, exp: &UBig) -> UBig {
        self.convert_to_normalized(self.convert_from_normalized(&base).pow(exp))
    }
}