Skip to content

Commit ec0a2b2

Browse files
committed
[WIP] 32-bit support
1 parent b78e966 commit ec0a2b2

1 file changed

Lines changed: 53 additions & 28 deletions

File tree

src/modular/bernstein_yang.rs

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
99
#![allow(clippy::needless_range_loop)]
1010

11+
use crate::Word;
12+
1113
/// Type of the modular multiplicative inverter based on the Bernstein-Yang method.
1214
/// The inverter can be created for a specified modulus M and adjusting parameter A
1315
/// to compute the adjusted multiplicative inverses of positive integers, i.e. for
@@ -48,19 +50,20 @@ type Matrix = [[i64; 2]; 2];
4850

4951
impl<const L: usize> BernsteinYangInverter<L> {
5052
/// Creates the inverter for specified modulus and adjusting parameter
51-
pub const fn new(modulus: &[u64], adjuster: &[u64]) -> Self {
53+
#[allow(trivial_numeric_casts)]
54+
pub const fn new(modulus: &[Word], adjuster: &[Word]) -> Self {
5255
Self {
53-
modulus: CInt::<62, L>(Self::convert::<64, 62, L>(modulus)),
54-
adjuster: CInt::<62, L>(Self::convert::<64, 62, L>(adjuster)),
55-
inverse: Self::inv(modulus[0]),
56+
modulus: CInt::<62, L>(convert_in::<{ Word::BITS as usize }, 62, L>(modulus)),
57+
adjuster: CInt::<62, L>(convert_in::<{ Word::BITS as usize }, 62, L>(adjuster)),
58+
inverse: Self::inv(modulus[0] as u64),
5659
}
5760
}
5861

5962
/// Returns either the adjusted modular multiplicative inverse for the argument or None
6063
/// depending on invertibility of the argument, i.e. its coprimality with the modulus
61-
pub const fn invert<const S: usize>(&self, value: &[u64]) -> Option<[u64; S]> {
64+
pub const fn invert<const S: usize>(&self, value: &[Word]) -> Option<[Word; S]> {
6265
let (mut d, mut e) = (CInt::ZERO, self.adjuster);
63-
let mut g = CInt::<62, L>(Self::convert::<64, 62, L>(value));
66+
let mut g = CInt::<62, L>(convert_in::<{ Word::BITS as usize }, 62, L>(value));
6467
let (mut delta, mut f) = (1, self.modulus);
6568
let mut matrix;
6669

@@ -76,7 +79,9 @@ impl<const L: usize> BernsteinYangInverter<L> {
7679
if !f.eq(&CInt::ONE) && !antiunit {
7780
return None;
7881
}
79-
Some(Self::convert::<62, 64, S>(&self.norm(d, antiunit).0))
82+
Some(convert_out::<62, { Word::BITS as usize }, S>(
83+
&self.norm(d, antiunit).0,
84+
))
8085
}
8186

8287
/// Returns the Bernstein-Yang transition matrix multiplied by 2^62 and the new value
@@ -182,10 +187,26 @@ impl<const L: usize> BernsteinYangInverter<L> {
182187
value
183188
}
184189

185-
/// Returns a big unsigned integer as an array of O-bit chunks, which is equal modulo
186-
/// 2 ^ (O * S) to the input big unsigned integer stored as an array of I-bit chunks.
187-
/// The ordering of the chunks in these arrays is little-endian
188-
const fn convert<const I: usize, const O: usize, const S: usize>(input: &[u64]) -> [u64; S] {
190+
/// Returns the multiplicative inverse of the argument modulo 2^62. The implementation is based
191+
/// on the Hurchalla's method for computing the multiplicative inverse modulo a power of two.
192+
/// For better understanding the implementation, the following paper is recommended:
193+
/// J. Hurchalla, "An Improved Integer Multiplicative Inverse (modulo 2^w)",
194+
/// https://arxiv.org/pdf/2204.04342.pdf
195+
const fn inv(value: u64) -> i64 {
196+
let x = value.wrapping_mul(3) ^ 2;
197+
let y = 1u64.wrapping_sub(x.wrapping_mul(value));
198+
let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
199+
let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
200+
let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
201+
(x.wrapping_mul(y.wrapping_add(1)) & CInt::<62, L>::MASK) as i64
202+
}
203+
}
204+
205+
/// Write an impl of a `convert_*` function.
206+
///
207+
/// Workaround for making this function generic while still allowing it to be `const fn`.
208+
macro_rules! impl_convert {
209+
($input_type:ty, $output_type:ty, $input:expr) => {{
189210
// This function is defined because the method "min" of the usize type is not constant
190211
const fn min(a: usize, b: usize) -> usize {
191212
if a > b {
@@ -195,15 +216,17 @@ impl<const L: usize> BernsteinYangInverter<L> {
195216
}
196217
}
197218

198-
let (total, mut output, mut bits) = (min(input.len() * I, S * O), [0; S], 0);
219+
let total = min($input.len() * I, S * O);
220+
let mut output = [0 as $output_type; S];
221+
let mut bits = 0;
199222

200223
while bits < total {
201224
let (i, o) = (bits % I, bits % O);
202-
output[bits / O] |= (input[bits / I] >> i) << o;
225+
output[bits / O] |= ($input[bits / I] >> i) as $output_type << o;
203226
bits += min(I - i, O - o);
204227
}
205228

206-
let mask = u64::MAX >> (64 - O);
229+
let mask = (<$output_type>::MAX as $output_type) >> (<$output_type>::BITS as usize - O);
207230
let mut filled = total / O + if total % O > 0 { 1 } else { 0 };
208231

209232
while filled > 0 {
@@ -212,21 +235,23 @@ impl<const L: usize> BernsteinYangInverter<L> {
212235
}
213236

214237
output
215-
}
238+
}};
239+
}
216240

217-
/// Returns the multiplicative inverse of the argument modulo 2^62. The implementation is based
218-
/// on the Hurchalla's method for computing the multiplicative inverse modulo a power of two.
219-
/// For better understanding the implementation, the following paper is recommended:
220-
/// J. Hurchalla, "An Improved Integer Multiplicative Inverse (modulo 2^w)",
221-
/// https://arxiv.org/pdf/2204.04342.pdf
222-
const fn inv(value: u64) -> i64 {
223-
let x = value.wrapping_mul(3) ^ 2;
224-
let y = 1u64.wrapping_sub(x.wrapping_mul(value));
225-
let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
226-
let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
227-
let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
228-
(x.wrapping_mul(y.wrapping_add(1)) & CInt::<62, L>::MASK) as i64
229-
}
241+
/// Returns a big unsigned integer as an array of O-bit chunks, which is equal modulo
242+
/// 2 ^ (O * S) to the input big unsigned integer stored as an array of I-bit chunks.
243+
/// The ordering of the chunks in these arrays is little-endian
244+
#[allow(trivial_numeric_casts)]
245+
const fn convert_in<const I: usize, const O: usize, const S: usize>(input: &[Word]) -> [u64; S] {
246+
impl_convert!(Word, u64, input)
247+
}
248+
249+
/// Returns a big unsigned integer as an array of O-bit chunks, which is equal modulo
250+
/// 2 ^ (O * S) to the input big unsigned integer stored as an array of I-bit chunks.
251+
/// The ordering of the chunks in these arrays is little-endian
252+
#[allow(trivial_numeric_casts)]
253+
const fn convert_out<const I: usize, const O: usize, const S: usize>(input: &[u64]) -> [Word; S] {
254+
impl_convert!(u64, Word, input)
230255
}
231256

232257
/// Big signed (B * L)-bit integer type, whose variables store

0 commit comments

Comments
 (0)