use crate::{
arch::word::{DoubleWord, Word},
buffer::Buffer,
div_const::{ConstDivisorRepr, ConstDoubleDivisor, ConstLargeDivisor, ConstSingleDivisor},
fast_div::ConstDivisor,
helper_macros::debug_assert_zero,
ibig::IBig,
primitive::shrink_dword,
repr::{Repr, TypedReprRef::*},
shift,
ubig::UBig,
Sign::*,
};
use dashu_base::UnsignedAbs;
use super::repr::{Reduced, ReducedDword, ReducedLarge, ReducedRepr, ReducedWord};
impl Reduced<'_> {
#[inline]
pub fn residue(&self) -> UBig {
let repr = match self.repr() {
ReducedRepr::Single(raw, ring) => Repr::from_word(raw.residue(ring)),
ReducedRepr::Double(raw, ring) => Repr::from_dword(raw.residue(ring)),
ReducedRepr::Large(raw, ring) => Repr::from_buffer(raw.residue(ring)),
};
UBig(repr)
}
pub fn modulus(&self) -> UBig {
let repr = match self.repr() {
ReducedRepr::Single(_, ring) => Repr::from_word(ring.divisor()),
ReducedRepr::Double(_, ring) => Repr::from_dword(ring.divisor()),
ReducedRepr::Large(_, ring) => Repr::from_buffer(ring.divisor()),
};
UBig(repr)
}
}
impl ReducedWord {
#[inline]
pub fn from_ubig(x: &UBig, ring: &ConstSingleDivisor) -> Self {
Self(match x.repr() {
RefSmall(dword) => {
if let Some(word) = shrink_dword(dword) {
ring.rem_word(word)
} else {
ring.rem_dword(dword)
}
}
RefLarge(words) => ring.rem_large(words),
})
}
#[inline]
pub fn residue(self, ring: &ConstSingleDivisor) -> Word {
debug_assert!(self.is_valid(ring));
self.0 >> ring.shift()
}
}
impl ReducedDword {
#[inline]
pub fn from_ubig(x: &UBig, ring: &ConstDoubleDivisor) -> Self {
Self(match x.repr() {
RefSmall(dword) => ring.rem_dword(dword),
RefLarge(words) => ring.rem_large(words),
})
}
#[inline]
pub fn residue(self, ring: &ConstDoubleDivisor) -> DoubleWord {
debug_assert!(self.is_valid(ring));
self.0 >> ring.shift()
}
}
impl ReducedLarge {
pub fn from_ubig(x: UBig, ring: &ConstLargeDivisor) -> ReducedLarge {
let mut buffer = ring.rem_repr(x.into_repr());
let modulus_len = ring.normalized_divisor.len();
buffer.ensure_capacity_exact(modulus_len);
buffer.push_zeros(modulus_len - buffer.len());
Self(buffer.into_boxed_slice())
}
pub fn residue(&self, ring: &ConstLargeDivisor) -> Buffer {
let mut buffer: Buffer = self.0.as_ref().into();
debug_assert_zero!(shift::shr_in_place(&mut buffer, ring.shift));
buffer
}
}
pub trait IntoRing<'a, RingReducer> {
type RingElement: 'a;
fn into_ring(self, reducer: &'a RingReducer) -> Self::RingElement;
}
impl<'a> IntoRing<'a, ConstDivisor> for UBig {
type RingElement = Reduced<'a>;
#[inline]
fn into_ring(self, ring: &ConstDivisor) -> Reduced {
match &ring.0 {
ConstDivisorRepr::Single(ring) => {
Reduced::from_single(ReducedWord::from_ubig(&self, ring), ring)
}
ConstDivisorRepr::Double(ring) => {
Reduced::from_double(ReducedDword::from_ubig(&self, ring), ring)
}
ConstDivisorRepr::Large(ring) => {
Reduced::from_large(ReducedLarge::from_ubig(self, ring), ring)
}
}
}
}
impl<'a> IntoRing<'a, ConstDivisor> for IBig {
type RingElement = Reduced<'a>;
#[inline]
fn into_ring(self, ring: &ConstDivisor) -> Reduced {
let sign = self.sign();
let modulo = self.unsigned_abs().into_ring(ring);
match sign {
Positive => modulo,
Negative => -modulo,
}
}
}
macro_rules! impl_into_ring_for_unsigned {
($t:ty) => {
impl<'a> IntoRing<'a, ConstDivisor> for $t {
type RingElement = Reduced<'a>;
#[inline]
fn into_ring(self, ring: &'a ConstDivisor) -> Reduced<'a> {
UBig::from(self).into_ring(ring)
}
}
};
}
macro_rules! impl_into_modulo_for_signed {
($t:ty) => {
impl<'a> IntoRing<'a, ConstDivisor> for $t {
type RingElement = Reduced<'a>;
#[inline]
fn into_ring(self, ring: &'a ConstDivisor) -> Reduced<'a> {
IBig::from(self).into_ring(ring)
}
}
};
}
impl_into_ring_for_unsigned!(bool);
impl_into_ring_for_unsigned!(u8);
impl_into_ring_for_unsigned!(u16);
impl_into_ring_for_unsigned!(u32);
impl_into_ring_for_unsigned!(u64);
impl_into_ring_for_unsigned!(u128);
impl_into_ring_for_unsigned!(usize);
impl_into_modulo_for_signed!(i8);
impl_into_modulo_for_signed!(i16);
impl_into_modulo_for_signed!(i32);
impl_into_modulo_for_signed!(i64);
impl_into_modulo_for_signed!(i128);
impl_into_modulo_for_signed!(isize);
impl ConstDivisor {
pub fn reduce<'a, T: IntoRing<'a, ConstDivisor, RingElement = Reduced<'a>>>(
&'a self,
x: T,
) -> Reduced {
x.into_ring(self)
}
}