| /* Miscellaneous test program support routines. |
| |
| Copyright 2000-2003, 2005, 2013, 2015, 2019 Free Software Foundation, Inc. |
| |
| This file is part of the GNU MP Library test suite. |
| |
| The GNU MP Library test suite is free software; you can redistribute it |
| and/or modify it under the terms of the GNU 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 test suite 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 General |
| Public License for more details. |
| |
| You should have received a copy of the GNU General Public License along with |
| the GNU MP Library test suite. If not, see https://www.gnu.org/licenses/. */ |
| |
| #include "config.h" |
| |
| #include <ctype.h> |
| #include <signal.h> |
| #include <stdio.h> |
| #include <stdlib.h> /* for getenv */ |
| #include <string.h> |
| |
| #if HAVE_FLOAT_H |
| #include <float.h> /* for DBL_MANT_DIG */ |
| #endif |
| |
| #if TIME_WITH_SYS_TIME |
| # include <sys/time.h> /* for struct timeval */ |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| #endif |
| |
| #include "gmp-impl.h" |
| #include "tests.h" |
| |
| |
| /* The various tests setups and final checks, collected up together. */ |
| void |
| tests_start (void) |
| { |
| char version[10]; |
| snprintf (version, 10, "%u.%u.%u", |
| __GNU_MP_VERSION, |
| __GNU_MP_VERSION_MINOR, |
| __GNU_MP_VERSION_PATCHLEVEL); |
| |
| if (strcmp (gmp_version, version) != 0) |
| { |
| fprintf (stderr, "tests are not linked to the newly compiled library\n"); |
| fprintf (stderr, " local version is: %s\n", version); |
| fprintf (stderr, " linked version is: %s\n", gmp_version); |
| abort (); |
| } |
| |
| /* don't buffer, so output is not lost if a test causes a segv etc */ |
| setbuf (stdout, NULL); |
| setbuf (stderr, NULL); |
| |
| tests_memory_start (); |
| tests_rand_start (); |
| } |
| void |
| tests_end (void) |
| { |
| tests_rand_end (); |
| tests_memory_end (); |
| } |
| |
| static void |
| seed_from_tod (gmp_randstate_ptr rands) |
| { |
| unsigned long seed; |
| #if HAVE_GETTIMEOFDAY |
| struct timeval tv; |
| gettimeofday (&tv, NULL); |
| seed = tv.tv_sec ^ ((unsigned long) tv.tv_usec << 12); |
| seed &= 0xffffffff; |
| #else |
| time_t tv; |
| time (&tv); |
| seed = tv; |
| #endif |
| gmp_randseed_ui (rands, seed); |
| printf ("Seed GMP_CHECK_RANDOMIZE=%lu (include this in bug reports)\n", seed); |
| } |
| |
| static void |
| seed_from_urandom (gmp_randstate_ptr rands, FILE *fs) |
| { |
| mpz_t seed; |
| unsigned char buf[6]; |
| fread (buf, 1, 6, fs); |
| mpz_init (seed); |
| mpz_import (seed, 6, 1, 1, 0, 0, buf); |
| gmp_randseed (rands, seed); |
| gmp_printf ("Seed GMP_CHECK_RANDOMIZE=%Zd (include this in bug reports)\n", seed); |
| mpz_clear (seed); |
| } |
| |
| void |
| tests_rand_start (void) |
| { |
| gmp_randstate_ptr rands; |
| char *seed_string; |
| |
| if (__gmp_rands_initialized) |
| { |
| printf ("Please let tests_start() initialize the global __gmp_rands.\n"); |
| printf ("ie. ensure that function is called before the first use of RANDS.\n"); |
| abort (); |
| } |
| |
| gmp_randinit_default (__gmp_rands); |
| __gmp_rands_initialized = 1; |
| rands = __gmp_rands; |
| |
| seed_string = getenv ("GMP_CHECK_RANDOMIZE"); |
| if (seed_string != NULL) |
| { |
| if (strcmp (seed_string, "0") != 0 && |
| strcmp (seed_string, "1") != 0) |
| { |
| mpz_t seed; |
| mpz_init_set_str (seed, seed_string, 0); |
| gmp_printf ("Re-seeding with GMP_CHECK_RANDOMIZE=%Zd\n", seed); |
| gmp_randseed (rands, seed); |
| mpz_clear (seed); |
| } |
| else |
| { |
| FILE *fs = fopen ("/dev/urandom", "r"); |
| if (fs != NULL) |
| { |
| seed_from_urandom (rands, fs); |
| fclose (fs); |
| } |
| else |
| seed_from_tod (rands); |
| } |
| fflush (stdout); |
| } |
| } |
| void |
| tests_rand_end (void) |
| { |
| RANDS_CLEAR (); |
| } |
| |
| |
| /* Only used if CPU calling conventions checking is available. */ |
| mp_limb_t (*calling_conventions_function) (ANYARGS); |
| |
| |
| /* Return p advanced to the next multiple of "align" bytes. "align" must be |
| a power of 2. Care is taken not to assume sizeof(int)==sizeof(pointer). |
| Using "unsigned long" avoids a warning on hpux. */ |
| void * |
| align_pointer (void *p, size_t align) |
| { |
| gmp_intptr_t d; |
| d = ((gmp_intptr_t) p) & (align-1); |
| d = (d != 0 ? align-d : 0); |
| return (void *) (((char *) p) + d); |
| } |
| |
| |
| /* Note that memory allocated with this function can never be freed, because |
| the start address of the block allocated is lost. */ |
| void * |
| __gmp_allocate_func_aligned (size_t bytes, size_t align) |
| { |
| return align_pointer ((*__gmp_allocate_func) (bytes + align-1), align); |
| } |
| |
| |
| void * |
| __gmp_allocate_or_reallocate (void *ptr, size_t oldsize, size_t newsize) |
| { |
| if (ptr == NULL) |
| return (*__gmp_allocate_func) (newsize); |
| else |
| return (*__gmp_reallocate_func) (ptr, oldsize, newsize); |
| } |
| |
| char * |
| __gmp_allocate_strdup (const char *s) |
| { |
| size_t len; |
| char *t; |
| len = strlen (s); |
| t = (char *) (*__gmp_allocate_func) (len+1); |
| memcpy (t, s, len+1); |
| return t; |
| } |
| |
| |
| char * |
| strtoupper (char *s_orig) |
| { |
| char *s; |
| for (s = s_orig; *s != '\0'; s++) |
| if (islower (*s)) |
| *s = toupper (*s); |
| return s_orig; |
| } |
| |
| |
| void |
| mpz_set_n (mpz_ptr z, mp_srcptr p, mp_size_t size) |
| { |
| ASSERT (size >= 0); |
| MPN_NORMALIZE (p, size); |
| MPZ_REALLOC (z, size); |
| MPN_COPY (PTR(z), p, size); |
| SIZ(z) = size; |
| } |
| |
| void |
| mpz_init_set_n (mpz_ptr z, mp_srcptr p, mp_size_t size) |
| { |
| ASSERT (size >= 0); |
| |
| MPN_NORMALIZE (p, size); |
| ALLOC(z) = MAX (size, 1); |
| PTR(z) = __GMP_ALLOCATE_FUNC_LIMBS (ALLOC(z)); |
| SIZ(z) = size; |
| MPN_COPY (PTR(z), p, size); |
| } |
| |
| |
| /* Find least significant limb position where p1,size and p2,size differ. */ |
| mp_size_t |
| mpn_diff_lowest (mp_srcptr p1, mp_srcptr p2, mp_size_t size) |
| { |
| mp_size_t i; |
| |
| for (i = 0; i < size; i++) |
| if (p1[i] != p2[i]) |
| return i; |
| |
| /* no differences */ |
| return -1; |
| } |
| |
| |
| /* Find most significant limb position where p1,size and p2,size differ. */ |
| mp_size_t |
| mpn_diff_highest (mp_srcptr p1, mp_srcptr p2, mp_size_t size) |
| { |
| mp_size_t i; |
| |
| for (i = size-1; i >= 0; i--) |
| if (p1[i] != p2[i]) |
| return i; |
| |
| /* no differences */ |
| return -1; |
| } |
| |
| |
| /* Find least significant byte position where p1,size and p2,size differ. */ |
| mp_size_t |
| byte_diff_lowest (const void *p1, const void *p2, mp_size_t size) |
| { |
| mp_size_t i; |
| |
| for (i = 0; i < size; i++) |
| if (((const char *) p1)[i] != ((const char *) p2)[i]) |
| return i; |
| |
| /* no differences */ |
| return -1; |
| } |
| |
| |
| /* Find most significant byte position where p1,size and p2,size differ. */ |
| mp_size_t |
| byte_diff_highest (const void *p1, const void *p2, mp_size_t size) |
| { |
| mp_size_t i; |
| |
| for (i = size-1; i >= 0; i--) |
| if (((const char *) p1)[i] != ((const char *) p2)[i]) |
| return i; |
| |
| /* no differences */ |
| return -1; |
| } |
| |
| |
| void |
| mpz_set_str_or_abort (mpz_ptr z, const char *str, int base) |
| { |
| if (mpz_set_str (z, str, base) != 0) |
| { |
| fprintf (stderr, "ERROR: mpz_set_str failed\n"); |
| fprintf (stderr, " str = \"%s\"\n", str); |
| fprintf (stderr, " base = %d\n", base); |
| abort(); |
| } |
| } |
| |
| void |
| mpq_set_str_or_abort (mpq_ptr q, const char *str, int base) |
| { |
| if (mpq_set_str (q, str, base) != 0) |
| { |
| fprintf (stderr, "ERROR: mpq_set_str failed\n"); |
| fprintf (stderr, " str = \"%s\"\n", str); |
| fprintf (stderr, " base = %d\n", base); |
| abort(); |
| } |
| } |
| |
| void |
| mpf_set_str_or_abort (mpf_ptr f, const char *str, int base) |
| { |
| if (mpf_set_str (f, str, base) != 0) |
| { |
| fprintf (stderr, "ERROR mpf_set_str failed\n"); |
| fprintf (stderr, " str = \"%s\"\n", str); |
| fprintf (stderr, " base = %d\n", base); |
| abort(); |
| } |
| } |
| |
| |
| /* Whether the absolute value of z is a power of 2. */ |
| int |
| mpz_pow2abs_p (mpz_srcptr z) |
| { |
| mp_size_t size, i; |
| mp_srcptr ptr; |
| |
| size = SIZ (z); |
| if (size == 0) |
| return 0; /* zero is not a power of 2 */ |
| size = ABS (size); |
| |
| ptr = PTR (z); |
| for (i = 0; i < size-1; i++) |
| if (ptr[i] != 0) |
| return 0; /* non-zero low limb means not a power of 2 */ |
| |
| return POW2_P (ptr[i]); /* high limb power of 2 */ |
| } |
| |
| |
| /* Exponentially distributed between 0 and 2^nbits-1, meaning the number of |
| bits in the result is uniformly distributed between 0 and nbits-1. |
| |
| FIXME: This is not a proper exponential distribution, since the |
| probability function will have a stepped shape due to using a uniform |
| distribution after choosing how many bits. */ |
| |
| void |
| mpz_erandomb (mpz_ptr rop, gmp_randstate_t rstate, unsigned long nbits) |
| { |
| mpz_urandomb (rop, rstate, gmp_urandomm_ui (rstate, nbits)); |
| } |
| |
| void |
| mpz_erandomb_nonzero (mpz_ptr rop, gmp_randstate_t rstate, unsigned long nbits) |
| { |
| mpz_erandomb (rop, rstate, nbits); |
| if (mpz_sgn (rop) == 0) |
| mpz_set_ui (rop, 1L); |
| } |
| |
| void |
| mpz_errandomb (mpz_ptr rop, gmp_randstate_t rstate, unsigned long nbits) |
| { |
| mpz_rrandomb (rop, rstate, gmp_urandomm_ui (rstate, nbits)); |
| } |
| |
| void |
| mpz_errandomb_nonzero (mpz_ptr rop, gmp_randstate_t rstate, unsigned long nbits) |
| { |
| mpz_errandomb (rop, rstate, nbits); |
| if (mpz_sgn (rop) == 0) |
| mpz_set_ui (rop, 1L); |
| } |
| |
| void |
| mpz_negrandom (mpz_ptr rop, gmp_randstate_t rstate) |
| { |
| mp_limb_t n; |
| _gmp_rand (&n, rstate, 1); |
| if (n != 0) |
| mpz_neg (rop, rop); |
| } |
| |
| void |
| mpz_clobber(mpz_ptr rop) |
| { |
| MPN_ZERO(PTR(rop), ALLOC(rop)); |
| PTR(rop)[0] = 0xDEADBEEF; |
| SIZ(rop) = 0xDEFACE; |
| } |
| |
| mp_limb_t |
| urandom (void) |
| { |
| #if GMP_NAIL_BITS == 0 |
| mp_limb_t n; |
| _gmp_rand (&n, RANDS, GMP_LIMB_BITS); |
| return n; |
| #else |
| mp_limb_t n[2]; |
| _gmp_rand (n, RANDS, GMP_LIMB_BITS); |
| return n[0] + (n[1] << GMP_NUMB_BITS); |
| #endif |
| } |
| |
| |
| /* Call (*func)() with various random number generators. */ |
| void |
| call_rand_algs (void (*func) (const char *, gmp_randstate_ptr)) |
| { |
| gmp_randstate_t rstate; |
| mpz_t a; |
| |
| mpz_init (a); |
| |
| gmp_randinit_default (rstate); |
| (*func) ("gmp_randinit_default", rstate); |
| gmp_randclear (rstate); |
| |
| gmp_randinit_mt (rstate); |
| (*func) ("gmp_randinit_mt", rstate); |
| gmp_randclear (rstate); |
| |
| gmp_randinit_lc_2exp_size (rstate, 8L); |
| (*func) ("gmp_randinit_lc_2exp_size 8", rstate); |
| gmp_randclear (rstate); |
| |
| gmp_randinit_lc_2exp_size (rstate, 16L); |
| (*func) ("gmp_randinit_lc_2exp_size 16", rstate); |
| gmp_randclear (rstate); |
| |
| gmp_randinit_lc_2exp_size (rstate, 128L); |
| (*func) ("gmp_randinit_lc_2exp_size 128", rstate); |
| gmp_randclear (rstate); |
| |
| /* degenerate always zeros */ |
| mpz_set_ui (a, 0L); |
| gmp_randinit_lc_2exp (rstate, a, 0L, 8L); |
| (*func) ("gmp_randinit_lc_2exp a=0 c=0 m=8", rstate); |
| gmp_randclear (rstate); |
| |
| /* degenerate always FFs */ |
| mpz_set_ui (a, 0L); |
| gmp_randinit_lc_2exp (rstate, a, 0xFFL, 8L); |
| (*func) ("gmp_randinit_lc_2exp a=0 c=0xFF m=8", rstate); |
| gmp_randclear (rstate); |
| |
| mpz_clear (a); |
| } |
| |
| |
| /* Return +infinity if available, or 0 if not. |
| We don't want to use libm, so INFINITY or other system values are not |
| used here. */ |
| double |
| tests_infinity_d (void) |
| { |
| #if _GMP_IEEE_FLOATS |
| union ieee_double_extract x; |
| x.s.exp = 2047; |
| x.s.manl = 0; |
| x.s.manh = 0; |
| x.s.sig = 0; |
| return x.d; |
| #else |
| return 0; |
| #endif |
| } |
| |
| |
| /* Return non-zero if d is an infinity (either positive or negative). |
| Don't want libm, so don't use isinf() or other system tests. */ |
| int |
| tests_isinf (double d) |
| { |
| #if _GMP_IEEE_FLOATS |
| union ieee_double_extract x; |
| x.d = d; |
| return (x.s.exp == 2047 && x.s.manl == 0 && x.s.manh == 0); |
| #else |
| return 0; |
| #endif |
| } |
| |
| |
| /* Set the hardware floating point rounding mode. Same mode values as mpfr, |
| namely 0=nearest, 1=tozero, 2=up, 3=down. Return 1 if successful, 0 if |
| not. */ |
| int |
| tests_hardware_setround (int mode) |
| { |
| #if ! defined NO_ASM && HAVE_HOST_CPU_FAMILY_x86 |
| int rc; |
| switch (mode) { |
| case 0: rc = 0; break; /* nearest */ |
| case 1: rc = 3; break; /* tozero */ |
| case 2: rc = 2; break; /* up */ |
| case 3: rc = 1; break; /* down */ |
| default: |
| return 0; |
| } |
| x86_fldcw ((x86_fstcw () & ~0xC00) | (rc << 10)); |
| return 1; |
| #endif |
| |
| return 0; |
| } |
| |
| /* Return the hardware floating point rounding mode, or -1 if unknown. */ |
| int |
| tests_hardware_getround (void) |
| { |
| #if ! defined NO_ASM && HAVE_HOST_CPU_FAMILY_x86 |
| switch ((x86_fstcw () & ~0xC00) >> 10) { |
| case 0: return 0; break; /* nearest */ |
| case 1: return 3; break; /* down */ |
| case 2: return 2; break; /* up */ |
| case 3: return 1; break; /* tozero */ |
| } |
| #endif |
| |
| return -1; |
| } |
| |
| |
| /* tests_dbl_mant_bits() determines by experiment the number of bits in the |
| mantissa of a "double". If it's not possible to find a value (perhaps |
| due to the compiler optimizing too aggressively), then return 0. |
| |
| This code is used rather than DBL_MANT_DIG from <float.h> since ancient |
| systems like SunOS don't have that file, and since one GNU/Linux ARM |
| system was seen where the float emulation seemed to have only 32 working |
| bits, not the 53 float.h claimed. */ |
| |
| int |
| tests_dbl_mant_bits (void) |
| { |
| static int n = -1; |
| volatile double x, y, d; |
| |
| if (n != -1) |
| return n; |
| |
| n = 1; |
| x = 2.0; |
| for (;;) |
| { |
| /* see if 2^(n+1)+1 can be formed without rounding, if so then |
| continue, if not then "n" is the answer */ |
| y = x + 1.0; |
| d = y - x; |
| if (d != 1.0) |
| { |
| #if defined (DBL_MANT_DIG) && DBL_RADIX == 2 |
| if (n != DBL_MANT_DIG) |
| printf ("Warning, tests_dbl_mant_bits got %d but DBL_MANT_DIG says %d\n", n, DBL_MANT_DIG); |
| #endif |
| break; |
| } |
| |
| x *= 2; |
| n++; |
| |
| if (n > 1000) |
| { |
| printf ("Oops, tests_dbl_mant_bits can't determine mantissa size\n"); |
| n = 0; |
| break; |
| } |
| } |
| return n; |
| } |
| |
| |
| /* See tests_setjmp_sigfpe in tests.h. */ |
| |
| jmp_buf tests_sigfpe_target; |
| |
| RETSIGTYPE |
| tests_sigfpe_handler (int sig) |
| { |
| longjmp (tests_sigfpe_target, 1); |
| } |
| |
| void |
| tests_sigfpe_done (void) |
| { |
| signal (SIGFPE, SIG_DFL); |
| } |