xref: /netbsd-src/external/lgpl3/gmp/dist/tests/mpn/t-mulmod_bnm1.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
1 /* Test for mulmod_bnm1 function.
2 
3    Contributed to the GNU project by Marco Bodrato.
4 
5 Copyright 2009 Free Software Foundation, Inc.
6 
7 This file is part of the GNU MP Library test suite.
8 
9 The GNU MP Library test suite is free software; you can redistribute it
10 and/or modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 3 of the License,
12 or (at your option) any later version.
13 
14 The GNU MP Library test suite is distributed in the hope that it will be
15 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
17 Public License for more details.
18 
19 You should have received a copy of the GNU General Public License along with
20 the GNU MP Library test suite.  If not, see http://www.gnu.org/licenses/.  */
21 
22 
23 #include <stdlib.h>
24 #include <stdio.h>
25 
26 #include "gmp.h"
27 #include "gmp-impl.h"
28 #include "tests.h"
29 
30 /* Sizes are up to 2^SIZE_LOG limbs */
31 #ifndef SIZE_LOG
32 #define SIZE_LOG 11
33 #endif
34 
35 #ifndef COUNT
36 #define COUNT 5000
37 #endif
38 
39 #define MAX_N (1L << SIZE_LOG)
40 #define MIN_N 1
41 
42 /*
43   Reference function for multiplication modulo B^rn-1.
44 
45   The result is expected to be ZERO if and only if one of the operand
46   already is. Otherwise the class [0] Mod(B^rn-1) is represented by
47   B^rn-1. This should not be a problem if mulmod_bnm1 is used to
48   combine results and obtain a natural number when one knows in
49   advance that the final value is less than (B^rn-1).
50 */
51 
52 static void
53 ref_mulmod_bnm1 (mp_ptr rp, mp_size_t rn, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn)
54 {
55   mp_limb_t cy;
56 
57   ASSERT (0 < an && an <= rn);
58   ASSERT (0 < bn && bn <= rn);
59 
60   if (an >= bn)
61     refmpn_mul (rp, ap, an, bp, bn);
62   else
63     refmpn_mul (rp, bp, bn, ap, an);
64   an += bn;
65   if (an > rn) {
66     cy = mpn_add (rp, rp, rn, rp + rn, an - rn);
67     /* If cy == 1, then the value of rp is at most B^rn - 2, so there can
68      * be no overflow when adding in the carry. */
69     MPN_INCR_U (rp, rn, cy);
70   }
71 }
72 
73 /*
74   Compare the result of the mpn_mulmod_bnm1 function in the library
75   with the reference function above.
76 */
77 
78 int
79 main (int argc, char **argv)
80 {
81   mp_ptr ap, bp, refp, pp, scratch;
82   int count = COUNT;
83   int test;
84   gmp_randstate_ptr rands;
85   TMP_DECL;
86   TMP_MARK;
87 
88   if (argc > 1)
89     {
90       char *end;
91       count = strtol (argv[1], &end, 0);
92       if (*end || count <= 0)
93 	{
94 	  fprintf (stderr, "Invalid test count: %s.\n", argv[1]);
95 	  return 1;
96 	}
97     }
98 
99   tests_start ();
100   rands = RANDS;
101 
102   ASSERT_ALWAYS (mpn_mulmod_bnm1_next_size (MAX_N) == MAX_N);
103 
104   ap = TMP_ALLOC_LIMBS (MAX_N);
105   bp = TMP_ALLOC_LIMBS (MAX_N);
106   refp = TMP_ALLOC_LIMBS (MAX_N * 4);
107   pp = 1+TMP_ALLOC_LIMBS (MAX_N + 2);
108   scratch
109     = 1+TMP_ALLOC_LIMBS (mpn_mulmod_bnm1_itch (MAX_N, MAX_N, MAX_N) + 2);
110 
111   for (test = 0; test < count; test++)
112     {
113       unsigned size_min;
114       unsigned size_range;
115       mp_size_t an,bn,rn,n;
116       mp_size_t itch;
117       mp_limb_t p_before, p_after, s_before, s_after;
118 
119       for (size_min = 1; (1L << size_min) < MIN_N; size_min++)
120 	;
121 
122       /* We generate an in the MIN_N <= n <= (1 << size_range). */
123       size_range = size_min
124 	+ gmp_urandomm_ui (rands, SIZE_LOG + 1 - size_min);
125 
126       n = MIN_N
127 	+ gmp_urandomm_ui (rands, (1L << size_range) + 1 - MIN_N);
128       n = mpn_mulmod_bnm1_next_size (n);
129 
130       if ( (test & 1) || n == 1) {
131 	/* Half of the tests are done with the main scenario in mind:
132 	   both an and bn >= rn/2 */
133 	an = ((n+1) >> 1) + gmp_urandomm_ui (rands, (n+1) >> 1);
134 	bn = ((n+1) >> 1) + gmp_urandomm_ui (rands, (n+1) >> 1);
135       } else {
136 	/* Second half of the tests are done using mulmod to compute a
137 	   full product with n/2 < an+bn <= n. */
138 	an = 1 + gmp_urandomm_ui (rands, n - 1);
139 	if (an >= n/2)
140 	  bn = 1 + gmp_urandomm_ui (rands, n - an);
141 	else
142 	  bn = n/2 + 1 - an + gmp_urandomm_ui (rands, (n+1)/2);
143       }
144 
145       /* Make sure an >= bn */
146       if (an < bn)
147 	MP_SIZE_T_SWAP (an, bn);
148 
149       mpn_random2 (ap, an);
150       mpn_random2 (bp, bn);
151 
152       /* Sometime trigger the borderline conditions
153 	 A = -1,0,+1 or B = -1,0,+1 or A*B == -1,0,1 Mod(B^{n/2}+1).
154 	 This only makes sense if there is at least a split, i.e. n is even. */
155       if ((test & 0x1f) == 1 && (n & 1) == 0) {
156 	mp_size_t x;
157 	MPN_COPY (ap, ap + (n >> 1), an - (n >> 1));
158 	MPN_ZERO (ap + an - (n >> 1) , n - an);
159 	MPN_COPY (bp, bp + (n >> 1), bn - (n >> 1));
160 	MPN_ZERO (bp + bn - (n >> 1) , n - bn);
161 	x = (n == an) ? 0 : gmp_urandomm_ui (rands, n - an);
162 	ap[x] += gmp_urandomm_ui (rands, 3) - 1;
163 	x = (n >> 1) - x % (n >> 1);
164 	bp[x] += gmp_urandomm_ui (rands, 3) - 1;
165 	/* We don't propagate carry, this means that the desired condition
166 	   is not triggered all the times. A few times are enough anyway. */
167       }
168       rn = MIN(n, an + bn);
169       mpn_random2 (pp-1, rn + 2);
170       p_before = pp[-1];
171       p_after = pp[rn];
172 
173       itch = mpn_mulmod_bnm1_itch (n, an, bn);
174       ASSERT_ALWAYS (itch <= mpn_mulmod_bnm1_itch (MAX_N, MAX_N, MAX_N));
175       mpn_random2 (scratch-1, itch+2);
176       s_before = scratch[-1];
177       s_after = scratch[itch];
178 
179       mpn_mulmod_bnm1 (  pp, n, ap, an, bp, bn, scratch);
180       ref_mulmod_bnm1 (refp, n, ap, an, bp, bn);
181       if (pp[-1] != p_before || pp[rn] != p_after
182 	  || scratch[-1] != s_before || scratch[itch] != s_after
183 	  || mpn_cmp (refp, pp, rn) != 0)
184 	{
185 	  printf ("ERROR in test %d, an = %d, bn = %d, n = %d\n",
186 		  test, (int) an, (int) bn, (int) n);
187 	  if (pp[-1] != p_before)
188 	    {
189 	      printf ("before pp:"); mpn_dump (pp -1, 1);
190 	      printf ("keep:   "); mpn_dump (&p_before, 1);
191 	    }
192 	  if (pp[rn] != p_after)
193 	    {
194 	      printf ("after pp:"); mpn_dump (pp + rn, 1);
195 	      printf ("keep:   "); mpn_dump (&p_after, 1);
196 	    }
197 	  if (scratch[-1] != s_before)
198 	    {
199 	      printf ("before scratch:"); mpn_dump (scratch-1, 1);
200 	      printf ("keep:   "); mpn_dump (&s_before, 1);
201 	    }
202 	  if (scratch[itch] != s_after)
203 	    {
204 	      printf ("after scratch:"); mpn_dump (scratch + itch, 1);
205 	      printf ("keep:   "); mpn_dump (&s_after, 1);
206 	    }
207 	  mpn_dump (ap, an);
208 	  mpn_dump (bp, bn);
209 	  mpn_dump (pp, rn);
210 	  mpn_dump (refp, rn);
211 
212 	  abort();
213 	}
214     }
215   TMP_FREE;
216   tests_end ();
217   return 0;
218 }
219