Value = {
Number.prototype.add = function (n) {
return this + n;
};
Number.prototype.sub = function (n) {
return this - n;
};
Number.prototype.mul = function (n) {
return this * n;
};
Number.prototype.pow = function (n) {
return this ** n;
};
Number.prototype.div = function (n) {
return this / n;
};
Number.prototype.mod = function (n) {
return this % n;
};
Number.prototype.powmod = function (n, m) {
return Value.Natural.from(this).powmod(n, m);
};
Number.prototype.minv = function (m) {
return Integer(this).modInv(m);
};
// comparison
// TODO define in terms of a single primitive, e.g. lt?
// define cmp as ternary compare?
// define lte, gte composites?
Number.prototype.lt = function (n) {
return this < n;
};
Number.prototype.eq = function (n) {
return this == n;
};
Number.prototype.gt = function (n) {
return this > n;
};
// unary
Number.prototype.floor = function () {
return Math.floor(this);
};
Number.prototype.ceil = function () {
return Math.ceil(this);
};
Number.prototype.round = function () {
return Math.round(this);
};
Number.prototype.trunc = function () {
return Math.trunc(this);
};
// TODO ast forms representing the underlying operation
Number.prototype.Power = (n) => {
return new Value.Power({ base: this, power: n });
};
// Number.prototype.PowerMod(n,m)
// Array.prototype.toTex = Array.prototype.toString
Boolean.prototype.toTex = function () {
return this ? "⊤" : "⊥";
};
Number.prototype.toTex = Number.prototype.toString;
String.prototype.toTex = String.prototype.toString;
Object.prototype.show = function () {
return tex`${this.toTex ? this.toTex() : String(this)}`;
};
const Value = class extends Number {
constructor(input) {
super(input);
this.input = input;
}
};
const _BigInteger = await require("big-integer@1.6.43/BigInteger");
const Integer = _BigInteger;
Value.Integer = Integer;
Integer.from = (n) => new Integer(n);
Integer.is = (n) => n instanceof Integer || Number.isInteger(n);
// Integer.prototype.neg = Integer.prototype.negate
// Integer.prototype.sub = Integer.prototype.subtract
// Integer.prototype.mul = Integer.prototype.multiply
// Integer.prototype.div = Integer.prototype.divide;
Integer.prototype.toTex = function () {
return String(this);
};
Integer.prototype.show = Object.prototype.show;
const binomial = (n, k) => {
// use shorter side for loop
k = n - k < k ? n - k : k;
// use add with array for avoiding overflow by mul n*(n-1)
const a = Array.from({ length: k + 1 }).fill(Integer.zero);
a[0] = Integer.one;
for (let i = 0; i < n; i++) {
for (let j = k; j >= 1; j--) a[j] = a[j].add(a[j - 1]);
}
return a[k] || Integer.zero;
};
Integer.binomial = (n, k) => {
if (n < 0) return (-1) ** k * binomial(n + k - 1, k);
if (n >= 0) return binomial(n, k);
};
Value.IntegerList = class IntegerList extends Array {
constructor(...args) {
super(...args.map((arg) => new Integer(arg)));
}
static of(...args) {
return new IntegerList(...args);
}
static is(a) {
if (!a) return false;
if (a instanceof IntegerList) return true;
try {
return a.every((n) => Integer.from(n));
} catch (err) {
return false;
}
}
static zeros(length) {
return IntegerList.from({ length }).fill(Integer.zero);
}
static ones(length) {
return IntegerList.from({ length }).fill(Integer.one);
}
neg() {
return this.map((a) => a.negate());
}
abs() {
return this.map((a) => a.abs());
}
sign() {
return this.map((a) => a.sign());
}
_thread(op, b) {
// if (typeof this[op] !== 'function') throw new TypeError('operation not available: ' + op)
// thread operation component-wise over list
if (Array.isArray(b)) {
return this.map((a, i) => a[op](b[i] || Integer.zero));
}
return this.map((a, i) => a[op](b));
}
add(b) {
return this._thread("add", b);
}
sub(b) {
return this._thread("subtract", b);
}
mul(b) {
return this._thread("multiply", b);
}
div(b) {
return this._thread("divide", b);
}
mod(b) {
return this._thread("mod", b);
}
pow(b) {
return this._thread("pow", b);
}
modPow(b) {
return this._thread("modPow", b);
}
minv(b) {
return this._thread("minv", b);
}
sum() {
return this.reduce((a, b) => a.add(b), Integer.zero);
}
// join () {
// return '[' + Array.prototype.join.call(this, '+') + ']'
// }
prod() {
return this.reduce((a, b) => a.multiply(b), Integer.one);
}
gcd() {
return this.reduce((a, b) => Integer.gcd(a, b));
}
// could be: this.prod().div(this.gcd())
lcm() {
return this.reduce((a, b) => Integer.lcm(a, b));
}
min() {
return this.reduce((a, b) => Integer.min(a, b));
}
max() {
return this.reduce((a, b) => Integer.max(a, b));
}
toTex() {
return `[${this.map((a) => a.toTex()).join(",")}]`;
}
};
// Quantity: how "many" of something...
// like a discrete magnitude, absolute value without respect to direction
// e.g. "unsigned" types
// thus "Cardinal" should just be for infinite cardinals
// all finite cardinals are 1-1 with finite ordinals... and naturals (including 0?)...
// all infinite cardinals are limit ordinals (but 1-1? LimitOrdinals more fine-grained?)...
Value.Quantity = class Quantity extends Value {
constructor(count) {
if (count instanceof Quantity) return count;
let value = count;
// coerce to int
try {
value = Integer(count);
count = value;
} catch (err) {
count = Integer(0);
}
super(value);
// assert value is non-negative
if (value.sign) throw new TypeError("c ∉ ℕ");
// TODO rework to track approx vs. exact values...
// alternatively, track count (integral part) separate from unit fraction?
this.count = count;
this.value = value;
}
// get [Symbol.toStringTag()] {
// // TODO
// }
};
Value.Cardinal = class Cardinal extends Value.Quantity {
constructor({ index = 0, finite = false } = {}) {
super((index + 1 - finite) / finite);
this.index = index;
this.finite = finite;
}
get [Symbol.toStringTag]() {
return "κ ∈ ℵ";
}
static from(index) {
const finite = Number.isInteger(index);
if (!finite) index = 0;
return new Cardinal({ finite: false });
}
// aleph-naught
static zero() {
return new Cardinal({ finite: false });
}
toString() {
return this.finite ? `|{${this.index}}|` : `|∞|`;
}
get tex() {
return this.finite
? tex`|\mathbf{${this.index}}|`
: tex`ℵ_{${this.index}}`;
}
};
Value.Ordinal = class Ordinal extends Value.Cardinal {};
Value.Natural = class Natural extends Value {
constructor(input = 0) {
if (input instanceof Natural) return input;
// coerce to int
const value = Integer(input);
super(value);
this.input = input;
this.value = value;
// assert value is non-negative
if (value.sign) throw new TypeError("n ∉ ℕ");
}
static is(n) {
try {
return n.gt(0) || n.eq(0);
} catch (err) {
return false;
}
}
static from(n) {
return new Natural(n);
}
add(n) {
return this.value.add(n);
}
sub(n) {
return this.value.subtract(n);
}
mul(n) {
return this.value.multiply(n);
}
pow(n) {
return this.value.pow(n);
}
div(n) {
return this.value.divide(n);
}
// reduce value modulo m
mod(n) {
return this.value.mod(n);
}
minv(n) {
return this.value.modInv(n);
}
// power n, reduced modulo m
// TODO reduce to pow in class representing integers mod m
// ... so mod(m).pow(n) would yield IntegersMod(m).get(...).pow(n), or some such...
// TODO refine to allow fractional powers (at least for factorized modulus?)
powmod(n, m) {
let v = this.value;
if (n < 0) {
v = v.modInv(m);
n = -n;
}
return v.modPow(n, m);
}
// comparison
lt(n) {
return this.value.lt(n);
}
eq(n) {
return this.value.eq(n);
}
gt(n) {
return this.value.gt(n);
}
// unary
floor() {
return this;
}
ceil() {
return this;
}
round() {
return this;
}
trunc() {
return this;
}
// async factorization () {
// // TODO find prime factorization
// return this._factorMap
// }
// TODO: partialFactorization
// keep track of aspects of integral multiples, powers, and which bases are known prime
// extend algebraic operations to propagate this information
// factorization as "bag" vs. radical as "set"...
// totient identities apply -- make general statements applying to both...
// relational algebra makes similar distinction:
// http://infolab.stanford.edu/~ullman/fcdb/aut07/slides/ra.pdf
// set union is idempotent: S ∪ S = S
// bag union is idempotent: B ∪ B ≠ B does not hold in general...
// if x appears n times in S, then it appears 2n times in S ∪ S
// {1} ∪ {1} = {1,1} ≠ {1}
// B1 ∪ B2 = B2 ∪ B1
// commutivity of bag union holds as it does for set union
// B1 ∪ B2 = B1 ∩ B2 holds iff bags are coprime (no bases in common)
// bag intersection/union have min/max relationships for multiplicity of factors
};
Value.Factor = class Factor extends Value.Natural {
constructor(base, multiplicity = 1) {
base = Value.Natural.from(base);
multiplicity = Value.Natural.from(multiplicity);
if (multiplicity.eq(1)) return base; // b**1 = b for all b
if (base.eq(1)) return base; // 1**p = 1 for all p
if (multiplicity.eq(0)) return Value.Natural.from(1); // b**0 = 1 for all b
if (base.eq(0)) return Value.Natural.from(0); // 0**p = 0 for all p ≠ 0
// TODO indeterminate form?
super(base.pow(multiplicity));
}
};
Value.Power = class extends Value.Factor {};
// arithmetic on prime factor exponents omits the concept of addition?
// or rather, it's a different ring structure entirely
// addition --> integer multiplication, p_i^(e_i+x_i) = p_i^e_i*p_i^x_i
// outer addition less algebraically coherent: (p_i^e_i)+x_i
// can be interpreted as componentwise affine shifts
// addition on the bottom meaningful via binomial expansion: (p_i+x_i)^e_i
// multiplication --> integer exponentiation: p_i^(e_i*x_i) = (p_i^e_i)^x_i
// mutiplication on the bottom transforms like pure product: (p_i x_i)^e_i = p_i^e_i x_i^e_i
// outside mutiplication looks like coefficients: (p_i^e_i) x_i
// only algebraically relevant when x_i has relation with p_i or powers with relation with e_i
// thus, exponentiation --> (one level of) integer tetration?
// adjacent exponentiation ambiguous: p_i^e_i^x_i -- usually interpeted as p_i^(e_i^x_i)
// exponentiation on the top the exponent vector: p_i^(e_i^x_i)
// though "prefix" exponentiation is a common notation for tetration: ^x_i p_i^e_i
// outside exponentiation transforms to multiplication: (p_i^e_i)^x_i = p_i^(e_i x_i)
// bottom exponentiation identical (up to commutativity of [e,x]): (p_i^x_i)^e_i = p_i^(x_i e_i)
// also, without -1, it'd be a rig (ring w/o negation) like ℕ...
// but 1 is additive identity, not multiplicative...
// is there a notion of `e`, which would be the multiplicative identity?
// adding -1 generates ℚ?
// is there a sensible notion of "successor" without integer addition?
// TODO extends FactoredNatural, restricted to a single prime factor, with multiplicity 1
Value.Prime = class Prime extends Value.Factor {
// TODO this should probably extend prime power object, since it specializes
// a prime power is just a "degenerate" factor/exponent bag with a single prime factor element
constructor(base, multiplicity = 1) {
if (base instanceof Prime) return base;
super(base, multiplicity);
// assert primality (probable)
if (!Prime.is(this.value)) throw new TypeError("p ∉ ℙ");
}
static from(n) {
return new Prime(n);
}
static is(n) {
return (typeof n.isPrime === "function" && n.isPrime()) || false;
}
fromIndex(n) {
// TODO iterate over primes gen for nth prime
// e.g. primes().get(n)
}
// // TODO specialize pow function for primes to generate prime powers?
// Jordan totient function, a generalization of Euler totient (which is just k=1)
// https://en.wikipedia.org/wiki/Jordan%27s_totient_function
totient(k = 1) {
if (k.eq(1)) {
if (this.multiplicity.eq(1)) return this.sub(1);
}
}
carmichael() {
if (this.multiplicity.eq(1)) {
return this.totient();
}
// TODO
}
};
// TODO detecting perfect powers
// consider all possible values for k across each of the divisors of n, up to k ≤ log_2(n)
// can be simplified by only testing prime values of k
// since minimal value of k must necessarily be prime
// if full factorization of n is known, e.g. n = ∏ p_i^{α_i}
// then n is perfect power iff gcd(α) > 1 -- that is, exponent vector has common factor
// gcd represents the maximal power for which n is a perfect power
// n is also a perfect power to power dividing
// TODO call this PrimaryNumber, a la abstract algebra, e.g. "primary" ideals?
Value.Prime.Power = class PrimePower extends Value.Power {
constructor(prime, power = 1) {
prime = Value.Prime.from(prime);
power = Value.Natural.from(power);
// prime^1 just prime
if (power.eq(1)) return prime;
const value = prime.pow(power);
super(value);
this.prime = prime;
this.power = power;
}
// // specialize to return prime power
// pow (n) {
// return new PrimePower(this.prime, this.power.add(n))
// }
// TODO MultiplicativeGroup... gets extended to GF(n)
};
// represents a value, fully factored into primes
Value.Factorization = class Factorization extends Value.Quantity {
constructor(factors) {
super(factors.prod());
this.factors = factors;
}
rad() {
return new Factorization(this.factors.rad());
}
// TODO define padic valuation, padic norms, etc...
// TODO
get MultiplicativeGroup() {
// NB definitely memoize multiplicative group
// also need to memoize factorized natural
// const value = this.factors.reduce...
// Object.defineProperty(this, 'MultiplicativeGroup', { value })
// return value
}
// Jordan totient function, a generalization of Euler totient (which is just k=1)
// https://en.wikipedia.org/wiki/Jordan%27s_totient_function
totient(k = 1) {
if (k.eq(1)) {
if (this.multiplicity.eq(1)) return this.sub(1);
}
// TODO exact integer arithmetic instead? just sum over divisors instead
let n = this.value.pow(k);
for (let p of this.rad().factors) {
n *= 1 - p ** -k;
}
return n;
}
// TODO return positive sqrt of k mod each prime power of factorization, if one exists
// for now, just support primes 3 (mod 4)
// NB can use LegendreSymbol
sqrt(k) {
if (this.multiplicity.eq(1)) throw new Error("NYI for ℙ^n | n>1");
const p = this.value;
if (p.gcd(k).gt(0)) return Integer(0);
const mod4 = p.mod(4);
if (mod4.eq(3)) {
// r = ±k^((p+1)/4)
return k.modPow(p.add(1).divide(4), p);
}
}
};
// nominal number: symbolic... no order of magnitude implied
// TODO Tuple, List (∞-dimensional tuple)
// Vector, VectorTranspose, Matrix, SymmetricMatrix, etc...
// start with matrix, then define vector via refinement...
// essentially, just a redefinition of ops when certain invariants are satisfied
// uses more optimized version of ops rather than fully generic version
// same approach makes sense for scalars... starting from Complex?
// rather than outright replacing ops, use a dispatch strategy
// a vector space has a base field
// all elements of a vector are of the same type, ops form a field...
// a module over a ring generalizes the notion of a vector space over a field
// scalars are the elements of the given ring (with identity)
// a multiplication (on the left and/or on the right) is defined
// between elements of the ring and elements of the module
// 𝑛×1 matrix: column vector
// 1×𝑛 matrix: row vector
/*
from https://math.stackexchange.com/questions/748010/difference-between-tuple-and-row-matrix
tuple space: ℝ^𝑛 := ℝ×⋯×ℝ -- numbers arranged in columns
row space: ℝ^{1,𝑛} -- defined as the space of "numbers arranged in a row"
difference emphasized in notation using different brackets: () vs. [], respectively
suggestive of relational algebra... each ℝ represents a "column"
both operands to a given relational operation must have same "relation schema"
*/
Value.Bag = class Bag extends Map {
set(k, v) {
if (!Number.isInteger(v))
throw new TypeError("Entry values must be integer");
return super.set(k, (this.get(k) || 0).add(v));
}
factors() {
return new Set([...this.keys()].sort());
}
};
// Value.FactorBag = class extends Bag {
// }
return Value;
}