Skip to main content

dashu_int/modular/
convert.rs

1//! Conversion between Modulo, UBig and IBig.
2
3use crate::{
4    arch::word::{DoubleWord, Word},
5    buffer::Buffer,
6    div_const::{ConstDivisorRepr, ConstDoubleDivisor, ConstLargeDivisor, ConstSingleDivisor},
7    fast_div::ConstDivisor,
8    helper_macros::debug_assert_zero,
9    ibig::IBig,
10    primitive::shrink_dword,
11    repr::{Repr, TypedReprRef::*},
12    shift,
13    ubig::UBig,
14    Sign::*,
15};
16use dashu_base::UnsignedAbs;
17
18use super::repr::{Reduced, ReducedDword, ReducedLarge, ReducedRepr, ReducedWord};
19
20impl Reduced<'_> {
21    /// Get the residue in range `0..n` in an n-element ring.
22    ///
23    /// # Examples
24    ///
25    /// ```
26    /// # use dashu_int::{fast_div::ConstDivisor, UBig};
27    /// let ring = ConstDivisor::new(UBig::from(100u8));
28    /// let x = ring.reduce(-1234);
29    /// assert_eq!(x.residue(), UBig::from(66u8));
30    /// ```
31    #[inline]
32    pub fn residue(&self) -> UBig {
33        let repr = match self.repr() {
34            ReducedRepr::Single(raw, ring) => Repr::from_word(raw.residue(ring)),
35            ReducedRepr::Double(raw, ring) => Repr::from_dword(raw.residue(ring)),
36            ReducedRepr::Large(raw, ring) => Repr::from_buffer(raw.residue(ring)),
37        };
38        UBig(repr)
39    }
40
41    /// Get the modulus of the ring that this element belongs to.
42    pub fn modulus(&self) -> UBig {
43        let repr = match self.repr() {
44            ReducedRepr::Single(_, ring) => Repr::from_word(ring.divisor()),
45            ReducedRepr::Double(_, ring) => Repr::from_dword(ring.divisor()),
46            ReducedRepr::Large(_, ring) => Repr::from_buffer(ring.divisor()),
47        };
48        UBig(repr)
49    }
50}
51
52impl ReducedWord {
53    #[inline]
54    pub fn from_ubig(x: &UBig, ring: &ConstSingleDivisor) -> Self {
55        Self(match x.repr() {
56            RefSmall(dword) => {
57                if let Some(word) = shrink_dword(dword) {
58                    ring.rem_word(word)
59                } else {
60                    ring.rem_dword(dword)
61                }
62            }
63            RefLarge(words) => ring.rem_large(words),
64        })
65    }
66
67    #[inline]
68    pub fn residue(self, ring: &ConstSingleDivisor) -> Word {
69        debug_assert!(self.is_valid(ring));
70        self.0 >> ring.shift()
71    }
72}
73
74impl ReducedDword {
75    #[inline]
76    pub fn from_ubig(x: &UBig, ring: &ConstDoubleDivisor) -> Self {
77        Self(match x.repr() {
78            RefSmall(dword) => ring.rem_dword(dword),
79            RefLarge(words) => ring.rem_large(words),
80        })
81    }
82
83    #[inline]
84    pub fn residue(self, ring: &ConstDoubleDivisor) -> DoubleWord {
85        debug_assert!(self.is_valid(ring));
86        self.0 >> ring.shift()
87    }
88}
89
90impl ReducedLarge {
91    pub fn from_ubig(x: UBig, ring: &ConstLargeDivisor) -> ReducedLarge {
92        let mut buffer = ring.rem_repr(x.into_repr());
93        let modulus_len = ring.normalized_divisor.len();
94        buffer.ensure_capacity_exact(modulus_len);
95        buffer.push_zeros(modulus_len - buffer.len());
96        Self(buffer.into_boxed_slice())
97    }
98
99    pub fn residue(&self, ring: &ConstLargeDivisor) -> Buffer {
100        let mut buffer: Buffer = self.0.as_ref().into();
101        debug_assert_zero!(shift::shr_in_place(&mut buffer, ring.shift));
102        buffer
103    }
104}
105
106/// Trait for types that can be converted into an [IntoRing::RingElement] by a `RingReducer`.
107pub trait IntoRing<'a, RingReducer> {
108    /// The ring-element representation produced by reducing `self` modulo the ring's modulus.
109    type RingElement: 'a;
110    /// Reduce `self` into the residue ring defined by `reducer`.
111    fn into_ring(self, reducer: &'a RingReducer) -> Self::RingElement;
112}
113
114impl<'a> IntoRing<'a, ConstDivisor> for UBig {
115    type RingElement = Reduced<'a>;
116    #[inline]
117    fn into_ring(self, ring: &'a ConstDivisor) -> Reduced<'a> {
118        match &ring.0 {
119            ConstDivisorRepr::Single(ring) => {
120                Reduced::from_single(ReducedWord::from_ubig(&self, ring), ring)
121            }
122            ConstDivisorRepr::Double(ring) => {
123                Reduced::from_double(ReducedDword::from_ubig(&self, ring), ring)
124            }
125            ConstDivisorRepr::Large(ring) => {
126                Reduced::from_large(ReducedLarge::from_ubig(self, ring), ring)
127            }
128        }
129    }
130}
131
132impl<'a> IntoRing<'a, ConstDivisor> for IBig {
133    type RingElement = Reduced<'a>;
134
135    #[inline]
136    fn into_ring(self, ring: &'a ConstDivisor) -> Reduced<'a> {
137        let sign = self.sign();
138        let modulo = self.unsigned_abs().into_ring(ring);
139        match sign {
140            Positive => modulo,
141            Negative => -modulo,
142        }
143    }
144}
145
146/// Implement `IntoModulo` for unsigned primitives.
147macro_rules! impl_into_ring_for_unsigned {
148    ($t:ty) => {
149        impl<'a> IntoRing<'a, ConstDivisor> for $t {
150            type RingElement = Reduced<'a>;
151            #[inline]
152            fn into_ring(self, ring: &'a ConstDivisor) -> Reduced<'a> {
153                UBig::from(self).into_ring(ring)
154            }
155        }
156    };
157}
158
159/// Implement `IntoModulo` for signed primitives.
160macro_rules! impl_into_modulo_for_signed {
161    ($t:ty) => {
162        impl<'a> IntoRing<'a, ConstDivisor> for $t {
163            type RingElement = Reduced<'a>;
164            #[inline]
165            fn into_ring(self, ring: &'a ConstDivisor) -> Reduced<'a> {
166                IBig::from(self).into_ring(ring)
167            }
168        }
169    };
170}
171
172impl_into_ring_for_unsigned!(bool);
173impl_into_ring_for_unsigned!(u8);
174impl_into_ring_for_unsigned!(u16);
175impl_into_ring_for_unsigned!(u32);
176impl_into_ring_for_unsigned!(u64);
177impl_into_ring_for_unsigned!(u128);
178impl_into_ring_for_unsigned!(usize);
179impl_into_modulo_for_signed!(i8);
180impl_into_modulo_for_signed!(i16);
181impl_into_modulo_for_signed!(i32);
182impl_into_modulo_for_signed!(i64);
183impl_into_modulo_for_signed!(i128);
184impl_into_modulo_for_signed!(isize);
185
186impl ConstDivisor {
187    /// Create an element of the modulo ring from another type.
188    ///
189    /// # Examples
190    ///
191    /// ```
192    /// # use dashu_int::{fast_div::ConstDivisor, UBig, IBig};
193    /// let ring = ConstDivisor::new(UBig::from(100u8));
194    /// let x = ring.reduce(-1234);
195    /// let y = ring.reduce(IBig::from(3366));
196    /// assert!(x == y);
197    /// ```
198    pub fn reduce<'a, T: IntoRing<'a, ConstDivisor, RingElement = Reduced<'a>>>(
199        &'a self,
200        x: T,
201    ) -> Reduced<'a> {
202        x.into_ring(self)
203    }
204}