rune_modules/
rand.rs

1#![allow(dead_code)]
2//! The native `rand` module for the [Rune Language].
3//!
4//! [Rune Language]: https://rune-rs.github.io
5//!
6//! ## Usage
7//!
8//! Add the following to your `Cargo.toml`:
9//!
10//! ```toml
11//! rune-modules = { version = "0.14.1", features = ["rand"] }
12//! ```
13//!
14//! Install it into your context:
15//!
16//! ```rust
17//! let mut context = rune::Context::with_default_modules()?;
18//! context.install(rune_modules::rand::module(true)?)?;
19//! # Ok::<_, rune::support::Error>(())
20//! ```
21//!
22//! Use it in Rune:
23//!
24//! ```rust,ignore
25//! fn main() {
26//!     let rng = rand::WyRand::new();
27//!     let rand_int = rng.int();
28//!     println(`Random int: {rand_int}`);
29//!     let rand_int_range = rng.int_range(-100, 100);
30//!     println(`Random int between -100 and 100: {rand_int_range}`);
31//! }
32//! ```
33
34use nanorand::Rng;
35use rune::{Any, ContextError, Module};
36
37/// Construct the `rand` module.
38pub fn module(_stdio: bool) -> Result<Module, ContextError> {
39    let mut module = Module::with_crate("rand")?;
40
41    module.ty::<WyRand>()?;
42    module
43        .function("new", WyRand::new)
44        .build_associated::<WyRand>()?;
45    module
46        .function("new_seed", WyRand::new_seed)
47        .build_associated::<WyRand>()?;
48    module.associated_function("int", WyRand::int)?;
49    module.associated_function("int_range", WyRand::int_range)?;
50
51    module.ty::<Pcg64>()?;
52    module
53        .function("new", Pcg64::new)
54        .build_associated::<Pcg64>()?;
55    module
56        .function("new_seed", Pcg64::new_seed)
57        .build_associated::<Pcg64>()?;
58    module.associated_function("int", Pcg64::int)?;
59    module.associated_function("int_range", Pcg64::int_range)?;
60
61    module.function("int", int).build()?;
62    module.function("int_range", int_range).build()?;
63    Ok(module)
64}
65
66#[derive(Any)]
67#[rune(item = ::rand)]
68struct WyRand {
69    inner: nanorand::WyRand,
70}
71
72impl WyRand {
73    /// Create a new RNG instance.
74    fn new() -> Self {
75        Self {
76            inner: nanorand::WyRand::new(),
77        }
78    }
79
80    /// Create a new RNG instance, using a custom seed.
81    fn new_seed(seed: i64) -> Self {
82        Self {
83            inner: nanorand::WyRand::new_seed(seed as u64),
84        }
85    }
86
87    /// Generate a random integer
88    fn int(&mut self) -> i64 {
89        self.inner.generate::<u64>() as i64
90    }
91
92    /// Generate a random integer within the specified range
93    fn int_range(&mut self, lower: i64, upper: i64) -> i64 {
94        self.inner.generate_range(0..(upper - lower) as u64) as i64 + lower
95    }
96}
97
98#[derive(Any)]
99#[rune(item = ::rand)]
100struct Pcg64 {
101    inner: nanorand::Pcg64,
102}
103
104impl Pcg64 {
105    /// Create a new RNG instance.
106    fn new() -> Self {
107        Self {
108            inner: nanorand::Pcg64::new(),
109        }
110    }
111
112    /// Create a new RNG instance, using a custom seed.
113    fn new_seed(seed: i64) -> Self {
114        Self {
115            inner: nanorand::Pcg64::new_seed(seed as u128),
116        }
117    }
118
119    /// Generate a random integer
120    fn int(&mut self) -> i64 {
121        self.inner.generate::<u64>() as i64
122    }
123
124    /// Generate a random integer within the specified range
125    fn int_range(&mut self, lower: i64, upper: i64) -> i64 {
126        self.inner.generate_range(0..(upper - lower) as u64) as i64 + lower
127    }
128}
129
130fn int() -> rune::support::Result<i64> {
131    Ok(nanorand::WyRand::new().generate::<u64>() as i64)
132}
133
134fn int_range(lower: i64, upper: i64) -> rune::support::Result<i64> {
135    Ok(nanorand::WyRand::new().generate_range(0..(upper - lower) as u64) as i64 + lower)
136}
137
138#[cfg(test)]
139mod tests {
140    use super::{int, int_range};
141
142    #[test]
143    fn test_range_is_exclusive() {
144        for _ in 0..100 {
145            assert_eq!(int_range(0, 1).unwrap(), 0);
146        }
147    }
148
149    #[test]
150    fn test_range_can_be_negative() {
151        for _ in 0..100 {
152            assert_eq!(int_range(-2, -1).unwrap(), -2);
153        }
154    }
155
156    #[test]
157    fn test_int_is_properly_signed() {
158        let mut any_negative = false;
159        let mut any_positive = false;
160
161        for _ in 0..100 {
162            let v: i64 = int().unwrap();
163            any_negative = any_negative || v < 0;
164            any_positive = any_positive || v > 0;
165        }
166
167        assert!(any_positive);
168        assert!(any_negative);
169    }
170}