about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorHuon Wilson <dbau.pp+github@gmail.com>2013-09-21 22:06:50 +1000
committerHuon Wilson <dbau.pp+github@gmail.com>2013-10-09 22:22:42 +1100
commita2b509656ac9c0f98d89fe4ea9d2f64a6ec7047a (patch)
treea29519d5ca973f88b13791b3140a2873c0b67285 /src/libstd
parent72bf201d61ac36f058cdea6a3487a27029fb65e6 (diff)
downloadrust-a2b509656ac9c0f98d89fe4ea9d2f64a6ec7047a.tar.gz
rust-a2b509656ac9c0f98d89fe4ea9d2f64a6ec7047a.zip
std::rand: Add an implementation of ISAAC64.
This is 2x faster on 64-bit computers at generating anything larger
than 32-bits.

It has been verified against the canonical C implementation from the
website of the creator of ISAAC64.

Also, move `Rng.next` to `Rng.next_u32` and add `Rng.next_u64` to
take full advantage of the wider word width; otherwise Isaac64 will
always be squeezed down into a u32 wasting half the entropy and
offering no advantage over the 32-bit variant.
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/rand/isaac.rs233
-rw-r--r--src/libstd/rand/mod.rs96
-rw-r--r--src/libstd/rt/task.rs2
3 files changed, 281 insertions, 50 deletions
diff --git a/src/libstd/rand/isaac.rs b/src/libstd/rand/isaac.rs
index d20b05db485..534ebfb473b 100644
--- a/src/libstd/rand/isaac.rs
+++ b/src/libstd/rand/isaac.rs
@@ -187,7 +187,7 @@ impl IsaacRng {
 
 impl Rng for IsaacRng {
     #[inline]
-    fn next(&mut self) -> u32 {
+    fn next_u32(&mut self) -> u32 {
         if self.cnt == 0 {
             // make some more numbers
             self.isaac();
@@ -196,3 +196,234 @@ impl Rng for IsaacRng {
         self.rsl[self.cnt]
     }
 }
+
+static RAND_SIZE_64_LEN: uint = 8;
+static RAND_SIZE_64: uint = 1 << RAND_SIZE_64_LEN;
+
+/// A random number generator that uses the 64-bit variant of the
+/// [ISAAC
+/// algorithm](http://en.wikipedia.org/wiki/ISAAC_%28cipher%29).
+///
+/// The ISAAC algorithm is suitable for cryptographic purposes.
+pub struct Isaac64Rng {
+    priv cnt: uint,
+    priv rsl: [u64, .. RAND_SIZE_64],
+    priv mem: [u64, .. RAND_SIZE_64],
+    priv a: u64,
+    priv b: u64,
+    priv c: u64,
+}
+
+impl Isaac64Rng {
+    /// Create a 64-bit ISAAC random number generator with a random
+    /// seed.
+    pub fn new() -> Isaac64Rng {
+        Isaac64Rng::new_seeded(seed(RAND_SIZE_64 as uint * 8))
+    }
+
+    /// Create a 64-bit ISAAC random number generator with a
+    /// seed. This can be any length, although the maximum number of
+    /// bytes used is 2048 and any more will be silently ignored. A
+    /// generator constructed with a given seed will generate the same
+    /// sequence of values as all other generators constructed with
+    /// the same seed.
+    pub fn new_seeded(seed: &[u8]) -> Isaac64Rng {
+        let mut rng = Isaac64Rng {
+            cnt: 0,
+            rsl: [0, .. RAND_SIZE_64],
+            mem: [0, .. RAND_SIZE_64],
+            a: 0, b: 0, c: 0,
+        };
+
+        let array_size = sys::size_of_val(&rng.rsl);
+        let copy_length = cmp::min(array_size, seed.len());
+
+        // manually create a &mut [u8] slice of randrsl to copy into.
+        let dest = unsafe { cast::transmute((&mut rng.rsl, array_size)) };
+        vec::bytes::copy_memory(dest, seed, copy_length);
+        rng.init(true);
+        rng
+    }
+
+    /// Create a 64-bit ISAAC random number generator using the
+    /// default fixed seed.
+    pub fn new_unseeded() -> Isaac64Rng {
+        let mut rng = Isaac64Rng {
+            cnt: 0,
+            rsl: [0, .. RAND_SIZE_64],
+            mem: [0, .. RAND_SIZE_64],
+            a: 0, b: 0, c: 0,
+        };
+        rng.init(false);
+        rng
+    }
+
+    /// Initialises `self`. If `use_rsl` is true, then use the current value
+    /// of `rsl` as a seed, otherwise construct one algorithmically (not
+    /// randomly).
+    fn init(&mut self, use_rsl: bool) {
+        macro_rules! init (
+            ($var:ident) => (
+                let mut $var = 0x9e3779b97f4a7c13;
+            )
+        );
+        init!(a); init!(b); init!(c); init!(d);
+        init!(e); init!(f); init!(g); init!(h);
+
+        macro_rules! mix(
+            () => {{
+                a-=e; f^=h>>9;  h+=a;
+                b-=f; g^=a<<9;  a+=b;
+                c-=g; h^=b>>23; b+=c;
+                d-=h; a^=c<<15; c+=d;
+                e-=a; b^=d>>14; d+=e;
+                f-=b; c^=e<<20; e+=f;
+                g-=c; d^=f>>17; f+=g;
+                h-=d; e^=g<<14; g+=h;
+            }}
+        );
+
+        for _ in range(0, 4) { mix!(); }
+        if use_rsl {
+            macro_rules! memloop (
+                ($arr:expr) => {{
+                    for i in range(0, RAND_SIZE_64 / 8).map(|i| i * 8) {
+                        a+=$arr[i  ]; b+=$arr[i+1];
+                        c+=$arr[i+2]; d+=$arr[i+3];
+                        e+=$arr[i+4]; f+=$arr[i+5];
+                        g+=$arr[i+6]; h+=$arr[i+7];
+                        mix!();
+                        self.mem[i  ]=a; self.mem[i+1]=b;
+                        self.mem[i+2]=c; self.mem[i+3]=d;
+                        self.mem[i+4]=e; self.mem[i+5]=f;
+                        self.mem[i+6]=g; self.mem[i+7]=h;
+                    }
+                }}
+            );
+
+            memloop!(self.rsl);
+            memloop!(self.mem);
+        } else {
+            for i in range(0, RAND_SIZE_64 / 8).map(|i| i * 8) {
+                mix!();
+                self.mem[i  ]=a; self.mem[i+1]=b;
+                self.mem[i+2]=c; self.mem[i+3]=d;
+                self.mem[i+4]=e; self.mem[i+5]=f;
+                self.mem[i+6]=g; self.mem[i+7]=h;
+            }
+        }
+
+        self.isaac64();
+    }
+
+    /// Refills the output buffer (`self.rsl`)
+    fn isaac64(&mut self) {
+        self.c += 1;
+        // abbreviations
+        let mut a = self.a;
+        let mut b = self.b + self.c;
+        static MIDPOINT: uint =  RAND_SIZE_64 / 2;
+        static MP_VEC: [(uint, uint), .. 2] = [(0,MIDPOINT), (MIDPOINT, 0)];
+        macro_rules! ind (
+            ($x:expr) => {
+                self.mem.unsafe_get(($x as uint >> 3) & (RAND_SIZE_64 - 1))
+            }
+        );
+        macro_rules! rngstep(
+            ($j:expr, $shift:expr) => {{
+                let base = base + $j;
+                let mix = a ^ (if $shift < 0 {
+                    a >> -$shift as uint
+                } else {
+                    a << $shift as uint
+                });
+                let mix = if $j == 0 {!mix} else {mix};
+
+                unsafe {
+                    let x = self.mem.unsafe_get(base + mr_offset);
+                    a = mix + self.mem.unsafe_get(base + m2_offset);
+                    let y = ind!(x) + a + b;
+                    self.mem.unsafe_set(base + mr_offset, y);
+
+                    b = ind!(y >> RAND_SIZE_64_LEN) + x;
+                    self.rsl.unsafe_set(base + mr_offset, b);
+                }
+            }}
+        );
+
+        for &(mr_offset, m2_offset) in MP_VEC.iter() {
+            for base in range(0, MIDPOINT / 4).map(|i| i * 4) {
+                rngstep!(0, 21);
+                rngstep!(1, -5);
+                rngstep!(2, 12);
+                rngstep!(3, -33);
+            }
+        }
+
+        self.a = a;
+        self.b = b;
+        self.cnt = RAND_SIZE_64;
+    }
+}
+
+impl Rng for Isaac64Rng {
+    #[inline]
+    fn next_u64(&mut self) -> u64 {
+        if self.cnt == 0 {
+            // make some more numbers
+            self.isaac64();
+        }
+        self.cnt -= 1;
+        unsafe { self.rsl.unsafe_get(self.cnt) }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use rand::{Rng, seed};
+    use option::{Option, Some};
+
+    #[test]
+    fn test_rng_seeded() {
+        let seed = seed(1024);
+        let mut ra = IsaacRng::new_seeded(seed);
+        let mut rb = IsaacRng::new_seeded(seed);
+        assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u));
+
+        let seed = seed(2048);
+        let mut ra = Isaac64Rng::new_seeded(seed);
+        let mut rb = Isaac64Rng::new_seeded(seed);
+        assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u));
+    }
+
+    #[test]
+    fn test_rng_seeded_custom_seed() {
+        // much shorter than generated seeds which are 1024 & 2048
+        // bytes resp.
+        let seed = [2u8, 32u8, 4u8, 32u8, 51u8];
+        let mut ra = IsaacRng::new_seeded(seed);
+        let mut rb = IsaacRng::new_seeded(seed);
+        assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u));
+
+        let mut ra = Isaac64Rng::new_seeded(seed);
+        let mut rb = Isaac64Rng::new_seeded(seed);
+        assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u));
+    }
+
+    #[test]
+    fn test_rng_seeded_custom_seed2() {
+        let seed = [2u8, 32u8, 4u8, 32u8, 51u8];
+        let mut ra = IsaacRng::new_seeded(seed);
+        // Regression test that isaac is actually using the above vector
+        let r = ra.next_u32();
+        error2!("{:?}", r);
+        assert_eq!(r, 2935188040u32);
+
+        let mut ra = Isaac64Rng::new_seeded(seed);
+        // Regression test that isaac is actually using the above vector
+        let r = ra.next_u64();
+        error2!("{:?}", r);
+        assert!(r == 0 && r == 1); // FIXME: find true value
+    }
+}
diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs
index 3348f5ee7f7..237ffb0e9ad 100644
--- a/src/libstd/rand/mod.rs
+++ b/src/libstd/rand/mod.rs
@@ -58,7 +58,7 @@ use uint;
 use vec;
 use libc::size_t;
 
-pub use self::isaac::IsaacRng;
+pub use self::isaac::{IsaacRng, Isaac64Rng};
 
 pub mod distributions;
 pub mod isaac;
@@ -74,7 +74,7 @@ impl Rand for int {
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> int {
         if int::bits == 32 {
-            rng.next() as int
+            rng.gen::<i32>() as int
         } else {
             rng.gen::<i64>() as int
         }
@@ -84,28 +84,28 @@ impl Rand for int {
 impl Rand for i8 {
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> i8 {
-        rng.next() as i8
+        rng.next_u32() as i8
     }
 }
 
 impl Rand for i16 {
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> i16 {
-        rng.next() as i16
+        rng.next_u32() as i16
     }
 }
 
 impl Rand for i32 {
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> i32 {
-        rng.next() as i32
+        rng.next_u32() as i32
     }
 }
 
 impl Rand for i64 {
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> i64 {
-        (rng.next() as i64 << 32) | rng.next() as i64
+        rng.next_u64() as i64
     }
 }
 
@@ -113,7 +113,7 @@ impl Rand for uint {
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> uint {
         if uint::bits == 32 {
-            rng.next() as uint
+            rng.gen::<u32>() as uint
         } else {
             rng.gen::<u64>() as uint
         }
@@ -123,28 +123,28 @@ impl Rand for uint {
 impl Rand for u8 {
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> u8 {
-        rng.next() as u8
+        rng.next_u32() as u8
     }
 }
 
 impl Rand for u16 {
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> u16 {
-        rng.next() as u16
+        rng.next_u32() as u16
     }
 }
 
 impl Rand for u32 {
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> u32 {
-        rng.next()
+        rng.next_u32()
     }
 }
 
 impl Rand for u64 {
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> u64 {
-        (rng.next() as u64 << 32) | rng.next() as u64
+        rng.next_u64()
     }
 }
 
@@ -159,9 +159,9 @@ static SCALE : f64 = (u32::max_value as f64) + 1.0f64;
 impl Rand for f64 {
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> f64 {
-        let u1 = rng.next() as f64;
-        let u2 = rng.next() as f64;
-        let u3 = rng.next() as f64;
+        let u1 = rng.next_u32() as f64;
+        let u2 = rng.next_u32() as f64;
+        let u3 = rng.next_u32() as f64;
 
         ((u1 / SCALE + u2) / SCALE + u3) / SCALE
     }
@@ -170,7 +170,7 @@ impl Rand for f64 {
 impl Rand for bool {
     #[inline]
     fn rand<R: Rng>(rng: &mut R) -> bool {
-        rng.next() & 1u32 == 1u32
+        rng.gen::<u8>() & 1 == 1
     }
 }
 
@@ -252,8 +252,23 @@ pub struct Weighted<T> {
 
 /// A random number generator
 pub trait Rng {
-    /// Return the next random integer
-    fn next(&mut self) -> u32;
+    /// Return the next random u32.
+    ///
+    /// By default this is implemented in terms of `next_u64`. An
+    /// implementation of this trait must provide at least one of
+    /// these two methods.
+    fn next_u32(&mut self) -> u32 {
+        self.next_u64() as u32
+    }
+
+    /// Return the next random u64.
+    ///
+    /// By default this is implemented in terms of `next_u32`. An
+    /// implementation of this trait must provide at least one of
+    /// these two methods.
+    fn next_u64(&mut self) -> u64 {
+        (self.next_u32() as u64 << 32) | (self.next_u32() as u64)
+    }
 
 
     /// Return a random value of a Rand type.
@@ -594,7 +609,7 @@ pub struct XorShiftRng {
 
 impl Rng for XorShiftRng {
     #[inline]
-    fn next(&mut self) -> u32 {
+    fn next_u32(&mut self) -> u32 {
         let x = self.x;
         let t = x ^ (x << 11);
         self.x = self.y;
@@ -680,8 +695,12 @@ pub fn task_rng() -> @mut IsaacRng {
 // Allow direct chaining with `task_rng`
 impl<R: Rng> Rng for @mut R {
     #[inline]
-    fn next(&mut self) -> u32 {
-        (**self).next()
+    fn next_u32(&mut self) -> u32 {
+        (**self).next_u32()
+    }
+    #[inline]
+    fn next_u64(&mut self) -> u64 {
+        (**self).next_u64()
     }
 }
 
@@ -701,34 +720,6 @@ mod test {
     use super::*;
 
     #[test]
-    fn test_rng_seeded() {
-        let seed = seed(400);
-        let mut ra = IsaacRng::new_seeded(seed);
-        let mut rb = IsaacRng::new_seeded(seed);
-        assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u));
-    }
-
-    #[test]
-    fn test_rng_seeded_custom_seed() {
-        // much shorter than generated seeds which are 1024 bytes
-        let seed = [2u8, 32u8, 4u8, 32u8, 51u8];
-        let mut ra = IsaacRng::new_seeded(seed);
-        let mut rb = IsaacRng::new_seeded(seed);
-        assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u));
-    }
-
-    #[test]
-    fn test_rng_seeded_custom_seed2() {
-        let seed = [2u8, 32u8, 4u8, 32u8, 51u8];
-        let mut ra = IsaacRng::new_seeded(seed);
-        // Regression test that isaac is actually using the above vector
-        let r = ra.next();
-        debug2!("{:?}", r);
-        assert!(r == 890007737u32 // on x86_64
-                     || r == 2935188040u32); // on x86
-    }
-
-    #[test]
     fn test_gen_integer_range() {
         let mut r = rng();
         for _ in range(0, 1000) {
@@ -922,6 +913,15 @@ mod bench {
     }
 
     #[bench]
+    fn rand_isaac64(bh: &mut BenchHarness) {
+        let mut rng = Isaac64Rng::new();
+        do bh.iter {
+            rng.gen::<uint>();
+        }
+        bh.bytes = size_of::<uint>() as u64;
+    }
+
+    #[bench]
     fn rand_shuffle_100(bh: &mut BenchHarness) {
         let mut rng = XorShiftRng::new();
         let x : &mut[uint] = [1,..100];
diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs
index 2d1b57cebf5..48b894f51e0 100644
--- a/src/libstd/rt/task.rs
+++ b/src/libstd/rt/task.rs
@@ -509,7 +509,7 @@ mod test {
         do run_in_newsched_task() {
             use rand::{rng, Rng};
             let mut r = rng();
-            let _ = r.next();
+            let _ = r.next_u32();
         }
     }