1use alloc::vec::Vec;
2use core::mem;
3use core::ops::Shl;
45use crate::big_digit::{self, BigDigit, BigDigits, DoubleBigDigit};
6use crate::biguint::BigUint;
78struct MontyReducer {
9 n0inv: BigDigit,
10}
1112// k0 = -m**-1 mod 2**BITS. Algorithm from: Dumas, J.G. "On Newton–Raphson
13// Iteration for Multiplicative Inverses Modulo Prime Powers".
14fn inv_mod_alt(b: BigDigit) -> BigDigit {
15{
match (&(b & 1), &0) {
(left_val, right_val) => {
if *left_val == *right_val {
let kind = ::core::panicking::AssertKind::Ne;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};assert_ne!(b & 1, 0);
1617let mut k0 = BigDigit::wrapping_sub(2, b);
18let mut t = b - 1;
19let mut i = 1;
20while i < big_digit::BITS {
21 t = t.wrapping_mul(t);
22 k0 = k0.wrapping_mul(t + 1);
2324 i <<= 1;
25 }
26if true {
{
match (&k0.wrapping_mul(b), &1) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};
};debug_assert_eq!(k0.wrapping_mul(b), 1);
27k0.wrapping_neg()
28}
2930impl MontyReducer {
31fn new(n: &BigUint) -> Self {
32let n0inv = inv_mod_alt(n.data[0]);
33MontyReducer { n0inv }
34 }
35}
3637/// Computes z mod m = x * y * 2 ** (-n*_W) mod m
38/// assuming k = -1/m mod 2**_W
39/// See Gueron, "Efficient Software Implementations of Modular Exponentiation".
40/// <https://eprint.iacr.org/2011/239.pdf>
41/// In the terminology of that paper, this is an "Almost Montgomery Multiplication":
42/// x and y are required to satisfy 0 <= z < 2**(n*_W) and then the result
43/// z is guaranteed to satisfy 0 <= z < 2**(n*_W), but it may not be < m.
44#[allow(clippy::many_single_char_names)]
45fn montgomery(x: &BigUint, y: &BigUint, m: &BigUint, k: BigDigit, n: usize) -> BigUint {
46// This code assumes x, y, m are all the same length, n.
47 // (required by addMulVVW and the for loop).
48 // It also assumes that x, y are already reduced mod m,
49 // or else the result will not be properly reduced.
50if !(x.data.len() == n && y.data.len() == n && m.data.len() == n) {
{
::core::panicking::panic_fmt(format_args!("{0:?} {1:?} {2:?} {3}", x,
y, m, n));
}
};assert!(
51 x.data.len() == n && y.data.len() == n && m.data.len() == n,
52"{:?} {:?} {:?} {}",
53 x,
54 y,
55 m,
56 n
57 );
5859let (x, y, m) = (&*x.data, &*y.data, &*m.data);
6061let mut z = ::alloc::vec::from_elem(0, n * 2)vec![0; n * 2];
6263let mut c: BigDigit = 0;
64for i in 0..n {
65let z = &mut z[i..];
66let c2 = add_mul_vvw(&mut z[..n], x, y[i]);
67let t = z[0].wrapping_mul(k);
68let c3 = add_mul_vvw(&mut z[..n], m, t);
69let cx = c.wrapping_add(c2);
70let cy = cx.wrapping_add(c3);
71 z[n] = cy;
72if cx < c2 || cy < c3 {
73 c = 1;
74 } else {
75 c = 0;
76 }
77 }
7879let data = if c == 0 {
80BigDigits::from_slice(&z[n..])
81 } else {
82let (first, second) = z.split_at_mut(n);
83sub_vv(first, second, m);
84BigDigits::from_slice(first)
85 };
86BigUint { data }
87}
8889#[inline(always)]
90fn add_mul_vvw(z: &mut [BigDigit], x: &[BigDigit], y: BigDigit) -> BigDigit {
91let mut c = 0;
92for (zi, xi) in z.iter_mut().zip(x.iter()) {
93let (z1, z0) = mul_add_www(*xi, y, *zi);
94let (c_, zi_) = add_ww(z0, c, 0);
95*zi = zi_;
96 c = c_ + z1;
97 }
9899c100}
101102/// The resulting carry c is either 0 or 1.
103#[inline(always)]
104fn sub_vv(z: &mut [BigDigit], x: &[BigDigit], y: &[BigDigit]) -> BigDigit {
105let mut c = 0;
106for (i, (xi, yi)) in x.iter().zip(y.iter()).enumerate().take(z.len()) {
107let zi = xi.wrapping_sub(*yi).wrapping_sub(c);
108 z[i] = zi;
109// see "Hacker's Delight", section 2-12 (overflow detection)
110c = ((yi & !xi) | ((yi | !xi) & zi)) >> (big_digit::BITS - 1)
111 }
112113c114}
115116/// z1<<_W + z0 = x+y+c, with c == 0 or 1
117#[inline(always)]
118fn add_ww(x: BigDigit, y: BigDigit, c: BigDigit) -> (BigDigit, BigDigit) {
119let yc = y.wrapping_add(c);
120let z0 = x.wrapping_add(yc);
121let z1 = if z0 < x || yc < y { 1 } else { 0 };
122123 (z1, z0)
124}
125126/// z1 << _W + z0 = x * y + c
127#[inline(always)]
128fn mul_add_www(x: BigDigit, y: BigDigit, c: BigDigit) -> (BigDigit, BigDigit) {
129let z = xas DoubleBigDigit * yas DoubleBigDigit + cas DoubleBigDigit;
130 ((z >> big_digit::BITS) as BigDigit, zas BigDigit)
131}
132133/// Calculates x ** y mod m using a fixed, 4-bit window.
134#[allow(clippy::many_single_char_names)]
135pub(super) fn monty_modpow(x: &BigUint, y: &BigUint, m: &BigUint) -> BigUint {
136if !(m.data[0] & 1 == 1) {
::core::panicking::panic("assertion failed: m.data[0] & 1 == 1")
};assert!(m.data[0] & 1 == 1);
137let mr = MontyReducer::new(m);
138let num_words = m.data.len();
139140let mut x = x.clone();
141142// We want the lengths of x and m to be equal.
143 // It is OK if x >= m as long as len(x) == len(m).
144if x.data.len() > num_words {
145x %= m;
146// Note: now len(x) <= numWords, not guaranteed ==.
147}
148if x.data.len() < num_words {
149x.data.resize(num_words, 0);
150 }
151152// rr = 2**(2*_W*len(m)) mod m
153let mut rr = BigUint::ONE;
154rr = (rr.shl(2 * num_wordsas u64 * u64::from(big_digit::BITS))) % m;
155if rr.data.len() < num_words {
156rr.data.resize(num_words, 0);
157 }
158// one = 1, with equal length to that of m
159let mut one = BigUint::ONE;
160one.data.resize(num_words, 0);
161162let n = 4;
163// powers[i] contains x^i
164let mut powers = Vec::with_capacity(1 << n);
165powers.push(montgomery(&one, &rr, m, mr.n0inv, num_words));
166powers.push(montgomery(&x, &rr, m, mr.n0inv, num_words));
167for i in 2..1 << n {
168let r = montgomery(&powers[i - 1], &powers[1], m, mr.n0inv, num_words);
169 powers.push(r);
170 }
171172// initialize z = 1 (Montgomery 1)
173let mut z = powers[0].clone();
174z.data.resize(num_words, 0);
175let mut zz = BigUint::ZERO;
176zz.data.resize(num_words, 0);
177178// same windowed exponent, but with Montgomery multiplications
179let y = &*y.data;
180for i in (0..y.len()).rev() {
181let mut yi = y[i];
182let mut j = 0;
183while j < big_digit::BITS {
184if i != y.len() - 1 || j != 0 {
185 zz = montgomery(&z, &z, m, mr.n0inv, num_words);
186 z = montgomery(&zz, &zz, m, mr.n0inv, num_words);
187 zz = montgomery(&z, &z, m, mr.n0inv, num_words);
188 z = montgomery(&zz, &zz, m, mr.n0inv, num_words);
189 }
190 zz = montgomery(
191&z,
192&powers[(yi >> (big_digit::BITS - n)) as usize],
193 m,
194 mr.n0inv,
195 num_words,
196 );
197 mem::swap(&mut z, &mut zz);
198 yi <<= n;
199 j += n;
200 }
201 }
202203// convert to regular number
204zz = montgomery(&z, &one, m, mr.n0inv, num_words);
205206zz.data.normalize();
207// One last reduction, just in case.
208 // See golang.org/issue/13907.
209if zz >= *m {
210// Common case is m has high bit set; in that case,
211 // since zz is the same length as m, there can be just
212 // one multiple of m to remove. Just subtract.
213 // We think that the subtract should be sufficient in general,
214 // so do that unconditionally, but double-check,
215 // in case our beliefs are wrong.
216 // The div is not expected to be reached.
217zz -= m;
218if zz >= *m {
219zz %= m;
220 }
221 }
222223zz.data.normalize();
224zz225}