@johnhenry/math 0.0.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.
Files changed (242) hide show
  1. package/README.md +165 -0
  2. package/dist/Bivector4.d.ts +52 -0
  3. package/dist/Bivector4.d.ts.map +1 -0
  4. package/dist/Bivector4.js +105 -0
  5. package/dist/Bivector4.js.map +1 -0
  6. package/dist/CellGraph.d.ts +259 -0
  7. package/dist/CellGraph.d.ts.map +1 -0
  8. package/dist/CellGraph.js +637 -0
  9. package/dist/CellGraph.js.map +1 -0
  10. package/dist/Clifford4Internal.d.ts +35 -0
  11. package/dist/Clifford4Internal.d.ts.map +1 -0
  12. package/dist/Clifford4Internal.js +86 -0
  13. package/dist/Clifford4Internal.js.map +1 -0
  14. package/dist/Combinatorics.d.ts +61 -0
  15. package/dist/Combinatorics.d.ts.map +1 -0
  16. package/dist/Combinatorics.js +186 -0
  17. package/dist/Combinatorics.js.map +1 -0
  18. package/dist/CombinatoricsGenerators.d.ts +44 -0
  19. package/dist/CombinatoricsGenerators.d.ts.map +1 -0
  20. package/dist/CombinatoricsGenerators.js +148 -0
  21. package/dist/CombinatoricsGenerators.js.map +1 -0
  22. package/dist/ComplexMath.d.ts +46 -0
  23. package/dist/ComplexMath.d.ts.map +1 -0
  24. package/dist/ComplexMath.js +98 -0
  25. package/dist/ComplexMath.js.map +1 -0
  26. package/dist/ComplexNumber.d.ts +174 -0
  27. package/dist/ComplexNumber.d.ts.map +1 -0
  28. package/dist/ComplexNumber.js +443 -0
  29. package/dist/ComplexNumber.js.map +1 -0
  30. package/dist/Cycle.d.ts +42 -0
  31. package/dist/Cycle.d.ts.map +1 -0
  32. package/dist/Cycle.js +120 -0
  33. package/dist/Cycle.js.map +1 -0
  34. package/dist/Decimal.d.ts +45 -0
  35. package/dist/Decimal.d.ts.map +1 -0
  36. package/dist/Decimal.js +135 -0
  37. package/dist/Decimal.js.map +1 -0
  38. package/dist/Distributions.d.ts +55 -0
  39. package/dist/Distributions.d.ts.map +1 -0
  40. package/dist/Distributions.js +222 -0
  41. package/dist/Distributions.js.map +1 -0
  42. package/dist/DualNumber.d.ts +42 -0
  43. package/dist/DualNumber.d.ts.map +1 -0
  44. package/dist/DualNumber.js +91 -0
  45. package/dist/DualNumber.js.map +1 -0
  46. package/dist/Environment.d.ts +44 -0
  47. package/dist/Environment.d.ts.map +1 -0
  48. package/dist/Environment.js +129 -0
  49. package/dist/Environment.js.map +1 -0
  50. package/dist/Expression.d.ts +19 -0
  51. package/dist/Expression.d.ts.map +1 -0
  52. package/dist/Expression.js +43 -0
  53. package/dist/Expression.js.map +1 -0
  54. package/dist/FFT.d.ts +21 -0
  55. package/dist/FFT.d.ts.map +1 -0
  56. package/dist/FFT.js +136 -0
  57. package/dist/FFT.js.map +1 -0
  58. package/dist/GeometricAlgebra4.d.ts +73 -0
  59. package/dist/GeometricAlgebra4.d.ts.map +1 -0
  60. package/dist/GeometricAlgebra4.js +115 -0
  61. package/dist/GeometricAlgebra4.js.map +1 -0
  62. package/dist/Geometry.d.ts +41 -0
  63. package/dist/Geometry.d.ts.map +1 -0
  64. package/dist/Geometry.js +167 -0
  65. package/dist/Geometry.js.map +1 -0
  66. package/dist/Graph.d.ts +72 -0
  67. package/dist/Graph.d.ts.map +1 -0
  68. package/dist/Graph.js +337 -0
  69. package/dist/Graph.js.map +1 -0
  70. package/dist/Graph3DUtils.d.ts +80 -0
  71. package/dist/Graph3DUtils.d.ts.map +1 -0
  72. package/dist/Graph3DUtils.js +156 -0
  73. package/dist/Graph3DUtils.js.map +1 -0
  74. package/dist/GraphUtils.d.ts +67 -0
  75. package/dist/GraphUtils.d.ts.map +1 -0
  76. package/dist/GraphUtils.js +72 -0
  77. package/dist/GraphUtils.js.map +1 -0
  78. package/dist/GroupTheory.d.ts +73 -0
  79. package/dist/GroupTheory.d.ts.map +1 -0
  80. package/dist/GroupTheory.js +194 -0
  81. package/dist/GroupTheory.js.map +1 -0
  82. package/dist/IntUtils.d.ts +33 -0
  83. package/dist/IntUtils.d.ts.map +1 -0
  84. package/dist/IntUtils.js +185 -0
  85. package/dist/IntUtils.js.map +1 -0
  86. package/dist/Interval.d.ts +44 -0
  87. package/dist/Interval.d.ts.map +1 -0
  88. package/dist/Interval.js +165 -0
  89. package/dist/Interval.js.map +1 -0
  90. package/dist/Lattice.d.ts +48 -0
  91. package/dist/Lattice.d.ts.map +1 -0
  92. package/dist/Lattice.js +65 -0
  93. package/dist/Lattice.js.map +1 -0
  94. package/dist/Logic.d.ts +22 -0
  95. package/dist/Logic.d.ts.map +1 -0
  96. package/dist/Logic.js +34 -0
  97. package/dist/Logic.js.map +1 -0
  98. package/dist/MatrixMath.d.ts +71 -0
  99. package/dist/MatrixMath.d.ts.map +1 -0
  100. package/dist/MatrixMath.js +302 -0
  101. package/dist/MatrixMath.js.map +1 -0
  102. package/dist/NumberTheory.d.ts +49 -0
  103. package/dist/NumberTheory.d.ts.map +1 -0
  104. package/dist/NumberTheory.js +230 -0
  105. package/dist/NumberTheory.js.map +1 -0
  106. package/dist/Numerical.d.ts +85 -0
  107. package/dist/Numerical.d.ts.map +1 -0
  108. package/dist/Numerical.js +343 -0
  109. package/dist/Numerical.js.map +1 -0
  110. package/dist/Permutation.d.ts +43 -0
  111. package/dist/Permutation.d.ts.map +1 -0
  112. package/dist/Permutation.js +142 -0
  113. package/dist/Permutation.js.map +1 -0
  114. package/dist/Polygon.d.ts +64 -0
  115. package/dist/Polygon.d.ts.map +1 -0
  116. package/dist/Polygon.js +221 -0
  117. package/dist/Polygon.js.map +1 -0
  118. package/dist/PolynomialRing.d.ts +74 -0
  119. package/dist/PolynomialRing.d.ts.map +1 -0
  120. package/dist/PolynomialRing.js +231 -0
  121. package/dist/PolynomialRing.js.map +1 -0
  122. package/dist/Quaternion.d.ts +37 -0
  123. package/dist/Quaternion.d.ts.map +1 -0
  124. package/dist/Quaternion.js +106 -0
  125. package/dist/Quaternion.js.map +1 -0
  126. package/dist/Rational.d.ts +34 -0
  127. package/dist/Rational.d.ts.map +1 -0
  128. package/dist/Rational.js +136 -0
  129. package/dist/Rational.js.map +1 -0
  130. package/dist/RealMath.d.ts +112 -0
  131. package/dist/RealMath.d.ts.map +1 -0
  132. package/dist/RealMath.js +262 -0
  133. package/dist/RealMath.js.map +1 -0
  134. package/dist/Rotor4.d.ts +180 -0
  135. package/dist/Rotor4.d.ts.map +1 -0
  136. package/dist/Rotor4.js +388 -0
  137. package/dist/Rotor4.js.map +1 -0
  138. package/dist/SpecialFunctions.d.ts +32 -0
  139. package/dist/SpecialFunctions.d.ts.map +1 -0
  140. package/dist/SpecialFunctions.js +161 -0
  141. package/dist/SpecialFunctions.js.map +1 -0
  142. package/dist/SpecialOperator.d.ts +21 -0
  143. package/dist/SpecialOperator.d.ts.map +1 -0
  144. package/dist/SpecialOperator.js +26 -0
  145. package/dist/SpecialOperator.js.map +1 -0
  146. package/dist/Statistics.d.ts +50 -0
  147. package/dist/Statistics.d.ts.map +1 -0
  148. package/dist/Statistics.js +131 -0
  149. package/dist/Statistics.js.map +1 -0
  150. package/dist/StringEvaluator.d.ts +48 -0
  151. package/dist/StringEvaluator.d.ts.map +1 -0
  152. package/dist/StringEvaluator.js +190 -0
  153. package/dist/StringEvaluator.js.map +1 -0
  154. package/dist/Structure.d.ts +130 -0
  155. package/dist/Structure.d.ts.map +1 -0
  156. package/dist/Structure.js +394 -0
  157. package/dist/Structure.js.map +1 -0
  158. package/dist/Symbolic.d.ts +501 -0
  159. package/dist/Symbolic.d.ts.map +1 -0
  160. package/dist/Symbolic.js +4077 -0
  161. package/dist/Symbolic.js.map +1 -0
  162. package/dist/Type.d.ts +31 -0
  163. package/dist/Type.d.ts.map +1 -0
  164. package/dist/Type.js +47 -0
  165. package/dist/Type.js.map +1 -0
  166. package/dist/Utilities.d.ts +27 -0
  167. package/dist/Utilities.d.ts.map +1 -0
  168. package/dist/Utilities.js +47 -0
  169. package/dist/Utilities.js.map +1 -0
  170. package/dist/Vec4.d.ts +39 -0
  171. package/dist/Vec4.d.ts.map +1 -0
  172. package/dist/Vec4.js +82 -0
  173. package/dist/Vec4.js.map +1 -0
  174. package/dist/Vector.d.ts +83 -0
  175. package/dist/Vector.d.ts.map +1 -0
  176. package/dist/Vector.js +173 -0
  177. package/dist/Vector.js.map +1 -0
  178. package/dist/VectorCalculus.d.ts +42 -0
  179. package/dist/VectorCalculus.d.ts.map +1 -0
  180. package/dist/VectorCalculus.js +82 -0
  181. package/dist/VectorCalculus.js.map +1 -0
  182. package/dist/VectorUtils.d.ts +172 -0
  183. package/dist/VectorUtils.d.ts.map +1 -0
  184. package/dist/VectorUtils.js +706 -0
  185. package/dist/VectorUtils.js.map +1 -0
  186. package/dist/differentiation-rules.d.ts +21 -0
  187. package/dist/differentiation-rules.d.ts.map +1 -0
  188. package/dist/differentiation-rules.js +52 -0
  189. package/dist/differentiation-rules.js.map +1 -0
  190. package/dist/index.d.ts +52 -0
  191. package/dist/index.d.ts.map +1 -0
  192. package/dist/index.js +66 -0
  193. package/dist/index.js.map +1 -0
  194. package/package.json +68 -0
  195. package/src/Bivector4.ts +136 -0
  196. package/src/CellGraph.ts +669 -0
  197. package/src/Clifford4Internal.ts +90 -0
  198. package/src/Combinatorics.ts +183 -0
  199. package/src/CombinatoricsGenerators.ts +144 -0
  200. package/src/ComplexMath.ts +162 -0
  201. package/src/ComplexNumber.ts +491 -0
  202. package/src/Cycle.ts +126 -0
  203. package/src/Decimal.ts +145 -0
  204. package/src/Distributions.ts +269 -0
  205. package/src/DualNumber.ts +105 -0
  206. package/src/Environment.ts +127 -0
  207. package/src/Expression.ts +48 -0
  208. package/src/FFT.ts +146 -0
  209. package/src/GeometricAlgebra4.ts +157 -0
  210. package/src/Geometry.ts +173 -0
  211. package/src/Graph.ts +337 -0
  212. package/src/Graph3DUtils.ts +269 -0
  213. package/src/GraphUtils.ts +159 -0
  214. package/src/GroupTheory.ts +214 -0
  215. package/src/IntUtils.ts +200 -0
  216. package/src/Interval.ts +171 -0
  217. package/src/Lattice.ts +87 -0
  218. package/src/Logic.ts +38 -0
  219. package/src/MatrixMath.ts +326 -0
  220. package/src/NumberTheory.ts +228 -0
  221. package/src/Numerical.ts +390 -0
  222. package/src/Permutation.ts +148 -0
  223. package/src/Polygon.ts +229 -0
  224. package/src/PolynomialRing.ts +242 -0
  225. package/src/Quaternion.ts +125 -0
  226. package/src/Rational.ts +148 -0
  227. package/src/RealMath.ts +346 -0
  228. package/src/Rotor4.ts +440 -0
  229. package/src/SpecialFunctions.ts +152 -0
  230. package/src/SpecialOperator.ts +28 -0
  231. package/src/Statistics.ts +170 -0
  232. package/src/StringEvaluator.ts +208 -0
  233. package/src/Structure.ts +481 -0
  234. package/src/Symbolic.ts +4313 -0
  235. package/src/Type.ts +48 -0
  236. package/src/Utilities.ts +49 -0
  237. package/src/Vec4.ts +98 -0
  238. package/src/Vector.ts +189 -0
  239. package/src/VectorCalculus.ts +87 -0
  240. package/src/VectorUtils.ts +797 -0
  241. package/src/differentiation-rules.ts +73 -0
  242. package/src/index.ts +140 -0
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Quaternion — a number of the form `w + x·i + y·j + z·k`, the four-dimensional
3
+ * division algebra. Extends the idea of {@link ComplexNumber} and is the
4
+ * standard tool for representing and composing 3D rotations.
5
+ */
6
+ export class Quaternion {
7
+ readonly w: number;
8
+ readonly x: number;
9
+ readonly y: number;
10
+ readonly z: number;
11
+
12
+ static readonly Identity = new Quaternion(1, 0, 0, 0);
13
+ static readonly Zero = new Quaternion(0, 0, 0, 0);
14
+
15
+ constructor(w = 0, x = 0, y = 0, z = 0) {
16
+ this.w = w;
17
+ this.x = x;
18
+ this.y = y;
19
+ this.z = z;
20
+ }
21
+
22
+ add(q: Quaternion): Quaternion {
23
+ return new Quaternion(this.w + q.w, this.x + q.x, this.y + q.y, this.z + q.z);
24
+ }
25
+
26
+ subtract(q: Quaternion): Quaternion {
27
+ return new Quaternion(this.w - q.w, this.x - q.x, this.y - q.y, this.z - q.z);
28
+ }
29
+
30
+ scale(s: number): Quaternion {
31
+ return new Quaternion(this.w * s, this.x * s, this.y * s, this.z * s);
32
+ }
33
+
34
+ negate(): Quaternion {
35
+ return new Quaternion(-this.w, -this.x, -this.y, -this.z);
36
+ }
37
+
38
+ /** The Hamilton product (non-commutative). */
39
+ multiply(q: Quaternion): Quaternion {
40
+ return new Quaternion(
41
+ this.w * q.w - this.x * q.x - this.y * q.y - this.z * q.z,
42
+ this.w * q.x + this.x * q.w + this.y * q.z - this.z * q.y,
43
+ this.w * q.y - this.x * q.z + this.y * q.w + this.z * q.x,
44
+ this.w * q.z + this.x * q.y - this.y * q.x + this.z * q.w,
45
+ );
46
+ }
47
+
48
+ conjugate(): Quaternion {
49
+ return new Quaternion(this.w, -this.x, -this.y, -this.z);
50
+ }
51
+
52
+ dot(q: Quaternion): number {
53
+ return this.w * q.w + this.x * q.x + this.y * q.y + this.z * q.z;
54
+ }
55
+
56
+ get norm(): number {
57
+ return Math.hypot(this.w, this.x, this.y, this.z);
58
+ }
59
+
60
+ normalize(): Quaternion {
61
+ const n = this.norm;
62
+ if (n === 0) throw new Error("Cannot normalize the zero quaternion.");
63
+ return this.scale(1 / n);
64
+ }
65
+
66
+ /** Multiplicative inverse `q⁻¹ = conj(q) / |q|²`. */
67
+ inverse(): Quaternion {
68
+ const n2 = this.w * this.w + this.x * this.x + this.y * this.y + this.z * this.z;
69
+ if (n2 === 0) throw new Error("The zero quaternion has no inverse.");
70
+ return this.conjugate().scale(1 / n2);
71
+ }
72
+
73
+ equals(q: Quaternion): boolean {
74
+ return this.w === q.w && this.x === q.x && this.y === q.y && this.z === q.z;
75
+ }
76
+
77
+ /** A unit quaternion representing a rotation of `angle` radians about `axis`. */
78
+ static fromAxisAngle(axis: [number, number, number], angle: number): Quaternion {
79
+ const [ax, ay, az] = axis;
80
+ const len = Math.hypot(ax, ay, az) || 1;
81
+ const half = angle / 2;
82
+ const s = Math.sin(half) / len;
83
+ return new Quaternion(Math.cos(half), ax * s, ay * s, az * s);
84
+ }
85
+
86
+ /** Rotate a 3D vector by this (unit) quaternion, returning `[x, y, z]`. */
87
+ rotateVector(v: [number, number, number]): [number, number, number] {
88
+ const q = this.normalize();
89
+ const p = new Quaternion(0, v[0], v[1], v[2]);
90
+ const r = q.multiply(p).multiply(q.conjugate());
91
+ return [r.x, r.y, r.z];
92
+ }
93
+
94
+ /** The 3×3 rotation matrix (as `number[][]`) for this quaternion. */
95
+ toRotationMatrix(): number[][] {
96
+ const { w, x, y, z } = this.normalize();
97
+ return [
98
+ [1 - 2 * (y * y + z * z), 2 * (x * y - z * w), 2 * (x * z + y * w)],
99
+ [2 * (x * y + z * w), 1 - 2 * (x * x + z * z), 2 * (y * z - x * w)],
100
+ [2 * (x * z - y * w), 2 * (y * z + x * w), 1 - 2 * (x * x + y * y)],
101
+ ];
102
+ }
103
+
104
+ /** Spherical linear interpolation between two unit quaternions. */
105
+ static slerp(a: Quaternion, b: Quaternion, t: number): Quaternion {
106
+ const qa = a.normalize();
107
+ let qb = b.normalize();
108
+ let cos = qa.dot(qb);
109
+ if (cos < 0) {
110
+ qb = qb.scale(-1);
111
+ cos = -cos;
112
+ }
113
+ if (cos > 0.9995) return qa.add(qb.subtract(qa).scale(t)).normalize();
114
+ const theta0 = Math.acos(cos);
115
+ const theta = theta0 * t;
116
+ const sinTheta0 = Math.sin(theta0);
117
+ const s0 = Math.sin(theta0 - theta) / sinTheta0;
118
+ const s1 = Math.sin(theta) / sinTheta0;
119
+ return qa.scale(s0).add(qb.scale(s1));
120
+ }
121
+
122
+ toString(): string {
123
+ return `${this.w} + ${this.x}i + ${this.y}j + ${this.z}k`;
124
+ }
125
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Rational — an exact rational number `numerator / denominator` backed by
3
+ * `bigint`, always stored in lowest terms with a positive denominator. Gives the
4
+ * library exact (error-free) arithmetic, e.g. for exact linear algebra or
5
+ * polynomial GCDs.
6
+ */
7
+
8
+ function bgcd(a: bigint, b: bigint): bigint {
9
+ let x = a < 0n ? -a : a;
10
+ let y = b < 0n ? -b : b;
11
+ while (y) [x, y] = [y, x % y];
12
+ return x;
13
+ }
14
+
15
+ const toBig = (x: bigint | number): bigint => {
16
+ if (typeof x === "bigint") return x;
17
+ if (!Number.isInteger(x)) throw new Error(`Rational requires integer inputs; got ${x}`);
18
+ return BigInt(x);
19
+ };
20
+
21
+ export class Rational {
22
+ readonly numerator: bigint;
23
+ readonly denominator: bigint;
24
+
25
+ static readonly Zero = new Rational(0n, 1n);
26
+ static readonly One = new Rational(1n, 1n);
27
+
28
+ constructor(numerator: bigint | number, denominator: bigint | number = 1n) {
29
+ let n = toBig(numerator);
30
+ let d = toBig(denominator);
31
+ if (d === 0n) throw new Error("Rational denominator must be non-zero.");
32
+ if (d < 0n) {
33
+ n = -n;
34
+ d = -d;
35
+ }
36
+ const g = bgcd(n, d) || 1n;
37
+ this.numerator = n / g;
38
+ this.denominator = d / g;
39
+ }
40
+
41
+ static from(value: bigint | number | Rational): Rational {
42
+ if (value instanceof Rational) return value;
43
+ if (typeof value === "bigint") return new Rational(value, 1n);
44
+ return Rational.fromNumber(value);
45
+ }
46
+
47
+ /** Best rational approximation of a float via continued fractions. */
48
+ static fromNumber(value: number, maxDenominator = 1_000_000): Rational {
49
+ if (Number.isInteger(value)) return new Rational(BigInt(value), 1n);
50
+ if (!Number.isFinite(value)) throw new Error("Cannot make a Rational from a non-finite number.");
51
+ let [h0, h1] = [0n, 1n];
52
+ let [k0, k1] = [1n, 0n];
53
+ let x = value;
54
+ const maxD = BigInt(maxDenominator);
55
+ for (let i = 0; i < 64; i++) {
56
+ const a = Math.floor(x);
57
+ const aB = BigInt(a);
58
+ [h0, h1] = [h1, aB * h1 + h0];
59
+ [k0, k1] = [k1, aB * k1 + k0];
60
+ if (k1 > maxD) return new Rational(h0, k0);
61
+ const frac = x - a;
62
+ if (frac < 1e-15) break;
63
+ x = 1 / frac;
64
+ }
65
+ return new Rational(h1, k1);
66
+ }
67
+
68
+ add(other: Rational): Rational {
69
+ return new Rational(
70
+ this.numerator * other.denominator + other.numerator * this.denominator,
71
+ this.denominator * other.denominator,
72
+ );
73
+ }
74
+
75
+ subtract(other: Rational): Rational {
76
+ return new Rational(
77
+ this.numerator * other.denominator - other.numerator * this.denominator,
78
+ this.denominator * other.denominator,
79
+ );
80
+ }
81
+
82
+ multiply(other: Rational): Rational {
83
+ return new Rational(this.numerator * other.numerator, this.denominator * other.denominator);
84
+ }
85
+
86
+ divide(other: Rational): Rational {
87
+ if (other.numerator === 0n) throw new Error("Division by zero Rational.");
88
+ return new Rational(this.numerator * other.denominator, this.denominator * other.numerator);
89
+ }
90
+
91
+ negate(): Rational {
92
+ return new Rational(-this.numerator, this.denominator);
93
+ }
94
+
95
+ reciprocal(): Rational {
96
+ if (this.numerator === 0n) throw new Error("Zero has no reciprocal.");
97
+ return new Rational(this.denominator, this.numerator);
98
+ }
99
+
100
+ abs(): Rational {
101
+ return this.numerator < 0n ? this.negate() : this;
102
+ }
103
+
104
+ /** Raise to an integer power (negative powers invert). */
105
+ pow(exponent: number): Rational {
106
+ if (!Number.isInteger(exponent)) throw new Error("Rational.pow requires an integer exponent.");
107
+ if (exponent < 0) return this.reciprocal().pow(-exponent);
108
+ let result = Rational.One;
109
+ let base: Rational = this;
110
+ let e = exponent;
111
+ while (e > 0) {
112
+ if (e & 1) result = result.multiply(base);
113
+ base = base.multiply(base);
114
+ e >>= 1;
115
+ }
116
+ return result;
117
+ }
118
+
119
+ equals(other: Rational): boolean {
120
+ return this.numerator === other.numerator && this.denominator === other.denominator;
121
+ }
122
+
123
+ /** -1, 0 or 1 depending on ordering against `other`. */
124
+ compare(other: Rational): number {
125
+ const diff = this.numerator * other.denominator - other.numerator * this.denominator;
126
+ return diff < 0n ? -1 : diff > 0n ? 1 : 0;
127
+ }
128
+
129
+ lessThan(other: Rational): boolean {
130
+ return this.compare(other) < 0;
131
+ }
132
+
133
+ get sign(): number {
134
+ return this.numerator < 0n ? -1 : this.numerator > 0n ? 1 : 0;
135
+ }
136
+
137
+ isZero(): boolean {
138
+ return this.numerator === 0n;
139
+ }
140
+
141
+ toNumber(): number {
142
+ return Number(this.numerator) / Number(this.denominator);
143
+ }
144
+
145
+ toString(): string {
146
+ return this.denominator === 1n ? `${this.numerator}` : `${this.numerator}/${this.denominator}`;
147
+ }
148
+ }
@@ -0,0 +1,346 @@
1
+ import { Vector } from "./Vector.ts";
2
+ import { VectorUtils } from "./VectorUtils.ts";
3
+
4
+ /**
5
+ * RealMath — arithmetic, trigonometry, numeric calculus and probability over
6
+ * real numbers, as plain exported functions (no wrapping class — a `static`
7
+ * method bag buys nothing over top-level functions for pure scalar math).
8
+ * Ported from Mallory's ActionScript `RealMath`.
9
+ *
10
+ * Matrix operations were dropped: `Structure.realField()` already provides
11
+ * generic linear algebra, and {@link MatrixMath} covers heavier real-only
12
+ * needs (LU/QR/Cholesky/SVD). Sample statistics (`mean`/`variance`/percentiles/
13
+ * regression/etc.) moved to {@link Statistics}, which is real-only by nature
14
+ * (order-dependent stats don't generalize to complex numbers).
15
+ *
16
+ * Bugs fixed from the AS3 original:
17
+ * - `subtract` returned `a*b` (!).
18
+ * - `equal` ignored its tolerance argument (`Math.abs(a-b) <= 0`).
19
+ * - `integrateN` used a nonsensical sample point and had an unreachable return.
20
+ * - `differentiateN` was a forward difference despite claiming to be symmetric.
21
+ * - `crossProduct` read index 3 for the z-component and used truthiness tests.
22
+ */
23
+
24
+ type RVec = Vector<number>;
25
+ type Unary = (x: number) => number;
26
+
27
+ // -- constants ---------------------------------------------------------------
28
+
29
+ export const Zero = 0;
30
+ export const One = 1;
31
+ export const E = Math.E;
32
+ export const PI = Math.PI;
33
+ export const PHI = 1.61803399;
34
+ export const PositiveInfinity = Infinity;
35
+ export const NegativeInfinity = -Infinity;
36
+
37
+ // -- Chapter 1: utilities -----------------------------------------------------
38
+
39
+ export function radiansToDegrees(r: number): number {
40
+ return (180 / Math.PI) * r;
41
+ }
42
+
43
+ export function degreesToRadians(d: number): number {
44
+ return (Math.PI / 180) * d;
45
+ }
46
+
47
+ // -- Chapter 2: comparison -----------------------------------------------------
48
+
49
+ /** Equality within `distance` (bug fix: AS3 ignored the tolerance). */
50
+ export function equal(a: number, b: number, distance = 0): boolean {
51
+ return Math.abs(a - b) <= distance;
52
+ }
53
+
54
+ export function compare(a: number, b: number): number {
55
+ if (a > b) return 1;
56
+ if (a < b) return -1;
57
+ return 0;
58
+ }
59
+
60
+ export function lessThan(a: number, b: number): boolean {
61
+ return a < b;
62
+ }
63
+
64
+ export function greaterThan(a: number, b: number): boolean {
65
+ return a > b;
66
+ }
67
+
68
+ // -- Chapter 3: unary -----------------------------------------------------------
69
+
70
+ export function negative(a = 0): number {
71
+ return -a;
72
+ }
73
+
74
+ /** `1/a` (matches `divide`'s directed-infinity convention rather than always returning NaN). */
75
+ export function reciprocal(a = 1): number {
76
+ return 1 / a;
77
+ }
78
+
79
+ /** Round away tiny floating-point noise (rounds to 10 decimal places). */
80
+ export function identity(a = 1): number {
81
+ return roundTo(a);
82
+ }
83
+
84
+ // -- Chapter 4: binary -----------------------------------------------------------
85
+
86
+ export function add(a = 0, b = 0): number {
87
+ return a + b;
88
+ }
89
+
90
+ /** Subtraction (bug fix: AS3 returned `a*b`). */
91
+ export function subtract(a = 0, b = 0): number {
92
+ return a - b;
93
+ }
94
+
95
+ export function multiply(a = 1, b = 1): number {
96
+ return a * b;
97
+ }
98
+
99
+ export function divide(a = 1, b = 1): number {
100
+ if (b === 0) {
101
+ if (a > 0) return Infinity;
102
+ if (a < 0) return -Infinity;
103
+ return NaN;
104
+ }
105
+ return a / b;
106
+ }
107
+
108
+ // -- Chapter 5: exponential / logarithmic -----------------------------------------
109
+
110
+ export function power(a = 1, exponent = 1): number {
111
+ return a ** exponent;
112
+ }
113
+
114
+ export function square(a = 1): number {
115
+ return a ** 2;
116
+ }
117
+
118
+ export function squareRoot(a = 1): number {
119
+ return Math.sqrt(a);
120
+ }
121
+
122
+ export function logarithm(a = 1, base = Math.E): number {
123
+ return Math.log(a) / Math.log(base);
124
+ }
125
+
126
+ // -- Chapter 6: trigonometry -----------------------------------------------------
127
+
128
+ export function sine(a = 0): number {
129
+ return identity(Math.sin(a));
130
+ }
131
+ export function cosine(a = 0): number {
132
+ return identity(Math.cos(a));
133
+ }
134
+ export function tangent(a = 0): number {
135
+ return identity(Math.tan(a));
136
+ }
137
+ export function cosecant(a = 0): number {
138
+ return identity(1 / Math.sin(a));
139
+ }
140
+ export function secant(a = 0): number {
141
+ return identity(1 / Math.cos(a));
142
+ }
143
+ export function cotangent(a = 0): number {
144
+ return identity(1 / Math.tan(a));
145
+ }
146
+
147
+ export function hyperbolicSine(a: number): number {
148
+ return identity(0.5 * (Math.exp(a) - Math.exp(-a)));
149
+ }
150
+ export function hyperbolicCosine(a: number): number {
151
+ return identity(0.5 * (Math.exp(a) + Math.exp(-a)));
152
+ }
153
+ export function hyperbolicTangent(a: number): number {
154
+ return hyperbolicSine(a) / hyperbolicCosine(a);
155
+ }
156
+ export function hyperbolicCosecant(a: number): number {
157
+ return 1 / hyperbolicSine(a);
158
+ }
159
+ export function hyperbolicSecant(a: number): number {
160
+ return 1 / hyperbolicCosine(a);
161
+ }
162
+ export function hyperbolicCotangent(a: number): number {
163
+ return hyperbolicCosine(a) / hyperbolicSine(a);
164
+ }
165
+
166
+ // -- Chapter 7: series, algebra, calculus -----------------------------------------
167
+
168
+ export function sumSeries(unaryOperation: Unary, start = 0, end = 0): number {
169
+ return VectorUtils.collapse(
170
+ VectorUtils.transform(VectorUtils.arithmeticSequence(start, end), unaryOperation),
171
+ (a, b) => a + b,
172
+ 0,
173
+ ) as number;
174
+ }
175
+
176
+ export function productSeries(unaryOperation: Unary, start = 0, end = 0): number {
177
+ return VectorUtils.collapse(
178
+ VectorUtils.transform(VectorUtils.arithmeticSequence(start, end), unaryOperation),
179
+ (a, b) => a * b,
180
+ 1,
181
+ ) as number;
182
+ }
183
+
184
+ /**
185
+ * Newton–Raphson root finder for `unaryOperation(x) == target`.
186
+ *
187
+ * Bug fix: the AS3 version updated `guess -= (f(guess) - target)`, i.e. Newton
188
+ * with an assumed derivative of 1 — it diverges for almost any nonlinear
189
+ * function. Here the derivative is estimated numerically ({@link differentiateN})
190
+ * so the iteration actually converges, and unbounded recursion is replaced by a
191
+ * bounded loop.
192
+ */
193
+ export function solveN(
194
+ unaryOperation: Unary,
195
+ target: number,
196
+ desiredAccuracy = 0.01,
197
+ guess = 0,
198
+ maxIterations = 10000,
199
+ ): number {
200
+ let x = guess;
201
+ for (let i = 0; i < maxIterations; i++) {
202
+ const difference = unaryOperation(x) - target;
203
+ if (Math.abs(difference) <= desiredAccuracy) return x;
204
+ const slope = differentiateN(unaryOperation, x, 1e-6);
205
+ if (slope === 0 || !Number.isFinite(slope)) break;
206
+ x = x - difference / slope;
207
+ }
208
+ return x;
209
+ }
210
+
211
+ /**
212
+ * Numeric integral via the midpoint rule (bug fix: the AS3 code sampled at
213
+ * `2*lowerLimit+interval` and had an unreachable return).
214
+ */
215
+ export function integrateN(unaryOperation: Unary, lowerLimit: number, upperLimit: number, interval = 0.01): number {
216
+ if (interval <= 0) throw new RangeError(`RealMath.integrateN: interval must be positive, got ${interval}`);
217
+ let result = 0;
218
+ for (let x = lowerLimit; x < upperLimit; x += interval) {
219
+ result += unaryOperation(x + interval / 2);
220
+ }
221
+ return interval * result;
222
+ }
223
+
224
+ /**
225
+ * Numeric derivative via the symmetric difference quotient (bug fix: the AS3
226
+ * code was a forward difference despite the "symmetric" comment).
227
+ */
228
+ export function differentiateN(unaryOperation: Unary, point: number, limit = 0.01): number {
229
+ if (limit <= 0) throw new RangeError(`RealMath.differentiateN: limit must be positive, got ${limit}`);
230
+ return (unaryOperation(point + limit) - unaryOperation(point - limit)) / (2 * limit);
231
+ }
232
+
233
+ // -- Chapter 9: probability -----------------------------------------------------
234
+
235
+ export function normalDistribution(mean: number, sDev: number): Unary {
236
+ return (x: number) => (1 / (sDev * Math.sqrt(2 * Math.PI))) * Math.E ** (-0.5 * ((x - mean) / sDev) ** 2);
237
+ }
238
+
239
+ export function standardNormalDistribution(x: number): number {
240
+ return normalDistribution(0, 1)(x);
241
+ }
242
+
243
+ export function normalProbability(
244
+ x: number,
245
+ mean: number,
246
+ sDev: number,
247
+ negativeInfinityApproximation = -5,
248
+ integrationInterval = 0.0001,
249
+ ): number {
250
+ return integrateN(normalDistribution(mean, sDev), negativeInfinityApproximation, x, integrationInterval);
251
+ }
252
+
253
+ export function standardNormalProbability(z: number, negativeInfinityApproximation = -5, interval = 0.0001): number {
254
+ return integrateN(standardNormalDistribution, negativeInfinityApproximation, z, interval);
255
+ }
256
+
257
+ // -- Chapter 8: precision & randomness -----------------------------------------
258
+
259
+ /** Round to `precision` decimal places (replaces the buggy Flex NumberFormatter). */
260
+ export function roundTo(num: number, precision = 10): number {
261
+ if (!Number.isFinite(num)) return num;
262
+ const factor = 10 ** precision;
263
+ return Math.round(num * factor) / factor;
264
+ }
265
+
266
+ /** Uniform random in `[min(r1,r2), max(r1,r2)]`. */
267
+ export function random(r1 = 1, r2 = 0, _inclusive = true): number {
268
+ const low = Math.min(r1, r2);
269
+ const high = Math.max(r1, r2);
270
+ return low + Math.random() * (high - low);
271
+ }
272
+
273
+ /** Placeholder for the random.org fetch; always falls back to {@link random}. */
274
+ export function randomOrg(lower = 0, upper = 1, inclusive = true, fallback = true): number {
275
+ if (fallback) return random(lower, upper, inclusive);
276
+ return 0;
277
+ }
278
+
279
+ export function jitter(a: number, magnitude = 0.1): number {
280
+ return a + (2 * Math.random() - 1) * magnitude;
281
+ }
282
+
283
+ // -- Part II Chapter 1: vectors -----------------------------------------------
284
+
285
+ export function addVector(alpha: RVec, beta: RVec): RVec {
286
+ return VectorUtils.combine(alpha, beta, add) as RVec;
287
+ }
288
+
289
+ export function scaleVector(alpha: RVec, scalar: number): RVec {
290
+ return VectorUtils.transform(alpha, (x) => multiply(scalar, x));
291
+ }
292
+
293
+ export function negativeVector(alpha: RVec): RVec {
294
+ return scaleVector(alpha, -1);
295
+ }
296
+
297
+ export function subtractVector(alpha: RVec, beta: RVec): RVec {
298
+ return addVector(alpha, negativeVector(beta));
299
+ }
300
+
301
+ export function dotProduct(alpha: RVec, beta: RVec): number {
302
+ const collapsable = VectorUtils.combine(alpha, beta, multiply, 0) as RVec;
303
+ return VectorUtils.collapse(collapsable, add) as number;
304
+ }
305
+
306
+ /**
307
+ * 3D cross product (bug fix: the AS3 code read index 3 for the z-component
308
+ * and used truthiness tests that dropped legitimate zero components).
309
+ */
310
+ export function crossProduct(alpha: RVec, beta: RVec): RVec {
311
+ const a = [0, 0, 0];
312
+ const b = [0, 0, 0];
313
+ for (let i = 0; i < 3; i++) {
314
+ if (i < alpha.length && alpha[i] != null) a[i] = alpha[i] as number;
315
+ if (i < beta.length && beta[i] != null) b[i] = beta[i] as number;
316
+ }
317
+ const i = a[1] * b[2] - a[2] * b[1];
318
+ const j = a[2] * b[0] - a[0] * b[2];
319
+ const k = a[0] * b[1] - a[1] * b[0];
320
+ return Vector.fromArray([i, j, k]);
321
+ }
322
+
323
+ export function kroneckerProduct(A: RVec, B: RVec): RVec {
324
+ let kronecker = VectorUtils.constantVector(A.length, null as unknown as number) as Vector<unknown>;
325
+ kronecker = VectorUtils.fillByIndex(kronecker, (i) => scaleVector(B, A[i] as number));
326
+ return VectorUtils.flattenSDLevels(kronecker, 2) as RVec;
327
+ }
328
+
329
+ export function distanceVector(alpha: RVec, beta: RVec, norm = 2): number {
330
+ return pNorm(subtractVector(alpha, beta), norm);
331
+ }
332
+
333
+ export function pNorm(alpha: RVec, norm = 2): number {
334
+ if (norm === 0) return Math.max(...VectorUtils.transform(alpha, Math.abs));
335
+ const summable = VectorUtils.transform(alpha, (x) => Math.abs(x) ** norm);
336
+ const sum = VectorUtils.collapse(summable, (a, b) => a + b, 0) as number;
337
+ return sum ** (1 / norm);
338
+ }
339
+
340
+ export function magnitudeVector(alpha: RVec): number {
341
+ return pNorm(alpha, 2);
342
+ }
343
+
344
+ export function angleBetween(alpha: RVec, beta: RVec): number {
345
+ return Math.acos(dotProduct(alpha, beta) / (magnitudeVector(alpha) * magnitudeVector(beta)));
346
+ }