Skip to content

Commit f163fdd

Browse files
committed
Actually compute binomial4
1 parent a27e98b commit f163fdd

File tree

1 file changed

+37
-23
lines changed

1 file changed

+37
-23
lines changed

ext/crates/fp/src/prime.rs

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -256,30 +256,51 @@ pub fn binomial(p : ValidPrime, n : i32, k : i32) -> u32 {
256256
}
257257

258258
/// Binomial coefficients mod 4 up to a sign (we always return 0, 1 or 2)
259-
/// This uses the fact that v_2(n choose r) is the number of borrows performed when subtracting r
260-
/// from n
261-
pub fn binomial4(n: u32, k: u32) -> u32 {
262-
if (n - k) & k == 0 {
263-
return 1
259+
/// This uses the algorithm from https://www.fq.math.ca/Scanned/29-1/davis.pdf
260+
pub fn binomial4(n: u32, j: u32) -> u32 {
261+
if n < 2 {
262+
return 1;
263+
}
264+
if (n - j) & j == 0 {
265+
// Answer is odd
266+
let k = 32 - n.leading_zeros() - 1;
267+
let l = n - (1 << k);
268+
if l < (1 << (k - 1)) {
269+
if j <= l {
270+
binomial4(l, j)
271+
} else {
272+
binomial4(l, j - (1 << k))
273+
}
274+
} else {
275+
if j < (1 << (k - 1)) {
276+
binomial4(l, j)
277+
} else if j <= l {
278+
(binomial4(l, j) + 2 * binomial2(l as i32, (j - (1 << k - 1)) as i32)) % 4
279+
} else if j <= l + (1 << k) {
280+
(2 * binomial2(l as i32, (j - (1 << k - 1)) as i32) + binomial4(l, j - (1 << k))) % 4
281+
} else {
282+
binomial4(l, j - (1 << k))
283+
}
284+
}
264285
} else {
265286
// 1 at the first borrow position
266-
let fb = 1 << ((n - k) & k).trailing_zeros();
287+
let fb = 1 << ((n - j) & j).trailing_zeros();
267288
if n & (fb << 1) == 0 {
268289
// This borrow requires a further borrow on the left
269290
return 0;
270-
} else if k & (fb << 1) != 0 {
271-
// In n there is a 1 to the left, but there is a 1 in k as well, so we need to borrow
291+
} else if j & (fb << 1) != 0 {
292+
// In n there is a 1 to the left, but there is a 1 in j as well, so we need to borrow
272293
// once more
273294
return 0;
274-
} else {
275-
// Remove the digit where we need to borrow. Check there is no further need to borrow
276-
let k2 = k ^ fb;
277-
if (n - k2) & k2 == 0 {
295+
} else {
296+
// Remove the digit where we need to borrow. Checj there is no further need to borrow
297+
let j2 = j ^ fb;
298+
if (n - j2) & j2 == 0 {
278299
return 2;
279300
} else {
280301
return 0;
281302
}
282-
}
303+
}
283304
}
284305
}
285306

@@ -290,9 +311,8 @@ pub fn multinomial4(l: &[u32]) -> u32 {
290311
let sum = l.iter().sum();
291312
match binomial4(sum, l[0]) {
292313
0 => 0,
293-
1 => multinomial4(&l[1..]),
294314
2 => 2 * multinomial2(&l[1..]),
295-
_ => unreachable!(),
315+
x => (x * multinomial4(&l[1..])) % 4,
296316
}
297317
}
298318
}
@@ -449,19 +469,13 @@ mod tests {
449469
}
450470

451471
#[test]
452-
fn test_binomial4() {
472+
fn binomial4_cmp() {
453473
for n in 0 .. 12 {
454474
for j in 0 ..= (n + 1) / 2 {
455-
let mut ans = match binomial_full(n, j) % 4 {
456-
1 | 3 => 1,
457-
2 => 2,
458-
_ => 0
459-
};
460-
assert_eq!(binomial4(n, j), ans);
475+
assert_eq!(binomial4(n, j), binomial_full(n, j) % 4);
461476
}
462477
}
463478
}
464-
465479
}
466480

467481
pub const PRIMES: [u32; NUM_PRIMES] = [2, 3, 5, 7, 11, 13, 17, 19];

0 commit comments

Comments
 (0)