Skip to content

Commit 0daa8b6

Browse files
j2kuncopybara-github
authored andcommitted
fix primitive root overflow bug
PiperOrigin-RevId: 891818905
1 parent 11568f0 commit 0daa8b6

3 files changed

Lines changed: 40 additions & 10 deletions

File tree

lib/Utils/APIntUtils.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ APInt modularExponentiation(const APInt& base, const APInt& exponent,
6969

7070
while (e.ugt(0)) {
7171
if (e[0]) {
72-
res = (res * b).urem(modulus);
72+
res = modularMultiplication(res, b, modulus);
7373
}
74-
b = (b * b).urem(modulus);
74+
b = modularMultiplication(b, b, modulus);
7575
e = e.lshr(1);
7676
}
7777
return res;
@@ -100,7 +100,7 @@ bool isPrime(const APInt& n) {
100100
if (x.isOne() || x == n - 1) continue;
101101
bool composite = true;
102102
for (unsigned r = 1; r < s; ++r) {
103-
x = (x * x).urem(n);
103+
x = modularMultiplication(x, x, n);
104104
if (x == n - 1) {
105105
composite = false;
106106
break;
@@ -115,8 +115,12 @@ std::vector<APInt> factorize(APInt n) {
115115
std::vector<APInt> factors;
116116
if (n.ult(2)) return factors;
117117

118-
APInt d(n.getBitWidth(), 2);
119-
while ((d * d).ule(n)) {
118+
unsigned width = n.getBitWidth();
119+
APInt d(width, 2);
120+
while (true) {
121+
APInt wide_d = d.zext(width * 2);
122+
if ((wide_d * wide_d).ugt(n.zext(width * 2))) break;
123+
120124
if (n.urem(d).isZero()) {
121125
factors.push_back(d);
122126
while (n.urem(d).isZero()) {

lib/Utils/APIntUtils.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,23 @@
33

44
#include <vector>
55

6+
#include "llvm/include/llvm/ADT/APInt.h" // from @llvm-project
67
#include "mlir/include/mlir/Support/LLVM.h" // from @llvm-project
78

89
namespace mlir {
910
namespace heir {
1011

1112
APInt multiplicativeInverse(const APInt& x, const APInt& modulo);
1213

14+
inline APInt modularMultiplication(const APInt& a, const APInt& b,
15+
const APInt& modulus) {
16+
unsigned width = modulus.getBitWidth();
17+
APInt wide_a = a.zext(width * 2);
18+
APInt wide_b = b.zext(width * 2);
19+
APInt wide_m = modulus.zext(width * 2);
20+
return (wide_a * wide_b).urem(wide_m).trunc(width);
21+
}
22+
1323
APInt modularExponentiation(const APInt& base, const APInt& exponent,
1424
const APInt& modulus);
1525

lib/Utils/MathUtilsTest.cpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,22 @@ TEST(MathUtilsTest, FindPrimitive2nthRoot) {
5757
}
5858
}
5959

60+
// A regression test for a bug
61+
TEST(MathUtilsTest, FindPrimitive2nthRootZeroRegression) {
62+
// q = 1095233372161, n = 1024
63+
auto root = findPrimitive2nthRoot(APInt(64, 1095233372161), 1024);
64+
ASSERT_TRUE(root.has_value());
65+
APInt q(64, 1095233372161);
66+
APInt two_n_ap(64, 2048);
67+
EXPECT_EQ(modularExponentiation(*root, two_n_ap, q), 1);
68+
69+
// Check it's PRIMITIVE 2n-th root
70+
auto factors = factorize(two_n_ap);
71+
for (const auto& p : factors) {
72+
EXPECT_NE(modularExponentiation(*root, two_n_ap.udiv(p), q), 1);
73+
}
74+
}
75+
6076
void Primitive2nthRootProperty(uint64_t q_val, uint64_t n) {
6177
APInt q(64, q_val);
6278
if (!isPrime(q)) return;
@@ -78,11 +94,11 @@ void Primitive2nthRootProperty(uint64_t q_val, uint64_t n) {
7894

7995
// Fuzz test with a set of known NTT-friendly primes and various degrees
8096
std::vector<uint64_t> ntt_primes = {
81-
65537, 114689, 147457, 163841, 557057,
82-
638977, 737281, 786433, 1032193, 1179649,
83-
1769473, 1785857, 2277377, 2424833, 2572289,
84-
2654209, 2752513, 2768897, 8380417, 2147565569,
85-
2148155393, 2148384769, 3221225473, 3221241857, 3758161921};
97+
65537, 114689, 147457, 163841, 557057, 638977,
98+
737281, 786433, 1032193, 1179649, 1769473, 1785857,
99+
2277377, 2424833, 2572289, 2654209, 2752513, 2768897,
100+
8380417, 2147565569, 2148155393, 2148384769, 3221225473, 3221241857,
101+
3758161921, 1095233372161};
86102

87103
FUZZ_TEST(MathUtilsTest, Primitive2nthRootProperty)
88104
.WithDomains(fuzztest::ElementOf(ntt_primes),

0 commit comments

Comments
 (0)