diff options
| author | Stefan Monnier | 2021-03-05 12:09:50 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2021-03-05 12:09:50 -0500 |
| commit | d582356a7f704f8a209a3ef31d6ea970520c6224 (patch) | |
| tree | 3d0458d152280e691a5ed05677a5f335cec9b995 /src | |
| parent | cb87eeff1bf88d9d1849c452e8b9953b06ada454 (diff) | |
| download | emacs-d582356a7f704f8a209a3ef31d6ea970520c6224.tar.gz emacs-d582356a7f704f8a209a3ef31d6ea970520c6224.zip | |
* src/fns.c (Frandom): Handle bignum `limit`s
(ccall2, get_random_bignum): New functions.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fns.c | 53 |
1 files changed, 52 insertions, 1 deletions
| @@ -54,10 +54,55 @@ DEFUN ("identity", Fidentity, Sidentity, 1, 1, 0, | |||
| 54 | return argument; | 54 | return argument; |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | static Lisp_Object | ||
| 58 | ccall2 (Lisp_Object (f) (ptrdiff_t nargs, Lisp_Object *args), | ||
| 59 | Lisp_Object arg1, Lisp_Object arg2) | ||
| 60 | { | ||
| 61 | Lisp_Object args[2] = {arg1, arg2}; | ||
| 62 | return f (2, args); | ||
| 63 | } | ||
| 64 | |||
| 65 | static Lisp_Object | ||
| 66 | get_random_bignum (Lisp_Object limit) | ||
| 67 | { | ||
| 68 | /* This is a naive transcription into bignums of the fixnum algorithm. | ||
| 69 | I'd be quite surprised if that's anywhere near the best algorithm | ||
| 70 | for it. */ | ||
| 71 | while (true) | ||
| 72 | { | ||
| 73 | Lisp_Object val = make_fixnum (0); | ||
| 74 | Lisp_Object lim = limit; | ||
| 75 | int bits = 0; | ||
| 76 | int bitsperiteration = FIXNUM_BITS - 1; | ||
| 77 | do | ||
| 78 | { | ||
| 79 | /* Shift by one so it is a valid positive fixnum. */ | ||
| 80 | EMACS_INT rand = get_random () >> 1; | ||
| 81 | Lisp_Object lrand = make_fixnum (rand); | ||
| 82 | bits += bitsperiteration; | ||
| 83 | val = ccall2 (Flogior, | ||
| 84 | Fash (val, make_fixnum (bitsperiteration)), | ||
| 85 | lrand); | ||
| 86 | lim = Fash (lim, make_fixnum (- bitsperiteration)); | ||
| 87 | } | ||
| 88 | while (!EQ (lim, make_fixnum (0))); | ||
| 89 | /* Return the remainder, except reject the rare case where | ||
| 90 | get_random returns a number so close to INTMASK that the | ||
| 91 | remainder isn't random. */ | ||
| 92 | Lisp_Object remainder = Frem (val, limit); | ||
| 93 | if (!NILP (ccall2 (Fleq, | ||
| 94 | ccall2 (Fminus, val, remainder), | ||
| 95 | ccall2 (Fminus, | ||
| 96 | Fash (make_fixnum (1), make_fixnum (bits)), | ||
| 97 | limit)))) | ||
| 98 | return remainder; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 57 | DEFUN ("random", Frandom, Srandom, 0, 1, 0, | 102 | DEFUN ("random", Frandom, Srandom, 0, 1, 0, |
| 58 | doc: /* Return a pseudo-random integer. | 103 | doc: /* Return a pseudo-random integer. |
| 59 | By default, return a fixnum; all fixnums are equally likely. | 104 | By default, return a fixnum; all fixnums are equally likely. |
| 60 | With positive fixnum LIMIT, return random integer in interval [0,LIMIT). | 105 | With positive integer LIMIT, return random integer in interval [0,LIMIT). |
| 61 | With argument t, set the random number seed from the system's entropy | 106 | With argument t, set the random number seed from the system's entropy |
| 62 | pool if available, otherwise from less-random volatile data such as the time. | 107 | pool if available, otherwise from less-random volatile data such as the time. |
| 63 | With a string argument, set the seed based on the string's contents. | 108 | 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. */) | |||
| 71 | init_random (); | 116 | init_random (); |
| 72 | else if (STRINGP (limit)) | 117 | else if (STRINGP (limit)) |
| 73 | seed_random (SSDATA (limit), SBYTES (limit)); | 118 | seed_random (SSDATA (limit), SBYTES (limit)); |
| 119 | if (BIGNUMP (limit)) | ||
| 120 | { | ||
| 121 | if (0 > mpz_sgn (*xbignum_val (limit))) | ||
| 122 | xsignal2 (Qwrong_type_argument, Qnatnump, limit); | ||
| 123 | return get_random_bignum (limit); | ||
| 124 | } | ||
| 74 | 125 | ||
| 75 | val = get_random (); | 126 | val = get_random (); |
| 76 | if (FIXNUMP (limit) && 0 < XFIXNUM (limit)) | 127 | if (FIXNUMP (limit) && 0 < XFIXNUM (limit)) |