alea_js/
lib.rs

1fn bit_or(num : f64)->f64{
2    // this function emulates the (num|0) operation in javascript
3    // in js, all numbers are 64 bits floating points
4    // on a bitwise operation, they are transformed into 32 bits signed integers
5    // then converted back into 64 bits javascript numbers
6    num as i64 as i32 as f64
7}
8/// Alea specific string hash function, faithful to javascript outputs.
9/// Normally used privately, but can be used alone.
10/// 
11/// # Example
12/// 
13/// ```
14/// use alea_js::Mash;
15/// let mut mash = Mash::new();
16/// mash.hash("string to hash");
17/// mash.hash("second string to hash");
18/// ```
19#[derive(Copy, Clone)]
20pub struct Mash{
21    n:f64
22}
23impl Mash{
24    pub fn new()->Self{
25        Self { n: 4022871197.0 }
26    }
27    /// Hashes a string.
28    /// Function alters Mash state:
29    /// Hashing the same string twice produces different results.
30    pub fn hash(&mut self, r:&str)->f64{
31        let e = 0.02519603282416938;
32        for s in r.encode_utf16().collect::<Vec<u16>>(){
33            self.n+=s as f64;
34            let f = e*self.n-bit_or(e*self.n);
35            let t = f* bit_or(e*self.n);
36            self.n = 2f64.powi(32) * (t - bit_or(t)) + bit_or(t);
37        }
38        bit_or(self.n) * 2f64.powi(-32)
39    }
40}
41/// Seedable random number generator, faithful to javascript.
42/// # Examples
43/// 
44/// ```
45/// use alea_js::Alea;
46/// let mut a = Alea::new("frank");
47/// assert_eq!(a.random(), 0.8080874253064394);
48/// assert_eq!(a.random(), 0.8366762748919427);
49/// assert_eq!(a.random(), 0.24404818122275174);
50/// let mut a = Alea::new("frank");
51/// assert_eq!(a.uint32(), 3470709064);
52/// ```
53#[derive(Copy, Clone)]
54pub struct Alea{
55    s0: f64, //three numbers that determine the internal state of alea.
56    s1: f64,
57    s2: f64,
58    x: f64
59}
60impl Alea {
61    /// Initializes the random number generator with a string seed.
62    pub fn new(seed: &str)->Self{
63        let mut mash = Mash::new();
64        let mut s0 = mash.hash(" ");
65        let mut s1 = mash.hash(" ");
66        let mut s2 = mash.hash(" ");
67        let x = 1.0;
68        s0 -= mash.hash(&seed);
69        s1 -= mash.hash(&seed);
70        s2 -= mash.hash(&seed);
71        if s0 < 0.0{
72            s0 +=1.0;
73        }
74        if s1 < 0.0{
75            s1 +=1.0;
76        }
77        if s2 < 0.0{
78            s2 +=1.0;
79        }
80        Self { s0,s1,s2,x}
81    }
82    /// Returns a random f64 value.
83    pub fn random(&mut self) -> f64{
84        let y = self.x * 2f64.powi(-32) + self.s0 * 2091639.0;
85        self.s0 = self.s1;
86        self.s1 = self.s2;
87        self.x = bit_or(y);
88        self.s2 = y - self.x;
89        self.s2
90    }
91    /// Returns a random u32 value.
92    pub fn uint32(&mut self)-> u32{
93        (self.random() * 2f64.powi(32)) as u32
94    }
95}
96
97struct MashFast{
98    n:f64
99}
100impl MashFast{
101    pub fn new()->Self{
102        Self { n: 4022871197.0 }
103    }
104    pub fn hash(&mut self, r:&str)->f64{
105        for s in r.encode_utf16().collect::<Vec<u16>>(){
106            self.n += s as f64;
107            let mut hash: f64 = self.n * 0.02519603282416938;
108            self.n = hash.trunc();
109            hash -= self.n;
110            hash *= self.n;
111            self.n = hash.trunc();
112            hash -= self.n;
113            self.n += (hash * 2f64.powi(32)).trunc();
114        }
115        (self.n)* 2f64.powi(-32)
116    }
117}
118/// Seedable random number generator.
119/// This version is more performant, but could vary from javascript with extreme values.
120#[derive(Copy, Clone)]
121pub struct AleaFast{
122    s0: f64, //three numbers that determine the internal state of alea
123    s1: f64,
124    s2: f64,
125    x: f64
126}
127impl AleaFast {
128    /// Initializes the random number generator with a string seed.
129    pub fn new(seed: &str)->Self{
130        let mut mash = MashFast::new();
131        let mut s0 = mash.hash(" ");
132        let mut s1 = mash.hash(" ");
133        let mut s2 = mash.hash(" ");
134        let x = 1.0;
135        s0 -= mash.hash(&seed);
136        s1 -= mash.hash(&seed);
137        s2 -= mash.hash(&seed);
138        if s0 < 0.0{
139            s0 +=1.0;
140        }
141        if s1 < 0.0{
142            s1 +=1.0;
143        }
144        if s2 < 0.0{
145            s2 +=1.0;
146        }
147        Self { s0,s1,s2,x}
148    }
149    /// Returns a random f64 value.
150    pub fn random(&mut self) -> f64{
151        let y = self.x * 2f64.powi(-32) + self.s0 * 2091639.0;
152        self.s0 = self.s1;
153        self.s1 = self.s2;
154        self.x = y.trunc();
155        self.s2 = y - self.x;
156        self.s2
157    }
158    /// Returns a random u32 value.
159    pub fn uint32(&mut self)-> u32{
160        (self.random() * 2f64.powi(32)) as u32
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use super::*;
167    #[test]
168    fn bit_or_test() {
169        assert_eq!(bit_or(0.2), 0.0); //near-zero testing
170        assert_eq!(bit_or(-1.8), -1.0);
171        assert_eq!(bit_or(1.9), 1.0);
172
173        assert_eq!(bit_or(4294967296.3), 0.0); //u32 min
174        assert_eq!(bit_or(4294967295.6), -1.0);
175        assert_eq!(bit_or(4294967297.9), 1.0);
176
177        assert_eq!(bit_or(2147483648.9), -2147483648.0); //i32 min
178        assert_eq!(bit_or(2147483647.3), 2147483647.0);
179        assert_eq!(bit_or(2147483649.1), -2147483647.0);
180    }
181    #[test]
182    fn mash_test(){
183        let mut mash = Mash::new();
184        assert_eq!(mash.hash(""), -0.06335230986587703);
185        let mut mash = Mash::new();
186        assert_eq!(mash.hash(" "), -0.1366710769943893);
187        let mut mash = Mash::new();
188        assert_eq!(mash.hash("frank"), 0.044354382902383804);
189        let mut mash = Mash::new();
190        assert_eq!(mash.hash("cat"), 0.06714190426282585);
191        assert_eq!(mash.hash("rat"), -0.24548634607344866);
192        assert_eq!(mash.hash("bat"), 0.05828765174373984);
193        assert_eq!(mash.hash(" "), 0.03728155279532075);
194        assert_eq!(mash.hash(" "), 0.32264634780585766);
195        assert_eq!(mash.hash(" "), -0.356016042875126);
196        assert_eq!(mash.hash(" "), -0.4360403118189424);
197    }
198    #[test]
199    fn alea_test(){
200        let mut a = Alea::new("frank");
201        assert_eq!(a.random(), 0.8080874253064394);
202        assert_eq!(a.random(), 0.8366762748919427);
203        assert_eq!(a.random(), 0.24404818122275174);
204        let mut a = Alea::new("frank");
205        assert_eq!(a.uint32(), 3470709064);
206    }
207    #[test]
208    fn alea_fast_test(){
209        let mut a = AleaFast::new("frank");
210        assert_eq!(a.random(), 0.8080874253064394);
211        assert_eq!(a.random(), 0.8366762748919427);
212        assert_eq!(a.random(), 0.24404818122275174);
213        let mut a = AleaFast::new("frank");
214        assert_eq!(a.uint32(), 3470709064);
215    }
216}