diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77c3d74..ae0360f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,9 +33,12 @@ jobs: - name: Install dependencies run: npm ci --no-audit --no-fund - # The committed dist/ is the 0.1.17 release build (gulp/browserify): the - # contract tests and the Jasmine specs must keep passing on it - - name: Tests on the committed dist (0.1.17 build) and the sources + # The committed dist/ is the 0.1.17 release build (gulp/browserify, Math.js + # 9.5.0): tested against the contract of that release + - name: Committed dist against the 0.1.17 contract + run: npm run test:baseline + + - name: Sources (browserify + the package transform) and Jasmine specs run: npm test # Isolated copy: npm ci, build, artifacts (ES5, source maps), tests on the @@ -71,7 +74,11 @@ jobs: run: npm run build - name: Tests on the fresh dist (standalone and module bundles, plain and minified), the sources and the Jasmine specs - run: npm test + shell: bash + run: | + set -e + npm run test:dist + npm test - name: The build changed nothing outside dist/ shell: bash diff --git a/CHANGELOG.md b/CHANGELOG.md index f34c8fb..08776f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,32 @@ ## Unreleased +### Dependencies: Math.js 15.2.0 (from 9.5.0) + +- **Runtime requirements**: Math.js 13+ requires ES2020 and `BigInt` (Chrome and + Edge 80+, Firefox 74+, Safari 14+ from the ES2020 features used, not tested in + those browsers; no Internet Explorer). The bundles contain + Math.js as published (ES2020 syntax); the package sources still compile to ES5. +- Fractions use fraction.js 5 (`bigint`): exact at any size (0.1.17: doubles, + exact up to 2^53). The arithmetic helpers stay exact; long sums such as the + mean of a probabilistic sensitivity analysis grow without limit; the + aggregation in sd-computations has to bound them (separate change there). `toFloat` returns the correctly rounded double (ties to + even) also for Fractions beyond the double range (fraction.js 5 `valueOf()` + gives Infinity or NaN there). +- `eval(expr, true)` rejects a `Unit` result as before (Math.js 14+ would convert + the value of the unit). +- The JSON form of a Fraction (`toJSON`, data model DTO of the job repository) + writes `n` and `d` as strings; the 0.1.17 form with numbers is still read. + Saved diagrams are unchanged (Fractions are saved as fraction strings). +- Behaviour of Math.js for user expressions changes in many places, some of them + silently: precedence of `%` (percent and modulus) and `mod`, implicit + multiplication after a signed constant division, tolerances of comparisons and + `isInteger`, `mod` round-off, matrix functions, `print` indices, `typeOf`, … + (`docs/MATHJS_UPGRADE.md`, with a proposal for old diagrams). +- Security: Math.js 15.2.0 fixes GHSA-jvff-x2qm-6286, GHSA-29qv-4j9f-fjw5 + (CVE-2026-40897) and GHSA-5v89-rwgr-qj6g (CVE-2026-41139), which affect + 13.1.0 - 15.1.x (not 9.5.0). + ### Build - The `dist/` bundles are built with Rollup and npm scripts instead of diff --git a/README.md b/README.md index 1046fb5..c000027 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # sd-expression-engine SilverDecisions expression engine module: evaluation of the expressions of -SilverDecisions diagrams with [Math.js](https://mathjs.org/) 9.5.0, exact +SilverDecisions diagrams with [Math.js](https://mathjs.org/) 15.2.0, exact Fraction arithmetic and the sd-random distributions. [![CI](https://github.com/SilverDecisions/sd-expression-engine/actions/workflows/ci.yml/badge.svg)](https://github.com/SilverDecisions/sd-expression-engine/actions/workflows/ci.yml) @@ -29,15 +29,23 @@ Built files (`dist/`, committed, rebuilt by the release): The plain files carry an inline source map, the minified ones a `.map` file. +Runtime requirements: Math.js 13+ requires ES2020 and `BigInt` (Chrome and Edge 80+, +Firefox 74+, Safari 14+ from the ES2020 features used, not tested in those +browsers; no Internet Explorer). The package sources compile to +ES5; the bundles and consumer bundles contain Math.js as published (ES2020). +Releases up to 0.1.17 (Math.js 9.5.0) ran on ES5 engines. + ## Development Node 24 (`.nvmrc`); CI runs Node 22 and 24 on Linux and a build smoke test on -Windows. Math.js is pinned to exactly 9.5.0. +Windows. Math.js is pinned to an exact version (15.2.0). ```bash npm ci npm run build # dist/ with Rollup (rollup.config.mjs), ~2 min (terser on the large bundles) -npm test # contract tests on dist/ and the sources, Jasmine specs in Node +npm test # contract tests on the sources (browserify + transform), Jasmine specs in Node +npm run test:dist # contract tests on the four bundles in dist/ (after npm run build) +npm run test:baseline # the committed dist/ (0.1.17 build) against the 0.1.17 contract npm run test:build # isolated build check, see scripts/check-build.js ``` @@ -49,8 +57,8 @@ and sd-random are compiled first by `scripts/browserify-transform.mjs` with the Babel 7 options of their own `browserify.transform` field and CommonJS modules, exactly as babelify compiles them for consumers, so the module interop (`src/mathjs.js` sets `module.exports`, `import * as math`) is Babel's. -Compilation target: ES5 syntax (`@babel/preset-env` without targets, no -browserslist config); class fields are assignments (Babel 6 +Compilation target of the package sources: ES5 syntax (`@babel/preset-env` +without targets, no browserslist config); class fields are assignments (Babel 6 `transform-class-properties`). Math.js (`lib/cjs`, through its `require` export condition, as browserify resolves `main`) and its dependencies are bundled as published, not compiled; the `browser` fields are honoured @@ -59,8 +67,8 @@ for compatibility with the consumers' transform; a migration of that configuration (and Babel 8) is a separate task. `npm run test:build` (`scripts/check-build.js`): copy of the working tree, -`npm ci`, build without warnings, the six artifacts exist and the bundles are -ES5, source maps are valid JSON with `sources`/`sourcesContent` and map a known +`npm ci`, build without warnings, the six artifacts exist and the bundles parse +as ES2020 (the package sources compiled by a consumer as ES5), source maps are valid JSON with `sources`/`sourcesContent` and map a known statement back to `src/expression-engine.js`, nothing changed outside `dist/`, contract tests on the four fresh bundles and the sources, Jasmine specs, `npm pack` installed into an empty browserify 17 + babelify 10 consumer (contract @@ -86,19 +94,28 @@ artifact, chosen with `SD_EXPRESSION_ENGINE_TARGET`: | a file path | a CommonJS module exporting the package namespace | `SD_EXPRESSION_ENGINE_DIST=` tests the bundles of another `dist/` directory. +`SD_EXPRESSION_ENGINE_BASELINE=` (`0.1.17`) tests built artifacts of +that release against its contract: the Math.js version it contains (9.5.0) and +the version-dependent expectations; without it the current contract applies +(Math.js of `package.json`) and a bundle with another Math.js version fails. ```bash -npm test # all five targets on ./dist and the sources, then Jasmine +npm test # the sources (target source), then Jasmine +node test/contract/run.js # all five targets on ./dist and the sources +npm run test:baseline # committed dist/ against the 0.1.17 contract SD_EXPRESSION_ENGINE_TARGET=module node --test test/contract/ # one target node test/contract/run.js --targets=module,source node test/contract/run.js --dist= # another dist/ directory -npm run test:published # the bundles of sd-expression-engine@0.1.17 from npm +npm run test:published # the bundles of sd-expression-engine@0.1.17 from npm (0.1.17 contract) npx jasmine --config=test/jasmine.json # the Jasmine specs (test/specs) in Node ``` -The committed `dist/` is the 0.1.17 release build (gulp/browserify): `npm test` -on a fresh checkout tests it (CI does so as well), `npm run test:build` tests a -fresh build. The Jasmine specs compile the sources with `@babel/register` +The committed `dist/` is the 0.1.17 release build (gulp/browserify, Math.js +9.5.0) until the next release: `npm run test:baseline` tests it against the +0.1.17 contract (CI does so as well), `npm test` the current sources and +`npm run test:build` a fresh build. Tests whose expectation depends on the Math.js +version (`test/contract/mathjs-version.test.js`, `[version difference]`) assert +both versions explicitly. The Jasmine specs compile the sources with `@babel/register` (`test/jasmine-babel.js`) and run in Node; the bundles are not run in a real browser or Web Worker by these tests (see the integration notes of the PRs). @@ -129,7 +146,9 @@ pass while the defect is present and fail once it is fixed. `eval(expr, false)` reject them. - `serialize(±Infinity)` and `round(±Infinity)` throw; `add` / `divide` of `Infinity` and a finite value throw (implicit conversion to Fraction). -- `median` and `std` do not accept Fractions (Math.js 9, `number` config). +- `median` and `std` do not accept Fractions (`number` config). +- An expression can change the shared Math.js configuration for all later + evaluations (`config({number: "BigNumber"})`). - `isExpressionObject` is false for a live Fraction; the replacer works inside `JSON.stringify`, which calls `toJSON()` first. - The Math.js instance is shared: `createUnit` in an expression changes it for diff --git a/docs/MATHJS_UPGRADE.md b/docs/MATHJS_UPGRADE.md new file mode 100644 index 0000000..8418070 --- /dev/null +++ b/docs/MATHJS_UPGRADE.md @@ -0,0 +1,149 @@ +# Math.js 9.5.0 → 15.2.0: behaviour changes and old diagrams + +Status: proposal for decision. Nothing of the handling of old diagrams below +is implemented; the engine (PR "upgrade Math.js to 15.2.0") only makes the +differences explicit in `test/contract/mathjs-version.test.js` (`DIFFERENCES`, +the complete list of reproduced cases, asserted for both versions). + +## Where the differences come from + +1. **Math.js itself** (HISTORY.md 10.0 - 15.2, each case reproduced): + - parser: `%` before a term is modulus and unary percent binds tighter + (15.0: `100 + 3% + 1` 104 → 100, `x% + 1` 1.02 → 0); `mod` has the + precedence of `*` and `/` (14.0: `10 / 4 mod 3` 10 → 2.5); implicit + multiplication after a signed constant division (11.0: `-1 / 2 x` -0.25 → + -1 for x = 2); empty true branch of `?:` is an error (15.1.1); a delimiter + after hex/bin/oct literals (15.0); + - tolerances: comparisons use `relTol` 1e-12 and `absTol` 1e-15 instead of a + relative 1e-12 and an absolute 2.2e-16 (13.0: `x > 0` for x = 1e-15 true → + false, `abs(x - 0.3) < 1e-15` for x = 0.1 + 0.2 true → false, `isZero`, + `isPositive`, `compare`); `isInteger` with tolerance (15.1: + `isInteger(0.1 * 3 * 10)` false → true); `mod` round-off (11.11.1: + `0.3 mod 0.1` 0.0999… → -5.6e-17); + - functions: element-wise `sqrt`/`exp`/`sin`/… of matrices removed (11.0), + `size` returns an array and subsets keep the shape of the index (15.0), + `print` uses one-based indices in expressions (11.11.1), `typeOf` of a + function is `function` (11.0), exact `combinations` (10.2), `mod` with a + negative divisor (12.1), US liquid units (13.0.3), new functions and + operators (`??`, `?.`, `isFinite`, `cumsum`, …). +2. **fraction.js 5** (Math.js 14.0): Fractions hold bigint numerators and + denominators, so they are exact at any size (0.1.17: doubles, exact up to + 2^53); numeric strings keep every digit; the JSON form writes `n` and `d` + as strings; `valueOf()` overflows for large parts (the engine's `toFloat` + converts with bigint, correctly rounded); long sums grow without limit (the + mean of the probabilistic sensitivity analysis, handled in sd-computations). +3. **The engine adapter**: only `toFloat` (bigint conversion) and the rejection + of Unit results in `toNumber` / `eval(expr, true)` (as in 0.1.17). The + arithmetic helpers are exact (no rounding policy). + +Expressions of the 16 saved diagrams in the test data of SilverDecisions and +sd-computations give the same values in both versions; the differences above +come from constructs those files do not use. Syntax alone does not show all of +them: tolerances, `isInteger`, `mod`, matrix functions depend on the values, +which change with sensitivity analyses and random draws. + +## Proposal: old diagrams + +### 1. Semantics version of a diagram + +- Saved files carry the SilverDecisions version (`"SilverDecisions": "1.2.1"`); + all releases up to 1.2.x use the engine 0.1.x (Math.js ≤ 9.5). A file without + the new field below is a **legacy** file. +- New field written on save, e.g. `data.expressionSemantics: "mathjs-15"` (a + name of the expression semantics, not of the app version, so that a later + Math.js upgrade can add another one). +- Jobs (sd-computations `fix/job-resume-semantics`, a draft, not ready to be + merged on its own, see the decision on expressions below): it stores an expression + semantics profile in every job execution: profile version, full Math.js + version and the whole Math.js configuration (records without it: 0.1.17 with + its defaults). A stopped job is continued only under the same profile; + otherwise the old job is kept and a new one starts from the beginning, and a + worker with another profile than the page does not compute the page's jobs. + The profile is read from the shared Math.js instance, so a decision on the + comparison tolerances (`absTol`) changes it, and so does `config(...)` in an + expression: such a diagram cannot run restartable analyses in the + SilverDecisions worker (the worker evaluated its code, the page did not) until + expressions either may not change the configuration or each evaluation gets a + configuration owned by the diagram (a decision, see below). This refusal + applies with 0.1.17 too. The profile does not cover the rest of the mutable + Math.js state (`createUnit`, `import`). + +### 2. Classification on opening a legacy file (before any recomputation) + +Every expression (global code, node code, payoffs, probabilities, text +templates with `math(...)`) is parsed with the **legacy parser** (Math.js 9.5 +`parse`, a separate bundle loaded only for legacy files) and with the current +parser. + +- **S, syntax only** (precedence of `%`/`mod`, unary percent, implicit + multiplication): migratable. The legacy tree printed with all parentheses + (`node.toString({parenthesis: 'all', implicit: 'show'})`) is parsed by the + current parser; the migration is offered only if the two trees are + structurally equal (node types, operators, names, constants). Expressions that + parse to equal trees in both versions need no change. +- **F, changed functions** (matrix element-wise functions, `size`, matrix + indexing, `print`, `typeOf`, `mod`, `isInteger`, `combinations`, units, …): + found by the names in the tree; not migrated automatically (the result + depends on the argument values and types). +- **T, tolerance-dependent** (`==`, `!=`, `<`, `<=`, `>`, `>=`, `compare`, + `equal`, `larger`, `smaller`, `isZero`, `isPositive`, `isNegative`, `sign`): + value-dependent. Option T1 (engine-wide): keep the 0.1.17 comparisons by + creating Math.js with `absTol: Number.EPSILON` — reproduced: it restores + every comparison case of the table (not `isInteger`, `mod`, `floor`/`ceil`). + Option T2: no change, flag as F. +- Parse errors in one version only (`c ? : b`, `??`, …) are reported. + +### 3. Dynamic check (value-dependent differences) + +For a legacy file the base case is evaluated with both engines (legacy bundle +and current) and all values are compared: diagram and node scopes, payoffs, +probabilities, the computed values of the current rule, interpolated texts. +This finds differences the syntax cannot show (a comparison at 1e-15, an +`isInteger` of a computed value). It covers the evaluated values only: a +sensitivity analysis or random draws can reach other values, so expressions of +class F/T stay flagged even when the base case is equal. + +### 4. Before recomputation + +The file is shown with its saved computed values (marked as computed by +SilverDecisions x.y), not recomputed, until the user decides: + +- nothing found (no S/F/T findings, equal base case): open normally, mark as + current semantics on the next save; +- only S: offer the migration (list old and new text of each expression), then + recompute; +- F/T findings or base-case differences: a report (node, field, expression, + value with 0.1.x, value now); choices: migrate S and accept the new + behaviour for the rest, keep the file read-only, or cancel. No silent + recomputation. + +### 5. No repeated warnings + +Accepting writes `expressionSemantics` (and e.g. `migratedFrom: "1.2.1"`) into +the diagram; the next save stores it and later openings do not warn. Without a +save the warning appears again (the file is still legacy). Jobs: results of +jobs completed by 0.1.x are data and are shown as they are; a re-run uses the +semantics of the current data model; a job stopped by 0.1.x is not continued +(section 1). + +### Costs and open points + +- The legacy parser bundle (Math.js 9.5) is loaded only for legacy files; size + and whether `parse` can be bundled without the rest of Math.js 9 are to be + measured. +- Templates of texts (`${...}`, lodash templates with `math("...")`) need a + parser of their own to find the expressions. + +## Decisions needed + +1. Runtime requirement ES2020 + BigInt (Math.js ≥ 13). +2. Comparisons: T1 (keep the 0.1.17 tolerances with `absTol: Number.EPSILON`) + or the Math.js 13+ tolerances. +3. Handling of legacy diagrams (sections 1-5) or an alternative, before a + SilverDecisions release with Math.js 15. +4. Expressions that change the shared Math.js instance (`config(...)`, + `import`, `createUnit`): allowed as now (their jobs in the worker are refused), + rejected, or isolated per diagram; this decides whether the expression + semantics profile of jobs can be guaranteed for every diagram. +5. JSON form of Fractions with string `n` / `d` in the job repository (read + back from 0.1.17 records works, the reverse does not). diff --git a/package-lock.json b/package-lock.json index 6f40f86..902b6c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.17", "license": "LGPL-3.0", "dependencies": { - "mathjs": "9.5.0", + "mathjs": "15.2.0", "sd-random": "^0.1.4", "sd-utils": "^0.1.15" }, @@ -1537,18 +1537,14 @@ } }, "node_modules/@babel/runtime": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", - "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", - "dependencies": { - "regenerator-runtime": "^0.13.4" + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.10.tgz", + "integrity": "sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@babel/runtime/node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, "node_modules/@babel/template": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", @@ -3820,9 +3816,17 @@ "license": "MIT" }, "node_modules/complex.js": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.15.tgz", - "integrity": "sha512-gDBvQU8IG139ZBQTSo2qvDFP+lANMGluM779csXOr6ny1NUtA3wkUnCFjlDNH/moAVfXtvClYt6G0zarFbtz5w==" + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.3.tgz", + "integrity": "sha512-UrQVSUur14tNX6tiP4y8T4w4FeJAX3bi2cIv0pu/DTLFNxoq7z2Yh83Vfzztj6Px3X/lubqQ9IrPp7Bpn6p4MQ==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } }, "node_modules/concat-map": { "version": "0.0.1", @@ -4140,9 +4144,10 @@ } }, "node_modules/decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" }, "node_modules/decode-uri-component": { "version": "0.2.2", @@ -5024,9 +5029,17 @@ } }, "node_modules/fraction.js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", - "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==" + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } }, "node_modules/fs-extra": { "version": "8.1.0", @@ -7107,19 +7120,26 @@ } }, "node_modules/mathjs": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-9.5.0.tgz", - "integrity": "sha512-a6QFEoLFLEI1cKr0B7lwAM1TTNAfNTxppimyqS5WhxGKXbV59DK4OX606z05PpuKpTiq7gTtPiSV0OAwFfxSvw==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-15.2.0.tgz", + "integrity": "sha512-UAQzSVob9rNLdGpqcFMYmSu9dkuLYy7Lr2hBEQS5SHQdknA9VppJz3cy2KkpMzTODunad6V6cNv+5kOLsePLow==", + "license": "Apache-2.0", "dependencies": { - "@babel/runtime": "^7.15.4", - "complex.js": "^2.0.15", - "decimal.js": "^10.3.1", + "@babel/runtime": "^7.26.10", + "complex.js": "^2.2.5", + "decimal.js": "^10.4.3", "escape-latex": "^1.2.0", - "fraction.js": "^4.1.1", + "fraction.js": "^5.2.1", "javascript-natural-sort": "^0.7.1", "seedrandom": "^3.0.5", "tiny-emitter": "^2.1.0", - "typed-function": "^2.0.0" + "typed-function": "^4.2.1" + }, + "bin": { + "mathjs": "bin/cli.js" + }, + "engines": { + "node": ">= 18" } }, "node_modules/md5.js": { @@ -9944,9 +9964,13 @@ } }, "node_modules/typed-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.0.0.tgz", - "integrity": "sha512-Hhy1Iwo/e4AtLZNK10ewVVcP2UEs408DS35ubP825w/YgSBK1KVLwALvvIG4yX75QJrxjCpcWkzkVRB0BwwYlA==" + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.2.tgz", + "integrity": "sha512-VwaXim9Gp1bngi/q3do8hgttYn2uC3MoT/gfuMWylnj1IeZBUAyPddHZlo1K05BDoj8DYPpMdiHqH1dDYdJf2A==", + "license": "MIT", + "engines": { + "node": ">= 18" + } }, "node_modules/typedarray": { "version": "0.0.6", diff --git a/package.json b/package.json index 7daeaa8..b8652c9 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,9 @@ "scripts": { "clean": "node scripts/clean.js", "build": "npm run clean && rollup -c", - "test": "node test/contract/run.js && jasmine --config=test/jasmine.json", + "test": "node test/contract/run.js --targets=source && jasmine --config=test/jasmine.json", + "test:dist": "node test/contract/run.js --targets=standalone,standalone.min,module,module.min", + "test:baseline": "node test/contract/run.js --baseline=0.1.17", "test:published": "node test/contract/run.js --published=0.1.17", "test:build": "node scripts/check-build.js", "release": "release-it --verbose" @@ -69,7 +71,7 @@ "rollup": "^4.67.3" }, "dependencies": { - "mathjs": "9.5.0", + "mathjs": "15.2.0", "sd-utils": "^0.1.15", "sd-random": "^0.1.4" } diff --git a/rollup.config.mjs b/rollup.config.mjs index 5390f6c..37dd82a 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -41,10 +41,16 @@ const plugins = () => [ ]; // Math.js lib/cjs has a few `/* #__PURE__ */` comments Rollup cannot place -// (INVALID_ANNOTATION, the comment is dropped, the code is unchanged); every -// other warning is reported. +// (INVALID_ANNOTATION, the comment is dropped, the code is unchanged) and, from +// Math.js 15, circular requires among its own utils (object -> is -> map -> +// customs), which CommonJS (strictRequires) resolves as Node does; every other +// warning is reported. +const MATHJS = /[\\/]node_modules[\\/]mathjs[\\/]/; function onwarn(warning, warn) { - if (warning.code === 'INVALID_ANNOTATION' && /[\\/]node_modules[\\/]mathjs[\\/]/.test(warning.id || '')) { + if (warning.code === 'INVALID_ANNOTATION' && MATHJS.test(warning.id || '')) { + return; + } + if (warning.code === 'CIRCULAR_DEPENDENCY' && (warning.ids || []).length && warning.ids.every(id => MATHJS.test(id))) { return; } warn(warning); diff --git a/scripts/check-build.js b/scripts/check-build.js index 95cd515..cf1ad48 100644 --- a/scripts/check-build.js +++ b/scripts/check-build.js @@ -3,7 +3,9 @@ // // 1. copy the files of the working tree (git ls-files) to a temp dir, `npm ci` there // 2. `npm run build`: exit code 0, no Rollup warnings -// 3. all dist artifacts exist, nothing else is in dist/; the bundles are ES5; +// 3. all dist artifacts exist, nothing else is in dist/; the bundles parse as +// ES2020 (BUNDLE_ECMA_VERSION: Math.js 13+ requires ES2020 and bigint and is +// bundled as published; the package's own sources are checked for ES5 in 6); // their source maps (inline in the plain files, .map files next to the // minified ones) are version 3 JSON with sources and sourcesContent, and the // position of a known statement of src/expression-engine.js maps back to @@ -54,6 +56,9 @@ const CONSUMER_DEPS = ['browserify@17', 'babelify@10', '@babel/core@7', '@babel/ 'babel-plugin-transform-class-properties@6', 'babel-plugin-transform-object-assign@6', 'babel-plugin-transform-builtin-extend@1']; const TARGETS = ['standalone', 'standalone.min', 'module', 'module.min']; +// syntax level of the dist bundles (Math.js 15 lib/cjs uses ES2020: bigint +// literals, optional chaining); the package sources compile to ES5 +const BUNDLE_ECMA_VERSION = 2020; const args = process.argv.slice(2); const flag = name => args.includes(name); @@ -120,10 +125,10 @@ function copyTree(dest) { // acorn from the isolated copy (installed by its `npm ci`) let toolsDir = root; -function isES5(file) { +function isES5(file, ecmaVersion) { const acorn = require(require.resolve('acorn', {paths: [toolsDir]})); try { - acorn.parse(fs.readFileSync(file, 'utf8'), {ecmaVersion: 5}); + acorn.parse(fs.readFileSync(file, 'utf8'), {ecmaVersion: ecmaVersion || 5}); return null; } catch (e) { return e.message; @@ -305,8 +310,8 @@ function main() { continue; } if (a.endsWith('.js')) { - const err = isES5(f); - err ? fail(a + ' is not ES5: ' + err) : null; + const err = isES5(f, BUNDLE_ECMA_VERSION); + err ? fail(a + ' is not ES' + BUNDLE_ECMA_VERSION + ': ' + err) : null; const problem = sourceMapProblem(f, a.endsWith('.min.js'), copy); problem ? fail(a + ': ' + problem) : null; } @@ -315,7 +320,7 @@ function main() { .map(f => 'dist/' + String(f).split(path.sep).join('/')) .filter(f => fs.statSync(path.join(copy, f)).isFile() && !ARTIFACTS.includes(f)); extra.length ? fail('unexpected files in dist/: ' + extra.join(', ')) : null; - ok(ARTIFACTS.length + ' artifacts checked (ES5, source maps: JSON, sources, sourcesContent, mapping of ' + ERROR_FILE + '); sizes: ' + + ok(ARTIFACTS.length + ' artifacts checked (ES' + BUNDLE_ECMA_VERSION + ', source maps: JSON, sources, sourcesContent, mapping of ' + ERROR_FILE + '); sizes: ' + ARTIFACTS.filter(a => fs.existsSync(path.join(copy, a))).map(a => path.basename(path.dirname(a)) + '/' + path.basename(a) + ' ' + Math.round(fs.statSync(path.join(copy, a)).size / 1024) + ' KiB').join(', ')); @@ -422,7 +427,9 @@ function checkPackage(tmp, copy) { return false; } }).map(n => n + '@' + require(path.join(modules, n, 'package.json')).version); - console.log('info full consumer bundle is not ES5 (' + err + '): fresh Math.js dependencies ' + (notES5.join(', ') || '?')); + console.log('info full consumer bundle is not ES5 (' + err + '): Math.js ' + installed('mathjs') + ' and its dependencies ' + (notES5.join(', ') || '?')); + const err2020 = isES5(bundle, BUNDLE_ECMA_VERSION); + err2020 ? fail('consumer bundle is not ES' + BUNDLE_ECMA_VERSION + ': ' + err2020) : ok('consumer bundle parses as ES' + BUNDLE_ECMA_VERSION); } else { ok('browserify bundle of require(\'' + NAME + '\') is ES5'); } diff --git a/src/expression-engine.js b/src/expression-engine.js index 59d3dac..b361034 100644 --- a/src/expression-engine.js +++ b/src/expression-engine.js @@ -13,6 +13,63 @@ sdRandom.functionNameList.forEach(fnName => { } }); +// Fractions of Math.js 14+ (fraction.js 5) hold bigint numerators and +// denominators. fraction.js 5 valueOf() is Number(n) / Number(d): it rounds n and +// d first and gives Infinity or NaN when they are beyond the double range, +// although the quotient may not be. toFloat therefore converts with bigint: +// the result is the double nearest to n / d, ties to even (IEEE 754 +// round-to-nearest), including subnormal results and overflow to +-Infinity. +const ZERO = BigInt(0); +const ONE = BigInt(1); +const TWO = BigInt(2); + +function bitLength(v) { + return v === ZERO ? 0 : (v < ZERO ? -v : v).toString(2).length; +} + +// x * 2^k without intermediate overflow or underflow (k may exceed the exponent range) +function scale(x, k) { + while (k !== 0) { + const step = Math.max(-1000, Math.min(1000, k)); + x *= Math.pow(2, step); + k -= step; + } + return x; +} + +function fractionToFloat(f) { + const n = f.n < ZERO ? -f.n : f.n; + const d = f.d; + const negative = f.s < ZERO; + let v; + if (n === ZERO) { + v = 0; + } else if (bitLength(n) <= 53 && bitLength(d) <= 53) { + // exact integers, one correctly rounded division + v = Number(n) / Number(d); + } else if ((n << BigInt(1022)) < d) { + // subnormal (below 2^-1022): round n / d * 2^1074 to an integer, ties to even + const t = n << BigInt(1074); + let q = t / d; + const r2 = (t - q * d) * TWO; + if (r2 > d || (r2 === d && (q & ONE) === ONE)) { + q += ONE; + } + v = Number(q) * Math.pow(2, -1074); + } else { + // q = floor(n / d * 2^k) with at least 55 bits; a set lowest bit for a + // non-zero remainder (sticky bit) lets Number(q) round correctly to 53 bits + const k = 55 - (bitLength(n) - bitLength(d)); + let q = k >= 0 ? (n << BigInt(k)) / d : n / (d << BigInt(-k)); + const exact = k >= 0 ? q * d === n << BigInt(k) : q * (d << BigInt(-k)) === n; + if (!exact) { + q |= ONE; + } + v = scale(Number(q), -k); + } + return negative ? -v : v; +} + export class ExpressionEngine { @@ -73,6 +130,10 @@ export class ExpressionEngine { } static toNumber(a) { + if (math.isUnit(a)) { + // Math.js 14+ converts the value of a Unit; a payoff or probability is a number + throw new TypeError('Unexpected type of argument in function fraction (actual: Unit)'); + } let parsed = parseFloat(a); if (parsed === Infinity || parsed === -Infinity) { return parsed; @@ -192,6 +253,9 @@ export class ExpressionEngine { } static toFloat(number) { + if (math.isFraction(number)) { + return fractionToFloat(number); + } return math.number(number); } diff --git a/test/contract/exports.test.js b/test/contract/exports.test.js index 1b803d2..bd58188 100644 --- a/test/contract/exports.test.js +++ b/test/contract/exports.test.js @@ -29,6 +29,12 @@ test('exports: ExpressionEngine only, with the __esModule flag', () => { assert.ok(new ExpressionEngine() instanceof ExpressionEngine); }); +test('the tested artifact contains the expected Math.js version', () => { + // package.json for the sources and fresh builds, the release's version for + // SD_EXPRESSION_ENGINE_BASELINE (the committed dist/ is the 0.1.17 build) + assert.equal(ExpressionEngine.eval('version', false), loaded.expectedMathjs); +}); + test('static and instance API', () => { const statics = Object.getOwnPropertyNames(ExpressionEngine).filter(n => !['length', 'name', 'prototype'].includes(n)); assert.deepEqual(statics.sort(), STATIC); @@ -59,7 +65,7 @@ test('package.json keeps the entry points, the runtime dependencies and the brow }]] }); assert.deepEqual(Object.keys(pkg.dependencies).sort(), ['mathjs', 'sd-random', 'sd-utils']); - assert.equal(pkg.dependencies.mathjs, '9.5.0', 'Math.js is pinned exactly'); + assert.match(pkg.dependencies.mathjs, /^\d+\.\d+\.\d+$/, 'Math.js is pinned exactly'); // sources consumers bundle (index.js -> src/) and the standalone entry for (const file of ['index.js', 'standalone.index.js', 'src/index.js', 'src/expression-engine.js', 'src/mathjs.js']) { assert.ok(fs.existsSync(path.join(loaded.root, file)), file); @@ -157,7 +163,6 @@ test('module bundle: global require exposing sd-expression-engine, cached; sd-ut const code = fs.readFileSync(bundleFile(), 'utf8'); assert.doesNotMatch(code, /Trapezoidal\(0\.0,1\.0,0\.25,0\.75\)/, 'sd-random not bundled'); assert.doesNotMatch(code, /lodash\.com|__lodash_hash_undefined__/, 'sd-utils / lodash not bundled'); - assert.equal(ExpressionEngine.eval('version', false), '9.5.0', 'Math.js 9.5.0 inside the bundle'); }); // Real browserify `require` bundle loaded after the engine, exposing a module diff --git a/test/contract/fixtures/README.md b/test/contract/fixtures/README.md index 72ceb79..ca0f5c4 100644 --- a/test/contract/fixtures/README.md +++ b/test/contract/fixtures/README.md @@ -16,3 +16,8 @@ Normalization: global code that calls `random()` cannot be repeated, so for such files the saved `expressionScope` (the result of that code when the file was saved) is used as the diagram scope instead of evaluating the code again. No other value is changed or recomputed from the current implementation. + +`tofloat_cases.json` (not a diagram): Fractions and the correctly rounded double +of each (ties to even, subnormals, overflow, random large Fractions), generated +with Python's `fractions.Fraction` by `tofloat_cases.py`; regenerate with +`python3 -I test/contract/fixtures/tofloat_cases.py > test/contract/fixtures/tofloat_cases.json`. diff --git a/test/contract/fixtures/tofloat_cases.json b/test/contract/fixtures/tofloat_cases.json new file mode 100644 index 0000000..4b9d957 --- /dev/null +++ b/test/contract/fixtures/tofloat_cases.json @@ -0,0 +1,1133 @@ +{ + "generator": "test/contract/fixtures/tofloat_cases.py", + "cases": [ + { + "n": "0x100000000000008000000000000000000000000000000000000", + "d": "0x100000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000000", + "note": "1 + half ulp: tie, to even (1)" + }, + { + "n": "0x100000000000008000000000000000000000000000000000001", + "d": "0x100000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000001", + "note": "1 + half ulp + tiny: up" + }, + { + "n": "0x100000000000007ffffffffffffffffffffffffffffffffffff", + "d": "0x100000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000000", + "note": "1 + half ulp - tiny: down" + }, + { + "n": "0x100000000000018000000000000000000000000000000000000", + "d": "0x100000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000002", + "note": "1 + 3 half ulps: tie, to even (1 + 2 ulps)" + }, + { + "n": "0x100000000000017ffffffffffffffffffffffffffffffffffff", + "d": "0x100000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000001", + "note": "1 + 3 half ulps - tiny: down (1 + 1 ulp)" + }, + { + "n": "0x10000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "d": "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000000", + "note": "1 + half ulp: tie, to even (1)" + }, + { + "n": "0x10000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "d": "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000001", + "note": "1 + half ulp + tiny: up" + }, + { + "n": "0x100000000000007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "d": "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000000", + "note": "1 + half ulp - tiny: down" + }, + { + "n": "0x10000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "d": "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000002", + "note": "1 + 3 half ulps: tie, to even (1 + 2 ulps)" + }, + { + "n": "0x100000000000017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "d": "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000001", + "note": "1 + 3 half ulps - tiny: down (1 + 1 ulp)" + }, + { + "n": "0x100000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "d": "0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000000", + "note": "1 + half ulp: tie, to even (1)" + }, + { + "n": "0x100000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "d": "0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000001", + "note": "1 + half ulp + tiny: up" + }, + { + "n": "0x100000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "d": "0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000000", + "note": "1 + half ulp - tiny: down" + }, + { + "n": "0x100000000000018000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "d": "0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000002", + "note": "1 + 3 half ulps: tie, to even (1 + 2 ulps)" + }, + { + "n": "0x100000000000017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "d": "0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000001", + "note": "1 + 3 half ulps - tiny: down (1 + 1 ulp)" + }, + { + "n": "0x111f249dd9ddb588cb812d850da7fe92e1f5b1", + "d": "0x111f249dd9ddb4ffd25c3eb620000000000000", + "expected": "3ff0000000000000", + "note": "tie (2^53+1)/2^53 scaled by 3^k, to even" + }, + { + "n": "0x111f249dd9ddb588cb812d850da7fe92e1f5b2", + "d": "0x111f249dd9ddb4ffd25c3eb620000000000000", + "expected": "3ff0000000000001", + "note": "above the tie, scaled" + }, + { + "n": "0x111f249dd9ddb588cb812d850da7fe92e1f5b0", + "d": "0x111f249dd9ddb4ffd25c3eb620000000000000", + "expected": "3ff0000000000000", + "note": "below the tie, scaled" + }, + { + "n": "0x16739ffe90b4bc3237d5508bbcff743883b7a52a55acbeb77317fcd910b842056bb1048dea5b2a45da521e6037c9ec11385e6d03c181e2f35d04307d34e67dc5c2671", + "d": "0x16739ffe90b4bb7e9ad55c0617237f61d8d7747139b1aff0b774730b8338bc49c818a8742478dc05150e7d3c70e9c368c474897c73669ccfb8b84ce20000000000000", + "expected": "3ff0000000000000", + "note": "tie (2^53+1)/2^53 scaled by 3^k, to even" + }, + { + "n": "0x16739ffe90b4bc3237d5508bbcff743883b7a52a55acbeb77317fcd910b842056bb1048dea5b2a45da521e6037c9ec11385e6d03c181e2f35d04307d34e67dc5c2672", + "d": "0x16739ffe90b4bb7e9ad55c0617237f61d8d7747139b1aff0b774730b8338bc49c818a8742478dc05150e7d3c70e9c368c474897c73669ccfb8b84ce20000000000000", + "expected": "3ff0000000000001", + "note": "above the tie, scaled" + }, + { + "n": "0x16739ffe90b4bc3237d5508bbcff743883b7a52a55acbeb77317fcd910b842056bb1048dea5b2a45da521e6037c9ec11385e6d03c181e2f35d04307d34e67dc5c2670", + "d": "0x16739ffe90b4bb7e9ad55c0617237f61d8d7747139b1aff0b774730b8338bc49c818a8742478dc05150e7d3c70e9c368c474897c73669ccfb8b84ce20000000000000", + "expected": "3ff0000000000000", + "note": "below the tie, scaled" + }, + { + "n": "0x100000000000008000000000000000000000000000000000001", + "d": "0x100000000000000000000000000000000000000000000000000", + "expected": "3ff0000000000001", + "note": "(2^200 + 2^147 + 1) / 2^200 (review example)" + }, + { + "n": "0x37a0790280d2f3d3fe01dfdfa997963442ea39ab8daa5014b22ea3b6aedb19210f8f7462987c46f2c2733de4a4b89e43587788fcba28fe3c3ba1030e0b4c2b301525eb9739fc51af291478abfb8736c10c186472be04f110d492400000000000000000000000000000000000000000000000000000000000000000000000000001", + "d": "0x3f1", + "expected": "7fac3a1654ff2649", + "note": "(10^310 + 1) / 1009" + }, + { + "n": "0x1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "d": "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "expected": "0170000000000000", + "note": "(2^1200 + 1) / (2^2200 + 1)" + }, + { + "n": "0x1", + "d": "0x80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "0000000000000000", + "note": "half of the smallest subnormal: tie, to even (0)" + }, + { + "n": "0x1", + "d": "0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "expected": "0000000000000001", + "note": "just above half of the smallest subnormal: up" + }, + { + "n": "0x3", + "d": "0x80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "0000000000000002", + "note": "1.5 x smallest subnormal: tie, to even (2 x)" + }, + { + "n": "0x5", + "d": "0x100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "0000000000000001", + "note": "1.25 x smallest subnormal: down" + }, + { + "n": "0xfffffffffffff", + "d": "0x40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "000fffffffffffff", + "note": "largest subnormal" + }, + { + "n": "0x5ffffffffffffd", + "d": "0x180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "0010000000000000", + "note": "between the largest subnormal and 2^-1022: tie, to even (2^-1022)" + }, + { + "n": "0x1", + "d": "0xc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "0005555555555555", + "note": "1/3 of 2^-1022 (subnormal, non-dyadic)" + }, + { + "n": "0x1", + "d": "0x12d8dc1d56a13cc9abd94f06861b833eade87dad8aa62c361c45297ed7ce20501b8c88b056cd6f94c4142bb31b48adac9ef37a52d21cc71880a3c01134060260fa8734c9469130f0d8ef3b8f12e33f44c667d142fe9a0763d71d3f5fb647717e40000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "0000000000000000", + "note": "1e-330 (subnormal)" + }, + { + "n": "0x1", + "d": "0x1b4ec7f91973ff3cb1ccf26fbc177c38db6e54582de258ff5190b8bc150afadb38030e5d0b7bf50120e5c0e6ad18e629853ce9946a0b2eccf6cfde0af8634bf8dc5a427fe4236ce5959baa0e776cdb47c4813e1e0c75ca271e1d059e08e4996aa4193a48ad3a24633ebdb32e9f0b824377e2fa9c10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "expected": "0000000000000000", + "note": "1e-400 (below the range: 0)" + }, + { + "n": "0xfffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "d": "0x1", + "expected": "Infinity", + "note": "midpoint MAX / 2^1024: tie, to even (Infinity)" + }, + { + "n": "0x2fffffffffffff3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "d": "0x3", + "expected": "7fefffffffffffff", + "note": "just below the midpoint: MAX" + }, + { + "n": "0x2fffffffffffff400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "d": "0x3", + "expected": "Infinity", + "note": "just above the midpoint: Infinity" + }, + { + "n": "-0x2fffffffffffff3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "d": "0x3", + "expected": "ffefffffffffffff", + "note": "negative, just below the midpoint: -MAX" + }, + { + "n": "0x1b4ec7f91973ff3cb1ccf26fbc177c38db6e54582de258ff5190b8bc150afadb38030e5d0b7bf50120e5c0e6ad18e629853ce9946a0b2eccf6cfde0af8634bf8dc5a427fe4236ce5959baa0e776cdb47c4813e1e0c75ca271e1d059e08e4996aa4193a48ad3a24633ebdb32e9f0b824377e2fa9c10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "d": "0x3", + "expected": "Infinity", + "note": "far beyond the range: Infinity" + }, + { + "n": "-0x58e354b1f5dab20f4aa46764e29b77f67a73f91da3a125", + "d": "0x544651d4cc381c4efba6b88aa3", + "expected": "c4f0e038a6948f64", + "note": "random" + }, + { + "n": "0x6dafae9302986547b52a0a119fa2c1be1e007df311b65e0acc655c88dd1c8af152f872fbb1ed0ed3cd1e901bf3a517ef8389c1a7157346352fef77de3f69506bf97c05a8330d10058604a9fc7c87be8e5b3e922e4e4a1174a44887d115906d658e2e6d7347079d53248e08f7199d6f6e5148b", + "d": "0x3448a11dd79d5a4f412961c75951ef03fb06e5557468ede4c32916369ad49d83d2d33abc3b2b34c280e26e207780255c44607645355fd934eae3d122eda5c8f", + "expected": "5980c88275634c65", + "note": "random" + }, + { + "n": "0x15635fd128e73e2c71b97e0f727480af", + "d": "0x37262ced5d895e3e73c0c99dedd2886bdc2642bfbbafbaf9201d4e6c4bb0b7e1a5010a2d85d8044822b5c0bec0f23fb8ade27bd2a3b868f345a2f8004195e422757df09a0b7cbf7c73cb018066d2982d768d8fbceaab62070a2485598ed94e40b56056f0701b9e98f5751a58feb72648ff7f328b8f2f34a6e4cc964fc5", + "expected": "0958d21dd4deff53", + "note": "random" + }, + { + "n": "0x52d36ab6b37aeb01e3d693614da9e0f7409ebbbb2ed4808d1f53eb8c0f0f90f35709e405a4f8bd08c6caf04cb25dfd526afe5eddb69a0745865e8784f9e9b29664c8765d49ef6429df8744c4a65187462aaf7a4a0965e9aa86c74967c880b74c3708d722fa2c58cf2d9efb0176f7f4587bd38a8435b20681", + "d": "0xed086ba0285ce52b12816ca66090985fe84e3c4a5bdf543ef827e5fd5fba5913dd74162ad849c812d81852b2a1dd36ee9a66a0725f51b4c54715b879e2017d1306fbe0bc38b4e54d58be046b0c20b3a7fc1f79035325471ff5bc51a9bae77ca3c6295c77c08e8765adb0bf2a22b1640956f02824fdf4e2166ede1870978513033587e8f7ec859194258220bac4cb7ab25", + "expected": "33965d05ccd30b12", + "note": "random" + }, + { + "n": "0x7942257832e8bae95c9281a0bf5d6ef2a454053508fd1b3664428edc5e91fc8ee2532b200f29dc2fa112bda9c4c7b912f11980996fd9a502e5e4db0b6c35a209175b880f74ea00d789682881f4bade7562eeed945a79972ed9eed2b4ae1c0719c485d0cfe811d9b119c661ca388afc72a858132839bb23ab2e3f179", + "d": "0x12d5312cf99c477bea704bdfb51cacd8ff1aeb085bc4886df3f5d014fec2f084136b0645002720968ccd0ebbe9683336d81a260e4feaaddfdeb1c077b56bc09e3355fa5a524391155d1aa247141b427ae538783dfe5797d58b740f1f886e675c4258977b76b68dc1b0cf76c6a7b8042a2c45ab2c3f00e7c9505c09afdc7a675c6a555f8b169aecc874154f78f685b", + "expected": "3699c13723779f1c", + "note": "random" + }, + { + "n": "-0x96536a7005b2e768df6026fcedda3193b2b7c5251bf99735ec7d1e13bef4f9047833ad4a401c8d456941362900b3a25df0161ffd014bf31a9a9f41338b11c8758128a46c4b31f6f40af477e0165ee99ebbfb4646619caacd14aa73aca3188b2fd96a6516b8de2bc5152a1013c045a9f34a7d74505328854887f", + "d": "0xf2709bed0842c9321865cb7643a62de1afad55ca380dc5bc9517ce896280d4fdeacbd9efd11", + "expected": "e9e3d77bb5fdf1dc", + "note": "random" + }, + { + "n": "0x268612f766e4e29340f8a411dda641867d9102b09f92a0f6475d8dbb02b86712b12b4851122b04ec3c093c89d4beedd012fa0e699ba225d40e4e7ebe69515b85e284c6c677fb96143d08eb4d7081cd7256ce34999247e3040b05e35f28689ce604c9875b57", + "d": "0x993c962406d2c99d8a4889f37c5a7282774e72e7de93defd", + "expected": "665016f0d2531a88", + "note": "random" + }, + { + "n": "-0x755ecc724867f10a87f8ef00c1eac52fe0e26a9dea2071592b55b4b5375e62b8df5acd85c25dffe77c096a1f32e5fc95ca489f0bac664598033493c8518d41fca5d08d0ab460ad263e4562e093afc78b5f6e7014b3d5a522ee1276ac915e7bd195a9f52097d12639ebbe49e45136dc6406cfdcf5247da49f0bc1108adedacc5720ff", + "d": "0x18d0c4f25a019d9f2ac3c2ef1e494d8f3bae82d1985ebc9590f04e0ab2a5ab2211076bc6a502d9c4d5459e327dab041f56831b3c9b29aa45d7f27c69f50228b9955727568ca8a5fb9e53953d3ef66aaa33614f683043ee625eafb2902b7712f01a86443ddd65f374ecdaba95b205182e9e655697d6b823525bc6aefb2dd658ad7", + "expected": "c0d2eb3ad514b7ab", + "note": "random" + }, + { + "n": "0x29ca5145bbff8da46d58793f599145a13ef3492ef850b9b089647b37c60e5f45932addffab55bd6b4b", + "d": "0x8ebb19c7152d7034516596c97ce9c84b6bdb8c914a7093a386658cb4addb3292cd8232ac6c934b264ee7f9c8ef362ec73eb992c92090b93f208ce9099bb2bd57e5aeb2973df62ff11537041268b8e5", + "expected": "2cd2bd163102b28f", + "note": "random" + }, + { + "n": "0x6b8520091071cbb5d0670edcaa42cda8580086bb8f907e89722e8226b5199e72015c64970fde37355b25cc7402a89d5b575737d9c91fe84dbd2abf2447a2ce7312bfbf20f038c115415a6c638e9d41c4867fac5f3a6442d04572a4e6c189f80c5b00abbbf46245963f93046885f68a6e540219d01156134fd2bba46681792554041b33b77414502825c074f59bbcc0563cceb1", + "d": "0x7a2266824caf86f46114cb7410bd9417afe071e83b92f5c4f6c1fca21e89ae2b30e6599d45eb2eb8691148f79eb885758045e20b515200e2ef1a73a1369790ce3ba2ca46471a62fe38e01586510c9", + "expected": "622c2bc2f67bc19d", + "note": "random" + }, + { + "n": "-0x112e1b5c15e3a7e70ddc1858faff436dc6457d75183d63889241e06f2d3bdb5", + "d": "0x1d5fffa2667e19579e0d3681a1609801ee99c2a0abc628479bb0cbe95fad762129", + "expected": "bf22b7214e3f105b", + "note": "random" + }, + { + "n": "0x781017c7e5b5d9a47f7a7b3e5b8288b849fe856d32f9709aeda67ef", + "d": "0xae8096a1129f50c84805148971c4e2829dfd5d6b2ff9d6c6b90602acc6bad9e2c42fb53e46bdfab04c3f0047e19b", + "expected": "36a604580c99bb00", + "note": "random" + }, + { + "n": "0xf4e7a794360184689b048830636a8b547d047bc669fe9cc16b342505be38288bc2f780ce7a1ea1a5a70def9f44e2b842795c194abced8229b9cb379ae73351db", + "d": "0xfa91d29560fa4c63a5ae7eb40caa3e65f8a6c63c10653b7f2adca57da1c816c645f13329448ca24152b75a59c3dcdb310153d53e324adae307305f774ea518b427c7336e10b4f124166d5e2c036ec5775b7d9c741490e1", + "expected": "346f46cceba70915", + "note": "random" + }, + { + "n": "0x95098a21fb807c015348f87d51224889b84e3b8132c55b38cf11b09c7893ea32c5326b65c18e232dbdcf9817ca90fd00b16a72152711fbe5b3fdc9f756d8b7a377cae96c531a2f59c0272e18a250b6f889a8768bf7bb1f284b258f92109eb5312c3c3054ca99003864fb53c7e242f56ef7", + "d": "0x15158f13632554a34b9c0a5748331898738623bb2d2c7be1f8f111e600cec5da65f58e72dce476b43cda309f35db5fbd01d312467632332baa9b44c2cc26cae8769a963d68bc6d3b9a9dd37c963ac458a24449d8bb2a17aef3c9c858d8600f216ca205924d701b210cc955b0ccaac837599ecfa1c003d11", + "expected": "3cdc46504f8aa9c4", + "note": "random" + }, + { + "n": "0x157a07ea78ad4a6df34c4f8e761fa8e725f8c450afbc25891f94b2bc126f4e714a410e1049739c6f37853e96ca31d19", + "d": "0x3f01d3e5621179d8b8533ef2dea7aed8ad33c5332b89569ca88f69", + "expected": "4a15d0ab1a105ad2", + "note": "random" + }, + { + "n": "0x853051fd3cb0d14986840a3ca6e818cec69449536fa6edb6646e7539cb01c5f82a1697df2e72772164f761cb38af6b328e24af24f614fc696ce5e658267aa2b7815782a74e488a49229b41567363403a0aaaaa2a207b7dfc974b17d4d131553447bf404c867ff66c905afdb88d7d66c93b3e5a330c1970fcdad1ea4f0830a23c7b2551ea7ba105ac53284d75ccde5a993", + "d": "0x719c03025a3377aafefcd210c8fe6b17defb853951cba85909f101d1e7f004114dbe0e3791f95a2b97dc9d39edae066a0ef9829f2545f58f317e43ebbe0fc45ce8038b8abefcde1a5c51a6bb72664663a640cfb4b4036f37f08b35ae2cf5659aa7f551cdca4155e2ef44401299ba64fcebce5e7fc8d5a94767269b5f7602bba16cecb5e7474cd764731bd", + "expected": "42f2c1e680f349bc", + "note": "random" + }, + { + "n": "-0x10d4449daaa2190c7be63fb6e79b3260eb9e237129421abb0cba5e96cd8dda3bd7946266e9d", + "d": "0x59f67a214b885fc9343a4194b6d1d2e2c302703e0d3344775158d1602d0e62019f94f070722c6891bcbdde4f9a41c3a9ddf5cced9d340138e4ea4d35b03045938a5abb90dd557d64df02c9f67bfeb166d6c9ab6f27374d1373660b1cfa2f025640e5baad17bb7", + "expected": "9f47f1d91f219f16", + "note": "random" + }, + { + "n": "-0xcc9eba221c561eb483c28c3b59aed305a9869f801063d0d1658de4d6de95e7ac380fbd18a1d523329171b2e56c48dcce4a2dd9f9572cc29f7519b2d29b39f0f75dee94f195c3598d4dd310d8d427be1a75b3cb44f7d81749f66b186de0f1421d7d798ad82bacc61ed3f853349d1ddeeb9d63c6a7a63be9627a118fe83c00149d5ad3301", + "d": "0x2919cdfe69b343c6e0a437c7524e1996d56afb6ec2ab50f6b54c310add13049aaa9655", + "expected": "f053e9f9f98ca799", + "note": "random" + }, + { + "n": "0x150b9f066b3d6c152479b13670e5374d1b1a65d7b1e9866c876cfc2e4094879d2d8ca6e7e481a8ed3ab916cd05732d11054f3374e3d329ee21df354251995840037387f73ed4048a473ea6dfb4e270da33dafa8bd4686d7cc9a64171a3bbbad57eacbf75", + "d": "0x1f8ec857b15fe29d761c695598e4349b473aacb8aed5f229d88f46deecee8e30c60534f9ef93bd2dc8b7f9433dac1a94971db06bacf6edd4b8219d42c21dd9930db9a0d69c55eb54be8301735ec5591a8f3400832e80ffba0162d4f54ad4eb0e09d70e09c75b74399b1febb94155f0f0bda41f3", + "expected": "3825571fccb291ea", + "note": "random" + }, + { + "n": "0x24875ca5d8ef59f610713e9ebb1d3a20bc6cfc974303dd15b3fd6d4bcfd393e1e27f0ca924f31199c4ef868ffff1ffed96af822b3ac4774d16711e7c64eb1ad785f5c2ce715e6415ea9f07a63d8bcb76e9f73368b", + "d": "0xb76425ca299baa3f10bc08ffcafa67abd3e81396511b0a165ab770f0dd72b0394dc031eb26aa9a0ca72dd9ae274099e429", + "expected": "51897ee40355408f", + "note": "random" + }, + { + "n": "0x17a278496e369663a7f860d47719e7989e9b5b6b71359e5c382dc76c1678cbc629a3025be0494c940d6ef178b53d6cc8a1a0019b845ebb53a3ba477dfe29ff936ee811645300c1f8c18923ea1e7cb9da6dd690f19d60767d691b0b52ae09aa20ef86daf96282904e7240febcb4aa3c6e3f49f25d02761ec0b27775f6ad384c813", + "d": "0xabd82107621c8f6665bcf05ffdbebf74ad3a25d6f1129c6e349dce9a6c089906c32d38936400e3c68def7683a91de9f869b50138f8833c4ee433e550db2cb69ca8a56e11f11a6b1300d543290d8c2198a5dd", + "expected": "57019ac1911f08d6", + "note": "random" + }, + { + "n": "0x75497e8beaa8611afa3053a865b60a2b", + "d": "0x95f9cd", + "expected": "4669067918f26b02", + "note": "random" + }, + { + "n": "0xa8297a1e68c34f46e677bbeb2766257c999e6035577a3c04dc1c9ea96d4a230239c1197ae150f130f40cb9492aac1f627a0fbf4696aa2fd4cd5f379cc0a7a31ca6848c04ae75726657248b31d9b2c399b16d579051142a342b17ea3cbcd723514413c0e5e696df20d10b0cfb7f29441bcfbe144635e8d", + "d": "0xea7f647dd516a191577b531ecf8f2b895f6cce6291626ebd1e9daccbfedb4a261ac2bd64a00fe1b3816595fc8aad53520c3be48c66e0d4d7a504a8b576c7ca2efe7af9911ae9d78194fde6442b59e7f7516342361d0bea23de6dae1ee0bb50efdcc23c1d1be3227b30406ef72474f1e1473c2373e081d2d2fac1eb8ce8c67c109b2913044bc43f79c2a7", + "expected": "3626f29d6d056a89", + "note": "random" + }, + { + "n": "0x936ab3bd17bc6e05fda9a43ac8b14f8102d7", + "d": "0xf23be432dc2d58e4c0234723e946645b3adcf0c314c3fa5ff412b688f529b356a734193fd7b8011d24fd44836e5bf22d8e2e796223897b75444b2303bf3103f2fa68a26670b6905a9eded3db07a306811af219b4bf79f5d38a68f995bb5748d941b0ea7577d7dfaee1403e4b6f5c884cc2fef", + "expected": "0fa3796c082cdb04", + "note": "random" + }, + { + "n": "-0x330fcafc6c48905ca1c4028922b68d319b335c7223ef089b63649bd4d902b75f1b5b2d7a321ed729497e2a82c39e05722521cec54fdd66e7f8181697000eb2bc8f6972a108b072b136e0749c244e79367da8d", + "d": "0x2930e56ae9a4170b36ecbee8b67", + "expected": "e273d588950d454e", + "note": "random" + }, + { + "n": "-0xcd5c044784fecba55aa9cd8d4a0afdc5580f5e825adc9", + "d": "0x3b618c2eacb8d7b48309f16e3bb548295dabff9f08516dc60ffcd466bd80ab1104d83b134d0d0347e1fc59ab239af77fa232dd40fbf718fb5aa6fcbd0d7523cdc11012212200184a2fd288e9c16ff4e59b4cfb9c6c7272de95fe969b8d5c51144f266054863b1710c4380be7", + "expected": "954baaaa98cea855", + "note": "random" + }, + { + "n": "0x5a7a6dab32c5909eaa2bd1da814e4cf6f0e5568f8b580d97a9f7947faa772657ab6a96e8f1142c7db2108b8f9b9c35980963ae2cc59a84a65e67e8c946c1196212deca1a8c543bc5390f8e3a1ed3409420beefbef822e7346fe0319e8e7f641f45201bb0918f3b8222c0170b93f318f79e6b1649a732138665ae9479fbc8e318199b", + "d": "0x2ea19a2865118f80227a02b3582a04f54b5d51fdc5b6f563bd698d9d5b43e2762e10b3b7be72235655e7eacf9c6ba3eb58526a465b2a7abd87658899e6fe1fc0f2bacaeb3eedd2ed75ad9f29f6d8eae82ecbe110f5720f23b9a86e9e11fb6b", + "expected": "517f0b6f21919efc", + "note": "random" + }, + { + "n": "-0x13b9efe270f1910900db238bba20c673211d4ef80af028558b19e5078e3cf62d819cba6397", + "d": "0x80d9a0032a2d7daa437aae615836ca13bc04360ba2f3b595760aeeeab6bbc7cfdcfd9b170ca924cf936d865c91fae4a134c7e06ccf0acd0986af749defed30c9a75d6bf3b75fe9d59777d43f210813e4913b9ae1f69dcf01a9cf215f0344ab0b01", + "expected": "a1c3989ea638ccbc", + "note": "random" + }, + { + "n": "-0x64659ec4994809603bb6eb1b4507544aac7f88afaaf7308b160097e69d", + "d": "0xf78b40392f132fb41ee8c36d47be4dc5c1959d466b5f38cf9854578dd3334c2c0b466f440f37f2079c6f50854f8cbce0a7f34dc1176b", + "expected": "b359f4e54511d38b", + "note": "random" + }, + { + "n": "-0x1af936b22617545f43", + "d": "0xadafc19241b930d98a37282ce524f50e7a34d4b31d4934c5d3085cebc2de9078a00628cb91f311eb3a9044a59dbfebacee514d8a38dbbf898219aa14cc7cd8ff5024243d45cc8f35a9759a478b11e860f5ca4ab76a76791ce7016ba06b6d7b1ef2bc6cc85bc8ac8eb0f4d", + "expected": "8f03e0dd5ffa5ddd", + "note": "random" + }, + { + "n": "0x44c88c465aa6cd0399beecd39d444d386504b4feb83c04a8acad6a81327672377e93e695d13eb924aefacb4bf96385484c198df6c57bf367c33f1bb45f3a7bdedab968397ae642cb9258f649fd84d15389b8861c891a1b796ba36ce8ee4e3098a5909f75368d14fc333231e85badb0873ea3a4e3e5f07f015a7edba559d", + "d": "0x1cd86113c5233d964bdeeba426759c9192f", + "expected": "76031397eeb43d56", + "note": "random" + }, + { + "n": "0x7e220d0ebcbce7a27ac3aef1e85b90f3e6d7951238de40e56db11c95acb8d647402193105c33e3ba9c9fa772014de92be6906f1397890315e968a23247b144aed80802f8f2c0cda7902ed", + "d": "0x74e1f144a630c0e81ce599b91c08cfd9c3cb73601d5994b5d258372376fad45a944c1a10cf9d3f1ca54b7fe7d281b1ff289e037ff9325bf1bf893dd43f89b8ef17d3be62273543dec24ee431d3e220f5d3948ae12dd5939a548accbc1b709b969d862962b06216ac77aa1fd7db0c67d646ef78d7977513f1cb7d8968da7cf877fee121f5b51589b777e70eb385fb85a6ece36e995", + "expected": "1af1442b51bfaacc", + "note": "random" + }, + { + "n": "-0x2ff08607f7df5708a8a8dfd87de11575e601fab50d1459cd508e70f8959d97524ee2b5423007615fbd860f2aab5c6e66ea3f6a11cd54f043c5ea27e8a22a7072221184511bcc365e9c46f71fe8a73bab9dcf4811b028cd3490c596ff939bebcebedd11a08ee605d72d643586d94c28e5423fb", + "d": "0xd92c0761287511295b2a437907a7e8e63428477603d8eaa128f8bf3dcad141a9c9de1f17c7c17b1e75f5096c42d2ba703ad7f54df4ff8a971af77d458fea23f4dd9487f36b14777293f4d677c4fa07ab8c67228980846b3f8a4cb99551175ae220e2df328f", + "expected": "c68c415bfae296dd", + "note": "random" + }, + { + "n": "0x5cad5931eca1e255ba557a599a67d7d1abe877d927d6afe98dd9ba4083bc4453b7e613babfc5610c44cb1b411bcdc670f4e45ea6b21c372bd8b", + "d": "0x416fc6b473f8a209d5bc59c153d3ffc30d196f29573995b", + "expected": "50f6a91e28713d42", + "note": "random" + }, + { + "n": "-0xf47f0431245e24285004d0aa66dd52a5c7a88aad672d62e4c32fb092e4d14ccb25dae2187c8c8ff1c9fcb53f5dd1ca8ada727c78a9b6b6f4d0c1c1", + "d": "0x30a67b8a66f0ce5e9911b26ae3d7ce625bd71a48139f916ecd23de2e5465d7f114244e5bc6945e0ace34bd4fc709ffbb2d376cb7342aa93051fd9a405996eef1878d24797382ed775fe53321ef84c18d727c13ed63bcf9ad2990defbbdd885821f", + "expected": "ad141a3204b18985", + "note": "random" + }, + { + "n": "-0x10d46976ee0cb08abd368846c7c534b07c9fffb03eb42372c8120730f3936c9e7fbfbf559222a0c2c9804f9206c10b5a66457dc06cf175eebb7d0fccc06539f58543efa9c4c97accbb9582ed045a21bfa3b1571ef4e0f1705dc679b2afc1ace0bed1fdfa513c1987552dffcd1ee36c9aa64ca475d1", + "d": "0x7789181cf86530b78bf8560508331ab931c24edd407b3ca08cba57e4bf756ac5790e7c1e28baba40ebe935b95a0cce7f917ac0e3dd2da1fd4a17bbc5df09a0cfa613eee8ec0ef1634f15f1d18b14f865", + "expected": "d242057f2001ea6a", + "note": "random" + }, + { + "n": "0x5d0104b9c26302a907936e13af242a657119884fa148b20ef451ab4e3cd19392d630258e2c0c9cead5ba22e9586ef3fc2fd0afc6429fc31bcbd16abc849c245d1c002a341544bf2305701a0d697f44dad480670fea84eaadfb88d5ea3ccaddad7", + "d": "0x7273dc91a9806b23af7048286fc17b305126871874ea5dfb1f8a8b3040a4c6298dc5d", + "expected": "5eea00ce33e0d1cf", + "note": "random" + }, + { + "n": "0x7a058161e7329e32219aef1c6b00feb6da51e62819d067649774885874aea88b80948a921f76d966cadcb4307283bcc86f1d854a9b29101c893042b5e7c47300769983ef6cf8463753f", + "d": "0x2d5ccfa8a32295ad938ea51cd1d4917a58248e7eddf66c561bae8bd25b649cefd25e20d66e3677e392d24ccd18f339cfc1e6220d7193bcb8a7399f697ce5aa223caf6bb41b7", + "expected": "420584f387beedf3", + "note": "random" + }, + { + "n": "0x3e48708a9d3816ee3360d69e9817b707fd76df1164f1076bb0222029b2e20c223285dbe8e401d7a0f6c13b19f75f2f447a543911a0a87d6b869ef1a989b25b59a0897037384e5ab24dd9fc5d6683934fe9db4687291d7969f6b2616f97fb4aaf277dff976ef9d7", + "d": "0x322864c8ae8924782fe561f7309b9255ff03f29f27e294c5ceaf6e947a98060bc7c0837e909fe3767fe240fcb40ba0a2759f9cb90c73d247173281e42a47e3205f112e5be0f03140a57c7ae01323ff8cc74901e2a482b918272e9e7020bbce941b97058c825b9401", + "expected": "3f73de2b8296116f", + "note": "random" + }, + { + "n": "-0x108eab8f594be0803a82544939ba4271859cf094418dff976f7b2db428cc556e5ee0ead1b2a6bc2bfb4f1c534e89c7cb743b20e606dfa63d553f25b526d789ebc54270d0c4c78551ed102779e22a2a93ebda360e1d4f84b91f9f070b10619662aa3d990433e02c1e0f7ce5b4cce61dea2be9d02f18a1b36dd585c94808369241440b3f29a6b9eab", + "d": "0x21fdd9c8d14393afacb3a15061feb50405f7d21055e87a49ccd948a4b6a4af93c3993f54b383733a1c4cd7f94286656412309066f99461a7f0038c59ab5f66b9c7e7f3d123bd82e339", + "expected": "df1f2ca4d65fff6a", + "note": "random" + }, + { + "n": "0x999774b01d1103008da0c3159ad351e95b8f4e7c249a6139b5b7b3f791db2b0153f76fe3cffb4558fbdb67b7476bdb0f71fa5324fb1", + "d": "0x2b370b3dfbaa57e90ec745", + "expected": "554c6ed9cace121e", + "note": "random" + }, + { + "n": "0x2611450703e02e4f1dbad0d8d65e81c8f21f820ed0429255137427a032b963f7fc12a79b3", + "d": "0x3dd48013442a71faa4dcc642f72c339f4d2bb81c60d96d2e05f35b1ba3fad1556a66b967f3e6141f6c677d05c80c73eb6fe7ee1c1c27e795d7646bee7b57ca128fd1ac6a4f311323b3f6839b3acb35992f7cee2ab5", + "expected": "27a3b3a3b47c2d61", + "note": "random" + }, + { + "n": "0x1a402181c2eb8875beb1c237b4a9ad7b5ed2f2569a0dbfcd91e63b4f0cd17fac3c86c070562044f71d349ce5410b8c768603b96aeca3dc3032d65ba9863f20640fc95d76acd707c3deba3aac1e08872e8e7f69f7393db988e485776dedd400b3d9a079956aed", + "d": "0xd8de830b231c6c7ef49e52f", + "expected": "6cfefcada8e8daee", + "note": "random" + }, + { + "n": "0x2f93984ccca558b782ff54b72e9d1b0706f6e5e163e49a8878598f26026ed4c078cf7a3d023928509bad79f41499841a8608679aea34fa91852a5a210ddaaf666378bd640df04d45bec8350c950e43967df00b3ef70eee57", + "d": "0x330e9f3dc6bec9d4da2d345c5", + "expected": "65add18cb27f4160", + "note": "random" + }, + { + "n": "-0x2057f45690a3d8c7afc37f54a805d", + "d": "0x383383555dde23f764908a988bab0ad0d64f6a70c67470e08b5", + "expected": "ba626a765526aee1", + "note": "random" + }, + { + "n": "-0xb7d787969001917b4559d96acc8c8a438e4354590ffe622062a5fca451d07248573e7cc750c9cb6131acb21d11090fa6ae3eeeccd8e33d8e1cda074e3eefe6f6334a18939b2af10afa475a397dd9097864a1863cefbd2007e9fb9998438ef78e8b32c87a4684dae5184b837bb91526405ad4daa95f82cbbdef9744baed1f9242a41b59d59e97ac3a45d5b", + "d": "0x6275ab043b4c905e1917aa6961133690cb8ed92b8957f64fdf14f3079acd764a52b938aa84c6964550fabad4be64f4d06d51f64182a1bdaec8a9105b529310f5891d16ef8f5764d3dbf739da450300b50bf78c2f759a92dd57b5243f8a504eca8331e691b10c59ae1abe84cfecc0396a4ff8114d825746b4b5", + "expected": "c8bddff89185525a", + "note": "random" + }, + { + "n": "-0x64451bebd5b1bbc17ef38d23fa4e8988fa770722c163477642540f085855e2104ba275fe03c8703122ec16d5d33d1ca89a333d2bdcb91c5db6ddd66977d583ca2f45c86b8618e3", + "d": "0x174004a0748a3e15793b80deecf79908661b69ee44f75e0d7381a71eac5c9ea9c804e91f3c8563a90ea60618406f02cba4ed7f403c68fd532adc146075f1b5ae42abe1db489b3db647284eca890ab7299e286fe24734a792261dc76773a05655eb7afbbb374a994ece2f0073defe340e1a3a535ab4f", + "expected": "a8d1402d69ebccb8", + "note": "random" + }, + { + "n": "-0xc21010d4c327e320b77acc32ddd79c8795a469a1e7d1c25a41253a6fd8130015bb3fe27a50013f8f4302eb5bc0b7d7eb7d55c81b454f95cceef415187fd36f44ede2890fddbff2e6c7943efe49c7416a430b59d30dd9593190d4106d4a589dec18c4781ecd2580444e7c79c3135de2549449c61450e9fc5e434da8e1d3f410e0571", + "d": "0xaa0df843e23e503f668ff1e803ac3b8d82bce3960bc2da123a4d87f70ddeedbdd5fdec3a058e9bb938d70c96055df2cde534aa14ff544092c4955dd7a986b9edac94fedbd47321", + "expected": "dd3242453d59a34b", + "note": "random" + }, + { + "n": "0x6d6e28945e5a70b32808fe36b17aa153f28f2eb9103dab9034f49888bc6f275ba9da53e8e8a6d44935058d2681008fe92fca9a7e0c59e56e15f92fb8fa1589174eb7c620e7cdf4bb0f75f4dc161df1521ae52f63a5cf6d9d0eeeec4095e3fd2a84afb6d26ca7f5a9e862fee5ac5a2452acb", + "d": "0xc60624da02729c46804d63b697acb6e8436bbe18a72e6c5ff5dae5cd296d79fa82eb94ece18f1076be068e2f5a39afca093ad3d02c730ce0fbf01b1", + "expected": "5ae1aefdf6daa563", + "note": "random" + }, + { + "n": "0x10b22cc163d3159418bd9206222023b3921db52f5fabb4d16a1115a4dd6ab59019326edcca045593d7675a297bc9965060e2fafc1ff13455ccfc6f8d52849ebcd2d3b18371827deeca1e0d50d3f9d70b7", + "d": "0x5e3909b6f32252943bc6a2f0bafb8edab66503c8c972d7716002d87d303f3", + "expected": "58c6ae63ea81ce65", + "note": "random" + }, + { + "n": "0x2b621fa386dd4d296733a57be37d16520a19dabc54603f0070d1cbf5be0a53031a583f2338ca32d2b289305d260336aa21cb104243ff598ecfb6d6080ecf6cd77fd88ae37b74311fedeb41", + "d": "0x3171b18ae4010ffd5f9abe5739c504dcda87d1ac26d3880e0fae3203b88d3c480cf07f36cb0674a315e2cc6b0c8b8ddb0f8e267f0951456b1cd9127cf50f161fb55d785c150e8b567e25b3b50937b69d5fa71dfa1a784326089a01cae45a300bf37be892a0f1e28a4cd739a778f682aef13ddbd523a73e7dcaffe518413f443f86922f9fb96cf4092710716b76937bc3", + "expected": "1d6c13d4c98314db", + "note": "random" + }, + { + "n": "0x18c8a8a3160314d68fc4b40200f7dade1d2da222f5881a9e0f10d485426af18ec438d8e6e56ee1bbe0499d5d34143cfdfc7b1deac1706a7827c45dd64df52fdb9443eafe7e7eefb33d5e678d8c8376a904d6b73c9a0509bae548c22f6529c281d1d933f8735018acdbdd2fbaa24d9d4f2353", + "d": "0x12e077e24f4dcc2677a6b6f7223e667bca819f227874e633bbbe872451678a4f0232d8cb241654395f4ca5e6490574e3951a92a3fa05bf67cf9ea3600d427a5835d970401485279a6bfde5d9ad9cefe50498e30c26e2120aadd50622591276fab9b0cc386390a733cead6ab3638e121a13cf8361da81507d1823a08bf68daf3b76c43b38c7663649a94b133028405ce5de1416b", + "expected": "2f3501bb8a3bd6b1", + "note": "random" + }, + { + "n": "0x1b27f2a6e578da5921ea8e9647cd69a91d60aa0d2b6aa1f", + "d": "0x7a676e336ab2aa7842d6a2280eb76fc98f805b561d55d2325b3ad038b69cad7f1e65e8f03dd8d4f969645be415947e45f1820bd57ca6a42f6537e117238b4fee2babb5e30d45bd37046073d0b6e2444efe493b551be3475c83dfce480aa0d56788ff96b315245cd024c90a28e279de39ccd3ffec7862c1fb7fffc93ab43a7c24b4e7ab69c2587ffe1ebee7", + "expected": "060c65c591e2b2ce", + "note": "random" + }, + { + "n": "0x5037c05a6c6793d1b49b979ebbb950eafd37730efcf17ee052abadac6ad3f9e21d97543b4912c87ba69bdf56fa20a483ea6a91", + "d": "0x2f63007fea4213eced", + "expected": "54fb15d74616115e", + "note": "random" + }, + { + "n": "-0x11ff81", + "d": "0x23718f", + "expected": "bfe03fda6d1bfdc8", + "note": "random" + }, + { + "n": "-0x5812b1f596bdf42553fe74e1a24b0d05c83c2c9da882fafe9457a30a376a4b7a5b25da3cf394fafb7f131aa25505bcbde2128be6b75e8162650c2654266c970ef0eb812783d3cf8c4902034bb73", + "d": "0x10cd607b84aad1c8feba34aa326616a614f8e19c42b0adf8d488189fb9c46f79cfa775dda79df6e5d6f3cad0b8b0d565d05a63e72abac2c0cbe7ec534ec72fa6422c51a27de299ab3ec7cbe32cba6dbaddd", + "expected": "be14f78a7088665b", + "note": "random" + }, + { + "n": "-0x6312fc4e558276a8e1c813ab9458bc4b15a45569cacaa98c8a9f", + "d": "0x32fec54b22b8ae236a44af5fdeab8ef82b9860b05c5f3e440f4d79109dd1eb4a35706a061541f0746d4206449a4d0d681bd14b9e2b21fd3bc94344a281393859f703f2faa06add4e0d11d97817b0009f273b1c52d3e0755872672263b58d0d68e084780a7d338a2446d126ca295", + "expected": "963f15c3ad7113b5", + "note": "random" + }, + { + "n": "-0x16ace2260e924d6b6b62876e30ac07db26d8425da566277bef6b9d0e7e8fb0a97e3e85198ac0f9b0c8a86f7", + "d": "0x137be2f998b4394c7d4349f7b6857cbf2faa805706a28fff1586f81df127b088f7af8110f935e16d45191cfc7e614f9dcb1eaeea3810e2bfcd2007a6297fd1a41bf4ceaa8b09299cdeb7", + "expected": "b0b29ee8be209ec7", + "note": "random" + }, + { + "n": "0x6505f10b44284d770c41651fe07262c98889d79c314113e9664382cd075af2dde44a552d80b5af2747efdb97c17b39b86444f616c545c9dbef0b31986c17445a05aa488f196079e4df5a16c8558d86312a6f90e2b7c1dd2998bba044918a2b03fdc2d079c339a650b81e1e96010cd3bd31e2a97e7d7fbd845", + "d": "0x59745e98c3a50534c9ddd099903165b290f943e4d6ea952823ab184642b97d9646a4e01b87c39c9e3ca6178af8af3faee8c6b7aba918f6425e230303dc89aa7f7a1577f3f05d8d81663c7b54bae01362d46e56799a9307c559e98f4720ca0315d04c772735e8ee8f6a4796716052daeeadb5179ee2dcea899662306fefdfaa6e9222ea3f8c6d0a1", + "expected": "387211b641ad4f27", + "note": "random" + }, + { + "n": "0x42f2e5d0d508a65856fa739b9583cb34c2b29dcea0476b7d4c1e04d0551700e25ddde27acda105c5f8d670edf4e3fac5fbf2ccfa5da08010abe071dab73c2c036916106ab41da8a6d88e745f5260970e73f31776c8e60941eceb0fb", + "d": "0x1412f405b1e7151d648f63bdde3279d5cef34aea9e2f808755c17af05fe8e9ffb3a389c57437011e3adbc2a95eacaea0bd1e4b3046539a1", + "expected": "520aae465812da34", + "note": "random" + }, + { + "n": "-0x64534e9a34c284b676359b8df67427a82fb30dc5df99479792eb156a5a6ba48aaa3995c3c99583dc72b70c62e3aaecb1b88220182a621c4eb36b04c2b4cb3cb819159ca8862b36a1e3ac6ef3bd827aee9ca2a3f3b5af9b12cc257e5c6cd1e2ffe6ad5", + "d": "0x7fa0963ffca500919734a60e4fb7574958d29dc46ed44747278e13d18ab192d95a848f615f16023357a945f0f7f051e625a7e0a9d188deda17a340b62ee1094b9ddf4928b6a6db68e14b0026cd56543a55ff36584ce6dd9979215c6cc5ae784a0c08acda93ef14eacda69", + "expected": "bbe92793ce73b05e", + "note": "random" + }, + { + "n": "0x41326e68d1b179f96c8864e7ea77c569e3f3a41f53e06382ed0c43c2fdcddffb4b43f644915c1a328a88538457ab21c5792adb6bc7c43ea4dc7662bd551b73124f5d57d2ba2251e6d0ed689c19dbf40b79449c28dd8efffa6856033b0e0cbfbd34ba71857d99852afa2ed4b4e9d1", + "d": "0x7d9e4a5c4f28f9a2826ac05db15aa2b350137cd694ef0a7e1b8cd0be4ff3dd492ecf05235303f9286c95f6acb2a768424f138cccdb", + "expected": "5c609bb80d535411", + "note": "random" + }, + { + "n": "0x30c8ae75a2a4c85f22a3bb8ae14d281e0b468ddb576cade9455f0581e976d40d955af51c1834fbdea2077f5c65b6dde92621fec9593cac57995195c3fc8ae08240bb5eb29dc728963452f9c6b92b18ed7521247004bb9efbec9f587c20176fbaf99415c996f245b24542d2ac7366803908c854a173512feab76590b80a0ce2c6d", + "d": "0x46868f3d917e4552b4c06eb", + "expected": "7a66228fd6fb04f0", + "note": "random" + }, + { + "n": "-0x5beaec1197e4a531e7f563a1c2120e8ac49ae076937ab9add5716fa898b36eada3ca8d81e00557c97e84c5a39c0e3b9a5c44f7e3a1935968fbca863f86c975159b7046e4b94810532dc6a8b3dac1bb7386e6ee2780b9c43eb10a108d8ee83cf6a5fec9a81e7042f72c9da6e23", + "d": "0x9fd73c1c93c9c4962c2a1c2fc2e2f071a29e3f56039289306b88da391ba882da2c2fd95528a844b6f91ec8365794945a4de05f8e10cf9abacb2965ca4d020309075863dbae18de989aafe68a75e189", + "expected": "cea266df761dd90f", + "note": "random" + }, + { + "n": "-0x23aaebfa9bb28cba68d6b848a22dbd804777a258715296b2aeb395eec8d67af1e1e0fba9dcd86956eb6d845385c98bb433e6f8cc0bad", + "d": "0x3458a5a6bec407c7fdf3ae5552d51fa5e439e2cb974ec4b40d0433ee85552ba96acdbeb82659e190453c1c0619de310d12aec6d668abf787e382e5ef005af05e978795bdf03f449078dab848e966643a8a164752403d3c4ea0f2f11d1e59ae05937b7ceca40efd94cad9beb16320f2b0c5042536b31f71b5bd4a46b113ad65c5f806e0bb515c38ae4875d58a8bbae253", + "expected": "92e5cddbb0da46b4", + "note": "random" + }, + { + "n": "0x1e7c07930561eb9782f4413b51a1b743c6ab74673cebf86a9b7b988af64f1d2eeec843cd7", + "d": "0x15ce07c2ff33e7aaac8a4adc05f7e1d924381ed0e277bf8bf3cdff61bbe009cac417161a8d2e2b56ed6517cf3af84c9", + "expected": "3a765e773876e6a2", + "note": "random" + }, + { + "n": "-0xde9834d438d9b23da446a196fbba1207bb17169797d0d75a4ef7044027b26d68a449dd54c96e34dd576bb07632fb0ffce67793e866468f5fba83750a434499f4e7371b859349679812da9", + "d": "0x654beaadb7dc10d3c54116f3a1bbc05695366f12116272c323c16d65bc04a736c1c6804cfda8c28c56dbde57d6067d756561c31ceb70e3fc236de73a41c0f99fcc2b3d38560917700e2cdb7566b6508440b3e317265cdb1b472ab9aa681e4a7bb9b5fda907e27bd388c515888e847f48f3d379d89cfb6e276b25de69bf413da82a4de63ab3bd5c3321e56de7bd76b891719cacd1", + "expected": "9b41946302b05f40", + "note": "random" + }, + { + "n": "0x2f831f7c84f54cdd4730f7c88525e0e218e7c02911f56a67d226a2e09b9f2a87aabf1d7ef", + "d": "0x1de783713b7559fcd21a60d85720da1b87efc102ffd47f24b5e667eaf", + "expected": "43f96bbf78520f21", + "note": "random" + }, + { + "n": "0x5b5d26ba54ec39e4d7f64ef1ad655d5624d003b896159f145b1ca9e21bde262e82c7021680235603d1cad3df578864de4c1cf2e5ba0493f849", + "d": "0x6cc086d73127957d7269a4083d494a1bc16448507ca270d04907801e59e73ae2d4996171e2f9e5553abe4a0634f73c6a467fccbb5339f2fc4e7750c73b20455321a96571d19a29deb283b2f14a86325072c6fb5b7d7843ccd51898d2f17218a526131f157f490e7b20e51c575851c58e545c45df86b", + "expected": "21aae231dc34b2c2", + "note": "random" + }, + { + "n": "-0x314a6dc99496b821e1a0bfe825bdeab5850cd19b6c57d361aaafe199700d63ac2397a798e0a513c3487d2a162180132eee7e26b9f4ccc88b33500d55033ebae994afe20e40a9bc67dbf795cddbd8483ab1dcb82b2204d2c000453fc5b", + "d": "0x1888e0e0f3904ba31cdd3fb2d68a35d526205a12eb1f4b3c40bf4a541edff6b6cb5ab47e04aed45d23d2bde9b09c3b8cd36091129bec54fb0ed6bfb19b8249be95ca991e1eebbd9432b9a15848335cc8f46771ea5933fe609da678797cbf1ad30f6ce80bb8679c582f1a60e3e6a51df72fa307b39853317f57c2fb716bd8c4c77c757dbe35da9813642cd1c99650b", + "expected": "a700127a9e919cf2", + "note": "random" + }, + { + "n": "0x85988b504d04920c18380fed3cebc7ed04c913a3c4c8b41268fff0e0583772cb32aff37fa1d7f0322d12e96184a7739fa17539b902b5ad33432e6f173b6335e7508712779138eb6400ebe473709", + "d": "0x28740cb1bc487aa46db50b63d65a099", + "expected": "5f0a6b7572a1799d", + "note": "random" + }, + { + "n": "0xb10b13ea34f7e95d6ff509aa271310481ce38d06b082039fd679ffd1d8c36bde3680c871268e71c7bf23a9bf142b9a4a9fb042566b2852a39015f6cbcaa0b020aeda9a2da873114dd6098174672372f262dfb2fbc55cc7d5c0e10d736de66fbf3bc726c27b235c66cfc89f03daa854be5b1d6c67a7faf8262919accfa9d7d499d95d8cd3c8aaf", + "d": "0x2ffa744e20faa8077f4e16ddd0e5ab3febba4facfd1c3a07670353a70168f24ea9baa1b407cb30f7e2e5c0cf35216629117a9c51db8d07e0348a78bc923eee6fb3d4f6a0baed58789409ab5995507e499c37effd1c48deb282ed127f70361c908b5deab2351207e63a0983ca42598b0694920a8e6c6d684ff7b9", + "expected": "464d8541c7d6033f", + "note": "random" + }, + { + "n": "0x14869ea93e35bc29aa9d9e3517ccc0935f2ab110af27ee8431479ed3ddbabb33f873c321d5112d77c7132cd2ce1ed55216272f8da1d0b6ee3139fbcab5647dd6edcd1eae5fdd026f1bf8dde47e00316918502b1f590069798b775ab23c5fe2d8bfcf3d69c39ddfb38f8d58f9b260c29e6422ac1235d4cd72c0a830624124844a44e577324f4afdce3aec5236a127796f756d81", + "d": "0x6a0bf3306048eb25e40321e944195820622f712ec5c24c86d84636a70dc053ee5c6e41bf821e9a7d7d21e8ac3014af99113cd6582e76d0e7623cf6f2e2b06f50291e906ed77b83bfbc6734eaac6090470576bf1e0c344d3fb6baa92205d736c672d7718965947681f396f158e72bc8ca20ec92d", + "expected": "4f88c668804828ed", + "note": "random" + }, + { + "n": "0x1adeac3560b265b3787190f243c44fe5721acd0bf166df4837a30b186f10337092deedd871c95f", + "d": "0x25a4ffe44d227b480c2034797c83efd61e6ebd86a8926dba5e6dc509bc7bd3ae5b8ee70bdb61360430d3abc15265d6c8f5e7d6d15cee4e7227e9a48efeaac95e4f292175607616aaf431eec1c6ee0df56caa19d02e5ba862102a865b6b215e09c88a89df9e669e1", + "expected": "1fa6d743be23f709", + "note": "random" + }, + { + "n": "-0x181f68845399411b6bd6721fac7174fc6fe24c6eb08fca07e7825851c3538587ea49b063ad", + "d": "0x2cd6f2a0126fed750aca40114e23c1f78d9e0d", + "expected": "c8e1371b3f742c62", + "note": "random" + }, + { + "n": "0x1d20fad456e3de78ceb74653e91769ab4d4040f1011080996766c7fc2d51363be12b312d041ab671cd14294424b17d08702aa57ac851d51c1f0eb4070daa908bb37551276bdaf963286ea98092c054b1d0d", + "d": "0x48ab774db22619770a7a3e23b803b26ba3458f88cc4bb5f6cbc0b0b682e0b8b07", + "expected": "5859a7554202e577", + "note": "random" + }, + { + "n": "0x2ef68787eeb6a09a4cb78a47ebbf4e928ee3b1da7959111efd29689930352442e19b1f681e4d40dc8023340ec29e920ba84297f31643dc2a7a01754c40224e7101c4b8108ebff75ff90c382ab30b7060b3f341cc749fc8c7bdbace0633016cf482f6bafc9d282c1335de9582d2ec3d6cf15f3d2dbb142f7b", + "d": "0x1859319de36f1ce0f2ad29b3605115b251cc476ad89567dfd70772a5e10051636d6da8c1b31927364b3c19ee89f0e347fe4cefc0f62a80068ed49277e8509a99496de9af0463c77ba8e602a80ab5", + "expected": "54fedc542af8d877", + "note": "random" + }, + { + "n": "-0x1742cc61f6b3f57e41cac9d22dfed3589b402eac9b", + "d": "0xdb34662028c70d39116855b396dbb479f33df39d167320fa251e34df718710b9b0028db4d1d9a3005fb60f7c5b008da398da7e53eb01b82490ca6b47d3cc2a7507198f879bfc0df8aa5952c6eefdbfa7b2bbd", + "expected": "a0fb2a5c47391960", + "note": "random" + }, + { + "n": "0xaa3ee1415eddc9ae66569", + "d": "0x2f7928914dcc307792965bf412ef87969b19010201283ed1edb6ff2ce79ad976c20008d4ac593a6becfdc8f9ac9827c44f", + "expected": "2cccb0681b2cc205", + "note": "random" + }, + { + "n": "-0x2d09e6c73f5da504d60a0d8822a47d624326126f844cd4d1cb4852cc54db1d9f50fa86cd5f232bf02e71c8e121f1bcaed3ef999168f31adad205166ad53ea64c997b342af2ec5cd7383", + "d": "0x69fc40da84bee6613c8192d976dbe216f3c2cdb4445370f81b531516d3764902474bfec8d3fc2414819a207e2f4b18c7120f357bb6593204eac0005daad82ba63748920d3bb1c7832892d990950ccc0d4dd437a7b9c6e83c7138933816d3620540d62a2d", + "expected": "b29b32694794caa9", + "note": "random" + }, + { + "n": "0x146482bbe6f8aabe00da2dc08a4492d9562713de92fde4730fee424acdba92458a8db205c528dcaeb4cd5f3ab222b65c7850e008ece57087a0b57da34ebf59a00e235986a18c0f387a2dc0ad0c87ceeeaa47dbd2163fab5c1b3bb469b5a49f443197b69dbdf131f8d5de55852f8035505885378774417e169089eb2f", + "d": "0x48d3e4c2c6464fe6705bf473a1d5ab11ab491495d80a2847b1e7202dfa2f8560650cdba7a32180f18a5434b8fdd36572c709cb25a9d8d8342365d00d7b39005d81c469ee8ef167387b88aee86f385771a01464398058a7e6da6d7f", + "expected": "5051ebb6b8342fa3", + "note": "random" + }, + { + "n": "-0xbe506ffb5a71a77eb4ceb7c5640128c2fa9032a576c27beefc17ef90c6ca8c16da92cb1302376c0c63f75f33eb7040185fd62aedeb2a1f531126179a1210d86dec4ad4f64842779dbb767213508c07d994b0cbd17c871884af0e7eadd476a078edce8cdc6e8630b0ce8b4b75b1c013cd3b46bf50a77a9f1beca10ee0c1921aa8cb99b45388c1b6024df", + "d": "0xc0003ccdac4735484be6f701e6d4011f1f47b87b95020eafe15a89cb82bb3958764a130b6a3a718e1303641a8463008f83c6a04f665e11efa6542aec4c94f1535a74821ef8ad18aa3496c6d4975429f7fc4aab28d0d061f17e6be3d8f66df0034f4229d2b45b9e74ed3de229fd58fd69dc9f67e03c03c8e2b34e25b3e94c195f2e5c1c19a2899b88c3", + "expected": "c02fb8089e676cf2", + "note": "random" + }, + { + "n": "0xd2ff925a7f55762825bb148dfb7257c98aa745ac38ab53ddcf35d45f4f2666b92b100caca8334aa9b5ea2ca378e4f94950a49525", + "d": "0x14df17b6979ab842c49398d92cc92cdb0ac42604af72e972ca95dbab4b5c1c38cd2054b41dc93a9d8c7396ce518dd0b37101bd9f9de63b3514732d642e9aecdddcd295", + "expected": "38a43806005199ed", + "note": "random" + }, + { + "n": "0xa7e8025c5af76245f9a4143f0c582e3db81016a3445b42d902bc7093d1cdced66a74a8d160a8a99a4c565640a423957cc7c61c19eaad4eb6cbc6b4b89a2bef", + "d": "0x36e1f9b90e2a796174a242115f6fd405cd53ebf317be91f6466b2a28d996348f5a87f838bbe37ca520ba9243feaeb9049cbcac599f73c3435800f6b42c3a9e6433c3655319f82be8d06df7ea6a6d0135c87a9cb8a2789d0a00b9da5f9eb4aaa867996699e513fc7eca6d354271bd65558feca5a77921db2d2ac3d930af23e405db9d05daf69aae6aa6e98bffcb86e73ab3755", + "expected": "16487994949ec04f", + "note": "random" + }, + { + "n": "0x2af91c0f3c0e1ed62800d84518bd4bcc300feb10321c70f6fbd30038ae706a1fe1ff507930f5f916a41", + "d": "0x7decd57ba11abd099bf10f5fe27b5bb3ea2fa65d4fb7d250cb0a1a56df3bac1d3308c98f0563e31827bf5f315c7222daf15cd0d68f8d4f7304122a3475e5cf292f9db26a671bd63ab1508456271792a8376877c0adf6a626d49d7f04bed85aaacb1bb28541ab88fec6e346df33bcf3793cef2477076782b5eb2ec1ba4061cf79c25a6cf758698c3485412c290c0346006f9c4f", + "expected": "0b15d72ff9d64eb4", + "note": "random" + }, + { + "n": "-0x3789d9def37a46d33", + "d": "0x93f0c51958134220a98a9ff884a6423427a35fcb1b4f71f536034d5f15b161f545943962852384a9cd631a1dae90fd6ea70c4cf5ceed7071f7a756be124ffe6e6338394f9ecf8207759d70cb074fe1f44b31720ff27d07acfe8651440768de8b86ec0d3", + "expected": "925806bb7c5c70ce", + "note": "random" + }, + { + "n": "0x7f2f89b952523aa6ef8b890a2950716d2fef560586bc127374091d7bb3ca1511a02db79aa3f95939f35cdd5ea34116eab33fa51c99c59d7ceed96ea9cf87e0faa0bbf4c46c9deb0c9a360e2a9fdae47d5f7856a4695b71d37cb63afa0016eed1e337c6f71c8ea4c6eb05867f4a4ce282356ba9933a600775b52a2aae0f86c06551323647af259193e909", + "d": "0x140d4fc468b3872fb5de206f9c42f6afacf62f8a7d25d8be28a24400d6135a181c26049c8d8f8ff7c72b2dcac004bf15d5d7580d16eb55eee55149493cf182fec5af1ded09920c115a507653d", + "expected": "5ed95f0564452a55", + "note": "random" + }, + { + "n": "0x47af0601533d3c334caf8f3dc188777c57bf956a71522e34f7c6a4ed1d5cf1fee41734b3744ef77e7a15fc3e56e4f7f07fb8b95bbf71d436b4b521f403d65f44e0e41593ca720d0a9a8f3bfa3d59d", + "d": "0xe2a05d61aeebd5d925bdf8a887fd9c33e5ce6f3d6313b9c3a8277a7e039b2b2aa0d1ec69ce8b64df2f29b1c89", + "expected": "50d43e62b100f926", + "note": "random" + }, + { + "n": "0x819e1b6334ea0a1f055c229", + "d": "0x6a22ccba58b54be29157a669304bf79f8efb17028d75a5beec86b0f8b2a8b11cd6979b9ebae3626f4281569f7140718d4bcb4b936ead2d1ce3fe4dd8a17da503687f101874ef4f91264df0455", + "expected": "1f738a347ade2602", + "note": "random" + }, + { + "n": "0xda3", + "d": "0x303cdb56cd273a1f31307bd6e8d2e243fcb2b48f6d27aec94c8062484a4e8273ce9b1555af9d", + "expected": "2dd217ba54c6b82d", + "note": "random" + }, + { + "n": "0x29037479520729603b6c56e4017ecd21ee24e0abc2348b003ec9dfeb4e6d882324ab8798ba786b620c153e279989cea7f7bed9b5c031abdbd930e311fef15c55e226b7a7", + "d": "0x17ba96d81d3b03018e437d5f3428441b0a54228851c82a95779cdd6147da671c877b05cf898bbb6f407a2f17413c28143781b1f4b1a2f527e0952fdef06e0e05f56bee12dbdebab894f7aca5eb76d5b8f146fcede1acf9b55d9d0af4d05416692f0e30228a8afd64953a404ca75b5e6c8360a0035abb52ac512573a3", + "expected": "23fba79e0d3b371f", + "note": "random" + }, + { + "n": "0x7cb7dd69b7d9cc1590886eb2851aea41dd1c51e1364d7834420327256f9ce11884e3526d8e894b3156fc6c67f671bc11eb424dd672a4ec7920eb4e445102133499454db866494dbd0e19a86562fe5af43669e40c76a0fdaab149bad9b0c6c4844d9d091853c05b2c8d7a4c926e9332d0885a244a5ef4712ade90c2806a10c883af2102f84f05787ee12d64ad587", + "d": "0xc9999075e6d704e339abf4778d9dca51a7ecd998003295ffe904ecb758c6302d9e0bb8550d008182479f9cc83bb6f5ac84a2264ad1a596dd51586fdbcebfa2e1865074330825594971b81f974a98b040b20910e42072f556b7888318b8d44209", + "expected": "56a3cbeb274b5df5", + "note": "random" + }, + { + "n": "0x3a9be456c47733c050e544405d61520fdec88af24e71e8c404dba004fc66e90e065d6e1a0543a629472c2481", + "d": "0x1be3ec46392fd4c87ffd21bec1d264da66d31fef595fb0b1154858a541402ef83cf8a7e70b196df75db04c16e33b9caf438e4f0ef795894dc6ceea31b32d7f59bd8d56755a5d7c359475a35ca109ad762e1ca3573555363e33f3d606b173a949c65eda66549dd033ee5ffd782fa6bf244301a1ee9fc34b1f1c2f7980025c0ad8749", + "expected": "1540cfaf0d748279", + "note": "random" + }, + { + "n": "0xed3b1df988e06d8383b2a33afd59335e86c1e292d08249239c8580e2e5edd16d39c5ad5debd3919311549dd9cdf6856931589625", + "d": "0x2e2141f7dde4c1736b64f160647f5017451fd83bd72dcf6582fc25740d2767474009ac3a3a0fcf24c3cc34370dc6f36682250df6031bed364d23050454e23d1978d5ace72a15950c8735e35ead7623967ef78e4af7a8c5c13c2b8fe58738f", + "expected": "2ad49218202766de", + "note": "random" + }, + { + "n": "0x64175a455fc05e40c1f019c305183c9b3c18c2bc255fe0f9aea553a71", + "d": "0xab1879ec7f5b2dbcd524cbf947030517ed6a55fe6b6e683b8a4789cf", + "expected": "4022b85576a07f4b", + "note": "random" + }, + { + "n": "0x3355cd834d2cd04d140dc7e450ace6604625c4b86fdd00a596c8b1d96738220a882d2fd58f359bca5ec590f06d6b9ff934caa00274b644fb7dab1fa4cb014074a4473a861e9dcc6253d318454906ac78d36f41451d80ed9df91d8abc2036259febeb5996b2556dfb9fab008f2c2cd5", + "d": "0x4bb5383577a4f96a3f8ceef0427f76f1888344862b5ae206bd5c401d5ce79963d050f338df7d66c3b058adf0e7e087228ffa7cb24d31511a192cc510d51b24511690cd47b0b1a9dfc7d12d4b", + "expected": "5165b2bd9c0ffc08", + "note": "random" + }, + { + "n": "0xeed133009", + "d": "0x21245f5d10442c06d0af9b69a055cbccd491e8f311232c9378455aac3e4568e5b067a9cc0ef8523cc2e3fbf95733a997d7078dedfef9db049793be4e2d5cb25f3", + "expected": "221cd2ccec804510", + "note": "random" + }, + { + "n": "0x1bbeb48776f5bdf2d184b32b774370660b617f98aa42a7f86ce92ac9ce2337d965c22ab0a27d1442d49995d9", + "d": "0x3817ce440890fde6f8dc1b3fdb7dea5aef62b557615", + "expected": "4b1fa7eb9c2b376e", + "note": "random" + }, + { + "n": "0x3b2d7becb953b12418dc5a4c9748250bb7850c2dd378c012eb04590eead22dd2ddebe536ae08a9e5a8c80196a8d07", + "d": "0x15cab963dddefc7d35d1f92579fc03095a347302ef884696686bd997eb5d7287599d5ab868542cca2e4d5235fdb59f145fc81d4696cc7d7e8a40d69ec2fc1dd9e82817886698fc86e2c9b2326a1eb16b77d52b46346c21", + "expected": "2bc5b983290cc40f", + "note": "random" + }, + { + "n": "0x19e8b1ee417bc08bd0420bdbb73e5156136428f", + "d": "0xeb57c5bd61b23", + "expected": "463c2ee0a8d55caf", + "note": "random" + }, + { + "n": "0x6629573a3b0fde52b19bf907c19182395e8a5990d9998336fc0a215a22ec158369cd56652a96aabb0d575cfd43f36550a39e4a4f157203c50ca453ea0eae112632e56a0921d86e344e30c761ea5e8026c062405615c7cb", + "d": "0x10ff323e33327e8134e7beb3c1a52387c8c0c452ac0a2f3", + "expected": "5fd80add2a521b9a", + "note": "random" + }, + { + "n": "0x71d8b495baca4ed7eef2d6fcd474a57b1c1058d616ac4188624bcbb8f14a1e45a6beb7640683980d329a11a0a45", + "d": "0x55718c5ec3d75662203fa4e2eaa9f96af1a96ac8d358f9a5fa8a500b87aa6c61ac58fb30f65973c1f52eaeb17bb2e811ab0e63b2c07afd5862189a957a4bd9102492f136eb6e2545f02133125bfd455c719e72358f28534b1989c4b1ed29d722625b8ca93571c8dc8b67ca785622ec90e25d8b", + "expected": "1d3551955721f73c", + "note": "random" + }, + { + "n": "0x34959de4765388fdc8771b4637547caa0e3fb79657bf4a985cfa7", + "d": "0x346668775ab9f2ed6fb6be6cd37e71bdfa2d530523a1e8a3237387ae613b7113f86548001b87a397147641af50fe59b", + "expected": "35700e6a3449f829", + "note": "random" + }, + { + "n": "0x90bfeb5c8ec748798ec56f2a041db763d26944231eaca279771733593e6cccfba67c61d2a80af1edd96cb5440be93cc1e8a29f79ed0395973c16e34f", + "d": "0x8be77be7b50a3ea56537cf4802b79e66dd41ffbda9e92f7c90e0b2f7c5546ede4d2741", + "expected": "4c708ddc6fda003d", + "note": "random" + }, + { + "n": "0x2ced4bae0590d65d244abb89d6a0e91c7187c34d6bcad7ee394252992f20f7f54b77f659218cf6022d50d3a45269326731", + "d": "0x1ff2be010c04ec4097a7eb24842ebf6f39db01f157debbb39d7aa4010581394fa5f45a097b867c323d9847558e442960e388338dead748b6b5ced0d2bce68c55", + "expected": "38767ff83b0e4cd3", + "note": "random" + }, + { + "n": "0xbf75e11b247c40ec56f717fd3aa8871bdaefb440d2fcf3c582fc339a37ee045eb567b7c8af5aa14f33f237eec12499449a7e0f80c58275d7426dfd2b1ba509b9f50ef693c555ededb", + "d": "0x81d12f277eaf907442f9384f8aef61412a721e9eb0d94133a129853c839764c517ed65ba9ff0cd5bf50f2b43850628b92392e1ca29c0ed2ab6eea346ff2ccfee126c9c89c2ce53f865c1277bc8f6291b79cd9e784dfb16108a56bc443e31aaf2c943a542b46ab3249d26f3907203ee12cc6f3c847f4436eaabd2377dae2e5f059", + "expected": "23f798f9b6c6e8c8", + "note": "random" + }, + { + "n": "-0x64e897a98cd5fb7d5e4a3aad39ef6fb8f049e56aca4af66abb5cbb789b39750bb14a8cbf78bb2f09ab76a58b4f39125cef3346c4c18aeabd6e7917b1e316b4e65dea347716ce9166227e866f3397777f19abc338a6f93b6ca39106abb9114261a3b9e83a6f7fb897e277dca4249696354163c01fd36f8ff351a5672a203177b27c51b55f677c3328f0cc962e7452341a3e7", + "d": "0x1fa136b8df873ac6d74bd63d29023a2e7286c5580822403d2caeeda0b5409e557de2b25c5c66c86483ce77fb36ef09ce853d0e3856bb026a4a4cbd10052ed735f456ecb9b5bb50a229863cdd74a890ee0e49362e67f", + "expected": "de0985bf55421dd5", + "note": "random" + }, + { + "n": "0x8f0af353733911ebaaa54e670b07049d4654c20d2c664b0cce612cebe15bb8d49d8ae42344b371fe7a5389d0f354c71f0cfea94db7ab7c20bdd7f4d2d83e33058c3cc51726c7a7", + "d": "0x572b631a01dfd298e04d9c81d32ac0f31d7930bc4b5c79a3bb11b42f2edc06b", + "expected": "53ba416d08a11a25", + "note": "random" + }, + { + "n": "-0x5c82ed91792020b8a1f4fe57a63453354d98c34ffa854a359e355635fdc463339a5d859", + "d": "0x1311ef3ff47e5f41f1c6b7a025978fe4e7b1714a725031b2de94f93eb82fbb5329e429c794c6a234824bdb147bc75c85eebf94c12cff221ebaa6af3d00f9feb9d86bcae738a40db96322ed89a1c980da31866256552fd8ceeed245a173c95a84c16d21f8c422798e9", + "expected": "9d9367902aa7b99b", + "note": "random" + }, + { + "n": "0x378e495e0915b304fc1b8f0ce27b4ca0097a30e5fe2c7882ec1a8e376d86e5821aec435431f6cd4182458522b6f38d4f40f10b4ed700819bead8f00990db2c2f027db4362d651996292f404d201631ee3fc5c9c0233f4ca741ab64befdc61e53c4eba8411ba278f05aac13fefacc0237fafa0db36e866f6acc8b61419eb7c226026fa89bfdb55f", + "d": "0x5001b2a70ec5134a4a433ee5c5653b29dfdae93ef7ebf65d0018ac0be0325774d05ada7f2755f390bc01", + "expected": "6e6638716b68ce34", + "note": "random" + }, + { + "n": "0x1c7cb06aca38388b635807328d31338c2cc8aaf7", + "d": "0x8fbd604018b07d46fe502e657a18f6e958a6f7e5fc96a4ed7500f5f67f16fd775b922c17b63fd44c3ffd46ec53cede267b8586f4820d08bc5016f20b99baf154dae32f2dc3ca7e9091898d91dad142a7b170fb8ce6155f08655cd4dfac29605e4b73c966042ad706f5bb1584abe9658dab00f87dbe31", + "expected": "0ec95e208b2262a4", + "note": "random" + }, + { + "n": "0x2f38e1ddb1e0ae9bb9934549c95864bc793a56a43fb6c7a75643e3217168041b0b6e0e4ec8fb69f", + "d": "0xd28509fd4b7031cb594b2c728a3a0b2c7c6a84036efd3c602176a4206a65e257ceff31d20bf39ccbdb6990240919688a79aa2eab835a2b79f4d063fe0d84a1b435265b86fad502982f1dab135d5b0488b0c242bc499e208a7", + "expected": "274cb645a0bd6bde", + "note": "random" + }, + { + "n": "0xb41921aa0398956c418d929267cd2080c1057cf4f5e5bf5acd525548d5491079d1e5da691a1d8da11960c021b5cc9eabad943d6db50c0a8e1bfd5d6cc19c7110c5163cb5e8e66d402e1b017b15cb709096ff87b75c3eb0f1236ee8c2dc1a56a2065b937", + "d": "0x1a77e1fd8ce3612b2491fccb629bfff3036fce7ff79a6ed9d16b1517d7ee90c863e52824339364d06f914bcb48df533c72b", + "expected": "591b379a1f6338e0", + "note": "random" + }, + { + "n": "-0x61de8049d041a6b9295f5b1d5f21b12525ad1e40c84282bf905aba0c9a17bd266117b0266ace13a75473976b139d62efd0ecfe61142e4c1381c1c292822b9841e23a793c47cebaa2f73b8d93426343eb4eba9eaef212b34628add69d6747e468888986d19e9", + "d": "0x1098d482cc6dea9589e3a89ebb2b1fa001a33eb9d32e229df065c2578d657c866294ab44cf8cb67ec422fea0d9208801a85d0dfdf957e8e15771eeda1d515f451fbc71279a74b2da87b6117a3221584f0002991b85ebb7a83df6c4e1a8c8b8abec22e1d8c8cb78a61ca94a48c90fcdb0a2df07dfde759084ece521288a1009a6b97426286d1a26a3cf592062639fc5", + "expected": "ab579652b8051ae7", + "note": "random" + }, + { + "n": "0x17061c40d57c4792e72e7b04b6daf3801450b909c6a6b4de5f0a632fc8e744352d5c5fadcd74144d879a80a05e1aff16473b5d3ce9ba657411e1f3a916db940e5", + "d": "0x6aa95b0e83a77ec1119a56db453467c1dbd48755e4ef689cd90bae19ad89b8d9aaf57800ea5dd87899779c81b838312cebe3e4e8bc5b8237870ef4bf3a91", + "expected": "410ba145a324da57", + "note": "random" + }, + { + "n": "0x6f550bcb7a4337a98416aa9ffdf9dae2a9bfdbd752fb9f27e1ca7b72c9b47fccc04512815816889a255c80072ebfdd82c4abaf57b124216876da1ac25d570db678e741e2c0ef2169782aa81bdfa9a3bbab5966d04b7fc289e6d0230cf79bc9bef6cdc9103e33a40f1bfb018393c5bcf090ac301d75e4ef0823dd603d599e753865025cdf5ebca0eb40eaaf2984748d7facec01492f", + "d": "0x74092e46e9a07cdb4eaade30d01ce804a571e61f0e23680160f8d3b5b8beba9ca0115", + "expected": "792eb3ed7584ff45", + "note": "random" + }, + { + "n": "0x19c9a0d750e8d029a1a8afc4a8f0d96f7b5ab607e66857cbed6ae3ce982478c3358f8cd1b6fc3cc25983d6389190182e6ad30a1b48dfc8101", + "d": "0xf55040d949aae157b0e6b1a1b818f3b72f6a91fbc3a3dbb6f545649b34e9acba66c7033a7eb3247c47926dd6660619", + "expected": "447ae9367c7e62ca", + "note": "random" + }, + { + "n": "0x10a50542155a5556953d56c0c0ba90a9c7a3369becea1d2c3cc6c50c554df844e65755f42fd7fedfaf72d87cbf2f5198b8fa3b1956299590ff2ddfdf6306f7e9893b8039bd2d5428a9b8f24630eae538063dd556722cb0f67f6545911bc36dcc44b9e9285a049", + "d": "0x3aed4215ce5f3d47081b95b2e443ff41a8e870dea18f824047652beb231a19add82008fd3087c4699ac51f57689e7872104b0460867cac15c6fe9eaf3b3eeb7b2ddb367ae4e1ac87a5913ff0eb7c88294129629", + "expected": "495213ddd1ab6df7", + "note": "random" + }, + { + "n": "0x169def832b6efc9d79fcc55c9def6236a15d57829ba9f1e91efc0e957f7124e1ea44474b1418450ce975050e9dbcb9ebf6d0203cdf9a84bb9fd4966d9a82d39dcab2ae2f4465567a074dce8e01cb141e162747ad4819f054f6134f4ba81", + "d": "0xe9a532d78974115f509a89d0e53b1e9aa29c4178e33e478ea673d15a3d1163039baa2c881105850eaa79c0132a020525ea66f3fe6fef6fc2bb784210d7cd20fd890a7a9aa1b39dea0bb1f6623faea3100775f62da14ea8109e4cb22ab35e1d1eb231abe30f62c1f2c4c39d23", + "expected": "3878c7e78ca87e9c", + "note": "random" + }, + { + "n": "0x4d1d81cf", + "d": "0x1b06a0c7ff9fa52c02a4235036438dc761255", + "expected": "38c6d3bb548088aa", + "note": "random" + }, + { + "n": "-0x450c4c635cd1a32fe08361fc92d0aaf01524db7a92cb0b7abfe3", + "d": "0x8551eef3ebace716026d0c1fe9c82f53f21376ba8e28af5805acb7227ba4c81a04d0f4dd7452c0631a1474b32ae306630ba6fa4b898bcf5f3a703767965a249471c51b2c24c1c8ba7d299b7b7bec7bc6707ba190dd61d115f8920b142da0fd1487bf1949dac83b35a4df823de1034770b27e95bd4d4fe40b42e64790d4ea2aef1", + "expected": "8ca092bbf5e047e2", + "note": "random" + }, + { + "n": "-0x21820ba6178f1f4dd8c76ebb55dddc36666eabffc0bd36337a26b72c6f6341f22792b43ce9072bb4aa79dd50ca3a6dd9f0b12a78f2e3b3119ac9bc2e80bc52fdd7bd1", + "d": "0x1a4b1490c05505d9bad97a19da55486eb5c450348bff065fc8a5345b16e94d90b20397c67a58ccca09e173338bb86bcb4e9b0b315d98043b8ce938a30578639f5568e42d75105887125a13ebd6ab5b5152679143d37931a4563e34e6a0cb19f149a5636b286ec6a9076b6b1c7b52426fe95a801129f5d6e29d", + "expected": "a4b463ea835a107d", + "note": "random" + }, + { + "n": "0x43f79016b5ced686cb6b646261e7cc6e290a76896ae86328e103e00187a9d0baf7e775ff3e7b571437bcdae414d0cc8b7d317571787decbc1e74d4b269236d15e16795330115f60da7956acf2be1afaa2931fa09938663369b46472c7d44a97301f450af14368ec4a7970d44ac28a5a85df3f84d", + "d": "0x17f02efc2d24b6a0324e033361ae56a2ce0dd4415c0a4a3df8617e443b01b6a4e4e4185153549c79c677cec4ec9b375c7ffcda79005703d43145511c7dcbdfb7efda5975afae15b2f3db0e85dc4eca5f6ff58d9c9460f7605aebb8c3f8a4afb", + "expected": "4a46b6d2c702eac0", + "note": "random" + }, + { + "n": "-0x8382e4b8a37a65c1b6fed410085c540cb8e5e7e5f5111780923bd4b96747c323317e89a29aa5ea86117470968342629f38fb2fb57d9b455b60979571e08dffce5272e1", + "d": "0x3c3b6bec213789f3ec9303b2b1697a1875bcb1", + "expected": "d801779ec8d0b850", + "note": "random" + }, + { + "n": "0x193db6b14ea4c669b0133781d4c4797c4511fac932c7bca02b2bb95ddbdd91f6b64987317aa6d730c02020d7c194e1ca0c015c02662ac415619f94e4a8df149e996cdc854e77b6e4725c774f01", + "d": "0x161338647da32c425b2df06aa74bac2fb436f8ce69a7b4c7178391aa3d2d0ed2e6a2eab78da822986877f06d9759f90365ba4cabdf91fc961f0b0f0c325ef59ec75e97b6777bfc53041c1c8ab618cd161889610d77f036b907444dd19304a23badb55c95d3a793b0498e3ac9346fd952e7cfd5c405e92b5dc207bf4e9c6d7d7f29cc1e3679d19", + "expected": "23324b71e80f6d57", + "note": "random" + }, + { + "n": "0x1a26fdb076fc4e509c4d3482b069dafefe01fffe9e9e9ff957ead9357115830fd5f36ce2b1fc78fdba81a0d3cb28579bb28a07c211d9fbf9522b714274d415deb330d144023410ec8138134a87d086b3b4f7f", + "d": "0x19d91fa10e44f895d4f9e427a6332169f90ab006994b20df5ca610c6d03db48ca0feced9b0b98282271521064e92d09c464ceddd54d6d9836e17e342ab146c89d1be1855f97e1383347507688d078c5dc3", + "expected": "40b030332f510bcd", + "note": "random" + }, + { + "n": "-0x39fbf57683454d552202dc1f1c4e9e7d850020c008826b5709e6fad8b0ee3fd4b0143c9", + "d": "0x1fecc7314f94c26af0d14be5bcc2e37cff869cd43718d65efd01e67426ab1f6260b6c51769ec181301cc25c3cc06eab463dbf92b335649132edd01de44596373fe163fae3ddb22cf88ae33c63ef7e2d5d2f95d98856deb9df47", + "expected": "a4fd0f6f7c2a08de", + "note": "random" + }, + { + "n": "0x130a9991a599282e4d1c244e51be08129951a9516b655", + "d": "0x159d683ac18b0f0607f74908c24f0f89ef5e5a25ff84a434033679c17c250bd354c1e9f1c04e200525ec22eba2177e2402d66587f0c28f7ad81cacec566444c208ffd4f359dde1bd6b32cc7e825be870c5db43f4819142cb5a53", + "expected": "1e2c30a9275b8295", + "note": "random" + }, + { + "n": "0xc35b2b5418a144dc46913e7f4bc7aebb4748b61840237e66e4c185397ac3b97f3d1c1a5a7fee724af495109c7af12b48988326824348d8b8fe1f6e5e6edbbcc9fde2402c0d485582f2fd2e4bcaa072fdf9d4d80fb70cbb5d451b629ca2abc6b86f9022a6f84be56d", + "d": "0x3808c32bc0abc80682442baf7a8e27455051284e8712263701fa4a4b200b41f770855859", + "expected": "620be416af9a0d23", + "note": "random" + }, + { + "n": "-0xe012225fe5169c5eb0433b939842d1756118a6483281", + "d": "0x635897f645cc51104fa46f86914683461679993b24e2162e10ee95d1ddccf87d51ef9047b8e1d0176b1c497602b03f5ef07470022a1b7732f432be0643d60becebd28cf26ce4000744daf921481c182c4576af38ac7907b215681b90cf19761927b13853857c701cf1bcda24961f2052e0bd4acb795d80603f848b66e6c9788191452d612a94ded93a8ca005fe10930c3b7251", + "expected": "81820b2d4ecb7a65", + "note": "random" + }, + { + "n": "0x75f610271aaa7ac33ece74ca568efa962eb1aa1a8276e1549a5cdac89c55734dcba97112f166cd", + "d": "0x4d14797b787a430284e1b", + "expected": "4e387c694b26fe4a", + "note": "random" + }, + { + "n": "0x57223bf42a1e6e19ca7212605803f08ce6ac54b2a93bad36d0fb6194912929c3207", + "d": "0x4f048a496f42127eeca798f784e48929c4133facd6dbdd294596c01a9a4687ff4e6057f1c8622818387abb2b0e77dd23ac8b35d6e10c3dce5806ca3eead7c9b13c0a6d3063026ec831139694547ec7f9b1481338d02030f18c7c87c11c5f26953a3100f9fa77a65afc9d24048ba1716f18de3", + "expected": "1771a4b44fb405a2", + "note": "random" + }, + { + "n": "0xcd808ad850d1036d6a090ab9b2be888e4090cff293", + "d": "0x7dcc37c524f52b509b408e5abf72cf088bcd2a43014564bac7c84a3e3c044c39484af650f3aaf903b8a0d706e4e5d9958e7da08762211c3ec48ebf4aab57e2adb57bc943450a0b9c5ca9a08c2506d7dce4b87e654b51eda9b4ebf7cc39b711e730db0460495b", + "expected": "177a233107b3413d", + "note": "random" + }, + { + "n": "0xe8a324ccbd88b1d0949e8e00d40df4ccedcdd7e0335b092e15b279c1be780c319be4f1e12563c6e68b996bbbaa7c4b54e22f7f2318e5506a0de32dcb2539ad100564f794a496ab35e9f733048a84b203fc60d1e18351fb64eec49caa7fdb8f278894a59fe4017bc5783cd6e78cb637bb4fe46cc8223b589b9784d78164bdc567d09df03b2779", + "d": "0x16ecacd7638534f6d8110f86b27", + "expected": "7c644bc2d1b72158", + "note": "random" + }, + { + "n": "0x18fcc3d387e6ce6cee294a9b163ecb50100119ed5efdb1d4de927d984887296f905eafcf001851cb7ebe71696c34a7a0eee00c8aca375605f26a5f3cc7e098bae994a05bd11488a714ce3350ee5b5f2ba5e18516d2416c058dbfae046ca8860c4743594ec43a339753fbe444ede2ee20b308834da94497880474b7de7cf6cdb6e1917de954d7baef2071dbca7f", + "d": "0x541dec6c6386b22d963ac931e6bab2c61e76c68b9eb927993df2683068b931e16414f6c558803d6e2528795058e0a9f8d7284082c50826675250fd03039c7dd4482977dd9a74b8a36a26944d6eafd150e8567b38e63ce0ceb20f74933207a155a7b9e785fe211bc4d", + "expected": "521302f4016e6d11", + "note": "random" + }, + { + "n": "-0x3b5a71efd25ec34ef7eb4bc9dc80f705f7f98c9e15ed05f036d52f561177c3a565e0798a2f56d3c4281b7f2079ac9e6e0880dd780b9f52449b48b6e5639cf5fc9474e08b76f2f3092ab057e9848b8004c4647109190a69596ba45c1545bf45ab93b30f10a1cdedaae640678b64ee1de9cfa2ecf4489852ce49e3", + "d": "0x798fa64a59ca99a138952f3798d60726af26fcf19f341b1d910db9cb5fa3cf6c5a26dd9f9d2450cf41c8b30ab852d7672fcf4229a1bf96036ec63aa536896a27e5c687b4d2811fa2960344109de62f6474ec7a0e51e77aa9db6427fe89dc6751ac5762675ddf93362757360579c02fdc7e806fee3da78b47fe49a9e294793eeb", + "expected": "bcdf3fa2262bf299", + "note": "random" + }, + { + "n": "0x3686e27f3411b7a11453d25dc3c9ed7382dc1e3362b98aa4734f27db838a3fe490b7ec8ea8fbaeb91b16575403d67adef4aefe226fee981600bd3370b1335f1994eee009f9772259f5bc8f5c456225986a18727e91ead25d12888d108c5cb6eef00749737e289ac0b8e2396041a014092187874d843ce197590e240599839806d2f58296cbc5efa2186dde3f", + "d": "0x1860e54904dc811893c2dbd6c7eb95b7d4ba626a7645efdd26b7effa55180e56601d56d5b6544884a291b4d6f1d7", + "expected": "6f01e4b856e634b3", + "note": "random" + }, + { + "n": "0x159ac29fc71bf314a5d124ded46b1d275d20de96ed8be2dc91a569febc9b8dc1558be9", + "d": "0x17d6d378bd3408b3fd650262f11eb14a471b7ad0f0201dc6a52bb4948f79cc1939d7b489731659d2c601aa6d1a6406976464d166c692800d8246974de189c63c741a7479b5a4c4da883d08fecb10a777e843c761b0bcd037432ff46fe15a6c22f901d25bdf2d0566d1c9d24192977798d973851df629093d0713816f9a60ddf5cce4f1dc01cabacfad163a03e4837e7bacd47", + "expected": "082d00197983b5bf", + "note": "random" + }, + { + "n": "-0x31f0eef7a462213df0c17f6c4021def7069cd27407d774e4a558f214b2676a62a7fec3be910b6b5e02b81068a73e1d865127", + "d": "0xc52e382d562699efbaf59938d42f849a90f7f3cf40200f49e190905", + "expected": "cb1035ad8898f340", + "note": "random" + }, + { + "n": "0x1b66c94dcd6c65ad5b11b3df7b8f0c88a008ef8bbbd401dd", + "d": "0x3f243051def4a9166c48a8c146bbabaef3edc9cd24c1d250d9e2ab25dad64a3dddb9f4cfdef1a7badf9142d10819ba1885e75db71ea37dbc347b8a23e26d6447", + "expected": "2bdbc62d924a8ba6", + "note": "random" + }, + { + "n": "0xced3848cf7c8cf52f4053e0cc01993560793fec142ed360ebf54b7822d6bc09c2a48e8244acae8d9c0dfea2531b0038f0745107a159ece4da4d5dc123a06461e93c3f884aa388dbce579576b7e69ea4342f97d2301ad9de1444c12f0512eb7278063fb118553828d89e0bf3909cfcdc2086d70fe5ca5d854ace9207e04b8aff64ff7ed83a79cd280095", + "d": "0xfd12e795e5a24b77d32bab083cea5bed124579ee12d202f1d3382d0cd78a5feb7e21971e4c889abd6043ef8d4ecd8d92f96ce4686133575eb845d5", + "expected": "672a26f710f9a0e2", + "note": "random" + }, + { + "n": "0x2f09b6607a2974789ecea0a0788786029cce7cf0bce0ffffa7e7e0873f15c3f73aa07721744ed", + "d": "0xc8b6af9264cd8545edcf8cb27bf4743dc8aec15bbf293310b0fe72ac0ca1429fcc5bd93fe0b54c6eda998af2749603dade259833", + "expected": "390dff4bab39df74", + "note": "random" + }, + { + "n": "0x59007c33dc3319a3c178ade0ae0ca8918fe960187b3546030c22c0d5b44515fd2cf34cb93002f1be05a9baa27f6346aed01ac837610093b7ad3ac5bcc627b835c3eaaba4e07db77d51119bd47fc834b4ca77a1850f945a261b94d0fdd8add68e45de690e626815e28a7f", + "d": "0x1b5c34d55828b97cf11748f6f981e4b098d38bbe02837afa04c05509a42dd1acb36eb30fd237a561b4c998767d", + "expected": "5e8a06160765aaa4", + "note": "random" + }, + { + "n": "-0x6521d8b869768a70266afaa5ff7db1187ba22badeab9830503c2b087989", + "d": "0x5d3579cc5b91ea57ebd769ce705434325b713", + "expected": "c5715c2f58e4db2a", + "note": "random" + }, + { + "n": "0x178a9cb720211acabd0eaaeb2e84e49df4d1285fb6b0b1d8721fcba6f1d7d7c83d43ef759bb5770216a7ccb0882a47d712aaad0c84b40872f60766be10a0cc8394c0fb5e27669f53cb86fc0ae61faf85475038d", + "d": "0x54d96fe35bfc5f2db58adf25a4ee8f2bac6dd28530881a536e9b98d40f", + "expected": "5b11c1bd8a2f5dc4", + "note": "random" + }, + { + "n": "-0x1f2049e2d9f98d0ddaba306856ba2d628a25eddeca0a44256d5a2d85ab8e12de3b4f6bd871add4ebf30dc9511706e24548663461f48ac8266992cf4f4da75f02a8190ea7fc2fa928a4a59b9c5b2a6340ff11c23aba637b4fed795cfda24688a415f06269240c990cc1ed988b2f284398ebc31475ca467fa74d43f8c60eb05601831569f4a801", + "d": "0x7f699f50e01feac990f456b01b937cce8a71811d68a0ade9c6a1b4c3146547f1aef90472e9e652f", + "expected": "ef0f450664e529fa", + "note": "random" + }, + { + "n": "0x18b42986e7bf4f123edca00844801b3030ee405789fc4bfeb26320e0e59de7415a4dd8b194ab6fe932d63d39ea70b872a817a268701b6199df750b105e9794a4311b1b0e17ea389ddbee3dfde031f283704fc980c8505d5b0ddc6f671ce191251dcfabf153f69", + "d": "0x2e9df873be85fa8a7bc7c6f8edefad622ca9811a8745a18e7eefb6bf9c0acc8e9a45d2e4edd51ce7", + "expected": "5f20f52e9158cd44", + "note": "random" + }, + { + "n": "0x64e5a89605396282bb5c310b5d2318eaa1c8e5d902654b369c65bc56c73cdafb42a7e260f14d8820ad2e66acb69f4697c0fa90d6399581312c31554afe9a0aeb828896d365cbe12d571cd5dd5a2b1aa92cde97886af8f8ae38f0e8c6d65945982453144368b8137a20183263c3720c1c76f", + "d": "0x8c34e8bf7c95a3d0457616a7b4eb652bfcb197b861150c92633c56f4ad69fa7023282f58d661d43260faaea35cc84b7289f3f62e06a3bc098b867aefd5b8bac3a78f35fa309db8e8314896684abab7d0cf1719053482908d655d0d89f67b0efa52ee9a4b48961318571d5b8b3", + "expected": "42670738153311e5", + "note": "random" + }, + { + "n": "0x63b65fec2585f66cb9e032ee01fe5fddac9b7c70e0e173f4ed9356f39659da0f971db", + "d": "0x73a6ac69f2814e6370db3efb88463872b42ad3de40a9a4432c31cc2182ecd3f93ac90016002811aba5c8d45fbc1b0f45cccb1969d9ff69f06fa5393e04c49ace528e42a02fc75430d", + "expected": "2ceb970118570abb", + "note": "random" + }, + { + "n": "0x278fec48da94648c17bdb9fe2175674295c9d692b55682491435553e3915a7d16453b48ca094d158ed95cc8df4b1131b705db8eaf2b708e523e88f7aa638c58bd2ea5bb89594b5ab79d27c2de40154ebe996f28ae7a91c995317ff805893c69d07357620ac4b70017ab3adbe49365644ba1540a400c06f90b51207f1baee0db", + "d": "0x1d8f2c5547c55a7", + "expected": "7bf56a18ab4b1860", + "note": "random" + } + ] +} diff --git a/test/contract/fixtures/tofloat_cases.py b/test/contract/fixtures/tofloat_cases.py new file mode 100644 index 0000000..d2f0cf5 --- /dev/null +++ b/test/contract/fixtures/tofloat_cases.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +# Generates tofloat_cases.json: Fractions n/d (hex integers) and the double nearest +# to n/d with ties to even (IEEE 754 round-to-nearest), as reference for +# ExpressionEngine.toFloat. The reference is Python's float(Fraction(n, d)), an +# integer true division that CPython rounds correctly; OverflowError means the +# rounded value is beyond the double range (+-Infinity). +# python3 -I test/contract/fixtures/tofloat_cases.py > test/contract/fixtures/tofloat_cases.json +import json +import random +import struct +from fractions import Fraction + + +def expected(f): + try: + x = float(f) + except OverflowError: + return 'Infinity' if f > 0 else '-Infinity' + return struct.pack('>d', x).hex() + + +def case(n, d, note): + f = Fraction(n, d) + return {'n': hex(n), 'd': hex(d), 'expected': expected(f), 'note': note} + + +cases = [] +M = 2 ** 53 +# ties and both sides of the midpoint between neighbouring doubles, with +# numerators and denominators beyond 53 bits (bigint path) +for k in (200, 1000, 1100): + D = 2 ** k + half = 2 ** (k - 53) # half an ulp of 1 + cases.append(case(D + half, D, '1 + half ulp: tie, to even (1)')) + cases.append(case(D + half + 1, D, '1 + half ulp + tiny: up')) + cases.append(case(D + half - 1, D, '1 + half ulp - tiny: down')) + cases.append(case(D + 3 * half, D, '1 + 3 half ulps: tie, to even (1 + 2 ulps)')) + cases.append(case(D + 3 * half - 1, D, '1 + 3 half ulps - tiny: down (1 + 1 ulp)')) +for k in (60, 300): + t = 3 ** k # non-dyadic denominators + cases.append(case((M + 1) * t, M * t, 'tie (2^53+1)/2^53 scaled by 3^k, to even')) + cases.append(case((M + 1) * t + 1, M * t, 'above the tie, scaled')) + cases.append(case((M + 1) * t - 1, M * t, 'below the tie, scaled')) +cases.append(case(2 ** 200 + 2 ** 147 + 1, 2 ** 200, '(2^200 + 2^147 + 1) / 2^200 (review example)')) +cases.append(case(10 ** 310 + 1, 1009, '(10^310 + 1) / 1009')) +cases.append(case(2 ** 1200 + 1, 2 ** 2200 + 1, '(2^1200 + 1) / (2^2200 + 1)')) +# subnormal range: quantum 2^-1074 +for num, den, note in [ + (1, 2 ** 1075, 'half of the smallest subnormal: tie, to even (0)'), + (1, 2 ** 1075 - 1, 'just above half of the smallest subnormal: up'), + (3, 2 ** 1075, '1.5 x smallest subnormal: tie, to even (2 x)'), + (5, 2 ** 1076, '1.25 x smallest subnormal: down'), + (2 ** 52 - 1, 2 ** 1074, 'largest subnormal'), + ((2 ** 53 - 1) * 3, 2 ** 1075 * 3, 'between the largest subnormal and 2^-1022: tie, to even (2^-1022)'), + (1, 3 * 2 ** 1022, '1/3 of 2^-1022 (subnormal, non-dyadic)'), + (1, 10 ** 330, '1e-330 (subnormal)'), + (1, 10 ** 400, '1e-400 (below the range: 0)'), +]: + cases.append(case(num, den, note)) +# overflow boundary: MAX = (2^53 - 1) 2^971, the midpoint to 2^1024 is (2^54 - 1) 2^970 +mid = (2 ** 54 - 1) * 2 ** 970 +cases.append(case(mid, 1, 'midpoint MAX / 2^1024: tie, to even (Infinity)')) +cases.append(case(3 * mid - 1, 3, 'just below the midpoint: MAX')) +cases.append(case(3 * mid + 1, 3, 'just above the midpoint: Infinity')) +cases.append(case(-(3 * mid - 1), 3, 'negative, just below the midpoint: -MAX')) +cases.append(case(10 ** 400 + 1, 3, 'far beyond the range: Infinity')) +# random large pairs (seeded), quotients across the whole range +rnd = random.Random(20261101) +for i in range(150): + n = rnd.getrandbits(rnd.randint(1, 1200)) | 1 + d = rnd.getrandbits(rnd.randint(1, 1200)) | 1 + if rnd.random() < 0.3: + n = -n + cases.append(case(n, d, 'random')) + +print(json.dumps({'generator': 'test/contract/fixtures/tofloat_cases.py', 'cases': cases}, indent=1)) diff --git a/test/contract/load.js b/test/contract/load.js index eb0e7e3..3563d00 100644 --- a/test/contract/load.js +++ b/test/contract/load.js @@ -15,6 +15,11 @@ // a CommonJS file exporting the module namespace // SD_EXPRESSION_ENGINE_DIST points at another dist/ directory (default: ./dist), // e.g. the dist/ of the published package extracted from `npm pack`. +// SD_EXPRESSION_ENGINE_BASELINE= tests built artifacts of a release +// against the contract of that release (its Math.js version and the +// version-dependent expectations), e.g. 0.1.17 for the committed dist/ and the +// published package; without it the expectations of the current sources and +// package.json (their Math.js version) apply. // SD_UTILS_BUNDLE / SD_RANDOM_BUNDLE are the `require` bundles loaded before the // module bundle (default: dist/sd-utils.js / dist/sd-random.js of the installed // packages). @@ -35,6 +40,17 @@ const childProcess = require('child_process'); const root = path.resolve(__dirname, '..', '..'); const target = process.env.SD_EXPRESSION_ENGINE_TARGET || 'standalone'; const dist = path.resolve(root, process.env.SD_EXPRESSION_ENGINE_DIST || 'dist'); +// Math.js version of each release whose artifacts can be tested +const BASELINES = {'0.1.17': {mathjs: '9.5.0'}}; +const baseline = process.env.SD_EXPRESSION_ENGINE_BASELINE || null; +if (baseline && !BASELINES[baseline]) { + throw new Error('unknown SD_EXPRESSION_ENGINE_BASELINE ' + baseline + ', known: ' + Object.keys(BASELINES).join(', ')); +} +if (baseline && target === 'source') { + throw new Error('SD_EXPRESSION_ENGINE_BASELINE applies to built artifacts, not to the sources'); +} +const expectedMathjs = baseline ? BASELINES[baseline].mathjs + : JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).dependencies.mathjs; const rng = (function () { const original = Math.random; @@ -169,6 +185,11 @@ function loadSdUtils() { const loaded = load(); loaded.target = target; +loaded.baseline = baseline; +// the Math.js version the tested artifact must contain, and its major version +// for the version-dependent expectations +loaded.expectedMathjs = expectedMathjs; +loaded.mathjsMajor = parseInt(expectedMathjs, 10); loaded.ExpressionEngine = loaded.engine.ExpressionEngine; loaded.isModuleBundle = /^module(\.min)?$/.test(target); loaded.isStandalone = /^standalone(\.min)?$/.test(target); diff --git a/test/contract/mathjs-version.test.js b/test/contract/mathjs-version.test.js new file mode 100644 index 0000000..8d5a0a4 --- /dev/null +++ b/test/contract/mathjs-version.test.js @@ -0,0 +1,256 @@ +// Contract points that depend on the Math.js version (0.1.17 / Math.js 9.5.0 and +// the current Math.js of package.json): exact Fraction arithmetic, toFloat, +// Units and the JSON form of 0.1.17, and the behaviour changes of Math.js 10-15 +// for user expressions. Version differences are asserted explicitly for both +// versions; mathjsMajor is the major version the tested artifact must contain +// (load.js). +'use strict'; + +const test = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('fs'); +const path = require('path'); +const {ExpressionEngine, mathjsMajor} = require('./load'); +const {assertFraction} = require('./helpers'); + +const N = ExpressionEngine.toNumber; +const legacy = mathjsMajor < 14; // Math.js 9.5.0 of 0.1.17 +const B = v => BigInt(v); +const babs = v => (v < B(0) ? -v : v); + +// exact rational arithmetic with bigint, independent of Math.js / fraction.js +function rational(f) { + const m = /^(-?)(\d+)(?:\/(\d+))?$/.exec(f.toFraction(false)); + return {n: B(m[1] + m[2]), d: B(m[3] || 1)}; +} +function sameRational(a, b) { + return a.n * b.d === b.n * a.d; +} +// approximate double of n/d from 25 significant decimal digits (relative error +// below 1e-24); NOT correctly rounded, used only with tolerances +function approxFloat(r) { + if (r.n === B(0)) { + return 0; + } + const sign = (r.n < B(0)) !== (r.d < B(0)) ? '-' : ''; + const n = babs(r.n), d = babs(r.d); + const k = 25 - (n.toString().length - d.toString().length); + const q = k >= 0 ? (n * B(10) ** B(k)) / d : n / (d * B(10) ** B(-k)); + return Number(sign + q.toString() + 'e' + (-k)); +} +// deterministic generator of positive odd bigints with up to `bits` bits +function generator(seed) { + let s = seed >>> 0; + const next = () => { + s = (s + 0x6D2B79F5) >>> 0; + let t = Math.imul(s ^ (s >>> 15), s | 1); + t ^= t + Math.imul(t ^ (t >>> 7), t | 61); + return ((t ^ (t >>> 14)) >>> 0); + }; + return { + int: (max) => next() % max, + big: (bits) => { + let v = B(0); + for (let i = 0; i < bits; i += 32) { + v = (v << B(32)) | B(next()); + } + return (v >> B(Math.max(0, Math.ceil(bits / 32) * 32 - bits))) | B(1); + } + }; +} +const reviver = new ExpressionEngine().getJsonReviver(); +const fraction = (n, d) => JSON.parse(JSON.stringify({mathjs: 'Fraction', n: String(n), d: String(d)}), reviver); + +test('arithmetic helpers: x + 0 equals x (exact arithmetic, regression of a rounding policy)', () => { + const x = N('1208925819614629174706176/3'); // 2^80 / 3 + const y = ExpressionEngine.add(x, 0); + assert.equal(Number(ExpressionEngine.compare(x, y)), 0); + assertFraction(ExpressionEngine.subtract(y, x), '0'); +}); + +test('arithmetic helpers keep small values', () => { + assert.equal(ExpressionEngine.toFloat(ExpressionEngine.divide(1, 1e20)), 1e-20); + assert.equal(ExpressionEngine.toFloat(ExpressionEngine.add('1/100000000000000000000', 0)), 1e-20); + assertFraction(ExpressionEngine.multiply(ExpressionEngine.divide(1, 1e20), 1e20), '1'); + assertFraction(ExpressionEngine.divide(1, 1e20), '1/100000000000000000000'); + // [version difference] fraction.js 4 stores 10^20 + 1 as the double 10^20, so 0.1.17 gives 0 + assertFraction(ExpressionEngine.subtract('1/100000000000000000000', '1/100000000000000000001'), + legacy ? '0' : '1/10000000000000000000100000000000000000000'); +}); + +test('arithmetic helpers are exact: random operands of 1e-30 to 1e30 against bigint rational arithmetic', {skip: legacy && 'Math.js 9.5.0: fraction.js 4 keeps numerator and denominator as doubles (not exact beyond 2^53)'}, () => { + const g = generator(7); + const ops = { + add: (a, b) => ({n: a.n * b.d + b.n * a.d, d: a.d * b.d}), + subtract: (a, b) => ({n: a.n * b.d - b.n * a.d, d: a.d * b.d}), + multiply: (a, b) => ({n: a.n * b.n, d: a.d * b.d}), + divide: (a, b) => ({n: a.n * b.d, d: a.d * b.n}) + }; + const operand = () => { + const k = g.int(61) - 30; + let n = g.big(1 + g.int(80)), d = g.big(1 + g.int(80)); + if (k >= 0) { + n *= B(10) ** B(k); + } else { + d *= B(10) ** B(-k); + } + return {n: g.int(2) ? -n : n, d}; + }; + for (let i = 0; i < 1000; i++) { + const a = operand(), b = operand(); + for (const [name, op] of Object.entries(ops)) { + const r = rational(ExpressionEngine[name](fraction(a.n, a.d), fraction(b.n, b.d))); + assert.ok(sameRational(r, op(a, b)), name + ' is exact'); + } + } +}); + +test('arithmetic helpers: the mean of many evaluated payoffs (probabilistic sensitivity analysis)', () => { + // sd-computations 0.6.1 prob-calculate-step: expectedValue = add(expectedValue, divide(payoff, numberOfRuns)) + let seed = 42; + const draw = () => { + seed = (seed * 1103515245 + 12345) % 2147483648; + return seed / 2147483648; + }; + const runs = 100; + let mean = N(0); + let exact = {n: B(0), d: B(1)}; + for (let i = 0; i < runs; i++) { + const payoff = ExpressionEngine.eval('2500 * pr - 250 * (1 - pr)', true, {pr: 0.1 + 0.8 * draw()}); + mean = ExpressionEngine.add(mean, ExpressionEngine.divide(payoff, runs)); + const p = rational(payoff); + exact = {n: exact.n * p.d * B(runs) + p.n * exact.d, d: exact.d * p.d * B(runs)}; + } + if (legacy) { + // fraction.js 4: doubles, every operation rounds (up to 2^-52 relative) + assert.ok(String(mean.d).length <= 16, 'denominator ' + String(mean.d)); + assert.ok(Math.abs(ExpressionEngine.toFloat(mean) - approxFloat(exact)) / approxFloat(exact) < 1e-12); + } else { + // fraction.js 5: exact; the size grows with the number of runs (hundreds of + // digits here, thousands for 1000 runs): the aggregation in sd-computations + // has to bound it + assert.ok(sameRational(rational(mean), exact)); + assert.ok(String(mean.d).length > 100, 'denominator digits ' + String(mean.d).length); + assert.ok(Math.abs(ExpressionEngine.toFloat(mean) - approxFloat(exact)) / approxFloat(exact) < 1e-15); + } +}); + +// toFloat contract: the double nearest to the Fraction, ties to even (IEEE 754 +// round-to-nearest), also for subnormal results and with overflow to +// +-Infinity. Reference: fixtures/tofloat_cases.json, generated with Python's +// fractions.Fraction (fixtures/tofloat_cases.py). +test('toFloat is correctly rounded: ties, both sides of midpoints, subnormals, overflow, random large Fractions', {skip: legacy && 'Math.js 9.5.0: fraction.js 4 holds no bigint Fractions'}, () => { + const {cases} = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures', 'tofloat_cases.json'), 'utf8')); + const big = h => (h.startsWith('-') ? -BigInt(h.slice(1)) : BigInt(h)); + const double = hex => { + if (hex === 'Infinity' || hex === '-Infinity') { + return Number(hex); + } + const view = new DataView(new ArrayBuffer(8)); + view.setBigUint64(0, BigInt('0x' + hex)); + return view.getFloat64(0); + }; + assert.ok(cases.length > 100); + for (const c of cases) { + const value = ExpressionEngine.toFloat(fraction(big(c.n), big(c.d))); + assert.ok(Object.is(value, double(c.expected)), c.note + ': ' + value + ' expected ' + double(c.expected)); + } +}); + +test('toFloat of small Fractions is the division of their numbers', () => { + assert.equal(ExpressionEngine.toFloat(N('1/3')), 1 / 3); + assert.equal(ExpressionEngine.toFloat(N('-7/2')), -3.5); + assert.equal(ExpressionEngine.toFloat(N('878291371642703/3279115770832143')), 878291371642703 / 3279115770832143); +}); + +test('the JSON form written by 0.1.17 (numbers) is revived exactly', () => { + const back = JSON.parse('{"a": {"mathjs": "Fraction", "n": -1, "d": 3}, "b": {"mathjs": "Fraction", "n": 878291371642703, "d": 3279115770832143}}', reviver); + assertFraction(back.a, '-1/3'); + assertFraction(back.b, '878291371642703/3279115770832143'); +}); + +test('eval with asNumber: a Unit result is rejected', () => { + for (const expr of ['2 cm + 3 mm', '5 inch to cm', 'unit(5, "m")']) { + assert.throws(() => ExpressionEngine.eval(expr), TypeError, expr); + assert.equal(ExpressionEngine.eval(expr, false).isUnit, true, expr); + } +}); + +// Behaviour changes of Math.js 10-15 for user expressions (Math.js HISTORY.md, +// each reproduced): [expression, scope, 0.1.17 (Math.js 9.5.0), current], values +// of eval(expr, false), 'error' when it throws. Changing the meaning of accepted +// expressions is a decision of the product owner (docs/MATHJS_UPGRADE.md); these +// tests make every known difference explicit. +const show = v => (v && v.isMatrix ? 'matrix ' + JSON.stringify(v.toArray()) : (Array.isArray(v) ? 'array ' + JSON.stringify(v) : String(v))); +const DIFFERENCES = [ + // 15.0: unary percent decoupled from modulus; `%` followed by a term is modulus + ['100 + 3% + 1', {}, '104', '100'], + ['100 * 3% + 1', {}, '4', '0'], + ['200 * 50% - 1', {}, '99', '0'], + ['x% + 1', {x: 2}, '1.02', '0'], + // 14.0: mod has the precedence of * and / (was higher) + ['10 / 4 mod 3', {}, '10', '2.5'], + ['10 * 4 mod 3', {}, '10', '1'], + ['10 / 4 % 3', {}, '10', '2.5'], + // 11.0: implicit multiplication after a division of constants with a sign + ['-1 / 2 x', {x: 2}, '-0.25', '-1'], + ['+1 / 2 x', {x: 2}, '0.25', '1'], + ['-1 / 2 pi', {}, String(-1 / (2 * Math.PI)), String(-Math.PI / 2)], + // 13.0: comparisons with absTol 1e-15 and relTol 1e-12 (was epsilon 1e-12 relative, 2.2e-16 absolute) + ['x > 0', {x: 1e-15}, 'true', 'false'], + ['x == 0', {x: 5e-16}, 'false', 'true'], + ['compare(x, 0)', {x: 1e-15}, '1', '0'], + ['abs(x - 0.3) < 1e-15', {x: 0.1 + 0.2}, 'true', 'false'], + ['isZero(1e-15)', {}, 'false', 'true'], + ['isPositive(1e-15)', {}, 'true', 'false'], + // 15.1: isInteger with tolerance + ['isInteger(2.00000000000001) ? 100 : 0', {}, '0', '100'], + ['isInteger(0.1 * 3 * 10)', {}, 'false', 'true'], + // 11.11.1: mod round-off + ['0.3 mod 0.1', {}, '0.09999999999999998', '-5.551115123125783e-17'], + // 12.1: mod with a negative divisor + ['10 mod -3', {}, 'error', '-2'], + // 10.2: exact combinations + ['combinations(60, 30)', {}, '118264581564861420', '118264581564861460'], + // 11.11.1: print in the parser uses one-based indices + ['print("$1 $2", [10, 20])', {}, '20 $2', '10 20'], + // 11.0: typeOf of a function in lower case + ['typeOf(f)', {f: function () {}}, 'Function', 'function'], + // 11.0: element-wise functions of matrices removed + ['sqrt([4, 9])', {}, 'matrix [2,3]', 'error'], + // 15.1.1: empty true-expression of a conditional + ['x ? : 1', {x: 2}, '1', 'error'], + // 15.0: size returns an array, subsets keep the shape of the index + ['size([1, 2, 3])', {}, 'matrix [3]', 'array [3]'], + ['[[1, 2], [3, 4]][1, :]', {}, 'matrix [[1,2]]', 'matrix [1,2]'], + // 13.0.3: US liquid volume units + ['number(1 gallon, "liter")', {}, '3.785412', '3.785411784'], + // new syntax (14.8) + ['a ?? 1', {a: null}, 'error', '1'] +]; + +test('[version difference] Math.js 10-15 behaviour changes of user expressions', () => { + for (const [expr, scope, before, after] of DIFFERENCES) { + let actual; + try { + actual = show(ExpressionEngine.eval(expr, false, Object.assign({}, scope))); + } catch (e) { + actual = 'error'; + } + assert.equal(actual, legacy ? before : after, expr); + } +}); + +test('[version difference] integers beyond 2^53 in numeric strings', () => { + // toNumber reads the string itself: fraction.js 5 (bigint) keeps every digit + assertFraction(ExpressionEngine.eval('9007199254740993'), legacy ? '9007199254740992' : '9007199254740993'); + // expressions are still evaluated with JS numbers + assert.equal(ExpressionEngine.eval('2^53 + 1', false), 9007199254740992); +}); + +test('unchanged: percent of a single operand, mod alone, implicit multiplication without a sign, rounding', () => { + for (const [expr, value] of [['100 + 3%', 103], ['3% * 100', 3], ['50% * 2', 1], ['7 % 3', 1], ['1 / 2 x', 1], ['2 x', 4], + ['0.1 + 0.2 == 0.3', true], ['round(1.005, 2)', 1.01], ['round(-2.5)', -3], ['floor(2.9999999999999996)', 3]]) { + assert.equal(ExpressionEngine.eval(expr, false, {x: 2}), value, expr); + } +}); diff --git a/test/contract/numbers.test.js b/test/contract/numbers.test.js index 2be4d9a..7ea2e7e 100644 --- a/test/contract/numbers.test.js +++ b/test/contract/numbers.test.js @@ -6,7 +6,7 @@ const test = require('node:test'); const assert = require('node:assert/strict'); -const {ExpressionEngine} = require('./load'); +const {ExpressionEngine, mathjsMajor} = require('./load'); const {assertFraction, knownBug} = require('./helpers'); const N = ExpressionEngine.toNumber; @@ -75,8 +75,11 @@ test('add, subtract, multiply, divide: exact on Fractions, arguments converted w test('arithmetic with Infinity [current behaviour]: only ±Infinity together are numbers, Infinity with a Fraction throws', () => { assert.ok(Number.isNaN(ExpressionEngine.add(Infinity, -Infinity))); assert.equal(ExpressionEngine.add(Infinity, Infinity), Infinity); - assert.throws(() => ExpressionEngine.add(Infinity, 1), /Cannot implicitly convert a number to a Fraction/); - assert.throws(() => ExpressionEngine.divide(1, Infinity), /Cannot implicitly convert a number to a Fraction/); + // the message is Math.js': 9.x refuses the implicit conversion, 14+ (fraction.js 5, + // bigint) cannot convert Infinity to a bigint + const message = mathjsMajor < 14 ? /Cannot implicitly convert a number to a Fraction/ : /Infinity cannot be converted to a BigInt/; + assert.throws(() => ExpressionEngine.add(Infinity, 1), message); + assert.throws(() => ExpressionEngine.divide(1, Infinity), message); }); test('compare: -1/0/1 for Fractions, ±Infinity handled before Math.js', () => { diff --git a/test/contract/run.js b/test/contract/run.js index 657bf5e..234332d 100644 --- a/test/contract/run.js +++ b/test/contract/run.js @@ -5,7 +5,10 @@ // node test/contract/run.js --targets=module,source // node test/contract/run.js --dist= the bundles of another dist/ directory // node test/contract/run.js --published[=0.1.17] the bundles of the package on the npm registry -// (npm pack into a temporary directory) +// (npm pack into a temporary directory), tested +// against the contract of that release +// node test/contract/run.js --baseline[=0.1.17] the committed dist/ (the build of that release) +// against the contract of that release // // Each target runs in its own `node --test` process (SD_EXPRESSION_ENGINE_TARGET, // SD_EXPRESSION_ENGINE_DIST, see load.js). Exits with 1 if any target fails. @@ -27,6 +30,10 @@ const BUNDLES = ['standalone', 'standalone.min', 'module', 'module.min']; let dist = option('dist'); let tmp = null; const published = option('published'); +const baselineOption = option('baseline'); +// the committed dist/ is the 0.1.17 release build until the next release +const BASELINE = '0.1.17'; +let baseline = baselineOption ? (baselineOption === true ? BASELINE : baselineOption) : null; function npm(npmArgs, cwd) { // npm is npm.cmd on Windows; run it through the shell there @@ -38,7 +45,8 @@ function npm(npmArgs, cwd) { } if (published) { - const version = published === true ? require(path.join(root, 'package.json')).version : published; + const version = published === true ? BASELINE : published; + baseline = version; tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'sd-ee-published-')); const tgz = npm(['pack', 'sd-expression-engine@' + version, '--silent'], tmp).trim().split(/\r?\n/).pop(); childProcess.execFileSync('tar', ['-xzf', tgz], {cwd: tmp}); @@ -47,14 +55,17 @@ if (published) { } const targetsOption = option('targets'); -const targets = targetsOption ? String(targetsOption).split(',') : (dist ? BUNDLES : BUNDLES.concat('source')); +const targets = targetsOption ? String(targetsOption).split(',') : (dist || baseline ? BUNDLES : BUNDLES.concat('source')); const files = fs.readdirSync(__dirname).filter(f => f.endsWith('.test.js')).sort().map(f => path.join(__dirname, f)); const failed = []; try { for (const target of targets) { - console.log('\n=== contract tests: ' + target + (dist ? ' (' + dist + ')' : '')); + console.log('\n=== contract tests: ' + target + (dist ? ' (' + dist + ')' : '') + (baseline ? ', contract of ' + baseline : '')); const env = Object.assign({}, process.env, {SD_EXPRESSION_ENGINE_TARGET: target}); + if (baseline) { + env.SD_EXPRESSION_ENGINE_BASELINE = baseline; + } if (dist) { env.SD_EXPRESSION_ENGINE_DIST = path.resolve(dist); } diff --git a/test/contract/serialization.test.js b/test/contract/serialization.test.js index 9cec8c5..a8e07f9 100644 --- a/test/contract/serialization.test.js +++ b/test/contract/serialization.test.js @@ -13,7 +13,7 @@ const test = require('node:test'); const assert = require('node:assert/strict'); -const {ExpressionEngine, sdUtils} = require('./load'); +const {ExpressionEngine, sdUtils, mathjsMajor} = require('./load'); const {assertFraction, readFixture, FIXTURES, evaluateDiagram} = require('./helpers'); const {Utils} = sdUtils; @@ -80,7 +80,8 @@ test('isExpressionObject: objects with a mathjs field (the JSON form of Math.js } // a live Fraction has no own mathjs field: the replacer sees its toJSON() form assert.ok(!ExpressionEngine.isExpressionObject(N('1/3'))); - assert.deepEqual(N('-1/3').toJSON(), {mathjs: 'Fraction', n: -1, d: 3}); + // [version difference] Math.js 14+ (fraction.js 5, bigint) writes n and d as strings + assert.deepEqual(N('-1/3').toJSON(), mathjsMajor < 14 ? {mathjs: 'Fraction', n: -1, d: 3} : {mathjs: 'Fraction', n: '-1', d: '3'}); }); test('getJsonReplacer in JSON.stringify: Fractions as fraction strings, other values unchanged', () => {