Skip to content

Commit 64130f1

Browse files
Dretchbrson
authored andcommitted
add a bunch more rand::rng extension methods - one for each primitive type and also some choose/shuffle functions (for rust-lang#2379)
1 parent 11e8195 commit 64130f1

File tree

1 file changed

+275
-38
lines changed

1 file changed

+275
-38
lines changed

src/libcore/rand.rs

Lines changed: 275 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#[doc = "Random number generation"];
22

3-
export rng, extensions;
3+
export rng, weighted, extensions;
44

55
enum rctx {}
66

@@ -17,44 +17,214 @@ iface rng {
1717
fn next() -> u32;
1818
}
1919

20+
#[doc = "A value with a particular weight compared to other values"]
21+
type weighted<T> = { weight: uint, item: T };
22+
2023
#[doc = "Extension methods for random number generators"]
2124
impl extensions for rng {
2225

26+
#[doc = "Return a random int"]
27+
fn gen_int() -> int {
28+
self.gen_i64() as int
29+
}
30+
31+
#[doc = "Return an int randomly chosen from the range [start, end], \
32+
failing if start > end"]
33+
fn gen_int_from(start: int, end: int) -> int {
34+
assert start <= end;
35+
start + int::abs(self.gen_int() % (end - start + 1))
36+
}
37+
38+
#[doc = "Return a random i8"]
39+
fn gen_i8() -> i8 {
40+
self.next() as i8
41+
}
42+
43+
#[doc = "Return a random i16"]
44+
fn gen_i16() -> i16 {
45+
self.next() as i16
46+
}
47+
48+
#[doc = "Return a random i32"]
49+
fn gen_i32() -> i32 {
50+
self.next() as i32
51+
}
52+
53+
#[doc = "Return a random i64"]
54+
fn gen_i64() -> i64 {
55+
(self.next() as i64 << 32) | self.next() as i64
56+
}
57+
58+
#[doc = "Return a random uint"]
59+
fn gen_uint() -> uint {
60+
self.gen_u64() as u64
61+
}
62+
63+
#[doc = "Return a uint randomly chosen from the range [start, end], \
64+
failing if start > end"]
65+
fn gen_uint_from(start: uint, end: uint) -> uint {
66+
assert start <= end;
67+
start + (self.gen_uint() % (end - start + 1u))
68+
}
69+
70+
#[doc = "Return a random u8"]
71+
fn gen_u8() -> u8 {
72+
self.next() as u8
73+
}
74+
75+
#[doc = "Return a random u16"]
76+
fn gen_u16() -> u16 {
77+
self.next() as u16
78+
}
79+
80+
#[doc = "Return a random u32"]
81+
fn gen_u32() -> u32 {
82+
self.next()
83+
}
84+
85+
#[doc = "Return a random u64"]
86+
fn gen_u64() -> u64 {
87+
(self.next() as u64 << 32) | self.next() as u64
88+
}
89+
2390
#[doc = "Return a random float"]
2491
fn gen_float() -> float {
25-
let u1 = self.next() as float;
26-
let u2 = self.next() as float;
27-
let u3 = self.next() as float;
28-
let scale = u32::max_value as float;
29-
ret ((u1 / scale + u2) / scale + u3) / scale;
92+
self.gen_f64() as float
3093
}
3194

32-
#[doc = "Return a random string composed of A-Z, a-z, 0-9."]
95+
#[doc = "Return a random f32"]
96+
fn gen_f32() -> f32 {
97+
self.gen_f64() as f32
98+
}
99+
100+
#[doc = "Return a random f64"]
101+
fn gen_f64() -> f64 {
102+
let u1 = self.next() as f64;
103+
let u2 = self.next() as f64;
104+
let u3 = self.next() as f64;
105+
let scale = u32::max_value as f64;
106+
ret ((u1 / scale + u2) / scale + u3) / scale;
107+
}
108+
109+
#[doc = "Return a random char"]
110+
fn gen_char() -> char {
111+
self.next() as char
112+
}
113+
114+
#[doc = "Return a char randomly chosen from chars, failing if chars is \
115+
empty"]
116+
fn gen_char_from(chars: str) -> char {
117+
assert !chars.is_empty();
118+
self.choose(str::chars(chars))
119+
}
120+
121+
#[doc = "Return a random bool"]
122+
fn gen_bool() -> bool {
123+
self.next() & 1u32 == 1u32
124+
}
125+
126+
#[doc = "Return a bool with a 1 in n chance of true"]
127+
fn gen_weighted_bool(n: uint) -> bool {
128+
if n == 0u {
129+
true
130+
} else {
131+
self.gen_uint_from(1u, n) == 1u
132+
}
133+
}
134+
135+
#[doc = "Return a random string of the specified length composed of A-Z, \
136+
a-z, 0-9"]
33137
fn gen_str(len: uint) -> str {
34138
let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
35139
"abcdefghijklmnopqrstuvwxyz" +
36140
"0123456789";
37141
let mut s = "";
38142
let mut i = 0u;
39143
while (i < len) {
40-
let n = self.next() as uint % charset.len();
41-
s = s + str::from_char(str::char_at(charset, n));
144+
s = s + str::from_char(self.gen_char_from(charset));
42145
i += 1u;
43146
}
44147
s
45148
}
46149

47-
#[doc = "Return a random byte string."]
150+
#[doc = "Return a random byte string of the specified length"]
48151
fn gen_bytes(len: uint) -> [u8] {
49-
let mut v = [];
50-
let mut i = 0u;
51-
while i < len {
52-
let n = self.next() as uint;
53-
v += [(n % (u8::max_value as uint)) as u8];
54-
i += 1u;
152+
vec::from_fn(len) {|_i|
153+
self.gen_u8()
154+
}
155+
}
156+
157+
#[doc = "Choose an item randomly, failing if values is empty"]
158+
fn choose<T:copy>(values: [T]) -> T {
159+
self.choose_option(values).get()
160+
}
161+
162+
#[doc = "Choose some(item) randomly, returning none if values is empty"]
163+
fn choose_option<T:copy>(values: [T]) -> option<T> {
164+
if values.is_empty() {
165+
none
166+
} else {
167+
some(values[self.gen_uint_from(0u, values.len() - 1u)])
55168
}
56-
v
57169
}
170+
171+
#[doc = "Choose an item respecting the relative weights, failing if \
172+
the sum of the weights is 0"]
173+
fn choose_weighted<T: copy>(v : [weighted<T>]) -> T {
174+
self.choose_weighted_option(v).get()
175+
}
176+
177+
#[doc = "Choose some(item) respecting the relative weights, returning \
178+
none if the sum of the weights is 0"]
179+
fn choose_weighted_option<T:copy>(v: [weighted<T>]) -> option<T> {
180+
let mut total = 0u;
181+
for v.each {|item|
182+
total += item.weight;
183+
}
184+
if total == 0u {
185+
ret none;
186+
}
187+
let chosen = self.gen_uint_from(0u, total - 1u);
188+
let mut so_far = 0u;
189+
for v.each {|item|
190+
so_far += item.weight;
191+
if so_far > chosen {
192+
ret some(item.item);
193+
}
194+
}
195+
unreachable();
196+
}
197+
198+
#[doc = "Return a vec containing copies of the items, in order, where \
199+
the weight of the item determines how many copies there are"]
200+
fn weighted_vec<T:copy>(v: [weighted<T>]) -> [T] {
201+
let mut r = [];
202+
for v.each {|item|
203+
uint::range(0u, item.weight) {|_i|
204+
r += [item.item];
205+
}
206+
}
207+
r
208+
}
209+
210+
#[doc = "Shuffle a vec"]
211+
fn shuffle<T:copy>(values: [T]) -> [T] {
212+
let mut m = vec::to_mut(values);
213+
self.shuffle_mut(m);
214+
ret vec::from_mut(m);
215+
}
216+
217+
#[doc = "Shuffle a mutable vec in place"]
218+
fn shuffle_mut<T>(&values: [mut T]) {
219+
let mut i = values.len();
220+
while i >= 2u {
221+
// invariant: elements with index >= i have been locked in place.
222+
i -= 1u;
223+
// lock element i in place.
224+
vec::swap(values, i, self.gen_uint_from(0u, i));
225+
}
226+
}
227+
58228
}
59229

60230
#[doc = "Create a random number generator"]
@@ -72,27 +242,33 @@ fn rng() -> rng {
72242
mod tests {
73243

74244
#[test]
75-
fn test() {
76-
let r1 = rand::rng();
77-
log(debug, r1.next());
78-
log(debug, r1.next());
79-
{
80-
let r2 = rand::rng();
81-
log(debug, r1.next());
82-
log(debug, r2.next());
83-
log(debug, r1.next());
84-
log(debug, r1.next());
85-
log(debug, r2.next());
86-
log(debug, r2.next());
87-
log(debug, r1.next());
88-
log(debug, r1.next());
89-
log(debug, r1.next());
90-
log(debug, r2.next());
91-
log(debug, r2.next());
92-
log(debug, r2.next());
93-
}
94-
log(debug, r1.next());
95-
log(debug, r1.next());
245+
fn gen_int_from() {
246+
let r = rand::rng();
247+
let a = r.gen_int_from(-3, 42);
248+
assert a >= -3 && a <= 42;
249+
assert r.gen_int_from(0, 0) == 0;
250+
assert r.gen_int_from(-12, -12) == -12;
251+
}
252+
253+
#[test]
254+
#[should_fail]
255+
fn gen_int_from_fail() {
256+
rand::rng().gen_int_from(5, -2);
257+
}
258+
259+
#[test]
260+
fn gen_uint_from() {
261+
let r = rand::rng();
262+
let a = r.gen_uint_from(3u, 42u);
263+
assert a >= 3u && a <= 42u;
264+
assert r.gen_uint_from(0u, 0u) == 0u;
265+
assert r.gen_uint_from(12u, 12u) == 12u;
266+
}
267+
268+
#[test]
269+
#[should_fail]
270+
fn gen_uint_from_fail() {
271+
rand::rng().gen_uint_from(5u, 2u);
96272
}
97273

98274
#[test]
@@ -103,6 +279,13 @@ mod tests {
103279
log(debug, (a, b));
104280
}
105281

282+
#[test]
283+
fn gen_weighted_bool() {
284+
let r = rand::rng();
285+
assert r.gen_weighted_bool(0u) == true;
286+
assert r.gen_weighted_bool(1u) == true;
287+
}
288+
106289
#[test]
107290
fn gen_str() {
108291
let r = rand::rng();
@@ -121,6 +304,60 @@ mod tests {
121304
assert r.gen_bytes(10u).len() == 10u;
122305
assert r.gen_bytes(16u).len() == 16u;
123306
}
307+
308+
#[test]
309+
fn choose() {
310+
let r = rand::rng();
311+
assert r.choose([1, 1, 1]) == 1;
312+
}
313+
314+
#[test]
315+
fn choose_option() {
316+
let r = rand::rng();
317+
assert r.choose_option([]) == none::<int>;
318+
assert r.choose_option([1, 1, 1]) == some(1);
319+
}
320+
321+
#[test]
322+
fn choose_weighted() {
323+
let r = rand::rng();
324+
assert r.choose_weighted([{weight: 1u, item: 42}]) == 42;
325+
assert r.choose_weighted([
326+
{weight: 0u, item: 42},
327+
{weight: 1u, item: 43}
328+
]) == 43;
329+
}
330+
331+
#[test]
332+
fn choose_weighted_option() {
333+
let r = rand::rng();
334+
assert r.choose_weighted_option([{weight: 1u, item: 42}]) == some(42);
335+
assert r.choose_weighted_option([
336+
{weight: 0u, item: 42},
337+
{weight: 1u, item: 43}
338+
]) == some(43);
339+
assert r.choose_weighted_option([]) == none::<int>;
340+
}
341+
342+
#[test]
343+
fn weighted_vec() {
344+
let r = rand::rng();
345+
let empty: [int] = [];
346+
assert r.weighted_vec([]) == empty;
347+
assert r.weighted_vec([
348+
{weight: 0u, item: 3u},
349+
{weight: 1u, item: 2u},
350+
{weight: 2u, item: 1u}
351+
]) == [2u, 1u, 1u];
352+
}
353+
354+
#[test]
355+
fn shuffle() {
356+
let r = rand::rng();
357+
let empty: [int] = [];
358+
assert r.shuffle([]) == empty;
359+
assert r.shuffle([1, 1, 1]) == [1, 1, 1];
360+
}
124361
}
125362

126363

0 commit comments

Comments
 (0)