use crate::{
add,
arch::word::Word,
buffer::Buffer,
cmp,
error::panic_different_rings,
helper_macros::{forward_modular_binop_to_assign, impl_modular_commutative_op_for_ref},
memory::{self, Memory, MemoryAllocation},
mul,
primitive::{double_word, extend_word, locate_top_word_plus_one, split_dword},
sqr,
};
use alloc::alloc::Layout;
use core::ops::{Mul, MulAssign};
use num_modular::Reducer;
use super::repr::{Montgomery, MontgomeryInner, MontgomeryLargeRepr, MontgomeryLargeVal};
forward_modular_binop_to_assign!(impl Mul, mul, MulAssign, mul_assign for Montgomery);
impl_modular_commutative_op_for_ref!(impl Mul, mul for Montgomery);
impl<'a> Mul<&Montgomery<'a>> for &Montgomery<'a> {
type Output = Montgomery<'a>;
#[inline]
fn mul(self, rhs: &Montgomery<'a>) -> Montgomery<'a> {
match (self.repr(), rhs.repr()) {
(MontgomeryInner::Large(raw0, ring), MontgomeryInner::Large(raw1, ring1)) => {
Montgomery::check_same_ring_large(ring, ring1);
let memory_requirement = mul_memory_requirement(ring);
let mut allocation = MemoryAllocation::new(memory_requirement);
let mut memory = allocation.memory();
let prod = if raw0.0.as_ptr() == raw1.0.as_ptr() {
sqr_normalized_large(ring, &raw0.0, &mut memory)
} else {
mul_normalized_large(ring, &raw0.0, &raw1.0, &mut memory)
};
Montgomery::from_large(
MontgomeryLargeVal(Buffer::from(prod).into_boxed_slice()),
ring,
)
}
_ => self.clone().mul(rhs),
}
}
}
impl<'a> MulAssign<&Montgomery<'a>> for Montgomery<'a> {
#[inline]
fn mul_assign(&mut self, rhs: &Montgomery<'a>) {
match (self.repr_mut(), rhs.repr()) {
(MontgomeryInner::Single(raw0, ring), MontgomeryInner::Single(raw1, ring1)) => {
Montgomery::check_same_ring_single(ring, ring1);
ring.0.mul_in_place(raw0, raw1);
}
(MontgomeryInner::Double(raw0, ring), MontgomeryInner::Double(raw1, ring1)) => {
Montgomery::check_same_ring_double(ring, ring1);
ring.0.mul_in_place(raw0, raw1);
}
(MontgomeryInner::Large(raw0, ring), MontgomeryInner::Large(raw1, ring1)) => {
Montgomery::check_same_ring_large(ring, ring1);
let memory_requirement = mul_memory_requirement(ring);
let mut allocation = MemoryAllocation::new(memory_requirement);
mul_in_place_large(ring, raw0, raw1, &mut allocation.memory());
}
_ => panic_different_rings(),
}
}
}
impl<'a> Montgomery<'a> {
pub fn sqr(&self) -> Self {
match self.repr() {
MontgomeryInner::Single(raw, ring) => Montgomery::from_single(ring.0.sqr(*raw), ring),
MontgomeryInner::Double(raw, ring) => Montgomery::from_double(ring.0.sqr(*raw), ring),
MontgomeryInner::Large(raw, ring) => {
let memory_requirement = mul_memory_requirement(ring);
let mut allocation = MemoryAllocation::new(memory_requirement);
let mut memory = allocation.memory();
let prod = sqr_normalized_large(ring, &raw.0, &mut memory);
Montgomery::from_large(
MontgomeryLargeVal(Buffer::from(prod).into_boxed_slice()),
ring,
)
}
}
}
}
pub(crate) fn mul_memory_requirement(ring: &MontgomeryLargeRepr) -> Layout {
let s = ring.modulus.len();
memory::add_layout(
memory::array_layout::<Word>(2 * s + 1),
memory::max_layout(
mul::memory_requirement_exact(2 * s, s),
sqr::memory_requirement_exact(s),
),
)
}
pub(crate) fn redc_in_place(t: &mut [Word], ring: &MontgomeryLargeRepr) {
let s = ring.modulus.len();
let n0_dword = ring.n0_dword;
let n0 = ring.n0_word();
let modulus = &ring.modulus;
debug_assert_eq!(t.len(), 2 * s + 1);
let mut i = 0;
while i + 1 < s {
let ti = double_word(t[i], t[i + 1]);
let q = ti.wrapping_mul(n0_dword);
let (q0, q1) = split_dword(q);
let (carry_lo, carry_hi) =
mul::add_mul_dword_same_len_in_place(&mut t[i..i + s], modulus, q0, q1);
let overflow = add::add_dword_in_place(&mut t[i + s..], double_word(carry_lo, carry_hi));
debug_assert!(!overflow);
debug_assert_eq!(t[i], 0);
debug_assert_eq!(t[i + 1], 0);
i += 2;
}
if i < s {
let q = t[i].wrapping_mul(n0);
let carry = mul::add_mul_word_same_len_in_place(&mut t[i..i + s], q, modulus);
let overflow = add::add_word_in_place(&mut t[i + s..], carry);
debug_assert!(!overflow);
debug_assert_eq!(t[i], 0);
}
}
pub(crate) fn redc_single_in_place(t: &mut [Word], ring: &MontgomeryLargeRepr) {
let s = ring.modulus.len();
let n0 = ring.n0_word();
let modulus = &ring.modulus;
debug_assert_eq!(t.len(), 2 * s + 1);
for i in 0..s {
let q = t[i].wrapping_mul(n0);
let carry = mul::add_mul_word_same_len_in_place(&mut t[i..i + s], q, modulus);
let overflow = add::add_word_in_place(&mut t[i + s..], carry);
debug_assert!(!overflow);
debug_assert_eq!(t[i], 0);
}
}
fn canonicalize<'a>(t: &'a mut [Word], ring: &MontgomeryLargeRepr) -> &'a [Word] {
let s = ring.modulus.len();
let modulus = &ring.modulus;
if t[2 * s] != 0 || cmp::cmp_same_len(&t[s..2 * s], modulus).is_ge() {
let borrow = add::sub_in_place(&mut t[s..2 * s + 1], modulus);
debug_assert!(!borrow);
}
debug_assert_eq!(t[2 * s], 0);
&t[s..2 * s]
}
#[inline]
fn finish_monty_product<'a>(product: &'a mut [Word], ring: &MontgomeryLargeRepr) -> &'a [Word] {
redc_in_place(product, ring);
canonicalize(product, ring)
}
pub(crate) fn mul_normalized_large<'a>(
ring: &MontgomeryLargeRepr,
a: &[Word],
b: &[Word],
memory: &'a mut Memory,
) -> &'a [Word] {
let s = ring.modulus.len();
debug_assert!(a.len() == s && b.len() == s);
let na = locate_top_word_plus_one(a);
let nb = locate_top_word_plus_one(b);
let (product, mut memory) = memory.allocate_slice_fill::<Word>(2 * s + 1, 0);
if na | nb == 0 {
return &product[s..2 * s];
} else if na == 1 && nb == 1 {
let (lo, hi) = split_dword(extend_word(a[0]) * extend_word(b[0]));
product[0] = lo;
product[1] = hi;
} else {
mul::multiply(&mut product[..na + nb], &a[..na], &b[..nb], &mut memory);
}
finish_monty_product(product, ring)
}
pub(crate) fn sqr_normalized_large<'a>(
ring: &MontgomeryLargeRepr,
a: &[Word],
memory: &'a mut Memory,
) -> &'a [Word] {
let s = ring.modulus.len();
debug_assert!(a.len() == s);
let na = locate_top_word_plus_one(a);
let (product, mut memory) = memory.allocate_slice_fill::<Word>(2 * s + 1, 0);
if na == 0 {
return &product[s..2 * s];
} else if na == 1 {
let (lo, hi) = split_dword(extend_word(a[0]) * extend_word(a[0]));
product[0] = lo;
product[1] = hi;
} else {
sqr::sqr(&mut product[..2 * na], &a[..na], &mut memory);
}
finish_monty_product(product, ring)
}
pub(crate) fn residue_normalized_large<'a>(
ring: &MontgomeryLargeRepr,
a: &[Word],
memory: &'a mut Memory,
) -> &'a [Word] {
let s = ring.modulus.len();
debug_assert_eq!(a.len(), s);
let (product, _memory) = memory.allocate_slice_fill::<Word>(2 * s + 1, 0);
product[..s].copy_from_slice(a);
redc_single_in_place(product, ring);
canonicalize(product, ring)
}
pub(crate) fn one_large(ring: &MontgomeryLargeRepr) -> MontgomeryLargeVal {
let memory_requirement = mul_memory_requirement(ring);
let mut allocation = MemoryAllocation::new(memory_requirement);
let mut memory = allocation.memory();
let res = residue_normalized_large(ring, &ring.r2_mod_m, &mut memory);
MontgomeryLargeVal(Buffer::from(res).into_boxed_slice())
}
pub(crate) fn mul_in_place_large(
ring: &MontgomeryLargeRepr,
lhs: &mut MontgomeryLargeVal,
rhs: &MontgomeryLargeVal,
memory: &mut Memory,
) {
let prod = if lhs.0.as_ptr() == rhs.0.as_ptr() {
sqr_normalized_large(ring, &lhs.0, memory)
} else {
mul_normalized_large(ring, &lhs.0, &rhs.0, memory)
};
lhs.0.copy_from_slice(prod);
}
pub(crate) fn sqr_in_place_large(
ring: &MontgomeryLargeRepr,
raw: &mut MontgomeryLargeVal,
memory: &mut Memory,
) {
let prod = sqr_normalized_large(ring, &raw.0, memory);
raw.0.copy_from_slice(prod);
}