Skip to content

Commit 3e571c3

Browse files
authored
Bernstein-Yang: minor refactoring (#457)
Extracts `from_uint` and `to_uint` methods onto `Uint62L`, rather than the previous `sat_to_unsat` and `unsat_to_sat` free functions. Also extracts the `impl_limb_convert!` macro into its own module, which will assist impl'ing a `BoxedUint62L`.
1 parent aafb7b1 commit 3e571c3

File tree

2 files changed

+91
-69
lines changed

2 files changed

+91
-69
lines changed

src/modular/bernstein_yang.rs

Lines changed: 54 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
#![allow(clippy::needless_range_loop)]
1212

13+
#[macro_use]
14+
mod macros;
15+
1316
use crate::{ConstChoice, Inverter, Limb, Uint, Word};
1417
use subtle::CtOption;
1518

@@ -59,13 +62,9 @@ impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize>
5962
/// Modulus must be odd. Returns `ConstChoice::FALSE` if it is not.
6063
#[allow(trivial_numeric_casts)]
6164
pub const fn new(modulus: &Uint<SAT_LIMBS>, adjuster: &Uint<SAT_LIMBS>) -> (Self, ConstChoice) {
62-
if UNSAT_LIMBS != bernstein_yang_nlimbs!(SAT_LIMBS * Limb::BITS as usize) {
63-
panic!("BernsteinYangInverter has incorrect number of limbs");
64-
}
65-
6665
let ret = Self {
67-
modulus: Uint62L::<UNSAT_LIMBS>(sat_to_unsat::<UNSAT_LIMBS>(modulus.as_words())),
68-
adjuster: Uint62L::<UNSAT_LIMBS>(sat_to_unsat::<UNSAT_LIMBS>(adjuster.as_words())),
66+
modulus: Uint62L::from_uint(modulus),
67+
adjuster: Uint62L::from_uint(adjuster),
6968
inverse: inv_mod2_62(modulus.as_words()),
7069
};
7170

@@ -76,7 +75,7 @@ impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize>
7675
/// depending on invertibility of the argument, i.e. its coprimality with the modulus
7776
pub const fn inv(&self, value: &Uint<SAT_LIMBS>) -> (Uint<SAT_LIMBS>, ConstChoice) {
7877
let (mut d, mut e) = (Uint62L::ZERO, self.adjuster);
79-
let mut g = Uint62L::<UNSAT_LIMBS>(sat_to_unsat::<UNSAT_LIMBS>(value.as_words()));
78+
let mut g = Uint62L::from_uint(value);
8079
let (mut delta, mut f) = (1, self.modulus);
8180
let mut matrix;
8281

@@ -89,9 +88,9 @@ impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize>
8988
// of the integer to be inverted and the modulus the inverter was created for.
9089
// Thus, if "f" is neither 1 nor -1, then the sought inverse does not exist
9190
let antiunit = f.eq(&Uint62L::MINUS_ONE);
92-
let words = unsat_to_sat::<SAT_LIMBS>(&self.norm(d, antiunit).0);
91+
let ret = self.norm(d, antiunit);
9392
let is_some = ConstChoice::from_word_lsb((f.eq(&Uint62L::ONE) || antiunit) as Word);
94-
(Uint::from_words(words), is_some)
93+
(ret.to_uint(), is_some)
9594
}
9695

9796
/// Returns the Bernstein-Yang transition matrix multiplied by 2^62 and the new value
@@ -249,66 +248,6 @@ const fn inv_mod2_62(value: &[Word]) -> i64 {
249248
(x.wrapping_mul(y.wrapping_add(1)) & (u64::MAX >> 2)) as i64
250249
}
251250

252-
/// Write an impl of a limb conversion function.
253-
///
254-
/// Workaround for making this function generic around limb types while still allowing it to be `const fn`.
255-
macro_rules! impl_limb_convert {
256-
($input_type:ty, $input_bits:expr, $output_type:ty, $output_bits:expr, $output_size:expr, $input:expr) => {{
257-
// This function is defined because the method "min" of the usize type is not constant
258-
const fn min(a: usize, b: usize) -> usize {
259-
if a > b {
260-
b
261-
} else {
262-
a
263-
}
264-
}
265-
266-
let total = min($input.len() * $input_bits, $output_size * $output_bits);
267-
let mut output = [0 as $output_type; $output_size];
268-
let mut bits = 0;
269-
270-
while bits < total {
271-
let (i, o) = (bits % $input_bits, bits % $output_bits);
272-
output[bits / $output_bits] |= ($input[bits / $input_bits] >> i) as $output_type << o;
273-
bits += min($input_bits - i, $output_bits - o);
274-
}
275-
276-
let mask = (<$output_type>::MAX as $output_type) >> (<$output_type>::BITS as usize - $output_bits);
277-
let mut filled = total / $output_bits + if total % $output_bits > 0 { 1 } else { 0 };
278-
279-
while filled > 0 {
280-
filled -= 1;
281-
output[filled] &= mask;
282-
}
283-
284-
output
285-
}};
286-
}
287-
288-
/// Convert from 64-bit saturated representation used by `Uint` to the 62-bit unsaturated representation used by
289-
/// `Uint62`.
290-
///
291-
/// Returns a big unsigned integer as an array of 62-bit chunks, which is equal modulo 2 ^ (62 * S) to the input big
292-
/// unsigned integer stored as an array of 64-bit chunks.
293-
///
294-
/// The ordering of the chunks in these arrays is little-endian.
295-
#[allow(trivial_numeric_casts)]
296-
const fn sat_to_unsat<const S: usize>(input: &[Word]) -> [u64; S] {
297-
impl_limb_convert!(Word, Word::BITS as usize, u64, 62, S, input)
298-
}
299-
300-
/// Convert from 62-bit unsaturated representation used by `Uint62` to the 64-bit saturated representation used by
301-
/// `Uint`.
302-
///
303-
/// Returns a big unsigned integer as an array of 64-bit chunks, which is equal modulo 2 ^ (64 * S) to the input big
304-
/// unsigned integer stored as an array of 62-bit chunks.
305-
///
306-
/// The ordering of the chunks in these arrays is little-endian
307-
#[allow(trivial_numeric_casts)]
308-
const fn unsat_to_sat<const S: usize>(input: &[u64]) -> [Word; S] {
309-
impl_limb_convert!(u64, 62, Word, Word::BITS as usize, S, input)
310-
}
311-
312251
/// `Uint`-like (62 * LIMBS)-bit integer type, whose variables store numbers in the two's complement code as arrays of
313252
/// 62-bit limbs.
314253
///
@@ -338,6 +277,52 @@ impl<const LIMBS: usize> Uint62L<LIMBS> {
338277
ret
339278
};
340279

280+
/// Convert from 64-bit saturated representation used by `Uint` to the 62-bit unsaturated representation used by
281+
/// `Uint62`.
282+
///
283+
/// Returns a big unsigned integer as an array of 62-bit chunks, which is equal modulo 2 ^ (62 * S) to the input big
284+
/// unsigned integer stored as an array of 64-bit chunks.
285+
///
286+
/// The ordering of the chunks in these arrays is little-endian.
287+
#[allow(trivial_numeric_casts)]
288+
pub const fn from_uint<const SAT_LIMBS: usize>(input: &Uint<SAT_LIMBS>) -> Self {
289+
if LIMBS != bernstein_yang_nlimbs!(SAT_LIMBS * Limb::BITS as usize) {
290+
panic!("incorrect number of limbs");
291+
}
292+
293+
Self(impl_limb_convert!(
294+
Word,
295+
Word::BITS as usize,
296+
u64,
297+
62,
298+
LIMBS,
299+
input.as_words()
300+
))
301+
}
302+
303+
/// Convert from 62-bit unsaturated representation used by `Uint62` to the 64-bit saturated representation used by
304+
/// `Uint`.
305+
///
306+
/// Returns a big unsigned integer as an array of 64-bit chunks, which is equal modulo 2 ^ (64 * S) to the input big
307+
/// unsigned integer stored as an array of 62-bit chunks.
308+
///
309+
/// The ordering of the chunks in these arrays is little-endian
310+
#[allow(trivial_numeric_casts, clippy::wrong_self_convention)]
311+
pub const fn to_uint<const SAT_LIMBS: usize>(&self) -> Uint<SAT_LIMBS> {
312+
if LIMBS != bernstein_yang_nlimbs!(SAT_LIMBS * Limb::BITS as usize) {
313+
panic!("incorrect number of limbs");
314+
}
315+
316+
Uint::from_words(impl_limb_convert!(
317+
u64,
318+
62,
319+
Word,
320+
Word::BITS as usize,
321+
SAT_LIMBS,
322+
&self.0
323+
))
324+
}
325+
341326
/// Returns the result of applying 62-bit right arithmetical shift to the current number.
342327
pub const fn shift(&self) -> Self {
343328
let mut ret = Self::ZERO;

src/modular/bernstein_yang/macros.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//! Bernstein-Yang macros.
2+
3+
/// Write an impl of a limb conversion function.
4+
///
5+
/// Workaround for making this function generic around limb types while still allowing it to be `const fn`.
6+
macro_rules! impl_limb_convert {
7+
($input_type:ty, $input_bits:expr, $output_type:ty, $output_bits:expr, $output_size:expr, $input:expr) => {{
8+
// This function is defined because the method "min" of the usize type is not constant
9+
const fn min(a: usize, b: usize) -> usize {
10+
if a > b {
11+
b
12+
} else {
13+
a
14+
}
15+
}
16+
17+
let total = min($input.len() * $input_bits, $output_size * $output_bits);
18+
let mut output = [0 as $output_type; $output_size];
19+
let mut bits = 0;
20+
21+
while bits < total {
22+
let (i, o) = (bits % $input_bits, bits % $output_bits);
23+
output[bits / $output_bits] |= ($input[bits / $input_bits] >> i) as $output_type << o;
24+
bits += min($input_bits - i, $output_bits - o);
25+
}
26+
27+
let mask = (<$output_type>::MAX as $output_type) >> (<$output_type>::BITS as usize - $output_bits);
28+
let mut filled = total / $output_bits + if total % $output_bits > 0 { 1 } else { 0 };
29+
30+
while filled > 0 {
31+
filled -= 1;
32+
output[filled] &= mask;
33+
}
34+
35+
output
36+
}};
37+
}

0 commit comments

Comments
 (0)