
/* Test mpn_mulmod_2expm1

Copyright 2009 Jason Moxham

This file is part of the MPIR Library.

The MPIR Library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License, or (at
your option) any later version.

The MPIR Library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
License for more details.

You should have received a copy of the GNU Lesser General Public License
along with the MPIR Library; see the file COPYING.LIB.  If not, write
to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/

#include <stdio.h>
#include <stdlib.h>
#include "mpir.h"
#include "gmp-impl.h"
#include "tests.h"

#define DISP(xp,xn)                        \
   do {                                    \
      int __t;                             \
      printf("%s=", #xp);                  \
      for(__t = (xn) - 1; __t >= 0; __t--) \
         printf("%lX ", (xp)[__t]);        \
      printf("\n");                        \
   } while(0)

int
main (void)
{
    unsigned long bp, xn, n, b, zn, c;
    gmp_randstate_t rands;
    int qpn, j, k, i, l, i1, k1, j1, i2, k2, j2;
    mp_limb_t xp[10000], dp[10000], qp[10000], yp[10000], rp[10000], zp[10000],
        tp[10000];
    tests_start ();
    gmp_randinit_default (rands);

    for (b = 1; b < 600; b++)
    {
        xn = BITS_TO_LIMBS (b);
        k = xn * GMP_NUMB_BITS - b;
        MPN_ZERO (dp, xn);
        mpn_com_n (dp, dp, xn);
        dp[xn - 1] &= GMP_NUMB_MASK >> k;       /* dp is 2^b-1 */
        for (c = 0; c < 5 * b; c++)
        {
            mpn_rrandom (xp, rands, xn);
            mpn_rrandom (yp, rands, xn);
            xp[xn - 1] &= GMP_NUMB_MASK >> k;
            yp[xn - 1] &= GMP_NUMB_MASK >> k;
            mpn_mul_n (zp, xp, yp, xn);
            zn = xn * 2;
            MPN_NORMALIZE (zp, zn);
            if (zn >= xn)
            {
                mpn_tdiv_qr (qp, rp, 0, zp, zn, dp, xn);
            }
            else
            {
                MPN_COPY (rp, zp, xn);
            }
            mpn_mulmod_2expm1 (tp, xp, yp, b, qp);
            if (mpn_cmp (tp, dp, xn) == 0)
                MPN_ZERO (tp, xn);      /* fully reduce */
            if (mpn_cmp (tp, rp, xn) != 0)
            {
                printf ("mpn_mulmod_2expm1 error %ld\n", b);
                abort ();
            }
        }
    }

    for (b = MULMOD_2EXPM1_THRESHOLD * GMP_NUMB_BITS;
         b < 2 * MULMOD_2EXPM1_THRESHOLD * GMP_NUMB_BITS; b += 2)
    {
        xn = BITS_TO_LIMBS (b);
        k = xn * GMP_NUMB_BITS - b;
        MPN_ZERO (dp, xn);
        mpn_com_n (dp, dp, xn);
        dp[xn - 1] &= GMP_NUMB_MASK >> k;       /* dp is 2^b-1 */
        for (c = 0; c < 5; c++)
        {
            mpn_rrandom (xp, rands, xn);
            mpn_rrandom (yp, rands, xn);
            xp[xn - 1] &= GMP_NUMB_MASK >> k;
            yp[xn - 1] &= GMP_NUMB_MASK >> k;
            mpn_mul_n (zp, xp, yp, xn);
            zn = xn * 2;
            MPN_NORMALIZE (zp, zn);
            if (zn >= xn)
            {
                mpn_tdiv_qr (qp, rp, 0, zp, zn, dp, xn);
            }
            else
            {
                MPN_COPY (rp, zp, xn);
            }
            mpn_mulmod_2expm1 (tp, xp, yp, b, qp);
            if (mpn_cmp (tp, dp, xn) == 0)
                MPN_ZERO (tp, xn);      /* fully reduce */
            if (mpn_cmp (tp, rp, xn) != 0)
            {
                printf ("mpn_mulmod_2expm1 error %ld\n", b);
                abort ();
            }
        }
    }

    gmp_randclear (rands);
    tests_end ();
    exit (0);
}
