mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-11-24 07:20:37 +00:00
ecf08f0621
dc4e6b1329
; Update copyright years in more files64b3777631
; Run set-copyright from admin.el8e1c56ae46
; Add 2024 to copyright years # Conflicts: # doc/misc/modus-themes.org # doc/misc/texinfo.tex # etc/NEWS # etc/refcards/ru-refcard.tex # etc/themes/modus-operandi-theme.el # etc/themes/modus-themes.el # etc/themes/modus-vivendi-theme.el # lib/alloca.in.h # lib/binary-io.h # lib/c-ctype.h # lib/c-strcasecmp.c # lib/c-strncasecmp.c # lib/careadlinkat.c # lib/cloexec.c # lib/close-stream.c # lib/diffseq.h # lib/dup2.c # lib/filemode.h # lib/fpending.c # lib/fpending.h # lib/fsusage.c # lib/getgroups.c # lib/getloadavg.c # lib/gettext.h # lib/gettime.c # lib/gettimeofday.c # lib/group-member.c # lib/malloc.c # lib/md5-stream.c # lib/md5.c # lib/md5.h # lib/memmem.c # lib/memrchr.c # lib/nanosleep.c # lib/save-cwd.h # lib/sha1.c # lib/sig2str.c # lib/stdlib.in.h # lib/strtoimax.c # lib/strtol.c # lib/strtoll.c # lib/time_r.c # lib/xalloc-oversized.h # lisp/auth-source-pass.el # lisp/emacs-lisp/lisp-mnt.el # lisp/emacs-lisp/timer.el # lisp/info-look.el # lisp/jit-lock.el # lisp/loadhist.el # lisp/mail/rmail.el # lisp/net/ntlm.el # lisp/net/webjump.el # lisp/progmodes/asm-mode.el # lisp/progmodes/project.el # lisp/progmodes/sh-script.el # lisp/textmodes/flyspell.el # lisp/textmodes/reftex-toc.el # lisp/textmodes/reftex.el # lisp/textmodes/tex-mode.el # lisp/url/url-gw.el # m4/alloca.m4 # m4/clock_time.m4 # m4/d-type.m4 # m4/dirent_h.m4 # m4/dup2.m4 # m4/euidaccess.m4 # m4/fchmodat.m4 # m4/filemode.m4 # m4/fsusage.m4 # m4/getgroups.m4 # m4/getloadavg.m4 # m4/getrandom.m4 # m4/gettime.m4 # m4/gettimeofday.m4 # m4/gnulib-common.m4 # m4/group-member.m4 # m4/inttypes.m4 # m4/malloc.m4 # m4/manywarnings.m4 # m4/mempcpy.m4 # m4/memrchr.m4 # m4/mkostemp.m4 # m4/mktime.m4 # m4/nproc.m4 # m4/nstrftime.m4 # m4/pathmax.m4 # m4/pipe2.m4 # m4/pselect.m4 # m4/pthread_sigmask.m4 # m4/readlink.m4 # m4/realloc.m4 # m4/sig2str.m4 # m4/ssize_t.m4 # m4/stat-time.m4 # m4/stddef_h.m4 # m4/stdint.m4 # m4/stdio_h.m4 # m4/stdlib_h.m4 # m4/stpcpy.m4 # m4/strnlen.m4 # m4/strtoimax.m4 # m4/strtoll.m4 # m4/time_h.m4 # m4/timegm.m4 # m4/timer_time.m4 # m4/timespec.m4 # m4/unistd_h.m4 # m4/warnings.m4 # nt/configure.bat # nt/preprep.c # test/lisp/register-tests.el
572 lines
15 KiB
C
572 lines
15 KiB
C
/* Big numbers for Emacs.
|
|
|
|
Copyright 2018-2024 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Emacs.
|
|
|
|
GNU Emacs 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.
|
|
|
|
GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|
|
|
#include <config.h>
|
|
|
|
#include "bignum.h"
|
|
|
|
#include "lisp.h"
|
|
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
|
|
/* mpz global temporaries. Making them global saves the trouble of
|
|
properly using mpz_init and mpz_clear on temporaries even when
|
|
storage is exhausted. Admittedly this is not ideal. An mpz value
|
|
in a temporary is made permanent by mpz_swapping it with a bignum's
|
|
value. Although typically at most two temporaries are needed,
|
|
rounddiv_q and rounding_driver both need four and time_arith needs
|
|
five. */
|
|
|
|
mpz_t mpz[5];
|
|
|
|
static void *
|
|
xrealloc_for_gmp (void *ptr, size_t ignore, size_t size)
|
|
{
|
|
return xrealloc (ptr, size);
|
|
}
|
|
|
|
static void
|
|
xfree_for_gmp (void *ptr, size_t ignore)
|
|
{
|
|
xfree (ptr);
|
|
}
|
|
|
|
void
|
|
init_bignum (void)
|
|
{
|
|
eassert (mp_bits_per_limb == GMP_NUMB_BITS);
|
|
integer_width = 1 << 16;
|
|
|
|
/* FIXME: The Info node `(gmp) Custom Allocation' states: "No error
|
|
return is allowed from any of these functions, if they return
|
|
then they must have performed the specified operation. [...]
|
|
There's currently no defined way for the allocation functions to
|
|
recover from an error such as out of memory, they must terminate
|
|
program execution. A 'longjmp' or throwing a C++ exception will
|
|
have undefined results." But xmalloc and xrealloc do call
|
|
'longjmp'. */
|
|
mp_set_memory_functions (xmalloc, xrealloc_for_gmp, xfree_for_gmp);
|
|
|
|
for (int i = 0; i < ARRAYELTS (mpz); i++)
|
|
mpz_init (mpz[i]);
|
|
}
|
|
|
|
/* Return the value of the Lisp bignum N, as a double. */
|
|
double
|
|
bignum_to_double (Lisp_Object n)
|
|
{
|
|
return mpz_get_d_rounded (*xbignum_val (n));
|
|
}
|
|
|
|
/* Return D, converted to a Lisp integer. Discard any fraction.
|
|
Signal an error if D cannot be converted. */
|
|
Lisp_Object
|
|
double_to_integer (double d)
|
|
{
|
|
if (!isfinite (d))
|
|
overflow_error ();
|
|
mpz_set_d (mpz[0], d);
|
|
return make_integer_mpz ();
|
|
}
|
|
|
|
/* Return a Lisp integer equal to mpz[0], which has BITS bits and which
|
|
must not be in fixnum range. Set mpz[0] to a junk value. */
|
|
static Lisp_Object
|
|
make_bignum_bits (size_t bits)
|
|
{
|
|
/* The documentation says integer-width should be nonnegative, so
|
|
comparing it to BITS works even though BITS is unsigned. Treat
|
|
integer-width as if it were at least twice the machine integer width,
|
|
so that timefns.c can safely use bignums for double-precision
|
|
timestamps. */
|
|
if (integer_width < bits && 2 * max (INTMAX_WIDTH, UINTMAX_WIDTH) < bits)
|
|
overflow_error ();
|
|
|
|
struct Lisp_Bignum *b = ALLOCATE_PLAIN_PSEUDOVECTOR (struct Lisp_Bignum,
|
|
PVEC_BIGNUM);
|
|
mpz_init (b->value);
|
|
mpz_swap (b->value, mpz[0]);
|
|
return make_lisp_ptr (b, Lisp_Vectorlike);
|
|
}
|
|
|
|
/* Return a Lisp integer equal to mpz[0], which must not be in fixnum range.
|
|
Set mpz[0] to a junk value. */
|
|
static Lisp_Object
|
|
make_bignum (void)
|
|
{
|
|
return make_bignum_bits (mpz_sizeinbase (mpz[0], 2));
|
|
}
|
|
|
|
/* Return a Lisp integer equal to N, which must not be in fixnum range. */
|
|
Lisp_Object
|
|
make_bigint (intmax_t n)
|
|
{
|
|
eassert (FIXNUM_OVERFLOW_P (n));
|
|
mpz_set_intmax (mpz[0], n);
|
|
return make_bignum ();
|
|
}
|
|
Lisp_Object
|
|
make_biguint (uintmax_t n)
|
|
{
|
|
eassert (FIXNUM_OVERFLOW_P (n));
|
|
mpz_set_uintmax (mpz[0], n);
|
|
return make_bignum ();
|
|
}
|
|
|
|
/* Return a Lisp integer equal to -N, which must not be in fixnum range. */
|
|
Lisp_Object
|
|
make_neg_biguint (uintmax_t n)
|
|
{
|
|
eassert (-MOST_NEGATIVE_FIXNUM < n);
|
|
mpz_set_uintmax (mpz[0], n);
|
|
mpz_neg (mpz[0], mpz[0]);
|
|
return make_bignum ();
|
|
}
|
|
|
|
/* Return a Lisp integer with value taken from mpz[0].
|
|
Set mpz[0] to a junk value. */
|
|
Lisp_Object
|
|
make_integer_mpz (void)
|
|
{
|
|
size_t bits = mpz_sizeinbase (mpz[0], 2);
|
|
|
|
if (bits <= FIXNUM_BITS)
|
|
{
|
|
EMACS_INT v = 0;
|
|
int i = 0, shift = 0;
|
|
|
|
do
|
|
{
|
|
EMACS_INT limb = mpz_getlimbn (mpz[0], i++);
|
|
v += limb << shift;
|
|
shift += GMP_NUMB_BITS;
|
|
}
|
|
while (shift < bits);
|
|
|
|
if (mpz_sgn (mpz[0]) < 0)
|
|
v = -v;
|
|
|
|
if (!FIXNUM_OVERFLOW_P (v))
|
|
return make_fixnum (v);
|
|
}
|
|
|
|
return make_bignum_bits (bits);
|
|
}
|
|
|
|
/* Set RESULT to V. This code is for when intmax_t is wider than long. */
|
|
void
|
|
mpz_set_intmax_slow (mpz_t result, intmax_t v)
|
|
{
|
|
int maxlimbs = (INTMAX_WIDTH + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS;
|
|
mp_limb_t *limb = mpz_limbs_write (result, maxlimbs);
|
|
int n = 0;
|
|
uintmax_t u = v;
|
|
bool negative = v < 0;
|
|
if (negative)
|
|
{
|
|
uintmax_t two = 2;
|
|
u = -u & ((two << (UINTMAX_WIDTH - 1)) - 1);
|
|
}
|
|
|
|
do
|
|
{
|
|
limb[n++] = u;
|
|
u = GMP_NUMB_BITS < UINTMAX_WIDTH ? u >> GMP_NUMB_BITS : 0;
|
|
}
|
|
while (u != 0);
|
|
|
|
mpz_limbs_finish (result, negative ? -n : n);
|
|
}
|
|
void
|
|
mpz_set_uintmax_slow (mpz_t result, uintmax_t v)
|
|
{
|
|
int maxlimbs = (UINTMAX_WIDTH + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS;
|
|
mp_limb_t *limb = mpz_limbs_write (result, maxlimbs);
|
|
int n = 0;
|
|
|
|
do
|
|
{
|
|
limb[n++] = v;
|
|
v = GMP_NUMB_BITS < INTMAX_WIDTH ? v >> GMP_NUMB_BITS : 0;
|
|
}
|
|
while (v != 0);
|
|
|
|
mpz_limbs_finish (result, n);
|
|
}
|
|
|
|
/* If Z fits into *PI, store its value there and return true.
|
|
Return false otherwise. */
|
|
bool
|
|
mpz_to_intmax (mpz_t const z, intmax_t *pi)
|
|
{
|
|
ptrdiff_t bits = mpz_sizeinbase (z, 2);
|
|
bool negative = mpz_sgn (z) < 0;
|
|
|
|
if (bits < INTMAX_WIDTH)
|
|
{
|
|
intmax_t v = 0;
|
|
int i = 0, shift = 0;
|
|
|
|
do
|
|
{
|
|
intmax_t limb = mpz_getlimbn (z, i++);
|
|
v += limb << shift;
|
|
shift += GMP_NUMB_BITS;
|
|
}
|
|
while (shift < bits);
|
|
|
|
*pi = negative ? -v : v;
|
|
return true;
|
|
}
|
|
if (bits == INTMAX_WIDTH && INTMAX_MIN < -INTMAX_MAX && negative
|
|
&& mpz_scan1 (z, 0) == INTMAX_WIDTH - 1)
|
|
{
|
|
*pi = INTMAX_MIN;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
bool
|
|
mpz_to_uintmax (mpz_t const z, uintmax_t *pi)
|
|
{
|
|
if (mpz_sgn (z) < 0)
|
|
return false;
|
|
ptrdiff_t bits = mpz_sizeinbase (z, 2);
|
|
if (UINTMAX_WIDTH < bits)
|
|
return false;
|
|
|
|
uintmax_t v = 0;
|
|
int i = 0, shift = 0;
|
|
|
|
do
|
|
{
|
|
uintmax_t limb = mpz_getlimbn (z, i++);
|
|
v += limb << shift;
|
|
shift += GMP_NUMB_BITS;
|
|
}
|
|
while (shift < bits);
|
|
|
|
*pi = v;
|
|
return true;
|
|
}
|
|
|
|
/* Return the value of the bignum X if it fits, 0 otherwise.
|
|
A bignum cannot be zero, so 0 indicates failure reliably. */
|
|
intmax_t
|
|
bignum_to_intmax (Lisp_Object x)
|
|
{
|
|
intmax_t i;
|
|
return mpz_to_intmax (*xbignum_val (x), &i) ? i : 0;
|
|
}
|
|
uintmax_t
|
|
bignum_to_uintmax (Lisp_Object x)
|
|
{
|
|
uintmax_t i;
|
|
return mpz_to_uintmax (*xbignum_val (x), &i) ? i : 0;
|
|
}
|
|
|
|
|
|
/* Multiply and exponentiate mpz_t values without aborting due to size
|
|
limits. */
|
|
|
|
/* GMP tests for this value and aborts (!) if it is exceeded.
|
|
This is as of GMP 6.1.2 (2016); perhaps future versions will differ. */
|
|
enum { GMP_NLIMBS_MAX = min (INT_MAX, ULONG_MAX / GMP_NUMB_BITS) };
|
|
|
|
/* An upper bound on limb counts, needed to prevent libgmp and/or
|
|
Emacs from aborting or otherwise misbehaving. This bound applies
|
|
to estimates of mpz_t sizes before the mpz_t objects are created,
|
|
as opposed to integer-width which operates on mpz_t values after
|
|
creation and before conversion to Lisp bignums. */
|
|
enum
|
|
{
|
|
NLIMBS_LIMIT = min (min (/* libgmp needs to store limb counts. */
|
|
GMP_NLIMBS_MAX,
|
|
|
|
/* Size calculations need to work. */
|
|
min (PTRDIFF_MAX, SIZE_MAX) / sizeof (mp_limb_t)),
|
|
|
|
/* Emacs puts bit counts into fixnums. */
|
|
MOST_POSITIVE_FIXNUM / GMP_NUMB_BITS)
|
|
};
|
|
|
|
/* Like mpz_size, but tell the compiler the result is a nonnegative int. */
|
|
|
|
static int
|
|
emacs_mpz_size (mpz_t const op)
|
|
{
|
|
mp_size_t size = mpz_size (op);
|
|
eassume (0 <= size && size <= INT_MAX);
|
|
return size;
|
|
}
|
|
|
|
/* Wrappers to work around GMP limitations. As of GMP 6.1.2 (2016),
|
|
the library code aborts when a number is too large. These wrappers
|
|
avoid the problem for functions that can return numbers much larger
|
|
than their arguments. For slowly-growing numbers, the integer
|
|
width checks in bignum.c should suffice. */
|
|
|
|
void
|
|
emacs_mpz_mul (mpz_t rop, mpz_t const op1, mpz_t const op2)
|
|
{
|
|
if (NLIMBS_LIMIT - emacs_mpz_size (op1) < emacs_mpz_size (op2))
|
|
overflow_error ();
|
|
mpz_mul (rop, op1, op2);
|
|
}
|
|
|
|
void
|
|
emacs_mpz_mul_2exp (mpz_t rop, mpz_t const op1, EMACS_INT op2)
|
|
{
|
|
/* Fudge factor derived from GMP 6.1.2, to avoid an abort in
|
|
mpz_mul_2exp (look for the '+ 1' in its source code). */
|
|
enum { mul_2exp_extra_limbs = 1 };
|
|
enum { lim = min (NLIMBS_LIMIT, GMP_NLIMBS_MAX - mul_2exp_extra_limbs) };
|
|
|
|
EMACS_INT op2limbs = op2 / GMP_NUMB_BITS;
|
|
if (lim - emacs_mpz_size (op1) < op2limbs)
|
|
overflow_error ();
|
|
mpz_mul_2exp (rop, op1, op2);
|
|
}
|
|
|
|
void
|
|
emacs_mpz_pow_ui (mpz_t rop, mpz_t const base, unsigned long exp)
|
|
{
|
|
/* This fudge factor is derived from GMP 6.1.2, to avoid an abort in
|
|
mpz_n_pow_ui (look for the '5' in its source code). */
|
|
enum { pow_ui_extra_limbs = 5 };
|
|
enum { lim = min (NLIMBS_LIMIT, GMP_NLIMBS_MAX - pow_ui_extra_limbs) };
|
|
|
|
int nbase = emacs_mpz_size (base), n;
|
|
if (ckd_mul (&n, nbase, exp) || lim < n)
|
|
overflow_error ();
|
|
mpz_pow_ui (rop, base, exp);
|
|
}
|
|
|
|
|
|
/* Yield an upper bound on the buffer size needed to contain a C
|
|
string representing the NUM in base BASE. This includes any
|
|
preceding '-' and the terminating null. */
|
|
static ptrdiff_t
|
|
mpz_bufsize (mpz_t const num, int base)
|
|
{
|
|
return mpz_sizeinbase (num, base) + 2;
|
|
}
|
|
ptrdiff_t
|
|
bignum_bufsize (Lisp_Object num, int base)
|
|
{
|
|
return mpz_bufsize (*xbignum_val (num), base);
|
|
}
|
|
|
|
/* Convert NUM to a nearest double, as opposed to mpz_get_d which
|
|
truncates toward zero. */
|
|
double
|
|
mpz_get_d_rounded (mpz_t const num)
|
|
{
|
|
ptrdiff_t size = mpz_bufsize (num, 10);
|
|
|
|
/* Use mpz_get_d as a shortcut for a bignum so small that rounding
|
|
errors cannot occur, which is possible if EMACS_INT (not counting
|
|
sign) has fewer bits than a double significand. */
|
|
if (! ((FLT_RADIX == 2 && DBL_MANT_DIG <= FIXNUM_BITS - 1)
|
|
|| (FLT_RADIX == 16 && DBL_MANT_DIG * 4 <= FIXNUM_BITS - 1))
|
|
&& size <= DBL_DIG + 2)
|
|
return mpz_get_d (num);
|
|
|
|
USE_SAFE_ALLOCA;
|
|
char *buf = SAFE_ALLOCA (size);
|
|
mpz_get_str (buf, 10, num);
|
|
double result = strtod (buf, NULL);
|
|
SAFE_FREE ();
|
|
return result;
|
|
}
|
|
|
|
/* Store into BUF (of size SIZE) the value of NUM as a base-BASE string.
|
|
If BASE is negative, use upper-case digits in base -BASE.
|
|
Return the string's length.
|
|
SIZE must equal bignum_bufsize (NUM, abs (BASE)). */
|
|
ptrdiff_t
|
|
bignum_to_c_string (char *buf, ptrdiff_t size, Lisp_Object num, int base)
|
|
{
|
|
eassert (bignum_bufsize (num, abs (base)) == size);
|
|
mpz_get_str (buf, base, *xbignum_val (num));
|
|
ptrdiff_t n = size - 2;
|
|
return !buf[n - 1] ? n - 1 : n + !!buf[n];
|
|
}
|
|
|
|
/* Convert NUM to a base-BASE Lisp string.
|
|
If BASE is negative, use upper-case digits in base -BASE. */
|
|
|
|
Lisp_Object
|
|
bignum_to_string (Lisp_Object num, int base)
|
|
{
|
|
ptrdiff_t size = bignum_bufsize (num, abs (base));
|
|
USE_SAFE_ALLOCA;
|
|
char *str = SAFE_ALLOCA (size);
|
|
ptrdiff_t len = bignum_to_c_string (str, size, num, base);
|
|
Lisp_Object result = make_unibyte_string (str, len);
|
|
SAFE_FREE ();
|
|
return result;
|
|
}
|
|
|
|
/* Create a bignum by scanning NUM, with digits in BASE.
|
|
NUM must consist of an optional '-', a nonempty sequence
|
|
of base-BASE digits, and a terminating null byte, and
|
|
the represented number must not be in fixnum range. */
|
|
|
|
Lisp_Object
|
|
make_bignum_str (char const *num, int base)
|
|
{
|
|
struct Lisp_Bignum *b = ALLOCATE_PLAIN_PSEUDOVECTOR (struct Lisp_Bignum,
|
|
PVEC_BIGNUM);
|
|
mpz_init (b->value);
|
|
int check = mpz_set_str (b->value, num, base);
|
|
eassert (check == 0);
|
|
return make_lisp_ptr (b, Lisp_Vectorlike);
|
|
}
|
|
|
|
/* Check that X is a Lisp integer in the range LO..HI.
|
|
Return X's value as an intmax_t. */
|
|
|
|
intmax_t
|
|
check_integer_range (Lisp_Object x, intmax_t lo, intmax_t hi)
|
|
{
|
|
CHECK_INTEGER (x);
|
|
intmax_t i;
|
|
if (! (integer_to_intmax (x, &i) && lo <= i && i <= hi))
|
|
args_out_of_range_3 (x, make_int (lo), make_int (hi));
|
|
return i;
|
|
}
|
|
|
|
/* Check that X is a Lisp integer in the range 0..HI.
|
|
Return X's value as an uintmax_t. */
|
|
|
|
uintmax_t
|
|
check_uinteger_max (Lisp_Object x, uintmax_t hi)
|
|
{
|
|
CHECK_INTEGER (x);
|
|
uintmax_t i;
|
|
if (! (integer_to_uintmax (x, &i) && i <= hi))
|
|
args_out_of_range_3 (x, make_fixnum (0), make_uint (hi));
|
|
return i;
|
|
}
|
|
|
|
/* Check that X is a Lisp integer no greater than INT_MAX,
|
|
and return its value or zero, whichever is greater. */
|
|
|
|
int
|
|
check_int_nonnegative (Lisp_Object x)
|
|
{
|
|
CHECK_INTEGER (x);
|
|
return NILP (Fnatnump (x)) ? 0 : check_integer_range (x, 0, INT_MAX);
|
|
}
|
|
|
|
/* Return a random mp_limb_t. */
|
|
|
|
static mp_limb_t
|
|
get_random_limb (void)
|
|
{
|
|
if (GMP_NUMB_BITS <= ULONG_WIDTH)
|
|
return get_random_ulong ();
|
|
|
|
/* Work around GCC -Wshift-count-overflow false alarm. */
|
|
int shift = GMP_NUMB_BITS <= ULONG_WIDTH ? 0 : ULONG_WIDTH;
|
|
|
|
/* This is in case someone builds GMP with unusual definitions for
|
|
MINI_GMP_LIMB_TYPE or _LONG_LONG_LIMB. */
|
|
mp_limb_t r = 0;
|
|
for (int i = 0; i < GMP_NUMB_BITS; i += ULONG_WIDTH)
|
|
r = (r << shift) | get_random_ulong ();
|
|
return r;
|
|
}
|
|
|
|
/* Return a random mp_limb_t I in the range 0 <= I < LIM.
|
|
If LIM is zero, simply return a random mp_limb_t. */
|
|
|
|
static mp_limb_t
|
|
get_random_limb_lim (mp_limb_t lim)
|
|
{
|
|
/* Return the remainder of a random mp_limb_t R divided by LIM,
|
|
except reject the rare case where R is so close to the maximum
|
|
mp_limb_t that the remainder isn't random. */
|
|
mp_limb_t difflim = - lim, diff, remainder;
|
|
do
|
|
{
|
|
mp_limb_t r = get_random_limb ();
|
|
if (lim == 0)
|
|
return r;
|
|
remainder = r % lim;
|
|
diff = r - remainder;
|
|
}
|
|
while (difflim < diff);
|
|
|
|
return remainder;
|
|
}
|
|
|
|
/* Return a random Lisp integer I in the range 0 <= I < LIMIT,
|
|
where LIMIT is a positive bignum. */
|
|
|
|
Lisp_Object
|
|
get_random_bignum (struct Lisp_Bignum const *limit)
|
|
{
|
|
mpz_t const *lim = bignum_val (limit);
|
|
mp_size_t nlimbs = mpz_size (*lim);
|
|
eassume (0 < nlimbs);
|
|
mp_limb_t *r_limb = mpz_limbs_write (mpz[0], nlimbs);
|
|
mp_limb_t const *lim_limb = mpz_limbs_read (*lim);
|
|
mp_limb_t limhi = lim_limb[nlimbs - 1];
|
|
eassert (limhi);
|
|
bool edgy;
|
|
|
|
do
|
|
{
|
|
/* Generate the result one limb at a time, most significant first.
|
|
Choose the most significant limb RHI randomly from 0..LIMHI,
|
|
where LIMHI is the LIM's first limb, except choose from
|
|
0..(LIMHI-1) if there is just one limb. RHI == LIMHI is an
|
|
unlucky edge case as later limbs might cause the result to be
|
|
exceed or equal LIM; if this happens, it causes another
|
|
iteration in the outer loop. */
|
|
|
|
mp_limb_t rhi = get_random_limb_lim (limhi + (1 < nlimbs));
|
|
edgy = rhi == limhi;
|
|
r_limb[nlimbs - 1] = rhi;
|
|
|
|
for (mp_size_t i = nlimbs - 1; 0 < i--; )
|
|
{
|
|
/* get_random_limb_lim (edgy ? limb_lim[i] + 1 : 0)
|
|
would be wrong here, as the full mp_limb_t range is
|
|
needed in later limbs for the edge case to have the
|
|
proper weighting. */
|
|
mp_limb_t ri = get_random_limb ();
|
|
if (edgy)
|
|
{
|
|
if (lim_limb[i] < ri)
|
|
break;
|
|
edgy = lim_limb[i] == ri;
|
|
}
|
|
r_limb[i] = ri;
|
|
}
|
|
}
|
|
while (edgy);
|
|
|
|
mpz_limbs_finish (mpz[0], nlimbs);
|
|
return make_integer_mpz ();
|
|
}
|