/* 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.skip('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) }) })