mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 08:54:53 +00:00
312 lines
8.1 KiB
Nim
312 lines
8.1 KiB
Nim
discard """
|
|
joinable: false # to avoid messing with global rand state
|
|
matrix: "--mm:refc; --mm:orc; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on"
|
|
"""
|
|
import std/[assertions, formatfloat]
|
|
import std/[random, math, stats, sets, tables]
|
|
import std/private/jsutils
|
|
when not defined(js):
|
|
import std/os
|
|
|
|
randomize(233)
|
|
|
|
proc main() =
|
|
var occur: array[1000, int] = default(array[1000, int])
|
|
|
|
for i in 0..100_000:
|
|
let x = rand(high(occur))
|
|
inc occur[x]
|
|
|
|
doAssert max(occur) <= 140 and min(occur) >= 60 # gives some slack
|
|
|
|
var a = [0, 1]
|
|
shuffle(a)
|
|
doAssert a in [[0,1], [1,0]]
|
|
|
|
doAssert rand(0) == 0
|
|
when not defined(nimscript):
|
|
doAssert sample("a") == 'a'
|
|
|
|
when compileOption("rangeChecks") and not defined(nimscript):
|
|
doAssertRaises(RangeDefect):
|
|
discard rand(-1)
|
|
|
|
doAssertRaises(RangeDefect):
|
|
discard rand(-1.0)
|
|
|
|
# don't use causes integer overflow
|
|
doAssert compiles(rand[int](low(int) .. high(int)))
|
|
|
|
main()
|
|
|
|
block:
|
|
when not defined(js):
|
|
doAssert almostEqual(rand(12.5), 7.355175342026979)
|
|
doAssert almostEqual(rand(2233.3322), 499.342386778917)
|
|
|
|
type DiceRoll = range[0..6]
|
|
when not defined(js):
|
|
doAssert rand(DiceRoll).int == 3
|
|
elif compileOption("jsbigint64"):
|
|
doAssert rand(DiceRoll).int == 1
|
|
else:
|
|
doAssert rand(DiceRoll).int == 6
|
|
|
|
var rs: RunningStat
|
|
for j in 1..5:
|
|
for i in 1 .. 100_000:
|
|
rs.push(gauss())
|
|
doAssert abs(rs.mean-0) < 0.08, $rs.mean
|
|
doAssert abs(rs.standardDeviation()-1.0) < 0.1
|
|
let bounds = [3.5, 5.0]
|
|
for a in [rs.max, -rs.min]:
|
|
doAssert a >= bounds[0] and a <= bounds[1]
|
|
rs.clear()
|
|
|
|
block:
|
|
type DiceRoll = range[3..6]
|
|
var flag = false
|
|
for i in 0..<100:
|
|
if rand(5.DiceRoll) < 3:
|
|
flag = true
|
|
doAssert flag # because of: rand(max: int): int
|
|
|
|
|
|
block: # random int
|
|
block: # there might be some randomness
|
|
var set = initHashSet[int](128)
|
|
|
|
for i in 1..1000:
|
|
incl(set, rand(high(int)))
|
|
doAssert len(set) == 1000
|
|
|
|
block: # single number bounds work
|
|
var rand: int
|
|
for i in 1..1000:
|
|
rand = rand(1000)
|
|
doAssert rand <= 1000
|
|
doAssert rand >= 0
|
|
|
|
block: # slice bounds work
|
|
var rand: int
|
|
for i in 1..1000:
|
|
rand = rand(100..1000)
|
|
doAssert rand <= 1000
|
|
doAssert rand >= 100
|
|
|
|
block: # again gives new numbers
|
|
var rand1 = rand(1000000)
|
|
when not (defined(js) or defined(nimscript)):
|
|
os.sleep(200)
|
|
|
|
var rand2 = rand(1000000)
|
|
doAssert rand1 != rand2
|
|
|
|
block: # random float
|
|
block: # there might be some randomness
|
|
var set = initHashSet[float](128)
|
|
|
|
for i in 1..100:
|
|
incl(set, rand(1.0))
|
|
doAssert len(set) == 100
|
|
|
|
block: # single number bounds work
|
|
var rand: float
|
|
for i in 1..1000:
|
|
rand = rand(1000.0)
|
|
doAssert rand <= 1000.0
|
|
doAssert rand >= 0.0
|
|
|
|
block: # slice bounds work
|
|
var rand: float
|
|
for i in 1..1000:
|
|
rand = rand(100.0..1000.0)
|
|
doAssert rand <= 1000.0
|
|
doAssert rand >= 100.0
|
|
|
|
block: # again gives new numbers
|
|
var rand1: float = rand(1000000.0)
|
|
when not (defined(js) or defined(nimscript)):
|
|
os.sleep(200)
|
|
|
|
var rand2: float = rand(1000000.0)
|
|
doAssert rand1 != rand2
|
|
|
|
block: # random sample
|
|
block: # "non-uniform array sample unnormalized int CDF
|
|
let values = [10, 20, 30, 40, 50] # values
|
|
let counts = [4, 3, 2, 1, 0] # weights aka unnormalized probabilities
|
|
var histo = initCountTable[int]()
|
|
let cdf = counts.cumsummed # unnormalized CDF
|
|
for i in 0 ..< 5000:
|
|
histo.inc(sample(values, cdf))
|
|
doAssert histo.len == 4 # number of non-zero in `counts`
|
|
# Any one bin is a binomial random var for n samples, each with prob p of
|
|
# adding a count to k; E[k]=p*n, Var k=p*(1-p)*n, approximately Normal for
|
|
# big n. So, P(abs(k - p*n)/sqrt(p*(1-p)*n))>3.0) =~ 0.0027, while
|
|
# P(wholeTestFails) =~ 1 - P(binPasses)^4 =~ 1 - (1-0.0027)^4 =~ 0.01.
|
|
for i, c in counts:
|
|
if c == 0:
|
|
doAssert values[i] notin histo
|
|
continue
|
|
let p = float(c) / float(cdf[^1])
|
|
let n = 5000.0
|
|
let expected = p * n
|
|
let stdDev = sqrt(n * p * (1.0 - p))
|
|
doAssert abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev
|
|
|
|
block: # non-uniform array sample normalized float CDF
|
|
let values = [10, 20, 30, 40, 50] # values
|
|
let counts = [0.4, 0.3, 0.2, 0.1, 0] # probabilities
|
|
var histo = initCountTable[int]()
|
|
let cdf = counts.cumsummed # normalized CDF
|
|
for i in 0 ..< 5000:
|
|
histo.inc(sample(values, cdf))
|
|
doAssert histo.len == 4 # number of non-zero in ``counts``
|
|
for i, c in counts:
|
|
if c == 0:
|
|
doAssert values[i] notin histo
|
|
continue
|
|
let p = float(c) / float(cdf[^1])
|
|
let n = 5000.0
|
|
let expected = p * n
|
|
let stdDev = sqrt(n * p * (1.0 - p))
|
|
# NOTE: like unnormalized int CDF test, P(wholeTestFails) =~ 0.01.
|
|
doAssert abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev
|
|
|
|
block:
|
|
# 0 is a valid seed
|
|
var r = initRand(0)
|
|
doAssert r.rand(1.0) != r.rand(1.0)
|
|
r = initRand(10)
|
|
doAssert r.rand(1.0) != r.rand(1.0)
|
|
# changing the seed changes the sequence
|
|
var r1 = initRand(123)
|
|
var r2 = initRand(124)
|
|
doAssert r1.rand(1.0) != r2.rand(1.0)
|
|
|
|
block: # bug #17467
|
|
let n = 1000
|
|
for i in -n .. n:
|
|
var r = initRand(i)
|
|
let x = r.rand(1.0)
|
|
doAssert x > 1e-4, $(x, i)
|
|
# This used to fail for each i in 0..<26844, i.e. the 1st produced value
|
|
# was predictable and < 1e-4, skewing distributions.
|
|
|
|
block: # bug #16360, Natural overload
|
|
var r = initRand()
|
|
template test(a) =
|
|
let a2 = a
|
|
block:
|
|
let a3 = r.rand(a2)
|
|
doAssert a3 <= a2
|
|
doAssert a3.type is a2.type
|
|
block:
|
|
let a3 = rand(a2)
|
|
doAssert a3 <= a2
|
|
doAssert a3.type is a2.type
|
|
test int.high
|
|
test int.high - 1
|
|
test int.high - 2
|
|
test 0
|
|
|
|
block: # same as above but use slice overload
|
|
var r = initRand()
|
|
template test[T](a: T) =
|
|
let a2: T = a
|
|
block:
|
|
let a3 = r.rand(T(0) .. a2)
|
|
doAssert a3 <= a2
|
|
doAssert a3.type is a2.type
|
|
block:
|
|
let a3 = rand(T(0) .. a2)
|
|
doAssert a3 <= a2
|
|
doAssert a3.type is a2.type
|
|
test cast[uint](int.high)
|
|
test cast[uint](int.high) + 1
|
|
when hasWorkingInt64 and defined(js):
|
|
# weirdly this has to run only in JS for the final int32.high test
|
|
# to be the same between C/C++ and --jsbigint64:on
|
|
test uint64.high
|
|
test uint64.high - 1
|
|
test uint.high - 2
|
|
test uint.high - 1
|
|
test uint.high
|
|
test int.high
|
|
test int.high - 1
|
|
test int.high - 2
|
|
test 0
|
|
test 0'u
|
|
test 0'u64
|
|
|
|
block: # bug #16296
|
|
var r = initRand()
|
|
template test(x) =
|
|
let a2 = x
|
|
let a3 = r.rand(a2)
|
|
doAssert a3 <= a2.b
|
|
doAssert a3 >= a2.a
|
|
doAssert a3.type is a2.a.type
|
|
test(-2 .. int.high-1)
|
|
test(int.low .. int.high)
|
|
test(int.low+1 .. int.high)
|
|
test(int.low .. int.high-1)
|
|
test(int.low .. 0)
|
|
test(int.low .. -1)
|
|
test(int.low .. 1)
|
|
test(int64.low .. 1'i64)
|
|
test(10'u64 .. uint64.high)
|
|
|
|
block: # bug #17670
|
|
type UInt48 = range[0'u64..2'u64^48-1]
|
|
let x = rand(UInt48)
|
|
doAssert x is UInt48
|
|
|
|
block: # bug #17898
|
|
# Checks whether `initRand()` generates unique states.
|
|
# size should be 2^64, but we don't have time and space.
|
|
|
|
# Disable this test for js until js gets proper skipRandomNumbers.
|
|
when not defined(js):
|
|
const size = 1000
|
|
var
|
|
rands: array[size, Rand]
|
|
randSet: HashSet[Rand]
|
|
for i in 0..<size:
|
|
rands[i] = initRand()
|
|
randSet.incl rands[i]
|
|
|
|
doAssert randSet.len == size
|
|
|
|
# Checks random number sequences overlapping.
|
|
const numRepeat = 100
|
|
for i in 0..<size:
|
|
for j in 0..<numRepeat:
|
|
discard rands[i].next
|
|
doAssert rands[i] notin randSet
|
|
|
|
block: # bug #22360
|
|
const size = 1000
|
|
var fc = 0
|
|
var tc = 0
|
|
|
|
for _ in 1..size:
|
|
let s = rand(bool)
|
|
|
|
if s:
|
|
inc tc
|
|
else:
|
|
inc fc
|
|
|
|
when defined(js) and not compileOption("jsbigint64"):
|
|
doAssert (tc, fc) == (515, 485), $(tc, fc)
|
|
else:
|
|
doAssert (tc, fc) == (510, 490), $(tc, fc)
|
|
|
|
block:
|
|
when defined(js) and not compileOption("jsbigint64"):
|
|
doAssert rand(int32.high) == 335507522
|
|
else:
|
|
doAssert rand(int32.high) == 607539621
|