Home | History | Annotate | Line # | Download | only in mpn
t-mulmod_bnm1.c revision 1.1.1.2
      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