Skip to content

Commit b31a663

Browse files
committed
Simplify isaac init code
1 parent adcd8e5 commit b31a663

File tree

2 files changed

+72
-140
lines changed

2 files changed

+72
-140
lines changed

src/prng/isaac.rs

+37-71
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ pub struct IsaacRng {
9696
}
9797

9898
// Cannot be derived because [u32; 256] does not implement Clone
99+
// FIXME: remove once RFC 2000 gets implemented
99100
impl Clone for IsaacRng {
100101
fn clone(&self) -> IsaacRng {
101102
IsaacRng {
@@ -115,62 +116,21 @@ impl fmt::Debug for IsaacRng {
115116
}
116117
}
117118

118-
fn mix(a: &mut w32, b: &mut w32, c: &mut w32, d: &mut w32,
119-
e: &mut w32, f: &mut w32, g: &mut w32, h: &mut w32) {
120-
*a ^= *b << 11; *d += *a; *b += *c;
121-
*b ^= *c >> 2; *e += *b; *c += *d;
122-
*c ^= *d << 8; *f += *c; *d += *e;
123-
*d ^= *e >> 16; *g += *d; *e += *f;
124-
*e ^= *f << 10; *h += *e; *f += *g;
125-
*f ^= *g >> 4; *a += *f; *g += *h;
126-
*g ^= *h << 8; *b += *g; *h += *a;
127-
*h ^= *a >> 9; *c += *h; *a += *b;
128-
}
129-
130119
impl IsaacRng {
131120
/// Creates an ISAAC random number generator using an u64 as seed.
132121
/// If `seed == 0` this will produce the same stream of random numbers as
133122
/// the reference implementation when used unseeded.
134123
pub fn new_from_u64(seed: u64) -> IsaacRng {
135-
let mut a = w(0x1367df5a);
136-
let mut b = w(0x95d90059);
137-
let mut c = w(0xc3163e4b);
138-
let mut d = w(0x0f421ad8);
139-
let mut e = w(0xd92a4a78);
140-
let mut f = w(0xa51a3c49);
141-
let mut g = w(0xc4efea1b);
142-
let mut h = w(0x30609119);
143-
144-
let mut mem = [w(0); RAND_SIZE];
145-
146-
a += w(seed as u32);
147-
b += w((seed >> 32) as u32);
148-
149-
for i in (0..RAND_SIZE/8).map(|i| i * 8) {
150-
mix(&mut a, &mut b, &mut c, &mut d, &mut e, &mut f, &mut g, &mut h);
151-
mem[i ] = a; mem[i+1] = b;
152-
mem[i+2] = c; mem[i+3] = d;
153-
mem[i+4] = e; mem[i+5] = f;
154-
mem[i+6] = g; mem[i+7] = h;
155-
}
124+
let mut key = [w(0); RAND_SIZE];
125+
key[0] = w(seed as u32);
126+
key[1] += w((seed >> 32) as u32);
127+
// Initialize with only one pass.
156128
// A second pass does not improve the quality here, because all of
157129
// the seed was already available in the first round.
158130
// Not doing the second pass has the small advantage that if `seed == 0`
159131
// this method produces exactly the same state as the reference
160132
// implementation when used unseeded.
161-
162-
let mut rng = IsaacRng {
163-
rsl: [w(0); RAND_SIZE],
164-
mem: mem,
165-
a: w(0),
166-
b: w(0),
167-
c: w(0),
168-
cnt: 0,
169-
};
170-
171-
// Prepare the first set of results
172-
rng.isaac();
173-
rng
133+
init(key, 1)
174134
}
175135

176136
/// Refills the output buffer (`self.rsl`)
@@ -278,7 +238,7 @@ impl Rng for IsaacRng {
278238
}
279239
}
280240

281-
/// Creates a new ISAAC-64 random number generator.
241+
/// Creates a new ISAAC random number generator.
282242
///
283243
/// The author Bob Jenkins describes how to best initialize ISAAC here:
284244
/// https://rt.cpan.org/Public/Bug/Display.html?id=64324
@@ -304,7 +264,7 @@ impl Rng for IsaacRng {
304264
/// mixes it, and combines that with the next 32 bytes, et cetera. Then loops
305265
/// over all the elements the same way a second time."
306266
#[inline]
307-
fn init(key: [w32; RAND_SIZE]) -> IsaacRng {
267+
fn init(mut mem: [w32; RAND_SIZE], rounds: u32) -> IsaacRng {
308268
// These numbers are the result of initializing a...h with the
309269
// fractional part of the golden ratio in binary (0x9e3779b9)
310270
// and applying mix() 4 times.
@@ -317,29 +277,23 @@ fn init(key: [w32; RAND_SIZE]) -> IsaacRng {
317277
let mut g = w(0xc4efea1b);
318278
let mut h = w(0x30609119);
319279

320-
let mut mem = [w(0); RAND_SIZE];
321-
322-
macro_rules! memloop {
323-
($arr:expr) => {{
324-
for i in (0..RAND_SIZE/8).map(|i| i * 8) {
325-
a += $arr[i ]; b += $arr[i+1];
326-
c += $arr[i+2]; d += $arr[i+3];
327-
e += $arr[i+4]; f += $arr[i+5];
328-
g += $arr[i+6]; h += $arr[i+7];
329-
mix(&mut a, &mut b, &mut c, &mut d,
330-
&mut e, &mut f, &mut g, &mut h);
331-
mem[i ] = a; mem[i+1] = b;
332-
mem[i+2] = c; mem[i+3] = d;
333-
mem[i+4] = e; mem[i+5] = f;
334-
mem[i+6] = g; mem[i+7] = h;
335-
}
336-
}}
280+
// Normally this should do two passes, to make all of the seed effect all
281+
// of `mem`
282+
for _ in 0..rounds {
283+
for i in (0..RAND_SIZE/8).map(|i| i * 8) {
284+
a += mem[i ]; b += mem[i+1];
285+
c += mem[i+2]; d += mem[i+3];
286+
e += mem[i+4]; f += mem[i+5];
287+
g += mem[i+6]; h += mem[i+7];
288+
mix(&mut a, &mut b, &mut c, &mut d,
289+
&mut e, &mut f, &mut g, &mut h);
290+
mem[i ] = a; mem[i+1] = b;
291+
mem[i+2] = c; mem[i+3] = d;
292+
mem[i+4] = e; mem[i+5] = f;
293+
mem[i+6] = g; mem[i+7] = h;
294+
}
337295
}
338296

339-
memloop!(key);
340-
// Do a second pass to make all of the seed affect all of `mem`
341-
memloop!(mem);
342-
343297
let mut rng = IsaacRng {
344298
rsl: [w(0); RAND_SIZE],
345299
mem: mem,
@@ -354,6 +308,18 @@ fn init(key: [w32; RAND_SIZE]) -> IsaacRng {
354308
rng
355309
}
356310

311+
fn mix(a: &mut w32, b: &mut w32, c: &mut w32, d: &mut w32,
312+
e: &mut w32, f: &mut w32, g: &mut w32, h: &mut w32) {
313+
*a ^= *b << 11; *d += *a; *b += *c;
314+
*b ^= *c >> 2; *e += *b; *c += *d;
315+
*c ^= *d << 8; *f += *c; *d += *e;
316+
*d ^= *e >> 16; *g += *d; *e += *f;
317+
*e ^= *f << 10; *h += *e; *f += *g;
318+
*f ^= *g >> 4; *a += *f; *g += *h;
319+
*g ^= *h << 8; *b += *g; *h += *a;
320+
*h ^= *a >> 9; *c += *h; *a += *b;
321+
}
322+
357323
impl SeedFromRng for IsaacRng {
358324
fn from_rng<R: Rng+?Sized>(other: &mut R) -> Result<Self> {
359325
let mut key = [w(0); RAND_SIZE];
@@ -364,7 +330,7 @@ impl SeedFromRng for IsaacRng {
364330
other.try_fill(slice)?;
365331
}
366332

367-
Ok(init(key))
333+
Ok(init(key, 2))
368334
}
369335
}
370336

@@ -385,7 +351,7 @@ impl<'a> SeedableRng<&'a [u32]> for IsaacRng {
385351
*rsl_elem = w(seed_elem);
386352
}
387353

388-
init(key)
354+
init(key, 2)
389355
}
390356
}
391357

src/prng/isaac64.rs

+35-69
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ pub struct Isaac64Rng {
8080
}
8181

8282
// Cannot be derived because [u64; 256] does not implement Clone
83+
// FIXME: remove once RFC 2000 gets implemented
8384
impl Clone for Isaac64Rng {
8485
fn clone(&self) -> Isaac64Rng {
8586
Isaac64Rng {
@@ -99,61 +100,20 @@ impl fmt::Debug for Isaac64Rng {
99100
}
100101
}
101102

102-
fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64,
103-
e: &mut w64, f: &mut w64, g: &mut w64, h: &mut w64) {
104-
*a -= *e; *f ^= *h >> 9; *h += *a;
105-
*b -= *f; *g ^= *a << 9; *a += *b;
106-
*c -= *g; *h ^= *b >> 23; *b += *c;
107-
*d -= *h; *a ^= *c << 15; *c += *d;
108-
*e -= *a; *b ^= *d >> 14; *d += *e;
109-
*f -= *b; *c ^= *e << 20; *e += *f;
110-
*g -= *c; *d ^= *f >> 17; *f += *g;
111-
*h -= *d; *e ^= *g << 14; *g += *h;
112-
}
113-
114103
impl Isaac64Rng {
115104
/// Creates an ISAAC-64 random number generator using an u64 as seed.
116105
/// If `seed == 0` this will produce the same stream of random numbers as
117106
/// the reference implementation when used unseeded.
118107
pub fn new_from_u64(seed: u64) -> Isaac64Rng {
119-
let mut a = w(0x647c4677a2884b7c);
120-
let mut b = w(0xb9f8b322c73ac862);
121-
let mut c = w(0x8c0ea5053d4712a0);
122-
let mut d = w(0xb29b2e824a595524);
123-
let mut e = w(0x82f053db8355e0ce);
124-
let mut f = w(0x48fe4a0fa5a09315);
125-
let mut g = w(0xae985bf2cbfc89ed);
126-
let mut h = w(0x98f5704f6c44c0ab);
127-
128-
let mut mem = [w(0); RAND_SIZE];
129-
130-
a += w(seed);
131-
132-
for i in (0..RAND_SIZE/8).map(|i| i * 8) {
133-
mix(&mut a, &mut b, &mut c, &mut d, &mut e, &mut f, &mut g, &mut h);
134-
mem[i ] = a; mem[i+1] = b;
135-
mem[i+2] = c; mem[i+3] = d;
136-
mem[i+4] = e; mem[i+5] = f;
137-
mem[i+6] = g; mem[i+7] = h;
138-
}
108+
let mut key = [w(0); RAND_SIZE];
109+
key[0] = w(seed);
110+
// Initialize with only one pass.
139111
// A second pass does not improve the quality here, because all of
140112
// the seed was already available in the first round.
141113
// Not doing the second pass has the small advantage that if `seed == 0`
142114
// this method produces exactly the same state as the reference
143115
// implementation when used unseeded.
144-
145-
let mut rng = Isaac64Rng {
146-
rsl: [w(0); RAND_SIZE],
147-
mem: mem,
148-
a: w(0),
149-
b: w(0),
150-
c: w(0),
151-
cnt: 0,
152-
};
153-
154-
// Prepare the first set of results
155-
rng.isaac64();
156-
rng
116+
init(key, 1)
157117
}
158118

159119
/// Refills the output buffer (`self.rsl`)
@@ -263,7 +223,7 @@ impl Rng for Isaac64Rng {
263223
}
264224

265225
/// Creates a new ISAAC-64 random number generator.
266-
fn init(key: [w64; RAND_SIZE]) -> Isaac64Rng {
226+
fn init(mut mem: [w64; RAND_SIZE], rounds: u32) -> Isaac64Rng {
267227
// These numbers are the result of initializing a...h with the
268228
// fractional part of the golden ratio in binary (0x9e3779b97f4a7c13)
269229
// and applying mix() 4 times.
@@ -276,29 +236,23 @@ fn init(key: [w64; RAND_SIZE]) -> Isaac64Rng {
276236
let mut g = w(0xae985bf2cbfc89ed);
277237
let mut h = w(0x98f5704f6c44c0ab);
278238

279-
let mut mem = [w(0); RAND_SIZE];
280-
281-
macro_rules! memloop {
282-
($arr:expr) => {{
283-
for i in (0..RAND_SIZE/8).map(|i| i * 8) {
284-
a += $arr[i ]; b += $arr[i+1];
285-
c += $arr[i+2]; d += $arr[i+3];
286-
e += $arr[i+4]; f += $arr[i+5];
287-
g += $arr[i+6]; h += $arr[i+7];
288-
mix(&mut a, &mut b, &mut c, &mut d,
289-
&mut e, &mut f, &mut g, &mut h);
290-
mem[i ] = a; mem[i+1] = b;
291-
mem[i+2] = c; mem[i+3] = d;
292-
mem[i+4] = e; mem[i+5] = f;
293-
mem[i+6] = g; mem[i+7] = h;
294-
}
295-
}}
239+
// Normally this should do two passes, to make all of the seed effect all
240+
// of `mem`
241+
for _ in 0..rounds {
242+
for i in (0..RAND_SIZE/8).map(|i| i * 8) {
243+
a += mem[i ]; b += mem[i+1];
244+
c += mem[i+2]; d += mem[i+3];
245+
e += mem[i+4]; f += mem[i+5];
246+
g += mem[i+6]; h += mem[i+7];
247+
mix(&mut a, &mut b, &mut c, &mut d,
248+
&mut e, &mut f, &mut g, &mut h);
249+
mem[i ] = a; mem[i+1] = b;
250+
mem[i+2] = c; mem[i+3] = d;
251+
mem[i+4] = e; mem[i+5] = f;
252+
mem[i+6] = g; mem[i+7] = h;
253+
}
296254
}
297255

298-
memloop!(key);
299-
// Do a second pass to make all of the seed affect all of `mem`
300-
memloop!(mem);
301-
302256
let mut rng = Isaac64Rng {
303257
rsl: [w(0); RAND_SIZE],
304258
mem: mem,
@@ -313,6 +267,18 @@ fn init(key: [w64; RAND_SIZE]) -> Isaac64Rng {
313267
rng
314268
}
315269

270+
fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64,
271+
e: &mut w64, f: &mut w64, g: &mut w64, h: &mut w64) {
272+
*a -= *e; *f ^= *h >> 9; *h += *a;
273+
*b -= *f; *g ^= *a << 9; *a += *b;
274+
*c -= *g; *h ^= *b >> 23; *b += *c;
275+
*d -= *h; *a ^= *c << 15; *c += *d;
276+
*e -= *a; *b ^= *d >> 14; *d += *e;
277+
*f -= *b; *c ^= *e << 20; *e += *f;
278+
*g -= *c; *d ^= *f >> 17; *f += *g;
279+
*h -= *d; *e ^= *g << 14; *g += *h;
280+
}
281+
316282
impl SeedFromRng for Isaac64Rng {
317283
fn from_rng<R: Rng+?Sized>(other: &mut R) -> Result<Self> {
318284
let mut key = [w(0); RAND_SIZE];
@@ -323,7 +289,7 @@ impl SeedFromRng for Isaac64Rng {
323289
other.try_fill(slice)?;
324290
}
325291

326-
Ok(init(key))
292+
Ok(init(key, 2))
327293
}
328294
}
329295

@@ -344,7 +310,7 @@ impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng {
344310
*rsl_elem = w(seed_elem);
345311
}
346312

347-
init(key)
313+
init(key, 2)
348314
}
349315
}
350316

0 commit comments

Comments
 (0)