| /* Test mpz_gcd, mpz_gcdext, and mpz_gcd_ui. |
| |
| Copyright 1991, 1993, 1994, 1996, 1997, 2000-2005, 2008, 2009, 2012 Free |
| Software Foundation, Inc. |
| |
| This file is part of the GNU MP Library test suite. |
| |
| The GNU MP Library test suite is free software; you can redistribute it |
| and/or modify it under the terms of the GNU General Public License as |
| published by the Free Software Foundation; either version 3 of the License, |
| or (at your option) any later version. |
| |
| The GNU MP Library test suite is distributed in the hope that it will be |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
| Public License for more details. |
| |
| You should have received a copy of the GNU General Public License along with |
| the GNU MP Library test suite. If not, see https://www.gnu.org/licenses/. */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include "gmp-impl.h" |
| #include "tests.h" |
| |
| void one_test (mpz_t, mpz_t, mpz_t, int); |
| void debug_mp (mpz_t, int); |
| |
| static int gcdext_valid_p (const mpz_t, const mpz_t, const mpz_t, const mpz_t); |
| |
| /* Keep one_test's variables global, so that we don't need |
| to reinitialize them for each test. */ |
| mpz_t gcd1, gcd2, s, temp1, temp2, temp3; |
| |
| #define MAX_SCHOENHAGE_THRESHOLD HGCD_REDUCE_THRESHOLD |
| |
| /* Define this to make all operands be large enough for Schoenhage gcd |
| to be used. */ |
| #ifndef WHACK_SCHOENHAGE |
| #define WHACK_SCHOENHAGE 0 |
| #endif |
| |
| #if WHACK_SCHOENHAGE |
| #define MIN_OPERAND_BITSIZE (MAX_SCHOENHAGE_THRESHOLD * GMP_NUMB_BITS) |
| #else |
| #define MIN_OPERAND_BITSIZE 1 |
| #endif |
| |
| |
| void |
| check_data (void) |
| { |
| static const struct { |
| const char *a; |
| const char *b; |
| const char *want; |
| } data[] = { |
| /* This tickled a bug in gmp 4.1.2 mpn/x86/k6/gcd_finda.asm. */ |
| { "0x3FFC000007FFFFFFFFFF00000000003F83FFFFFFFFFFFFFFF80000000000000001", |
| "0x1FFE0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000000000000000001", |
| "5" } |
| }; |
| |
| mpz_t a, b, got, want; |
| int i; |
| |
| mpz_inits (a, b, got, want, NULL); |
| |
| for (i = 0; i < numberof (data); i++) |
| { |
| mpz_set_str_or_abort (a, data[i].a, 0); |
| mpz_set_str_or_abort (b, data[i].b, 0); |
| mpz_set_str_or_abort (want, data[i].want, 0); |
| mpz_gcd (got, a, b); |
| MPZ_CHECK_FORMAT (got); |
| if (mpz_cmp (got, want) != 0) |
| { |
| printf ("mpz_gcd wrong on data[%d]\n", i); |
| printf (" a %s\n", data[i].a); |
| printf (" b %s\n", data[i].b); |
| mpz_trace (" a", a); |
| mpz_trace (" b", b); |
| mpz_trace (" want", want); |
| mpz_trace (" got ", got); |
| abort (); |
| } |
| } |
| |
| mpz_clears (a, b, got, want, NULL); |
| } |
| |
| void |
| make_chain_operands (mpz_t ref, mpz_t a, mpz_t b, gmp_randstate_t rs, int nb1, int nb2, int chain_len) |
| { |
| mpz_t bs, temp1, temp2; |
| int j; |
| |
| mpz_inits (bs, temp1, temp2, NULL); |
| |
| /* Generate a division chain backwards, allowing otherwise unlikely huge |
| quotients. */ |
| |
| mpz_set_ui (a, 0); |
| mpz_urandomb (bs, rs, 32); |
| mpz_urandomb (bs, rs, mpz_get_ui (bs) % nb1 + 1); |
| mpz_rrandomb (b, rs, mpz_get_ui (bs)); |
| mpz_add_ui (b, b, 1); |
| mpz_set (ref, b); |
| |
| for (j = 0; j < chain_len; j++) |
| { |
| mpz_urandomb (bs, rs, 32); |
| mpz_urandomb (bs, rs, mpz_get_ui (bs) % nb2 + 1); |
| mpz_rrandomb (temp2, rs, mpz_get_ui (bs) + 1); |
| mpz_add_ui (temp2, temp2, 1); |
| mpz_mul (temp1, b, temp2); |
| mpz_add (a, a, temp1); |
| |
| mpz_urandomb (bs, rs, 32); |
| mpz_urandomb (bs, rs, mpz_get_ui (bs) % nb2 + 1); |
| mpz_rrandomb (temp2, rs, mpz_get_ui (bs) + 1); |
| mpz_add_ui (temp2, temp2, 1); |
| mpz_mul (temp1, a, temp2); |
| mpz_add (b, b, temp1); |
| } |
| |
| mpz_clears (bs, temp1, temp2, NULL); |
| } |
| |
| /* Test operands from a table of seed data. This variant creates the operands |
| using plain ol' mpz_rrandomb. This is a hack for better coverage of the gcd |
| code, which depends on that the random number generators give the exact |
| numbers we expect. */ |
| void |
| check_kolmo1 (void) |
| { |
| static const struct { |
| unsigned int seed; |
| int nb; |
| const char *want; |
| } data[] = { |
| { 59618, 38208, "5"}, |
| { 76521, 49024, "3"}, |
| { 85869, 54976, "1"}, |
| { 99449, 63680, "1"}, |
| {112453, 72000, "1"} |
| }; |
| |
| gmp_randstate_t rs; |
| mpz_t bs, a, b, want; |
| int i, unb, vnb, nb; |
| |
| gmp_randinit_default (rs); |
| |
| mpz_inits (bs, a, b, want, NULL); |
| |
| for (i = 0; i < numberof (data); i++) |
| { |
| nb = data[i].nb; |
| |
| gmp_randseed_ui (rs, data[i].seed); |
| |
| mpz_urandomb (bs, rs, 32); |
| unb = mpz_get_ui (bs) % nb; |
| mpz_urandomb (bs, rs, 32); |
| vnb = mpz_get_ui (bs) % nb; |
| |
| mpz_rrandomb (a, rs, unb); |
| mpz_rrandomb (b, rs, vnb); |
| |
| mpz_set_str_or_abort (want, data[i].want, 0); |
| |
| one_test (a, b, want, -1); |
| } |
| |
| mpz_clears (bs, a, b, want, NULL); |
| gmp_randclear (rs); |
| } |
| |
| /* Test operands from a table of seed data. This variant creates the operands |
| using a division chain. This is a hack for better coverage of the gcd |
| code, which depends on that the random number generators give the exact |
| numbers we expect. */ |
| void |
| check_kolmo2 (void) |
| { |
| static const struct { |
| unsigned int seed; |
| int nb, chain_len; |
| } data[] = { |
| { 917, 15, 5 }, |
| { 1032, 18, 6 }, |
| { 1167, 18, 6 }, |
| { 1174, 18, 6 }, |
| { 1192, 18, 6 }, |
| }; |
| |
| gmp_randstate_t rs; |
| mpz_t bs, a, b, want; |
| int i; |
| |
| gmp_randinit_default (rs); |
| |
| mpz_inits (bs, a, b, want, NULL); |
| |
| for (i = 0; i < numberof (data); i++) |
| { |
| gmp_randseed_ui (rs, data[i].seed); |
| make_chain_operands (want, a, b, rs, data[i].nb, data[i].nb, data[i].chain_len); |
| one_test (a, b, want, -1); |
| } |
| |
| mpz_clears (bs, a, b, want, NULL); |
| gmp_randclear (rs); |
| } |
| |
| int |
| main (int argc, char **argv) |
| { |
| mpz_t op1, op2, ref; |
| int i, chain_len; |
| gmp_randstate_ptr rands; |
| mpz_t bs; |
| unsigned long bsi, size_range; |
| long int reps = 200; |
| |
| tests_start (); |
| TESTS_REPS (reps, argv, argc); |
| |
| rands = RANDS; |
| |
| mpz_inits (bs, op1, op2, ref, gcd1, gcd2, temp1, temp2, temp3, s, NULL); |
| |
| check_data (); |
| check_kolmo1 (); |
| check_kolmo2 (); |
| |
| /* Testcase to exercise the u0 == u1 case in mpn_gcdext_lehmer_n. */ |
| /* mpz_set_ui (op2, GMP_NUMB_MAX); */ /* FIXME: Huge limb doesn't always fit */ |
| mpz_set_ui (op2, 0); |
| mpz_setbit (op2, GMP_NUMB_BITS); |
| mpz_sub_ui (op2, op2, 1); |
| mpz_mul_2exp (op1, op2, 100); |
| mpz_add (op1, op1, op2); |
| mpz_mul_ui (op2, op2, 2); |
| one_test (op1, op2, NULL, -1); |
| |
| for (i = 0; i < reps; i++) |
| { |
| /* Generate plain operands with unknown gcd. These types of operands |
| have proven to trigger certain bugs in development versions of the |
| gcd code. The "hgcd->row[3].rsize > M" ASSERT is not triggered by |
| the division chain code below, but that is most likely just a result |
| of that other ASSERTs are triggered before it. */ |
| |
| mpz_urandomb (bs, rands, 32); |
| size_range = mpz_get_ui (bs) % 17 + 2; |
| |
| mpz_urandomb (bs, rands, size_range); |
| mpz_rrandomb (op1, rands, mpz_get_ui (bs) + MIN_OPERAND_BITSIZE); |
| mpz_urandomb (bs, rands, size_range); |
| mpz_rrandomb (op2, rands, mpz_get_ui (bs) + MIN_OPERAND_BITSIZE); |
| |
| mpz_urandomb (bs, rands, 8); |
| bsi = mpz_get_ui (bs); |
| |
| if ((bsi & 0x3c) == 4) |
| mpz_mul (op1, op1, op2); /* make op1 a multiple of op2 */ |
| else if ((bsi & 0x3c) == 8) |
| mpz_mul (op2, op1, op2); /* make op2 a multiple of op1 */ |
| |
| if ((bsi & 1) != 0) |
| mpz_neg (op1, op1); |
| if ((bsi & 2) != 0) |
| mpz_neg (op2, op2); |
| |
| one_test (op1, op2, NULL, i); |
| |
| /* Generate a division chain backwards, allowing otherwise unlikely huge |
| quotients. */ |
| |
| mpz_urandomb (bs, rands, 32); |
| chain_len = mpz_get_ui (bs) % LOG2C (GMP_NUMB_BITS * MAX_SCHOENHAGE_THRESHOLD); |
| mpz_urandomb (bs, rands, 32); |
| chain_len = mpz_get_ui (bs) % (1 << chain_len) / 32; |
| |
| make_chain_operands (ref, op1, op2, rands, 16, 12, chain_len); |
| |
| one_test (op1, op2, ref, i); |
| } |
| |
| /* Check that we can use NULL as first argument of mpz_gcdext. */ |
| mpz_set_si (op1, -10); |
| mpz_set_si (op2, 0); |
| mpz_gcdext (NULL, temp1, temp2, op1, op2); |
| ASSERT_ALWAYS (mpz_cmp_si (temp1, -1) == 0); |
| ASSERT_ALWAYS (mpz_cmp_si (temp2, 0) == 0); |
| mpz_set_si (op2, 6); |
| mpz_gcdext (NULL, temp1, temp2, op1, op2); |
| ASSERT_ALWAYS (mpz_cmp_si (temp1, 1) == 0); |
| ASSERT_ALWAYS (mpz_cmp_si (temp2, 2) == 0); |
| |
| mpz_clears (bs, op1, op2, ref, gcd1, gcd2, temp1, temp2, temp3, s, NULL); |
| |
| tests_end (); |
| exit (0); |
| } |
| |
| void |
| debug_mp (mpz_t x, int base) |
| { |
| mpz_out_str (stderr, base, x); fputc ('\n', stderr); |
| } |
| |
| void |
| one_test (mpz_t op1, mpz_t op2, mpz_t ref, int i) |
| { |
| /* |
| printf ("%d %d %d\n", SIZ (op1), SIZ (op2), ref != NULL ? SIZ (ref) : 0); |
| fflush (stdout); |
| */ |
| |
| /* |
| fprintf (stderr, "op1="); debug_mp (op1, -16); |
| fprintf (stderr, "op2="); debug_mp (op2, -16); |
| */ |
| |
| mpz_gcdext (gcd1, s, NULL, op1, op2); |
| MPZ_CHECK_FORMAT (gcd1); |
| MPZ_CHECK_FORMAT (s); |
| |
| if (ref && mpz_cmp (ref, gcd1) != 0) |
| { |
| fprintf (stderr, "ERROR in test %d\n", i); |
| fprintf (stderr, "mpz_gcdext returned incorrect result\n"); |
| fprintf (stderr, "op1="); debug_mp (op1, -16); |
| fprintf (stderr, "op2="); debug_mp (op2, -16); |
| fprintf (stderr, "expected result:\n"); debug_mp (ref, -16); |
| fprintf (stderr, "mpz_gcdext returns:\n");debug_mp (gcd1, -16); |
| abort (); |
| } |
| |
| if (!gcdext_valid_p(op1, op2, gcd1, s)) |
| { |
| fprintf (stderr, "ERROR in test %d\n", i); |
| fprintf (stderr, "mpz_gcdext returned invalid result\n"); |
| fprintf (stderr, "op1="); debug_mp (op1, -16); |
| fprintf (stderr, "op2="); debug_mp (op2, -16); |
| fprintf (stderr, "mpz_gcdext returns:\n");debug_mp (gcd1, -16); |
| fprintf (stderr, "s="); debug_mp (s, -16); |
| abort (); |
| } |
| |
| mpz_gcd (gcd2, op1, op2); |
| MPZ_CHECK_FORMAT (gcd2); |
| |
| if (mpz_cmp (gcd2, gcd1) != 0) |
| { |
| fprintf (stderr, "ERROR in test %d\n", i); |
| fprintf (stderr, "mpz_gcd returned incorrect result\n"); |
| fprintf (stderr, "op1="); debug_mp (op1, -16); |
| fprintf (stderr, "op2="); debug_mp (op2, -16); |
| fprintf (stderr, "expected result:\n"); debug_mp (gcd1, -16); |
| fprintf (stderr, "mpz_gcd returns:\n"); debug_mp (gcd2, -16); |
| abort (); |
| } |
| |
| /* This should probably move to t-gcd_ui.c */ |
| if (mpz_fits_ulong_p (op1) || mpz_fits_ulong_p (op2)) |
| { |
| if (mpz_fits_ulong_p (op1)) |
| mpz_gcd_ui (gcd2, op2, mpz_get_ui (op1)); |
| else |
| mpz_gcd_ui (gcd2, op1, mpz_get_ui (op2)); |
| if (mpz_cmp (gcd2, gcd1)) |
| { |
| fprintf (stderr, "ERROR in test %d\n", i); |
| fprintf (stderr, "mpz_gcd_ui returned incorrect result\n"); |
| fprintf (stderr, "op1="); debug_mp (op1, -16); |
| fprintf (stderr, "op2="); debug_mp (op2, -16); |
| fprintf (stderr, "expected result:\n"); debug_mp (gcd1, -16); |
| fprintf (stderr, "mpz_gcd_ui returns:\n"); debug_mp (gcd2, -16); |
| abort (); |
| } |
| } |
| |
| mpz_gcdext (gcd2, temp1, temp2, op1, op2); |
| MPZ_CHECK_FORMAT (gcd2); |
| MPZ_CHECK_FORMAT (temp1); |
| MPZ_CHECK_FORMAT (temp2); |
| |
| mpz_mul (temp1, temp1, op1); |
| mpz_mul (temp2, temp2, op2); |
| mpz_add (temp1, temp1, temp2); |
| |
| if (mpz_cmp (gcd1, gcd2) != 0 |
| || mpz_cmp (gcd2, temp1) != 0) |
| { |
| fprintf (stderr, "ERROR in test %d\n", i); |
| fprintf (stderr, "mpz_gcdext returned incorrect result\n"); |
| fprintf (stderr, "op1="); debug_mp (op1, -16); |
| fprintf (stderr, "op2="); debug_mp (op2, -16); |
| fprintf (stderr, "expected result:\n"); debug_mp (gcd1, -16); |
| fprintf (stderr, "mpz_gcdext returns:\n");debug_mp (gcd2, -16); |
| abort (); |
| } |
| } |
| |
| /* Called when g is supposed to be gcd(a,b), and g = s a + t b, for some t. |
| Uses temp1, temp2 and temp3. */ |
| static int |
| gcdext_valid_p (const mpz_t a, const mpz_t b, const mpz_t g, const mpz_t s) |
| { |
| /* It's not clear that gcd(0,0) is well defined, but we allow it and require that |
| gcd(0,0) = 0. */ |
| if (mpz_sgn (g) < 0) |
| return 0; |
| |
| if (mpz_sgn (a) == 0) |
| { |
| /* Must have g == abs (b). Any value for s is in some sense "correct", |
| but it makes sense to require that s == 0. */ |
| return mpz_cmpabs (g, b) == 0 && mpz_sgn (s) == 0; |
| } |
| else if (mpz_sgn (b) == 0) |
| { |
| /* Must have g == abs (a), s == sign (a) */ |
| return mpz_cmpabs (g, a) == 0 && mpz_cmp_si (s, mpz_sgn (a)) == 0; |
| } |
| |
| if (mpz_sgn (g) <= 0) |
| return 0; |
| |
| mpz_tdiv_qr (temp1, temp3, a, g); |
| if (mpz_sgn (temp3) != 0) |
| return 0; |
| |
| mpz_tdiv_qr (temp2, temp3, b, g); |
| if (mpz_sgn (temp3) != 0) |
| return 0; |
| |
| /* Require that 2 |s| < |b/g|, or |s| == 1. */ |
| if (mpz_cmpabs_ui (s, 1) > 0) |
| { |
| mpz_mul_2exp (temp3, s, 1); |
| if (mpz_cmpabs (temp3, temp2) >= 0) |
| return 0; |
| } |
| |
| /* Compute the other cofactor. */ |
| mpz_mul(temp2, s, a); |
| mpz_sub(temp2, g, temp2); |
| mpz_tdiv_qr(temp2, temp3, temp2, b); |
| |
| if (mpz_sgn (temp3) != 0) |
| return 0; |
| |
| /* Require that 2 |t| < |a/g| or |t| == 1*/ |
| if (mpz_cmpabs_ui (temp2, 1) > 0) |
| { |
| mpz_mul_2exp (temp2, temp2, 1); |
| if (mpz_cmpabs (temp2, temp1) >= 0) |
| return 0; |
| } |
| return 1; |
| } |