use crate::{
arch::word::{DoubleWord, Word},
buffer::Buffer,
cmp,
error::panic_different_rings,
primitive::{double_word, shrink_dword, split_dword, DWORD_BITS, WORD_BITS_USIZE},
repr::TypedReprRef,
ubig::UBig,
};
use alloc::boxed::Box;
use core::ptr;
use num_modular::{Montgomery as NumMontgomery, Reducer};
pub struct MontgomeryRepr(pub(crate) MontgomeryReprData);
pub(crate) enum MontgomeryReprData {
Single(MontgomerySingleRepr),
Double(MontgomeryDoubleRepr),
Large(MontgomeryLargeRepr),
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct MontgomerySingleRepr(pub(crate) NumMontgomery<Word>);
#[derive(Clone, Copy, Debug)]
pub(crate) struct MontgomeryDoubleRepr(pub(crate) NumMontgomery<DoubleWord>);
#[derive(Debug)]
pub(crate) struct MontgomeryLargeRepr {
pub(crate) modulus: Box<[Word]>,
pub(crate) n0_dword: DoubleWord,
pub(crate) r2_mod_m: Box<[Word]>,
}
pub struct Montgomery<'a>(MontgomeryInner<'a>);
pub(crate) enum MontgomeryInner<'a> {
Single(Word, &'a MontgomerySingleRepr),
Double(DoubleWord, &'a MontgomeryDoubleRepr),
Large(MontgomeryLargeVal, &'a MontgomeryLargeRepr),
}
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct MontgomeryLargeVal(pub(crate) Box<[Word]>);
impl MontgomeryRepr {
pub fn new(m: UBig) -> Self {
match m.repr() {
TypedReprRef::RefSmall(dword) => {
assert!(
dword & 1 == 1 && dword > 1,
"Montgomery modulus must be odd and greater than 1"
);
if let Some(word) = shrink_dword(dword) {
Self(MontgomeryReprData::Single(MontgomerySingleRepr(
<NumMontgomery<Word> as Reducer<Word>>::new(&word),
)))
} else {
Self(MontgomeryReprData::Double(MontgomeryDoubleRepr(<NumMontgomery<
DoubleWord,
> as Reducer<DoubleWord>>::new(
&dword
))))
}
}
TypedReprRef::RefLarge(words) => {
assert!(words[0] & 1 == 1, "Montgomery modulus must be odd and greater than 1");
Self(MontgomeryReprData::Large(MontgomeryLargeRepr::new(m)))
}
}
}
#[inline]
pub(crate) fn data(&self) -> &MontgomeryReprData {
&self.0
}
}
impl MontgomeryLargeRepr {
fn new(m: UBig) -> Self {
let words = m.as_words();
let s = words.len();
debug_assert!(s >= 3 && words[0] & 1 == 1);
let modulus = Buffer::from(words).into_boxed_slice();
let n0_dword = neginv_dword(double_word(modulus[0], modulus[1]));
debug_assert_eq!(
double_word(modulus[0], modulus[1])
.wrapping_mul(n0_dword)
.wrapping_add(1),
0,
"n0_dword is not the negated modular inverse of the modulus's low double word"
);
let r = UBig::ONE << (s * WORD_BITS_USIZE);
let r2_mod_m = (&r * &r) % &m;
Self {
modulus,
n0_dword,
r2_mod_m: to_exact_words(&r2_mod_m, s),
}
}
#[inline]
pub(crate) fn n0_word(&self) -> Word {
split_dword(self.n0_dword).0
}
}
pub(crate) fn to_exact_words(u: &UBig, s: usize) -> Box<[Word]> {
let words = u.as_words();
debug_assert!(words.len() <= s);
let mut buffer = Buffer::allocate_exact(s);
buffer.push_slice(words);
buffer.push_zeros(s - words.len());
buffer.into_boxed_slice()
}
const fn neginv_dword(m: DoubleWord) -> DoubleWord {
let two: DoubleWord = 2;
let mut i: DoubleWord = 1; let mut correct_bits: u32 = 1;
while correct_bits < DWORD_BITS {
i = i.wrapping_mul(two.wrapping_sub(m.wrapping_mul(i)));
correct_bits <<= 1;
}
i.wrapping_neg()
}
impl<'a> Montgomery<'a> {
#[inline]
pub(crate) fn repr(&self) -> &MontgomeryInner<'a> {
&self.0
}
#[inline]
pub(crate) fn repr_mut(&mut self) -> &mut MontgomeryInner<'a> {
&mut self.0
}
#[inline]
pub(crate) fn into_repr(self) -> MontgomeryInner<'a> {
self.0
}
#[inline]
pub(crate) const fn from_single(raw: Word, ring: &'a MontgomerySingleRepr) -> Self {
Montgomery(MontgomeryInner::Single(raw, ring))
}
#[inline]
pub(crate) const fn from_double(raw: DoubleWord, ring: &'a MontgomeryDoubleRepr) -> Self {
Montgomery(MontgomeryInner::Double(raw, ring))
}
#[inline]
pub(crate) fn from_large(raw: MontgomeryLargeVal, ring: &'a MontgomeryLargeRepr) -> Self {
debug_assert!(raw.is_valid(ring));
Montgomery(MontgomeryInner::Large(raw, ring))
}
#[inline]
pub(crate) fn check_same_ring_single(lhs: &MontgomerySingleRepr, rhs: &MontgomerySingleRepr) {
if !ptr::eq(lhs, rhs) {
panic_different_rings();
}
}
#[inline]
pub(crate) fn check_same_ring_double(lhs: &MontgomeryDoubleRepr, rhs: &MontgomeryDoubleRepr) {
if !ptr::eq(lhs, rhs) {
panic_different_rings();
}
}
#[inline]
pub(crate) fn check_same_ring_large(lhs: &MontgomeryLargeRepr, rhs: &MontgomeryLargeRepr) {
if !ptr::eq(lhs, rhs) {
panic_different_rings();
}
}
}
impl MontgomeryLargeVal {
pub(crate) fn one(ring: &MontgomeryLargeRepr) -> Self {
super::mul::one_large(ring)
}
#[inline]
pub(crate) fn is_valid(&self, ring: &MontgomeryLargeRepr) -> bool {
self.0.len() == ring.modulus.len() && cmp::cmp_same_len(&self.0, &ring.modulus).is_lt()
}
}
impl Clone for Montgomery<'_> {
#[inline]
fn clone(&self) -> Self {
Montgomery(self.0.clone())
}
#[inline]
fn clone_from(&mut self, source: &Self) {
self.0.clone_from(&source.0);
}
}
impl Clone for MontgomeryInner<'_> {
#[inline]
fn clone(&self) -> Self {
match self {
MontgomeryInner::Single(raw, ring) => MontgomeryInner::Single(*raw, ring),
MontgomeryInner::Double(raw, ring) => MontgomeryInner::Double(*raw, ring),
MontgomeryInner::Large(raw, ring) => MontgomeryInner::Large(raw.clone(), ring),
}
}
#[inline]
fn clone_from(&mut self, source: &Self) {
if let (MontgomeryInner::Large(raw, ring), MontgomeryInner::Large(src_raw, src_ring)) =
(&mut *self, source)
{
*ring = src_ring;
raw.0.clone_from(&src_raw.0);
} else {
*self = source.clone();
}
}
}
impl PartialEq for Montgomery<'_> {
#[inline]
fn eq(&self, other: &Self) -> bool {
match (self.repr(), other.repr()) {
(MontgomeryInner::Single(raw0, ring0), MontgomeryInner::Single(raw1, ring1)) => {
Montgomery::check_same_ring_single(ring0, ring1);
raw0.eq(raw1)
}
(MontgomeryInner::Double(raw0, ring0), MontgomeryInner::Double(raw1, ring1)) => {
Montgomery::check_same_ring_double(ring0, ring1);
raw0.eq(raw1)
}
(MontgomeryInner::Large(raw0, ring0), MontgomeryInner::Large(raw1, ring1)) => {
Montgomery::check_same_ring_large(ring0, ring1);
raw0.eq(raw1)
}
_ => panic_different_rings(),
}
}
}
impl Eq for Montgomery<'_> {}