Austin Schuh | dace2a6 | 2020-08-18 10:56:48 -0700 | [diff] [blame] | 1 | /* Test mpn_hgcd. |
| 2 | |
| 3 | Copyright 1991, 1993, 1994, 1996, 1997, 2000-2004 Free Software Foundation, |
| 4 | Inc. |
| 5 | |
| 6 | This file is part of the GNU MP Library test suite. |
| 7 | |
| 8 | The GNU MP Library test suite is free software; you can redistribute it |
| 9 | and/or modify it under the terms of the GNU General Public License as |
| 10 | published by the Free Software Foundation; either version 3 of the License, |
| 11 | or (at your option) any later version. |
| 12 | |
| 13 | The GNU MP Library test suite is distributed in the hope that it will be |
| 14 | useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
| 16 | Public License for more details. |
| 17 | |
| 18 | You should have received a copy of the GNU General Public License along with |
| 19 | the GNU MP Library test suite. If not, see https://www.gnu.org/licenses/. */ |
| 20 | |
| 21 | #include <stdio.h> |
| 22 | #include <stdlib.h> |
| 23 | |
| 24 | #include "gmp-impl.h" |
| 25 | #include "tests.h" |
| 26 | |
| 27 | static mp_size_t one_test (mpz_t, mpz_t, int); |
| 28 | static void debug_mp (mpz_t, int); |
| 29 | |
| 30 | #define MIN_OPERAND_SIZE 2 |
| 31 | |
| 32 | /* Fixed values, for regression testing of mpn_hgcd. */ |
| 33 | struct value { int res; const char *a; const char *b; }; |
| 34 | static const struct value hgcd_values[] = { |
| 35 | #if GMP_NUMB_BITS == 32 |
| 36 | { 5, |
| 37 | "0x1bddff867272a9296ac493c251d7f46f09a5591fe", |
| 38 | "0xb55930a2a68a916450a7de006031068c5ddb0e5c" }, |
| 39 | { 4, |
| 40 | "0x2f0ece5b1ee9c15e132a01d55768dc13", |
| 41 | "0x1c6f4fd9873cdb24466e6d03e1cc66e7" }, |
| 42 | { 3, "0x7FFFFC003FFFFFFFFFC5", "0x3FFFFE001FFFFFFFFFE3"}, |
| 43 | #endif |
| 44 | { -1, NULL, NULL } |
| 45 | }; |
| 46 | |
| 47 | struct hgcd_ref |
| 48 | { |
| 49 | mpz_t m[2][2]; |
| 50 | }; |
| 51 | |
| 52 | static void hgcd_ref_init (struct hgcd_ref *); |
| 53 | static void hgcd_ref_clear (struct hgcd_ref *); |
| 54 | static int hgcd_ref (struct hgcd_ref *, mpz_t, mpz_t); |
| 55 | static int hgcd_ref_equal (const struct hgcd_matrix *, const struct hgcd_ref *); |
| 56 | |
| 57 | int |
| 58 | main (int argc, char **argv) |
| 59 | { |
| 60 | mpz_t op1, op2, temp1, temp2; |
| 61 | int i, j, chain_len; |
| 62 | gmp_randstate_ptr rands; |
| 63 | mpz_t bs; |
| 64 | unsigned long size_range; |
| 65 | |
| 66 | tests_start (); |
| 67 | rands = RANDS; |
| 68 | |
| 69 | mpz_init (bs); |
| 70 | mpz_init (op1); |
| 71 | mpz_init (op2); |
| 72 | mpz_init (temp1); |
| 73 | mpz_init (temp2); |
| 74 | |
| 75 | for (i = 0; hgcd_values[i].res >= 0; i++) |
| 76 | { |
| 77 | mp_size_t res; |
| 78 | |
| 79 | mpz_set_str (op1, hgcd_values[i].a, 0); |
| 80 | mpz_set_str (op2, hgcd_values[i].b, 0); |
| 81 | |
| 82 | res = one_test (op1, op2, -1-i); |
| 83 | if (res != hgcd_values[i].res) |
| 84 | { |
| 85 | fprintf (stderr, "ERROR in test %d\n", -1-i); |
| 86 | fprintf (stderr, "Bad return code from hgcd\n"); |
| 87 | fprintf (stderr, "op1="); debug_mp (op1, -16); |
| 88 | fprintf (stderr, "op2="); debug_mp (op2, -16); |
| 89 | fprintf (stderr, "expected: %d\n", hgcd_values[i].res); |
| 90 | fprintf (stderr, "hgcd: %d\n", (int) res); |
| 91 | abort (); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | for (i = 0; i < 15; i++) |
| 96 | { |
| 97 | /* Generate plain operands with unknown gcd. These types of operands |
| 98 | have proven to trigger certain bugs in development versions of the |
| 99 | gcd code. */ |
| 100 | |
| 101 | mpz_urandomb (bs, rands, 32); |
| 102 | size_range = mpz_get_ui (bs) % 13 + 2; |
| 103 | |
| 104 | mpz_urandomb (bs, rands, size_range); |
| 105 | mpz_rrandomb (op1, rands, mpz_get_ui (bs) + MIN_OPERAND_SIZE); |
| 106 | mpz_urandomb (bs, rands, size_range); |
| 107 | mpz_rrandomb (op2, rands, mpz_get_ui (bs) + MIN_OPERAND_SIZE); |
| 108 | |
| 109 | if (mpz_cmp (op1, op2) < 0) |
| 110 | mpz_swap (op1, op2); |
| 111 | |
| 112 | if (mpz_size (op1) > 0) |
| 113 | one_test (op1, op2, i); |
| 114 | |
| 115 | /* Generate a division chain backwards, allowing otherwise |
| 116 | unlikely huge quotients. */ |
| 117 | |
| 118 | mpz_set_ui (op1, 0); |
| 119 | mpz_urandomb (bs, rands, 32); |
| 120 | mpz_urandomb (bs, rands, mpz_get_ui (bs) % 16 + 1); |
| 121 | mpz_rrandomb (op2, rands, mpz_get_ui (bs)); |
| 122 | mpz_add_ui (op2, op2, 1); |
| 123 | |
| 124 | #if 0 |
| 125 | chain_len = 1000000; |
| 126 | #else |
| 127 | mpz_urandomb (bs, rands, 32); |
| 128 | chain_len = mpz_get_ui (bs) % (GMP_NUMB_BITS * GCD_DC_THRESHOLD / 256); |
| 129 | #endif |
| 130 | |
| 131 | for (j = 0; j < chain_len; j++) |
| 132 | { |
| 133 | mpz_urandomb (bs, rands, 32); |
| 134 | mpz_urandomb (bs, rands, mpz_get_ui (bs) % 12 + 1); |
| 135 | mpz_rrandomb (temp2, rands, mpz_get_ui (bs) + 1); |
| 136 | mpz_add_ui (temp2, temp2, 1); |
| 137 | mpz_mul (temp1, op2, temp2); |
| 138 | mpz_add (op1, op1, temp1); |
| 139 | |
| 140 | /* Don't generate overly huge operands. */ |
| 141 | if (SIZ (op1) > 3 * GCD_DC_THRESHOLD) |
| 142 | break; |
| 143 | |
| 144 | mpz_urandomb (bs, rands, 32); |
| 145 | mpz_urandomb (bs, rands, mpz_get_ui (bs) % 12 + 1); |
| 146 | mpz_rrandomb (temp2, rands, mpz_get_ui (bs) + 1); |
| 147 | mpz_add_ui (temp2, temp2, 1); |
| 148 | mpz_mul (temp1, op1, temp2); |
| 149 | mpz_add (op2, op2, temp1); |
| 150 | |
| 151 | /* Don't generate overly huge operands. */ |
| 152 | if (SIZ (op2) > 3 * GCD_DC_THRESHOLD) |
| 153 | break; |
| 154 | } |
| 155 | if (mpz_cmp (op1, op2) < 0) |
| 156 | mpz_swap (op1, op2); |
| 157 | |
| 158 | if (mpz_size (op1) > 0) |
| 159 | one_test (op1, op2, i); |
| 160 | } |
| 161 | |
| 162 | mpz_clear (bs); |
| 163 | mpz_clear (op1); |
| 164 | mpz_clear (op2); |
| 165 | mpz_clear (temp1); |
| 166 | mpz_clear (temp2); |
| 167 | |
| 168 | tests_end (); |
| 169 | exit (0); |
| 170 | } |
| 171 | |
| 172 | static void |
| 173 | debug_mp (mpz_t x, int base) |
| 174 | { |
| 175 | mpz_out_str (stderr, base, x); fputc ('\n', stderr); |
| 176 | } |
| 177 | |
| 178 | static int |
| 179 | mpz_mpn_equal (const mpz_t a, mp_srcptr bp, mp_size_t bsize); |
| 180 | |
| 181 | static mp_size_t |
| 182 | one_test (mpz_t a, mpz_t b, int i) |
| 183 | { |
| 184 | struct hgcd_matrix hgcd; |
| 185 | struct hgcd_ref ref; |
| 186 | |
| 187 | mpz_t ref_r0; |
| 188 | mpz_t ref_r1; |
| 189 | mpz_t hgcd_r0; |
| 190 | mpz_t hgcd_r1; |
| 191 | |
| 192 | mp_size_t res[2]; |
| 193 | mp_size_t asize; |
| 194 | mp_size_t bsize; |
| 195 | |
| 196 | mp_size_t hgcd_init_scratch; |
| 197 | mp_size_t hgcd_scratch; |
| 198 | |
| 199 | mp_ptr hgcd_init_tp; |
| 200 | mp_ptr hgcd_tp; |
| 201 | |
| 202 | asize = a->_mp_size; |
| 203 | bsize = b->_mp_size; |
| 204 | |
| 205 | ASSERT (asize >= bsize); |
| 206 | |
| 207 | hgcd_init_scratch = MPN_HGCD_MATRIX_INIT_ITCH (asize); |
| 208 | hgcd_init_tp = refmpn_malloc_limbs (hgcd_init_scratch); |
| 209 | mpn_hgcd_matrix_init (&hgcd, asize, hgcd_init_tp); |
| 210 | |
| 211 | hgcd_scratch = mpn_hgcd_itch (asize); |
| 212 | hgcd_tp = refmpn_malloc_limbs (hgcd_scratch); |
| 213 | |
| 214 | #if 0 |
| 215 | fprintf (stderr, |
| 216 | "one_test: i = %d asize = %d, bsize = %d\n", |
| 217 | i, a->_mp_size, b->_mp_size); |
| 218 | |
| 219 | gmp_fprintf (stderr, |
| 220 | "one_test: i = %d\n" |
| 221 | " a = %Zx\n" |
| 222 | " b = %Zx\n", |
| 223 | i, a, b); |
| 224 | #endif |
| 225 | hgcd_ref_init (&ref); |
| 226 | |
| 227 | mpz_init_set (ref_r0, a); |
| 228 | mpz_init_set (ref_r1, b); |
| 229 | res[0] = hgcd_ref (&ref, ref_r0, ref_r1); |
| 230 | |
| 231 | mpz_init_set (hgcd_r0, a); |
| 232 | mpz_init_set (hgcd_r1, b); |
| 233 | if (bsize < asize) |
| 234 | { |
| 235 | _mpz_realloc (hgcd_r1, asize); |
| 236 | MPN_ZERO (hgcd_r1->_mp_d + bsize, asize - bsize); |
| 237 | } |
| 238 | res[1] = mpn_hgcd (hgcd_r0->_mp_d, |
| 239 | hgcd_r1->_mp_d, |
| 240 | asize, |
| 241 | &hgcd, hgcd_tp); |
| 242 | |
| 243 | if (res[0] != res[1]) |
| 244 | { |
| 245 | fprintf (stderr, "ERROR in test %d\n", i); |
| 246 | fprintf (stderr, "Different return value from hgcd and hgcd_ref\n"); |
| 247 | fprintf (stderr, "op1="); debug_mp (a, -16); |
| 248 | fprintf (stderr, "op2="); debug_mp (b, -16); |
| 249 | fprintf (stderr, "hgcd_ref: %ld\n", (long) res[0]); |
| 250 | fprintf (stderr, "mpn_hgcd: %ld\n", (long) res[1]); |
| 251 | abort (); |
| 252 | } |
| 253 | if (res[0] > 0) |
| 254 | { |
| 255 | if (!hgcd_ref_equal (&hgcd, &ref) |
| 256 | || !mpz_mpn_equal (ref_r0, hgcd_r0->_mp_d, res[1]) |
| 257 | || !mpz_mpn_equal (ref_r1, hgcd_r1->_mp_d, res[1])) |
| 258 | { |
| 259 | fprintf (stderr, "ERROR in test %d\n", i); |
| 260 | fprintf (stderr, "mpn_hgcd and hgcd_ref returned different values\n"); |
| 261 | fprintf (stderr, "op1="); debug_mp (a, -16); |
| 262 | fprintf (stderr, "op2="); debug_mp (b, -16); |
| 263 | abort (); |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | refmpn_free_limbs (hgcd_init_tp); |
| 268 | refmpn_free_limbs (hgcd_tp); |
| 269 | hgcd_ref_clear (&ref); |
| 270 | mpz_clear (ref_r0); |
| 271 | mpz_clear (ref_r1); |
| 272 | mpz_clear (hgcd_r0); |
| 273 | mpz_clear (hgcd_r1); |
| 274 | |
| 275 | return res[0]; |
| 276 | } |
| 277 | |
| 278 | static void |
| 279 | hgcd_ref_init (struct hgcd_ref *hgcd) |
| 280 | { |
| 281 | unsigned i; |
| 282 | for (i = 0; i<2; i++) |
| 283 | { |
| 284 | unsigned j; |
| 285 | for (j = 0; j<2; j++) |
| 286 | mpz_init (hgcd->m[i][j]); |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | static void |
| 291 | hgcd_ref_clear (struct hgcd_ref *hgcd) |
| 292 | { |
| 293 | unsigned i; |
| 294 | for (i = 0; i<2; i++) |
| 295 | { |
| 296 | unsigned j; |
| 297 | for (j = 0; j<2; j++) |
| 298 | mpz_clear (hgcd->m[i][j]); |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | |
| 303 | static int |
| 304 | sdiv_qr (mpz_t q, mpz_t r, mp_size_t s, const mpz_t a, const mpz_t b) |
| 305 | { |
| 306 | mpz_fdiv_qr (q, r, a, b); |
| 307 | if (mpz_size (r) <= s) |
| 308 | { |
| 309 | mpz_add (r, r, b); |
| 310 | mpz_sub_ui (q, q, 1); |
| 311 | } |
| 312 | |
| 313 | return (mpz_sgn (q) > 0); |
| 314 | } |
| 315 | |
| 316 | static int |
| 317 | hgcd_ref (struct hgcd_ref *hgcd, mpz_t a, mpz_t b) |
| 318 | { |
| 319 | mp_size_t n = MAX (mpz_size (a), mpz_size (b)); |
| 320 | mp_size_t s = n/2 + 1; |
| 321 | mp_size_t asize; |
| 322 | mp_size_t bsize; |
| 323 | mpz_t q; |
| 324 | int res; |
| 325 | |
| 326 | if (mpz_size (a) <= s || mpz_size (b) <= s) |
| 327 | return 0; |
| 328 | |
| 329 | res = mpz_cmp (a, b); |
| 330 | if (res < 0) |
| 331 | { |
| 332 | mpz_sub (b, b, a); |
| 333 | if (mpz_size (b) <= s) |
| 334 | return 0; |
| 335 | |
| 336 | mpz_set_ui (hgcd->m[0][0], 1); mpz_set_ui (hgcd->m[0][1], 0); |
| 337 | mpz_set_ui (hgcd->m[1][0], 1); mpz_set_ui (hgcd->m[1][1], 1); |
| 338 | } |
| 339 | else if (res > 0) |
| 340 | { |
| 341 | mpz_sub (a, a, b); |
| 342 | if (mpz_size (a) <= s) |
| 343 | return 0; |
| 344 | |
| 345 | mpz_set_ui (hgcd->m[0][0], 1); mpz_set_ui (hgcd->m[0][1], 1); |
| 346 | mpz_set_ui (hgcd->m[1][0], 0); mpz_set_ui (hgcd->m[1][1], 1); |
| 347 | } |
| 348 | else |
| 349 | return 0; |
| 350 | |
| 351 | mpz_init (q); |
| 352 | |
| 353 | for (;;) |
| 354 | { |
| 355 | ASSERT (mpz_size (a) > s); |
| 356 | ASSERT (mpz_size (b) > s); |
| 357 | |
| 358 | if (mpz_cmp (a, b) > 0) |
| 359 | { |
| 360 | if (!sdiv_qr (q, a, s, a, b)) |
| 361 | break; |
| 362 | mpz_addmul (hgcd->m[0][1], q, hgcd->m[0][0]); |
| 363 | mpz_addmul (hgcd->m[1][1], q, hgcd->m[1][0]); |
| 364 | } |
| 365 | else |
| 366 | { |
| 367 | if (!sdiv_qr (q, b, s, b, a)) |
| 368 | break; |
| 369 | mpz_addmul (hgcd->m[0][0], q, hgcd->m[0][1]); |
| 370 | mpz_addmul (hgcd->m[1][0], q, hgcd->m[1][1]); |
| 371 | } |
| 372 | } |
| 373 | |
| 374 | mpz_clear (q); |
| 375 | |
| 376 | asize = mpz_size (a); |
| 377 | bsize = mpz_size (b); |
| 378 | return MAX (asize, bsize); |
| 379 | } |
| 380 | |
| 381 | static int |
| 382 | mpz_mpn_equal (const mpz_t a, mp_srcptr bp, mp_size_t bsize) |
| 383 | { |
| 384 | mp_srcptr ap = a->_mp_d; |
| 385 | mp_size_t asize = a->_mp_size; |
| 386 | |
| 387 | MPN_NORMALIZE (bp, bsize); |
| 388 | return asize == bsize && mpn_cmp (ap, bp, asize) == 0; |
| 389 | } |
| 390 | |
| 391 | static int |
| 392 | hgcd_ref_equal (const struct hgcd_matrix *hgcd, const struct hgcd_ref *ref) |
| 393 | { |
| 394 | unsigned i; |
| 395 | |
| 396 | for (i = 0; i<2; i++) |
| 397 | { |
| 398 | unsigned j; |
| 399 | |
| 400 | for (j = 0; j<2; j++) |
| 401 | if (!mpz_mpn_equal (ref->m[i][j], hgcd->p[i][j], hgcd->n)) |
| 402 | return 0; |
| 403 | } |
| 404 | |
| 405 | return 1; |
| 406 | } |