1 /* test fb_root_in_qlattice_31bits */
2 
3 #include "cado.h" // IWYU pragma: keep
4 #include <cinttypes>               // for PRId64, PRIu64
5 #include <cstdint>                 // for uint32_t, uint64_t
6 #include <cstring>                 // for strcmp
7 #include <cstdio>
8 #include <cstdlib>
9 #include <memory>                   // for allocator_traits<>::value_type
10 #include <vector>
11 #include <array>
12 #include <iostream>
13 #include <sstream>
14 #include <gmp.h>
15 #include "fb-types.h"               // for fbprime_t, FBPRIME_FORMAT
16 #include "gmp_aux.h"                // for mpz_add_int64, mpz_init_set_int64
17 #include "las-arith.hpp"            // for invmod_redc_32
18 #include "macros.h"
19 #include "las-qlattice.hpp"
20 #include "las-fbroot-qlattice.hpp"
21 #include "fb.hpp"
22 #include "timing.h"  // seconds
23 #include "fmt/format.h"
24 #include "tests_common.h"
25 
26 /*
27  * R encodes (p:1) if R<p, or (1:R-p) if R >= p
28  *
29  * when R < p (affine root),
30  *      return num/den%p with num=R*b1-a1 den=a0-R*b0
31  * when R >= p (projective root),
32  *      return num/den%p with num=b1-(R-p)*a1 den=(R-p)*a0-b0
33  *
34  * It is assumed that R*b1-a1 and a0-R*b0 cannot be both multiples of p,
35  * since the matrix (a0,b0,a1,b1) has determinant coprime to p, and
36  * gcd(1,R) == 1
37  *
38  * if den in either expression is divisile by p, then the
39  * resulting root is projective. Therefore, we return p + den/num
40  * */
41 
42 template<typename T>
operator <<(std::ostream & os,fb_root_p1_t<T> const & R)43 std::ostream& operator<<(std::ostream& os, fb_root_p1_t<T> const & R)
44 {
45     if (R.proj)
46         os << "(1:" << R.r << ")";
47     else
48         os << "(" << R.r << ":1)";
49     return os;
50 }
51 
52 /* declare these in the fmt namespace to work around a bug in g++-6
53  * https://stackoverflow.com/questions/25594644/warning-specialization-of-template-in-different-namespace
54  */
55 namespace fmt {
56 template <typename T> struct /* fmt:: */ formatter<fb_root_p1_t<T>>: formatter<string_view> {
57     template <typename FormatContext>
formatfmt::formatter58         auto format(fb_root_p1_t<T> const & c, FormatContext& ctx) -> decltype(ctx.out()) {
59             std::ostringstream os;
60             os << c;
61             return formatter<string_view>::format( string_view(os.str()), ctx);
62         }
63 };
64 
65 template <> struct /* fmt:: */ formatter<qlattice_basis>: formatter<string_view> {
66     template <typename FormatContext>
formatfmt::formatter67         auto format(qlattice_basis const & c, FormatContext& ctx) -> decltype(ctx.out()) {
68             std::ostringstream os;
69             os << c;
70             return formatter<string_view>::format( string_view(os.str()), ctx);
71         }
72 };
73 }
74 
75 
76 static fb_root_p1_t<cxx_mpz>
ref_fb_root_in_qlattice(fbprime_t p,fb_root_p1 R,qlattice_basis basis)77 ref_fb_root_in_qlattice (fbprime_t p, fb_root_p1 R, qlattice_basis basis)
78 {
79   cxx_mpz num, den;
80 
81   if (R.is_affine()) {
82       den = -basis.b0;
83       mpz_mul_ui (den, den, R.r);
84       mpz_add_int64 (den, den, basis.a0);
85 
86       num = basis.b1;
87       mpz_mul_ui (num, num, R.r);
88       mpz_sub_int64 (num, num, basis.a1);
89   } else {
90       den = basis.a0;
91       mpz_mul_ui (den, den, R.r);
92       mpz_sub_int64 (den, den, basis.b0);
93 
94       num = -basis.a1;
95       mpz_mul_ui (num, num, R.r);
96       mpz_add_int64 (num, num, basis.b1);
97   }
98 
99   if (mpz_gcd_ui(NULL, den, p) != 1) {
100       /* root is projective */
101       mpz_invert (num, num, cxx_mpz(p));
102       mpz_mul(num, num, den);
103       mpz_mod_ui(num, num, p);
104       return { num, true };
105   } else {
106       mpz_invert (den, den, cxx_mpz(p));
107       mpz_mul(num, num, den);
108       mpz_mod_ui(num, num, p);
109       return { num, false };
110   }
111 }
112 
113 typedef std::vector<qlattice_basis>::const_iterator basis_citer_t;
114 
115 /* Make a random prime in the interval [2^(FBPRIME_BITS-1), 2^FBPRIME_BITS].
116  * t is a temp variable that will get clobbered. */
117 static fbprime_t
make_random_fb_prime(mpz_ptr t)118 make_random_fb_prime(mpz_ptr t)
119 {
120     do {
121         tests_common_urandomb (t, FBPRIME_BITS);
122         mpz_nextprime (t, t);
123     } while (mpz_sizeinbase (t, 2) != FBPRIME_BITS);
124     return mpz_get_ui (t);
125 }
126 
127 /* Make a random root mod p.
128  * t is a temp variable that will get clobbered. */
129 static fbroot_t
make_random_fb_root(const fbprime_t p,mpz_ptr t)130 make_random_fb_root(const fbprime_t p, mpz_ptr t)
131 {
132     mpz_set_ui (t, p);
133     tests_common_urandomm (t, t);
134     fbroot_t r = mpz_get_ui (t);
135     ASSERT_ALWAYS (r < p);
136     return r;
137 }
138 
139 /* Compute -1/p[i] mod 2^32.
140  * t and u are temp variables that will get clobbered. */
141 static uint32_t
make_invp32(const fbprime_t p,mpz_ptr t,mpz_ptr u)142 make_invp32 (const fbprime_t p, mpz_ptr t, mpz_ptr u)
143 {
144     mpz_ui_pow_ui (u, 2, 32);
145     mpz_set_ui (t, p);
146     mpz_neg (t, t);
147     mpz_invert (t, t, u);
148     return mpz_get_ui (t);
149 }
150 
151 /* Compute -1/p[i] mod 2^64.
152  * t and u are temp variables that will get clobbered. */
153 static uint64_t
make_invp64(const fbprime_t p,mpz_t t,mpz_t u)154 make_invp64 (const fbprime_t p, mpz_t t, mpz_t u)
155 {
156     mpz_ui_pow_ui (u, 2, 64);
157     mpz_set_ui (t, p);
158     mpz_neg (t, t);
159     mpz_invert (t, t, u);
160     return mpz_get_uint64 (t);
161 }
162 
163 /* Make a random qlattice_basis where each coordinate is in [-2^31, 2^31-1]
164  * if bits==31, or in [-2^63, 2^63-1] if bits == 63. */
165 static void
make_random_basis(qlattice_basis & basis,mpz_t t,const int bits)166 make_random_basis (qlattice_basis &basis, mpz_t t, const int bits)
167 {
168     ASSERT_ALWAYS(bits == 31 || bits == 63);
169     const int64_t offset = (bits == 31) ? INT32_MIN : INT64_MIN;
170     tests_common_urandomb (t, bits + 1);
171     basis.a0 = mpz_get_ui (t) + offset;
172     tests_common_urandomb (t, bits + 1);
173     basis.a1 = mpz_get_ui (t) + offset;
174     tests_common_urandomb (t, bits + 1);
175     basis.b0 = mpz_get_ui (t) + offset;
176     tests_common_urandomb (t, bits + 1);
177     basis.b1 = mpz_get_ui (t) + offset;
178     if (bits == 31) {
179         ASSERT_ALWAYS(basis.fits_31bits());
180     }
181 }
182 
183 static void
make_extremal_basis(qlattice_basis & basis,const int bits,const unsigned int count)184 make_extremal_basis (qlattice_basis &basis, const int bits, const unsigned int count)
185 {
186     const int64_t min = (bits == 31) ? INT32_MIN : INT64_MIN / 4;
187     const int64_t max = (bits == 31) ? INT32_MAX : INT64_MAX / 4;
188 
189     basis.a0 = (count & 1) != 0 ? min : max;
190     basis.a1 = (count & 2) != 0 ? min : max;
191     basis.b0 = (count & 4) != 0 ? min : max; /* with bits==31, min-2 happens to pass, min-3 fails */
192     basis.b1 = (count & 8) != 0 ? min : max; /* with bits==31, min-2 happens to pass, min-3 fails */
193 }
194 
195 static void
print_error_and_exit(const fbprime_t p,fb_root_p1 const & Rab,fb_root_p1 const & rt,fb_root_p1_t<cxx_mpz> const & rref,const qlattice_basis & basis,const int bits)196 print_error_and_exit(const fbprime_t p, fb_root_p1 const & Rab, fb_root_p1 const & rt, fb_root_p1_t<cxx_mpz> const & rref, const qlattice_basis &basis, const int bits)
197 {
198     std::cerr
199         << fmt::format(FMT_STRING("Error for p={}; R={}; {};\n"), p, Rab, basis)
200         << fmt::format(FMT_STRING("fb_root_in_qlattice_{}bits gives {}\n"), bits, rt)
201         << fmt::format(FMT_STRING("ref_fb_root_in_qlattice gives {}\n"), rref);
202     abort();
203 }
204 
205 template<int Nr_roots>
206 static
make_random_fb_entry_x_roots(mpz_ptr t,mpz_ptr u)207 fb_entry_x_roots<Nr_roots> make_random_fb_entry_x_roots(
208         mpz_ptr t, mpz_ptr u)
209 {
210     fbprime_t p = make_random_fb_prime(t);
211     redc_invp_t invq = make_invp32(p, t, u);
212     fbroot_t rr[Nr_roots];
213     for (int i_root = 0; i_root < Nr_roots; i_root++) {
214         rr[i_root] = make_random_fb_root(p, t);
215     }
216     return fb_entry_x_roots<Nr_roots>(p, invq, rr);
217 }
218 
219 template<int Nr_roots>
220 static void
test_chain_fb_root_in_qlattice_batch(basis_citer_t basis_begin,basis_citer_t basis_end,const unsigned long N,mpz_t t,mpz_t u,const bool do_speed,const bool do_test,const int bits)221 test_chain_fb_root_in_qlattice_batch(basis_citer_t basis_begin,
222                                 basis_citer_t basis_end,
223                                 const unsigned long N, mpz_t t, mpz_t u,
224                                 const bool do_speed, const bool do_test,
225                                 const int bits)
226 {
227     /* Test N random factor base entries with Nr_roots roots each against
228      * each of the bases in [basis_begin, basis_end] */
229     /* The reason why this is a recursively called function template is
230      * that I'd like to call fb_entry_x_roots<Nr_roots>::transform() in
231      * for the test eventually, but currently it is decided at compile
232      * time (via SUPPORT_LARGE_Q macro) whether
233      * fb_root_in_qlattice_31bits_batch() or
234      * fb_root_in_qlattice_127bits_batch() gets called from there.
235      * It should not be too hard to make that decision at run-time,
236      * then this test code can be adapted accordingly.
237     */
238 
239     std::vector<fb_entry_x_roots<Nr_roots>> fbv;
240     fbv.reserve(N);
241 
242     /* Make N random factor base entries */
243     for (unsigned long i = 0; i < N; i++)
244         fbv.emplace_back(make_random_fb_entry_x_roots<Nr_roots>(t, u));
245 
246     if (do_speed) {
247         double st = seconds ();
248         fbroot_t fake_sum = 0; /* Fake sum to stop compiler from optimizing away
249                                   everything due to unused results */
250         for (basis_citer_t basis_iter = basis_begin;
251                 basis_iter != basis_end; basis_iter++) {
252             for (unsigned long i_fb = 0; i_fb < N; i_fb++) {
253                 typename fb_entry_x_roots<Nr_roots>::transformed_entry_t fbt;
254                 const fbprime_t p = fbv[i_fb].get_q();
255                 // fbv->transform_roots(fbt, *basis_iter);
256                 if (bits == 31) {
257                     fb_root_in_qlattice_31bits_batch (fbt.roots.data(), p, fbv[i_fb].roots.data(), fbv[i_fb].invq, *basis_iter, Nr_roots);
258                 } else {
259                     fb_root_in_qlattice_127bits_batch (fbt.roots.data(), p, fbv[i_fb].roots.data(), fbv[i_fb].invq, *basis_iter, Nr_roots);
260                 }
261                 for (unsigned long i_root = 0; i_root + 1 < Nr_roots + 1; i_root++)
262                     fake_sum += fbt.get_r(i_root);
263             }
264         }
265         st = seconds() - st;
266         volatile fbroot_t fake_sum_vol = fake_sum;
267         if (fake_sum_vol) {}
268         printf ("fb_entry_x_roots<%d>::transform_roots with %d-bit basis: %lu tests took %.2fs\n",
269                 Nr_roots, bits, N * N, st);
270     }
271 
272     if (do_test) {
273         /* Compare all the transformed roots with the reference implementation */
274         for (basis_citer_t basis_iter = basis_begin;
275                 basis_iter != basis_end; basis_iter++) {
276             for (unsigned long i_fb = 0; i_fb < N; i_fb++) {
277                 typename fb_entry_x_roots<Nr_roots>::transformed_entry_t fbt;
278                 std::array<fb_root_p1_t<cxx_mpz>, Nr_roots> fbt_ref;
279                 const fbprime_t p = fbv[i_fb].get_q();
280 
281                 /* Compute reference roots */
282                 bool had_any_proj = false;
283                 for (unsigned char i_root = 0; i_root + 1 < Nr_roots + 1; i_root++) {
284                     const fbroot_t original_root = fbv[i_fb].get_r(i_root);
285                     fbt_ref[i_root] = ref_fb_root_in_qlattice (p, original_root, *basis_iter);
286                     had_any_proj |= fbt_ref[i_root].is_projective();
287                 }
288 
289                 /* Compute batch transform of roots */
290                 fbt.p = p;
291                 const bool batch_worked = (bits == 31) ? fb_root_in_qlattice_31bits_batch (fbt.roots.data(), p, fbv[i_fb].roots.data(), fbv[i_fb].invq, *basis_iter, Nr_roots)
292                                                        : fb_root_in_qlattice_127bits_batch (fbt.roots.data(), p, fbv[i_fb].roots.data(), fbv[i_fb].invq, *basis_iter, Nr_roots);
293 
294                 /* If the batch transform did not work, verify that at least
295                  * one transformed root is projective */
296                 if (!batch_worked) {
297                     if (!had_any_proj) {
298                         fprintf(stderr, "Batch transform failed, but none of the "
299                                 "transformed roots are projective according to the "
300                                 "reference implementation\n");
301                         abort();
302                     }
303                     /* If batch transform did not work and there actually was a
304                      * projective transformed root, then all went as it should. */
305                 } else {
306                     /* If batch transform worked, check that it agrees with reference roots */
307                     if (0) {
308                         fprintf(stderr, "p = %" FBPRIME_FORMAT ", fbt.get_q() = %" FBPRIME_FORMAT "\n",
309                                        p, fbt.get_q());
310                     }
311                     for (unsigned char i_root = 0; i_root + 1 < Nr_roots + 1; i_root++) {
312                         if (fbt_ref[i_root].r != fbt.get_r(i_root) || fbt_ref[i_root].is_projective()) {
313                             print_error_and_exit(p, fbv[i_fb].get_r(i_root), fbt.get_r(i_root),
314                                     fbt_ref[i_root],
315                                     *basis_iter, bits);
316                         }
317                     }
318                 }
319             }
320         }
321     }
322 
323     /* Repeat the test with factor base entries that have one fewer root each */
324     test_chain_fb_root_in_qlattice_batch<Nr_roots - 1>(basis_begin, basis_end, N, t, u, do_speed, do_test, bits);
325 }
326 
327 /* Specialize the test for length 0 to terminate the recursion.
328  *
329  * (we used to specialize for length -1, but length 0 actually doesn't do
330  * anything either)
331  */
332 template<> void
test_chain_fb_root_in_qlattice_batch(basis_citer_t basis_begin MAYBE_UNUSED,basis_citer_t basis_end MAYBE_UNUSED,const unsigned long N MAYBE_UNUSED,mpz_t t MAYBE_UNUSED,mpz_t u MAYBE_UNUSED,const bool do_speed MAYBE_UNUSED,const bool do_test MAYBE_UNUSED,const int bits MAYBE_UNUSED)333 test_chain_fb_root_in_qlattice_batch<0>(basis_citer_t basis_begin MAYBE_UNUSED,
334                                          basis_citer_t basis_end MAYBE_UNUSED,
335                                          const unsigned long N MAYBE_UNUSED,
336                                          mpz_t t MAYBE_UNUSED,
337                                          mpz_t u MAYBE_UNUSED,
338                                          const bool do_speed MAYBE_UNUSED,
339                                          const bool do_test MAYBE_UNUSED,
340                                          const int bits MAYBE_UNUSED) {
341     return;
342 }
343 
test_one_root_31bits(const fbprime_t p,fb_root_p1 const & Rab,const uint32_t invp,const qlattice_basis & basis)344 void test_one_root_31bits(const fbprime_t p, fb_root_p1 const & Rab, const uint32_t invp,
345                      const qlattice_basis &basis)
346 {
347     fb_root_p1 r31 = fb_root_in_qlattice_31bits (p, Rab, invp, basis);
348     auto rref = ref_fb_root_in_qlattice (p, Rab, basis);
349     if (rref != r31) {
350         print_error_and_exit(p, Rab, r31, rref, basis, 31);
351     }
352 }
353 
354 static void
test_fb_root_in_qlattice_31bits(const bool test_timing,const bool test_correctness,const unsigned long N)355 test_fb_root_in_qlattice_31bits (const bool test_timing,
356     const bool test_correctness, const unsigned long N)
357 {
358   fbprime_t *p, *R, r;
359   uint32_t *invp;
360   unsigned long i, j;
361   mpz_t t, u;
362   std::vector<qlattice_basis> basis;
363   double st;
364 
365   mpz_init (t);
366   mpz_init (u);
367 
368   p = (fbprime_t*) malloc (N * sizeof (fbprime_t));
369   ASSERT_ALWAYS(p != NULL);
370   R = (fbprime_t*) malloc (N * sizeof (fbprime_t));
371   ASSERT_ALWAYS(R != NULL);
372   invp = (uint32_t*) malloc (N * sizeof (uint32_t));
373   ASSERT_ALWAYS(invp != NULL);
374   basis.assign(N, qlattice_basis());
375 
376   if (0 < N) {
377       p[0] = 4294967291U;
378       R[0] = p[0] - 1;
379       invp[0] = make_invp32(p[0], t, u);
380   }
381 
382   /* generate p[i], R[i], invp[i] for 0 <= i < N */
383   for (i = 1; i < N; i++) {
384       p[i] = make_random_fb_prime(t);
385       R[i] = make_random_fb_root(p[i], t);
386       /* invp[i] is -1/p[i] mod 2^32 */
387       invp[i] = make_invp32(p[i], t, u);
388   }
389 
390     /* Fill the first up to 16 basis entries with extremal bases */
391     for (j = 0; j < N && j < 16; j++) {
392         make_extremal_basis(basis[j], 31, j);
393     }
394 
395     /* Fill the remaining basis entries with random bases */
396     for (j = 16; j < N; j++) {
397         make_random_basis(basis[j], t, 31);
398     }
399 
400     test_chain_fb_root_in_qlattice_batch<MAX_DEGREE>(basis.cbegin(), basis.cend(), N, t, u, test_timing, test_correctness, 31);
401 
402     /* Timing of fb_root_in_qlattice_31bits(), i.e., without batch inversion */
403     if (test_timing) {
404       /* efficiency test */
405       st = seconds ();
406       r = 0;
407       for (j = 0; j < N; j++)
408           for (i = 0; i < N; i++) {
409               fb_root_p1 Rab { R[i], false };
410               auto Rij = fb_root_in_qlattice_31bits (p[i], Rab, invp[i], basis[j]);
411               r += Rij.r;
412           }
413       st = seconds () - st;
414       printf ("fb_root_in_qlattice_31bits: %lu tests took %.2fs (r=%" FBPRIME_FORMAT ")\n",
415               N * N, st, r);
416     }
417 
418   /* Test of fb_root_in_qlattice_31bits(), i.e., without batch inversion */
419   if (test_correctness) {
420       for (i = 0; i < N; i++)
421           for (j = 0; j < N; j++)
422               test_one_root_31bits(p[i], R[i], invp[i], basis[j]);
423   }
424 
425   free (p);
426   free (R);
427   free (invp);
428 
429   mpz_clear (t);
430   mpz_clear (u);
431 }
432 
433 static void
test_fb_root_in_qlattice_127bits(const bool test_timing,const bool test_correctness,const unsigned long N)434 test_fb_root_in_qlattice_127bits (const bool test_timing,
435     const bool test_correctness, const unsigned long N)
436 {
437   fbprime_t *p, *R, r;
438   uint32_t *invp32;
439   uint64_t *invp64;
440   unsigned long i, j;
441   mpz_t t, u;
442   std::vector<qlattice_basis> basis;
443   double st;
444 
445   mpz_init (t);
446   mpz_init (u);
447 
448   p = (fbprime_t*) malloc (N * sizeof (fbprime_t));
449   ASSERT_ALWAYS(p != NULL);
450   R = (fbprime_t*) malloc (N * sizeof (fbprime_t));
451   ASSERT_ALWAYS(R != NULL);
452   invp32 = (uint32_t*) malloc (N * sizeof (uint32_t));
453   ASSERT_ALWAYS(invp32 != NULL);
454   invp64 = (uint64_t*) malloc (N * sizeof (uint64_t));
455   ASSERT_ALWAYS(invp64 != NULL);
456   basis.assign(N, qlattice_basis());
457 
458   /* generate p[i], R[i], invp32[i], invp64[i] for 0 <= i < N */
459   for (i = 0; i < N; i++) {
460       p[i] = make_random_fb_prime(t);
461       R[i] = make_random_fb_root(p[i], t);
462       invp32[i] = make_invp32(p[i], t, u);
463       invp64[i] = make_invp64(p[i], t, u);
464   }
465 
466     /* Fill the first up to 16 basis entries with extremal bases */
467     for (j = 0; j < N && j < 16; j++) {
468         make_extremal_basis(basis[j], 63, j);
469     }
470 
471   /* generate basis[j] for 0 <= j < N */
472   for (j = 16; j < N; j++) {
473       make_random_basis(basis[j], t, 63);
474   }
475 
476   test_chain_fb_root_in_qlattice_batch<MAX_DEGREE>(basis.cbegin(), basis.cend(), N, t, u, test_timing, test_correctness, 127);
477 
478   if (test_timing) {
479       /* efficiency test */
480       st = seconds ();
481       r = 0;
482       for (j = 0; j < N; j++)
483           for (i = 0; i < N; i++) {
484               fb_root_p1 Rab = R[i];
485               auto Rij = fb_root_in_qlattice_127bits (p[i], Rab, invp64[i], basis[j]);
486               r += Rij.r;
487           }
488       st = seconds () - st;
489       printf ("fb_root_in_qlattice_127bits: %lu tests took %.2fs (r=%" FBPRIME_FORMAT ")\n",
490               N * N, st, r);
491   }
492 
493   if (test_correctness) {
494       /* correctness test */
495       for (i = 0; i < N; i++) {
496           for (j = 0; j < N; j++) {
497               fb_root_p1 Rab = R[i];
498               fb_root_p1 r127 = fb_root_in_qlattice_127bits (p[i], R[i], invp64[i], basis[j]);
499               auto rref = ref_fb_root_in_qlattice (p[i], R[i], basis[j]);
500               if (rref != r127)
501                   print_error_and_exit(p[i], Rab, r127, rref, basis[j], 127);
502           }
503       }
504   }
505 
506   free (p);
507   free (R);
508   free (invp32);
509   free (invp64);
510 
511   mpz_clear (t);
512   mpz_clear (u);
513 }
514 
515 /* exercise bugs in fb_root_in_qlattice_127bits */
516 static void
bug20200225(void)517 bug20200225 (void)
518 {
519     {
520         /* exercises bug in assembly part of invmod_redc_32 (starting
521          * around line 326 with 2nd #ifdef HAVE_GCC_STYLE_AMD64_INLINE_ASM) */
522         unsigned long a = 8088625;
523         unsigned long b = 2163105767;
524         uint64_t expected = 2062858318;
525         uint64_t got = invmod_redc_32 (a, b, -invmod_po2(b));
526         if (expected != got) {
527             fprintf (stderr, "Error in invmod_redc_32 for a=%lu b=%lu\n", a, b);
528             gmp_fprintf (stderr, "Expected %Zd\n", mpz_srcptr(expected));
529             fprintf (stderr, "Got      %" PRIu64 "\n", got);
530             exit (1);
531         }
532     }
533 
534     {
535         unsigned long a = 76285;
536         unsigned long b = 2353808591;
537         uint64_t expected = 2102979166;
538         uint64_t got = invmod_redc_32(a, b, -invmod_po2(b));
539         if (expected != got) {
540             fprintf (stderr, "Error in invmod_redc_32 for a:=%lu; b:=%lu;\n",
541                     a, b);
542             gmp_fprintf (stderr, "Expected %Zd\n", mpz_srcptr(expected));
543             fprintf (stderr, "Got      %" PRIu64 "\n", got);
544             exit (1);
545         }
546     }
547 
548     {
549         fbprime_t p = 3628762957;
550         fb_root_p1 Rab { 1702941053 };
551         uint64_t invp = 5839589727713490555UL;
552         qlattice_basis basis {
553             -2503835703516628395L, 238650852,
554                 -3992552824749287692L, 766395543
555         };
556         auto rref = ref_fb_root_in_qlattice (p, Rab, basis);
557         fb_root_p1 r127 = fb_root_in_qlattice_127bits (p, Rab, invp, basis);
558         if (rref != r127)
559             print_error_and_exit(p, Rab, r127, rref, basis, 127);
560     }
561 
562     {
563         /* exercises bug in invmod_redc_32, already tested directly above */
564         fbprime_t p = 2163105767;
565         fb_root_p1 Rab = 1743312141;
566         uint64_t invp = 3235101737;
567         qlattice_basis basis {
568             -30118114923155082L, -749622022,
569                 -2851499432479966615L, -443074848,
570         };
571         auto rref = ref_fb_root_in_qlattice (p, Rab, basis);
572 
573         fb_root_p1 r31 = fb_root_in_qlattice_31bits (p, Rab, invp, basis);
574         if (rref != r31)
575             print_error_and_exit(p, Rab, r31, rref, basis, 31);
576     }
577 
578     {
579         fbprime_t p = 3725310689;
580         fb_root_p1 Rab = 2661839516;
581         uint64_t invp = 1066179678986106591UL;
582         qlattice_basis basis {
583             3008222006914909739L, 877054135,
584             3170231873717741170L, 932375769,
585         };
586         auto rref = ref_fb_root_in_qlattice (p, Rab, basis);
587         fb_root_p1 r127 = fb_root_in_qlattice_127bits (p, Rab, invp, basis);
588         if (rref != r127)
589             print_error_and_exit(p, Rab, r127, rref, basis, 127);
590     }
591 
592     {
593         /* This is playing with the 32-bit limit */
594         fbprime_t p = 3486784401;       // 3^20
595         fb_root_p1 Rab = 2009510725;
596         uint64_t invp = 898235023;
597         qlattice_basis basis {
598             -1353180941,
599                 -5,
600                 -223660881,
601                 8,
602         };
603         auto rref = ref_fb_root_in_qlattice (p, Rab, basis);
604         fb_root_p1 r127 = fb_root_in_qlattice_127bits (p, Rab, invp, basis);
605         if (rref != r127)
606             print_error_and_exit(p, Rab, r127, rref, basis, 127);
607     }
608     {
609         fbprime_t p = 3;
610         fb_root_p1 Rab = 2;
611         uint64_t invp = 1431655765;
612         qlattice_basis basis {
613             -14730287151, 11, -6528529, -2,
614         };
615         auto rref = ref_fb_root_in_qlattice (p, Rab, basis);
616         fb_root_p1 r127 = fb_root_in_qlattice_127bits (p, Rab, invp, basis);
617         if (rref != r127)
618             print_error_and_exit(p, Rab, r127, rref, basis, 127);
619     }
620 }
621 
622 /* The usual tests command line parameters "-seed" and "-iter" are accepted.
623    Giving the "-check" parameter does only correctness tests but not timing.
624    Giving only "-time" does only timing. Giving neither or both does both. */
625 
626 // coverity[root_function]
627 int
main(int argc,const char * argv[])628 main (int argc, const char *argv[])
629 {
630   unsigned long N = 100;
631   int test_correctness = 1, test_timing = 1;
632 
633   setbuf(stdout, NULL);
634   setbuf(stderr, NULL);
635 
636   tests_common_cmdline(&argc, &argv, PARSE_SEED | PARSE_ITER | PARSE_CHECK | PARSE_TIME);
637   tests_common_get_iter(&N);
638   tests_common_get_check_and_time(&test_correctness, &test_timing);
639 
640   bug20200225 ();
641   test_fb_root_in_qlattice_31bits (test_timing != 0, test_correctness != 0, N);
642   test_fb_root_in_qlattice_127bits (test_timing != 0, test_correctness != 0, N);
643 
644   return 0;
645 }
646