github.com/aergoio/aergo@v1.3.1/libtool/src/gmp-6.1.2/tests/mpn/t-mulmod_bnm1.c (about)

     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 https://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  }