1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-12-26 10:49:33 +00:00

* src/fns.c (Frandom): Handle bignum limits

(ccall2, get_random_bignum): New functions.
This commit is contained in:
Stefan Monnier 2021-03-05 12:09:50 -05:00
parent cb87eeff1b
commit d582356a7f
2 changed files with 53 additions and 2 deletions

View File

@ -1250,7 +1250,7 @@ other strings to choose various seed values.
This function returns a pseudo-random integer. Repeated calls return a This function returns a pseudo-random integer. Repeated calls return a
series of pseudo-random integers. series of pseudo-random integers.
If @var{limit} is a positive fixnum, the value is chosen to be If @var{limit} is a positive integer, the value is chosen to be
nonnegative and less than @var{limit}. Otherwise, the value might be nonnegative and less than @var{limit}. Otherwise, the value might be
any fixnum, i.e., any integer from @code{most-negative-fixnum} through any fixnum, i.e., any integer from @code{most-negative-fixnum} through
@code{most-positive-fixnum} (@pxref{Integer Basics}). @code{most-positive-fixnum} (@pxref{Integer Basics}).

View File

@ -54,10 +54,55 @@ DEFUN ("identity", Fidentity, Sidentity, 1, 1, 0,
return argument; return argument;
} }
static Lisp_Object
ccall2 (Lisp_Object (f) (ptrdiff_t nargs, Lisp_Object *args),
Lisp_Object arg1, Lisp_Object arg2)
{
Lisp_Object args[2] = {arg1, arg2};
return f (2, args);
}
static Lisp_Object
get_random_bignum (Lisp_Object limit)
{
/* This is a naive transcription into bignums of the fixnum algorithm.
I'd be quite surprised if that's anywhere near the best algorithm
for it. */
while (true)
{
Lisp_Object val = make_fixnum (0);
Lisp_Object lim = limit;
int bits = 0;
int bitsperiteration = FIXNUM_BITS - 1;
do
{
/* Shift by one so it is a valid positive fixnum. */
EMACS_INT rand = get_random () >> 1;
Lisp_Object lrand = make_fixnum (rand);
bits += bitsperiteration;
val = ccall2 (Flogior,
Fash (val, make_fixnum (bitsperiteration)),
lrand);
lim = Fash (lim, make_fixnum (- bitsperiteration));
}
while (!EQ (lim, make_fixnum (0)));
/* Return the remainder, except reject the rare case where
get_random returns a number so close to INTMASK that the
remainder isn't random. */
Lisp_Object remainder = Frem (val, limit);
if (!NILP (ccall2 (Fleq,
ccall2 (Fminus, val, remainder),
ccall2 (Fminus,
Fash (make_fixnum (1), make_fixnum (bits)),
limit))))
return remainder;
}
}
DEFUN ("random", Frandom, Srandom, 0, 1, 0, DEFUN ("random", Frandom, Srandom, 0, 1, 0,
doc: /* Return a pseudo-random integer. doc: /* Return a pseudo-random integer.
By default, return a fixnum; all fixnums are equally likely. By default, return a fixnum; all fixnums are equally likely.
With positive fixnum LIMIT, return random integer in interval [0,LIMIT). With positive integer LIMIT, return random integer in interval [0,LIMIT).
With argument t, set the random number seed from the system's entropy With argument t, set the random number seed from the system's entropy
pool if available, otherwise from less-random volatile data such as the time. pool if available, otherwise from less-random volatile data such as the time.
With a string argument, set the seed based on the string's contents. With a string argument, set the seed based on the string's contents.
@ -71,6 +116,12 @@ See Info node `(elisp)Random Numbers' for more details. */)
init_random (); init_random ();
else if (STRINGP (limit)) else if (STRINGP (limit))
seed_random (SSDATA (limit), SBYTES (limit)); seed_random (SSDATA (limit), SBYTES (limit));
if (BIGNUMP (limit))
{
if (0 > mpz_sgn (*xbignum_val (limit)))
xsignal2 (Qwrong_type_argument, Qnatnump, limit);
return get_random_bignum (limit);
}
val = get_random (); val = get_random ();
if (FIXNUMP (limit) && 0 < XFIXNUM (limit)) if (FIXNUMP (limit) && 0 < XFIXNUM (limit))