Skip to content

Commit 1fac898

Browse files
Fix test_full for recover_bezout_y: template-N, XorShift64 prng, and carry semantics
- Use template<int N> lambda so Polynomial<N> size matches indices (was Polynomial<16> with runtime N, causing wrong ax[N+i] vs ax[16+i]) - Replace std::mt19937_64 with detail::XorShift64 (no STL dependency) - Accept carry=1 from y+q=2^k (expected when a ≠ 1; q = top N limbs of a*x) - Add missing using dw_t in u64 N=2 test section - Add #include <dyadic/prng.h>
1 parent 54e2bc0 commit 1fac898

1 file changed

Lines changed: 31 additions & 24 deletions

File tree

test/test_full.cpp

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "dyadic.h"
55
#include <cstdio>
6+
#include <dyadic/prng.h>
67

78
using namespace dyadic;
89

@@ -1866,51 +1867,56 @@ static int test_arith_helpers() {
18661867
}
18671868
}
18681869

1869-
// recover_bezout_y — verify a*x + 2^k*y = 1 for N=1..8
1870-
// Using uint32_t where quad_width (uint64_t) is safe for the cross-check.
1870+
// recover_bezout_y — verify a*x + 2^(N*BITS)*y = 1 for N=1..8
1871+
// Uses uint32_t with compile-time N so indices match Polynomial<N>.
18711872
{
1872-
auto check_bezout = [&](int N, auto make_a, const char* label) {
1873-
using W = uint32_t;
1874-
constexpr int BITS = 8 * sizeof(W);
1875-
Polynomial<16, W, MonomialBasis> a{};
1873+
using W = uint32_t;
1874+
constexpr int BITS = 8 * sizeof(W);
1875+
1876+
auto check_bezout = [&]<int N>(auto make_a, const char* label) -> void {
1877+
Polynomial<N, W, MonomialBasis> a{};
18761878
for (int i = 0; i < N; ++i) a[i] = make_a(i, N);
18771879

18781880
auto x = modinv_pow2(a);
18791881
auto y = recover_bezout_y(a, x);
18801882

1881-
// Verify via mul_unsigned (safe for uint32_t up to N=16)
1883+
// Full product a*x — yields 2N limbs, low N are [1,0,..], high N = q
18821884
auto ax = detail::mul_unsigned(a, x);
18831885

1884-
// Check ax + y*2^k == 1
18851886
using dw_t = dword_t<W>;
1886-
dw_t carry = 0;
18871887
bool ok = true;
1888-
// Position 0: should be 1 (from a*x, since x is the modular inverse)
18891888
if (ax[0] != W(1)) ok = false;
1890-
// Positions 1..N-1: should be 0
18911889
for (int i = 1; i < N && ok; ++i)
18921890
if (ax[i] != W(0)) ok = false;
1893-
// Positions N..2N-1: add y and verify sum wraps to 0
1891+
// Check ax[N..2N-1] + y[0..N-1] = 0 (mod 2^BITS)
1892+
// (carry beyond 2N limbs is expected when a ≠ 1)
1893+
dw_t carry = 0;
18941894
for (int i = 0; i < N && ok; ++i) {
18951895
dw_t s = static_cast<dw_t>(ax[N + i]) + static_cast<dw_t>(y[i]) + carry;
18961896
if (static_cast<W>(s) != W(0)) ok = false;
18971897
carry = s >> BITS;
18981898
}
1899-
// Final carry should also be 0 (no overflow beyond 2N limbs)
1900-
if (carry != 0) ok = false;
19011899

19021900
if (!ok) {
19031901
std::printf("FAIL recover_bezout_y %s N=%d\n", label, N);
19041902
f++;
19051903
}
19061904
};
19071905

1908-
for (int N : {1, 2, 3, 4, 5, 6, 8}) {
1909-
auto all_ff = [](int, int) { return uint32_t(-1); };
1910-
auto inc_mod = [](int i, int) { return uint32_t((i * 12345U + 1) | 1); };
1911-
check_bezout(N, all_ff, "all_ff");
1912-
check_bezout(N, inc_mod, "inc_mod");
1913-
}
1906+
check_bezout.operator()<1>([](int, int) { return W(-1); }, "all_ff");
1907+
check_bezout.operator()<1>([](int i, int) { return W((i * 12345U + 1) | 1); }, "inc_mod");
1908+
check_bezout.operator()<2>([](int, int) { return W(-1); }, "all_ff");
1909+
check_bezout.operator()<2>([](int i, int) { return W((i * 12345U + 1) | 1); }, "inc_mod");
1910+
check_bezout.operator()<3>([](int, int) { return W(-1); }, "all_ff");
1911+
check_bezout.operator()<3>([](int i, int) { return W((i * 12345U + 1) | 1); }, "inc_mod");
1912+
check_bezout.operator()<4>([](int, int) { return W(-1); }, "all_ff");
1913+
check_bezout.operator()<4>([](int i, int) { return W((i * 12345U + 1) | 1); }, "inc_mod");
1914+
check_bezout.operator()<5>([](int, int) { return W(-1); }, "all_ff");
1915+
check_bezout.operator()<5>([](int i, int) { return W((i * 12345U + 1) | 1); }, "inc_mod");
1916+
check_bezout.operator()<6>([](int, int) { return W(-1); }, "all_ff");
1917+
check_bezout.operator()<6>([](int i, int) { return W((i * 12345U + 1) | 1); }, "inc_mod");
1918+
check_bezout.operator()<8>([](int, int) { return W(-1); }, "all_ff");
1919+
check_bezout.operator()<8>([](int i, int) { return W((i * 12345U + 1) | 1); }, "inc_mod");
19141920
}
19151921

19161922
// recover_bezout_y with uint64_t — spot-check N=1 (trivial) and N=2 (small)
@@ -1922,9 +1928,9 @@ static int test_arith_helpers() {
19221928
// q = (a*x - 1) / 2^64. Since a and x are both < 2^64, a*x < 2^128.
19231929
// q is the high 64 bits of the 128-bit product.
19241930
{
1925-
std::mt19937_64 rng(42);
1931+
detail::XorShift64 rng(42);
19261932
for (int t = 0; t < 20; ++t) {
1927-
W av = rng() | 1;
1933+
W av = rng.next() | 1;
19281934
Polynomial<1, W> a{{av}};
19291935
auto x = modinv_pow2(a);
19301936
auto y = recover_bezout_y(a, x);
@@ -1945,9 +1951,9 @@ static int test_arith_helpers() {
19451951

19461952
// N=2: verify y = -(a*x-1) / 2^128 using safe full product
19471953
{
1948-
std::mt19937_64 rng(123);
1954+
detail::XorShift64 rng(123);
19491955
for (int t = 0; t < 20; ++t) {
1950-
Polynomial<2, W> a{{rng() | 1, rng()}};
1956+
Polynomial<2, W> a{{rng.next() | 1, rng.next()}};
19511957
auto x = modinv_pow2(a);
19521958
auto y = recover_bezout_y(a, x);
19531959

@@ -1968,6 +1974,7 @@ static int test_arith_helpers() {
19681974
}
19691975

19701976
// q = ax[2..3], y = -q = ~q + 1
1977+
using dw_t = dword_t<W>;
19711978
dw_t carry = 1;
19721979
bool ok = true;
19731980
for (int i = 0; i < 2; ++i) {

0 commit comments

Comments
 (0)