xref: /freebsd/contrib/bc/src/rand.c (revision 50696a6e8cbfdbf4a0d00f2f85f1951aa0d8e23d)
1*50696a6eSStefan Eßer /*
2*50696a6eSStefan Eßer  * *****************************************************************************
3*50696a6eSStefan Eßer  *
4*50696a6eSStefan Eßer  * SPDX-License-Identifier: BSD-2-Clause
5*50696a6eSStefan Eßer  *
6*50696a6eSStefan Eßer  * Copyright (c) 2018-2019 Gavin D. Howard and contributors.
7*50696a6eSStefan Eßer  *
8*50696a6eSStefan Eßer  * Redistribution and use in source and binary forms, with or without
9*50696a6eSStefan Eßer  * modification, are permitted provided that the following conditions are met:
10*50696a6eSStefan Eßer  *
11*50696a6eSStefan Eßer  * * Redistributions of source code must retain the above copyright notice, this
12*50696a6eSStefan Eßer  *   list of conditions and the following disclaimer.
13*50696a6eSStefan Eßer  *
14*50696a6eSStefan Eßer  * * Redistributions in binary form must reproduce the above copyright notice,
15*50696a6eSStefan Eßer  *   this list of conditions and the following disclaimer in the documentation
16*50696a6eSStefan Eßer  *   and/or other materials provided with the distribution.
17*50696a6eSStefan Eßer  *
18*50696a6eSStefan Eßer  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19*50696a6eSStefan Eßer  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*50696a6eSStefan Eßer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*50696a6eSStefan Eßer  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22*50696a6eSStefan Eßer  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23*50696a6eSStefan Eßer  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24*50696a6eSStefan Eßer  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25*50696a6eSStefan Eßer  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26*50696a6eSStefan Eßer  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27*50696a6eSStefan Eßer  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28*50696a6eSStefan Eßer  * POSSIBILITY OF SUCH DAMAGE.
29*50696a6eSStefan Eßer  *
30*50696a6eSStefan Eßer  * *****************************************************************************
31*50696a6eSStefan Eßer  *
32*50696a6eSStefan Eßer  * Parts of this code are adapted from the following:
33*50696a6eSStefan Eßer  *
34*50696a6eSStefan Eßer  * PCG, A Family of Better Random Number Generators.
35*50696a6eSStefan Eßer  *
36*50696a6eSStefan Eßer  * You can find the original source code at:
37*50696a6eSStefan Eßer  *   https://github.com/imneme/pcg-c
38*50696a6eSStefan Eßer  *
39*50696a6eSStefan Eßer  * -----------------------------------------------------------------------------
40*50696a6eSStefan Eßer  *
41*50696a6eSStefan Eßer  * Parts of this code are also under the following license:
42*50696a6eSStefan Eßer  *
43*50696a6eSStefan Eßer  * Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors
44*50696a6eSStefan Eßer  *
45*50696a6eSStefan Eßer  * Permission is hereby granted, free of charge, to any person obtaining a copy
46*50696a6eSStefan Eßer  * of this software and associated documentation files (the "Software"), to deal
47*50696a6eSStefan Eßer  * in the Software without restriction, including without limitation the rights
48*50696a6eSStefan Eßer  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
49*50696a6eSStefan Eßer  * copies of the Software, and to permit persons to whom the Software is
50*50696a6eSStefan Eßer  * furnished to do so, subject to the following conditions:
51*50696a6eSStefan Eßer  *
52*50696a6eSStefan Eßer  * The above copyright notice and this permission notice shall be included in
53*50696a6eSStefan Eßer  * all copies or substantial portions of the Software.
54*50696a6eSStefan Eßer  *
55*50696a6eSStefan Eßer  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
56*50696a6eSStefan Eßer  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
57*50696a6eSStefan Eßer  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
58*50696a6eSStefan Eßer  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
59*50696a6eSStefan Eßer  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
60*50696a6eSStefan Eßer  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
61*50696a6eSStefan Eßer  * SOFTWARE.
62*50696a6eSStefan Eßer  *
63*50696a6eSStefan Eßer  * *****************************************************************************
64*50696a6eSStefan Eßer  *
65*50696a6eSStefan Eßer  * Code for the RNG.
66*50696a6eSStefan Eßer  *
67*50696a6eSStefan Eßer  */
68*50696a6eSStefan Eßer 
69*50696a6eSStefan Eßer #include <assert.h>
70*50696a6eSStefan Eßer #include <stdlib.h>
71*50696a6eSStefan Eßer #include <string.h>
72*50696a6eSStefan Eßer #include <time.h>
73*50696a6eSStefan Eßer #include <fcntl.h>
74*50696a6eSStefan Eßer #include <unistd.h>
75*50696a6eSStefan Eßer 
76*50696a6eSStefan Eßer #include <status.h>
77*50696a6eSStefan Eßer #include <rand.h>
78*50696a6eSStefan Eßer #include <vm.h>
79*50696a6eSStefan Eßer 
80*50696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND
81*50696a6eSStefan Eßer 
82*50696a6eSStefan Eßer #if !BC_RAND_BUILTIN
83*50696a6eSStefan Eßer 
84*50696a6eSStefan Eßer static BcRandState bc_rand_addition(uint_fast64_t a, uint_fast64_t b) {
85*50696a6eSStefan Eßer 
86*50696a6eSStefan Eßer 	BcRandState res;
87*50696a6eSStefan Eßer 
88*50696a6eSStefan Eßer 	res.lo = a + b;
89*50696a6eSStefan Eßer 	res.hi = (res.lo < a);
90*50696a6eSStefan Eßer 
91*50696a6eSStefan Eßer 	return res;
92*50696a6eSStefan Eßer }
93*50696a6eSStefan Eßer 
94*50696a6eSStefan Eßer static BcRandState bc_rand_addition2(BcRandState a, BcRandState b) {
95*50696a6eSStefan Eßer 
96*50696a6eSStefan Eßer 	BcRandState temp, res;
97*50696a6eSStefan Eßer 
98*50696a6eSStefan Eßer 	res = bc_rand_addition(a.lo, b.lo);
99*50696a6eSStefan Eßer 	temp = bc_rand_addition(a.hi, b.hi);
100*50696a6eSStefan Eßer 	res.hi += temp.lo;
101*50696a6eSStefan Eßer 
102*50696a6eSStefan Eßer 	return res;
103*50696a6eSStefan Eßer }
104*50696a6eSStefan Eßer 
105*50696a6eSStefan Eßer static BcRandState bc_rand_multiply(uint_fast64_t a, uint_fast64_t b) {
106*50696a6eSStefan Eßer 
107*50696a6eSStefan Eßer 	uint_fast64_t al, ah, bl, bh, c0, c1, c2, c3;
108*50696a6eSStefan Eßer 	BcRandState carry, res;
109*50696a6eSStefan Eßer 
110*50696a6eSStefan Eßer 	al = BC_RAND_TRUNC32(a);
111*50696a6eSStefan Eßer 	ah = BC_RAND_CHOP32(a);
112*50696a6eSStefan Eßer 	bl = BC_RAND_TRUNC32(b);
113*50696a6eSStefan Eßer 	bh = BC_RAND_CHOP32(b);
114*50696a6eSStefan Eßer 
115*50696a6eSStefan Eßer 	c0 = al * bl;
116*50696a6eSStefan Eßer 	c1 = al * bh;
117*50696a6eSStefan Eßer 	c2 = ah * bl;
118*50696a6eSStefan Eßer 	c3 = ah * bh;
119*50696a6eSStefan Eßer 
120*50696a6eSStefan Eßer 	carry = bc_rand_addition(c1, c2);
121*50696a6eSStefan Eßer 
122*50696a6eSStefan Eßer 	res = bc_rand_addition(c0, (BC_RAND_TRUNC32(carry.lo)) << 32);
123*50696a6eSStefan Eßer 	res.hi += BC_RAND_CHOP32(carry.lo) + c3 + (carry.hi << 32);
124*50696a6eSStefan Eßer 
125*50696a6eSStefan Eßer 	return res;
126*50696a6eSStefan Eßer }
127*50696a6eSStefan Eßer 
128*50696a6eSStefan Eßer static BcRandState bc_rand_multiply2(BcRandState a, BcRandState b) {
129*50696a6eSStefan Eßer 
130*50696a6eSStefan Eßer 	BcRandState c0, c1, c2, carry;
131*50696a6eSStefan Eßer 
132*50696a6eSStefan Eßer 	c0 = bc_rand_multiply(a.lo, b.lo);
133*50696a6eSStefan Eßer 	c1 = bc_rand_multiply(a.lo, b.hi);
134*50696a6eSStefan Eßer 	c2 = bc_rand_multiply(a.hi, b.lo);
135*50696a6eSStefan Eßer 
136*50696a6eSStefan Eßer 	carry = bc_rand_addition2(c1, c2);
137*50696a6eSStefan Eßer 	carry.hi = carry.lo;
138*50696a6eSStefan Eßer 	carry.lo = 0;
139*50696a6eSStefan Eßer 
140*50696a6eSStefan Eßer 	return bc_rand_addition2(c0, carry);
141*50696a6eSStefan Eßer }
142*50696a6eSStefan Eßer 
143*50696a6eSStefan Eßer #endif // BC_RAND_BUILTIN
144*50696a6eSStefan Eßer 
145*50696a6eSStefan Eßer static void bc_rand_setModified(BcRNGData *r) {
146*50696a6eSStefan Eßer 
147*50696a6eSStefan Eßer #if BC_RAND_BUILTIN
148*50696a6eSStefan Eßer 	r->inc |= (BcRandState) 1UL;
149*50696a6eSStefan Eßer #else // BC_RAND_BUILTIN
150*50696a6eSStefan Eßer 	r->inc.lo |= (uint_fast64_t) 1UL;
151*50696a6eSStefan Eßer #endif // BC_RAND_BUILTIN
152*50696a6eSStefan Eßer }
153*50696a6eSStefan Eßer 
154*50696a6eSStefan Eßer static void bc_rand_clearModified(BcRNGData *r) {
155*50696a6eSStefan Eßer 
156*50696a6eSStefan Eßer #if BC_RAND_BUILTIN
157*50696a6eSStefan Eßer 	r->inc &= ~((BcRandState) 1UL);
158*50696a6eSStefan Eßer #else // BC_RAND_BUILTIN
159*50696a6eSStefan Eßer 	r->inc.lo &= ~(1UL);
160*50696a6eSStefan Eßer #endif // BC_RAND_BUILTIN
161*50696a6eSStefan Eßer }
162*50696a6eSStefan Eßer 
163*50696a6eSStefan Eßer static void bc_rand_copy(BcRNGData *d, BcRNGData *s) {
164*50696a6eSStefan Eßer 	bool unmod = BC_RAND_NOTMODIFIED(d);
165*50696a6eSStefan Eßer 	memcpy(d, s, sizeof(BcRNGData));
166*50696a6eSStefan Eßer 	if (!unmod) bc_rand_setModified(d);
167*50696a6eSStefan Eßer 	else if (!BC_RAND_NOTMODIFIED(s)) bc_rand_clearModified(d);
168*50696a6eSStefan Eßer }
169*50696a6eSStefan Eßer 
170*50696a6eSStefan Eßer static ulong bc_rand_frand(void *ptr) {
171*50696a6eSStefan Eßer 
172*50696a6eSStefan Eßer 	ulong buf[1];
173*50696a6eSStefan Eßer 	int fd;
174*50696a6eSStefan Eßer 	ssize_t nread;
175*50696a6eSStefan Eßer 
176*50696a6eSStefan Eßer 	assert(ptr != NULL);
177*50696a6eSStefan Eßer 
178*50696a6eSStefan Eßer 	fd = *((int*) ptr);
179*50696a6eSStefan Eßer 
180*50696a6eSStefan Eßer 	nread = read(fd, buf, sizeof(ulong));
181*50696a6eSStefan Eßer 
182*50696a6eSStefan Eßer 	if (BC_ERR(nread != sizeof(ulong))) bc_vm_err(BC_ERR_FATAL_IO_ERR);
183*50696a6eSStefan Eßer 
184*50696a6eSStefan Eßer 	return *((ulong*) buf);
185*50696a6eSStefan Eßer }
186*50696a6eSStefan Eßer 
187*50696a6eSStefan Eßer static ulong bc_rand_rand(void *ptr) {
188*50696a6eSStefan Eßer 
189*50696a6eSStefan Eßer 	size_t i;
190*50696a6eSStefan Eßer 	ulong res = 0;
191*50696a6eSStefan Eßer 
192*50696a6eSStefan Eßer 	BC_UNUSED(ptr);
193*50696a6eSStefan Eßer 
194*50696a6eSStefan Eßer 	for (i = 0; i < sizeof(ulong); ++i)
195*50696a6eSStefan Eßer 		res |= ((ulong) (rand() & BC_RAND_SRAND_BITS)) << (i * CHAR_BIT);
196*50696a6eSStefan Eßer 
197*50696a6eSStefan Eßer 	return res;
198*50696a6eSStefan Eßer }
199*50696a6eSStefan Eßer 
200*50696a6eSStefan Eßer static BcRandState bc_rand_inc(BcRNGData *r) {
201*50696a6eSStefan Eßer 
202*50696a6eSStefan Eßer 	BcRandState inc;
203*50696a6eSStefan Eßer 
204*50696a6eSStefan Eßer #if BC_RAND_BUILTIN
205*50696a6eSStefan Eßer 	inc = r->inc | 1;
206*50696a6eSStefan Eßer #else // BC_RAND_BUILTIN
207*50696a6eSStefan Eßer 	inc.lo = r->inc.lo | 1;
208*50696a6eSStefan Eßer 	inc.hi = r->inc.hi;
209*50696a6eSStefan Eßer #endif // BC_RAND_BUILTIN
210*50696a6eSStefan Eßer 
211*50696a6eSStefan Eßer 	return inc;
212*50696a6eSStefan Eßer }
213*50696a6eSStefan Eßer 
214*50696a6eSStefan Eßer static void bc_rand_setInc(BcRNGData *r) {
215*50696a6eSStefan Eßer 
216*50696a6eSStefan Eßer #if BC_RAND_BUILTIN
217*50696a6eSStefan Eßer 	r->inc <<= 1UL;
218*50696a6eSStefan Eßer #else // BC_RAND_BUILTIN
219*50696a6eSStefan Eßer 	r->inc.hi <<= 1UL;
220*50696a6eSStefan Eßer 	r->inc.hi |= (r->inc.lo & (1UL << (BC_LONG_BIT - 1))) >> (BC_LONG_BIT - 1);
221*50696a6eSStefan Eßer 	r->inc.lo <<= 1UL;
222*50696a6eSStefan Eßer #endif // BC_RAND_BUILTIN
223*50696a6eSStefan Eßer }
224*50696a6eSStefan Eßer 
225*50696a6eSStefan Eßer static void bc_rand_seedState(BcRandState *state, ulong val1, ulong val2) {
226*50696a6eSStefan Eßer 
227*50696a6eSStefan Eßer #if BC_RAND_BUILTIN
228*50696a6eSStefan Eßer 	*state = ((BcRandState) val1) | ((BcRandState) val2) << (BC_LONG_BIT);
229*50696a6eSStefan Eßer #else // BC_RAND_BUILTIN
230*50696a6eSStefan Eßer 	state->lo = val1;
231*50696a6eSStefan Eßer 	state->hi = val2;
232*50696a6eSStefan Eßer #endif // BC_RAND_BUILTIN
233*50696a6eSStefan Eßer }
234*50696a6eSStefan Eßer 
235*50696a6eSStefan Eßer static void bc_rand_seedRNG(BcRNGData *r, ulong state1, ulong state2,
236*50696a6eSStefan Eßer                             ulong inc1, ulong inc2)
237*50696a6eSStefan Eßer {
238*50696a6eSStefan Eßer 	bc_rand_seedState(&r->state, state1, state2);
239*50696a6eSStefan Eßer 	bc_rand_seedState(&r->inc, inc1, inc2);
240*50696a6eSStefan Eßer 	bc_rand_setInc(r);
241*50696a6eSStefan Eßer }
242*50696a6eSStefan Eßer 
243*50696a6eSStefan Eßer static void bc_rand_fill(BcRNGData *r, BcRandUlong fulong, void *ptr) {
244*50696a6eSStefan Eßer 
245*50696a6eSStefan Eßer 	ulong state1, state2, inc1, inc2;
246*50696a6eSStefan Eßer 
247*50696a6eSStefan Eßer 	state1 = fulong(ptr);
248*50696a6eSStefan Eßer 	state2 = fulong(ptr);
249*50696a6eSStefan Eßer 
250*50696a6eSStefan Eßer 	inc1 = fulong(ptr);
251*50696a6eSStefan Eßer 	inc2 = fulong(ptr);
252*50696a6eSStefan Eßer 
253*50696a6eSStefan Eßer 	bc_rand_seedRNG(r, state1, state2, inc1, inc2);
254*50696a6eSStefan Eßer }
255*50696a6eSStefan Eßer 
256*50696a6eSStefan Eßer static void bc_rand_step(BcRNGData *r) {
257*50696a6eSStefan Eßer 	BcRandState temp = bc_rand_mul2(r->state, bc_rand_multiplier);
258*50696a6eSStefan Eßer 	r->state = bc_rand_add2(temp, bc_rand_inc(r));
259*50696a6eSStefan Eßer }
260*50696a6eSStefan Eßer 
261*50696a6eSStefan Eßer static BcRand bc_rand_output(BcRNGData *r) {
262*50696a6eSStefan Eßer 	return BC_RAND_ROT(BC_RAND_FOLD(r->state), BC_RAND_ROTAMT(r->state));
263*50696a6eSStefan Eßer }
264*50696a6eSStefan Eßer 
265*50696a6eSStefan Eßer static void bc_rand_seedZeroes(BcRNG *r, BcRNGData *rng, size_t idx) {
266*50696a6eSStefan Eßer 
267*50696a6eSStefan Eßer 	BcRNGData *rng2;
268*50696a6eSStefan Eßer 
269*50696a6eSStefan Eßer 	if (r->v.len <= idx) return;
270*50696a6eSStefan Eßer 
271*50696a6eSStefan Eßer 	rng2 = bc_vec_item_rev(&r->v, idx);
272*50696a6eSStefan Eßer 
273*50696a6eSStefan Eßer 	if (BC_RAND_ZERO(rng2)) {
274*50696a6eSStefan Eßer 		size_t i;
275*50696a6eSStefan Eßer 		for (i = 1; i < r->v.len; ++i)
276*50696a6eSStefan Eßer 			bc_rand_copy(bc_vec_item_rev(&r->v, i), rng);
277*50696a6eSStefan Eßer 	}
278*50696a6eSStefan Eßer }
279*50696a6eSStefan Eßer 
280*50696a6eSStefan Eßer void bc_rand_srand(BcRNGData *rng) {
281*50696a6eSStefan Eßer 
282*50696a6eSStefan Eßer 	int fd;
283*50696a6eSStefan Eßer 
284*50696a6eSStefan Eßer 	BC_SIG_LOCK;
285*50696a6eSStefan Eßer 
286*50696a6eSStefan Eßer 	fd = open("/dev/urandom", O_RDONLY);
287*50696a6eSStefan Eßer 
288*50696a6eSStefan Eßer 	if (BC_NO_ERR(fd >= 0)) {
289*50696a6eSStefan Eßer 		bc_rand_fill(rng, bc_rand_frand, &fd);
290*50696a6eSStefan Eßer 		close(fd);
291*50696a6eSStefan Eßer 	}
292*50696a6eSStefan Eßer 
293*50696a6eSStefan Eßer 	while (BC_ERR(BC_RAND_ZERO(rng))) bc_rand_fill(rng, bc_rand_rand, NULL);
294*50696a6eSStefan Eßer 
295*50696a6eSStefan Eßer 	BC_SIG_UNLOCK;
296*50696a6eSStefan Eßer }
297*50696a6eSStefan Eßer 
298*50696a6eSStefan Eßer static void bc_rand_propagate(BcRNG *r, BcRNGData *rng) {
299*50696a6eSStefan Eßer 
300*50696a6eSStefan Eßer 	if (r->v.len <= 1) return;
301*50696a6eSStefan Eßer 
302*50696a6eSStefan Eßer 	if (BC_RAND_NOTMODIFIED(rng)) {
303*50696a6eSStefan Eßer 
304*50696a6eSStefan Eßer 		size_t i;
305*50696a6eSStefan Eßer 		bool go = true;
306*50696a6eSStefan Eßer 
307*50696a6eSStefan Eßer 		for (i = 1; go && i < r->v.len; ++i) {
308*50696a6eSStefan Eßer 			BcRNGData *rng2 = bc_vec_item_rev(&r->v, i);
309*50696a6eSStefan Eßer 			go = BC_RAND_NOTMODIFIED(rng2);
310*50696a6eSStefan Eßer 			bc_rand_copy(rng2, rng);
311*50696a6eSStefan Eßer 		}
312*50696a6eSStefan Eßer 
313*50696a6eSStefan Eßer 		bc_rand_seedZeroes(r, rng, i);
314*50696a6eSStefan Eßer 	}
315*50696a6eSStefan Eßer 	else bc_rand_seedZeroes(r, rng, 1);
316*50696a6eSStefan Eßer }
317*50696a6eSStefan Eßer 
318*50696a6eSStefan Eßer BcRand bc_rand_int(BcRNG *r) {
319*50696a6eSStefan Eßer 
320*50696a6eSStefan Eßer 	BcRNGData *rng = bc_vec_top(&r->v);
321*50696a6eSStefan Eßer 
322*50696a6eSStefan Eßer 	if (BC_ERR(BC_RAND_ZERO(rng))) bc_rand_srand(rng);
323*50696a6eSStefan Eßer 
324*50696a6eSStefan Eßer 	bc_rand_step(rng);
325*50696a6eSStefan Eßer 	bc_rand_propagate(r, rng);
326*50696a6eSStefan Eßer 
327*50696a6eSStefan Eßer 	return bc_rand_output(rng);
328*50696a6eSStefan Eßer }
329*50696a6eSStefan Eßer 
330*50696a6eSStefan Eßer BcRand bc_rand_bounded(BcRNG *r, BcRand bound) {
331*50696a6eSStefan Eßer 
332*50696a6eSStefan Eßer 	BcRand rand, threshold = (0 - bound) % bound;
333*50696a6eSStefan Eßer 
334*50696a6eSStefan Eßer 	do {
335*50696a6eSStefan Eßer 		rand = bc_rand_int(r);
336*50696a6eSStefan Eßer 	} while (rand < threshold);
337*50696a6eSStefan Eßer 
338*50696a6eSStefan Eßer 	return rand % bound;
339*50696a6eSStefan Eßer }
340*50696a6eSStefan Eßer 
341*50696a6eSStefan Eßer void bc_rand_seed(BcRNG *r, ulong state1, ulong state2, ulong inc1, ulong inc2)
342*50696a6eSStefan Eßer {
343*50696a6eSStefan Eßer 	BcRNGData *rng = bc_vec_top(&r->v);
344*50696a6eSStefan Eßer 
345*50696a6eSStefan Eßer 	bc_rand_seedState(&rng->inc, inc1, inc2);
346*50696a6eSStefan Eßer 	bc_rand_setInc(rng);
347*50696a6eSStefan Eßer 	bc_rand_setModified(rng);
348*50696a6eSStefan Eßer 
349*50696a6eSStefan Eßer 	if (!state1 && !state2) {
350*50696a6eSStefan Eßer 		memcpy(&rng->state, &rng->inc, sizeof(BcRandState));
351*50696a6eSStefan Eßer 		bc_rand_step(rng);
352*50696a6eSStefan Eßer 	}
353*50696a6eSStefan Eßer 	else bc_rand_seedState(&rng->state, state1, state2);
354*50696a6eSStefan Eßer 
355*50696a6eSStefan Eßer 	bc_rand_propagate(r, rng);
356*50696a6eSStefan Eßer }
357*50696a6eSStefan Eßer 
358*50696a6eSStefan Eßer static BcRandState bc_rand_getInc(BcRNGData *r) {
359*50696a6eSStefan Eßer 
360*50696a6eSStefan Eßer 	BcRandState res;
361*50696a6eSStefan Eßer 
362*50696a6eSStefan Eßer #if BC_RAND_BUILTIN
363*50696a6eSStefan Eßer 	res = r->inc >> 1;
364*50696a6eSStefan Eßer #else // BC_RAND_BUILTIN
365*50696a6eSStefan Eßer 	res = r->inc;
366*50696a6eSStefan Eßer 	res.lo >>= 1;
367*50696a6eSStefan Eßer 	res.lo |= (res.hi & 1) << (BC_LONG_BIT - 1);
368*50696a6eSStefan Eßer 	res.hi >>= 1;
369*50696a6eSStefan Eßer #endif // BC_RAND_BUILTIN
370*50696a6eSStefan Eßer 
371*50696a6eSStefan Eßer 	return res;
372*50696a6eSStefan Eßer }
373*50696a6eSStefan Eßer 
374*50696a6eSStefan Eßer void bc_rand_getRands(BcRNG *r, BcRand *s1, BcRand *s2, BcRand *i1, BcRand *i2)
375*50696a6eSStefan Eßer {
376*50696a6eSStefan Eßer 	BcRandState inc;
377*50696a6eSStefan Eßer 	BcRNGData *rng = bc_vec_top(&r->v);
378*50696a6eSStefan Eßer 
379*50696a6eSStefan Eßer 	if (BC_ERR(BC_RAND_ZERO(rng))) bc_rand_srand(rng);
380*50696a6eSStefan Eßer 
381*50696a6eSStefan Eßer 	inc = bc_rand_getInc(rng);
382*50696a6eSStefan Eßer 
383*50696a6eSStefan Eßer 	*s1 = BC_RAND_TRUNC(rng->state);
384*50696a6eSStefan Eßer 	*s2 = BC_RAND_CHOP(rng->state);
385*50696a6eSStefan Eßer 
386*50696a6eSStefan Eßer 	*i1 = BC_RAND_TRUNC(inc);
387*50696a6eSStefan Eßer 	*i2 = BC_RAND_CHOP(inc);
388*50696a6eSStefan Eßer }
389*50696a6eSStefan Eßer 
390*50696a6eSStefan Eßer void bc_rand_push(BcRNG *r) {
391*50696a6eSStefan Eßer 	BcRNGData rng;
392*50696a6eSStefan Eßer 	memset(&rng, 0, sizeof(BcRNGData));
393*50696a6eSStefan Eßer 	if (r->v.len > 0) bc_rand_copy(&rng, bc_vec_top(&r->v));
394*50696a6eSStefan Eßer 	bc_vec_push(&r->v, &rng);
395*50696a6eSStefan Eßer }
396*50696a6eSStefan Eßer 
397*50696a6eSStefan Eßer void bc_rand_pop(BcRNG *r, bool reset) {
398*50696a6eSStefan Eßer 	bc_vec_npop(&r->v, reset ? r->v.len - 1 : 1);
399*50696a6eSStefan Eßer }
400*50696a6eSStefan Eßer 
401*50696a6eSStefan Eßer void bc_rand_init(BcRNG *r) {
402*50696a6eSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
403*50696a6eSStefan Eßer 	bc_vec_init(&r->v, sizeof(BcRNGData), NULL);
404*50696a6eSStefan Eßer 	bc_rand_push(r);
405*50696a6eSStefan Eßer }
406*50696a6eSStefan Eßer 
407*50696a6eSStefan Eßer #ifndef NDEBUG
408*50696a6eSStefan Eßer void bc_rand_free(BcRNG *r) {
409*50696a6eSStefan Eßer 	BC_SIG_ASSERT_LOCKED;
410*50696a6eSStefan Eßer 	bc_vec_free(&r->v);
411*50696a6eSStefan Eßer }
412*50696a6eSStefan Eßer #endif // NDEBUG
413*50696a6eSStefan Eßer 
414*50696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH && BC_ENABLE_RAND
415