Skip to content

Commit f75ce9d

Browse files
rand: move functions from rand.util to the main module (#13840)
1 parent 875ad1f commit f75ce9d

File tree

5 files changed

+142
-131
lines changed

5 files changed

+142
-131
lines changed

vlib/rand/rand.v

Lines changed: 93 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,76 @@ pub fn (mut rng PRNG) ascii(len int) string {
274274
return internal_string_from_set(mut rng, rand.ascii_chars, len)
275275
}
276276

277+
// Configuration struct for the shuffle functions.
278+
// The start index is inclusive and the end index is exclusive.
279+
// Set the end to 0 to shuffle until the end of the array.
280+
[params]
281+
pub struct ShuffleConfigStruct {
282+
pub:
283+
start int
284+
end int
285+
}
286+
287+
fn (config ShuffleConfigStruct) validate_for<T>(a []T) ? {
288+
if config.start < 0 || config.start >= a.len {
289+
return error("argument 'config.start' must be in range [0, a.len)")
290+
}
291+
if config.end < 0 || config.end > a.len {
292+
return error("argument 'config.end' must be in range [0, a.len]")
293+
}
294+
}
295+
296+
// shuffle randomly permutates the elements in `a`. The range for shuffling is
297+
// optional and the entire array is shuffled by default. Leave the end as 0 to
298+
// shuffle all elements until the end.
299+
[direct_array_access]
300+
pub fn (mut rng PRNG) shuffle<T>(mut a []T, config ShuffleConfigStruct) ? {
301+
config.validate_for(a) ?
302+
new_end := if config.end == 0 { a.len } else { config.end }
303+
for i in config.start .. new_end {
304+
x := rng.int_in_range(i, new_end) or { config.start }
305+
// swap
306+
a_i := a[i]
307+
a[i] = a[x]
308+
a[x] = a_i
309+
}
310+
}
311+
312+
// shuffle_clone returns a random permutation of the elements in `a`.
313+
// The permutation is done on a fresh clone of `a`, so `a` remains unchanged.
314+
pub fn (mut rng PRNG) shuffle_clone<T>(a []T, config ShuffleConfigStruct) ?[]T {
315+
mut res := a.clone()
316+
rng.shuffle(mut res, config) ?
317+
return res
318+
}
319+
320+
// choose samples k elements from the array without replacement.
321+
// This means the indices cannot repeat and it restricts the sample size to be less than or equal to the size of the given array.
322+
// Note that if the array has repeating elements, then the sample may have repeats as well.
323+
pub fn (mut rng PRNG) choose<T>(array []T, k int) ?[]T {
324+
n := array.len
325+
if k > n {
326+
return error('Cannot choose $k elements without replacement from a $n-element array.')
327+
}
328+
mut results := []T{len: k}
329+
mut indices := []int{len: n, init: it}
330+
rng.shuffle(mut indices) ?
331+
for i in 0 .. k {
332+
results[i] = array[indices[i]]
333+
}
334+
return results
335+
}
336+
337+
// sample samples k elements from the array with replacement.
338+
// This means the elements can repeat and the size of the sample may exceed the size of the array.
339+
pub fn (mut rng PRNG) sample<T>(array []T, k int) []T {
340+
mut results := []T{len: k}
341+
for i in 0 .. k {
342+
results[i] = array[rng.intn(array.len) or { 0 }]
343+
}
344+
return results
345+
}
346+
277347
__global default_rng &PRNG
278348

279349
// new_default returns a new instance of the default RNG. If the seed is not provided, the current time will be used to seed the instance.
@@ -440,17 +510,17 @@ const (
440510
// users or business transactions.
441511
// (https://news.ycombinator.com/item?id=14526173)
442512
pub fn ulid() string {
443-
return internal_ulid_at_millisecond(mut default_rng, u64(time.utc().unix_time_milli()))
513+
return default_rng.ulid()
444514
}
445515

446516
// ulid_at_millisecond does the same as `ulid` but takes a custom Unix millisecond timestamp via `unix_time_milli`.
447517
pub fn ulid_at_millisecond(unix_time_milli u64) string {
448-
return internal_ulid_at_millisecond(mut default_rng, unix_time_milli)
518+
return default_rng.ulid_at_millisecond(unix_time_milli)
449519
}
450520

451521
// string_from_set returns a string of length `len` containing random characters sampled from the given `charset`
452522
pub fn string_from_set(charset string, len int) string {
453-
return internal_string_from_set(mut default_rng, charset, len)
523+
return default_rng.string_from_set(charset, len)
454524
}
455525

456526
// string returns a string of length `len` containing random characters in range `[a-zA-Z]`.
@@ -468,19 +538,28 @@ pub fn ascii(len int) string {
468538
return string_from_set(rand.ascii_chars, len)
469539
}
470540

471-
// shuffle randomly permutates the elements in `a`.
472-
pub fn shuffle<T>(mut a []T) {
473-
len := a.len
474-
for i in 0 .. len {
475-
si := i + intn(len - i) or { len }
476-
a[si], a[i] = a[i], a[si]
477-
}
541+
// shuffle randomly permutates the elements in `a`. The range for shuffling is
542+
// optional and the entire array is shuffled by default. Leave the end as 0 to
543+
// shuffle all elements until the end.
544+
pub fn shuffle<T>(mut a []T, config ShuffleConfigStruct) ? {
545+
default_rng.shuffle(mut a, config) ?
478546
}
479547

480548
// shuffle_clone returns a random permutation of the elements in `a`.
481549
// The permutation is done on a fresh clone of `a`, so `a` remains unchanged.
482-
pub fn shuffle_clone<T>(a []T) []T {
483-
mut res := a.clone()
484-
shuffle(mut res)
485-
return res
550+
pub fn shuffle_clone<T>(a []T, config ShuffleConfigStruct) ?[]T {
551+
return default_rng.shuffle_clone(a, config)
552+
}
553+
554+
// choose samples k elements from the array without replacement.
555+
// This means the indices cannot repeat and it restricts the sample size to be less than or equal to the size of the given array.
556+
// Note that if the array has repeating elements, then the sample may have repeats as well.
557+
pub fn choose<T>(array []T, k int) ?[]T {
558+
return default_rng.choose(array, k)
559+
}
560+
561+
// sample samples k elements from the array with replacement.
562+
// This means the elements can repeat and the size of the sample may exceed the size of the array.
563+
pub fn sample<T>(array []T, k int) []T {
564+
return default_rng.sample(array, k)
486565
}

vlib/rand/random_numbers_test.v

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,6 @@ fn test_shuffle() {
326326
a := get_n_random_ints(seed, 10)
327327
arrays << a
328328
}
329-
//
330329
mut digits := []map[int]int{len: 10}
331330
for digit in 0 .. 10 {
332331
digits[digit] = {}
@@ -337,7 +336,7 @@ fn test_shuffle() {
337336
for mut a in arrays {
338337
o := a.clone()
339338
for _ in 0 .. 100 {
340-
rand.shuffle(mut a)
339+
rand.shuffle(mut a) or { panic('shuffle failed') }
341340
assert *a != o
342341
for idx in 0 .. 10 {
343342
digits[idx][a[idx]]++
@@ -355,12 +354,25 @@ fn test_shuffle() {
355354
}
356355
}
357356

357+
fn test_shuffle_partial() ? {
358+
mut a := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
359+
mut b := a.clone()
360+
361+
rand.shuffle(mut a, start: 4) ?
362+
assert a[..4] == b[..4]
363+
364+
a = b.clone()
365+
rand.shuffle(mut a, start: 3, end: 7) ?
366+
assert a[..3] == b[..3]
367+
assert a[7..] == b[7..]
368+
}
369+
358370
fn test_shuffle_clone() {
359371
original := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
360372
mut a := original.clone()
361373
mut results := [][]int{}
362374
for _ in 0 .. 10 {
363-
results << rand.shuffle_clone(a)
375+
results << rand.shuffle_clone(a) or { panic('shuffle failed') }
364376
}
365377
assert original == a
366378
for idx in 1 .. 10 {
@@ -369,3 +381,33 @@ fn test_shuffle_clone() {
369381
assert results[idx] != original
370382
}
371383
}
384+
385+
fn test_choose() ? {
386+
lengths := [1, 3, 4, 5, 6, 7]
387+
a := ['one', 'two', 'three', 'four', 'five', 'six', 'seven']
388+
for length in lengths {
389+
b := rand.choose(a, length) ?
390+
assert b.len == length
391+
for element in b {
392+
assert element in a
393+
// make sure every element occurs once
394+
mut count := 0
395+
for e in b {
396+
if e == element {
397+
count++
398+
}
399+
}
400+
assert count == 1
401+
}
402+
}
403+
}
404+
405+
fn test_sample() {
406+
k := 20
407+
a := ['heads', 'tails']
408+
b := rand.sample(a, k)
409+
assert b.len == k
410+
for element in b {
411+
assert element in a
412+
}
413+
}

vlib/rand/util/util.v

Lines changed: 0 additions & 52 deletions
This file was deleted.

vlib/rand/util/util_test.v

Lines changed: 0 additions & 57 deletions
This file was deleted.

vlib/v/tests/generics_with_nested_external_generics_fn_test.v

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
import rand.util
21
import rand
32

4-
pub fn sample<T>(arr []T, k int) []T {
3+
pub fn sample<T>(arr []T, k int) ?[]T {
54
mut result := arr.clone()
65

76
rand.seed([u32(1), 2]) // set seed to produce same results in order
8-
util.shuffle<T>(mut result, k)
7+
rand.shuffle<T>(mut result) ?
98

109
return result[0..k]
1110
}
1211

13-
fn test_generics_with_nested_external_generics_fn() {
12+
fn test_generics_with_nested_external_generics_fn() ? {
1413
mut arr := [11, 32, 24, 45, 57, 32, 37, 52, 37, 24]
1514
println(arr)
1615

17-
ret := sample<int>(arr, 5)
16+
ret := sample<int>(arr, 5) ?
1817
println(ret)
1918

2019
assert ret == [32, 45, 57, 11, 37]

0 commit comments

Comments
 (0)