Skip to main content

dashu_int/modular/
mul.rs

1use crate::{
2    add,
3    arch::word::Word,
4    cmp, div,
5    div_const::ConstLargeDivisor,
6    error::panic_different_rings,
7    helper_macros::{
8        debug_assert_zero, forward_modular_binop_to_assign, impl_modular_binop_ref_ref_by_clone,
9        impl_modular_commutative_op_for_ref,
10    },
11    memory::{self, Memory, MemoryAllocation},
12    modular::repr::{Reduced, ReducedRepr},
13    mul,
14    primitive::{extend_word, locate_top_word_plus_one, split_dword},
15    shift, sqr,
16};
17use alloc::alloc::Layout;
18use core::ops::{Deref, Mul, MulAssign};
19use num_modular::Reducer;
20
21use super::repr::{ReducedDword, ReducedLarge, ReducedWord};
22
23forward_modular_binop_to_assign!(impl Mul, mul, MulAssign, mul_assign for Reduced);
24impl_modular_commutative_op_for_ref!(impl Mul, mul for Reduced);
25impl_modular_binop_ref_ref_by_clone!(impl Mul, mul for Reduced);
26
27impl<'a> MulAssign<&Reduced<'a>> for Reduced<'a> {
28    #[inline]
29    fn mul_assign(&mut self, rhs: &Reduced<'a>) {
30        match (self.repr_mut(), rhs.repr()) {
31            (ReducedRepr::Single(raw0, ring), ReducedRepr::Single(raw1, ring1)) => {
32                Reduced::check_same_ring_single(ring, ring1);
33                ring.0.mul_in_place(&mut raw0.0, &raw1.0)
34            }
35            (ReducedRepr::Double(raw0, ring), ReducedRepr::Double(raw1, ring1)) => {
36                Reduced::check_same_ring_double(ring, ring1);
37                ring.0.mul_in_place(&mut raw0.0, &raw1.0)
38            }
39            (ReducedRepr::Large(raw0, ring), ReducedRepr::Large(raw1, ring1)) => {
40                Reduced::check_same_ring_large(ring, ring1);
41                let memory_requirement = mul_memory_requirement(ring);
42                let mut allocation = MemoryAllocation::new(memory_requirement);
43                mul_in_place(ring, raw0, raw1, &mut allocation.memory());
44            }
45            _ => panic_different_rings(),
46        }
47    }
48}
49
50impl<'a> Reduced<'a> {
51    /// Calculate target^2 mod m in reduced form
52    ///
53    /// # Examples
54    ///
55    /// ```
56    /// # use dashu_int::{fast_div::ConstDivisor, UBig};
57    /// let p = UBig::from(0x1234u16);
58    /// let ring = ConstDivisor::new(p.clone());
59    /// let a = ring.reduce(4000);
60    /// assert_eq!(a.sqr(), ring.reduce(4000 * 4000));
61    /// ```
62    pub fn sqr(&self) -> Self {
63        match self.repr() {
64            ReducedRepr::Single(raw, ring) => {
65                Reduced::from_single(ReducedWord(ring.0.sqr(raw.0)), ring)
66            }
67            ReducedRepr::Double(raw, ring) => {
68                Reduced::from_double(ReducedDword(ring.0.sqr(raw.0)), ring)
69            }
70            ReducedRepr::Large(raw, ring) => {
71                let mut result = raw.clone();
72                let memory_requirement = sqr::sqr_memory_requirement(ring.normalized_divisor.len());
73                let mut allocation = MemoryAllocation::new(memory_requirement);
74                sqr_in_place(ring, &mut result, &mut allocation.memory());
75                Reduced::from_large(result, ring)
76            }
77        }
78    }
79}
80
81pub(crate) fn mul_memory_requirement(ring: &ConstLargeDivisor) -> Layout {
82    let n = ring.normalized_divisor.len();
83    memory::add_layout(
84        memory::array_layout::<Word>(2 * n),
85        memory::max_layout(
86            mul::memory_requirement_exact(2 * n, n),
87            div::memory_requirement_exact(2 * n, n),
88        ),
89    )
90}
91
92/// Returns a * b allocated in memory.
93pub(crate) fn mul_normalized<'a>(
94    ring: &ConstLargeDivisor,
95    a: &[Word],
96    b: &[Word],
97    memory: &'a mut Memory,
98) -> &'a [Word] {
99    let modulus = ring.normalized_divisor.deref();
100    let n = modulus.len();
101    debug_assert!(a.len() == n && b.len() == n);
102
103    // trim the leading zeros in a, b
104    let na = locate_top_word_plus_one(a);
105    let nb = locate_top_word_plus_one(b);
106
107    // product = a * b
108    let (product, mut memory) = memory.allocate_slice_fill::<Word>(n.max(na + nb), 0);
109    if na | nb == 0 {
110        return product;
111    } else if na == 1 && nb == 1 {
112        let (a0, b0) = (extend_word(a[0]), extend_word(b[0]));
113        let (lo, hi) = split_dword(a0 * b0);
114        product[0] = lo;
115        product[1] = hi;
116    } else {
117        mul::multiply(&mut product[..na + nb], &a[..na], &b[..nb], &mut memory);
118    }
119
120    // return (product >> shift) % normalized_modulus
121    debug_assert_zero!(shift::shr_in_place(product, ring.shift));
122    if na + nb > n {
123        let _overflow = div::div_rem_in_place(product, modulus, ring.fast_div_top, &mut memory);
124        &product[..n]
125    } else {
126        if cmp::cmp_same_len(product, modulus).is_ge() {
127            debug_assert_zero!(add::sub_same_len_in_place(product, modulus));
128        }
129        product
130    }
131}
132
133/// lhs *= rhs
134pub(crate) fn mul_in_place(
135    ring: &ConstLargeDivisor,
136    lhs: &mut ReducedLarge,
137    rhs: &ReducedLarge,
138    memory: &mut Memory,
139) {
140    if lhs.0 == rhs.0 {
141        // shortcut to squaring
142        let prod = sqr_normalized(ring, &lhs.0, memory);
143        lhs.0.copy_from_slice(prod)
144    } else {
145        let prod = mul_normalized(ring, &lhs.0, &rhs.0, memory);
146        lhs.0.copy_from_slice(prod)
147    }
148}
149
150/// Returns a^2 allocated in memory.
151pub(crate) fn sqr_normalized<'a>(
152    ring: &ConstLargeDivisor,
153    a: &[Word],
154    memory: &'a mut Memory,
155) -> &'a [Word] {
156    let modulus = ring.normalized_divisor.deref();
157    let n = modulus.len();
158    debug_assert!(a.len() == n);
159
160    // trim the leading zeros in a
161    let na = locate_top_word_plus_one(a);
162
163    // product = a * a
164    let (product, mut memory) = memory.allocate_slice_fill::<Word>(n.max(na * 2), 0);
165    if na == 0 {
166        return product;
167    } else if na == 1 {
168        let a0 = extend_word(a[0]);
169        let (lo, hi) = split_dword(a0 * a0);
170        product[0] = lo;
171        product[1] = hi;
172    } else {
173        sqr::sqr(&mut product[..na * 2], &a[..na], &mut memory);
174    }
175
176    // return (product >> shift) % normalized_modulus
177    debug_assert_zero!(shift::shr_in_place(product, ring.shift));
178    if na * 2 > n {
179        let _overflow = div::div_rem_in_place(product, modulus, ring.fast_div_top, &mut memory);
180        &product[..n]
181    } else {
182        if cmp::cmp_same_len(product, modulus).is_ge() {
183            debug_assert_zero!(add::sub_same_len_in_place(product, modulus));
184        }
185        product
186    }
187}
188
189/// raw = raw^2
190pub(crate) fn sqr_in_place(ring: &ConstLargeDivisor, raw: &mut ReducedLarge, memory: &mut Memory) {
191    let prod = sqr_normalized(ring, &raw.0, memory);
192    raw.0.copy_from_slice(prod)
193}