monogate 0.1.0

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/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # monogate
2
+
3
+ > A single binary operator that generates all elementary functions.
4
+
5
+ ```
6
+ eml(x, y) = exp(x) − ln(y)
7
+ ```
8
+
9
+ From this one operator and the constant `1`, every elementary arithmetic function can be constructed as a pure expression tree. Implementation of:
10
+
11
+ > **"All elementary functions from a single operator"**
12
+ > Andrzej Odrzywołek, Jagiellonian University
13
+ > [arXiv:2603.21852v2](https://arxiv.org/abs/2603.21852) · CC BY 4.0
14
+
15
+ Live explorer: **https://monogate.dev** (or your deployed URL)
16
+
17
+ ---
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ npm install monogate
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ```js
28
+ import { op, exp, ln, add, mul, pow, E, ZERO } from "monogate";
29
+
30
+ // The core operator
31
+ op(1, 1); // e (exp(1) − ln(1))
32
+ op(1, op(op(1,1), 1)); // 0 (e − e = 0)
33
+
34
+ // Derived functions — all built from eml + 1
35
+ exp(3); // e³
36
+ ln(Math.E); // 1
37
+ add(2, 3); // 5
38
+ mul(4, 5); // 20
39
+ pow(2, 10); // 1024
40
+ ```
41
+
42
+ ## API
43
+
44
+ All functions are pure and stateless. Domain constraints are noted — violating them produces `NaN` or `±Infinity`.
45
+
46
+ ### Core
47
+
48
+ | Export | Formula | Nodes | Depth |
49
+ |--------|---------|-------|-------|
50
+ | `op(x, y)` | `exp(x) − ln(y)` | — | — |
51
+ | `E` | `op(1,1)` | 1 | 1 |
52
+ | `ZERO` | `op(1, op(op(1,1), 1))` | 3 | 3 |
53
+ | `NEG_ONE` | `op(ZERO, op(2,1))` | 5 | 4 |
54
+
55
+ ### Elementary functions
56
+
57
+ | Export | Math | Domain | Nodes | Depth |
58
+ |--------|------|--------|-------|-------|
59
+ | `exp(x)` | eˣ | ℝ | 1 | 1 |
60
+ | `ln(x)` | ln x | x > 0 | 3 | 3 |
61
+ | `sub(x, y)` | x − y | x > 0 | 5 | 4 |
62
+ | `neg(y)` | −y | ℝ (two-regime) | 9 | 5 |
63
+ | `add(x, y)` | x + y | ℝ | 11 | 6 |
64
+ | `mul(x, y)` | x × y | x,y > 0 | 13 | 7 |
65
+ | `div(x, y)` | x / y | x,y > 0 | 15 | 8 |
66
+ | `pow(x, n)` | xⁿ | x > 0 | 15 | 8 |
67
+ | `recip(x)` | 1/x | x > 0 | 5 | 4 |
68
+
69
+ The **depth** ranking of elementary functions by EML tree depth is new to mathematics.
70
+
71
+ ### `IDENTITIES`
72
+
73
+ An array of `{ name, emlForm, nodes, depth, status }` records — useful for building explorers or documentation.
74
+
75
+ ## Open challenges
76
+
77
+ These functions have no known EML construction:
78
+
79
+ - **sin x** — no construction found
80
+ - **cos x** — no construction found
81
+ - **π** — no construction as a closed EML expression
82
+ - **i** (√−1) — requires extending the domain
83
+
84
+ Pull requests welcome. If you crack one, open an issue — it's publishable.
85
+
86
+ ## How it works
87
+
88
+ The grammar is just two production rules:
89
+
90
+ ```
91
+ S → 1
92
+ S → eml(S, S)
93
+ ```
94
+
95
+ Any expression built from this grammar computes some value. The paper proves that the specific compositions above equal the named functions exactly (not approximately). Floating-point errors are at machine epsilon (`< 1e-13`).
96
+
97
+ The `neg` function uses a two-regime construction to avoid overflow:
98
+ - **y ≤ 0**: tower formula via `exp(eʸ)` — stable because `eʸ ≤ 1`
99
+ - **y > 0**: shift formula — computes `exp(y+1)` instead of a tower
100
+
101
+ ## License
102
+
103
+ MIT — see [LICENSE](./LICENSE). The underlying mathematics is CC BY 4.0 per the original paper.
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "monogate",
3
+ "version": "0.1.0",
4
+ "description": "A single binary operator eml(x,y) = exp(x) − ln(y) that generates all elementary functions. Implementation of arXiv:2603.21852 (Odrzywołek, 2026).",
5
+ "type": "module",
6
+ "main": "./src/index.js",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./src/index.js"
10
+ }
11
+ },
12
+ "files": [
13
+ "src",
14
+ "README.md"
15
+ ],
16
+ "keywords": [
17
+ "mathematics",
18
+ "eml",
19
+ "elementary-functions",
20
+ "algebra",
21
+ "monogate",
22
+ "exp-minus-log"
23
+ ],
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/your-username/monogate.git"
28
+ },
29
+ "homepage": "https://monogate.dev",
30
+ "bugs": {
31
+ "url": "https://github.com/your-username/monogate/issues"
32
+ },
33
+ "engines": {
34
+ "node": ">=16"
35
+ },
36
+ "devDependencies": {
37
+ "vitest": "^1.0.0"
38
+ },
39
+ "scripts": {
40
+ "test": "vitest run"
41
+ }
42
+ }
package/src/complex.js ADDED
@@ -0,0 +1,212 @@
1
+ /**
2
+ * complex.js — Immutable complex number type for the monogate EML system.
3
+ *
4
+ * This file provides standard ℂ arithmetic. It does NOT use EML grammar
5
+ * constructions — it is the numeric substrate for complex_eml.js.
6
+ *
7
+ * All transcendental functions use the PRINCIPAL BRANCH of ln:
8
+ * arg(z) ∈ (−π, π] (branch cut on the negative real axis)
9
+ *
10
+ * Precision: results accurate to machine epsilon (~1e-15) away from branch
11
+ * cuts. Near the negative real axis (|Im| < 1e-12, Re < 0) arg() is subject
12
+ * to the sign-of-zero ambiguity in IEEE 754 — this is the source of the
13
+ * floating-point ±iπ sign in the EML escape-from-reals chain.
14
+ *
15
+ * @module monogate/complex
16
+ */
17
+
18
+ export class Complex {
19
+ /**
20
+ * @param {number} re real part
21
+ * @param {number} im imaginary part (default 0)
22
+ */
23
+ constructor(re, im = 0) {
24
+ this.re = re;
25
+ this.im = im;
26
+ Object.freeze(this);
27
+ }
28
+
29
+ // ─── Factories ──────────────────────────────────────────────────────────────
30
+
31
+ /** @param {number} re @param {number} [im=0] @returns {Complex} */
32
+ static of(re, im = 0) { return new Complex(re, im); }
33
+
34
+ /**
35
+ * r · exp(iθ)
36
+ * @param {number} r magnitude (must be ≥ 0)
37
+ * @param {number} theta argument in radians
38
+ * @returns {Complex}
39
+ */
40
+ static fromPolar(r, theta) {
41
+ return new Complex(r * Math.cos(theta), r * Math.sin(theta));
42
+ }
43
+
44
+ /** @param {number} x @returns {Complex} */
45
+ static real(x) { return new Complex(x, 0); }
46
+
47
+ // ─── Arithmetic ─────────────────────────────────────────────────────────────
48
+
49
+ /** @param {Complex} other @returns {Complex} */
50
+ add(other) {
51
+ return new Complex(this.re + other.re, this.im + other.im);
52
+ }
53
+
54
+ /** @param {Complex} other @returns {Complex} */
55
+ sub(other) {
56
+ return new Complex(this.re - other.re, this.im - other.im);
57
+ }
58
+
59
+ /**
60
+ * (a + bi)(c + di) = (ac − bd) + (ad + bc)i
61
+ * @param {Complex} other @returns {Complex}
62
+ */
63
+ mul(other) {
64
+ return new Complex(
65
+ this.re * other.re - this.im * other.im,
66
+ this.re * other.im + this.im * other.re,
67
+ );
68
+ }
69
+
70
+ /**
71
+ * Division via conjugate: this / other = (this · conj(other)) / |other|²
72
+ * @param {Complex} other @returns {Complex}
73
+ */
74
+ div(other) {
75
+ const d = other.absSquared();
76
+ return new Complex(
77
+ (this.re * other.re + this.im * other.im) / d,
78
+ (this.im * other.re - this.re * other.im) / d,
79
+ );
80
+ }
81
+
82
+ /** Additive inverse. @returns {Complex} */
83
+ neg() { return new Complex(-this.re, -this.im); }
84
+
85
+ /** Complex conjugate. @returns {Complex} */
86
+ conj() { return new Complex(this.re, -this.im); }
87
+
88
+ /**
89
+ * Multiplicative inverse: 1/z = conj(z) / |z|²
90
+ * @returns {Complex}
91
+ */
92
+ recip() {
93
+ const d = this.absSquared();
94
+ return new Complex(this.re / d, -this.im / d);
95
+ }
96
+
97
+ /**
98
+ * Scale by a real scalar.
99
+ * @param {number} r @returns {Complex}
100
+ */
101
+ scale(r) { return new Complex(this.re * r, this.im * r); }
102
+
103
+ // ─── Transcendental (principal branch) ──────────────────────────────────────
104
+
105
+ /**
106
+ * exp(z) = e^re · (cos(im) + i·sin(im))
107
+ * @returns {Complex}
108
+ */
109
+ exp() {
110
+ return Complex.fromPolar(Math.exp(this.re), this.im);
111
+ }
112
+
113
+ /**
114
+ * Principal branch of ln(z): ln|z| + i·arg(z), arg ∈ (−π, π].
115
+ * Branch cut: negative real axis.
116
+ * @returns {Complex}
117
+ * @throws {RangeError} if z = 0
118
+ */
119
+ ln() {
120
+ if (this.isZero()) throw new RangeError("ln_c: argument must be non-zero");
121
+ return new Complex(Math.log(this.abs()), this.arg());
122
+ }
123
+
124
+ /**
125
+ * Principal-value power: z^w = exp(w · ln(z))
126
+ * @param {Complex} w @returns {Complex}
127
+ */
128
+ pow(w) {
129
+ return this.ln().mul(w).exp();
130
+ }
131
+
132
+ // ─── Measurement ────────────────────────────────────────────────────────────
133
+
134
+ /**
135
+ * |z| = hypot(re, im) — uses Math.hypot to avoid overflow on large components.
136
+ * @returns {number}
137
+ */
138
+ abs() { return Math.hypot(this.re, this.im); }
139
+
140
+ /**
141
+ * arg(z) = atan2(im, re) ∈ (−π, π]
142
+ * @returns {number}
143
+ */
144
+ arg() { return Math.atan2(this.im, this.re); }
145
+
146
+ /** |z|² = re² + im² (cheaper than abs() when only comparing magnitudes). @returns {number} */
147
+ absSquared() { return this.re * this.re + this.im * this.im; }
148
+
149
+ // ─── Predicates ─────────────────────────────────────────────────────────────
150
+
151
+ /** @param {number} [tol=0] @returns {boolean} */
152
+ isZero(tol = 0) { return this.abs() <= tol; }
153
+
154
+ /** @param {number} [tol=0] @returns {boolean} */
155
+ isReal(tol = 0) { return Math.abs(this.im) <= tol; }
156
+
157
+ /** @param {number} [tol=0] @returns {boolean} */
158
+ isPureImaginary(tol = 0) {
159
+ return Math.abs(this.re) <= tol && !this.isZero(tol);
160
+ }
161
+
162
+ // ─── Equality ───────────────────────────────────────────────────────────────
163
+
164
+ /**
165
+ * Component-wise approximate equality.
166
+ * @param {Complex} other
167
+ * @param {number} [tol=1e-10]
168
+ * @returns {boolean}
169
+ */
170
+ equals(other, tol = 1e-10) {
171
+ return Math.abs(this.re - other.re) <= tol && Math.abs(this.im - other.im) <= tol;
172
+ }
173
+
174
+ // ─── Output ─────────────────────────────────────────────────────────────────
175
+
176
+ /**
177
+ * Human-readable form. Treats components with |value| < 1e-12 as zero.
178
+ * Examples: "3", "-2i", "1+4i", "2.718-3.14i"
179
+ * @returns {string}
180
+ */
181
+ toString() {
182
+ const fmt = (v) => {
183
+ const s = parseFloat(v.toPrecision(10)).toString();
184
+ return s;
185
+ };
186
+ const EPS = 1e-12;
187
+ const reZero = Math.abs(this.re) < EPS;
188
+ const imZero = Math.abs(this.im) < EPS;
189
+
190
+ if (reZero && imZero) return "0";
191
+ if (imZero) return fmt(this.re);
192
+ if (reZero) {
193
+ if (Math.abs(this.im - 1) < EPS) return "i";
194
+ if (Math.abs(this.im + 1) < EPS) return "-i";
195
+ return `${fmt(this.im)}i`;
196
+ }
197
+ const sign = this.im < 0 ? "-" : "+";
198
+ const absIm = Math.abs(this.im);
199
+ const imStr = Math.abs(absIm - 1) < EPS ? "i" : `${fmt(absIm)}i`;
200
+ return `${fmt(this.re)}${sign}${imStr}`;
201
+ }
202
+
203
+ /**
204
+ * Fixed-decimal form for test output.
205
+ * @param {number} digits @returns {string}
206
+ */
207
+ toFixed(digits) {
208
+ return `${this.re.toFixed(digits)} + ${this.im.toFixed(digits)}i`;
209
+ }
210
+ }
211
+
212
+ export default Complex;
@@ -0,0 +1,206 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { Complex } from "./complex.js";
3
+
4
+ const TIGHT = 1e-14; // direct arithmetic
5
+ const TOL = 1e-10; // EML chains
6
+
7
+ const near = (a, b, tol = TIGHT) => Math.abs(a - b) <= tol;
8
+
9
+ describe("Complex — factories", () => {
10
+ it("of(re, im)", () => {
11
+ const z = Complex.of(3, -4);
12
+ expect(z.re).toBe(3);
13
+ expect(z.im).toBe(-4);
14
+ });
15
+
16
+ it("of(re) defaults im to 0", () => {
17
+ expect(Complex.of(5).im).toBe(0);
18
+ });
19
+
20
+ it("fromPolar(r, theta)", () => {
21
+ const z = Complex.fromPolar(2, Math.PI / 2);
22
+ expect(near(z.re, 0)).toBe(true);
23
+ expect(near(z.im, 2)).toBe(true);
24
+ });
25
+
26
+ it("real(x) alias", () => {
27
+ const z = Complex.real(7);
28
+ expect(z.re).toBe(7);
29
+ expect(z.im).toBe(0);
30
+ });
31
+
32
+ it("is frozen / immutable", () => {
33
+ const z = Complex.of(1, 2);
34
+ expect(() => { z.re = 99; }).toThrow();
35
+ });
36
+ });
37
+
38
+ describe("Complex — arithmetic", () => {
39
+ const a = Complex.of(3, 4);
40
+ const b = Complex.of(1, -2);
41
+
42
+ it("add", () => {
43
+ const r = a.add(b);
44
+ expect(r.re).toBe(4);
45
+ expect(r.im).toBe(2);
46
+ });
47
+
48
+ it("sub", () => {
49
+ const r = a.sub(b);
50
+ expect(r.re).toBe(2);
51
+ expect(r.im).toBe(6);
52
+ });
53
+
54
+ it("mul: (3+4i)(1-2i) = 11-2i", () => {
55
+ const r = a.mul(b);
56
+ expect(r.re).toBe(11);
57
+ expect(r.im).toBe(-2);
58
+ });
59
+
60
+ it("div: (3+4i)/(1-2i) = -1+2i", () => {
61
+ const r = a.div(b);
62
+ expect(near(r.re, -1)).toBe(true);
63
+ expect(near(r.im, 2)).toBe(true);
64
+ });
65
+
66
+ it("neg", () => {
67
+ expect(a.neg().re).toBe(-3);
68
+ expect(a.neg().im).toBe(-4);
69
+ });
70
+
71
+ it("conj", () => {
72
+ expect(a.conj().re).toBe(3);
73
+ expect(a.conj().im).toBe(-4);
74
+ });
75
+
76
+ it("recip: 1/(3+4i) = 3/25 - 4/25·i", () => {
77
+ const r = a.recip();
78
+ expect(near(r.re, 3/25)).toBe(true);
79
+ expect(near(r.im, -4/25)).toBe(true);
80
+ });
81
+
82
+ it("scale", () => {
83
+ const r = a.scale(2);
84
+ expect(r.re).toBe(6);
85
+ expect(r.im).toBe(8);
86
+ });
87
+
88
+ it("z · z.recip() ≈ 1", () => {
89
+ const r = a.mul(a.recip());
90
+ expect(near(r.re, 1)).toBe(true);
91
+ expect(near(r.im, 0)).toBe(true);
92
+ });
93
+ });
94
+
95
+ describe("Complex — measurement", () => {
96
+ it("abs of 3+4i = 5", () => {
97
+ expect(near(Complex.of(3, 4).abs(), 5)).toBe(true);
98
+ });
99
+
100
+ it("arg of i = π/2", () => {
101
+ expect(near(Complex.of(0, 1).arg(), Math.PI / 2)).toBe(true);
102
+ });
103
+
104
+ it("arg of −1 = π", () => {
105
+ expect(near(Complex.of(-1, 0).arg(), Math.PI)).toBe(true);
106
+ });
107
+
108
+ it("absSquared", () => {
109
+ expect(Complex.of(3, 4).absSquared()).toBe(25);
110
+ });
111
+ });
112
+
113
+ describe("Complex — transcendental", () => {
114
+ it("exp(0) = 1", () => {
115
+ const r = Complex.of(0).exp();
116
+ expect(near(r.re, 1)).toBe(true);
117
+ expect(near(r.im, 0)).toBe(true);
118
+ });
119
+
120
+ it("exp(1) = e", () => {
121
+ expect(near(Complex.of(1).exp().re, Math.E)).toBe(true);
122
+ });
123
+
124
+ it("exp(iπ) = −1 (Euler's identity)", () => {
125
+ const r = Complex.of(0, Math.PI).exp();
126
+ expect(near(r.re, -1)).toBe(true);
127
+ expect(near(r.im, 0)).toBe(true);
128
+ });
129
+
130
+ it("ln(e) = 1", () => {
131
+ const r = Complex.of(Math.E).ln();
132
+ expect(near(r.re, 1)).toBe(true);
133
+ expect(near(r.im, 0)).toBe(true);
134
+ });
135
+
136
+ it("ln(−1) = iπ (principal branch)", () => {
137
+ const r = Complex.of(-1).ln();
138
+ expect(near(r.re, 0)).toBe(true);
139
+ expect(near(r.im, Math.PI)).toBe(true);
140
+ });
141
+
142
+ it("ln(i) = iπ/2", () => {
143
+ const r = Complex.of(0, 1).ln();
144
+ expect(near(r.re, 0)).toBe(true);
145
+ expect(near(r.im, Math.PI / 2)).toBe(true);
146
+ });
147
+
148
+ it("exp(ln(z)) = z for various z", () => {
149
+ const zs = [
150
+ Complex.of(2, 3),
151
+ Complex.of(-1, 1),
152
+ Complex.of(0.5, -0.7),
153
+ ];
154
+ for (const z of zs) {
155
+ const r = z.ln().exp();
156
+ expect(near(r.re, z.re)).toBe(true);
157
+ expect(near(r.im, z.im)).toBe(true);
158
+ }
159
+ });
160
+
161
+ it("ln(0) throws", () => {
162
+ expect(() => Complex.of(0).ln()).toThrow(RangeError);
163
+ });
164
+
165
+ it("pow: i² = −1", () => {
166
+ const i = Complex.of(0, 1);
167
+ const r = i.pow(Complex.of(2));
168
+ expect(near(r.re, -1)).toBe(true);
169
+ expect(near(r.im, 0)).toBe(true);
170
+ });
171
+ });
172
+
173
+ describe("Complex — predicates and equality", () => {
174
+ it("isZero", () => {
175
+ expect(Complex.of(0, 0).isZero()).toBe(true);
176
+ expect(Complex.of(1e-15).isZero(1e-14)).toBe(true);
177
+ expect(Complex.of(1).isZero()).toBe(false);
178
+ });
179
+
180
+ it("isReal", () => {
181
+ expect(Complex.of(5).isReal()).toBe(true);
182
+ expect(Complex.of(5, 1e-13).isReal(1e-12)).toBe(true);
183
+ expect(Complex.of(5, 1).isReal()).toBe(false);
184
+ });
185
+
186
+ it("isPureImaginary", () => {
187
+ expect(Complex.of(0, 3).isPureImaginary()).toBe(true);
188
+ expect(Complex.of(1, 3).isPureImaginary()).toBe(false);
189
+ expect(Complex.of(0).isPureImaginary()).toBe(false);
190
+ });
191
+
192
+ it("equals with tolerance", () => {
193
+ expect(Complex.of(1, 2).equals(Complex.of(1 + 1e-11, 2))).toBe(true);
194
+ expect(Complex.of(1, 2).equals(Complex.of(1 + 1e-9, 2))).toBe(false);
195
+ });
196
+ });
197
+
198
+ describe("Complex — toString", () => {
199
+ it("real-only", () => expect(Complex.of(3).toString()).toBe("3"));
200
+ it("imaginary-only", () => expect(Complex.of(0, 2).toString()).toBe("2i"));
201
+ it("pure i", () => expect(Complex.of(0, 1).toString()).toBe("i"));
202
+ it("negative imaginary", () => expect(Complex.of(0, -1).toString()).toBe("-i"));
203
+ it("a+bi", () => expect(Complex.of(3, 4).toString()).toBe("3+4i"));
204
+ it("a-bi", () => expect(Complex.of(3, -4).toString()).toBe("3-4i"));
205
+ it("zero", () => expect(Complex.of(0, 0).toString()).toBe("0"));
206
+ });