Improve math module (#17019)
* Improve documentation for math Support empty input for cumsummed Use runnableExamples Move some examples to tests Add more tests * Update tests/stdlib/tmath.nim Move some tests to trandom.nim Move tests into main template where possible Add test for #17017 * Add more tests for gamma & lgamma Remove gamma(-1.0) example Small fixes/changes * Move more tests into template main() * Fix typos * Add edge case examples for copySign
This commit is contained in:
parent
e40ff24c23
commit
afa87f223c
3 changed files with 765 additions and 789 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -6,207 +6,31 @@ discard """
|
||||||
# xxx: there should be a test with `-d:nimTmathCase2 -d:danger --passc:-ffast-math`,
|
# xxx: there should be a test with `-d:nimTmathCase2 -d:danger --passc:-ffast-math`,
|
||||||
# but it requires disabling certain lines with `when not defined(nimTmathCase2)`
|
# but it requires disabling certain lines with `when not defined(nimTmathCase2)`
|
||||||
|
|
||||||
import std/[math, random, os]
|
import std/math
|
||||||
import std/[unittest]
|
|
||||||
import std/[sets, tables]
|
|
||||||
|
|
||||||
|
|
||||||
# Function for approximate comparison of floats
|
# Function for approximate comparison of floats
|
||||||
proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9)
|
proc `==~`(x, y: float): bool = abs(x - y) < 1e-9
|
||||||
|
|
||||||
block: # random int
|
|
||||||
block: # there might be some randomness
|
|
||||||
var set = initHashSet[int](128)
|
|
||||||
|
|
||||||
for i in 1..1000:
|
|
||||||
incl(set, rand(high(int)))
|
|
||||||
check len(set) == 1000
|
|
||||||
|
|
||||||
block: # single number bounds work
|
|
||||||
var rand: int
|
|
||||||
for i in 1..1000:
|
|
||||||
rand = rand(1000)
|
|
||||||
check rand < 1000
|
|
||||||
check rand > -1
|
|
||||||
|
|
||||||
block: # slice bounds work
|
|
||||||
var rand: int
|
|
||||||
for i in 1..1000:
|
|
||||||
rand = rand(100..1000)
|
|
||||||
when defined(js): # xxx bug: otherwise fails
|
|
||||||
check rand <= 1000
|
|
||||||
else:
|
|
||||||
check rand < 1000
|
|
||||||
check rand >= 100
|
|
||||||
|
|
||||||
block: # again gives new numbers
|
|
||||||
var rand1 = rand(1000000)
|
|
||||||
when not defined(js):
|
|
||||||
os.sleep(200)
|
|
||||||
|
|
||||||
var rand2 = rand(1000000)
|
|
||||||
check 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))
|
|
||||||
check len(set) == 100
|
|
||||||
|
|
||||||
block: # single number bounds work
|
|
||||||
var rand: float
|
|
||||||
for i in 1..1000:
|
|
||||||
rand = rand(1000.0)
|
|
||||||
check rand < 1000.0
|
|
||||||
check rand > -1.0
|
|
||||||
|
|
||||||
block: # slice bounds work
|
|
||||||
var rand: float
|
|
||||||
for i in 1..1000:
|
|
||||||
rand = rand(100.0..1000.0)
|
|
||||||
check rand < 1000.0
|
|
||||||
check rand >= 100.0
|
|
||||||
|
|
||||||
block: # again gives new numbers
|
|
||||||
|
|
||||||
var rand1:float = rand(1000000.0)
|
|
||||||
when not defined(js):
|
|
||||||
os.sleep(200)
|
|
||||||
|
|
||||||
var rand2:float = rand(1000000.0)
|
|
||||||
check rand1 != rand2
|
|
||||||
|
|
||||||
block: # cumsum
|
|
||||||
block: # cumsum int seq return
|
|
||||||
let counts = [ 1, 2, 3, 4 ]
|
|
||||||
check counts.cumsummed == [ 1, 3, 6, 10 ]
|
|
||||||
|
|
||||||
block: # cumsum float seq return
|
|
||||||
let counts = [ 1.0, 2.0, 3.0, 4.0 ]
|
|
||||||
check counts.cumsummed == [ 1.0, 3.0, 6.0, 10.0 ]
|
|
||||||
|
|
||||||
block: # cumsum int in-place
|
|
||||||
var counts = [ 1, 2, 3, 4 ]
|
|
||||||
counts.cumsum
|
|
||||||
check counts == [ 1, 3, 6, 10 ]
|
|
||||||
|
|
||||||
block: # cumsum float in-place
|
|
||||||
var counts = [ 1.0, 2.0, 3.0, 4.0 ]
|
|
||||||
counts.cumsum
|
|
||||||
check counts == [ 1.0, 3.0, 6.0, 10.0 ]
|
|
||||||
|
|
||||||
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))
|
|
||||||
check 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:
|
|
||||||
check 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))
|
|
||||||
check 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))
|
|
||||||
check histo.len == 4 # number of non-zero in ``counts``
|
|
||||||
for i, c in counts:
|
|
||||||
if c == 0:
|
|
||||||
check 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.
|
|
||||||
check abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev
|
|
||||||
|
|
||||||
block: # ^
|
|
||||||
block: # compiles for valid types
|
|
||||||
check: compiles(5 ^ 2)
|
|
||||||
check: compiles(5.5 ^ 2)
|
|
||||||
check: compiles(5.5 ^ 2.int8)
|
|
||||||
check: compiles(5.5 ^ 2.uint)
|
|
||||||
check: compiles(5.5 ^ 2.uint8)
|
|
||||||
check: not compiles(5.5 ^ 2.2)
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
when not defined(js):
|
when not defined(js):
|
||||||
# Check for no side effect annotation
|
# check for no side effect annotation
|
||||||
proc mySqrt(num: float): float {.noSideEffect.} =
|
proc mySqrt(num: float): float {.noSideEffect.} =
|
||||||
# xxx unused
|
# xxx unused
|
||||||
return sqrt(num)
|
sqrt(num)
|
||||||
|
|
||||||
# check gamma function
|
# check gamma function
|
||||||
doAssert(gamma(5.0) == 24.0) # 4!
|
doAssert gamma(5.0) == 24.0 # 4!
|
||||||
doAssert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0
|
doAssert almostEqual(gamma(0.5), sqrt(PI))
|
||||||
doAssert(erf(6.0) > erf(5.0))
|
doAssert almostEqual(gamma(-0.5), -2 * sqrt(PI))
|
||||||
doAssert(erfc(6.0) < erfc(5.0))
|
doAssert lgamma(1.0) == 0.0 # ln(1.0) == 0.0
|
||||||
|
doAssert almostEqual(lgamma(0.5), 0.5 * ln(PI))
|
||||||
|
doAssert erf(6.0) > erf(5.0)
|
||||||
|
doAssert erfc(6.0) < erfc(5.0)
|
||||||
|
|
||||||
block: # prod
|
when not defined(js) and not defined(windows): # xxx pending bug #17017
|
||||||
doAssert prod([1, 2, 3, 4]) == 24
|
doAssert gamma(-1.0).isNaN
|
||||||
doAssert prod([1.5, 3.4]).almostEqual 5.1
|
|
||||||
let x: seq[float] = @[]
|
|
||||||
doAssert prod(x) == 1.0
|
|
||||||
|
|
||||||
block: # splitDecimal() tests
|
|
||||||
doAssert splitDecimal(54.674).intpart == 54.0
|
|
||||||
doAssert splitDecimal(54.674).floatpart ==~ 0.674
|
|
||||||
doAssert splitDecimal(-693.4356).intpart == -693.0
|
|
||||||
doAssert splitDecimal(-693.4356).floatpart ==~ -0.4356
|
|
||||||
doAssert splitDecimal(0.0).intpart == 0.0
|
|
||||||
doAssert splitDecimal(0.0).floatpart == 0.0
|
|
||||||
|
|
||||||
block: # trunc tests for vcc
|
|
||||||
doAssert(trunc(-1.1) == -1)
|
|
||||||
doAssert(trunc(1.1) == 1)
|
|
||||||
doAssert(trunc(-0.1) == -0)
|
|
||||||
doAssert(trunc(0.1) == 0)
|
|
||||||
|
|
||||||
#special case
|
|
||||||
doAssert(classify(trunc(1e1000000)) == fcInf)
|
|
||||||
doAssert(classify(trunc(-1e1000000)) == fcNegInf)
|
|
||||||
when not defined(nimTmathCase2):
|
|
||||||
doAssert(classify(trunc(0.0/0.0)) == fcNan)
|
|
||||||
doAssert(classify(trunc(0.0)) == fcZero)
|
|
||||||
|
|
||||||
#trick the compiler to produce signed zero
|
|
||||||
let
|
|
||||||
f_neg_one = -1.0
|
|
||||||
f_zero = 0.0
|
|
||||||
f_nan = f_zero / f_zero
|
|
||||||
|
|
||||||
doAssert(classify(trunc(f_neg_one*f_zero)) == fcNegZero)
|
|
||||||
|
|
||||||
doAssert(trunc(-1.1'f32) == -1)
|
|
||||||
doAssert(trunc(1.1'f32) == 1)
|
|
||||||
doAssert(trunc(-0.1'f32) == -0)
|
|
||||||
doAssert(trunc(0.1'f32) == 0)
|
|
||||||
doAssert(classify(trunc(1e1000000'f32)) == fcInf)
|
|
||||||
doAssert(classify(trunc(-1e1000000'f32)) == fcNegInf)
|
|
||||||
when not defined(nimTmathCase2):
|
|
||||||
doAssert(classify(trunc(f_nan.float32)) == fcNan)
|
|
||||||
doAssert(classify(trunc(0.0'f32)) == fcZero)
|
|
||||||
|
|
||||||
|
template main() =
|
||||||
block: # sgn() tests
|
block: # sgn() tests
|
||||||
doAssert sgn(1'i8) == 1
|
doAssert sgn(1'i8) == 1
|
||||||
doAssert sgn(1'i16) == 1
|
doAssert sgn(1'i16) == 1
|
||||||
|
|
@ -225,6 +49,8 @@ block:
|
||||||
doAssert sgn(NaN) == 0
|
doAssert sgn(NaN) == 0
|
||||||
|
|
||||||
block: # fac() tests
|
block: # fac() tests
|
||||||
|
when nimvm: discard
|
||||||
|
else:
|
||||||
try:
|
try:
|
||||||
discard fac(-1)
|
discard fac(-1)
|
||||||
except AssertionDefect:
|
except AssertionDefect:
|
||||||
|
|
@ -235,6 +61,7 @@ block:
|
||||||
doAssert fac(2) == 2
|
doAssert fac(2) == 2
|
||||||
doAssert fac(3) == 6
|
doAssert fac(3) == 6
|
||||||
doAssert fac(4) == 24
|
doAssert fac(4) == 24
|
||||||
|
doAssert fac(5) == 120
|
||||||
|
|
||||||
block: # floorMod/floorDiv
|
block: # floorMod/floorDiv
|
||||||
doAssert floorDiv(8, 3) == 2
|
doAssert floorDiv(8, 3) == 2
|
||||||
|
|
@ -280,6 +107,45 @@ block:
|
||||||
doAssert euclDiv(-9, -3) == 3
|
doAssert euclDiv(-9, -3) == 3
|
||||||
doAssert euclMod(-9, -3) == 0
|
doAssert euclMod(-9, -3) == 0
|
||||||
|
|
||||||
|
block: # splitDecimal() tests
|
||||||
|
doAssert splitDecimal(54.674).intpart == 54.0
|
||||||
|
doAssert splitDecimal(54.674).floatpart ==~ 0.674
|
||||||
|
doAssert splitDecimal(-693.4356).intpart == -693.0
|
||||||
|
doAssert splitDecimal(-693.4356).floatpart ==~ -0.4356
|
||||||
|
doAssert splitDecimal(0.0).intpart == 0.0
|
||||||
|
doAssert splitDecimal(0.0).floatpart == 0.0
|
||||||
|
|
||||||
|
block: # trunc tests for vcc
|
||||||
|
doAssert trunc(-1.1) == -1
|
||||||
|
doAssert trunc(1.1) == 1
|
||||||
|
doAssert trunc(-0.1) == -0
|
||||||
|
doAssert trunc(0.1) == 0
|
||||||
|
|
||||||
|
# special case
|
||||||
|
doAssert classify(trunc(1e1000000)) == fcInf
|
||||||
|
doAssert classify(trunc(-1e1000000)) == fcNegInf
|
||||||
|
when not defined(nimTmathCase2):
|
||||||
|
doAssert classify(trunc(0.0/0.0)) == fcNan
|
||||||
|
doAssert classify(trunc(0.0)) == fcZero
|
||||||
|
|
||||||
|
# trick the compiler to produce signed zero
|
||||||
|
let
|
||||||
|
f_neg_one = -1.0
|
||||||
|
f_zero = 0.0
|
||||||
|
f_nan = f_zero / f_zero
|
||||||
|
|
||||||
|
doAssert classify(trunc(f_neg_one*f_zero)) == fcNegZero
|
||||||
|
|
||||||
|
doAssert trunc(-1.1'f32) == -1
|
||||||
|
doAssert trunc(1.1'f32) == 1
|
||||||
|
doAssert trunc(-0.1'f32) == -0
|
||||||
|
doAssert trunc(0.1'f32) == 0
|
||||||
|
doAssert classify(trunc(1e1000000'f32)) == fcInf
|
||||||
|
doAssert classify(trunc(-1e1000000'f32)) == fcNegInf
|
||||||
|
when not defined(nimTmathCase2):
|
||||||
|
doAssert classify(trunc(f_nan.float32)) == fcNan
|
||||||
|
doAssert classify(trunc(0.0'f32)) == fcZero
|
||||||
|
|
||||||
block: # log
|
block: # log
|
||||||
doAssert log(4.0, 3.0) ==~ ln(4.0) / ln(3.0)
|
doAssert log(4.0, 3.0) ==~ ln(4.0) / ln(3.0)
|
||||||
doAssert log2(8.0'f64) == 3.0'f64
|
doAssert log2(8.0'f64) == 3.0'f64
|
||||||
|
|
@ -294,8 +160,43 @@ block:
|
||||||
doAssert log2(1.0'f32) == 0.0'f32
|
doAssert log2(1.0'f32) == 0.0'f32
|
||||||
doAssert classify(log2(0.0'f32)) == fcNegInf
|
doAssert classify(log2(0.0'f32)) == fcNegInf
|
||||||
|
|
||||||
template main =
|
block: # cumsum
|
||||||
# xxx wrap all under `main` so it also gets tested in vm.
|
block: # cumsum int seq return
|
||||||
|
let counts = [1, 2, 3, 4]
|
||||||
|
doAssert counts.cumsummed == @[1, 3, 6, 10]
|
||||||
|
let empty: seq[int] = @[]
|
||||||
|
doAssert empty.cumsummed == @[]
|
||||||
|
|
||||||
|
block: # cumsum float seq return
|
||||||
|
let counts = [1.0, 2.0, 3.0, 4.0]
|
||||||
|
doAssert counts.cumsummed == @[1.0, 3.0, 6.0, 10.0]
|
||||||
|
let empty: seq[float] = @[]
|
||||||
|
doAssert empty.cumsummed == @[]
|
||||||
|
|
||||||
|
block: # cumsum int in-place
|
||||||
|
var counts = [1, 2, 3, 4]
|
||||||
|
counts.cumsum
|
||||||
|
doAssert counts == [1, 3, 6, 10]
|
||||||
|
var empty: seq[int] = @[]
|
||||||
|
empty.cumsum
|
||||||
|
doAssert empty == @[]
|
||||||
|
|
||||||
|
block: # cumsum float in-place
|
||||||
|
var counts = [1.0, 2.0, 3.0, 4.0]
|
||||||
|
counts.cumsum
|
||||||
|
doAssert counts == [1.0, 3.0, 6.0, 10.0]
|
||||||
|
var empty: seq[float] = @[]
|
||||||
|
empty.cumsum
|
||||||
|
doAssert empty == @[]
|
||||||
|
|
||||||
|
block: # ^ compiles for valid types
|
||||||
|
doAssert: compiles(5 ^ 2)
|
||||||
|
doAssert: compiles(5.5 ^ 2)
|
||||||
|
doAssert: compiles(5.5 ^ 2.int8)
|
||||||
|
doAssert: compiles(5.5 ^ 2.uint)
|
||||||
|
doAssert: compiles(5.5 ^ 2.uint8)
|
||||||
|
doAssert: not compiles(5.5 ^ 2.2)
|
||||||
|
|
||||||
block: # signbit
|
block: # signbit
|
||||||
doAssert not signbit(0.0)
|
doAssert not signbit(0.0)
|
||||||
doAssert signbit(-0.0)
|
doAssert signbit(-0.0)
|
||||||
|
|
@ -310,6 +211,8 @@ template main =
|
||||||
doAssert NaN.isNaN
|
doAssert NaN.isNaN
|
||||||
doAssert not Inf.isNaN
|
doAssert not Inf.isNaN
|
||||||
doAssert isNaN(Inf - Inf)
|
doAssert isNaN(Inf - Inf)
|
||||||
|
doAssert not isNaN(3.1415926)
|
||||||
|
doAssert not isNaN(0'f32)
|
||||||
|
|
||||||
block: # signbit
|
block: # signbit
|
||||||
let x1 = NaN
|
let x1 = NaN
|
||||||
|
|
@ -324,16 +227,19 @@ template main =
|
||||||
doAssert signbit(x3)
|
doAssert signbit(x3)
|
||||||
|
|
||||||
block: # copySign
|
block: # copySign
|
||||||
|
doAssert copySign(10.0, 1.0) == 10.0
|
||||||
doAssert copySign(10.0, -1.0) == -10.0
|
doAssert copySign(10.0, -1.0) == -10.0
|
||||||
doAssert copySign(-10.0, -1.0) == -10.0
|
doAssert copySign(-10.0, -1.0) == -10.0
|
||||||
doAssert copySign(-10.0, 1.0) == 10.0
|
doAssert copySign(-10.0, 1.0) == 10.0
|
||||||
doAssert copySign(float(10), -1.0) == -10.0
|
doAssert copySign(float(10), -1.0) == -10.0
|
||||||
|
|
||||||
|
doAssert copySign(10.0'f64, 1.0) == 10.0
|
||||||
doAssert copySign(10.0'f64, -1.0) == -10.0
|
doAssert copySign(10.0'f64, -1.0) == -10.0
|
||||||
doAssert copySign(-10.0'f64, -1.0) == -10.0
|
doAssert copySign(-10.0'f64, -1.0) == -10.0
|
||||||
doAssert copySign(-10.0'f64, 1.0) == 10.0
|
doAssert copySign(-10.0'f64, 1.0) == 10.0
|
||||||
doAssert copySign(10'f64, -1.0) == -10.0
|
doAssert copySign(10'f64, -1.0) == -10.0
|
||||||
|
|
||||||
|
doAssert copySign(10.0'f32, 1.0) == 10.0
|
||||||
doAssert copySign(10.0'f32, -1.0) == -10.0
|
doAssert copySign(10.0'f32, -1.0) == -10.0
|
||||||
doAssert copySign(-10.0'f32, -1.0) == -10.0
|
doAssert copySign(-10.0'f32, -1.0) == -10.0
|
||||||
doAssert copySign(-10.0'f32, 1.0) == 10.0
|
doAssert copySign(-10.0'f32, 1.0) == 10.0
|
||||||
|
|
@ -363,6 +269,16 @@ template main =
|
||||||
doAssert copySign(-NaN, 0.0).isNaN
|
doAssert copySign(-NaN, 0.0).isNaN
|
||||||
doAssert copySign(-NaN, -0.0).isNaN
|
doAssert copySign(-NaN, -0.0).isNaN
|
||||||
|
|
||||||
|
block: # almostEqual
|
||||||
|
doAssert almostEqual(3.141592653589793, 3.1415926535897936)
|
||||||
|
doAssert almostEqual(1.6777215e7'f32, 1.6777216e7'f32)
|
||||||
|
doAssert almostEqual(Inf, Inf)
|
||||||
|
doAssert almostEqual(-Inf, -Inf)
|
||||||
|
doAssert not almostEqual(Inf, -Inf)
|
||||||
|
doAssert not almostEqual(-Inf, Inf)
|
||||||
|
doAssert not almostEqual(Inf, NaN)
|
||||||
|
doAssert not almostEqual(NaN, NaN)
|
||||||
|
|
||||||
block: # round() tests
|
block: # round() tests
|
||||||
block: # Round to 0 decimal places
|
block: # Round to 0 decimal places
|
||||||
doAssert round(54.652) == 55.0
|
doAssert round(54.652) == 55.0
|
||||||
|
|
@ -416,5 +332,36 @@ template main =
|
||||||
doAssert abs(NaN).isNaN
|
doAssert abs(NaN).isNaN
|
||||||
doAssert abs(-NaN).isNaN
|
doAssert abs(-NaN).isNaN
|
||||||
|
|
||||||
|
block: # classify
|
||||||
|
doAssert classify(0.3) == fcNormal
|
||||||
|
doAssert classify(-0.3) == fcNormal
|
||||||
|
doAssert classify(5.0e-324) == fcSubnormal
|
||||||
|
doAssert classify(-5.0e-324) == fcSubnormal
|
||||||
|
doAssert classify(0.0) == fcZero
|
||||||
|
doAssert classify(-0.0) == fcNegZero
|
||||||
|
doAssert classify(NaN) == fcNan
|
||||||
|
doAssert classify(0.3 / 0.0) == fcInf
|
||||||
|
doAssert classify(Inf) == fcInf
|
||||||
|
doAssert classify(-0.3 / 0.0) == fcNegInf
|
||||||
|
doAssert classify(-Inf) == fcNegInf
|
||||||
|
|
||||||
|
block: # sum
|
||||||
|
let empty: seq[int] = @[]
|
||||||
|
doAssert sum(empty) == 0
|
||||||
|
doAssert sum([1, 2, 3, 4]) == 10
|
||||||
|
doAssert sum([-4, 3, 5]) == 4
|
||||||
|
|
||||||
|
block: # prod
|
||||||
|
let empty: seq[int] = @[]
|
||||||
|
doAssert prod(empty) == 1
|
||||||
|
doAssert prod([1, 2, 3, 4]) == 24
|
||||||
|
doAssert prod([-4, 3, 5]) == -60
|
||||||
|
doAssert almostEqual(prod([1.5, 3.4]), 5.1)
|
||||||
|
let x: seq[float] = @[]
|
||||||
|
doAssert prod(x) == 1.0
|
||||||
|
|
||||||
|
when not defined(windows): # xxx pending bug #17017
|
||||||
|
doAssert sqrt(-1.0).isNaN
|
||||||
|
|
||||||
static: main()
|
static: main()
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ discard """
|
||||||
targets: "c js"
|
targets: "c js"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import std/[random, stats]
|
import std/[random, math, os, stats, sets, tables]
|
||||||
|
|
||||||
randomize(233)
|
randomize(233)
|
||||||
|
|
||||||
proc main =
|
proc main() =
|
||||||
var occur: array[1000, int]
|
var occur: array[1000, int]
|
||||||
|
|
||||||
for i in 0..100_000:
|
for i in 0..100_000:
|
||||||
|
|
@ -33,11 +33,8 @@ proc main =
|
||||||
# don't use causes integer overflow
|
# don't use causes integer overflow
|
||||||
doAssert compiles(rand[int](low(int) .. high(int)))
|
doAssert compiles(rand[int](low(int) .. high(int)))
|
||||||
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
import math
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
when not defined(js):
|
when not defined(js):
|
||||||
doAssert almostEqual(rand(12.5), 4.012897747078944)
|
doAssert almostEqual(rand(12.5), 4.012897747078944)
|
||||||
|
|
@ -64,3 +61,106 @@ block:
|
||||||
if rand(5.DiceRoll) < 3:
|
if rand(5.DiceRoll) < 3:
|
||||||
flag = true
|
flag = true
|
||||||
doAssert flag # because of: rand(max: int): int
|
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):
|
||||||
|
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):
|
||||||
|
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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue