1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use {
koto_runtime::{
external_error, get_external_instance, make_external_value, num2, num4, ExternalValue,
Value, ValueMap,
},
rand::{Rng, SeedableRng},
rand_chacha::ChaCha20Rng,
std::fmt,
};
pub fn make_module() -> ValueMap {
use Value::*;
let mut result = ChaChaRng::make_value_map(ChaCha20Rng::from_entropy());
result.add_fn("generator", |vm, args| match vm.get_args(args) {
[] => Ok(Map(ChaChaRng::make_value_map(ChaCha20Rng::from_entropy()))),
[Number(n)] => Ok(Map(ChaChaRng::make_value_map(ChaCha20Rng::seed_from_u64(
n.to_bits(),
)))),
_ => external_error!("random.generator - expected no arguments, or seed number"),
});
result
}
#[derive(Debug)]
struct ChaChaRng(ChaCha20Rng);
impl ChaChaRng {
fn make_value_map(rng: ChaCha20Rng) -> ValueMap {
use Value::*;
let mut result = ValueMap::new();
result.add_instance_fn("bool", |vm, args| {
let args = vm.get_args(args);
get_external_instance!(args, "random", "bool", Self, rng, {
Ok(Bool(rng.0.gen::<bool>()))
})
});
result.add_instance_fn("number", |vm, args| {
let args = vm.get_args(args);
get_external_instance!(args, "random", "number", Self, rng, {
Ok(Number(rng.0.gen::<f64>()))
})
});
result.add_instance_fn("number2", |vm, args| {
let args = vm.get_args(args);
get_external_instance!(args, "random", "number2", Self, rng, {
let result = num2::Num2(rng.0.gen::<f64>(), rng.0.gen::<f64>());
Ok(Num2(result))
})
});
result.add_instance_fn("number4", |vm, args| {
let args = vm.get_args(args);
get_external_instance!(args, "random", "number4", Self, rng, {
let result = num4::Num4(
rng.0.gen::<f32>(),
rng.0.gen::<f32>(),
rng.0.gen::<f32>(),
rng.0.gen::<f32>(),
);
Ok(Num4(result))
})
});
result.add_instance_fn("pick", |vm, args| {
let args = vm.get_args(args);
get_external_instance!(args, "random", "number", Self, rng, {
match &args[1..] {
[List(l)] => {
let index = rng.0.gen_range(0, l.len());
Ok(l.data()[index].clone())
}
[Range(r)] => {
let (start, end) = if r.end > r.start {
(r.start, r.end)
} else {
(r.end, r.start)
};
let size = end - start;
let index = rng.0.gen_range(0, size);
Ok(Number((start + index) as f64))
}
_ => external_error!("random.pick - expected list or range as argument"),
}
})
});
result.add_instance_fn("seed", |vm, args| {
let args = vm.get_args(args);
get_external_instance!(args, "random", "seed", Self, rng, {
match &args[1..] {
[Number(n)] => {
*rng = ChaChaRng(ChaCha20Rng::seed_from_u64(n.to_bits()));
Ok(Empty)
}
_ => external_error!("random.seed - expected number as argument"),
}
})
});
result.insert(Value::ExternalDataId, make_external_value(Self(rng)));
result
}
}
impl ExternalValue for ChaChaRng {
fn value_type(&self) -> String {
"Rng".to_string()
}
}
impl fmt::Display for ChaChaRng {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Rng")
}
}