/* Test mpz_urandomm.

Copyright 2002 Free Software Foundation, Inc.

This file is part of the GNU MP Library.

The GNU MP 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 3 of the License, or (at your
option) any later version.

The GNU MP 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 GNU MP Library.  If not, see http://www.gnu.org/licenses/.  */

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

#ifndef TRUE
#define TRUE (1)
#endif
#ifndef FALSE
#define FALSE (0)
#endif

int
check_params (void)
{
  gmp_randstate_t r1, r2;
  mpz_t a, b, m;
  int i;
  int result;

  result = TRUE;

  mpz_init (a);
  mpz_init (b);
  mpz_init (m);

  if (result)
    {
      /* Test the consistency between urandomm and urandomb. */
      gmp_randinit_default (r1);
      gmp_randinit_default (r2);
      gmp_randseed_ui (r1, 85L);
      gmp_randseed_ui (r2, 85L);
      mpz_set_ui (m, 0L);
      mpz_setbit (m, 80L);
      for (i = 0; i < 100; i++)
	{
	  mpz_urandomm (a, r1, m);
	  mpz_urandomb (b, r2, 80L);
	  if (mpz_cmp (a, b) != 0)
	    {
	      result = FALSE;
	      printf ("mpz_urandomm != mpz_urandomb\n");
	      break;
	    }
	}
      gmp_randclear (r1);
      gmp_randclear (r2);
    }

  if (result)
    {
      /* Test that mpz_urandomm returns the correct result with a
	 broken LC.  */
      mpz_set_ui (a, 0L);
      gmp_randinit_lc_2exp (r1, a, 0xffL, 8L);
      mpz_set_ui (m, 5L);
      /* Warning: This code hangs in gmp 4.1 and below */
      for (i = 0; i < 100; i++)
	{
	  mpz_urandomm (a, r1, m);
	  if (mpz_cmp_ui (a, 2L) != 0)
	    {
	      result = FALSE;
	      gmp_printf ("mpz_urandomm returns %Zd instead of 2\n", a);
	      break;
	    }
	}
      gmp_randclear (r1);
    }

  if (result)
    {
      /* Test that the results are always in range for either
         positive or negative values of m.  */
      gmp_randinit_default (r1);
      mpz_set_ui (m, 5L);
      mpz_set_si (b, -5L);
      for (i = 0; i < 100; i++)
	{
	  mpz_urandomm (a, r1, m);
	  if (mpz_cmp_ui (a, 5L) >= 0 || mpz_sgn (a) < 0)
	    {
	      result = FALSE;
	      gmp_printf ("Out-of-range or non-positive value: %Zd\n", a);
	      break;
	    }
	  mpz_urandomm (a, r1, b);
	  if (mpz_cmp_ui (a, 5L) >= 0 || mpz_sgn (a) < 0)
	    {
	      result = FALSE;
	      gmp_printf ("Out-of-range or non-positive value (from negative modulus): %Zd\n", a);
	      break;
	    }
	}
      gmp_randclear (r1);
    }

  if (result)
    {
      /* Test that m=1 forces always result=0.  */
      gmp_randinit_default (r1);
      mpz_set_ui (m, 1L);
      for (i = 0; i < 100; i++)
	{
	  mpz_urandomm (a, r1, m);
	  if (mpz_sgn (a) != 0)
	    {
	      result = FALSE;
	      gmp_printf ("mpz_urandomm fails with m=1 (result=%Zd)\n", a);
	      break;
	    }
	}
      gmp_randclear (r1);
    }

  mpz_clear (a);
  mpz_clear (b);
  mpz_clear (m);
  return result;
}

int
main (int argc, char *argv[])
{
  int result = TRUE;

  tests_start ();

  if (result)
    if (!check_params ())
      result = FALSE;

  tests_end ();

  if (result)
    return 0; /* pass */
  else
    return 1; /* fail */
}
