|
| 1 | +/* global BigInt */ |
| 2 | +/* eslint-disable new-cap, no-new, no-unused-expressions */ |
| 3 | + |
| 4 | +const { ZZ } = require('..') ; |
| 5 | +var benchmark = require('benchmark'); |
| 6 | +var crypto = require('crypto'); |
| 7 | +var bn = require('bn.js'); |
| 8 | +var bignum; |
| 9 | +try { |
| 10 | + bignum = require('bignum'); |
| 11 | +} catch (err) { |
| 12 | + console.log('Load bignum error: ' + err.message.split('\n')[0]); |
| 13 | +} |
| 14 | +var sjcl = require('eccjs').sjcl.bn; |
| 15 | +var bigi = require('bigi'); |
| 16 | +var BigInteger = require('js-big-integer').BigInteger; |
| 17 | +var JSBI = require('jsbi'); |
| 18 | +var SilentMattBigInteger = require('biginteger').BigInteger; |
| 19 | +var XorShift128Plus = require('xorshift.js').XorShift128Plus; |
| 20 | +var benchmarks = []; |
| 21 | + |
| 22 | +var selfOnly = process.env.SELF_ONLY; |
| 23 | +var seed = process.env.SEED || crypto.randomBytes(16).toString('hex'); |
| 24 | +console.log('Seed: ' + seed); |
| 25 | +var prng = new XorShift128Plus(seed); |
| 26 | + |
| 27 | +const NFIXTURES = 25; |
| 28 | +var fixtures = []; |
| 29 | +var findex = 0; |
| 30 | +function findexRefresh () { |
| 31 | + if (++findex === fixtures.length) findex = 0; |
| 32 | +} |
| 33 | + |
| 34 | +const filter = process.argv[3] ? new RegExp(process.argv[3], 'i') : /./; |
| 35 | + |
| 36 | +const k256 = 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f' ; |
| 37 | + |
| 38 | +function add (op, ...args) { |
| 39 | + benchmarks.push({ |
| 40 | + name: op, |
| 41 | + start: function start () { |
| 42 | + var suite = new benchmark.Suite(); |
| 43 | + |
| 44 | + console.log('Benchmarking: ' + op); |
| 45 | + |
| 46 | + Object.keys(fns).forEach(function (name) { |
| 47 | + if (!filter.test(name)) return ; |
| 48 | + if (name === 'BigInt' && typeof BigInt === 'undefined') return ; |
| 49 | + if (name === 'bignum' && bignum === undefined) return ; |
| 50 | + |
| 51 | + if (!selfOnly || name === '@aureooms/js-integer') { |
| 52 | + const opFn = fns[name][op]; |
| 53 | + if (!opFn) return; |
| 54 | + if (!(opFn instanceof Function)) throw new Error(`opFN is not a function: ${opFN}`) ; |
| 55 | + const fixture = fixtures[findex][name]; |
| 56 | + |
| 57 | + if (args.length === 1) { |
| 58 | + const key = op + '-' + args[0]; |
| 59 | + const x = fixture.args[args[0]]; |
| 60 | + const outs = fixture.outs; |
| 61 | + const testFn = () => outs[key] = opFn(x) ; |
| 62 | + suite.add(name + '#' + key, testFn, { |
| 63 | + onStart: findexRefresh, |
| 64 | + onCycle: findexRefresh |
| 65 | + }); |
| 66 | + } |
| 67 | + else if (args.length === 2) { |
| 68 | + const key = op + '-' + args[0] + '-' + args[1]; |
| 69 | + const a = fixture.args[args[0]]; |
| 70 | + const b = fixture.args[args[1]]; |
| 71 | + const outs = fixture.outs; |
| 72 | + const testFn = () => outs[key] = opFn(a, b) ; |
| 73 | + suite.add(name + '#' + key, testFn, { |
| 74 | + onStart: findexRefresh, |
| 75 | + onCycle: findexRefresh |
| 76 | + }); |
| 77 | + } |
| 78 | + else throw new Error('Too many args.') ; |
| 79 | + } |
| 80 | + }); |
| 81 | + |
| 82 | + suite |
| 83 | + .on('cycle', function (event) { |
| 84 | + console.log(String(event.target)); |
| 85 | + }) |
| 86 | + .on('complete', function () { |
| 87 | + console.log('------------------------'); |
| 88 | + console.log('Fastest is ' + this.filter('fastest')[0].name); |
| 89 | + }) |
| 90 | + .run(); |
| 91 | + |
| 92 | + console.log('========================'); |
| 93 | + } |
| 94 | + }); |
| 95 | +} |
| 96 | + |
| 97 | +function start () { |
| 98 | + var re = process.argv[2] ? new RegExp(process.argv[2], 'i') : /./; |
| 99 | + |
| 100 | + benchmarks |
| 101 | + .filter(function (b) { |
| 102 | + return re.test(b.name); |
| 103 | + }) |
| 104 | + .forEach(function (b) { |
| 105 | + b.start(); |
| 106 | + }); |
| 107 | +} |
| 108 | + |
| 109 | +if (/fast/i.test(process.argv[4])) { |
| 110 | + console.log('Running in fast mode...'); |
| 111 | + benchmark.options.minTime = 0.3; |
| 112 | + benchmark.options.maxTime = 1; |
| 113 | + benchmark.options.minSamples = 3; |
| 114 | +} else { |
| 115 | + benchmark.options.minTime = 1; |
| 116 | +} |
| 117 | + |
| 118 | +const fns = { |
| 119 | + BigInt: { |
| 120 | + from10: s => BigInt(s), |
| 121 | + from16: s => BigInt('0x'+s), |
| 122 | + to10: x => x.toString(10), |
| 123 | + to16: x => x.toString(16), |
| 124 | + add: (a,b) => a + b, |
| 125 | + sub: (a,b) => a - b, |
| 126 | + mul: (a,b) => a * b, |
| 127 | + sqr: x => x**2n, |
| 128 | + pow: (a,b) => a**b, |
| 129 | + div: (a,b) => a / b, |
| 130 | + mod: (a, b) => { |
| 131 | + const remainder = a % b; |
| 132 | + return remainder < 0 ? remainder + b : remainer; |
| 133 | + }, |
| 134 | + |
| 135 | + toRed: x => x % k256_BigInt, |
| 136 | + fromRed: x => x, |
| 137 | + sqrm: x => (a**2) % k256_BigInt, |
| 138 | + powm: (a,b) => (a**b) % k256_BigInt, |
| 139 | + } , |
| 140 | + 'bn.js': { |
| 141 | + from10: s => new bn(s, 10), |
| 142 | + from16: s => new bn(s, 16), |
| 143 | + to10: x => x.toString(10), |
| 144 | + to16: x => x.toString(16), |
| 145 | + add: (a,b) => a.add(b), |
| 146 | + sub: (a,b) => a.sub(b), |
| 147 | + mul: (a,b) => a.mul(b), |
| 148 | + sqr: x => x.mul(x), |
| 149 | + pow: (a,b) => a.pow(b), |
| 150 | + div: (a,b) => a.div(b), |
| 151 | + mod: (a,b) => a.mod(b), |
| 152 | + |
| 153 | + toRed: x => x.toRed(bn.red('k256')), |
| 154 | + fromRed: x => x.fromRed(), |
| 155 | + sqrm: x => x.redSqr(), |
| 156 | + powm: (a,b) => a.redPow(b), |
| 157 | + invm: x => x.redInvm(), |
| 158 | + |
| 159 | + gcd: (a,b) => a.gcd(b), |
| 160 | + egcd: (a,b) => a.egcd(b), |
| 161 | + } , |
| 162 | + '@aureooms/js-integer': { |
| 163 | + from10: s => ZZ.from(s, 10), |
| 164 | + from16: s => ZZ.from(s, 16), |
| 165 | + to10: x => x.toString(10), |
| 166 | + to16: x => x.toString(16), |
| 167 | + add: (a,b) => a.add(b), |
| 168 | + sub: (a,b) => a.sub(b), |
| 169 | + mul: (a,b) => a.mul(b), |
| 170 | + sqr: x => x.square(), |
| 171 | + pow: (a,b) => a.pow(b), |
| 172 | + div: (a,b) => a.div(b), |
| 173 | + mod: (a,b) => a.mod(b), |
| 174 | + |
| 175 | + gcd: (a,b) => a.gcd(b), |
| 176 | + egcd: (a,b) => a.egcd(b), |
| 177 | + } , |
| 178 | + |
| 179 | + jsbi: { |
| 180 | + from10: s => JSBI.BigInt(s), |
| 181 | + from16: s => JSBI.BigInt('0x'+s), |
| 182 | + to10: x => x.toString(10), |
| 183 | + to16: x => x.toString(16), |
| 184 | + add: (a,b) => JSBI.add(a, b), |
| 185 | + sub: (a,b) => JSBI.subtract(a, b), |
| 186 | + mul: (a,b) => JSBI.multiply(a, b), |
| 187 | + sqr: x => JSBI.multiply(x, x), |
| 188 | + pow: (a, b) => JSBI.exponentiate(a, b), |
| 189 | + div: (a,b) => JSBI.divide(a, b), |
| 190 | + mod: (a, b) => { |
| 191 | + const remainder = JSBI.remainder(a,b); |
| 192 | + return JSBI.lessThan(remainder,JSBI.BigInt(0)) |
| 193 | + ? JSBI.add(remainder,b) |
| 194 | + : remainder; |
| 195 | + }, |
| 196 | + }, |
| 197 | + yaffle: { |
| 198 | + from10: s => BigInteger.BigInt(s), |
| 199 | + from16: s => BigInteger.BigInt('0x'+s), |
| 200 | + to10: x => x.toString(10), |
| 201 | + to16: x => x.toString(16), |
| 202 | + add: (a,b) => BigInteger.add(a, b), |
| 203 | + sub: (a,b) => BigInteger.subtract(a, b), |
| 204 | + mul: (a,b) => BigInteger.multiply(a, b), |
| 205 | + sqr: x => BigInteger.multiply(x, x), |
| 206 | + pow: (a, b) => BigInteger.exponentiate(a, b), |
| 207 | + div: (a,b) => BigInteger.divide(a, b), |
| 208 | + mod: (a, b) => { |
| 209 | + const remainder = BigInteger.remainder(a,b); |
| 210 | + return BigInteger.lessThan(remainder,BigInteger.BigInt(0)) |
| 211 | + ? BigInteger.add(remainder,b) |
| 212 | + : remainder; |
| 213 | + }, |
| 214 | + }, |
| 215 | + bigi: { |
| 216 | + from10: s => new bigi(s, 10), |
| 217 | + from16: s => new bigi(s, 16), |
| 218 | + to10: x => x.toString(10), |
| 219 | + to16: x => x.toString(16), |
| 220 | + add: (a,b) => a.add(b), |
| 221 | + sub: (a,b) => a.subtract(b), |
| 222 | + mul: (a,b) => a.multiply(b), |
| 223 | + sqr: x => x.square(), |
| 224 | + pow: (a,b) => a.pow(b), |
| 225 | + div: (a,b) => a.divide(b), |
| 226 | + mod: (a,b) => a.remainder(b), |
| 227 | + |
| 228 | + gcd: (a,b) => a.gcd(b), |
| 229 | + }, |
| 230 | + 'silentmatt-biginteger': { |
| 231 | + from10: s => SilentMattBigInteger.parse(s, 10), |
| 232 | + from16: s => SilentMattBigInteger.parse(s, 16), |
| 233 | + to10: x => x.toString(10), |
| 234 | + to16: x => x.toString(16), |
| 235 | + add: (a,b) => a.add(b), |
| 236 | + sub: (a,b) => a.subtract(b), |
| 237 | + mul: (a,b) => a.multiply(b), |
| 238 | + sqr: x => x.square(x), |
| 239 | + pow: (a,b) => a.pow(b), |
| 240 | + div: (a,b) => a.quotient(b), |
| 241 | + mod: (a, b) => { |
| 242 | + const remainder = a.remainder(b); |
| 243 | + return remainder.isNegative() |
| 244 | + ? remainder.add(b) |
| 245 | + : remainder; |
| 246 | + }, |
| 247 | + }, |
| 248 | + bignum: { |
| 249 | + from10: s => new bignum(s, 10), |
| 250 | + from16: s => new bignum(s, 16), |
| 251 | + to10: x => x.toString(10), |
| 252 | + to16: x => x.toString(16).replace(/^0+/, ''), |
| 253 | + add: (a,b) => a.add(b), |
| 254 | + sub: (a,b) => a.sub(b), |
| 255 | + mul: (a,b) => a.mul(b), |
| 256 | + sqr: x => x.mul(x), |
| 257 | + pow: (a,b) => a.pow(b), |
| 258 | + div: (a,b) => a.div(b), |
| 259 | + mod: (a,b) => a.mod(b), |
| 260 | + fromRed: x => x, |
| 261 | + toRed: x => x.mod(k256_bignum), |
| 262 | + powm: (a,b) => a.powm(b, k256_bignum), |
| 263 | + sqrm: x => a.pown(2, k256_bignum), |
| 264 | + invm: x => x.invertm(k256_bignum), |
| 265 | + } , |
| 266 | + sjcl: { |
| 267 | + from10: undefined, |
| 268 | + from16: s => new sjcl(s), |
| 269 | + to10: undefined, |
| 270 | + to16: x => x.toString().slice(2), |
| 271 | + add: (a,b) => a.add(b), |
| 272 | + sub: (a,b) => a.sub(b), |
| 273 | + mul: (a,b) => a.mul(b), |
| 274 | + sqr: x => x.mul(x), |
| 275 | + pow: (a,b) => a.power(b), |
| 276 | + fromRed: x => x, |
| 277 | + toRed: x => new sjcl.prime.p256k(x), |
| 278 | + invm: x => x.inverseMod(k256_sjcl), |
| 279 | + sqrm: x => x.square().fullReduce(), |
| 280 | + } |
| 281 | + |
| 282 | +} ; |
| 283 | + |
| 284 | +const k256_BigInt = fns.BigInt.from16(k256); |
| 285 | +const k256_bignum = fns.bignum.from16(k256); |
| 286 | +const k256_sjcl = fns.sjcl.from16(k256); |
| 287 | + |
| 288 | +fns.BigInt.k256 = k256_BigInt ; |
| 289 | +fns.bignum.k256 = k256_bignum ; |
| 290 | +fns.sjcl.k256 = k256_sjcl ; |
| 291 | +fns['bn.js'].k256 = 'k256' ; |
| 292 | + |
| 293 | +function newFixture ( ) { |
| 294 | + const fixture = {}; |
| 295 | + |
| 296 | + const a = prng.randomBytes(32).toString('hex'); |
| 297 | + const b = prng.randomBytes(32).toString('hex'); |
| 298 | + const aj = prng.randomBytes(768).toString('hex'); |
| 299 | + const bj = prng.randomBytes(768).toString('hex'); |
| 300 | + |
| 301 | + const a10base = new bn(a, 16).toString(10); |
| 302 | + const a16base = new bn(a, 16).toString(16); |
| 303 | + |
| 304 | + const init = fn => { |
| 305 | + const a1 = fn.from16(a); |
| 306 | + const b1 = fn.from16(b); |
| 307 | + const a1j = fn.from16(aj); |
| 308 | + const b1j = fn.from16(bj); |
| 309 | + const x = fn.from16('2adbeef'); |
| 310 | + const as1 = fn.add(fn.sqr(a1), x); |
| 311 | + const am1 = fn.toRed && fn.toRed(a1); |
| 312 | + const pow1 = fn.fromRed && fn.fromRed(am1); |
| 313 | + return { |
| 314 | + a1, b1, a1j, b1j, as1, am1, pow1, a10base, a16base, |
| 315 | + } ; |
| 316 | + } |
| 317 | + |
| 318 | + Object.keys(fns).forEach( name => { |
| 319 | + fixture[name] = {}; |
| 320 | + fixture[name].args = init(fns[name]); |
| 321 | + fixture[name].outs = {}; |
| 322 | + }) ; |
| 323 | + |
| 324 | + return fixture; |
| 325 | + |
| 326 | +} |
| 327 | + |
| 328 | +while (fixtures.length < NFIXTURES) fixtures.push(newFixture()) ; |
| 329 | + |
| 330 | +add('from10', 'a10base'); |
| 331 | +add('from16', 'a16base'); |
| 332 | +add('to10', 'a1'); |
| 333 | +add('to16', 'a1'); |
| 334 | +add('add', 'a1', 'b1'); |
| 335 | +add('add', 'a1j', 'b1j'); |
| 336 | +add('sub', 'a1', 'b1'); |
| 337 | +add('sub', 'a1j', 'b1j'); |
| 338 | +add('mul', 'a1', 'b1'); |
| 339 | +add('mul', 'a1j', 'b1j'); |
| 340 | +add('sqr', 'a1'); |
| 341 | +add('div', 'as1', 'a1'); |
| 342 | +add('mod', 'as1', 'a1'); |
| 343 | +add('sqrm', 'am1'); |
| 344 | +add('powm', 'am1', 'pow1'); |
| 345 | +add('invm', 'am1'); |
| 346 | +add('gcd', 'a1', 'b1'); |
| 347 | +add('egcd', 'a1', 'b1'); |
| 348 | + |
| 349 | +start(); |
| 350 | + |
| 351 | + |
| 352 | +// Forces ALL computations ? |
| 353 | +const results = new Array(NFIXTURES) ; |
| 354 | +for ( let i = 0; i < NFIXTURES; ++i ) { |
| 355 | + results[i] = {}; |
| 356 | + Object.keys(fns).forEach( name => { |
| 357 | + const fn = fns[name]; |
| 358 | + const fixture = fixtures[i][name]; |
| 359 | + Object.keys(fixture.outs).forEach( key => { |
| 360 | + results[i][key] = results[i][key] || {} ; |
| 361 | + |
| 362 | + const result = fixture.outs[key] ; |
| 363 | + const str = result instanceof String ? result : fn.to16(result).toLowerCase() ; |
| 364 | + results[i][key][name] = str; |
| 365 | + }) ; |
| 366 | + } ) ; |
| 367 | + |
| 368 | + for ( const key in results[i] ) { |
| 369 | + const theseResults = results[i][key] ; |
| 370 | + const distinctResults = new Set(Object.values(theseResults)); |
| 371 | + if (distinctResults.size !== 1) { |
| 372 | + console.error('DIFFERENT OUTPUTS for', i, key) ; |
| 373 | + console.error(distinctResults) ; |
| 374 | + console.error(i, key, theseResults) ; |
| 375 | + } |
| 376 | + else { |
| 377 | + console.error(i, key, JSON.stringify(Object.keys(theseResults)), 'OK') ; |
| 378 | + } |
| 379 | + } |
| 380 | + |
| 381 | +} |
0 commit comments