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];
}
}
});
@nullhook
Copy link

nullhook commented Dec 24, 2020

This looks clever. Can valueOf differ per operator and can it handle constants?

@pyrocto
Copy link
Author

pyrocto commented Dec 24, 2020

This looks clever.

Thanks!

Can valueOf differ per operator

No, it's the same for all objects (see line 122). The only difference is how the context (Complex, Dual, Poly, etc.) processes the result from valueOf().

and can it handle constants?

It can, but you have to think of them as needing an explicit cast. For example, you have to represent the complex number 2 as {r:2, i:0}, so you might want to write a function like let cast=x=>({r:x, i:0}) to do the cast from reals to the type the context expects.

@dy
Copy link

dy commented Aug 24, 2021

That proposal has a lot of potential.
From what I can see:

  • use BigInt to allow any-precision ops
  • pipe operator a | b | c
  • units with (units) { 10em + 10px }

@pyrocto
Copy link
Author

pyrocto commented Aug 24, 2021

It would be straightforward to wrap BigInts to do fixed-point arithmetic, but for irrational numbers, you'd probably want a stream-based implementation. This gist could help you keep the client code cleaner.

I suppose it might be possible to overload pipe if you generate random numbers in the range [0, 2^32) rather than in [0, 1). If you used the latter, the random numbers would all be coerced to 0.

For computing with units, the symbols have to be valid identifiers, so you'd either have to put the unit before the quantity:
with (units) { em10 + px10 }
or prefix the quantity with some symbol like $ or _ or something :
with (units) { $10em + $10px }

But all this is a big joke; the real tc39 operator overloading proposal can be found here.

@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