1375 lines
42 KiB
JavaScript
1375 lines
42 KiB
JavaScript
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||
|
|
||
|
/* eslint-disable camelcase */
|
||
|
/* eslint-env jest */
|
||
|
const random = require('../random')
|
||
|
|
||
|
describe('Random', () => {
|
||
|
test('init', () => {
|
||
|
expect(() => {
|
||
|
random.number()
|
||
|
}).toThrow()
|
||
|
random.init(1)
|
||
|
random.number()
|
||
|
})
|
||
|
|
||
|
test('number() corner cases', () => {
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
let sum = 0
|
||
|
|
||
|
for (let i = 0; i < 100; ++i) {
|
||
|
sum += random.number(0)
|
||
|
}
|
||
|
expect(sum).toEqual(0)
|
||
|
|
||
|
for (let i = 0; i < 100; ++i) {
|
||
|
sum += random.number(1)
|
||
|
}
|
||
|
expect(sum).toEqual(0)
|
||
|
|
||
|
let bins = new Uint32Array(2)
|
||
|
for (let i = 0; i < 100; ++i) {
|
||
|
++bins[random.number(2)]
|
||
|
}
|
||
|
expect(bins[0] + bins[1]).toEqual(100)
|
||
|
expect(bins[0]).toBeGreaterThan(20)
|
||
|
|
||
|
sum = 0
|
||
|
for (let i = 0; i < 15; ++i) {
|
||
|
sum |= random.number()
|
||
|
}
|
||
|
expect(sum >>> 0).toEqual(0xffffffff)
|
||
|
})
|
||
|
|
||
|
test('number() uniform distriution', () => {
|
||
|
const N = Math.pow(2, 17)
|
||
|
const TRIES = 3
|
||
|
const XSQ = 564.7 // quantile of chi-square dist. k=511, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(512)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.number(bins.length)
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 511)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('float() uniform distribution', () => {
|
||
|
const N = Math.pow(2, 17)
|
||
|
const TRIES = 3
|
||
|
const XSQ = 564.7 // quantile of chi-square dist. k=511, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(512)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = (random.float() * bins.length) >>> 0
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 511)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('range() uniform distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 66.34 // quantile of chi-square dist. k=49, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(50)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.range(0, bins.length - 1)
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 49)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('range() uniform distribution with offset', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 66.34 // quantile of chi-square dist. k=49, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(50)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.range(10, 10 + bins.length - 1) - 10
|
||
|
expect(tmp).toBeGreaterThanOrEqual(0)
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 49)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('range() PRNG reproducibility', () => {
|
||
|
let seed, result1, result2
|
||
|
seed = Math.random() * 0x100000000
|
||
|
for (let t = 0; t < 50; ++t) {
|
||
|
random.init(seed)
|
||
|
result1 = random.range(1, 20)
|
||
|
for (let i = 0; i < 5; ++i) {
|
||
|
random.init(seed)
|
||
|
result2 = random.range(1, 20)
|
||
|
expect(result1).toBe(result2)
|
||
|
}
|
||
|
seed = random.number()
|
||
|
}
|
||
|
})
|
||
|
|
||
|
test('item() exception cases', () => {
|
||
|
expect(() => {
|
||
|
random.item()
|
||
|
}).toThrowError('random.item() received invalid object: (undefined)')
|
||
|
expect(() => {
|
||
|
random.item(1)
|
||
|
}).toThrowError('random.item() received invalid object: (1)')
|
||
|
expect(() => {
|
||
|
random.item('1')
|
||
|
}).toThrowError('random.item() received invalid object: (1)')
|
||
|
expect(() => {
|
||
|
random.item({})
|
||
|
}).toThrowError('random.item() received invalid object: ([object Object])')
|
||
|
})
|
||
|
|
||
|
test('item() distribution with list', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(3)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.item([99, 100, 101]) - 99
|
||
|
expect(tmp).toBeGreaterThanOrEqual(0)
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('key() distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(3)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.key({ 99: 0, 100: 0, 101: 0 }) - 99
|
||
|
expect(tmp).toBeGreaterThanOrEqual(0)
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('bool() distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 3.84 // quantile of chi-square dist. k=1, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(2)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.bool()
|
||
|
if (tmp === true) {
|
||
|
tmp = 1
|
||
|
} else if (tmp === false) {
|
||
|
tmp = 0
|
||
|
} else {
|
||
|
throw new Error(`Unexpected random.bool() result: ${tmp}`)
|
||
|
}
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 1)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('pick() cases', () => {
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
for (let i = 0; i < 100; ++i) {
|
||
|
let tmp = Math.random()
|
||
|
expect(random.pick(tmp)).toEqual(tmp)
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < 100; ++i) {
|
||
|
let tmp = (Math.random() * 100) >>> 0
|
||
|
expect(random.pick(tmp)).toEqual(tmp)
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < 100; ++i) {
|
||
|
let tmp = Math.random() + ''
|
||
|
expect(random.pick(tmp)).toEqual(tmp)
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < 100; ++i) {
|
||
|
let tmp = Math.random()
|
||
|
expect(random.pick([tmp])).toEqual(tmp)
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < 100; ++i) {
|
||
|
let tmp = Math.random()
|
||
|
expect(random.pick(() => tmp)).toEqual(tmp)
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < 100; ++i) {
|
||
|
let tmp = Math.random()
|
||
|
expect(random.pick(() => [tmp])).toEqual([tmp])
|
||
|
}
|
||
|
})
|
||
|
|
||
|
test('pick() with equal distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(3)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.pick([
|
||
|
0,
|
||
|
[1, 1],
|
||
|
() => 2
|
||
|
])
|
||
|
expect(tmp).toBeGreaterThanOrEqual(0)
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('pick() with unequal distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(3)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.pick([
|
||
|
[0, 1],
|
||
|
[1],
|
||
|
() => 2
|
||
|
])
|
||
|
expect(tmp).toBeGreaterThanOrEqual(0)
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq =
|
||
|
Math.pow(bins[0] - N / 6, 2) / (N / 6) +
|
||
|
Math.pow(bins[1] - N / 2, 2) / (N / 2) +
|
||
|
Math.pow(bins[2] - N / 3, 2) / (N / 3)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('chance(undefined) distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 3.84 // quantile of chi-square dist. k=1, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(2)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.chance()
|
||
|
if (tmp === true) {
|
||
|
tmp = 1
|
||
|
} else if (tmp === false) {
|
||
|
tmp = 0
|
||
|
} else {
|
||
|
throw new Error(`Unexpected random.chance() result: ${tmp}`)
|
||
|
}
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 1)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('chance(2) distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 3.84 // quantile of chi-square dist. k=1, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(2)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.chance(2)
|
||
|
if (tmp === true) {
|
||
|
tmp = 1
|
||
|
} else if (tmp === false) {
|
||
|
tmp = 0
|
||
|
} else {
|
||
|
throw new Error(`Unexpected random.chance() result: ${tmp}`)
|
||
|
}
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 1)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('chance(3) distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 3.84 // quantile of chi-square dist. k=1, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(2)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.chance(3)
|
||
|
if (tmp === true) {
|
||
|
tmp = 0
|
||
|
} else if (tmp === false) {
|
||
|
tmp = 1
|
||
|
} else {
|
||
|
throw new Error(`Unexpected random.chance() result: ${tmp}`)
|
||
|
}
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq =
|
||
|
Math.pow(bins[0] - N / 3, 2) / (N / 3) +
|
||
|
Math.pow(bins[1] - (2 * N) / 3, 2) / ((2 * N) / 3)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 1)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('chance(1000) distribution', () => {
|
||
|
const N = 1e6
|
||
|
const TRIES = 3
|
||
|
const XSQ = 3.84 // quantile of chi-square dist. k=1, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(2)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.chance(1000)
|
||
|
if (tmp === true) {
|
||
|
tmp = 0
|
||
|
} else if (tmp === false) {
|
||
|
tmp = 1
|
||
|
} else {
|
||
|
throw new Error(`Unexpected random.chance() result: ${tmp}`)
|
||
|
}
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq =
|
||
|
Math.pow(bins[0] - N / 1000, 2) / (N / 1000) +
|
||
|
Math.pow(bins[1] - (999 * N) / 1000, 2) / ((999 * N) / 1000)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 1)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('choose() with equal distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(3)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.choose([[1, 0], [1, 1], [1, 2]])
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('choose() with unequal distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(3)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.choose([[1, 0], [2, 1], [1, 2]])
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq =
|
||
|
Math.pow(bins[0] - N / 4, 2) / (N / 4) +
|
||
|
Math.pow(bins[1] - N / 2, 2) / (N / 2) +
|
||
|
Math.pow(bins[2] - N / 4, 2) / (N / 4)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('choose(flat) equal distribution with types not picked', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05
|
||
|
const v1 = 1
|
||
|
const v2 = [12]
|
||
|
const v3 = () => {}
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(3)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.choose([[1, v1], [1, v2], [1, v3]], true)
|
||
|
if (tmp === v1) {
|
||
|
tmp = 0
|
||
|
} else if (tmp === v2) {
|
||
|
tmp = 1
|
||
|
} else if (tmp === v3) {
|
||
|
tmp = 2
|
||
|
} else {
|
||
|
throw new Error(`Unexpected random.choose() result: ${tmp}`)
|
||
|
}
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('weighted() with equal distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(3)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.item(random.weighted([{ w: 1, v: 0 }, { w: 1, v: 1 }, { w: 1, v: 2 }]))
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('choose(flat) with unequal distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(3)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.choose([[1, 0], [2, 1], [1, 2]], true)
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq =
|
||
|
Math.pow(bins[0] - N / 4, 2) / (N / 4) +
|
||
|
Math.pow(bins[1] - N / 2, 2) / (N / 2) +
|
||
|
Math.pow(bins[2] - N / 4, 2) / (N / 4)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('weighted() with unequal distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(3)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.item(random.weighted([{ w: 1, v: 0 }, { w: 2, v: 1 }, { w: 1, v: 2 }]))
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq =
|
||
|
Math.pow(bins[0] - N / 4, 2) / (N / 4) +
|
||
|
Math.pow(bins[1] - N / 2, 2) / (N / 2) +
|
||
|
Math.pow(bins[2] - N / 4, 2) / (N / 4)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": " + tries)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('weighted() equal distribution with types not picked', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05
|
||
|
const v1 = 1
|
||
|
const v2 = [12]
|
||
|
const v3 = () => {}
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(3)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.item(
|
||
|
random.weighted([{ w: 1, v: v1 }, { w: 1, v: v2 }, { w: 1, v: v3 }])
|
||
|
)
|
||
|
if (tmp === v1) {
|
||
|
tmp = 0
|
||
|
} else if (tmp === v2) {
|
||
|
tmp = 1
|
||
|
} else if (tmp === v3) {
|
||
|
tmp = 2
|
||
|
} else {
|
||
|
throw new Error(`Unexpected random.weighted() result: ${tmp}`)
|
||
|
}
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries);
|
||
|
return false
|
||
|
}
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('choose() with unequal distribution and pick', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(3)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.choose([
|
||
|
[1, 0],
|
||
|
[2, [1, 2]],
|
||
|
[
|
||
|
1,
|
||
|
() => 2
|
||
|
]
|
||
|
])
|
||
|
expect(tmp).toBeGreaterThanOrEqual(0)
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq =
|
||
|
Math.pow(bins[0] - N / 4, 2) / (N / 4) +
|
||
|
Math.pow(bins[1] - N / 4, 2) / (N / 4) +
|
||
|
Math.pow(bins[2] - N / 2, 2) / (N / 2)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries);
|
||
|
return false
|
||
|
}
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('weighted() equal distribution with types not picked', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05
|
||
|
const v1 = 1
|
||
|
const v2 = [12]
|
||
|
const v3 = () => {}
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(3)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.item(
|
||
|
random.weighted([{ w: 1, v: v1 }, { w: 1, v: v2 }, { w: 1, v: v3 }])
|
||
|
)
|
||
|
if (tmp === v1) {
|
||
|
tmp = 0
|
||
|
} else if (tmp === v2) {
|
||
|
tmp = 1
|
||
|
} else if (tmp === v3) {
|
||
|
tmp = 2
|
||
|
} else {
|
||
|
throw new Error(`Unexpected random.weighted() result: ${tmp}`)
|
||
|
}
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries);
|
||
|
return false
|
||
|
}
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('use() distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 3.84 // quantile of chi-square dist. k=1, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(2)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let rnd = Math.random()
|
||
|
let use = random.use(rnd)
|
||
|
if (use === rnd) {
|
||
|
use = 1
|
||
|
} else if (use === '') {
|
||
|
use = 0
|
||
|
} else {
|
||
|
throw new Error(`Unexpected random.use() result: ${use}`)
|
||
|
}
|
||
|
++bins[use]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 1)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries);
|
||
|
return false
|
||
|
}
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('shuffle() distribution', () => {
|
||
|
const N = 1e4
|
||
|
const M = 10
|
||
|
const TRIES = 3
|
||
|
const XSQ = 123.23 // quantile of chi-square dist. k=M*M-1, p=.05
|
||
|
// XXX: shouldn't k be M! ?
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(M * M)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let array = []
|
||
|
for (let j = 0; j < M; ++j) array.push(j)
|
||
|
random.shuffle(array)
|
||
|
for (let j = 0; j < M; ++j) ++bins[j * M + array[j]]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / M
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 99)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries);
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('shuffled() distribution', () => {
|
||
|
const N = 1e4
|
||
|
const M = 10
|
||
|
const TRIES = 3
|
||
|
const XSQ = 123.23 // quantile of chi-square dist. k=M*M-1, p=.05
|
||
|
// XXX: shouldn't k be M! ?
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(M * M)
|
||
|
let array_ref = []
|
||
|
for (let j = 0; j < M; ++j) {
|
||
|
array_ref.push(j)
|
||
|
}
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let array = random.shuffled(array_ref)
|
||
|
for (let j = 0; j < M; ++j) {
|
||
|
++bins[j * M + array[j]]
|
||
|
expect(array_ref[j]).toEqual(j)
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / M
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 99)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries);
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test.skip('subset() with equal distribution', () => {
|
||
|
/*
|
||
|
* This doesn't specify limit, so length distribution should be even,
|
||
|
* and selections should be even within each length.
|
||
|
*/
|
||
|
const N = 1e4
|
||
|
const M = 3
|
||
|
const TRIES = 3
|
||
|
const B0_XSQ = 5.99
|
||
|
const B1_XSQ = 15.51
|
||
|
const B2_XSQ = 38.89
|
||
|
const LEN_XSQ = 7.81 // quantile of chi-square dist. k=[2,8,26,3], p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let bin0_xsq, bin1_xsq, bin2_xsq, length_xsq
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = [new Uint32Array(3), new Uint32Array(9), new Uint32Array(27)]
|
||
|
let lengths = new Uint32Array(M + 1)
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.subset([
|
||
|
0,
|
||
|
[1, 1],
|
||
|
() => 2
|
||
|
])
|
||
|
expect(tmp.length).toBeLessThanOrEqual(M)
|
||
|
++lengths[tmp.length]
|
||
|
if (tmp.length) {
|
||
|
++bins[tmp.length - 1][tmp.reduce((a, v) => a * 3 + v, 0)]
|
||
|
}
|
||
|
}
|
||
|
bin0_xsq = bins[0].reduce((a, v) => {
|
||
|
let e = N / (M + 1) / Math.pow(M, 1)
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
bin1_xsq = bins[1].reduce((a, v) => {
|
||
|
let e = N / (M + 1) / Math.pow(M, 2)
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
bin2_xsq = bins[2].reduce((a, v) => {
|
||
|
let e = N / (M + 1) / Math.pow(M, 3)
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
length_xsq = lengths.reduce((a, v) => {
|
||
|
let e = N / (M + 1)
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (bin0_xsq < B0_XSQ && bin1_xsq < B1_XSQ && bin2_xsq < B2_XSQ && length_xsq < LEN_XSQ) {
|
||
|
console.log(`Expected lengths x^2 to be < ${LEN_XSQ} got ${length_xsq} on attempt #${attempt + 1}`)
|
||
|
console.log(`Expected length=1 x^2 to be < ${B0_XSQ} got ${bin0_xsq} on attempt # ${attempt + 1}`)
|
||
|
console.log(`Expected length=2 x^2 to be < ${B1_XSQ} got ${bin1_xsq} on attempt #${attempt + 1}`)
|
||
|
console.log(`Expected length=3 x^2 to be < ${B2_XSQ} got ${bin2_xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
console.log(`Expected lengths x^2 to be < ${LEN_XSQ} got ${length_xsq}`)
|
||
|
console.log(`Expected length=1 x^2 to be < ${B0_XSQ} got ${bin0_xsq}`)
|
||
|
console.log(`Expected length=2 x^2 to be < ${B1_XSQ} got ${bin1_xsq}`)
|
||
|
console.log(`Expected length=3 x^2 to be < ${B2_XSQ} got ${bin2_xsq}`)
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq low enough");
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test.skip('subset(limit) with equal distribution', () => {
|
||
|
/*
|
||
|
* limit is specified, so length should always == limit, and selections should be even
|
||
|
*/
|
||
|
const N = 1e4
|
||
|
const M = 3
|
||
|
const TRIES = 3
|
||
|
const B0_XSQ = 5.99
|
||
|
const B1_XSQ = 15.51
|
||
|
const B2_XSQ = 38.89
|
||
|
const B3_XSQ = 101.88 // quantile of chi-square dist. k=[2,8,26,80], p=.05
|
||
|
|
||
|
let bin0_xsq, bin1_xsq, bin2_xsq, bin3_xsq
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
for (let attempt = 0; attempt < 100; ++attempt) {
|
||
|
expect(random.subset([1, 2, 3], 0).length).toBe(0)
|
||
|
}
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = [
|
||
|
new Uint32Array(3),
|
||
|
new Uint32Array(9),
|
||
|
new Uint32Array(27),
|
||
|
new Uint32Array(81)
|
||
|
]
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.subset([0, 1, 2], 1)
|
||
|
expect(tmp.length).toBe(1)
|
||
|
|
||
|
++bins[0][tmp.reduce((a, v) => a * 3 + v, 0)]
|
||
|
tmp = random.subset([0, 1, 2], 2)
|
||
|
expect(tmp.length).toBe(2)
|
||
|
|
||
|
++bins[1][tmp.reduce((a, v) => a * 3 + v, 0)]
|
||
|
tmp = random.subset([0, 1, 2], 3)
|
||
|
expect(tmp.length).toBe(3)
|
||
|
|
||
|
++bins[2][tmp.reduce((a, v) => a * 3 + v, 0)]
|
||
|
tmp = random.subset([0, 1, 2], 4)
|
||
|
expect(tmp.length).toBe(4)
|
||
|
|
||
|
++bins[3][tmp.reduce((a, v) => a * 3 + v, 0)]
|
||
|
}
|
||
|
bin0_xsq = bins[0].reduce((a, v) => {
|
||
|
let e = N / Math.pow(M, 1)
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
bin1_xsq = bins[1].reduce((a, v) => {
|
||
|
let e = N / Math.pow(M, 2)
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
bin2_xsq = bins[2].reduce((a, v) => {
|
||
|
let e = N / Math.pow(M, 3)
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
bin3_xsq = bins[3].reduce((a, v) => {
|
||
|
let e = N / Math.pow(M, 4)
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (bin0_xsq < B0_XSQ && bin1_xsq < B1_XSQ && bin2_xsq < B2_XSQ && bin3_xsq < B3_XSQ) {
|
||
|
// assert.ok(true, "Expected length=1 x^2 to be < " + B0_XSQ + ", got " + bin0_xsq + " on attempt #" + (attempt + 1));
|
||
|
// assert.ok(true, "Expected length=2 x^2 to be < " + B1_XSQ + ", got " + bin1_xsq);
|
||
|
// assert.ok(true, "Expected length=3 x^2 to be < " + B2_XSQ + ", got " + bin2_xsq);
|
||
|
// assert.ok(true, "Expected length=4 x^2 to be < " + B3_XSQ + ", got " + bin3_xsq);
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
console.log(`Expected length=1 x^2 to be < ${B0_XSQ} got ${bin0_xsq}`)
|
||
|
console.log(`Expected length=2 x^2 to be < ${B1_XSQ} got ${bin1_xsq}`)
|
||
|
console.log(`Expected length=3 x^2 to be < ${B2_XSQ} got ${bin2_xsq}`)
|
||
|
console.log(`Expected length=4 x^2 to be < ${B3_XSQ} got ${bin3_xsq}`)
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq low enough");
|
||
|
return false
|
||
|
}
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('pop() distribution', () => {
|
||
|
const N = 1e4
|
||
|
const TRIES = 3
|
||
|
const XSQ = 5.99 // quantile of chi-square dist. k=2, p=.05
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(3)
|
||
|
const orig = [99, 100, 101]
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let arr = orig.slice()
|
||
|
let tmp = random.pop(arr) - 99
|
||
|
expect(tmp).toBeGreaterThanOrEqual(0)
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
expect(arr.length).toBe(2)
|
||
|
expect(arr.reduce((a, v) => { return a + v }, tmp)).toBe(201)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
let xsq = bins.reduce((a, v) => {
|
||
|
let e = N / bins.length
|
||
|
return a + Math.pow(v - e, 2) / e
|
||
|
}, 0)
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 2)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries);
|
||
|
return false
|
||
|
}
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
|
||
|
test('ludOneTo() distribution', () => {
|
||
|
const N = 1e5
|
||
|
const TRIES = 3
|
||
|
const XSQ = 123.22 // quantile of chi-square dist. k=99, p=.05
|
||
|
let dist = new Uint32Array(100)
|
||
|
|
||
|
random.init(Math.random() * 0x100000000)
|
||
|
|
||
|
/* Build the ideal distribution for comparison
|
||
|
* I thought this would be the PDF of the log-normal distribution, but I couldn't get mu & sigma figured out? */
|
||
|
for (let i = 0; i < 100 * dist.length; ++i) {
|
||
|
dist[Math.floor(Math.exp((i / (100 * dist.length)) * Math.log(dist.length)))] +=
|
||
|
N / (dist.length * 100)
|
||
|
}
|
||
|
expect(dist[0]).toEqual(0)
|
||
|
|
||
|
const _test = () => {
|
||
|
let tries = []
|
||
|
for (let attempt = 0; attempt < TRIES; ++attempt) {
|
||
|
let bins = new Uint32Array(dist.length)
|
||
|
let xsq = 0
|
||
|
for (let i = 0; i < N; ++i) {
|
||
|
let tmp = random.ludOneTo(bins.length) >>> 0
|
||
|
expect(tmp).toBeLessThan(bins.length)
|
||
|
++bins[tmp]
|
||
|
}
|
||
|
expect(bins[0]).toEqual(0)
|
||
|
|
||
|
for (let i = 1; i < bins.length; ++i) {
|
||
|
xsq += Math.pow(bins[i] - dist[i], 2) / dist[i]
|
||
|
}
|
||
|
/*
|
||
|
* XSQ = scipy.stats.chi2.isf(.05, 99)
|
||
|
* if xsq > XSQ, the result is biased at 95% significance
|
||
|
*/
|
||
|
if (xsq < XSQ) {
|
||
|
console.log(`Expected x^2 to be < ${XSQ}, got ${xsq} on attempt #${attempt + 1}`)
|
||
|
return true
|
||
|
}
|
||
|
tries.push(xsq)
|
||
|
}
|
||
|
// assert.ok(false, "Failed in " + TRIES + " attempts to get xsq lower than " + XSQ + ": "+ tries);
|
||
|
return false
|
||
|
}
|
||
|
expect(_test()).toBe(true)
|
||
|
})
|
||
|
})
|