Skip to content

Commit 336dda1

Browse files
authored
Refactor Bernstein-Yang implementation(s) (#495)
- Extracts a `divsteps` function in both the stack-allocated and boxed implementations which can be reused for both inversions and GCD - Changes the boxed implementation to use more in-place operations
1 parent 17ae741 commit 336dda1

File tree

2 files changed

+74
-57
lines changed

2 files changed

+74
-57
lines changed

src/modular/bernstein_yang.rs

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,13 @@ impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize>
7575
/// Returns either the adjusted modular multiplicative inverse for the argument or `None`
7676
/// depending on invertibility of the argument, i.e. its coprimality with the modulus
7777
pub const fn inv(&self, value: &Uint<SAT_LIMBS>) -> ConstCtOption<Uint<SAT_LIMBS>> {
78-
let mut d = Int62L::ZERO;
79-
let mut e = self.adjuster;
80-
let mut f = self.modulus;
81-
let mut g = Int62L::from_uint(value);
82-
let mut delta = 1;
83-
let mut matrix;
84-
85-
while !g.eq(&Int62L::ZERO) {
86-
(delta, matrix) = jump(&f.0, &g.0, delta);
87-
(f, g) = fg(f, g, matrix);
88-
(d, e) = de(&self.modulus, self.inverse, d, e, matrix);
89-
}
78+
let (d, f) = divsteps(
79+
self.adjuster,
80+
self.modulus,
81+
Int62L::from_uint(value),
82+
self.inverse,
83+
);
84+
9085
// At this point the absolute value of "f" equals the greatest common divisor of the
9186
// integer to be inverted and the modulus the inverter was created for.
9287
// Thus, if "f" is neither 1 nor -1, then the sought inverse does not exist.
@@ -102,21 +97,11 @@ impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize>
10297
/// `UNSAT_LIMBS` which are computed when defining `PrecomputeInverter::Inverter` for various
10398
/// `Uint` limb sizes.
10499
pub(crate) const fn gcd(f: &Uint<SAT_LIMBS>, g: &Uint<SAT_LIMBS>) -> Uint<SAT_LIMBS> {
105-
let f_0 = Int62L::<UNSAT_LIMBS>::from_uint(f);
106100
let inverse = inv_mod2_62(f.as_words());
107-
108-
let mut d = Int62L::ZERO;
109-
let mut e = Int62L::ONE;
110-
let mut f = f_0;
111-
let mut g = Int62L::from_uint(g);
112-
let mut delta = 1;
113-
let mut matrix;
114-
115-
while !g.eq(&Int62L::ZERO) {
116-
(delta, matrix) = jump(&f.0, &g.0, delta);
117-
(f, g) = fg(f, g, matrix);
118-
(d, e) = de(&f_0, inverse, d, e, matrix);
119-
}
101+
let e = Int62L::<UNSAT_LIMBS>::ONE;
102+
let f = Int62L::from_uint(f);
103+
let g = Int62L::from_uint(g);
104+
let (_, mut f) = divsteps(e, f, g, inverse);
120105

121106
if f.is_negative() {
122107
f = f.neg();
@@ -188,6 +173,28 @@ const fn inv_mod2_62(value: &[Word]) -> i64 {
188173
(x.wrapping_mul(y.wrapping_add(1)) & (u64::MAX >> 2)) as i64
189174
}
190175

176+
/// Algorithm `divsteps2` to compute (δₙ, fₙ, gₙ) = divstepⁿ(δ, f, g) as described in Figure 10.1
177+
/// of <https://eprint.iacr.org/2019/266.pdf>.
178+
const fn divsteps<const LIMBS: usize>(
179+
mut e: Int62L<LIMBS>,
180+
f_0: Int62L<LIMBS>,
181+
mut g: Int62L<LIMBS>,
182+
inverse: i64,
183+
) -> (Int62L<LIMBS>, Int62L<LIMBS>) {
184+
let mut d = Int62L::ZERO;
185+
let mut f = f_0;
186+
let mut delta = 1;
187+
let mut matrix;
188+
189+
while !g.eq(&Int62L::ZERO) {
190+
(delta, matrix) = jump(&f.0, &g.0, delta);
191+
(f, g) = fg(f, g, matrix);
192+
(d, e) = de(&f_0, inverse, matrix, d, e);
193+
}
194+
195+
(d, f)
196+
}
197+
191198
/// Returns the Bernstein-Yang transition matrix multiplied by 2^62 and the new value of the
192199
/// delta variable for the 62 basic steps of the Bernstein-Yang method, which are to be
193200
/// performed sequentially for specified initial values of f, g and delta
@@ -252,9 +259,9 @@ const fn fg<const LIMBS: usize>(
252259
const fn de<const LIMBS: usize>(
253260
modulus: &Int62L<LIMBS>,
254261
inverse: i64,
262+
t: Matrix,
255263
d: Int62L<LIMBS>,
256264
e: Int62L<LIMBS>,
257-
t: Matrix,
258265
) -> (Int62L<LIMBS>, Int62L<LIMBS>) {
259266
let mask = Int62L::<LIMBS>::MASK as i64;
260267
let mut md = t[0][0] * d.is_negative() as i64 + t[0][1] * e.is_negative() as i64;

src/modular/bernstein_yang/boxed.rs

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,9 @@ impl Inverter for BoxedBernsteinYangInverter {
6060

6161
fn invert(&self, value: &BoxedUint) -> CtOption<Self::Output> {
6262
let mut d = BoxedInt62L::zero(self.modulus.0.len());
63-
let mut e = self.adjuster.clone();
64-
let mut f = self.modulus.clone();
6563
let mut g = BoxedInt62L::from(value).widen(d.0.len());
64+
let f = divsteps(&mut d, &self.adjuster, &self.modulus, &mut g, self.inverse);
6665

67-
debug_assert_eq!(g.0.len(), self.modulus.0.len());
68-
69-
let mut delta = 1;
70-
let mut matrix;
71-
72-
while !g.is_zero() {
73-
(delta, matrix) = jump(&f.0, &g.0, delta);
74-
(f, g) = fg(f, g, matrix);
75-
(d, e) = de(&self.modulus, self.inverse, d, e, matrix);
76-
}
7766
// At this point the absolute value of "f" equals the greatest common divisor of the
7867
// integer to be inverted and the modulus the inverter was created for.
7968
// Thus, if "f" is neither 1 nor -1, then the sought inverse does not exist.
@@ -85,28 +74,48 @@ impl Inverter for BoxedBernsteinYangInverter {
8574
}
8675
}
8776

77+
/// Algorithm `divsteps2` to compute (δₙ, fₙ, gₙ) = divstepⁿ(δ, f, g) as described in Figure 10.1
78+
/// of <https://eprint.iacr.org/2019/266.pdf>.
79+
fn divsteps(
80+
d: &mut BoxedInt62L,
81+
e: &BoxedInt62L,
82+
f_0: &BoxedInt62L,
83+
g: &mut BoxedInt62L,
84+
inverse: i64,
85+
) -> BoxedInt62L {
86+
debug_assert_eq!(f_0.0.len(), g.0.len());
87+
88+
let mut e = e.clone();
89+
let mut f = f_0.clone();
90+
let mut delta = 1;
91+
let mut matrix;
92+
93+
while !g.is_zero() {
94+
(delta, matrix) = jump(&f.0, &g.0, delta);
95+
fg(&mut f, g, matrix);
96+
de(f_0, inverse, matrix, d, &mut e);
97+
}
98+
99+
f
100+
}
101+
88102
/// Returns the updated values of the variables f and g for specified initial ones and
89103
/// Bernstein-Yang transition matrix multiplied by 2^62. The returned vector is
90104
/// "matrix * (f, g)' / 2^62", where "'" is the transpose operator.
91-
fn fg(f: BoxedInt62L, g: BoxedInt62L, t: Matrix) -> (BoxedInt62L, BoxedInt62L) {
92-
(
93-
f.mul(t[0][0]).add(&g.mul(t[0][1])).shr(),
94-
f.mul(t[1][0]).add(&g.mul(t[1][1])).shr(),
95-
)
105+
fn fg(f: &mut BoxedInt62L, g: &mut BoxedInt62L, t: Matrix) {
106+
// TODO(tarcieri): reduce allocations
107+
let f2 = f.mul(t[0][0]).add(&g.mul(t[0][1])).shr();
108+
let g2 = f.mul(t[1][0]).add(&g.mul(t[1][1])).shr();
109+
*f = f2;
110+
*g = g2;
96111
}
97112

98113
/// Returns the updated values of the variables d and e for specified initial ones and
99114
/// Bernstein-Yang transition matrix multiplied by 2^62. The returned vector is congruent modulo
100115
/// M to "matrix * (d, e)' / 2^62 (mod M)", where M is the modulus the inverter was created for
101116
/// and "'" stands for the transpose operator. Both the input and output values lie in the
102117
/// interval (-2 * M, M).
103-
fn de(
104-
modulus: &BoxedInt62L,
105-
inverse: i64,
106-
d: BoxedInt62L,
107-
e: BoxedInt62L,
108-
t: Matrix,
109-
) -> (BoxedInt62L, BoxedInt62L) {
118+
fn de(modulus: &BoxedInt62L, inverse: i64, t: Matrix, d: &mut BoxedInt62L, e: &mut BoxedInt62L) {
110119
let mask = BoxedInt62L::MASK as i64;
111120
let mut md = t[0][0] * d.is_negative() as i64 + t[0][1] * e.is_negative() as i64;
112121
let mut me = t[1][0] * d.is_negative() as i64 + t[1][1] * e.is_negative() as i64;
@@ -127,7 +136,8 @@ fn de(
127136
let cd = d.mul(t[0][0]).add(&e.mul(t[0][1])).add(&modulus.mul(md));
128137
let ce = d.mul(t[1][0]).add(&e.mul(t[1][1])).add(&modulus.mul(me));
129138

130-
(cd.shr(), ce.shr())
139+
*d = cd.shr();
140+
*e = ce.shr();
131141
}
132142

133143
/// "Bigint"-like (62 * LIMBS)-bit signed integer type, whose variables store numbers in the two's
@@ -402,7 +412,7 @@ mod tests {
402412
.into(),
403413
);
404414
let inverse = 3687945983376704433;
405-
let d = BoxedInt62L(
415+
let mut d = BoxedInt62L(
406416
vec![
407417
3490544662636853909,
408418
2211268325417683828,
@@ -413,7 +423,7 @@ mod tests {
413423
]
414424
.into(),
415425
);
416-
let e = BoxedInt62L(
426+
let mut e = BoxedInt62L(
417427
vec![
418428
4004071259428196451,
419429
1262234674432503659,
@@ -426,9 +436,9 @@ mod tests {
426436
);
427437
let t = [[-45035996273704960, 409827566090715136], [-14, 25]];
428438

429-
let (new_d, new_e) = super::de(&modulus, inverse, d, e, t);
439+
super::de(&modulus, inverse, t, &mut d, &mut e);
430440
assert_eq!(
431-
new_d,
441+
d,
432442
BoxedInt62L(
433443
vec![
434444
1211048314408256470,
@@ -442,7 +452,7 @@ mod tests {
442452
)
443453
);
444454

445-
assert_eq!(new_e, BoxedInt62L(vec![0, 0, 0, 0, 0, 0].into()));
455+
assert_eq!(e, BoxedInt62L(vec![0, 0, 0, 0, 0, 0].into()));
446456
}
447457

448458
#[test]

0 commit comments

Comments
 (0)