diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7abc4e0..b619776 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - name: Build example run: | cd ./vlib/random/ - v test random_test.v + v test tests/rand_test.v macos: runs-on: macos-latest @@ -41,7 +41,7 @@ jobs: - name: Build example run: | cd ./vlib/random/ - v test random_test.v + v test tests/rand_test.v windows-msvc: runs-on: windows-latest @@ -57,7 +57,7 @@ jobs: with: path: vlib/random - name: Build V - run: .\make.bat -msvc + run: .\make.bat -msvc -skip-path # Don't move applying V directory to PATH, to other steps - name: Build example - run: .\v.exe test .\vlib\random\random_test.v + run: .\v.exe test .\vlib\random\tests\rand_test.v diff --git a/README.md b/README.md index bd774dd..9d0e8d2 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,10 @@ fn bool() bool // shuffle returns the new shuffled array fn shuffle(arr []T) []T +// sample returns the new k-sized array +// no_repetitions must be true when no repetitions are needed +// else it must be false +fn sample(arr []T, k int) []T // choose returns a random element from the array fn choose(arr []T) T diff --git a/examples/rand.v b/examples/rand.v index 11353ba..4fd8e8a 100644 --- a/examples/rand.v +++ b/examples/rand.v @@ -9,7 +9,7 @@ fn main() { println(random.bool()) println(random.choose([2, 4, 6, 8, 9, 7])) println(random.shuffle([2, 4, 6, 8, 9, 7])) - println(random.sample([2, 4, 6, 7, 12, 8], 4)) + println(random.sample([2, 4, 6, 7, 12, 8, 4 , 7, 5], 6, false)) println(random.numeric(5)) println(random.int_range(start: 5, stop: 15, step: 2)) println(random.float_range(start: 5.3, stop: 15.4, step: 2.0)) diff --git a/rand.v b/rand.v index f620e4b..39056a7 100644 --- a/rand.v +++ b/rand.v @@ -5,129 +5,153 @@ import math // struct for `int_range()` pub struct IntRange { - start int - stop int - step int = 1 + start int + stop int + step int = 1 } // struct for `float_range()` pub struct FloatRange { - start f32 - stop f32 - step f32 = 1.0 + start f32 + stop f32 + step f32 = 1.0 } // uniform returns a random number between the range [a, b) or [a, b] depending on rounding pub fn uniform(a, b f32) f32 { - return a + (b - a) * rand.f32() + return a + (b - a) * rand.f32() } // int_range returns a random int between the specified range pub fn int_range(range IntRange) int { - if range.stop == 0 { - if range.start > 1 { - return rand.intn(range.start) - } - eprintln('random: empty range for int_range()') - exit(1) - } - width := range.stop - range.start - mut n := 0 - if range.step == 1 { - if width > 0 { - return range.start + rand.intn(width) - } - eprintln('random: empty range for int_range($range.start, $range.stop, $width)') - exit(1) - } - if range.step > 0 { - n = (width + range.step - 1) / range.step - } else if range.step < 0 { - n = (width + range.step + 1) / range.step - } else { - eprintln('random: empty range provided') - exit(1) - } - return range.start + range.step * rand.intn(n) + if range.stop == 0 { + if range.start > 1 { + return rand.intn(range.start) + } + eprintln('random: empty range for int_range()') + exit(1) + } + width := range.stop - range.start + mut n := 0 + if range.step == 1 { + if width > 0 { + return range.start + rand.intn(width) + } + eprintln('random: empty range for int_range($range.start, $range.stop, $width)') + exit(1) + } + if range.step > 0 { + n = (width + range.step - 1) / range.step + } else if range.step < 0 { + n = (width + range.step + 1) / range.step + } else { + eprintln('random.int_range: empty range provided') + exit(1) + } + return range.start + range.step * rand.intn(n) } // float_range returns a random float upon the given range pub fn float_range(range FloatRange) f32 { - if range.stop == 0 { - if range.start >= 1 { - return rand.f32n(range.start) - } - eprintln('random: empty range for float_range()') - exit(1) - } - width := range.stop - range.start - mut n := f32(0) - if range.step == 1 { - if width > 0 { - return range.start + rand.f32n(width) - } - eprintln('random: empty range for float_range($range.start, $range.stop, $width)') - exit(1) - } - if range.step > 0 { - n = (width + range.step - 1) / range.step - } else if range.step < 0 { - n = (width + range.step + 1) / range.step - } else { - eprintln('random: empty range provided') - exit(1) - } - return range.start + range.step * rand.f32n(n) + if range.stop == 0 { + if range.start >= 1 { + return rand.f32n(range.start) + } + eprintln('random: empty range for float_range()') + exit(1) + } + width := range.stop - range.start + mut n := f32(0) + if range.step == 1 { + if width > 0 { + return range.start + rand.f32n(width) + } + eprintln('random: empty range for float_range($range.start, $range.stop, $width)') + exit(1) + } + if range.step > 0 { + n = (width + range.step - 1) / range.step + } else if range.step < 0 { + n = (width + range.step + 1) / range.step + } else { + eprintln('random.float_range: empty range provided') + exit(1) + } + mut result := range.start + range.step * rand.f32n(n) + // this happens in worst cases + if result > range.stop && range.step > 0 { + for result > range.stop { + result = range.start + range.step * rand.f32n(n) + } + } + return result } // numeric returns a number with n digits long pub fn numeric(n int) int { - if n <= 0 { - eprintln('numeric: number must be greater than one') - exit(1) - } - return rand.int() % int(math.pow(10, n)) + if n <= 0 { + eprintln('random.numeric: number must be greater than one') + exit(1) + } + return rand.int() % int(math.pow(10, n)) } // bool returns a random bool pub fn bool() bool { - b := [true, false] - return b[rand.intn(b.len)] + b := [true, false] + return b[rand.intn(b.len)] } -// shuffle returns the new shuffled array. If more_randomize is true -// then more random array will be produced which slowers the function. -pub fn shuffle(arr []T, more_randomize bool) []T { - mut clone := arr.clone() - for i in range(0, arr.len).reverse() { - j := rand.intn(i + 1) - temp := clone[j] - clone[j] = clone[i] - clone[i] = temp - if more_randomize { - rand.seed([u32(j), 0]) - } - } - return clone +// shuffle returns the new shuffled array. +pub fn shuffle(arr []T) []T { + mut clone := arr.clone() + for i in range(0, arr.len).reverse() { + j := rand.intn(i + 1) + temp := clone[j] + clone[j] = clone[i] + clone[i] = temp + } + return clone } -pub fn sample(arr []T, k int, more_randomize bool) []T { - mut a := []T{} - if k <= 0 { - eprintln('random.sample: number should be greater than 0') - exit(1) - } - for i in range(0, k) { - j := rand.intn(arr.len) - a << arr[j] - if more_randomize { - rand.seed([u32(arr.len), 0]) - } - } - return a +// sample returns the new k-sized array +// no_repetitions must be true when no repetitions are needed +// else it must be false +pub fn sample(arr []T, k int, no_repetitions bool) []T { + mut sub_array := []T{} + if k <= 0 { + eprintln('random.sample: number should be greater than 0') + exit(1) + } + if k == arr.len { + eprintln('random.sample: k and length of array must not be equal') + exit(1) + } + for _ in range(0, k) { + j := rand.intn(arr.len) + if no_repetitions { + if arr[j] !in sub_array { + sub_array << arr[j] + } + } else { + sub_array << arr[j] + } + } + // this can happen in the worst cases + if sub_array.len != k { + for sub_array.len != k { + len := rand.intn(arr.len) + if no_repetitions { + if arr[len] !in sub_array { + sub_array << arr[len] + } + } + } + } + return sub_array } // choose returns a random element from the array pub fn choose(arr []T) T { - return arr[rand.intn(arr.len)] + return arr[rand.intn(arr.len)] } diff --git a/tests/rand_test.v b/tests/rand_test.v new file mode 100644 index 0000000..afaf2fe --- /dev/null +++ b/tests/rand_test.v @@ -0,0 +1,81 @@ +import random +import time +import rand + +fn init() { + rand.seed([u32(time.now().unix), 0]) +} + +fn test_bool() { + for i := 0; i < 10; i++ { + b := random.bool() + assert b == true || b == false + } +} + +fn test_numeric() { + for i := 0; i < 10; i++ { + b := random.numeric(5) + //println(b.str().len) + assert b.str().len == 5 || b.str().len == 6 || b.str().len == 4 + } +} + +fn test_int_range() { + for i := 0; i < 10; i++ { + b := random.int_range(start: 5, stop: 15, step: 2) + assert b >= 5 + assert b < 15 + } + + for i := 0; i < 10; i++ { + b := random.int_range(start: 15, stop: 1, step:-1) + assert b <= 15 + assert b > 1 + } +} + +fn test_float_range() { + for i := 0; i < 10; i++ { + b := random.float_range(start: 5.3, stop: 15.4, step: 2.0) + assert b >= f32(5.3) + assert b < f32(15.4) + } + + for i := 0; i < 10; i++ { + b := random.float_range(start: 15.0, stop: 5.3, step: -1) + assert b <= f32(15.0) + assert b > f32(5.3) + } +} + +fn test_choose() { + for i := 0; i < 10; i++ { + arr_int := [2, 5, 7, 8, 9, 10, 13] + arr_u64 := [u64(2), 37, 56, 79, 10] + + assert random.choose(arr_int) in arr_int + assert random.choose(arr_u64) in arr_u64 + } +} + +fn test_shuffle() { + for i := 0; i < 10; i++ { + arr_int := [2, 5, 7, 8, 9, 10, 13] + arr_u64 := [u64(2), 37, 56, 79, 10] + + assert random.shuffle(arr_int).len == arr_int.len + assert random.shuffle(arr_u64).len == arr_u64.len + } +} +/* +fn test_sample() { + for i := 0; i < 10; i++ { + arr_int := [2, 5, 7, 8, 9, 10, 13] + arr_u64 := [u64(2), 37, 56, 79, 10] + + assert random.sample(arr_int, 5, false).len == 5 + assert random.sample(arr_u64, 6, true).len == 6 + } +} +*/ diff --git a/v.mod b/v.mod index b37195f..4c849a9 100644 --- a/v.mod +++ b/v.mod @@ -1,7 +1,7 @@ Module { name: 'range' author: 'Swastik Baranwal' - version: '0.0.1' + version: '0.0.2' repo_url: 'https://github.com/Delta456/random' vcs: 'git' tags: ['random', 'python', 'vlang', 'variate']