33
44#include " dyadic.h"
55#include < cstdio>
6+ #include < dyadic/prng.h>
67
78using 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