(root)/
gmp-6.3.0/
tests/
mpn/
t-hgcd_appr.c
       1  /* Test mpn_hgcd_appr.
       2  
       3  Copyright 1991, 1993, 1994, 1996, 1997, 2000-2004, 2011 Free Software
       4  Foundation, 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  #include <string.h>
      24  
      25  #include "gmp-impl.h"
      26  #include "tests.h"
      27  
      28  static mp_size_t one_test (mpz_t, mpz_t, int);
      29  static void debug_mp (mpz_t, int);
      30  
      31  #define MIN_OPERAND_SIZE 2
      32  
      33  struct hgcd_ref
      34  {
      35    mpz_t m[2][2];
      36  };
      37  
      38  static void hgcd_ref_init (struct hgcd_ref *hgcd);
      39  static void hgcd_ref_clear (struct hgcd_ref *hgcd);
      40  static int hgcd_ref (struct hgcd_ref *hgcd, mpz_t a, mpz_t b);
      41  static int hgcd_ref_equal (const struct hgcd_ref *, const struct hgcd_ref *);
      42  static int hgcd_appr_valid_p (mpz_t, mpz_t, mp_size_t, struct hgcd_ref *,
      43  			      mpz_t, mpz_t, mp_size_t, struct hgcd_matrix *);
      44  
      45  static int verbose_flag = 0;
      46  
      47  int
      48  main (int argc, char **argv)
      49  {
      50    mpz_t op1, op2, temp1, temp2;
      51    int i, j, chain_len;
      52    gmp_randstate_ptr rands;
      53    mpz_t bs;
      54    unsigned long size_range;
      55  
      56    if (argc > 1)
      57      {
      58        if (strcmp (argv[1], "-v") == 0)
      59  	verbose_flag = 1;
      60        else
      61  	{
      62  	  fprintf (stderr, "Invalid argument.\n");
      63  	  return 1;
      64  	}
      65      }
      66  
      67    tests_start ();
      68    rands = RANDS;
      69  
      70    mpz_init (bs);
      71    mpz_init (op1);
      72    mpz_init (op2);
      73    mpz_init (temp1);
      74    mpz_init (temp2);
      75  
      76    for (i = 0; i < 15; i++)
      77      {
      78        /* Generate plain operands with unknown gcd.  These types of operands
      79  	 have proven to trigger certain bugs in development versions of the
      80  	 gcd code. */
      81  
      82        mpz_urandomb (bs, rands, 32);
      83        size_range = mpz_get_ui (bs) % 13 + 2;
      84  
      85        mpz_urandomb (bs, rands, size_range);
      86        mpz_urandomb (op1, rands, mpz_get_ui (bs) + MIN_OPERAND_SIZE);
      87        mpz_urandomb (bs, rands, size_range);
      88        mpz_urandomb (op2, rands, mpz_get_ui (bs) + MIN_OPERAND_SIZE);
      89  
      90        if (mpz_cmp (op1, op2) < 0)
      91  	mpz_swap (op1, op2);
      92  
      93        if (mpz_size (op1) > 0)
      94  	one_test (op1, op2, i);
      95  
      96        /* Generate a division chain backwards, allowing otherwise
      97  	 unlikely huge quotients.  */
      98  
      99        mpz_set_ui (op1, 0);
     100        mpz_urandomb (bs, rands, 32);
     101        mpz_urandomb (bs, rands, mpz_get_ui (bs) % 16 + 1);
     102        mpz_rrandomb (op2, rands, mpz_get_ui (bs));
     103        mpz_add_ui (op2, op2, 1);
     104  
     105  #if 0
     106        chain_len = 1000000;
     107  #else
     108        mpz_urandomb (bs, rands, 32);
     109        chain_len = mpz_get_ui (bs) % (GMP_NUMB_BITS * GCD_DC_THRESHOLD / 256);
     110  #endif
     111  
     112        for (j = 0; j < chain_len; j++)
     113  	{
     114  	  mpz_urandomb (bs, rands, 32);
     115  	  mpz_urandomb (bs, rands, mpz_get_ui (bs) % 12 + 1);
     116  	  mpz_rrandomb (temp2, rands, mpz_get_ui (bs) + 1);
     117  	  mpz_add_ui (temp2, temp2, 1);
     118  	  mpz_mul (temp1, op2, temp2);
     119  	  mpz_add (op1, op1, temp1);
     120  
     121  	  /* Don't generate overly huge operands.  */
     122  	  if (SIZ (op1) > 3 * GCD_DC_THRESHOLD)
     123  	    break;
     124  
     125  	  mpz_urandomb (bs, rands, 32);
     126  	  mpz_urandomb (bs, rands, mpz_get_ui (bs) % 12 + 1);
     127  	  mpz_rrandomb (temp2, rands, mpz_get_ui (bs) + 1);
     128  	  mpz_add_ui (temp2, temp2, 1);
     129  	  mpz_mul (temp1, op1, temp2);
     130  	  mpz_add (op2, op2, temp1);
     131  
     132  	  /* Don't generate overly huge operands.  */
     133  	  if (SIZ (op2) > 3 * GCD_DC_THRESHOLD)
     134  	    break;
     135  	}
     136        if (mpz_cmp (op1, op2) < 0)
     137  	mpz_swap (op1, op2);
     138  
     139        if (mpz_size (op1) > 0)
     140  	one_test (op1, op2, i);
     141      }
     142  
     143    mpz_clear (bs);
     144    mpz_clear (op1);
     145    mpz_clear (op2);
     146    mpz_clear (temp1);
     147    mpz_clear (temp2);
     148  
     149    tests_end ();
     150    exit (0);
     151  }
     152  
     153  static void
     154  debug_mp (mpz_t x, int base)
     155  {
     156    mpz_out_str (stderr, base, x); fputc ('\n', stderr);
     157  }
     158  
     159  static mp_size_t
     160  one_test (mpz_t a, mpz_t b, int i)
     161  {
     162    struct hgcd_matrix hgcd;
     163    struct hgcd_ref ref;
     164  
     165    mpz_t ref_r0;
     166    mpz_t ref_r1;
     167    mpz_t hgcd_r0;
     168    mpz_t hgcd_r1;
     169  
     170    int res[2];
     171    mp_size_t asize;
     172    mp_size_t bsize;
     173  
     174    mp_size_t hgcd_init_scratch;
     175    mp_size_t hgcd_scratch;
     176  
     177    mp_ptr hgcd_init_tp;
     178    mp_ptr hgcd_tp;
     179    mp_limb_t marker[4];
     180  
     181    asize = a->_mp_size;
     182    bsize = b->_mp_size;
     183  
     184    ASSERT (asize >= bsize);
     185  
     186    hgcd_init_scratch = MPN_HGCD_MATRIX_INIT_ITCH (asize);
     187    hgcd_init_tp = refmpn_malloc_limbs (hgcd_init_scratch + 2) + 1;
     188    mpn_hgcd_matrix_init (&hgcd, asize, hgcd_init_tp);
     189  
     190    hgcd_scratch = mpn_hgcd_appr_itch (asize);
     191    hgcd_tp = refmpn_malloc_limbs (hgcd_scratch + 2) + 1;
     192  
     193    mpn_random (marker, 4);
     194  
     195    hgcd_init_tp[-1] = marker[0];
     196    hgcd_init_tp[hgcd_init_scratch] = marker[1];
     197    hgcd_tp[-1] = marker[2];
     198    hgcd_tp[hgcd_scratch] = marker[3];
     199  
     200  #if 0
     201    fprintf (stderr,
     202  	   "one_test: i = %d asize = %d, bsize = %d\n",
     203  	   i, a->_mp_size, b->_mp_size);
     204  
     205    gmp_fprintf (stderr,
     206  	       "one_test: i = %d\n"
     207  	       "  a = %Zx\n"
     208  	       "  b = %Zx\n",
     209  	       i, a, b);
     210  #endif
     211    hgcd_ref_init (&ref);
     212  
     213    mpz_init_set (ref_r0, a);
     214    mpz_init_set (ref_r1, b);
     215    res[0] = hgcd_ref (&ref, ref_r0, ref_r1);
     216  
     217    mpz_init_set (hgcd_r0, a);
     218    mpz_init_set (hgcd_r1, b);
     219    if (bsize < asize)
     220      {
     221        _mpz_realloc (hgcd_r1, asize);
     222        MPN_ZERO (hgcd_r1->_mp_d + bsize, asize - bsize);
     223      }
     224    res[1] = mpn_hgcd_appr (hgcd_r0->_mp_d,
     225  			  hgcd_r1->_mp_d,
     226  			  asize,
     227  			  &hgcd, hgcd_tp);
     228  
     229    if (hgcd_init_tp[-1] != marker[0]
     230        || hgcd_init_tp[hgcd_init_scratch] != marker[1]
     231        || hgcd_tp[-1] != marker[2]
     232        || hgcd_tp[hgcd_scratch] != marker[3])
     233      {
     234        fprintf (stderr, "ERROR in test %d\n", i);
     235        fprintf (stderr, "scratch space overwritten!\n");
     236  
     237        if (hgcd_init_tp[-1] != marker[0])
     238  	gmp_fprintf (stderr,
     239  		     "before init_tp: %Mx\n"
     240  		     "expected: %Mx\n",
     241  		     hgcd_init_tp[-1], marker[0]);
     242        if (hgcd_init_tp[hgcd_init_scratch] != marker[1])
     243  	gmp_fprintf (stderr,
     244  		     "after init_tp: %Mx\n"
     245  		     "expected: %Mx\n",
     246  		     hgcd_init_tp[hgcd_init_scratch], marker[1]);
     247        if (hgcd_tp[-1] != marker[2])
     248  	gmp_fprintf (stderr,
     249  		     "before tp: %Mx\n"
     250  		     "expected: %Mx\n",
     251  		     hgcd_tp[-1], marker[2]);
     252        if (hgcd_tp[hgcd_scratch] != marker[3])
     253  	gmp_fprintf (stderr,
     254  		     "after tp: %Mx\n"
     255  		     "expected: %Mx\n",
     256  		     hgcd_tp[hgcd_scratch], marker[3]);
     257  
     258        abort ();
     259      }
     260  
     261    if (!hgcd_appr_valid_p (a, b, res[0], &ref, ref_r0, ref_r1,
     262  			  res[1], &hgcd))
     263      {
     264        fprintf (stderr, "ERROR in test %d\n", i);
     265        fprintf (stderr, "Invalid results for hgcd and hgcd_ref\n");
     266        fprintf (stderr, "op1=");                 debug_mp (a, -16);
     267        fprintf (stderr, "op2=");                 debug_mp (b, -16);
     268        fprintf (stderr, "hgcd_ref: %ld\n", (long) res[0]);
     269        fprintf (stderr, "mpn_hgcd_appr: %ld\n", (long) res[1]);
     270        abort ();
     271      }
     272  
     273    refmpn_free_limbs (hgcd_init_tp - 1);
     274    refmpn_free_limbs (hgcd_tp - 1);
     275    hgcd_ref_clear (&ref);
     276    mpz_clear (ref_r0);
     277    mpz_clear (ref_r1);
     278    mpz_clear (hgcd_r0);
     279    mpz_clear (hgcd_r1);
     280  
     281    return res[0];
     282  }
     283  
     284  static void
     285  hgcd_ref_init (struct hgcd_ref *hgcd)
     286  {
     287    unsigned i;
     288    for (i = 0; i<2; i++)
     289      {
     290        unsigned j;
     291        for (j = 0; j<2; j++)
     292  	mpz_init (hgcd->m[i][j]);
     293      }
     294  }
     295  
     296  static void
     297  hgcd_ref_clear (struct hgcd_ref *hgcd)
     298  {
     299    unsigned i;
     300    for (i = 0; i<2; i++)
     301      {
     302        unsigned j;
     303        for (j = 0; j<2; j++)
     304  	mpz_clear (hgcd->m[i][j]);
     305      }
     306  }
     307  
     308  static int
     309  sdiv_qr (mpz_t q, mpz_t r, mp_size_t s, const mpz_t a, const mpz_t b)
     310  {
     311    mpz_fdiv_qr (q, r, a, b);
     312    if (mpz_size (r) <= s)
     313      {
     314        mpz_add (r, r, b);
     315        mpz_sub_ui (q, q, 1);
     316      }
     317  
     318    return (mpz_sgn (q) > 0);
     319  }
     320  
     321  static int
     322  hgcd_ref (struct hgcd_ref *hgcd, mpz_t a, mpz_t b)
     323  {
     324    mp_size_t n = MAX (mpz_size (a), mpz_size (b));
     325    mp_size_t s = n/2 + 1;
     326    mpz_t q;
     327    int res;
     328  
     329    if (mpz_size (a) <= s || mpz_size (b) <= s)
     330      return 0;
     331  
     332    res = mpz_cmp (a, b);
     333    if (res < 0)
     334      {
     335        mpz_sub (b, b, a);
     336        if (mpz_size (b) <= s)
     337  	return 0;
     338  
     339        mpz_set_ui (hgcd->m[0][0], 1); mpz_set_ui (hgcd->m[0][1], 0);
     340        mpz_set_ui (hgcd->m[1][0], 1); mpz_set_ui (hgcd->m[1][1], 1);
     341      }
     342    else if (res > 0)
     343      {
     344        mpz_sub (a, a, b);
     345        if (mpz_size (a) <= s)
     346  	return 0;
     347  
     348        mpz_set_ui (hgcd->m[0][0], 1); mpz_set_ui (hgcd->m[0][1], 1);
     349        mpz_set_ui (hgcd->m[1][0], 0); mpz_set_ui (hgcd->m[1][1], 1);
     350      }
     351    else
     352      return 0;
     353  
     354    mpz_init (q);
     355  
     356    for (;;)
     357      {
     358        ASSERT (mpz_size (a) > s);
     359        ASSERT (mpz_size (b) > s);
     360  
     361        if (mpz_cmp (a, b) > 0)
     362  	{
     363  	  if (!sdiv_qr (q, a, s, a, b))
     364  	    break;
     365  	  mpz_addmul (hgcd->m[0][1], q, hgcd->m[0][0]);
     366  	  mpz_addmul (hgcd->m[1][1], q, hgcd->m[1][0]);
     367  	}
     368        else
     369  	{
     370  	  if (!sdiv_qr (q, b, s, b, a))
     371  	    break;
     372  	  mpz_addmul (hgcd->m[0][0], q, hgcd->m[0][1]);
     373  	  mpz_addmul (hgcd->m[1][0], q, hgcd->m[1][1]);
     374  	}
     375      }
     376  
     377    mpz_clear (q);
     378  
     379    return 1;
     380  }
     381  
     382  static int
     383  hgcd_ref_equal (const struct hgcd_ref *A, const struct hgcd_ref *B)
     384  {
     385    unsigned i;
     386  
     387    for (i = 0; i<2; i++)
     388      {
     389        unsigned j;
     390  
     391        for (j = 0; j<2; j++)
     392  	if (mpz_cmp (A->m[i][j], B->m[i][j]) != 0)
     393  	  return 0;
     394      }
     395  
     396    return 1;
     397  }
     398  
     399  static int
     400  hgcd_appr_valid_p (mpz_t a, mpz_t b, mp_size_t res0,
     401  		   struct hgcd_ref *ref, mpz_t ref_r0, mpz_t ref_r1,
     402  		   mp_size_t res1, struct hgcd_matrix *hgcd)
     403  {
     404    mp_size_t n = MAX (mpz_size (a), mpz_size (b));
     405    mp_size_t s = n/2 + 1;
     406  
     407    mp_bitcnt_t dbits, abits, margin;
     408    mpz_t appr_r0, appr_r1, t, q;
     409    struct hgcd_ref appr;
     410  
     411    if (!res0)
     412      {
     413        if (!res1)
     414  	return 1;
     415  
     416        fprintf (stderr, "mpn_hgcd_appr returned 1 when no reduction possible.\n");
     417        return 0;
     418      }
     419  
     420    /* NOTE: No *_clear calls on error return, since we're going to
     421       abort anyway. */
     422    mpz_init (t);
     423    mpz_init (q);
     424    hgcd_ref_init (&appr);
     425    mpz_init (appr_r0);
     426    mpz_init (appr_r1);
     427  
     428    if (mpz_size (ref_r0) <= s)
     429      {
     430        fprintf (stderr, "ref_r0 too small!!!: "); debug_mp (ref_r0, 16);
     431        return 0;
     432      }
     433    if (mpz_size (ref_r1) <= s)
     434      {
     435        fprintf (stderr, "ref_r1 too small!!!: "); debug_mp (ref_r1, 16);
     436        return 0;
     437      }
     438  
     439    mpz_sub (t, ref_r0, ref_r1);
     440    dbits = mpz_sizeinbase (t, 2);
     441    if (dbits > s*GMP_NUMB_BITS)
     442      {
     443        fprintf (stderr, "ref |r0 - r1| too large!!!: "); debug_mp (t, 16);
     444        return 0;
     445      }
     446  
     447    if (!res1)
     448      {
     449        mpz_set (appr_r0, a);
     450        mpz_set (appr_r1, b);
     451      }
     452    else
     453      {
     454        unsigned i;
     455  
     456        for (i = 0; i<2; i++)
     457  	{
     458  	  unsigned j;
     459  
     460  	  for (j = 0; j<2; j++)
     461  	    {
     462  	      mp_size_t mn = hgcd->n;
     463  	      MPN_NORMALIZE (hgcd->p[i][j], mn);
     464  	      mpz_realloc (appr.m[i][j], mn);
     465  	      MPN_COPY (PTR (appr.m[i][j]), hgcd->p[i][j], mn);
     466  	      SIZ (appr.m[i][j]) = mn;
     467  	    }
     468  	}
     469        mpz_mul (appr_r0, appr.m[1][1], a);
     470        mpz_mul (t, appr.m[0][1], b);
     471        mpz_sub (appr_r0, appr_r0, t);
     472        if (mpz_sgn (appr_r0) <= 0
     473  	  || mpz_size (appr_r0) <= s)
     474  	{
     475  	  fprintf (stderr, "appr_r0 too small: "); debug_mp (appr_r0, 16);
     476  	  return 0;
     477  	}
     478  
     479        mpz_mul (appr_r1, appr.m[1][0], a);
     480        mpz_mul (t, appr.m[0][0], b);
     481        mpz_sub (appr_r1, t, appr_r1);
     482        if (mpz_sgn (appr_r1) <= 0
     483  	  || mpz_size (appr_r1) <= s)
     484  	{
     485  	  fprintf (stderr, "appr_r1 too small: "); debug_mp (appr_r1, 16);
     486  	  return 0;
     487  	}
     488      }
     489  
     490    mpz_sub (t, appr_r0, appr_r1);
     491    abits = mpz_sizeinbase (t, 2);
     492    if (abits < dbits)
     493      {
     494        fprintf (stderr, "|r0 - r1| too small: "); debug_mp (t, 16);
     495        return 0;
     496      }
     497  
     498    /* We lose one bit each time we discard the least significant limbs.
     499       For the lehmer code, that can happen at most s * (GMP_NUMB_BITS)
     500       / (GMP_NUMB_BITS - 1) times. For the dc code, we lose an entire
     501       limb (or more?) for each level of recursion. */
     502  
     503    margin = (n/2+1) * GMP_NUMB_BITS / (GMP_NUMB_BITS - 1);
     504    {
     505      mp_size_t rn;
     506      for (rn = n; ABOVE_THRESHOLD (rn, HGCD_APPR_THRESHOLD); rn = (rn + 1)/2)
     507        margin += GMP_NUMB_BITS;
     508    }
     509  
     510    if (verbose_flag && abits > dbits)
     511      fprintf (stderr, "n = %u: sbits = %u: ref #(r0-r1): %u, appr #(r0-r1): %u excess: %d, margin: %u\n",
     512  	     (unsigned) n, (unsigned) s*GMP_NUMB_BITS,
     513  	     (unsigned) dbits, (unsigned) abits,
     514  	     (int) (abits - s * GMP_NUMB_BITS), (unsigned) margin);
     515  
     516    if (abits > s*GMP_NUMB_BITS + margin)
     517      {
     518        fprintf (stderr, "appr |r0 - r1| much larger than minimal (by %u bits, margin = %u bits)\n",
     519  	       (unsigned) (abits - s*GMP_NUMB_BITS), (unsigned) margin);
     520        return 0;
     521      }
     522  
     523    while (mpz_cmp (appr_r0, ref_r0) > 0 || mpz_cmp (appr_r1, ref_r1) > 0)
     524      {
     525        ASSERT (mpz_size (appr_r0) > s);
     526        ASSERT (mpz_size (appr_r1) > s);
     527  
     528        if (mpz_cmp (appr_r0, appr_r1) > 0)
     529  	{
     530  	  if (!sdiv_qr (q, appr_r0, s, appr_r0, appr_r1))
     531  	    break;
     532  	  mpz_addmul (appr.m[0][1], q, appr.m[0][0]);
     533  	  mpz_addmul (appr.m[1][1], q, appr.m[1][0]);
     534  	}
     535        else
     536  	{
     537  	  if (!sdiv_qr (q, appr_r1, s, appr_r1, appr_r0))
     538  	    break;
     539  	  mpz_addmul (appr.m[0][0], q, appr.m[0][1]);
     540  	  mpz_addmul (appr.m[1][0], q, appr.m[1][1]);
     541  	}
     542      }
     543  
     544    if (mpz_cmp (appr_r0, ref_r0) != 0
     545        || mpz_cmp (appr_r1, ref_r1) != 0
     546        || !hgcd_ref_equal (ref, &appr))
     547      {
     548        fprintf (stderr, "appr_r0: "); debug_mp (appr_r0, 16);
     549        fprintf (stderr, "ref_r0: "); debug_mp (ref_r0, 16);
     550  
     551        fprintf (stderr, "appr_r1: "); debug_mp (appr_r1, 16);
     552        fprintf (stderr, "ref_r1: "); debug_mp (ref_r1, 16);
     553  
     554        return 0;
     555      }
     556    mpz_clear (t);
     557    mpz_clear (q);
     558    hgcd_ref_clear (&appr);
     559    mpz_clear (appr_r0);
     560    mpz_clear (appr_r1);
     561  
     562    return 1;
     563  }