Skip to content

Commit a4dd489

Browse files
committed
32-bit support
1 parent b78e966 commit a4dd489

File tree

1 file changed

+66
-28
lines changed

1 file changed

+66
-28
lines changed

src/modular/bernstein_yang.rs

Lines changed: 66 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: inv_mod62(modulus),
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
@@ -181,11 +186,40 @@ impl<const L: usize> BernsteinYangInverter<L> {
181186

182187
value
183188
}
189+
}
190+
191+
/// Returns the multiplicative inverse of the argument modulo 2^62. The implementation is based
192+
/// on the Hurchalla's method for computing the multiplicative inverse modulo a power of two.
193+
/// For better understanding the implementation, the following paper is recommended:
194+
/// J. Hurchalla, "An Improved Integer Multiplicative Inverse (modulo 2^w)",
195+
/// https://arxiv.org/pdf/2204.04342.pdf
196+
const fn inv_mod62(value: &[Word]) -> i64 {
197+
let value = {
198+
#[cfg(target_pointer_width = "32")]
199+
{
200+
debug_assert!(value.len() >= 2);
201+
value[0] as u64 | (value[1] as u64) << 32
202+
}
184203

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] {
204+
#[cfg(target_pointer_width = "64")]
205+
{
206+
value[0]
207+
}
208+
};
209+
210+
let x = value.wrapping_mul(3) ^ 2;
211+
let y = 1u64.wrapping_sub(x.wrapping_mul(value));
212+
let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
213+
let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
214+
let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y));
215+
(x.wrapping_mul(y.wrapping_add(1)) & (u64::MAX >> 2)) as i64
216+
}
217+
218+
/// Write an impl of a `convert_*` function.
219+
///
220+
/// Workaround for making this function generic while still allowing it to be `const fn`.
221+
macro_rules! impl_convert {
222+
($input_type:ty, $output_type:ty, $input:expr) => {{
189223
// This function is defined because the method "min" of the usize type is not constant
190224
const fn min(a: usize, b: usize) -> usize {
191225
if a > b {
@@ -195,15 +229,17 @@ impl<const L: usize> BernsteinYangInverter<L> {
195229
}
196230
}
197231

198-
let (total, mut output, mut bits) = (min(input.len() * I, S * O), [0; S], 0);
232+
let total = min($input.len() * I, S * O);
233+
let mut output = [0 as $output_type; S];
234+
let mut bits = 0;
199235

200236
while bits < total {
201237
let (i, o) = (bits % I, bits % O);
202-
output[bits / O] |= (input[bits / I] >> i) << o;
238+
output[bits / O] |= ($input[bits / I] >> i) as $output_type << o;
203239
bits += min(I - i, O - o);
204240
}
205241

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

209245
while filled > 0 {
@@ -212,21 +248,23 @@ impl<const L: usize> BernsteinYangInverter<L> {
212248
}
213249

214250
output
215-
}
251+
}};
252+
}
216253

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-
}
254+
/// Returns a big unsigned integer as an array of O-bit chunks, which is equal modulo
255+
/// 2 ^ (O * S) to the input big unsigned integer stored as an array of I-bit chunks.
256+
/// The ordering of the chunks in these arrays is little-endian
257+
#[allow(trivial_numeric_casts)]
258+
const fn convert_in<const I: usize, const O: usize, const S: usize>(input: &[Word]) -> [u64; S] {
259+
impl_convert!(Word, u64, input)
260+
}
261+
262+
/// Returns a big unsigned integer as an array of O-bit chunks, which is equal modulo
263+
/// 2 ^ (O * S) to the input big unsigned integer stored as an array of I-bit chunks.
264+
/// The ordering of the chunks in these arrays is little-endian
265+
#[allow(trivial_numeric_casts)]
266+
const fn convert_out<const I: usize, const O: usize, const S: usize>(input: &[u64]) -> [Word; S] {
267+
impl_convert!(u64, Word, input)
230268
}
231269

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

0 commit comments

Comments
 (0)