Skip to content

Instantly share code, notes, and snippets.

@pyrtsa
Created March 19, 2015 10:31
Show Gist options
  • Save pyrtsa/e82bbf0a416c215cc55c to your computer and use it in GitHub Desktop.
Save pyrtsa/e82bbf0a416c215cc55c to your computer and use it in GitHub Desktop.
A human-readable, order-preserving serialisation for JSON-like data in JavaScript
<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<script src="orders.js"></script>
<style>
#display {
white-space: pre-wrap;
font-family: 'Menlo', monospace;
font-size: 12px;
line-height: 16px;
}
.output {
color: #777;
}
</style>
<div id="display"></div>
<script>
// scrutinise.verbose = true
testElen()
randTestElen()
testCerealise()
display(scrutinise.stats())
display()
display("Examples")
display("========")
display()
function leftAlign(n, s) {
s = '' + s
return s + repeatString(' ', n - s.length)
}
function rightAlign(n, s) {
s = '' + s
return repeatString(' ', n - s.length) + s
}
function demo(f, x) {
var input = scrutinyFcall(f, x)
var output = scrutinyStringify(f(x))
if (input.length <= 40) {
display('>>> ' + leftAlign(40, input) + ' // ' + output)
} else {
display('>>> ' + input + '\n // ' + output)
}
}
demo(elenNat, 0)
demo(elenNat, 1)
demo(elenNat, 2)
demo(elenNat, 10)
demo(elenNat, 100)
demo(elenNat, 1000)
demo(elenNat, '1234567890987654321')
display()
demo(elenInt, 1)
demo(elenInt, 0)
demo(elenInt, -1)
demo(elenInt, -2)
demo(elenInt, -10)
demo(elenInt, -99)
demo(elenInt, -100)
demo(elenInt, -1000)
demo(elenInt, '-1234567890987654321')
display()
demo(elenDec, -12)
demo(elenDec, -11)
demo(elenDec, -10)
demo(elenDec, -9)
demo(elenDec, -8)
demo(elenDec, -7)
demo(elenDec, -6)
demo(elenDec, -5)
demo(elenDec, -4)
demo(elenDec, -3)
demo(elenDec, -2)
demo(elenDec, -1)
demo(elenDec, 0)
demo(elenDec, 1)
demo(elenDec, 2)
demo(elenDec, 3)
demo(elenDec, 4)
demo(elenDec, 5)
demo(elenDec, 6)
demo(elenDec, 7)
demo(elenDec, 8)
demo(elenDec, 9)
demo(elenDec, 10)
demo(elenDec, 11)
demo(elenDec, 12)
display()
demo(elenDec, '-3.13e+100')
demo(elenDec, '-3.13e+50')
demo(elenDec, '-3.13e+20')
demo(elenDec, '-3.13e+10')
demo(elenDec, '-3.13e+5')
demo(elenDec, '-3.13e+2')
demo(elenDec, '-3.13e+1')
demo(elenDec, '-3.13')
demo(elenDec, '-3.13e-1')
demo(elenDec, '-3.13e-2')
demo(elenDec, '-3.13e-5')
demo(elenDec, '-3.13e-10')
demo(elenDec, '-3.13e-20')
demo(elenDec, '-3.13e-50')
demo(elenDec, '-3.13e-100')
demo(elenDec, '+3.13e-100')
demo(elenDec, '+3.13e-50')
demo(elenDec, '+3.13e-20')
demo(elenDec, '+3.13e-10')
demo(elenDec, '+3.13e-5')
demo(elenDec, '+3.13e-2')
demo(elenDec, '+3.13e-1')
demo(elenDec, '+3.13')
demo(elenDec, '+3.13e+1')
demo(elenDec, '+3.13e+2')
demo(elenDec, '+3.13e+5')
demo(elenDec, '+3.13e+10')
demo(elenDec, '+3.13e+20')
demo(elenDec, '+3.13e+50')
demo(elenDec, '+3.13e+100')
display()
demo(elenDec, 3.14)
demo(elenDec, 3.14e50)
demo(elenDec, 3.14e100)
demo(elenDec, 3.14e-100)
demo(elenDec, 299792458)
demo(elenDec, 2432902008176640000)
demo(elenDec, '6.02214129e-23')
demo(elenDec, -3.14)
demo(elenDec, -3.14e100)
demo(elenDec, -3.14e-100)
display()
demo(elenDec, -Infinity)
demo(elenDec, "-1.23e1000")
demo(elenDec, "-1.23e999")
demo(elenDec, "-1.23e987")
demo(elenDec, "-1.23e123")
demo(elenDec, "-1.23e100")
demo(elenDec, "-1.23e99")
demo(elenDec, "-1.23e88")
demo(elenDec, "-1.23e11")
demo(elenDec, "-1.23e10")
demo(elenDec, "-1.23e9")
demo(elenDec, "-1.23e1")
demo(elenDec, "-1.23e0")
demo(elenDec, "-1.23e-1")
demo(elenDec, "-1.23e-2")
demo(elenDec, "-1.23e-1000")
demo(elenDec, -12.34)
demo(elenDec, -12.3)
demo(elenDec, -12)
demo(elenDec, -1.23)
demo(elenDec, -0.123)
demo(elenDec, 0.123)
demo(elenDec, 1.23)
demo(elenDec, 12)
demo(elenDec, 12.3)
demo(elenDec, 12.34)
demo(elenDec, Infinity)
display()
demo(cerealise, 0)
demo(cerealise, 1)
demo(cerealise, 'one')
demo(cerealise, 'two')
demo(cerealise, [])
demo(cerealise, [1, 2, 3])
demo(cerealise, [1, 2, 4])
demo(cerealise, [1, 2, 'four'])
demo(cerealise, [[1, 2], 'four'])
demo(cerealise, [[123456789, 2], 'four'])
demo(cerealise, [[123456789, 2], 'one hundred and fourty-two'])
demo(cerealise, [[123456789, 2], 'one thousand!!!'])
demo(cerealise, [['doe', 'john', 'jr'], 'deadbeef-1234-...'])
demo(cerealise, [['franklin', 'ben'], 'fooo1337-5687-...'])
display()
demo(cerealise, genArray(randAny)())
display()
demo(cerealise, genArray(randAny)())
display()
demo(cerealise, genArray(randAny)())
function compareMemoryEfficiency(descr, gen) {
var objs = range(100).map(gen)
var json = objs.map(function (x) { return JSON.stringify(x).length }).sum()
var cere = objs.map(function (x) { return cerealise(x).length }).sum()
var fact = Math.round(100 * cere / json) / 100
display('For a random sample of ' + leftAlign(27, descr + ', ') +
'cerealise(x) took ' + fact + 'x the space of the equivalent JSON string.')
}
display()
compareMemoryEfficiency('integers 0 .. 9', genNat(10))
compareMemoryEfficiency('integers 0 .. 99', genNat(100))
compareMemoryEfficiency('integers 0 .. 999', genNat(1000))
compareMemoryEfficiency('integers 0 .. 9,999', genNat(10000))
compareMemoryEfficiency('integers 0 .. 99,999', genNat(100000))
compareMemoryEfficiency('integers 0 .. 999,999', genNat(1000000))
compareMemoryEfficiency('integers 0 .. 999,999,999', genNat(1000000000))
compareMemoryEfficiency('32-bit integers', randInt)
compareMemoryEfficiency('decimal numbers', randDecimal)
compareMemoryEfficiency('floating point numbers', randFloat)
compareMemoryEfficiency('arbitrary data', randObject)
</script>
function display(message) {
var parent = document.getElementById('display')
if (!parent) return
var child = document.createElement('div')
child.className = 'entry'
child.textContent = message || '\n'
parent.appendChild(child)
console.log.apply(console, arguments)
}
function identity(x) { return x }
function constantly(x) { return function () { return x }}
function isSome(x) { return x != null }
Array.prototype.sum = function () {
return this.reduce(function (a, b) { return a + b }, 0)
}
Array.prototype.mapSome = function (f) {
return this.map(f).filter(isSome)
}
Array.prototype.groupBy = function (f) {
var obj = {}
this.forEach(function (x) {
var k = f(x)
obj[k] = obj[k] || []
obj[k].push(x)
})
return obj
}
Function.prototype.map = function (g) {
var f = this
return function () {
return g(f.apply(null, arguments))
}
}
function range(n) {
return Array.apply(null, Array(Math.max(0, n))).map(function (_, i) { return i })
}
function slice(xs, start, end) {
var f = Array.prototype.slice
return f.apply(xs, f.call(arguments, 1))
}
function nanMin(a, b) {
return a < b ? a : b < a ? b : a === a ? a : b
}
function nanMax(a, b) {
return a > b ? a : b > a ? b : a === a ? a : b
}
function repeatedly(f) {
var args = slice(arguments, 1)
return function () { return f.apply(null, args) }
}
function sign(x) {
return x < 0 ? -1 : x > 0 ? 1 : x == 0 ? 0 : x
}
function clamp(x, a, b) {
return x < a ? a : x > b ? b : x
}
function randBool() {
return Math.random() >= 0.5
}
function randSign() {
return randBool() ? -1 : 1
}
function randUnif(start, end) {
if (start == null) start = 0
if (end == null) end = 1
return start + (end - start) * Math.random()
}
function randInt(start, end) {
if (start == null) start = -Math.pow(2, 31)
if (end == null) end = Math.pow(2, 31) - 1
return clamp(Math.floor(randUnif(start, end)), start, end - 1)
}
function genChoose(rands) {
if (!Array.isArray(rands) || rands.length < 1) {
throw TypeError('rands is not a non-empty array: ' + rands)
}
return function () {
return rands[randNat(rands.length)]()
}
}
function genChooseWeighted(weightedRands) {
if (!Array.isArray(weightedRands) || weightedRands.length < 1) {
throw TypeError('weightedRands is not a non-empty array: ' + weightedRands)
}
var totalWeight = 0
var cumulativeWeights = []
for (var i = 0, n = weightedRands.length; i < n; i++) {
var weight = +weightedRands[i][0]
if (!(0 < weight && weight < Infinity)) {
throw TypeError('weightedRands[' + i + '] has invalid weight: ' + weight)
}
totalWeight += weight
cumulativeWeights.push(totalWeight)
}
return function () {
var position = randUnif(0, totalWeight)
var i = 0
while (position > cumulativeWeights[i]) i++
return weightedRands[i][1]()
}
}
function randDenormal() {
var mant = randNat(1, Math.pow(2, 52))
return Number.MIN_VALUE * mant
}
function randIntegral() {
return Math.floor(Math.pow(2, randInt(0, 54)) * (1 + Math.random()))
}
function randDecimal() {
var precision = randInt(1, 8)
var mant = randInt(0, Math.pow(10, precision))
var exp = randInt(-4, 10) - precision
return mant * Math.pow(10, exp)
}
function randSimpleDecimal() {
var exp = randInt(-20, 21)
var mant = randInt(-9, 10)
return mant * Math.pow(10, exp)
}
function randNormal() {
var exp = randInt(-1022, 1024)
return (1 + Math.random()) * Math.pow(2, exp)
}
var randFloat = (function () {
var choice = genChooseWeighted([
[1, constantly(0)],
[1, constantly(Number.MIN_VALUE)],
[1, constantly(0.5)],
[1, constantly(1 - Math.pow(2, -53))],
[1, constantly(1)],
[1, constantly(1 + Math.pow(2, -52))],
[1, constantly(2)],
[1, constantly(Number.MAX_VALUE)],
[1, constantly(Infinity)],
[1, randDenormal],
[10, randIntegral],
[10, randDecimal],
[5, randNormal]
])
return function () {
return randSign() * choice()
}
}())
function randFloat() {
var exp = randSign() * randInt(-1023, 1024)
var mantLen = randNat(53) // 0, 1, 2, ..., 52
var mant = randNat(Math.pow(2, mantLen))
return (exp == -1023) ? sgn * Number.MIN_VALUE * mant
: sgn * (1 + Math.pow(2, -52) * mant) * Math.pow(2, exp)
}
function randNat(n) {
return randInt(0, n)
}
function kind(x) {
if (x == null) return kind.NULL
if (typeof x == 'boolean') return kind.BOOLEAN
if (typeof x == 'number') return kind.NUMBER
if (typeof x == 'string') return kind.STRING
if (Array.isArray(x)) return kind.ARRAY
return kind.OBJECT
}
kind.BOOLEAN = 1
kind.NUMBER = 2
kind.STRING = 3
kind.OBJECT = 4
kind.ARRAY = 5
kind.NULL = 6
function compare(a, b) {
if (!(a === a) || !(b === b)) throw new TypeError('NaN')
var k = kind(a)
var l = kind(b)
if (k != l) return sign(k - l)
if (k == kind.NULL) return 0
if (k == kind.BOOLEAN) return sign(a - b)
if (k == kind.NUMBER) return sign(a - b)
if (k == kind.STRING) return a < b ? -1 : a > b ? 1 : 0
if (k == kind.OBJECT) {
var as = Object.keys(a); as.sort()
var bs = Object.keys(b); bs.sort()
var n = Math.min(as.length, bs.length)
for (var i = 0; i < n; i++) {
var c1 = compare(as[i], bs[i])
if (c1) return c1
var k1 = as[i]
var c2 = compare(a[k1], b[k1])
if (c2) return c2
}
return sign(as.length - bs.length)
}
if (k == kind.ARRAY) {
var n = Math.min(a.length, b.length)
for (var i = 0; i < n; i++) {
var c = compare(a[i], b[i])
if (c) return c
}
return sign(a.length - b.length)
}
throw new TypeError('unknown kind ' + k)
}
function comparing(f) {
return function (a, b) {
return compare(f(a), f(b))
}
}
function sorted(xs, cmp) {
return slice(xs).sort(cmp || compare)
}
function eq(a, b) { return compare(a, b) === 0 }
function ne(a, b) { return compare(a, b) !== 0 }
function lt(a, b) { return compare(a, b) < 0 }
function gt(a, b) { return compare(a, b) > 0 }
function le(a, b) { return compare(a, b) <= 0 }
function ge(a, b) { return compare(a, b) >= 0 }
function scrutinise(handler, pred, expected, fn) {
var args = slice(arguments, 4)
var status = 'skip'
var actual = null
var error = null
try {
actual = fn.apply(null, args)
status = pred(actual, expected) ? 'pass' : 'fail'
} catch (e) {
error = e
status = 'abort'
}
return handler.call(this, {
passed: status === 'pass',
status: status,
error: error,
pred: pred,
expected: expected,
actual: actual,
function: fn,
arguments: args
})
}
scrutinise.stats = function () {
var rs = this.results
if (!rs.length) return 'Nothing scrutinised yet.'
var gs = rs.groupBy(function (r) { return r.status })
var msg = 'Scrutinised ' + (rs.length == 1 ? 'one check' : rs.length + ' checks')
if (gs.pass) msg += ', ' + gs.pass.length + ' passed'
if (gs.fail) msg += ', ' + gs.fail.length + ' failed'
if (gs.abort) msg += ', ' + gs.abort.length + ' aborted'
if (gs.skip) msg += ', ' + gs.skip.length + ' skipped'
return msg + '.'
}
scrutinise.results = []
function scrutinyStringify(x) {
if (x._scrutinyName) return x._scrutinyName
if (x == null) return '' + x
if (typeof x == 'number') return '' + x
if (typeof x != 'function') return JSON.stringify(x) || '' + x
if (x.name) return x.name
var s = ('' + x).replace(/\s+/g, ' ')
return s.length <= 50 ? s : '<lambda>'
}
function scrutinyFcall(f) {
var fname = scrutinyStringify(f)
return fname + '(' + slice(arguments, 1).map(scrutinyStringify).join(', ') + ')'
}
function scrutinyToString(result) {
var predStr = scrutinyFcall(result.pred, result.expected, {_scrutinyName: 'x'})
var xStr = scrutinyFcall.apply(null, [result.function].concat(result.arguments))
var labelStr = predStr + ' where x = ' + xStr
if (result.status === 'skip') return 'skipped: ' + labelStr
if (result.status === 'abort') return 'aborted: ' + labelStr + ' raised ' + scrutinyStringify('' + result.error)
return (result.passed ? 'passed: ' : 'failed: ') + labelStr + ' returned ' + scrutinyStringify(result.actual)
}
function scrutiniser(handler) {
return function (pred, expected, fn) {
return scrutinise.apply(null, [handler].concat(slice(arguments)))
}
}
var check = scrutiniser(function (result) {
if (this !== window) {
this.addResult(result)
} else {
scrutinise.results.push(result)
if (!result.passed || scrutinise.verbose) {
display(scrutinyToString(result), result)
}
}
return result.passed
})
var verify = scrutiniser(function (result) {
if (this !== window) {
this.addResult(result)
} else {
scrutinise.results.push(result)
if (!result.passed || scrutinise.verbose) {
display(scrutinyToString(result), result)
}
}
if (!result.passed) {
throw new Error('verification failed')
} else {
return true
}
})
function toNatString(n) {
if (!(n >= 0 && n == Math.floor(n))) throw new TypeError('not a natural number: ' + n)
if (n < Math.pow(2, 53)) return '' + n
throw new TypeError('too big to represent a natural number: ' + n)
}
function elenNat(n, strict) {
n = '' + n
if (!/^(0|[1-9]\d*)$/g.test(n)) throw new TypeError('not a natural: ' + n)
var prefix = n.length > 1 ? '=' + elenNat(n.length - 1) : ''
return (strict ? '=' : '') + prefix + n
}
function inverseElen(n) {
return ('' + n).replace(/[\d=-]/g, function (s) {
return s === '-' ? '=' :
s === '=' ? '-' : 9 - parseInt(s, 10)
})
}
function elenInt(n) {
n = '' + n
if (n == '0') return '0'
var m = /^(-?)([1-9]\d*)$/.exec(n)
if (!m) throw new TypeError('not an integer: ' + n)
if (m[1]) { // negative
return inverseElen(elenNat(m[2], 'strict'))
} else { // positive
return elenNat(m[2])
}
}
function repeatString(s, n) {
return range(n).map(function () { return s }).join('')
}
function splitDec(d) {
function ret(sgn, nat, frac, fmt) {
return {positive: sgn, integral: nat, fractional: frac, format: fmt}
}
d = '' + d
var full = /^([+-]?)([1-9]\d*)\.(\d*?)0*$/
var dec = /^([+-]?)([1-9]\d*)$/
var frac = /^([+-]?)(?:0?)\.(0*[1-9]\d*?)0*$/
var eng = /^([+-]?)([1-9]\d*)(?:\.(\d+?)0*)?e([+-]?\d+)$/
var m = null
if ((m = full.exec(d))) return ret(m[1] !== '-', m[2] || '', m[3] || '', 'full')
if ((m = dec.exec(d))) return ret(m[1] !== '-', m[2] || '', '' , 'dec')
if ((m = frac.exec(d))) return ret(m[1] !== '-', '', m[2] || '', 'frac')
if ((m = eng.exec(d))) {
var e = parseInt(m[4], 10)
var g = e + m[2].length
var n = m[2] + (m[3] || '')
return ret(
m[1] !== '-',
n.slice(0, Math.max(0, g)) + repeatString('0', g - n.length),
repeatString('0', -g) + n.slice(Math.max(0, g)).replace(/0*$/, ''),
'eng'
)
}
throw new TypeError('cannot split as decimal number: ' + d)
}
function splitEng(d) {
function ret(sgn, mant, exp, fmt) {
return {
positive: sgn,
mantissa: mant.replace(/0*$/, ''),
exponent: exp,
format: fmt
}
}
d = '' + d
var full = /^([+-]?)([1-9]\d*)\.(\d*?)0*$/
var dec = /^([+-]?)([1-9]\d*)$/
var frac = /^([+-]?)(?:0?)\.(0*[1-9]\d*?)0*$/
var eng = /^([+-]?)([1-9]\d*)(?:\.(\d+?)0*)?e([+-]?\d+)$/
var lead = /^0*/
var m = null
if ((m = full.exec(d))) {
var i = m[2] || ''
var f = m[3] || ''
var x = i + f
return ret(m[1] !== '-',
x.replace(lead, ''),
i.length - lead.exec(x)[0].length - 1,
'full')
}
if ((m = dec.exec(d))) {
return ret(m[1] !== '-', m[2], m[2].length - 1, 'dec')
}
if ((m = frac.exec(d))) {
return ret(m[1] !== '-', m[2].replace(lead, ''), -lead.exec(m[2])[0].length - 1, 'frac')
}
if ((m = eng.exec(d))) {
var e = parseInt(m[4], 10)
var g = e + m[2].length - 1
var n = m[2] + (m[3] || '')
return ret(m[1] !== '-', n, g, 'eng')
}
throw new TypeError('cannot split as decimal number: ' + d)
}
function elenSmallDec(d) {
if (!(-1 < d && d < 1)) throw new TypeError('not a small number within (-1, 1): ' + d)
if (d == 0) return '0'
var s = splitDec(d)
if (s.positive) {
return '=' + s.fractional + '-'
} else {
return '-' + inverseElen(s.fractional) + '='
}
}
function elenLargeDec(d) {
if (d == 0) return '0'
if (d == -Infinity) return '-'
if (d == Infinity) return '=>'
var s = splitDec(d)
if (s.positive) {
return (s.integral ? elenInt(s.integral) : '0') + s.fractional
} else {
return (s.integral ? elenInt('-' + s.integral) : '-9') + inverseElen(s.fractional) + '='
}
}
function elenDec(d) {
if (d == '0') return '0'
if (d == '-Infinity') return '-'
if (d == 'Infinity') return '=>'
var s = splitEng(d)
if (s.positive) {
return '=' + elenInt(s.exponent) + s.mantissa
} else {
return '-' + inverseElen(elenInt(s.exponent) + s.mantissa) + '='
}
}
function cerealEscape(s) {
return ('' + s).replace(/([\x00-!])/g, '!$1')
}
function keyvals(x) {
var result = []
var keys = Object.keys(x)
keys.sort()
keys.forEach(function (k) {
result.push(k)
result.push(x[k])
})
return result
}
function cerealise(x, depth) {
var prefix = depth ? ' ' : ''
if (x == null) {
return prefix + '~'
}
if (typeof x == 'boolean') {
return prefix + (x ? '#1' : '#0')
}
if (typeof x == 'number') {
return prefix + elenDec(x) // result guaranteed to not need cerealEscape
}
if (typeof x == 'string') {
return prefix + '^' + cerealEscape(x)
}
if (Array.isArray(x)) {
return prefix + '{' + x.map(recur).join('') + ' !}'
}
// TODO: Objects should be able to define their own order-preserving
// serialisation, consisting of the above forms or arrays with quoted
// elements (recursively).
// Under debate: The unnamed class sorts last and class names should not
// the '{' character. I guess we can live with that.
if (x.constructor === ({}).constructor) {
return prefix + '`' + cerealise(keyvals(x))
} else {
var ctor = x.constructor && x.constructor.name || 'null'
return prefix + '`' + cerealEscape(ctor) + cerealise(keyvals(x))
}
function recur(x) {
return cerealise(x, (depth || 0) + 1)
}
}
function genNat(end) {
return function () {
return randNat(end)
}
}
function genInt(start, end) {
return function () {
return randInt(start, end)
}
}
function genUnif(start, end) {
return function () {
return randUnif(start, end)
}
}
function genArray(randElem) {
return function () {
return range(randInt(1, 5)).map(randElem)
}
}
var randString = (function () {
function charsWeighted(weight, minimum, maximum) {
var a = minimum.charCodeAt(0)
var b = maximum.charCodeAt(0) + 1
return [weight * (b - a), genInt(a, b).map(String.fromCharCode)]
}
var gen = genChooseWeighted([
charsWeighted( 1, '\x00', '\x31'), // control chars 0..31
charsWeighted( 3, ' ', ' '),
charsWeighted( 3, '!', '@'),
charsWeighted( 5, 'A', 'Z'),
charsWeighted( 3, '[', '`'),
charsWeighted(200, 'a', 'z'),
charsWeighted( 3, '{', '~'),
charsWeighted( 1, '\x7f', '\x7f'), // DEL control char
charsWeighted( 1, '\u00a0', '\u02af')
])
return function () {
var n = Math.floor(Math.pow(10, randUnif(-1, 2)))
return range(n).map(gen).join('')
}
}())
var randIdentifier = (function () {
function charsWeighted(weight, minimum, maximum) {
var a = minimum.charCodeAt(0)
var b = maximum.charCodeAt(0) + 1
return [weight * (b - a), genInt(a, b).map(String.fromCharCode)]
}
var randAlpha = genChooseWeighted([
charsWeighted(1, 'A', 'Z'),
charsWeighted(1, '_', '_'),
charsWeighted(1, 'a', 'z')
])
var randAlnum = genChooseWeighted([
charsWeighted(1, 'A', 'Z'),
charsWeighted(1, '_', '_'),
charsWeighted(1, 'a', 'z'),
charsWeighted(1, '0', '9')
])
return function randIdentifier() {
var s = randAlpha()
return s + range(randNat(3)).map(randAlnum).join('')
}
}())
// function randString() {
// return range(Math.floor(Math.pow(2, randInt(0, 6))) - 1).map(function (_) {
// return String.fromCharCode(randInt(0, 1024))
// }).join('')
// }
function genObject(keyGen, valGen) {
return function () {
var n = randInt(1, 6)
var result = {}
for (var i = 0; i < n; i++) {
var k = keyGen()
result[k] = valGen()
}
return result
}
}
function randAny() {
return _randAny()
}
function randObject() {
return _randObject()
}
var _randAny = genChooseWeighted([
[1, constantly(null)],
[1, randBool],
[5, randFloat],
[5, randString],
[3, genArray(randAny)],
[1, randObject]
])
var _randObject = genObject(randIdentifier, randAny)
function testElen() {
this.check(eq, '0', elenNat, 0)
this.check(eq, '1', elenNat, 1)
this.check(eq, '2', elenNat, 2)
this.check(eq, '3', elenNat, 3)
this.check(eq, '4', elenNat, 4)
this.check(eq, '9', elenNat, 9)
this.check(eq, '=110', elenNat, 10)
this.check(eq, '=111', elenNat, 11)
this.check(eq, '=112', elenNat, 12)
this.check(eq, '=119', elenNat, 19)
this.check(eq, '=120', elenNat, 20)
this.check(eq, '=199', elenNat, 99)
this.check(eq, '=2100', elenNat, 100)
this.check(eq, '=2999', elenNat, 999)
this.check(eq, '=31000', elenNat, 1000)
this.check(eq, '=39999', elenNat, 9999)
this.check(eq, '=410000', elenNat, 10000)
this.check(eq, '=91234567890', elenNat, 1234567890)
this.check(eq, '==11012345678900', elenNat, '12345678900')
this.check(eq, '==1991234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890',
elenNat, '1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890')
this.check(eq, '==210012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901',
elenNat, '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901')
this.check(eq, '---8008765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098765432109',
elenInt, '-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890')
this.check(eq, '---789987654321098765432109876543210987654321098765432109876543210987654321098765432109876543210987654321098',
elenInt, '-12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901')
this.check(eq, '---88987654321098', elenInt, -12345678901)
this.check(eq, '---88987654321099', elenInt, -12345678900)
this.check(eq, '---88987654321100', elenInt, -12345678899)
this.check(eq, '--888', elenInt, -11)
this.check(eq, '--889', elenInt, -10)
this.check(eq, '-0', elenInt, -9)
this.check(eq, '-7', elenInt, -2)
this.check(eq, '-8', elenInt, -1)
this.check(eq, '0', elenInt, 0)
this.check(eq, '1', elenInt, 1)
this.check(eq, '2', elenInt, 2)
this.check(eq, '9', elenInt, 9)
this.check(eq, '=110', elenInt, 10)
this.check(eq, '=111', elenInt, 11)
this.check(eq, '==11012345678899', elenInt, 12345678899)
this.check(eq, '==11012345678900', elenInt, 12345678900)
this.check(eq, '==11012345678901', elenInt, 12345678901)
this.check(eq, '==1991234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890',
elenInt, '1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890')
this.check(eq, '==210012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901',
elenInt, '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901')
this.check(eq, '-0004=', elenSmallDec, -0.9995)
this.check(eq, '-000=', elenSmallDec, -0.999)
this.check(eq, '-9876=', elenSmallDec, -0.0123)
this.check(eq, '-98=', elenSmallDec, -0.01)
this.check(eq, '-99876=', elenSmallDec, -0.00123)
this.check(eq, '-998766=', elenSmallDec, -0.001233)
this.check(eq, '-999876=', elenSmallDec, -0.000123)
this.check(eq, '0', elenSmallDec, 0)
this.check(eq, '=000123-', elenSmallDec, 0.000123)
this.check(eq, '=001233-', elenSmallDec, 0.001233)
this.check(eq, '=00123-', elenSmallDec, 0.00123)
this.check(eq, '=01-', elenSmallDec, 0.01)
this.check(eq, '=0123-', elenSmallDec, 0.0123)
this.check(eq, '=999-', elenSmallDec, 0.999)
this.check(eq, '=9995-', elenSmallDec, 0.9995)
this.check(eq, '-', elenLargeDec, -Infinity)
this.check(eq, '---8888999999999994=', elenLargeDec, -100000000000.5)
this.check(eq, '---889899999999994=', elenLargeDec, -10000000000.5)
this.check(eq, '--78994=', elenLargeDec, -100.5)
this.check(eq, '--8894=', elenLargeDec, -10.5)
this.check(eq, '-6854=', elenLargeDec, -3.145)
this.check(eq, '-685=', elenLargeDec, -3.14)
this.check(eq, '-898=', elenLargeDec, -1.01)
this.check(eq, '-8=', elenLargeDec, -1)
this.check(eq, '-998=', elenLargeDec, -0.01)
this.check(eq, '-99998766=', elenLargeDec, -0.0001233)
this.check(eq, '-9999876=', elenLargeDec, -0.000123)
this.check(eq, '0', elenLargeDec, 0)
this.check(eq, '0000123', elenLargeDec, 0.000123)
this.check(eq, '00001233', elenLargeDec, 0.0001233)
this.check(eq, '001', elenLargeDec, 0.01)
this.check(eq, '1', elenLargeDec, 1)
this.check(eq, '101', elenLargeDec, 1.01)
this.check(eq, '314', elenLargeDec, 3.14)
this.check(eq, '3145', elenLargeDec, 3.145)
this.check(eq, '=1105', elenLargeDec, 10.5)
this.check(eq, '=21005', elenLargeDec, 100.5)
this.check(eq, '=910000000005', elenLargeDec, 1000000000.5)
this.check(eq, '==110100000000005', elenLargeDec, 10000000000.5)
this.check(eq, '==1111000000000005', elenLargeDec, 100000000000.5)
this.check(eq, '=>', elenLargeDec, Infinity)
this.check(eq, '-', elenDec, -Infinity)
this.check(eq, '--8898=', elenDec, -1e10)
this.check(eq, '-08=', elenDec, -1e9)
this.check(eq, '-985=', elenDec, -1.4)
this.check(eq, '-986=', elenDec, -1.3)
this.check(eq, '-98=', elenDec, -1)
this.check(eq, '-=1876=', elenDec, -0.123)
this.check(eq, '-=2876=', elenDec, -0.0123)
this.check(eq, '-=38766=', elenDec, -0.001233)
this.check(eq, '-=3876=', elenDec, -0.00123)
this.check(eq, '0', elenDec, 0)
this.check(eq, '=-6123', elenDec, 0.00123)
this.check(eq, '=-61233', elenDec, 0.001233)
this.check(eq, '=-7123', elenDec, 0.0123)
this.check(eq, '=-8123', elenDec, 0.123)
this.check(eq, '=01', elenDec, 1)
this.check(eq, '=013', elenDec, 1.3)
this.check(eq, '=014', elenDec, 1.4)
this.check(eq, '=91', elenDec, 1e9)
this.check(eq, '==1101', elenDec, 1e10)
this.check(eq, '=>', elenDec, Infinity)
}
function randTestElen() {
for (var i = 0; i < 1000; i++) {
var xs = [randNat(), randNat(), randNat(), randNat()]
this.check(eq, sorted(xs), sorted, xs, comparing(elenNat))
}
for (var i = 0; i < 1000; i++) {
var xs = [randInt(), randInt(), randInt(), randInt()]
this.check(eq, sorted(xs), sorted, xs, comparing(elenInt))
}
for (var i = 0; i < 1000; i++) {
var xs = [randUnif(-1, 1), randUnif(-1, 1), randUnif(-1, 1), randUnif(-1, 1)]
this.check(eq, sorted(xs), sorted, xs, comparing(elenSmallDec))
}
for (var i = 0; i < 1000; i++) {
var xs = [Math.pow(10, randUnif(20)) * randInt(-1, 2),
Math.pow(10, randUnif(20)) * randInt(-1, 2),
Math.pow(10, randUnif(20)) * randInt(-1, 2),
Math.pow(10, randUnif(20)) * randSign(),
Math.pow(10, randUnif(20)) * randSign(),
Math.pow(10, randUnif(20)) * randSign()]
this.check(eq, sorted(xs), sorted, xs, comparing(elenLargeDec))
}
for (var i = 0; i < 1000; i++) {
var e = Math.pow(2, -52)
var x = randFloat()
var xs = [x, (1 + e) * x, (1 + 2 * e) * x, 0.5 * (2 - e) * x, (1 - e) * x]
this.check(eq, sorted(xs), sorted, xs, comparing(elenDec))
}
for (var i = 0; i < 1000; i++) {
var xs = [randDecimal(), randDecimal(), randDecimal(), randDecimal(), randDecimal()]
this.check(eq, sorted(xs), sorted, xs, comparing(elenDec))
}
for (var i = 0; i < 1000; i++) {
var xs = [randSimpleDecimal(), randSimpleDecimal(), randSimpleDecimal(), randSimpleDecimal(), randSimpleDecimal()]
this.check(eq, sorted(xs), sorted, xs, comparing(elenDec))
}
for (var i = 0; i < 1000; i++) {
var xs = [randIntegral(), randIntegral(), randIntegral(), randIntegral(), randIntegral()]
this.check(eq, sorted(xs), sorted, xs, comparing(elenDec))
}
for (var i = 0; i < 1000; i++) {
var xs = [randInt(), randInt(), randInt(), randInt(), randInt()]
this.check(eq, sorted(xs), sorted, xs, comparing(elenDec))
}
for (var i = 0; i < 1000; i++) {
var xs = [randDenormal(), randDenormal(), randDenormal(), randDenormal(), randDenormal()]
this.check(eq, sorted(xs), sorted, xs, comparing(elenDec))
}
for (var i = 0; i < 1000; i++) {
var xs = [randFloat(), randFloat(), randFloat(), randFloat(), randFloat()]
this.check(eq, sorted(xs), sorted, xs, comparing(elenDec))
}
}
function testCerealise() {
for (var i = 0; i < 1000; i++) {
var xs = genArray(randAny)()
this.check(eq, sorted(xs), sorted, xs, comparing(cerealise))
}
for (var i = 0; i < 100; i++) {
var xs = genArray(genArray(genInt()))()
this.check(eq, sorted(xs), sorted, xs, comparing(cerealise))
}
function testOne(xs) {
this.check(eq, sorted(xs), sorted, xs, comparing(cerealise))
}
testOne.call(this, [[1, 2, 3], [1, 1, 2], [2, 1, 2], [10, 1, 2], [1.1, 1.2, 1]])
testOne.call(this, [[[1], 2, 3], [1, [1], 2], [[2], [1], 2], [10, 1, 2], [1.1, 1.2, 1]])
for (var i = 0; i < 100; i++) {
var xs = genArray(genArray(randFloat))()
this.check(eq, sorted(xs), sorted, xs, comparing(cerealise))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment