dashu_int/modular/
repr.rs

1//! Element of modular arithmetic.
2
3use crate::div_const::{ConstDoubleDivisor, ConstLargeDivisor, ConstSingleDivisor};
4use crate::{
5    arch::word::{DoubleWord, Word},
6    buffer::Buffer,
7    cmp,
8    error::panic_different_rings,
9    math,
10};
11use alloc::boxed::Box;
12use core::ptr;
13
14/// Modular arithmetic.
15///
16/// # Examples
17///
18/// ```
19/// # use dashu_int::{fast_div::ConstDivisor, UBig};
20/// let ring = ConstDivisor::new(UBig::from(10000u32));
21/// let x = ring.reduce(12345);
22/// let y = ring.reduce(55443);
23/// assert_eq!((x - y).residue(), UBig::from(6902u32));
24/// ```
25pub struct Reduced<'a>(ReducedRepr<'a>);
26
27pub(crate) enum ReducedRepr<'a> {
28    Single(ReducedWord, &'a ConstSingleDivisor),
29    Double(ReducedDword, &'a ConstDoubleDivisor),
30    Large(ReducedLarge, &'a ConstLargeDivisor),
31}
32
33/// Single word modular value in some unknown ring. The ring must be provided to operations.
34///
35/// The internal value must be in range 0..modulus and divisible by the shift for ModuloSingleRing
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37pub(crate) struct ReducedWord(pub(crate) Word);
38
39/// Double word modular value in some unknown ring. The ring must be provided to operations.
40///
41/// The internal value must be in range 0..modulus and divisible by the shift for ModuloDoubleRing
42#[derive(Clone, Copy, Debug, Eq, PartialEq)]
43pub(crate) struct ReducedDword(pub(crate) DoubleWord);
44
45/// Multi-word modular value in some unknown ring. `self.0.len() == ring.normalized_modulus.len()`
46#[derive(Clone, PartialEq, Eq)]
47pub(crate) struct ReducedLarge(pub(crate) Box<[Word]>);
48
49impl<'a> Reduced<'a> {
50    /// Get representation.
51    #[inline]
52    pub(crate) fn repr(&self) -> &ReducedRepr<'a> {
53        &self.0
54    }
55
56    /// Get mutable representation.
57    #[inline]
58    pub(crate) fn repr_mut(&mut self) -> &mut ReducedRepr<'a> {
59        &mut self.0
60    }
61
62    #[inline]
63    pub(crate) fn into_repr(self) -> ReducedRepr<'a> {
64        self.0
65    }
66
67    #[inline]
68    pub(crate) const fn from_single(raw: ReducedWord, ring: &'a ConstSingleDivisor) -> Self {
69        debug_assert!(raw.is_valid(ring));
70        Reduced(ReducedRepr::Single(raw, ring))
71    }
72
73    #[inline]
74    pub(crate) const fn from_double(raw: ReducedDword, ring: &'a ConstDoubleDivisor) -> Self {
75        debug_assert!(raw.is_valid(ring));
76        Reduced(ReducedRepr::Double(raw, ring))
77    }
78
79    #[inline]
80    pub(crate) fn from_large(raw: ReducedLarge, ring: &'a ConstLargeDivisor) -> Self {
81        debug_assert!(raw.is_valid(ring));
82        Reduced(ReducedRepr::Large(raw, ring))
83    }
84
85    #[inline]
86    pub(crate) fn check_same_ring_single(lhs: &ConstSingleDivisor, rhs: &ConstSingleDivisor) {
87        if !ptr::eq(lhs, rhs) {
88            // Equality is identity: two rings are not equal even if they have the same modulus.
89            panic_different_rings();
90        }
91    }
92
93    #[inline]
94    pub(crate) fn check_same_ring_double(lhs: &ConstDoubleDivisor, rhs: &ConstDoubleDivisor) {
95        if !ptr::eq(lhs, rhs) {
96            // Equality is identity: two rings are not equal even if they have the same modulus.
97            panic_different_rings();
98        }
99    }
100
101    #[inline]
102    pub(crate) fn check_same_ring_large(lhs: &ConstLargeDivisor, rhs: &ConstLargeDivisor) {
103        if !ptr::eq(lhs, rhs) {
104            // Equality is identity: two rings are not equal even if they have the same modulus.
105            panic_different_rings();
106        }
107    }
108}
109
110impl ReducedWord {
111    pub const fn one(ring: &ConstSingleDivisor) -> Self {
112        Self(1 << ring.shift())
113    }
114
115    #[inline]
116    pub(crate) const fn is_valid(&self, ring: &ConstSingleDivisor) -> bool {
117        self.0 & math::ones_word(ring.shift()) == 0 && self.0 < ring.normalized_divisor()
118    }
119}
120
121impl ReducedDword {
122    pub const fn one(ring: &ConstDoubleDivisor) -> Self {
123        Self(1 << ring.shift())
124    }
125
126    #[inline]
127    pub(crate) const fn is_valid(&self, ring: &ConstDoubleDivisor) -> bool {
128        self.0 & math::ones_dword(ring.shift()) == 0 && self.0 < ring.normalized_divisor()
129    }
130}
131
132impl ReducedLarge {
133    pub fn one(ring: &ConstLargeDivisor) -> Self {
134        let modulus = &ring.normalized_divisor;
135        let mut buf = Buffer::allocate_exact(modulus.len());
136        buf.push(1 << ring.shift);
137        buf.push_zeros(modulus.len() - 1);
138        Self(buf.into_boxed_slice())
139    }
140
141    #[inline]
142    pub(crate) fn is_valid(&self, ring: &ConstLargeDivisor) -> bool {
143        self.0.len() == ring.normalized_divisor.len()
144            && cmp::cmp_same_len(&self.0, &ring.normalized_divisor).is_le()
145            && self.0[0] & math::ones_word(ring.shift) == 0
146    }
147}
148
149impl Clone for Reduced<'_> {
150    #[inline]
151    fn clone(&self) -> Self {
152        Reduced(self.0.clone())
153    }
154    #[inline]
155    fn clone_from(&mut self, source: &Self) {
156        self.0.clone_from(&source.0);
157    }
158}
159
160impl Clone for ReducedRepr<'_> {
161    #[inline]
162    fn clone(&self) -> Self {
163        match self {
164            ReducedRepr::Single(modulo, ring) => ReducedRepr::Single(*modulo, ring),
165            ReducedRepr::Double(modulo, ring) => ReducedRepr::Double(*modulo, ring),
166            ReducedRepr::Large(modulo, ring) => ReducedRepr::Large(modulo.clone(), ring),
167        }
168    }
169
170    #[inline]
171    fn clone_from(&mut self, source: &Self) {
172        if let (ReducedRepr::Large(raw, ring), ReducedRepr::Large(src_raw, src_ring)) =
173            (&mut *self, source)
174        {
175            *ring = src_ring;
176
177            // this can be efficient if ring.len() == src_ring.len()
178            raw.0.clone_from(&src_raw.0);
179        } else {
180            *self = source.clone();
181        }
182    }
183}
184
185/// Equality within a ring.
186///
187/// # Panics
188///
189/// Panics if the two values are from different rings.
190impl PartialEq for Reduced<'_> {
191    #[inline]
192    fn eq(&self, other: &Self) -> bool {
193        match (self.repr(), other.repr()) {
194            (ReducedRepr::Single(raw0, ring0), ReducedRepr::Single(raw1, ring1)) => {
195                Reduced::check_same_ring_single(ring0, ring1);
196                raw0.eq(raw1)
197            }
198            (ReducedRepr::Double(raw0, ring0), ReducedRepr::Double(raw1, ring1)) => {
199                Reduced::check_same_ring_double(ring0, ring1);
200                raw0.eq(raw1)
201            }
202            (ReducedRepr::Large(raw0, ring0), ReducedRepr::Large(raw1, ring1)) => {
203                Reduced::check_same_ring_large(ring0, ring1);
204                raw0.eq(raw1)
205            }
206            _ => panic_different_rings(),
207        }
208    }
209}
210
211impl Eq for Reduced<'_> {}