Skip to content

Instantly share code, notes, and snippets.

@pyrocto
Last active January 25, 2024 19:13
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save pyrocto/5a068100abd5ff6dfbe69a73bbc510d7 to your computer and use it in GitHub Desktop.
Save pyrocto/5a068100abd5ff6dfbe69a73bbc510d7 to your computer and use it in GitHub Desktop.
A modest proposal for operator overloading in JavaScript
/*
It is a melancholy object-oriented language to those who walk through
this great town or travel in the country, when they see the JavaScript
programs crowded with method calls begging for more succinct syntax.
I think it is agreed by all parties, that whoever could find out a
fair, cheap and easy method of making these operators sound and useful
members of the language, would deserve so well of the publick, as
to have his statue set up for a preserver of the nation. I do therefore
humbly offer to publick consideration a pure JS library I've written
for overloading operators, which permits computations like these:
Complex numbers:
>> Complex()({r: 2, i: 0} / {r: 1, i: 1} + {r: -3, i: 2}))
<- {r: -2, i: 1}
Automatic differentiation:
Let f(x) = x^3 - 5x:
>> var f = x => Dual()(x * x * x - {x:5, dx:0} * x);
Now map it over some values:
>> [-2,-1,0,1,2].map(a=>({x:a,dx:1})).map(f).map(a=>a.dx)
<- [ 7, -2, -5, -2, 7 ]
i.e. f'(x) = 3x^2 - 5.
Polynoomials:
>> Poly()([1,-2,3,-4]*[5,-6]).map((c,p)=>''+c+'x^'+p).join(' + ')
<- "5x^0 + -16x^1 + 27x^2 + -38x^3 + 24x^4"
Big rational numbers (with concise syntax!):
// 112341324242 / 22341234124 + 334123124 / 5242342
// > with(Rat){Rat()(n112341324242d22341234124 + n334123124d5242342).join(' / ')}
// "2013413645483934535 / 29280097495019602"
The implementation is only mildly worse than eating babies.
*/
((global) => {
// Increase for more complicated fancier expressions
var numVars = 5;
var vars = Array.from(Array(numVars)).map((_,i)=>i);
var randoms = vars.map(() => Math.random());
var table = {};
// n is number of internal nodes
// f is a function to process each result
var trees = (n, f) => {
  // h is the "height", thinking of 1 as a step up and 0 as a step down
  // s is the current state
  var enumerate = (n, h, s, f) => {
    if (n === 0 && h === 0) {
      f(s + '0');
    } else {
      if (h > 0) {
        enumerate(n, h - 1, s + '0', f)
      }
      if (n > 0) {
        enumerate(n - 1, h + 1, s + '1', f)
      }
    }
  };
  enumerate(n, 0, '', f);
};
var toFunction = (s, pos, opCount, varCount) => {
if (s[pos] == '0') {
return [`x[${varCount}]`, pos + 1, opCount, varCount + 1];
}
var left, right, op;
pos++;
op = `ops[${opCount}]`;
[left, pos, opCount, varCount] = toFunction(s, pos, opCount + 1, varCount);
[right, pos, opCount, varCount] = toFunction(s, pos, opCount, varCount);
return [`${op}(${left},${right})`, pos, opCount, varCount];
};
var add = (x,y) => x+y; add.toString = ()=>'+';
var sub = (x,y) => x-y; sub.toString = ()=>'-';
var mul = (x,y) => x*y; mul.toString = ()=>'*';
var div = (x,y) => x/y; div.toString = ()=>'/';
var round = (x) => {
var million = Math.pow(2,20);
return Math.floor(x * million) / million;
};
var populate = (expr, ops, opCount) => {
if (ops.length == opCount) {
var result;
var order=[];
var x = vars.map(y => ({
valueOf:()=>{
order.push(y);
return randoms[order.length - 1];
}
}));
with ({ops, x}) { result = round(eval(expr)); }
table[result] = {ops: ops.map(x => '' + x), expr, order};
} else {
populate(expr, ops.concat(add), opCount);
populate(expr, ops.concat(sub), opCount);
populate(expr, ops.concat(mul), opCount);
populate(expr, ops.concat(div), opCount);
}
};
var allExprs = (s) => {
var [expr, , opCount, ] = toFunction(s, 0, 0, 0);
populate(expr, [], opCount);
};
vars.forEach(x=>trees(x, allExprs));
var makeContext = (constr, opTable) => () => {
// Set up values array
var V = [];
// Install temporary valueOf
var valueOf = constr.prototype.valueOf;
constr.prototype.valueOf = function () {
return randoms[V.push(this) - 1];
};
// Return function expecting key
return (key) => {
var {ops, expr, order} = table[round(+key)];
constr.prototype.valueOf = valueOf;
var result;
var index = 0;
var W = [];
V.forEach((v, i) => W[order[i]] = V[i]);
with ({ops: ops.map(x => opTable[x]), x: W}) { result = eval(expr); }
V = [];
return result;
};
};
global.makeContext = makeContext;
})(this);
var Complex = makeContext(Object, {
'+': (x, y) => ({r: x.r + y.r, i: x.i + y.i}),
'-': (x, y) => ({r: x.r - y.r, i: x.i - y.i}),
'*': (x, y) => ({r: x.r * y.r - x.i * y.i, i: x.r * y.i + x.i * y.r}),
'/': (x, y) => {
const norm = y.r**2 + y.i**2;
return {r: (x.r * y.r + x.i * y.i) / norm, i: (x.i * y.r - x.r * y.i) / norm};
}
});
var Dual = makeContext(Object, {
'+': (a, b) => ({x: a.x + b.x, dx: a.dx + b.dx}),
'-': (a, b) => ({x: a.x - b.x, dx: a.dx - b.dx}),
'*': (a, b) => ({x: a.x * b.x, dx: a.x * b.dx + a.dx * b.x}),
'/': (a, b) => ({x: a.x / b.x, dx: (a.dx * b.x - a.x * b.dx) / (b.x ** 2)})
});
var Poly = makeContext(Array, {
'+': (a, b) => (a.length >= b.length ? a.map((x,i) => x + (b[i]?b[i]:0)) : b.map((x,i) => x + (a[i]?a[i]:0))),
'-': (a, b) => (a.length >= b.length ? a.map((x,i) => x - (b[i]?b[i]:0)) : b.map((x,i) => x - (a[i]?a[i]:0))),
'*': (a, b) => {
var result = [];
for (var i = 0; i < a.length; ++i) {
for (var j = 0; j < b.length; ++j) {
result[i+j] = result[i+j] ? result[i+j] : 0;
result[i+j] += a[i] * b[j];
}
}
return result;
},
'/': (a, b) => { throw new Error('Not implemented'); }
});
var Str = new Proxy(makeContext(Array, {
'+':(a,b) => [a[0]+b[0]],
'*':(a,b) => [Array.from(Array(b[0])).map(x=>a[0]).join('')]
}), {
has (target, key) { return key.match(/^[a-z0-9A-Z_]+$/) && key !== 'Str'; },
get(target, prop, receiver) {
if (typeof prop == 'string') {
return [prop];
} else {
return target[prop];
}
}
});
function reduce(numerator,denominator) {
var gcd = function gcd(a,b){
return b ? gcd(b, a%b) : a;
};
gcd = gcd(numerator,denominator);
return [numerator/gcd, denominator/gcd];
}
var numDenom = /^n([0-9]+)d([0-9]+)$/;
var Rat = new Proxy(makeContext(Array, {
'+':(a,b) => reduce(a[0]*b[1] + a[1]*b[0], a[1]*b[1]),
'-':(a,b) => reduce(a[0]*b[1] - a[1]*b[0], a[1]*b[1]),
'*':(a,b) => reduce(a[0]*b[0], a[1]*b[1]),
'/':(a,b) => reduce(a[0]*b[1], a[1]*b[0])
}), {
has (target, key) { return !!key.match(numDenom); },
get(target, prop, receiver) {
if (typeof prop == 'string') {
var m = prop.match(numDenom);
return reduce(BigInt(m[1]), BigInt(m[2]));
} else {
return target[prop];
}
}
});
@jasoncortese
Copy link

Awesome, I had done something similar years ago, and was just thinking that modern js may make some of that easier. I will include a link to it if I can find it -- hopefully will provide some inspiration as yours has for me.

One of the uses I found was for something I was calling ValentNumbers, where a number can have multiple values. Useful to define a SQRT2, which alternates between [Math.sqrt(2), 2/Math.sqrt(2)] so that: SQRT2 * SQRT2 == 2 (instead of 2.000000004). Or INV7, which alternates between [1/7, 1/7, (1-6/7)].

A few thoughts:

  • "with" won't work in strict mode
  • create a Context class which returns a Proxy, then add the "+"/symbol methods to the handlers object passed in to the proxy
  • love the dual numbers example!

@jasoncortese
Copy link

jasoncortese commented Dec 7, 2023

I make no claims as to the readiness of this code -- this seems to be a more recent attempt than I recall...

/*
 * mathlib.js
 *
 */

class Primitive extends Number {
    static list = [];
    static stack = [];

    constructor(n) {
        super(n ?? Math.random());
        const parent = this.constructor;
        parent.list.push(this);
    }

    valueOf() {
        const parent = this.constructor;
        parent.stack.push(this);
        setTimeout(() => parent.stack.pop(), 1);
        return this;
    }
}

class Tuple extends Primitive {
    constructor(...args) {
        super();
        var buffer = new ArrayBuffer(4 * args.length);
        var matrix = new Float32Array(buffer);
        matrix.__defineGetter__('length', () => args.length);
        matrix.__defineSetter__('length', () => args.length);
    }

    parse(r, n, m, op) {
        var primitives = Tuple.primitives = Tuple.primitives || [];
        return (primitives.find(n => r == n)) ||
            ((n = primitives.find(n => m = primitives.find(m => op = findop(r, n, m)))) && n[op](m)) ||
            ((n = primitives.find(n => op = findopMatrix(r, n))) && n[op](m)) ||
            ((n = primitives.find(n => op = findopInt(r, n))) && n[op](m)) ||
            ((n = primitives.find(n => op = findopFloat(r, n))) && n[op](m)) ||
            ((n = r.constructor()) && (n.primitive = r) || n);

        function findop(r, n, m) {
            if (r == n * m) return 'times';
            if (r == n / m) return 'over';
            if (r == m / n) return 'into';
            if (r == n + m) return 'plus';
            if (r == n - m) return 'minus';
            if (r == m - n) return 'from';
        }

        function findopMatrix(r, n) {
            if (r == 0 - n) return 'neg';
            if (r == 1 / n) return 'inv';
            //if (r == n.adjugate)    return 'adj';
            //if (r == n.transpose)   return 'trans';
            //if (r == n.conjugate)   return 'conj';
            //if (r == n.transjugate) return 'transj';
        }

        function findopInt(r, n) {
            if (r / n == (r / n) >> 0) return m = r / n, 'times';
            if (n / r == (n / r) >> 0) return m = n / r, 'over';
            if (n * r == (n * r) >> 0) return m = n * r, 'into';
            if (r - n == (r - n) >> 0) return m = r - n, 'plus';
            if (n - r == (n - r) >> 0) return m = n - r, 'minus';
            if (n + r == (n + r) >> 0) return m = n + r, 'from';
        }

        function findopFloat(r, n) {
            if (r.insignificand(7) == (r - n).insignificand(7)) return m = (r - n).significand(7), 'plus';
            if (r.insignificand(7) == (n - r).insignificand(7)) return m = (n - r).significand(7), 'minus';
            if (r.insignificand(7) == (n + r).insignificand(7)) return m = (n + r).significand(7), 'from';
        }
    }
}

class Scalar extends Tuple {
    constructor(...args) {
        super(...args.slice(0, 1));
        this.order = 1;
        Object.defineProperties(this, {
            transpose: {get: () => Scalar(this[0])},
        });
    }
}

class Vector extends Tuple {
    constructor(...args) {
        super(...args.slice(0, 2));
        this.order = 2;
        Object.defineProperties(this, {
            transpose: {get: (b) => (b = Matrix(2,1)) && b.set(a) || b},
        });
    }
}

class Matrix2 extends Tuple {
    constructor(...args) {
        super(...args.slice(0, 4));
        this.order = 2;
        //this.primitive = arguments[4] || a.primitive;
    }

    tr    = () => this[0] + this[3]; //trace
    det   = () => Math.abs(this[0] * this[3] - this[1] * this[2]); //determinant
    adj   = () => Matrix2(this[3], -this[1], -this[2], this[0]); //adjugate
    inv   = () => Tuple.parse(this.adj / this.det); //-1 = inverse
    trans = () => Matrix2(this[0], this[2], this[1], this[3]); //T = transpose
    conj  = () => Matrix2(this[0], -this[1], -this[2], this[3]); //conjugate
    tranj = () => Matrix2(this[0], -this[2], -this[1], this[3]); //H = transjugate
    neg   = () => Matrix2(-this[0], -this[1], -this[2], -this[3]); //(-) = negative
    exp   = () => this.det - this.tr * this.tr / 4; //? = exponent

    times = (b) => Matrix2(Matrix2['*'][b.constructor.name](this, b));
    over  = (b) => Matrix2(Matrix2['*'][b.constructor.name](this, 1 / b));
    into  = (b) => Matrix2(Matrix2['*'][b.constructor.name](this.inv, b));
    dot   = (b) => Matrix2(Matrix2['*'][b.constructor.name](this.trans, b));
    plus  = (b) => Matrix2(Matrix2['+'][b.constructor.name](this, b));
    minus = (b) => Matrix2(Matrix2['+'][b.constructor.name](this, -b));
    from  = (b) => Matrix2(Matrix2['+'][b.constructor.name](this.neg, b));

    ['*'] = {
        Number: (a, b) => [
            a[0] * b,
            a[1] * b,
            a[2] * b,
            a[3] * b
        ],
        Scalar: (a, b) => [
            a[0] * b[0],
            a[1] * b[0],
            a[2] * b[0],
            a[3] * b[0]
        ],
        Vector: (a, b) => [
            a[0] * b[0],
            a[1] * b[0],
            a[2] * b[1],
            a[3] * b[1]
        ],
        Matrix2: (a, b) => [
            a[0] * b[0] + a[2] * b[1],
            a[1] * b[0] + a[3] * b[1],
            a[2] * b[3] + a[0] * b[2],
            a[3] * b[3] + a[1] * b[2]
        ],
    };

    ['+'] = {
        Number: (a, b) => [
            a[0] + b,
            a[1],
            a[2],
            a[3] + b
        ],
        Scalar: (a, b) => [
            a[0] + b[0],
            a[1],
            a[2],
            a[3] + b[0]
        ],
        Vector: (a, b) => [
            a[0] + b[0],
            a[1] + b[1],
            a[2] + b[1],
            a[3] + b[0]
        ],
        Matrix2: (a, b) => [
            a[0] + b[0],
            a[1] + b[1],
            a[2] + b[2],
            a[3] + b[3]
        ],
    };

    _exp() {
        a.delta = () => sqrt((a[0] - a[3]) * (a[0] - a[3]) + (4 * a[1] * a[2]));
        a.exp = () => Matrix2(a.exp_0, a.exp_1, a.exp_2, a.exp_3) / a.delta;
        a.exp[1] = () => Math.exp((a[0] + a[3]) / 2) * (2 * a[1]) * Math.sinh(a.delta / 2);
        a.exp[2] = () => Math.exp((a[0] + a[3]) / 2) * (2 * a[2]) * Math.sinh(a.delta / 2);
        a.exp[3] = () => Math.exp((a[0] + a[3]) / 2) * (a.delta * Math.cosh(a.delta / 2) - (a[0] - a[3]) * Math.sinh(a.delta / 2));
        a.exp[0] = () => Math.exp((a[0] + a[3]) / 2) * (a.delta * Math.cosh(a.delta / 2) + (a[0] - a[3]) * Math.sinh(a.delta / 2));
        a.exp_est = () => Math.exp((a[0] + a[3]) / 2) * Matrix2((1 + (a[0] - a[3]) / 2), a[1], a[2], (1 - (a[0] - a[3]) / 2));
    }
}

const l = new Lookup();


class Matrix3 extends Tuple {
    constructor() {
        super(...args.slice(0, 9));
        a.trace = () => (a[0] + a[3]);
        a.determinant = () => Math.abs(0
            + a[0] * a[4] * a[8] + a[1] * a[5] * a[6] + a[2] * a[3] * a[7]
            - a[2] * a[4] * a[6] - a[1] * a[3] * a[8] - a[0] * a[5] * a[7]);
        a.transpose = () => Matrix2(a[0], a[1], a[2], a[3]); //TODO
        a.adjoint = () => Matrix2(a[3], -a[2], -a[1], a[0]); //TODO
        a.negative = () => Matrix2(-a[0], -a[2], -a[1], -a[3]); //TODO
        a.inverse = () => Tuple.parse(a.determinant() * a.adjoint());
        //constant.call(window);
    }
}

class MatrixSquare extends Tuple {
    constructor() {
        super(...args.slice(0));
        var a = new Tuple([].concat.apply([], arguments));
        a.constructor = Matrix2;
        a.trace = () => [].reduce.call(a, (t,e,i) => t += i % (a.order + 1) ? 0 : e);
        a.minor = (y,z) => ((z = y / a.order | 0), (y = y % a.order), [].filter.call(a, (x,i,j) => ((j = i / a.order | 0), (i = i % a.order), i != y && j != z)));
        a.cofactor = (y,z) => ((z = y / a.order | 0), (y = y % a.order), [].filter.call(a, (x,i,j) => ((j = i / a.order | 0), (i = i % a.order), i != y && j != z)));
        a.determinant = () => [].reduce.call(a, (t,e,i) => i > a.order ? t : a.minor(i).times(i % 2 ? -e : e));
        //constant.call(window);
    }
}

function MatrixDiagonal() {
    a.exp = () => MatrixDiag(Math.pow(ê, a[0]), Math.pow(ê, a[3]));
}

function NaturalNumber(n) {
    var buffer = new ArrayBuffer(4);
    var natural = new Int32Array(buffer);
    natural.constructor = NaturalNumber;
    natural.set(n);
    return natural;
}

function IntegralNumber(n) {
    var buffer = new ArrayBuffer(4);
    var integer = new Uint32Array(buffer);
    integer.constructor = IntegralNumber;
    integer.set(n);
    return integer;
}

function RationalNumber(n, d) {
    var rational = ε.times(Number(n));
    infinitesimal.constructor = InfinitesimalNumber;
    return infinitesimal;
}

function ValentNumber() {
    var a = new Tuple([].concat.apply([], arguments));
    a.valueOf = () => ([].unshift.call(a, [].slice.call(a, -1)), a[0]);
    a.constructor = ValentNumber;
    return a;
}

function DuplexNumber(n, m) {
    var infinitesimal = ε.times(Number(n));
    infinitesimal.constructor = InfinitesimalNumber;
    return infinitesimal;
}

function ContinuedFractionNumber() {
    var rational = ε.times(Number(n));
    infinitesimal.constructor = InfinitesimalNumber;
    return infinitesimal;
}

function RealNumber(n) {
    var buffer = new ArrayBuffer(4);
    var real = new Float32Array(buffer);
    real.constructor = RealNumber;
    real.set(n);
    return real;
}

function ComplexNumber(n, m) {
    //nn = n; nm = mn = m; mm = -n;
    var imaginary = î.times(Number(n));
    imaginary.constructor = ImaginaryNumber;
    return imaginary;
}

function QuaternionNumber(n, m1, m2, m3) {
    var e = new Matrix2(1, 0, 0, 1);
    var i = new Matrix2(0, 1,-1, 0);
    var j = new Matrix2(0, 1,-1, 0);
    var k = new Matrix2(0, 1,-1, 0);
    var quaternion = new Tuple(n * e, m1 * i, m2 * j, m3 * k);
    //nn = n; nm = mn = m; mm = -n; m1m2 = -m2m1 = m3; m1m2m3 = -n;
    quaternion.constructor = QuaternionNumber;
    quaternion.conj = () => e - i - j - k;
    quaternion.scalar = (a) => (a + a.conj) / 2;
    quaternion.vector = (a) => (a - a.conj) / 2;
    quaternion.norm = (a) => sqrt(a * a.conj);
    quaternion.det = (a) => sqrt(a * a.conj);
    quaternion.plus = (a, b) => [
        a[0] + b[0],
        a[1] + b[1],
        a[2] + b[2],
        a[3] + b[3]
    ];
    quaternion.times = (a, b) => [
        a[0] * b[0] - a[1] * b[1] - a[2] * b[2] - a[3] * b[3],
        a[0] * b[1] + a[1] * b[0] + a[2] * b[3] - a[3] * b[2],
        a[0] * b[2] - a[1] * b[3] + a[2] * b[0] - a[3] * b[1],
        a[0] * b[3] + a[1] * b[2] - a[2] * b[1] + a[3] * b[0]
    ];
    quaternion.vector.dot = (a, b) => a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
    quaternion.vector.cross = (a, b) => (1) + (2) + (3);
    return quaternion;
}

function OctonionNumber(n, m1, m2, m3, m4, m5, m6, m7) {
    //nn = n; nm = mn = m; mm = -n; m3m4 = -m4m3 = m7
    //conjugate = n - m1 - m2 - m3 - m4 - m5 - m6 - m7;
    var octonion = î.times(Number(m1));
    octonion.constructor = OctonionNumber;
    return octonion;
}

function SedenionNumber(n, m1, m2, m3, m4, m5, m6, m7, m8, m9, mA, mB, mC, mD, mE, mF) {
    //nn = n; nm = mn = m; mm = -n; m7m8 = -m8m7 = mF; m1(m2m3) = -(m1m2)m3;
    var sedenion = î.times(Number(n));
    sedenion.constructor = SedenionNumber;
    return sedenion;
}

function PerplexNumber(n, m) {
    var unitesimal = ĵ.times(Number(n));
    unitesimal.constructor = UnitesimalNumber;
    return unitesimal;
}

function CoQuaternionNumber(n, m1, m2, m3) {
    //conjugate = w − xi − yj − zk
    var coquaternion = î.times(Number(n));
    coquaternion.constructor = CoQuaternionNumber;
    return coquaternion;
}



const Ω = 1/0;
const Ʊ = 0/0;
const î = new Matrix2(0, 1,-1, 0, 0.20787957635076190854695561983497877003387784163176960807); //imaginary, sqrt(-1)
const ĵ = new Matrix2(0, 1, 1, 0, 0.99999999999999994448884876874217297881841659545898437499); //unitesimal, sqrt(1)
const ε = new Matrix2(0, 1, 0, 0, 1.57172778470262867296192256111765622564011176170918094545e-162); //infinitesimal, sqrt(0)
const ω = new Matrix2(0, 1, Ω, 0, 1.79769313486231580793728971405303415079934132710037826936e+308); //infinite, sqrt(1/0)
const I = new Matrix2(1, 0, 0, 1, 1.0); //IdentityMatrix
const Ø = new Matrix2(0, 0, 0, 0, 0.0); //ZeroMatrix
const δ = 0.00000000000000011102230246251565404236316680908203125001; //Number.EPSILON/2 ~ 2^-53
const π = 3.14159265358979323846264338327950288419716939937510582097;
const ℮ = 2.71828182845904523536028747135266249775724709369995957496;
const φ = 1.61803398874989484820458683436563811772030917980576286213;
const γ = 0.57721566490153286060651209008240243104215933593992359880;

const ℕ = NaturalNumber;
const ℤ = IntegralNumber;
const ℚ = RationalNumber;
const ℝ = RealNumber;
const ℂ = ComplexNumber;
const ℍ = QuaternionNumber;
const ℙ = PerplexNumber;


const SQRT2 = new ValentNumber(
    1.41421356237309504880168872420969807856967187537694807317,
    1.41421356237309481240771447119186632335186004638671875000);

const SQRT3 = new ValentNumber(
    1.73205080756887729352744634150587236694280525381038062805,
    1.73205080756887724868775535469467286020517349243164062501);

const SQRT5 = new ValentNumber(
    2.23606797749978969640917366873127623544061835961152572427,
    2.23606797749978956912908500953562906943261623382568359375);

const SQRT7 = new ValentNumber(
    2.64575131106459059050161575363926042571025918308245018036,
    2.64575131106459043861534041752747725695371627807617187501);

const SQRT11 = new ValentNumber(
    3.31662479035539984911493273667068668392708854558935359705,
    3.31662479035539984911493273667068668392708854558935359704);

const SQRT13 = new ValentNumber(
    3.60555127546398929311922126747049594625129657384524621271,
    3.60555127546398929311922126747049594625129657384524621270);

const SQRT17 = new ValentNumber(
    4.12310562561766054982140985597407702514719922537362043439,
    4.12310562561766054982140985597407702514719922537362043438);

const SQRT19 = new ValentNumber(
    4.35889894354067355223698198385961565913700392523244493689,
    4.35889894354067355223698198385961565913700392523244493688);

const RCPR3 = new ValentNumber(
    0.33333333333333333333333333333333333333333333333333333333,
    0.33333333333333333333333333333333333333333333333333333333,
    0.33333333333333333333333333333333333333333333333333333334);

const RCPR7 = new ValentNumber(
    0.14285714285714285714285714285714285714285714285714285715,
    0.14285714285714285814285714285714285714285714285714285714,
    0.14285714285714285914285714285714285714285714285714285715);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment