xref: /netbsd-src/external/lgpl3/gmp/dist/tests/mpn/t-hgcd.c (revision 72c7faa4dbb41dbb0238d6b4a109da0d4b236dd4)
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
main(int argc,char ** argv)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
debug_mp(mpz_t x,int base)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
one_test(mpz_t a,mpz_t b,int i)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
hgcd_ref_init(struct hgcd_ref * hgcd)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
hgcd_ref_clear(struct hgcd_ref * hgcd)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
sdiv_qr(mpz_t q,mpz_t r,mp_size_t s,const mpz_t a,const mpz_t b)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
hgcd_ref(struct hgcd_ref * hgcd,mpz_t a,mpz_t b)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
mpz_mpn_equal(const mpz_t a,mp_srcptr bp,mp_size_t bsize)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
hgcd_ref_equal(const struct hgcd_matrix * hgcd,const struct hgcd_ref * ref)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 }
407