mc173/
rand.rs

1//! Different kind of pseudo-random number generator.
2
3use std::sync::atomic::{AtomicI64, Ordering};
4use std::time::{UNIX_EPOCH, SystemTime};
5use std::num::Wrapping;
6
7use glam::{Vec3, DVec3};
8
9
10const MULTIPLIER: Wrapping<i64> = Wrapping(0x5DEECE66D);
11const ADDEND: Wrapping<i64> = Wrapping(0xB);
12const MASK: Wrapping<i64> = Wrapping((1 << 48) - 1);
13
14const FLOAT_DIV: f32 = (1u32 << 24) as f32;
15const DOUBLE_DIV: f64 = (1u64 << 53) as f64;
16
17
18#[inline]
19fn initial_scramble(seed: i64) -> Wrapping<i64> {
20    (Wrapping(seed) ^ MULTIPLIER) & MASK
21}
22
23
24/// Generate a new seed in the same way as `java.f.Random` (same constants).
25fn gen_seed() -> i64 {
26    static SEED: AtomicI64 = AtomicI64::new(8682522807148012);
27    let mut current = SEED.load(Ordering::Relaxed);
28    loop {
29        let next = current.wrapping_mul(181783497276652981);
30        match SEED.compare_exchange_weak(current, next, Ordering::Relaxed, Ordering::Relaxed) {
31            Ok(_) => {
32                // This is a bit different from Java implementation because the nano time
33                // as an integer value is not available in Rust, even with Instant.
34                // So we're using duration since unix epoch of the system time, maybe not
35                // as safe as the Java implementation.
36                return match SystemTime::now().duration_since(UNIX_EPOCH) {
37                    Ok(d) => next ^ (d.as_nanos() as i64),
38                    Err(_) => next
39                };
40            }
41            Err(old) => current = old
42        }
43    }
44}
45
46
47/// A pseudo-random number generator ported from the Java standard *RNG* with additional
48/// utility methods better suited for rust.
49#[derive(Debug, Clone)]
50pub struct JavaRandom {
51    seed: Wrapping<i64>,
52    next_gaussian: Option<f64>,
53}
54
55impl Default for JavaRandom {
56    fn default() -> Self {
57        Self::new_seeded()
58    }
59}
60
61impl JavaRandom {
62
63    #[inline]
64    pub fn new(seed: i64) -> JavaRandom {
65        JavaRandom { seed: initial_scramble(seed), next_gaussian: None }
66    }
67
68    #[inline]
69    pub fn new_seeded() -> JavaRandom {
70        Self::new(gen_seed())
71    }
72
73    #[inline]
74    pub fn new_blank() -> JavaRandom {
75        JavaRandom { seed: Wrapping(0), next_gaussian: None }
76    }
77
78    #[inline]
79    pub fn set_seed(&mut self, seed: i64) {
80        self.seed = initial_scramble(seed);
81    }
82
83    #[inline]
84    pub fn get_seed(&self) -> i64 {
85        self.seed.0
86    }
87
88    pub fn next_blank(&mut self) {
89        self.seed = (self.seed * MULTIPLIER + ADDEND) & MASK;
90    }
91
92    #[inline]
93    fn next(&mut self, bits: u8) -> i32 {
94        self.next_blank();
95        (self.seed.0 as u64 >> (48 - bits)) as i32
96    }
97
98    #[inline]
99    pub fn next_int(&mut self) -> i32 {
100        self.next(32)
101    }
102
103    pub fn next_int_bounded(&mut self, bound: i32) -> i32 {
104
105        debug_assert!(bound >= 0, "bound is negative");
106
107        if (bound & -bound) == bound {
108            // If the bound is a power of two, this is simpler.
109            (((bound as i64).wrapping_mul(self.next(31) as i64)) >> 31) as i32
110        } else {
111
112            let mut bits;
113            let mut val;
114
115            loop {
116                bits = self.next(31);
117                val = bits.rem_euclid(bound);
118                if bits.wrapping_sub(val).wrapping_add(bound - 1) >= 0 {
119                    break;
120                }
121            }
122
123            val
124
125        }
126
127    }
128
129    pub fn next_long(&mut self) -> i64 {
130        ((self.next(32) as i64) << 32).wrapping_add(self.next(32) as i64)
131    }
132
133    /// Get the next pseudo-random single-precision float.
134    pub fn next_float(&mut self) -> f32 {
135        self.next(24) as f32 / FLOAT_DIV
136    }
137
138    /// Get the next pseudo-random double-precision float.
139    pub fn next_double(&mut self) -> f64 {
140        let high = (self.next(26) as i64) << 27;
141        let low = self.next(27) as i64;
142        (high.wrapping_add(low) as f64) / DOUBLE_DIV
143    }
144
145    /// Get the next pseudo-random double-precision Gaussian random number.
146    pub fn next_gaussian(&mut self) -> f64 {
147        if let Some(next_gaussian) = self.next_gaussian.take() {
148            next_gaussian
149        } else {
150            loop {
151                let v1 = self.next_double() * 2.0 - 1.0;
152                let v2 = self.next_double() * 2.0 - 1.0;
153                let s = v1 * v1 + v2 * v2;
154                if s < 1.0 && s != 0.0 {
155                    let multiplier = (-2.0 * s.ln() / s).sqrt();
156                    self.next_gaussian = Some(v2 * multiplier);
157                    break v1 * multiplier;
158                }
159            }
160        }
161    }
162    
163    /// Get the next pseudo-random single-precision float vector, x, y and z.
164    /// **This is not part of the standard Java class.**
165    pub fn next_float_vec(&mut self) -> Vec3 {
166        Vec3 { 
167            x: self.next_float(), 
168            y: self.next_float(),
169            z: self.next_float(),
170        }
171    }
172
173    /// Get the next pseudo-random double-precision float vector, x, y and z.
174    /// **This is not part of the standard Java class.**
175    pub fn next_double_vec(&mut self) -> DVec3 {
176        DVec3 {
177            x: self.next_double(), 
178            y: self.next_double(),
179            z: self.next_double(),
180        }
181    }
182
183    /// Get the next pseudo-random double-precision double vector, x, y and z, 
184    /// with Gaussian distribution.
185    /// **This is not part of the standard Java class.**
186    pub fn next_gaussian_vec(&mut self) -> DVec3 {
187        DVec3 {
188            x: self.next_gaussian(), 
189            y: self.next_gaussian(),
190            z: self.next_gaussian(),
191        }
192    }
193
194    /// Randomly pick an item in the given slice.
195    /// **This is not part of the standard Java class.**
196    #[inline]
197    pub fn next_choice<T: Copy>(&mut self, items: &[T]) -> T {
198        items[self.next_int_bounded(items.len() as i32) as usize]
199    }
200
201    /// Randomly pick an item in the given slice and return mutable reference to it.
202    /// **This is not part of the standard Java class.**
203    #[inline]
204    pub fn next_choice_ref<'a, T>(&mut self, items: &'a [T]) -> &'a T {
205        &items[self.next_int_bounded(items.len() as i32) as usize]
206    }
207
208    /// Randomly pick an item in the given slice and return mutable reference to it.
209    /// **This is not part of the standard Java class.**
210    #[inline]
211    pub fn next_choice_mut<'a, T>(&mut self, items: &'a mut [T]) -> &'a mut T {
212        &mut items[self.next_int_bounded(items.len() as i32) as usize]
213    }
214
215}