'use strict' function tryRequire(name) { try { return require(name) } catch (_) { console.warn(`Skipping '${name}'`) } } const I = require('infestines') const L = require('../dist/partial.lenses.cjs') const P = tryRequire('ramda-lens') const M = tryRequire('immer') const R = require('ramda') const O = tryRequire('flunc-optics') const U = tryRequire('unchanged') const K = tryRequire('optika') const sprintf = require('sprintf-js').sprintf const _get = tryRequire('lodash.get') const xyz = {x: 1, y: 2, z: 3} const xs = [1, 2, 3] const axay = [{x: [{y: 1}]}] const xs10 = Array(10).fill(1) const xs100 = Array(100).fill(1) const xs1000 = Array(1000).fill(1) const xs10000 = Array(10000).fill(1) const xs100000 = Array(100000).fill(1) const ids = R.range(0, 10000).map(i => ({id: i, value: i})) const L_find_id_5000 = L.find(o => o.id === 5000, {hint: 5000}) const xs10o = Object.assign({}, xs10) const xs100o = Object.assign({}, xs100) const xs1000o = Object.assign({}, xs1000) const xs10000o = Object.assign({}, xs10000) const xsss100 = Array(100).fill([[1]]) const xyzn = {x: {y: {z: 1}}} const l_0 = R.lensIndex(0) const l_1 = R.lensIndex(1) const l_50 = R.lensIndex(50) const l_x = R.lensProp('x') const l_y = R.lensProp('y') const l_z = R.lensProp('z') const l_0_x_0_y = R.compose( l_0, l_x, l_0, l_y ) const f_0_x_0_y = L.toFunction([0, 'x', 0, 'y']) const l_0x0y = R.lensPath([0, 'x', 0, 'y']) const l_xyz = R.lensPath(['x', 'y', 'z']) const f_xyz = L.toFunction(['x', 'y', 'z']) const l_x_y_z = R.compose( l_x, l_y, l_z ) const K_0_x_0_y = K && K.idx(0) .key('x') .idx(0) .key('y') const K_xyz = K && K.key('x') .key('y') .key('z') const K_t_t_t = K && K.traversed() .traversed() .traversed() const L_e_e_e = L.toFunction([L.elems, L.elems, L.elems]) const o_x_y_z = O && R.compose( O.Lens.atObject('x'), O.Prism._Just, O.Lens.atObject('y'), O.Prism._Just, O.Lens.atObject('z'), O.Prism._Just ) const id = I.id const always = I.always const inc = x => x + 1 const dec = x => x - 1 const add = (x, y) => x + y const addC = x => y => x + y const L_get_1 = L.get(1) const R_nth_1 = R.nth(1) const U_get_1 = U && U.get(1) const L_get_y = L.get('y') const R_prop_y = R.prop('y') const U_get_y = U && U.get('y') const Sum = {empty: () => 0, concat: add} const List = {empty: always([]), concat: (x, y) => x.concat(y)} const toList = x => (x !== undefined ? [x] : []) const valueOr1 = L.valueOr(1) const define1 = L.define(1) const defaults1 = L.defaults(1) const valueOr0x0y = [L.valueOr([]), 0, 'x', 0, 'y'] const define0x0y = [L.define([]), 0, 'x', 0, 'y'] const defaults0x0y = [L.defaults([]), 0, 'x', 0, 'y'] const flatten = [ L.optional, L.lazy(rec => { const elems = L.toFunction([L.elems, rec]) const values = L.toFunction([L.values, rec]) return L.choose( x => x instanceof Array ? elems : x instanceof Object ? values : L.identity ) }) ] const naiveBST = L.rewrite(n => { if (undefined !== n.value) return n const s = n.smaller, g = n.greater if (!s) return g if (!g) return s return L.set(search(s.key), s, g) }) const search = key => L.lazy(rec => { const smaller = ['smaller', rec] const greater = ['greater', rec] const insert = L.defaults({key}) return [ naiveBST, L.choose(n => { if (!n || key === n.key) return insert return key < n.key ? smaller : greater }) ] }) const valueOf = key => [search(key), 'value'] const fromPairs = R.reduce( (t, kv) => L.set(valueOf(kv[0]), kv[1], t), undefined ) const values = L.lazy(rec => [ L.optional, naiveBST, L.branch({ smaller: rec, value: L.identity, greater: rec }) ]) const bstPairs = [[3, 'g'], [2, 'a'], [1, 'm'], [4, 'i'], [5, 'c']] const bst = fromPairs(bstPairs) const incNum = x => (typeof x === 'number' ? x + 1 : x) const nested = [{x: 1, y: [2, {d: 3}, 4], z: {a: 5}}] const everywhere = [ L.optional, L.lazy(rec => { const elems = L.seq([L.elems, rec], L.identity) const values = L.seq([L.values, rec], L.identity) return L.choose( x => x instanceof Array ? elems : x instanceof Object ? values : L.identity ) }) ] const xyzs = L.seq('x', 'y', 'z') const pi = [3, 1, 4, 1, 5] const aEb = L.orElse('b', 'a') const aEbEc = L.orElse(L.orElse('c', 'b'), 'a') const abM = L.choice('a', 'b') const abS = L.choices('a', 'b') const abcM = L.choice('a', 'b', 'c') const abcS = L.choices('a', 'b', 'c') const Ident = { map: (xy, x) => xy(x), ap: (xy, x) => xy(x), of: x => x, chain: (xy, x) => xy(x) } const Benchmark = require('benchmark') Benchmark.options.maxTime = Number(process.argv[2]) || Benchmark.options.maxTime const pattern = new RegExp(process.argv[3] || '') const dropped = [ M ? '' : 'M', P ? '' : 'P', O ? '' : 'O', K ? '' : 'K', U ? '' : 'U', _get ? '' : '_get' ].filter(I.id) R.forEach( bs => { if (bs.find(s => pattern.test(s))) { global.gc() const s = new Benchmark.Suite() bs.reverse().forEach(b => { b = b instanceof Array ? b : [b] const code = b[0].replace(/[ \n]+/g, ' ') const note = b[1] || '' if (-1 === dropped.findIndex(p => code.indexOf(p) === 0)) s.add(code, eval('() => ' + code), {note}) }) s.on('complete', complete) s.run() } }, [ [`L.get(L_find_id_5000, ids)`], [ `L.foldr(add, 0, L.elems, xs100)`, `O.Fold.foldrOf(O.Traversal.traversed, addC, 0, xs100)`, `R.reduceRight(add, 0, xs100)`, `xs100.reduceRight(add, 0)` ], [ `L.foldr(add, 0, L.elems, xs100000)`, [ `O.Fold.foldrOf(O.Traversal.traversed, addC, 0, xs100000)`, 'STACK OVERFLOW' ], `R.reduceRight(add, 0, xs100000)`, `xs100000.reduceRight(add, 0)` ], [ `K.traversed().sumOf(xs100)`, `L.concat(Sum, L.elems, xs100)`, `L.foldl(add, 0, L.elems, xs100)`, `L.sum(L.elems, xs100)`, `O.Fold.foldlOf(O.Traversal.traversed, addC, 0, xs100)`, `O.Fold.sumOf(O.Traversal.traversed, xs100)`, `P.sumOf(P.traversed, xs100)`, `R.reduce(add, 0, xs100)`, `R.sum(xs100)`, `xs100.reduce(add, 0)`, `{let s=0; for (let i=0; i {x0.forEach(x1 => {acc = acc.concat(x1)})}); return acc}` ], [ `L.collect(L.flatten, xsss100)`, `L.collect(L.leafs, xsss100)`, `R.flatten(xsss100)` ], [ `K.traversed().over(xs, inc)`, `L.modify(L.elems, inc, xs)`, `L.traverse(Ident, inc, L.elems, xs)`, `M.produce(xs, xs => { for (let i=0, n=xs.length; i { for (let i=0, n=xs1000.length; i { xsss100.forEach(xss => xss.forEach(xs => { for (let i=0, n=xs.length; i x0.map(x1 => x1.map(inc)))` ], [ `K.idx(1).get(xs)`, `L.get(1, xs)`, `R.nth(1, xs)`, `R.view(l_1, xs)`, `U.get(1, xs)`, `_get(xs, 1)` ], [ `L.get(1)(xs)`, `L_get_1(xs)`, `R.nth(1)(xs)`, `R_nth_1(xs)`, `U.get(1)(xs)`, `U_get_1(xs)` ], [ `K.idx(1).set(xs, 0)`, `L.set(1, 0, xs)`, `M.produce(xs, xs => { xs[1] = 0 })`, `R.set(l_1, 0, xs)`, `R.update(1, 0, xs)`, `U.set(1, 0, xs)`, `xs.map((x, i) => i === 1 ? 0 : x)`, `{let ys = xs.slice(); ys[1] = 0; return ys}` ], [ `K.key('y').get(xyz)`, `L.get('y', xyz)`, `R.prop('y', xyz)`, `R.view(l_y, xyz)`, `U.get('y', xyz)`, `_get(xyz, 'y')` ], [ `L.get('y')(xyz)`, `L_get_y(xyz)`, `R.prop('y')(xyz)`, `R_prop_y(xyz)`, `U.get('y')(xyz)`, `U_get_y(xyz)` ], [ `K.key('y').set(xyz, 0)`, `L.set('y', 0, xyz)`, `M.produce(xyz, xyz => { xyz.y = 0 })`, `R.assoc('y', 0, xyz)`, `R.set(l_y, 0, xyz)`, `U.set('y', 0, xyz)` ], [ `K_0_x_0_y.get(axay)`, `L.get([0, 'x', 0, 'y'], axay)`, `L.get(f_0_x_0_y, axay)`, `R.path([0, 'x', 0, 'y'], axay)`, `R.view(l_0_x_0_y, axay)`, `R.view(l_0x0y, axay)`, `U.get([0, 'x', 0, 'y'], axay)`, `_get(axay, [0, 'x', 0, 'y'])` ], [ `K_0_x_0_y.set(axay, 0)`, `L.set([0, 'x', 0, 'y'], 0, axay)`, `M.produce(axay, axay => { axay[0].x[0].y = 0 })`, `R.assocPath([0, 'x', 0, 'y'], 0, axay)`, `R.set(l_0_x_0_y, 0, axay)`, `R.set(l_0x0y, 0, axay)`, `U.set([0, 'x', 0, 'y'], 0, axay)` ], [ `K_0_x_0_y.over(axay, inc)`, `L.modify([0, 'x', 0, 'y'], inc, axay)`, `M.produce(axay, axay => { axay[0].x[0].y++ })`, `R.over(l_0_x_0_y, inc, axay)`, `R.over(l_0x0y, inc, axay)` ], [ `L.remove(1, xs)`, `M.produce(xs, xs => { xs.splice(1, 1) })`, `R.remove(1, 1, xs)`, `U.remove(1, xs)` ], [ `L.remove('y', xyz)`, `M.produce(xyz, xyz => { delete xyz.y })`, `R.dissoc('y', xyz)`, `U.remove('y', xyz)` ], [ `K_xyz.get(xyzn)`, `L.get(['x', 'y', 'z'], xyzn)`, `L.get(f_xyz, xyzn)`, `O.Getter.view(o_x_y_z, xyzn)`, `R.path(['x', 'y', 'z'], xyzn)`, `R.view(l_x_y_z, xyzn)`, `R.view(l_xyz, xyzn)`, `U.get(['x', 'y', 'z'], xyzn)`, `_get(xyzn, ['x', 'y', 'z'])` ], [ `K_xyz.set(xyzn, 0)`, `L.set(['x', 'y', 'z'], 0, xyzn)`, `M.produce(xyzn, xyzn => { xyzn.x.y.z = 0 })`, `O.Setter.set(o_x_y_z, 0, xyzn)`, `R.assocPath(['x', 'y', 'z'], 0, xyzn)`, `R.set(l_x_y_z, 0, xyzn)`, `R.set(l_xyz, 0, xyzn)`, `U.set(['x', 'y', 'z'], 0, xyzn)` ], [ `L.getAs(x => x > 3 ? x : undefined, L.elems, xs100)`, `R.find(x => x > 3, xs100)`, `O.Fold.findOf(O.Traversal.traversed, x => x > 3, xs100)` ], [ `L.getAs(x => x < 3 ? x : undefined, L.elems, xs100)`, `R.find(x => x < 3, xs100)`, [ `O.Fold.findOf(O.Traversal.traversed, x => x < 3, xs100)`, 'NO SHORTCUT EVALUATION' ] ], [ `L.sum([L.elems, x => x+1, x => x*2, L.when(x => x%2 === 0)], xs1000)`, `R.pipe(R.map(x => x+1), R.map(x => x*2), R.filter(x => x%2 === 0), R.sum)(xs1000)`, `R.transduce(R.compose(R.map(x => x+1), R.map(x => x*2), R.filter(x => x%2 === 0)), (x, y) => x+y, 0, xs1000)` ], [ `L.forEach(I.id, L.elems, xs1000)`, `R.forEach(I.id, xs1000)`, `xs1000.forEach(I.id)` ], [ `L.forEach(I.id, L_e_e_e, xsss100)`, `L.forEach(I.id, [L.elems, L.elems, L.elems], xsss100)`, `R.forEach(R.forEach(R.forEach(I.id)), xsss100)`, `xsss100.forEach(xss100 => xss100.forEach(xs100 => xs100.forEach(I.id)))` ], [ `L.minimum(L.elems, xs10000)`, `L.minimumBy(x => -x, L.elems, xs10000)`, `R.reduce(R.min, -Infinity, xs10000)`, `R.reduceRight(R.min, -Infinity, xs10000)`, `R.reduce(R.minBy(x => -x), Infinity, xs10000)`, `R.reduceRight(R.minBy(x => -x), Infinity, xs10000)` ], [`L.mean(L.elems, xs1000)`, `R.mean(xs1000)`], [`L.remove(50, xs100)`, `R.remove(50, 1, xs100)`], [ `K.idx(50).set(xs100, 2)`, `L.set(50, 2, xs100)`, `R.set(l_50, 2, xs100)`, `R.update(50, 2, xs100)` ], [`L.remove(5000, xs10000)`, `R.remove(5000, 1, xs10000)`], [`L.set(5000, 2, xs10000)`, `R.update(5000, 2, xs10000)`], [ `L.modify(L.values, inc, xyz)`, `L.traverse(Ident, inc, L.values, xyz)`, `R.map(inc, xyz)` ], [ `L.modify(L.values, inc, xs10o)`, `L.traverse(Ident, inc, L.values, xs10o)`, `R.map(inc, xs10o)` ], [ `L.modify(L.values, inc, xs100o)`, `L.traverse(Ident, inc, L.values, xs100o)`, `R.map(inc, xs100o)` ], [ `L.modify(L.values, inc, xs1000o)`, `L.traverse(Ident, inc, L.values, xs1000o)`, `R.map(inc, xs1000o)` ], [ `L.modify(L.values, inc, xs10000o)`, `L.traverse(Ident, inc, L.values, xs10000o)`, `R.map(inc, xs10000o)` ], [ `L.modify(L.leafs, inc, nested)`, `L.modify(everywhere, incNum, nested)`, `L.modify(flatten, inc, nested)`, `L.traverse(Ident, inc, L.leafs, nested)`, `L.traverse(Ident, inc, flatten, nested)`, `L.traverse(Ident, incNum, everywhere, nested)` ], [ `L.modify(L.leafs, inc, xs10)`, `L.modify(everywhere, incNum, xs10)`, `L.modify(flatten, inc, xs10)`, `L.traverse(Ident, inc, L.leafs, xs10)`, `L.traverse(Ident, inc, flatten, xs10)`, `L.traverse(Ident, incNum, everywhere, xs10)` ], [ `L.modify(L.leafs, inc, xs100)`, `L.modify(everywhere, incNum, xs100)`, `L.modify(flatten, inc, xs100)`, `L.traverse(Ident, inc, L.leafs, xs100)`, `L.traverse(Ident, inc, flatten, xs100)`, `L.traverse(Ident, incNum, everywhere, xs100)` ], [ `L.modify(L.leafs, inc, xs1000)`, `L.modify(everywhere, incNum, xs1000)`, `L.modify(flatten, inc, xs1000)`, `L.traverse(Ident, inc, L.leafs, xs1000)`, `L.traverse(Ident, inc, flatten, xs1000)`, `L.traverse(Ident, incNum, everywhere, xs1000)` ], [`L.set(L.seq('x', 'y', 'z'), 1, undefined)`, `L.set(xyzs, 1, undefined)`], [ `L.modify(values, x => x + x, bst)`, `L.traverse(Ident, x => x + x, values, bst)` ], [`L.collect(values, bst)`], [`fromPairs(bstPairs)`], [`L.get(L.slice(100, -100), xs10000)`, `R.slice(100, -100, xs10000)`], [`L.get(L.slice(1, -1), xs)`, `R.slice(1, -1, xs)`], [`L.get(L.slice(10, -10), xs100)`, `R.slice(10, -10, xs100)`], [`L.get(L.defaults(1), 2)`, `L.get(L.defaults(1), undefined)`], [`L.get(defaults1, 2)`, `L.get(defaults1, undefined)`], [`L.get(L.define(1), 2)`, `L.get(L.define(1), undefined)`], [`L.get(define1, 2)`, `L.get(define1, undefined)`], [ `L.get(L.valueOr(1), 2)`, `L.get(L.valueOr(1), null)`, `L.get(L.valueOr(1), undefined)` ], [ `L.get(valueOr1, 2)`, `L.get(valueOr1, null)`, `L.get(valueOr1, undefined)` ], [`L.concatAs(toList, List, L.elems, xs100)`], [ `L.modify(L.flatten, inc, xsss100)`, `L.modify(L.leafs, inc, xsss100)`, `L.traverse(Ident, inc, L.flatten, xsss100)`, `L.traverse(Ident, inc, L.leafs, xsss100)` ], [ `L.getAs(x => x > 3 ? x : undefined, L.elems, pi)`, `R.find(x => x > 3, pi)`, `O.Fold.findOf(O.Traversal.traversed, x => x > 3, pi)` ], [ `L.get(L.find(x => x !== 1, {hint: 0}), xs)`, `L.get(L.find(x => x !== 1), xs)`, `R.find(x => x !== 1, xs)` ], [ `L.get(L.find(x => x !== 1, {hint: 0}), xs100)`, `L.get(L.find(x => x !== 1), xs100)`, `R.find(x => x !== 1, xs100)` ], [ `L.get(L.find(x => x !== 1, {hint: 0}), xs1000)`, `L.get(L.find(x => x !== 1), xs1000)`, `R.find(x => x !== 1, xs1000)` ], [ `L.get(defaults0x0y, axay)`, `L.get(define0x0y, axay)`, `L.get(valueOr0x0y, axay)` ], [ `L.set(defaults0x0y, 1, undefined)`, `L.set(define0x0y, 1, undefined)`, `L.set(valueOr0x0y, 1, undefined)` ], [`L.set(L.findWith('x'), 2, axay)`], [ `L.get(L.orElse('a', 'b'), {x: 1})`, `L.get(L.choices('a', 'b'), {x: 1})`, `L.get(abS, {x: 1})`, `L.get(abM, {x: 1})`, `L.get(aEb, {x: 1})` ], [ `L.get(L.choice('a', 'b', 'c'), {x: 1})`, `L.get(L.choices('a', 'b', 'c'), {x: 1})`, `L.get(aEbEc, {x: 1})`, `L.get(abcM, {x: 1})`, `L.get(abcS, {x: 1})` ], [`L.set(L.props('x', 'y'), {x: 2, y: 3}, {x: 1, y: 2, z: 4})`], [ `L.transform( L.lazy(rec => L.seq(L.modifyOp(dec), L.choose(n => n === 0 ? L.identity : rec))), 100)` ] ] ) function complete() { const bs = I.seq( this, R.values, R.filter(R.is(Benchmark)), R.sortBy(R.prop('hz')), R.reverse ) const fastest = I.seq(bs, R.map(R.prop('hz')), R.reduce(R.max, 0)) bs.forEach(b => { console.log( sprintf( '%12s/s %8.2f %s%s%s', Math.round(b.hz).toLocaleString(), fastest / b.hz, b.name, b.options.note && ' -- ', b.options.note ) ) }) console.log() }