gracio 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/examples/benchmark.js +94 -0
- package/dist/examples/collision-example.js +28 -0
- package/dist/examples/memory-audit.js +71 -0
- package/dist/examples/precision-demo.js +17 -0
- package/dist/examples/validate-precision.js +79 -0
- package/dist/gracio.bundle.js +1 -0
- package/dist/precise-calculator.bundle.js +1 -0
- package/dist/src/audit-util.js +39 -0
- package/dist/src/calc-number.js +415 -0
- package/dist/src/constants.js +32 -0
- package/dist/src/demo.js +55 -0
- package/dist/src/gracio.js +415 -0
- package/dist/src/index.js +1 -0
- package/dist/src/lexer.js +51 -0
- package/dist/src/library-entry.js +4 -0
- package/dist/src/parser.js +187 -0
- package/dist/src/types.js +1 -0
- package/dist/test/advanced-arithmetic.test.js +63 -0
- package/dist/test/basic-arithmetic.test.js +40 -0
- package/dist/test/precision-comparison.test.js +40 -0
- package/dist/test/src/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Gracio } from '../src/gracio.js';
|
|
2
|
+
import Big from 'big.js';
|
|
3
|
+
import Decimal from 'decimal.js';
|
|
4
|
+
import Fraction from 'fraction.js';
|
|
5
|
+
function benchmark(name, fn) {
|
|
6
|
+
const start = performance.now();
|
|
7
|
+
fn();
|
|
8
|
+
const end = performance.now();
|
|
9
|
+
console.log(`${name.padEnd(30)}: ${(end - start).toFixed(2)}ms`);
|
|
10
|
+
}
|
|
11
|
+
async function runBenchmarks() {
|
|
12
|
+
console.log("--- 1. Additive Chain (1M iterations of +0.1) ---");
|
|
13
|
+
const addIterations = 1_000_000;
|
|
14
|
+
benchmark("Standard Float", () => {
|
|
15
|
+
let sum = 0;
|
|
16
|
+
for (let i = 0; i < addIterations; i++)
|
|
17
|
+
sum += 0.1;
|
|
18
|
+
});
|
|
19
|
+
benchmark("Gracio (Mutable)", () => {
|
|
20
|
+
const sum = Gracio.fromInt(0);
|
|
21
|
+
const inc = Gracio.fromFloat(0.1);
|
|
22
|
+
for (let i = 0; i < addIterations; i++)
|
|
23
|
+
sum.add(inc);
|
|
24
|
+
});
|
|
25
|
+
benchmark("big.js", () => {
|
|
26
|
+
let sum = new Big(0);
|
|
27
|
+
const inc = new Big(0.1);
|
|
28
|
+
for (let i = 0; i < addIterations; i++)
|
|
29
|
+
sum = sum.plus(inc);
|
|
30
|
+
});
|
|
31
|
+
benchmark("decimal.js", () => {
|
|
32
|
+
let sum = new Decimal(0);
|
|
33
|
+
const inc = new Decimal(0.1);
|
|
34
|
+
for (let i = 0; i < addIterations; i++)
|
|
35
|
+
sum = sum.plus(inc);
|
|
36
|
+
});
|
|
37
|
+
benchmark("fraction.js", () => {
|
|
38
|
+
let sum = new Fraction(0);
|
|
39
|
+
const inc = new Fraction(0.1);
|
|
40
|
+
for (let i = 0; i < addIterations; i++)
|
|
41
|
+
sum = sum.add(inc);
|
|
42
|
+
});
|
|
43
|
+
console.log("\n--- 2. Multiplicative Chain (1K iterations of * 1.1) ---");
|
|
44
|
+
const multIterations = 1_000;
|
|
45
|
+
benchmark("Standard Float", () => {
|
|
46
|
+
let val = 1.0;
|
|
47
|
+
for (let i = 0; i < multIterations; i++)
|
|
48
|
+
val *= 1.1;
|
|
49
|
+
});
|
|
50
|
+
benchmark("Gracio (No Limit)", () => {
|
|
51
|
+
const val = Gracio.fromInt(1);
|
|
52
|
+
const mul = Gracio.fromFloat(1.1);
|
|
53
|
+
for (let i = 0; i < multIterations; i++)
|
|
54
|
+
val.multiply(mul);
|
|
55
|
+
});
|
|
56
|
+
benchmark("Gracio (Limit 50)", () => {
|
|
57
|
+
const val = Gracio.fromInt(1);
|
|
58
|
+
val.precisionLimit = 50;
|
|
59
|
+
const mul = Gracio.fromFloat(1.1);
|
|
60
|
+
for (let i = 0; i < multIterations; i++)
|
|
61
|
+
val.multiply(mul);
|
|
62
|
+
});
|
|
63
|
+
benchmark("big.js", () => {
|
|
64
|
+
let val = new Big(1);
|
|
65
|
+
const mul = new Big(1.1);
|
|
66
|
+
for (let i = 0; i < multIterations; i++)
|
|
67
|
+
val = val.times(mul);
|
|
68
|
+
});
|
|
69
|
+
benchmark("decimal.js", () => {
|
|
70
|
+
let val = new Decimal(1);
|
|
71
|
+
const mul = new Decimal(1.1);
|
|
72
|
+
for (let i = 0; i < multIterations; i++)
|
|
73
|
+
val = val.times(mul);
|
|
74
|
+
});
|
|
75
|
+
console.log("\n--- 3. Chaotic Loop (Logistic Map, 500 iterations) ---");
|
|
76
|
+
const chaosIterations = 500;
|
|
77
|
+
benchmark("Standard Float", () => {
|
|
78
|
+
let x = 0.7;
|
|
79
|
+
for (let i = 0; i < chaosIterations; i++)
|
|
80
|
+
x = 4 * x * (1 - x);
|
|
81
|
+
});
|
|
82
|
+
benchmark("Gracio (Limit 100)", () => {
|
|
83
|
+
const x = Gracio.fromFloat(0.7);
|
|
84
|
+
x.precisionLimit = 100;
|
|
85
|
+
const r = Gracio.fromInt(4);
|
|
86
|
+
const one = Gracio.fromInt(1);
|
|
87
|
+
for (let i = 0; i < chaosIterations; i++) {
|
|
88
|
+
const temp = one.clone().subtract(x);
|
|
89
|
+
x.multiply(r).multiply(temp);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
console.log("fraction.js : N/A (Digit Explosion - would hang process)");
|
|
93
|
+
}
|
|
94
|
+
runBenchmarks().catch(console.error);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Gracio } from '../src/gracio.js';
|
|
2
|
+
/**
|
|
3
|
+
* Simple example showing how precise coordinates prevent "jitter"
|
|
4
|
+
* and clipping in collision detection for games.
|
|
5
|
+
*/
|
|
6
|
+
function getIntersection(x1, y1, x2, y2, x3, y3, x4, y4) {
|
|
7
|
+
// Determinant formula for line intersection
|
|
8
|
+
const den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
|
9
|
+
if (den === 0n)
|
|
10
|
+
return null; // Parallel lines
|
|
11
|
+
const pxNum = (x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4);
|
|
12
|
+
const pyNum = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4);
|
|
13
|
+
return {
|
|
14
|
+
x: new Gracio(pxNum, den),
|
|
15
|
+
y: new Gracio(pyNum, den)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
console.log("--- Collision Detection Demo ---");
|
|
19
|
+
// Two lines that are nearly parallel and very far from origin
|
|
20
|
+
// Line 1: (1000000, 1000000) to (1000001, 1000001)
|
|
21
|
+
// Line 2: (1000000, 1000001) to (1000001, 1000000)
|
|
22
|
+
const intersection = getIntersection(1000000n, 1000000n, 1000001n, 1000001n, 1000000n, 1000001n, 1000001n, 1000000n);
|
|
23
|
+
if (intersection) {
|
|
24
|
+
console.log(`Exact Intersection Point: X=${intersection.x.toString()}, Y=${intersection.y.toString()}`);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
console.log("Lines are parallel");
|
|
28
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Gracio } from '../src/gracio.js';
|
|
2
|
+
import Big from 'big.js';
|
|
3
|
+
import Decimal from 'decimal.js';
|
|
4
|
+
import Fraction from 'fraction.js';
|
|
5
|
+
async function auditMemory() {
|
|
6
|
+
console.log("--- Memory Usage Audit (1M Additions) ---");
|
|
7
|
+
console.log("Note: Run with 'node --expose-gc' for accurate results.\n");
|
|
8
|
+
const iterations = 1_000_000;
|
|
9
|
+
const stepValue = 0.1;
|
|
10
|
+
const libs = [
|
|
11
|
+
{
|
|
12
|
+
name: "Gracio (Mutable)",
|
|
13
|
+
fn: () => {
|
|
14
|
+
const sum = Gracio.fromInt(0);
|
|
15
|
+
const inc = Gracio.fromFloat(stepValue);
|
|
16
|
+
for (let i = 0; i < iterations; i++) {
|
|
17
|
+
sum.add(inc);
|
|
18
|
+
}
|
|
19
|
+
return sum;
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "big.js",
|
|
24
|
+
fn: () => {
|
|
25
|
+
let sum = new Big(0);
|
|
26
|
+
const inc = new Big(stepValue);
|
|
27
|
+
for (let i = 0; i < iterations; i++) {
|
|
28
|
+
sum = sum.plus(inc);
|
|
29
|
+
}
|
|
30
|
+
return sum;
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "decimal.js",
|
|
35
|
+
fn: () => {
|
|
36
|
+
let sum = new Decimal(0);
|
|
37
|
+
const inc = new Decimal(stepValue);
|
|
38
|
+
for (let i = 0; i < iterations; i++) {
|
|
39
|
+
sum = sum.plus(inc);
|
|
40
|
+
}
|
|
41
|
+
return sum;
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "fraction.js",
|
|
46
|
+
fn: () => {
|
|
47
|
+
let sum = new Fraction(0);
|
|
48
|
+
const inc = new Fraction(stepValue);
|
|
49
|
+
for (let i = 0; i < iterations; i++) {
|
|
50
|
+
sum = sum.add(inc);
|
|
51
|
+
}
|
|
52
|
+
return sum;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
];
|
|
56
|
+
for (const lib of libs) {
|
|
57
|
+
// Force GC if available
|
|
58
|
+
if (global.gc) {
|
|
59
|
+
global.gc();
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.warn(`Warning: global.gc() not found. Run with --expose-gc.`);
|
|
63
|
+
}
|
|
64
|
+
const startMem = process.memoryUsage().heapUsed;
|
|
65
|
+
const result = lib.fn();
|
|
66
|
+
const endMem = process.memoryUsage().heapUsed;
|
|
67
|
+
const deltaMB = (endMem - startMem) / 1024 / 1024;
|
|
68
|
+
console.log(`${lib.name.padEnd(20)}: Delta ${deltaMB.toFixed(2)} MB`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
auditMemory().catch(console.error);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Gracio } from '../src/gracio.js';
|
|
2
|
+
console.log("--- Precision Drift Demo ---");
|
|
3
|
+
// Floating point drift example
|
|
4
|
+
let floatSum = 0;
|
|
5
|
+
for (let i = 0; i < 10; i++) {
|
|
6
|
+
floatSum += 0.1;
|
|
7
|
+
}
|
|
8
|
+
console.log(`Floating Point Sum (0.1 * 10): ${floatSum}`);
|
|
9
|
+
// Result: 0.9999999999999999
|
|
10
|
+
// PreciseNumber example
|
|
11
|
+
let preciseSum = Gracio.fromInt(0);
|
|
12
|
+
const step = new Gracio(1n, 10n); // 1/10
|
|
13
|
+
for (let i = 0; i < 10; i++) {
|
|
14
|
+
preciseSum.add(step);
|
|
15
|
+
}
|
|
16
|
+
console.log(`PreciseNumber Sum (1/10 * 10): ${preciseSum.toString()}`);
|
|
17
|
+
// Result: 1/1
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Gracio } from '../src/gracio.js';
|
|
2
|
+
import Big from 'big.js';
|
|
3
|
+
import Decimal from 'decimal.js';
|
|
4
|
+
import Fraction from 'fraction.js';
|
|
5
|
+
function validate(testName, preciseResult, others) {
|
|
6
|
+
console.log(`\n--- ${testName} ---`);
|
|
7
|
+
console.log(`Gracio: ${preciseResult.toFixed(20)}`);
|
|
8
|
+
for (const [lib, val] of Object.entries(others)) {
|
|
9
|
+
const diff = Math.abs(preciseResult - val);
|
|
10
|
+
const status = diff === 0 ? "✅ MATCH" : `❌ DRIFT (${diff.toExponential(4)})`;
|
|
11
|
+
console.log(`${lib.padEnd(15)}: ${val.toFixed(20)} ${status}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async function runValidation() {
|
|
15
|
+
console.log("🚀 Starting Cross-Library Precision Validation...");
|
|
16
|
+
// Test 1: The Reciprocal Trap (1/3 * 3)
|
|
17
|
+
const t1Precise = new Gracio(1n, 3n).multiply(Gracio.fromInt(3)).toFloat();
|
|
18
|
+
const t1Big = new Big(1).div(3).times(3).toNumber();
|
|
19
|
+
const t1Dec = new Decimal(1).div(3).times(3).toNumber();
|
|
20
|
+
const t1Frac = Number(new Fraction(1).div(3).mul(3));
|
|
21
|
+
validate("Reciprocal Trap (1/3 * 3)", t1Precise, {
|
|
22
|
+
"big.js": t1Big,
|
|
23
|
+
"decimal.js": t1Dec,
|
|
24
|
+
"fraction.js": t1Frac
|
|
25
|
+
});
|
|
26
|
+
// Test 2: Compound Interest Chain (1.05 ^ 100)
|
|
27
|
+
const iterations = 100;
|
|
28
|
+
const rate = 1.05;
|
|
29
|
+
const t2PreciseObj = Gracio.fromInt(1);
|
|
30
|
+
const t2Mul = Gracio.fromFloat(rate);
|
|
31
|
+
for (let i = 0; i < iterations; i++)
|
|
32
|
+
t2PreciseObj.multiply(t2Mul);
|
|
33
|
+
const t2Precise = t2PreciseObj.toFloat();
|
|
34
|
+
let t2Big = new Big(1);
|
|
35
|
+
const t2BigMul = new Big(rate);
|
|
36
|
+
for (let i = 0; i < iterations; i++)
|
|
37
|
+
t2Big = t2Big.times(t2BigMul);
|
|
38
|
+
let t2Dec = new Decimal(1);
|
|
39
|
+
const t2DecMul = new Decimal(rate);
|
|
40
|
+
for (let i = 0; i < iterations; i++)
|
|
41
|
+
t2Dec = t2Dec.times(t2DecMul);
|
|
42
|
+
let t2Frac = new Fraction(1);
|
|
43
|
+
const t2FracMul = new Fraction(rate);
|
|
44
|
+
for (let i = 0; i < iterations; i++)
|
|
45
|
+
t2Frac = t2Frac.mul(t2FracMul);
|
|
46
|
+
validate(`Compound Growth (${rate}^${iterations})`, t2Precise, {
|
|
47
|
+
"big.js": t2Big.toNumber(),
|
|
48
|
+
"decimal.js": t2Dec.toNumber(),
|
|
49
|
+
"fraction.js": Number(t2Frac)
|
|
50
|
+
});
|
|
51
|
+
// Test 3: The "Salami Slicing" (Small increments on large base)
|
|
52
|
+
const base = 1000000;
|
|
53
|
+
const inc = 0.0000001;
|
|
54
|
+
const steps = 10000;
|
|
55
|
+
const t3PreciseObj = Gracio.fromInt(base);
|
|
56
|
+
const t3Inc = Gracio.fromFloat(inc);
|
|
57
|
+
for (let i = 0; i < steps; i++)
|
|
58
|
+
t3PreciseObj.add(t3Inc);
|
|
59
|
+
const t3Precise = t3PreciseObj.toFloat();
|
|
60
|
+
let t3Big = new Big(base);
|
|
61
|
+
const t3BigInc = new Big(inc);
|
|
62
|
+
for (let i = 0; i < steps; i++)
|
|
63
|
+
t3Big = t3Big.plus(t3BigInc);
|
|
64
|
+
let t3Dec = new Decimal(base);
|
|
65
|
+
const t3DecInc = new Decimal(inc);
|
|
66
|
+
for (let i = 0; i < steps; i++)
|
|
67
|
+
t3Dec = t3Dec.plus(t3DecInc);
|
|
68
|
+
let t3Frac = new Fraction(base);
|
|
69
|
+
const t3FracInc = new Fraction(inc);
|
|
70
|
+
for (let i = 0; i < steps; i++)
|
|
71
|
+
t3Frac = t3Frac.add(t3FracInc);
|
|
72
|
+
validate(`Salami Slicing (${steps} x ${inc})`, t3Precise, {
|
|
73
|
+
"big.js": t3Big.toNumber(),
|
|
74
|
+
"decimal.js": t3Dec.toNumber(),
|
|
75
|
+
"fraction.js": Number(t3Frac)
|
|
76
|
+
});
|
|
77
|
+
console.log("\n✅ Validation Complete.");
|
|
78
|
+
}
|
|
79
|
+
runValidation().catch(console.error);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var GracioLib=(()=>{var S=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var G=Object.prototype.hasOwnProperty;var N=(h,t)=>{for(var n in t)S(h,n,{get:t[n],enumerable:!0})},R=(h,t,n,e)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of P(t))!G.call(h,i)&&i!==n&&S(h,i,{get:()=>t[i],enumerable:!(e=D(t,i))||e.enumerable});return h};var B=h=>R(S({},"__esModule",{value:!0}),h);var M={};N(M,{Constants:()=>x,Gracio:()=>c,Lexer:()=>k,Parser:()=>v});var T=[2n,3n,5n,7n,11n,13n,17n,19n,23n,29n,31n,37n,41n,43n,47n,53n,59n,61n,67n,71n,73n,79n,83n,89n,97n,101n,103n,107n,109n,113n,127n,131n,137n,139n,149n,151n,157n,163n,167n,173n,179n,181n,191n,193n,197n,199n,211n,223n,227n,229n,233n,239n,241n,251n,257n,263n,269n,271n,277n,281n,283n,293n,307n,311n,313n,317n,331n,337n,347n,349n,353n,359n,367n,373n,379n,383n,389n,397n,401n,409n,419n,421n,431n,433n,439n,443n,449n,457n,461n,463n,467n,479n,487n,491n,499n],c=class h{numerator;denominator;precisionLimit;static MIN_SIMPLIFY_THRESHOLD=10n**20n;lastSimplifiedDigits=0;constructor(t,n=1n,e){if(n===0n)throw new Error("Denominator cannot be zero");n<0n&&(t*=-1n,n*=-1n),this.numerator=t,this.denominator=n,this.precisionLimit=e,this.simplify()}static fromInt(t){return new h(BigInt(t),1n)}static fromFloat(t){let n=String(t).trim().toLowerCase();if(n==="nan"||n==="infinity"||n==="-infinity")throw new Error("Cannot represent NaN or Infinity as a ratio");let e=n.indexOf("e");if(e!==-1){let f=n.substring(0,e),u=n.substring(e+1),p=parseInt(u,10);if(isNaN(p))throw new Error(`Invalid exponent in float: ${n}`);let g=h.fromFloat(f),m=p>=0?10n**BigInt(p):1n,w=p<0?10n**BigInt(-p):1n;return new h(g.numerator*m,g.denominator*w)}let i=n.indexOf(".");if(i===-1)return new h(BigInt(n),1n);let o=n.substring(0,i),r=n.substring(i+1),s=n.startsWith("-"),l=o.replace("-","")+r,d=BigInt(l||"0"),b=10n**BigInt(r.length);return new h(s?-d:d,b)}abs(t){return t<0n?-t:t}clone(){return new h(this.numerator,this.denominator)}lastSimplifiedBits=0;shouldSimplify(){let t=this.abs(this.numerator).toString().length;return this.abs(this.numerator)>h.MIN_SIMPLIFY_THRESHOLD&&t>this.lastSimplifiedDigits+20}checkPrecisionLimit(){this.precisionLimit!==void 0&&this.abs(this.denominator).toString().length>this.precisionLimit*2&&this.pureApproximate(this.precisionLimit)}simplify(){let t=performance.now(),n=this.abs(this.numerator),e=this.abs(this.denominator);if(n===0n)return this;if(e===0n)throw new Error("Denominator cannot be zero");for(let r of T)for(;n%r===0n&&e%r===0n;)n/=r,e/=r,this.numerator/=r,this.denominator/=r;let i=this.calculateGCD(n,e);i>1n&&(this.numerator/=i,this.denominator/=i);let o=performance.now();return o-t>10&&console.log(`[Simplify] Took ${(o-t).toFixed(2)}ms | Digits: ${n.toString().length}`),this.lastSimplifiedDigits=this.abs(this.numerator).toString().length,this.lastSimplifiedBits=this.abs(this.numerator).toString(2).length,this}add(t){return this.denominator===t.denominator?this.numerator+=t.numerator:(this.numerator=this.numerator*t.denominator+t.numerator*this.denominator,this.denominator=this.denominator*t.denominator),this.shouldSimplify()&&this.simplify(),this.checkPrecisionLimit(),this}subtract(t){return this.denominator===t.denominator?this.numerator-=t.numerator:(this.numerator=this.numerator*t.denominator-t.numerator*this.denominator,this.denominator=this.denominator*t.denominator),this.shouldSimplify()&&this.simplify(),this.checkPrecisionLimit(),this}multiply(t){let n=this.abs(this.numerator),e=this.abs(this.denominator),i=this.abs(t.numerator),o=this.abs(t.denominator),r=18446744073709551616n;if(n<r||e<r||i<r||o<r)return this.numerator*=t.numerator,this.denominator*=t.denominator,this.shouldSimplify()&&this.simplify(),this.checkPrecisionLimit(),this;let s=this.calculateGCD(n,o),a=this.calculateGCD(i,e);return this.numerator=this.numerator/s*(t.numerator/a),this.denominator=this.denominator/a*(t.denominator/s),this.checkPrecisionLimit(),this}divide(t){if(t.numerator===0n)throw new Error("Cannot divide by zero");let n=this.abs(this.numerator),e=this.abs(this.denominator),i=this.abs(t.numerator),o=this.abs(t.denominator),r=18446744073709551616n;if(n<r||e<r||i<r||o<r)return this.numerator*=t.denominator,this.denominator*=t.numerator,this.denominator<0n&&(this.numerator*=-1n,this.denominator*=-1n),this.shouldSimplify()&&this.simplify(),this.checkPrecisionLimit(),this;let s=this.calculateGCD(n,i),a=this.calculateGCD(o,e);return this.numerator=this.numerator/s*(o/a),this.denominator=this.denominator/a*(i/s),this.denominator<0n&&(this.numerator*=-1n,this.denominator*=-1n),this.checkPrecisionLimit(),this}calculateGCD(t,n){let e=t,i=n;if(e===0n)return i;if(i===0n)return e;let o=0n;for(;((e|i)&1n)===0n;)e>>=1n,i>>=1n,o++;for(;(e&1n)===0n;)e>>=1n;do{for(;(i&1n)===0n;)i>>=1n;if(e>i){let r=e;e=i,i=r}i=i-e}while(i!==0n);return e<<o}pow(t){if(t===0n)return this.numerator=1n,this.denominator=1n,this;let n=t<0n?-t:t,e=this.numerator**n,i=this.denominator**n;return t<0n&&([e,i]=[i,e]),this.numerator=e,this.denominator=i,this}toFloat(){this.simplify();let t=this.abs(this.numerator),n=this.abs(this.denominator);if(t===0n)return 0;let e=t.toString(2).length,i=n.toString(2).length;if(Math.max(e,i)>1e3){let r=BigInt(e-60),s=BigInt(i-60),a=Number(this.numerator>>r),l=Number(this.denominator>>s);return a/l*Math.pow(2,e-i)}return Number(this.numerator)/Number(this.denominator)}approximate(t=15){return this.pureApproximate(t)}pureApproximate(t=50){console.log(`[PrecisionLimit] Approximating to ${t} digits...`);let n=this.abs(this.numerator),e=this.abs(this.denominator),i=this.numerator<0n!=this.denominator<0n;if(n===0n)return this;let o=0n,r=1n,s=1n,a=0n;for(;e!==0n;){let l=n/e,d=n%e,b=l*r+o,f=l*a+s;if(f.toString().length>t)break;o=r,r=b,s=a,a=f,n=e,e=d}return this.numerator=i?-r:r,this.denominator=a,this}toString(){return this.simplify(),`${this.numerator}/${this.denominator}`}static root(t,n){if(console.log(`[Debug] Calculating root(${t}, ${n.toString()})`),t===0n)throw new Error("Root index cannot be zero");if(t===1n)return n.clone();let e=n.numerator,i=n.denominator,o=this.integerRoot(e,t),r=this.integerRoot(i,t);if(o!==null&&r!==null)return new h(o,r);let s=n.toFloat(),a=Math.pow(s,1/Number(t)),l=0n,d=1n,b=1n,f=0n,u=a,p=Math.floor(u),g=0n,m=1n,w=1n,y=0n;for(let E=0;E<15;E++){let I=BigInt(Math.floor(u))*m+g,L=BigInt(Math.floor(u))*y+w;if(g=m,m=I,w=y,y=L,u-Math.floor(u)===0)break;u=1/(u-Math.floor(u))}return new h(m,y)}static integerRoot(t,n){if(t===0n)return 0n;if(t<0n&&n%2n===0n)return null;let e=t<0n,i=t<0n?-t:t;for(let s of T)if(i%s===0n){let a=0,l=i;for(;l%s===0n;)l/=s,a++;if(a%Number(n)!==0)return null}let o=1n,r=i;for(n>=2n;o<=r;){let s=(o+r)/2n;if(s===0n){o=1n;continue}let a=s**n;if(a===i)return e?-s:s;a<i?o=s+1n:r=s-1n}return null}};var v=class{position=0;tokens;logger;constructor(t,n){this.tokens=t,this.logger=n}parse(){let t=this.parseExpression(),n=this.collapse(t);return new c(n.n,n.d)}collapse(t){if(t.type==="ratio")return{n:t.n,d:t.d};let n=new c(t.value.n,t.value.d),e=c.root(t.index,n);return{n:e.numerator,d:e.denominator}}parseExpression(){let t=this.parseTerm();for(;this.position<this.tokens.length&&this.currentToken().type==="operator"&&["+","-"].includes(this.currentToken().value);){let n=this.currentToken().value;this.position++;let e=this.parseTerm(),i=this.collapse(t),o=this.collapse(e);n==="+"?t={type:"ratio",n:i.n*o.d+o.n*i.d,d:i.d*o.d}:t={type:"ratio",n:i.n*o.d-o.n*i.d,d:i.d*o.d}}return t}parseTerm(){let t=this.parseExponent();for(;this.position<this.tokens.length&&this.currentToken().type==="operator"&&["*","/"].includes(this.currentToken().value);){let n=this.currentToken().value;this.position++;let e=this.parseExponent();if(n==="*")if(t.type==="root"&&e.type==="root"&&t.index===e.index)t={type:"root",index:t.index,value:{n:t.value.n*e.value.n,d:t.value.d*e.value.d}};else{let i=this.collapse(t),o=this.collapse(e);t={type:"ratio",n:i.n*o.n,d:i.d*o.d}}else if(t.type==="root"&&e.type==="root"&&t.index===e.index)t={type:"root",index:t.index,value:{n:t.value.n*e.value.d,d:t.value.d*e.value.n}};else{let i=this.collapse(t),o=this.collapse(e);t={type:"ratio",n:i.n*o.d,d:i.d*o.n}}}return t}parseExponent(){let t=this.parseFactor();for(;this.position<this.tokens.length&&this.currentToken().type==="operator"&&this.currentToken().value==="^";){this.position++;let n=this.parseFactor(),e=this.collapse(n);if(e.d!==1n)throw new Error("Exponents must be integers in the current implementation");let i=e.n;if(t.type==="root"){let o=i<0n?-i:i,r=t.value.n**o,s=t.value.d**o;i<0n&&([r,s]=[s,r]),t={type:"root",index:t.index,value:{n:r,d:s}}}else{let o=this.collapse(t),r=i<0n?-i:i,s=o.n**r,a=o.d**r;i<0n&&([s,a]=[a,s]),t={type:"ratio",n:s,d:a}}}return t}parseFactor(){let t=this.currentToken();if(t.type==="number"){this.position++;let n=c.fromFloat(t.value);return{type:"ratio",n:n.numerator,d:n.denominator}}if(t.type==="function"&&t.value==="root"){if(this.position++,this.currentToken().type!=="(")throw new Error("Expected '(' after root");this.position++;let n=this.parseExpression();if(this.currentToken().type!=="operator"||this.currentToken().value!==",")throw new Error("Expected ',' separating root index and value");this.position++;let e=this.parseExpression();if(this.currentToken().type!==")")throw new Error("Expected ')' after root arguments");return this.position++,{type:"root",index:this.collapse(n).n,value:this.collapse(e)}}if(t.type==="("){this.position++;let n=this.parseExpression();if(this.currentToken().type!==")")throw new Error("Expected closing parenthesis");return this.position++,n}throw new Error(`Unexpected token: ${JSON.stringify(t)}`)}currentToken(){if(this.position>=this.tokens.length)throw new Error("Unexpected end of input");return this.tokens[this.position]}};var k=class{position=0;tokenize(t){let n=[];for(;this.position<t.length;){let e=t[this.position];if(e.match(/\s/)){this.position++;continue}if(e.match(/[0-9.]/)||e==="-"&&(n.length===0||n[n.length-1].type==="operator"||n[n.length-1].type==="(")){let i="";for(e==="-"&&(i+=e,this.position++);this.position<t.length&&t[this.position].match(/[0-9.]/);)i+=t[this.position],this.position++;n.push({type:"number",value:i})}else if(["+","-","*","/","(",")","^",","].includes(e)){let i=["(",")"].includes(e)?e:"operator";n.push({type:i,value:e}),this.position++}else if(e.match(/[a-zA-Z]/)){let i="";for(;this.position<t.length&&t[this.position].match(/[a-zA-Z0-9]/);)i+=t[this.position],this.position++;n.push({type:"function",value:i})}}return n}};var x=class{static get PI(){return new c(5419351n,1724137n)}static get E(){return new c(2718281828459n,1000000000000n)}static get PHI(){let t=c.root(2n,new c(5n,1n));return new c(1n,1n).add(t).divide(new c(2n,1n))}static get GRAVITY(){return c.fromFloat("6.67430e-11")}};return B(M);})();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var PreciseLib=(()=>{var S=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var D=Object.prototype.hasOwnProperty;var P=(h,t)=>{for(var n in t)S(h,n,{get:t[n],enumerable:!0})},R=(h,t,n,e)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of C(t))!D.call(h,i)&&i!==n&&S(h,i,{get:()=>t[i],enumerable:!(e=L(t,i))||e.enumerable});return h};var B=h=>R(S({},"__esModule",{value:!0}),h);var M={};P(M,{CalcNumber:()=>l,Constants:()=>x,Lexer:()=>k,Parser:()=>v});var T=[2n,3n,5n,7n,11n,13n,17n,19n,23n,29n,31n,37n,41n,43n,47n,53n,59n,61n,67n,71n,73n,79n,83n,89n,97n,101n,103n,107n,109n,113n,127n,131n,137n,139n,149n,151n,157n,163n,167n,173n,179n,181n,191n,193n,197n,199n,211n,223n,227n,229n,233n,239n,241n,251n,257n,263n,269n,271n,277n,281n,283n,293n,307n,311n,313n,317n,331n,337n,347n,349n,353n,359n,367n,373n,379n,383n,389n,397n,401n,409n,419n,421n,431n,433n,439n,443n,449n,457n,461n,463n,467n,479n,487n,491n,499n],l=class h{numerator;denominator;precisionLimit;static MIN_SIMPLIFY_THRESHOLD=10n**20n;lastSimplifiedDigits=0;constructor(t,n=1n,e){if(n===0n)throw new Error("Denominator cannot be zero");n<0n&&(t*=-1n,n*=-1n),this.numerator=t,this.denominator=n,this.precisionLimit=e,this.simplify()}static fromInt(t){return new h(BigInt(t),1n)}static fromFloat(t){let n=String(t).trim().toLowerCase();if(n==="nan"||n==="infinity"||n==="-infinity")throw new Error("Cannot represent NaN or Infinity as a ratio");let e=n.indexOf("e");if(e!==-1){let f=n.substring(0,e),u=n.substring(e+1),p=parseInt(u,10);if(isNaN(p))throw new Error(`Invalid exponent in float: ${n}`);let g=h.fromFloat(f),m=p>=0?10n**BigInt(p):1n,w=p<0?10n**BigInt(-p):1n;return new h(g.numerator*m,g.denominator*w)}let i=n.indexOf(".");if(i===-1)return new h(BigInt(n),1n);let o=n.substring(0,i),r=n.substring(i+1),s=n.startsWith("-"),c=o.replace("-","")+r,d=BigInt(c||"0"),b=10n**BigInt(r.length);return new h(s?-d:d,b)}abs(t){return t<0n?-t:t}clone(){return new h(this.numerator,this.denominator)}lastSimplifiedBits=0;shouldSimplify(){let t=this.abs(this.numerator).toString().length;return this.abs(this.numerator)>h.MIN_SIMPLIFY_THRESHOLD&&t>this.lastSimplifiedDigits+20}checkPrecisionLimit(){this.precisionLimit!==void 0&&this.abs(this.denominator).toString().length>this.precisionLimit*2&&this.pureApproximate(this.precisionLimit)}simplify(){let t=performance.now(),n=this.abs(this.numerator),e=this.abs(this.denominator);if(n===0n)return this;if(e===0n)throw new Error("Denominator cannot be zero");for(let r of T)for(;n%r===0n&&e%r===0n;)n/=r,e/=r,this.numerator/=r,this.denominator/=r;let i=this.calculateGCD(n,e);i>1n&&(this.numerator/=i,this.denominator/=i);let o=performance.now();return o-t>10&&console.log(`[Simplify] Took ${(o-t).toFixed(2)}ms | Digits: ${n.toString().length}`),this.lastSimplifiedDigits=this.abs(this.numerator).toString().length,this.lastSimplifiedBits=this.abs(this.numerator).toString(2).length,this}add(t){return this.denominator===t.denominator?this.numerator+=t.numerator:(this.numerator=this.numerator*t.denominator+t.numerator*this.denominator,this.denominator=this.denominator*t.denominator),this.shouldSimplify()&&this.simplify(),this.checkPrecisionLimit(),this}subtract(t){return this.denominator===t.denominator?this.numerator-=t.numerator:(this.numerator=this.numerator*t.denominator-t.numerator*this.denominator,this.denominator=this.denominator*t.denominator),this.shouldSimplify()&&this.simplify(),this.checkPrecisionLimit(),this}multiply(t){let n=this.abs(this.numerator),e=this.abs(this.denominator),i=this.abs(t.numerator),o=this.abs(t.denominator),r=18446744073709551616n;if(n<r||e<r||i<r||o<r)return this.numerator*=t.numerator,this.denominator*=t.denominator,this.shouldSimplify()&&this.simplify(),this.checkPrecisionLimit(),this;let s=this.calculateGCD(n,o),a=this.calculateGCD(i,e);return this.numerator=this.numerator/s*(t.numerator/a),this.denominator=this.denominator/a*(t.denominator/s),this.checkPrecisionLimit(),this}divide(t){if(t.numerator===0n)throw new Error("Cannot divide by zero");let n=this.abs(this.numerator),e=this.abs(this.denominator),i=this.abs(t.numerator),o=this.abs(t.denominator),r=18446744073709551616n;if(n<r||e<r||i<r||o<r)return this.numerator*=t.denominator,this.denominator*=t.numerator,this.denominator<0n&&(this.numerator*=-1n,this.denominator*=-1n),this.shouldSimplify()&&this.simplify(),this.checkPrecisionLimit(),this;let s=this.calculateGCD(n,i),a=this.calculateGCD(o,e);return this.numerator=this.numerator/s*(o/a),this.denominator=this.denominator/a*(i/s),this.denominator<0n&&(this.numerator*=-1n,this.denominator*=-1n),this.checkPrecisionLimit(),this}calculateGCD(t,n){let e=t,i=n;if(e===0n)return i;if(i===0n)return e;let o=0n;for(;((e|i)&1n)===0n;)e>>=1n,i>>=1n,o++;for(;(e&1n)===0n;)e>>=1n;do{for(;(i&1n)===0n;)i>>=1n;if(e>i){let r=e;e=i,i=r}i=i-e}while(i!==0n);return e<<o}pow(t){if(t===0n)return this.numerator=1n,this.denominator=1n,this;let n=t<0n?-t:t,e=this.numerator**n,i=this.denominator**n;return t<0n&&([e,i]=[i,e]),this.numerator=e,this.denominator=i,this}toFloat(){this.simplify();let t=this.abs(this.numerator),n=this.abs(this.denominator);if(t===0n)return 0;let e=t.toString(2).length,i=n.toString(2).length;if(Math.max(e,i)>1e3){let r=BigInt(e-60),s=BigInt(i-60),a=Number(this.numerator>>r),c=Number(this.denominator>>s);return a/c*Math.pow(2,e-i)}return Number(this.numerator)/Number(this.denominator)}approximate(t=15){return this.pureApproximate(t)}pureApproximate(t=50){console.log(`[PrecisionLimit] Approximating to ${t} digits...`);let n=this.abs(this.numerator),e=this.abs(this.denominator),i=this.numerator<0n!=this.denominator<0n;if(n===0n)return this;let o=0n,r=1n,s=1n,a=0n;for(;e!==0n;){let c=n/e,d=n%e,b=c*r+o,f=c*a+s;if(f.toString().length>t)break;o=r,r=b,s=a,a=f,n=e,e=d}return this.numerator=i?-r:r,this.denominator=a,this}toString(){return this.simplify(),`${this.numerator}/${this.denominator}`}static root(t,n){if(console.log(`[Debug] Calculating root(${t}, ${n.toString()})`),t===0n)throw new Error("Root index cannot be zero");if(t===1n)return n.clone();let e=n.numerator,i=n.denominator,o=this.integerRoot(e,t),r=this.integerRoot(i,t);if(o!==null&&r!==null)return new h(o,r);let s=n.toFloat(),a=Math.pow(s,1/Number(t)),c=0n,d=1n,b=1n,f=0n,u=a,p=Math.floor(u),g=0n,m=1n,w=1n,y=0n;for(let E=0;E<15;E++){let N=BigInt(Math.floor(u))*m+g,I=BigInt(Math.floor(u))*y+w;if(g=m,m=N,w=y,y=I,u-Math.floor(u)===0)break;u=1/(u-Math.floor(u))}return new h(m,y)}static integerRoot(t,n){if(t===0n)return 0n;if(t<0n&&n%2n===0n)return null;let e=t<0n,i=t<0n?-t:t;for(let s of T)if(i%s===0n){let a=0,c=i;for(;c%s===0n;)c/=s,a++;if(a%Number(n)!==0)return null}let o=1n,r=i;for(n>=2n;o<=r;){let s=(o+r)/2n;if(s===0n){o=1n;continue}let a=s**n;if(a===i)return e?-s:s;a<i?o=s+1n:r=s-1n}return null}};var v=class{position=0;tokens;logger;constructor(t,n){this.tokens=t,this.logger=n}parse(){let t=this.parseExpression(),n=this.collapse(t);return new l(n.n,n.d)}collapse(t){if(t.type==="ratio")return{n:t.n,d:t.d};let n=new l(t.value.n,t.value.d),e=l.root(t.index,n);return{n:e.numerator,d:e.denominator}}parseExpression(){let t=this.parseTerm();for(;this.position<this.tokens.length&&this.currentToken().type==="operator"&&["+","-"].includes(this.currentToken().value);){let n=this.currentToken().value;this.position++;let e=this.parseTerm(),i=this.collapse(t),o=this.collapse(e);n==="+"?t={type:"ratio",n:i.n*o.d+o.n*i.d,d:i.d*o.d}:t={type:"ratio",n:i.n*o.d-o.n*i.d,d:i.d*o.d}}return t}parseTerm(){let t=this.parseExponent();for(;this.position<this.tokens.length&&this.currentToken().type==="operator"&&["*","/"].includes(this.currentToken().value);){let n=this.currentToken().value;this.position++;let e=this.parseExponent();if(n==="*")if(t.type==="root"&&e.type==="root"&&t.index===e.index)t={type:"root",index:t.index,value:{n:t.value.n*e.value.n,d:t.value.d*e.value.d}};else{let i=this.collapse(t),o=this.collapse(e);t={type:"ratio",n:i.n*o.n,d:i.d*o.d}}else if(t.type==="root"&&e.type==="root"&&t.index===e.index)t={type:"root",index:t.index,value:{n:t.value.n*e.value.d,d:t.value.d*e.value.n}};else{let i=this.collapse(t),o=this.collapse(e);t={type:"ratio",n:i.n*o.d,d:i.d*o.n}}}return t}parseExponent(){let t=this.parseFactor();for(;this.position<this.tokens.length&&this.currentToken().type==="operator"&&this.currentToken().value==="^";){this.position++;let n=this.parseFactor(),e=this.collapse(n);if(e.d!==1n)throw new Error("Exponents must be integers in the current implementation");let i=e.n;if(t.type==="root"){let o=i<0n?-i:i,r=t.value.n**o,s=t.value.d**o;i<0n&&([r,s]=[s,r]),t={type:"root",index:t.index,value:{n:r,d:s}}}else{let o=this.collapse(t),r=i<0n?-i:i,s=o.n**r,a=o.d**r;i<0n&&([s,a]=[a,s]),t={type:"ratio",n:s,d:a}}}return t}parseFactor(){let t=this.currentToken();if(t.type==="number"){this.position++;let n=l.fromFloat(t.value);return{type:"ratio",n:n.numerator,d:n.denominator}}if(t.type==="function"&&t.value==="root"){if(this.position++,this.currentToken().type!=="(")throw new Error("Expected '(' after root");this.position++;let n=this.parseExpression();if(this.currentToken().type!=="operator"||this.currentToken().value!==",")throw new Error("Expected ',' separating root index and value");this.position++;let e=this.parseExpression();if(this.currentToken().type!==")")throw new Error("Expected ')' after root arguments");return this.position++,{type:"root",index:this.collapse(n).n,value:this.collapse(e)}}if(t.type==="("){this.position++;let n=this.parseExpression();if(this.currentToken().type!==")")throw new Error("Expected closing parenthesis");return this.position++,n}throw new Error(`Unexpected token: ${JSON.stringify(t)}`)}currentToken(){if(this.position>=this.tokens.length)throw new Error("Unexpected end of input");return this.tokens[this.position]}};var k=class{position=0;tokenize(t){let n=[];for(;this.position<t.length;){let e=t[this.position];if(e.match(/\s/)){this.position++;continue}if(e.match(/[0-9.]/)||e==="-"&&(n.length===0||n[n.length-1].type==="operator"||n[n.length-1].type==="(")){let i="";for(e==="-"&&(i+=e,this.position++);this.position<t.length&&t[this.position].match(/[0-9.]/);)i+=t[this.position],this.position++;n.push({type:"number",value:i})}else if(["+","-","*","/","(",")","^",","].includes(e)){let i=["(",")"].includes(e)?e:"operator";n.push({type:i,value:e}),this.position++}else if(e.match(/[a-zA-Z]/)){let i="";for(;this.position<t.length&&t[this.position].match(/[a-zA-Z0-9]/);)i+=t[this.position],this.position++;n.push({type:"function",value:i})}}return n}};var x=class{static get PI(){return new l(5419351n,1724137n)}static get E(){return new l(2718281828459n,1000000000000n)}static get PHI(){let t=l.root(2n,new l(5n,1n));return new l(1n,1n).add(t).divide(new l(2n,1n))}static get GRAVITY(){return l.fromFloat("6.67430e-11")}};return B(M);})();
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { performance } from 'perf_hooks';
|
|
2
|
+
/**
|
|
3
|
+
* Compares a PreciseNumber result with a float result.
|
|
4
|
+
* Returns the absolute drift between them.
|
|
5
|
+
*/
|
|
6
|
+
export function calculateDrift(ratio, floatVal) {
|
|
7
|
+
const ratioFloat = typeof ratio.toFloat === 'function' ? ratio.toFloat() : ratio;
|
|
8
|
+
return Math.abs(ratioFloat - floatVal);
|
|
9
|
+
}
|
|
10
|
+
export async function auditOperation(name, ratioOp, floatOp, exactValue, getFinalRatioValue, iterations = 10_000) {
|
|
11
|
+
// Benchmark Floats
|
|
12
|
+
const startFloat = performance.now();
|
|
13
|
+
let fRes = 0;
|
|
14
|
+
for (let i = 0; i < iterations; i++) {
|
|
15
|
+
fRes = floatOp();
|
|
16
|
+
}
|
|
17
|
+
const endFloat = performance.now();
|
|
18
|
+
// Benchmark Ratios
|
|
19
|
+
const startRatio = performance.now();
|
|
20
|
+
let rRes;
|
|
21
|
+
for (let i = 0; i < iterations; i++) {
|
|
22
|
+
rRes = ratioOp();
|
|
23
|
+
}
|
|
24
|
+
const endRatio = performance.now();
|
|
25
|
+
const floatTime = endFloat - startFloat;
|
|
26
|
+
const ratioTime = endRatio - startRatio;
|
|
27
|
+
// The "Truth" is the Ratio result (which we assume is exact)
|
|
28
|
+
const finalRatioValue = getFinalRatioValue(rRes);
|
|
29
|
+
const drift = Math.abs(fRes - finalRatioValue);
|
|
30
|
+
console.log(`\n[Audit: ${name}]`);
|
|
31
|
+
console.log(` Exact Result: ${finalRatioValue}`);
|
|
32
|
+
console.log(` Float result: ${fRes}`);
|
|
33
|
+
console.log(` Precision: Float drifted by ${drift.toExponential(4)}`);
|
|
34
|
+
console.log(` Performance: Ratio is ${(ratioTime / floatTime).toFixed(2)}x slower than Float`);
|
|
35
|
+
return {
|
|
36
|
+
precisionDrift: drift,
|
|
37
|
+
performanceTax: ratioTime / floatTime
|
|
38
|
+
};
|
|
39
|
+
}
|