@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,90 @@
1
+ /**
2
+ * Internal Cl(4,0) geometric-algebra engine, used by {@link Vec4}, {@link Bivector4}
3
+ * and {@link Rotor4} to implement the geometric product, sandwich product, and
4
+ * exponential/logarithm maps correctly.
5
+ *
6
+ * Not exported from the package index — it is an implementation detail. Basis
7
+ * vectors e1..e4 are represented as bits 0..3 of a "blade" bitmask (0..15),
8
+ * e.g. blade 0b0011 = e1^e2, blade 0b1111 = e1^e2^e3^e4 (the pseudoscalar).
9
+ * Because the metric is Euclidean (e_i * e_i = +1 for every i — no negative or
10
+ * null basis vectors), the geometric product of two basis blades only needs a
11
+ * *reordering sign* (how many transpositions sort the concatenated index list
12
+ * into canonical order); there is no metric-signature contribution. This is
13
+ * the standard bitmask/blade technique used by geometric-algebra libraries
14
+ * (e.g. ganja.js), and is far less error-prone than hand-deriving each
15
+ * product formula term by term.
16
+ */
17
+
18
+ export const BLADE_COUNT = 16; // 2^4 basis blades in Cl(4,0)
19
+
20
+ function popcount(n: number): number {
21
+ let count = 0;
22
+ let x = n;
23
+ while (x !== 0) {
24
+ count += x & 1;
25
+ x >>= 1;
26
+ }
27
+ return count;
28
+ }
29
+
30
+ /** Sign picked up reordering the concatenation of blade `a` then blade `b` into canonical order. */
31
+ function reorderingSign(a: number, b: number): number {
32
+ let aShifted = a >> 1;
33
+ let swaps = 0;
34
+ while (aShifted !== 0) {
35
+ swaps += popcount(aShifted & b);
36
+ aShifted >>= 1;
37
+ }
38
+ return swaps % 2 === 0 ? 1 : -1;
39
+ }
40
+
41
+ export type Multivector4 = Float64Array; // length BLADE_COUNT, indexed by blade bitmask
42
+
43
+ export function mvZero(): Multivector4 {
44
+ return new Float64Array(BLADE_COUNT);
45
+ }
46
+
47
+ /** Full geometric product of two multivectors: a * b. */
48
+ export function geometricProductMV(a: Multivector4, b: Multivector4): Multivector4 {
49
+ const out = mvZero();
50
+ for (let i = 0; i < BLADE_COUNT; i++) {
51
+ const ai = a[i];
52
+ if (!ai) continue;
53
+ for (let j = 0; j < BLADE_COUNT; j++) {
54
+ const bj = b[j];
55
+ if (!bj) continue;
56
+ const blade = i ^ j;
57
+ out[blade] += reorderingSign(i, j) * ai * bj;
58
+ }
59
+ }
60
+ return out;
61
+ }
62
+
63
+ /** Outer (wedge) product: nonzero only for blade pairs that share no basis vector. */
64
+ export function wedgeProductMV(a: Multivector4, b: Multivector4): Multivector4 {
65
+ const out = mvZero();
66
+ for (let i = 0; i < BLADE_COUNT; i++) {
67
+ const ai = a[i];
68
+ if (!ai) continue;
69
+ for (let j = 0; j < BLADE_COUNT; j++) {
70
+ const bj = b[j];
71
+ if (!bj || (i & j) !== 0) continue;
72
+ const blade = i ^ j;
73
+ out[blade] += reorderingSign(i, j) * ai * bj;
74
+ }
75
+ }
76
+ return out;
77
+ }
78
+
79
+ // Basis-vector and basis-bivector blade indices.
80
+ export const E1 = 1 << 0;
81
+ export const E2 = 1 << 1;
82
+ export const E3 = 1 << 2;
83
+ export const E4 = 1 << 3;
84
+ export const E12 = E1 | E2;
85
+ export const E13 = E1 | E3;
86
+ export const E14 = E1 | E4;
87
+ export const E23 = E2 | E3;
88
+ export const E24 = E2 | E4;
89
+ export const E34 = E3 | E4;
90
+ export const PSEUDOSCALAR4 = E1 | E2 | E3 | E4;
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Combinatorics — exact counting mathematics: factorials, binomial/multinomial
3
+ * coefficients, Catalan/Stirling/Bell numbers, integer partitions, and
4
+ * derangements. The natural counting-math sibling to {@link Permutation},
5
+ * {@link Cycle}, and {@link GroupTheory} — none of those previously had a way
6
+ * to just compute `nCr`.
7
+ *
8
+ * Everything here is `bigint`-based (matching {@link NumberTheory}'s
9
+ * convention) since factorial-scale values exceed `Number.MAX_SAFE_INTEGER`
10
+ * well before n reaches 20.
11
+ */
12
+
13
+ import { combinations, combinationsWithReplacement, permutations, product } from "./CombinatoricsGenerators.ts";
14
+
15
+ const big = (x: bigint | number): bigint => (typeof x === "bigint" ? x : BigInt(x));
16
+
17
+ export class Combinatorics {
18
+ /** `n!` */
19
+ static factorial(n: bigint | number): bigint {
20
+ const num = big(n);
21
+ if (num < 0n) throw new RangeError("factorial is undefined for negative numbers");
22
+ let result = 1n;
23
+ for (let i = 2n; i <= num; i++) result *= i;
24
+ return result;
25
+ }
26
+
27
+ /** `nCk` — the number of ways to choose `k` items from `n` (order doesn't matter). */
28
+ static binomial(n: bigint | number, k: bigint | number): bigint {
29
+ const N = big(n);
30
+ const K = big(k);
31
+ if (K < 0n || K > N) return 0n;
32
+ const kk = K > N - K ? N - K : K; // symmetry: nCk = nC(n-k)
33
+ let result = 1n;
34
+ for (let i = 0n; i < kk; i++) result = (result * (N - i)) / (i + 1n);
35
+ return result;
36
+ }
37
+
38
+ /** `nPk` — the number of ordered arrangements of `k` items chosen from `n`. */
39
+ static permutationsCount(n: bigint | number, k: bigint | number): bigint {
40
+ const N = big(n);
41
+ const K = big(k);
42
+ if (K < 0n || K > N) return 0n;
43
+ let result = 1n;
44
+ for (let i = 0n; i < K; i++) result *= N - i;
45
+ return result;
46
+ }
47
+
48
+ /** `n! / (k1! · k2! · … · km!)`, the number of ways to partition `n` labeled items into groups of the given sizes. */
49
+ static multinomial(n: bigint | number, groupSizes: Array<bigint | number>): bigint {
50
+ const N = big(n);
51
+ const sizes = groupSizes.map(big);
52
+ const sum = sizes.reduce((s, k) => s + k, 0n);
53
+ if (sum !== N) throw new RangeError("multinomial: group sizes must sum to n");
54
+ let result = Combinatorics.factorial(N);
55
+ for (const k of sizes) result /= Combinatorics.factorial(k);
56
+ return result;
57
+ }
58
+
59
+ /** The `n`th Catalan number: `C(2n, n) / (n + 1)` — counts balanced parenthesizations, binary trees, etc. */
60
+ static catalan(n: bigint | number): bigint {
61
+ const N = big(n);
62
+ if (N < 0n) throw new RangeError("catalan is undefined for negative numbers");
63
+ return Combinatorics.binomial(2n * N, N) / (N + 1n);
64
+ }
65
+
66
+ /** Stirling numbers of the second kind `S(n, k)` — ways to partition an `n`-set into `k` nonempty unlabeled subsets. */
67
+ static stirlingSecond(n: bigint | number, k: bigint | number): bigint {
68
+ const N = Number(big(n));
69
+ const K = Number(big(k));
70
+ if (N < 0 || K < 0) throw new RangeError("stirlingSecond is undefined for negative arguments");
71
+ if (K > N) return 0n;
72
+ // dp[j] holds S(i, j) for the current row i, built bottom-up.
73
+ const dp: bigint[] = new Array(N + 1).fill(0n);
74
+ dp[0] = 1n; // S(0, 0) = 1
75
+ for (let i = 1; i <= N; i++) {
76
+ for (let j = Math.min(i, K); j >= 1; j--) {
77
+ dp[j] = BigInt(j) * dp[j] + dp[j - 1];
78
+ }
79
+ dp[0] = 0n; // S(i, 0) = 0 for i > 0
80
+ }
81
+ return dp[K];
82
+ }
83
+
84
+ /** Unsigned Stirling numbers of the first kind `c(n, k)` — permutations of `n` elements with exactly `k` cycles. */
85
+ static stirlingFirstUnsigned(n: bigint | number, k: bigint | number): bigint {
86
+ const N = Number(big(n));
87
+ const K = Number(big(k));
88
+ if (N < 0 || K < 0) throw new RangeError("stirlingFirstUnsigned is undefined for negative arguments");
89
+ if (K > N) return 0n;
90
+ const dp: bigint[] = new Array(N + 1).fill(0n);
91
+ dp[0] = 1n; // c(0, 0) = 1
92
+ for (let i = 1; i <= N; i++) {
93
+ for (let j = Math.min(i, K); j >= 1; j--) {
94
+ dp[j] = BigInt(i - 1) * dp[j] + dp[j - 1];
95
+ }
96
+ dp[0] = 0n; // c(i, 0) = 0 for i > 0
97
+ }
98
+ return dp[K];
99
+ }
100
+
101
+ /**
102
+ * Full row of Stirling numbers of the second kind: `S(n, j)` for every `j` from `0` to `n`,
103
+ * built in a single `O(n²)` DP pass (same recurrence as {@link stirlingSecond}, but computed
104
+ * once for all columns instead of re-running the whole table per column).
105
+ */
106
+ private static stirlingSecondRow(n: number): bigint[] {
107
+ const dp: bigint[] = new Array(n + 1).fill(0n);
108
+ dp[0] = 1n; // S(0, 0) = 1
109
+ for (let i = 1; i <= n; i++) {
110
+ for (let j = i; j >= 1; j--) {
111
+ dp[j] = BigInt(j) * dp[j] + dp[j - 1];
112
+ }
113
+ dp[0] = 0n; // S(i, 0) = 0 for i > 0
114
+ }
115
+ return dp;
116
+ }
117
+
118
+ /** The `n`th Bell number — the total number of partitions of an `n`-set (`Σₖ S(n, k)`). */
119
+ static bell(n: bigint | number): bigint {
120
+ const N = Number(big(n));
121
+ if (N < 0) throw new RangeError("bell is undefined for negative numbers");
122
+ const row = Combinatorics.stirlingSecondRow(N);
123
+ let total = 0n;
124
+ for (let k = 0; k <= N; k++) total += row[k] as bigint;
125
+ return total;
126
+ }
127
+
128
+ /** The number of integer partitions of `n` (ways to write `n` as a sum of positive integers, order irrelevant). */
129
+ static partitionCount(n: bigint | number): bigint {
130
+ const N = Number(big(n));
131
+ if (N < 0) throw new RangeError("partitionCount is undefined for negative numbers");
132
+ const dp: bigint[] = new Array(N + 1).fill(0n);
133
+ dp[0] = 1n;
134
+ for (let part = 1; part <= N; part++) {
135
+ for (let total = part; total <= N; total++) dp[total] += dp[total - part];
136
+ }
137
+ return dp[N];
138
+ }
139
+
140
+ /** The number of derangements of `n` items (permutations with no fixed point), aka the subfactorial `!n`. */
141
+ static derangements(n: bigint | number): bigint {
142
+ const N = Number(big(n));
143
+ if (N < 0) throw new RangeError("derangements is undefined for negative numbers");
144
+ if (N === 0) return 1n;
145
+ if (N === 1) return 0n;
146
+ let prev2 = 1n; // !0
147
+ let prev1 = 0n; // !1
148
+ for (let i = 2; i <= N; i++) {
149
+ const cur = BigInt(i - 1) * (prev1 + prev2);
150
+ prev2 = prev1;
151
+ prev1 = cur;
152
+ }
153
+ return prev1;
154
+ }
155
+
156
+ // ---- enumeration ---------------------------------------------------------
157
+ // The counting functions above answer "how many?"; these yield the objects
158
+ // themselves. See CombinatoricsGenerators.ts for the algorithms.
159
+
160
+ /**
161
+ * Cartesian product of the given iterables, last varying fastest.
162
+ * Counting counterpart: multiply the pool sizes.
163
+ */
164
+ static product = product;
165
+
166
+ /**
167
+ * `r`-length permutations of `iterable` (default `r` = pool size).
168
+ * Counting counterpart: {@link Combinatorics.permutationsCount}.
169
+ */
170
+ static permutations = permutations;
171
+
172
+ /**
173
+ * `r`-length combinations of `iterable`, in input order.
174
+ * Counting counterpart: {@link Combinatorics.binomial}.
175
+ */
176
+ static combinations = combinations;
177
+
178
+ /**
179
+ * `r`-length combinations of `iterable` allowing repeats.
180
+ * Counting counterpart: `binomial(n + r - 1, r)`.
181
+ */
182
+ static combinationsWithReplacement = combinationsWithReplacement;
183
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Combinatorial generators: `product`, `permutations`, `combinations`,
3
+ * `combinationsWithReplacement` — ports of the iterative equivalent-code
4
+ * algorithms documented in CPython's itertools docs (chosen over a recursive
5
+ * approach to avoid recursion-depth limits on large inputs).
6
+ *
7
+ * These are the **enumerating** counterpart to {@link Combinatorics}'s
8
+ * **counting** functions: `Combinatorics.permutationsCount(n, r)` tells you how
9
+ * many there are, `Combinatorics.permutations(iterable, r)` yields them. They
10
+ * are surfaced as static methods on {@link Combinatorics} so both faces of each
11
+ * concept sit together.
12
+ *
13
+ * These are inherently "materialize fully, then compute" algorithms — CPython's
14
+ * own docs note that `product()` completely consumes its inputs before running,
15
+ * so a fully lazy formulation isn't available. Pools are therefore buffered in
16
+ * memory; enumerating a large product is bounded by that, not by laziness.
17
+ *
18
+ * Originally written for the library now published as `@johnhenry/iteration`;
19
+ * moved here because enumeration is mathematics rather than stream plumbing.
20
+ * The async duals stayed behind — they only added a "collect the source, then
21
+ * run this same sync generator" wrapper, and @johnhenry/math is deliberately
22
+ * synchronous throughout.
23
+ */
24
+
25
+ function* productFromPools<T>(pools: T[][]): Generator<T[]> {
26
+ if (pools.some((pool) => pool.length === 0)) return;
27
+ const indices = pools.map(() => 0);
28
+ while (true) {
29
+ yield pools.map((pool, i) => pool[indices[i]]);
30
+ let i = indices.length - 1;
31
+ while (i >= 0) {
32
+ indices[i]++;
33
+ if (indices[i] < pools[i].length) break;
34
+ indices[i] = 0;
35
+ i--;
36
+ }
37
+ if (i < 0) return;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Cartesian product of the given iterables. Mirrors Python's
43
+ * itertools.product(*iterables) -- the last iterable varies fastest.
44
+ */
45
+ export function* product<T>(...iterables: Array<Iterable<T>>): Generator<T[]> {
46
+ yield* productFromPools(iterables.map((it) => [...it]));
47
+ }
48
+
49
+ function* permutationsFromPool<T>(pool: T[], r?: number | null): Generator<T[]> {
50
+ const n = pool.length;
51
+ if (r === undefined || r === null) r = n;
52
+ if (r > n || r < 0) return;
53
+ const indices = Array.from({ length: n }, (_, i) => i);
54
+ const cycles = Array.from({ length: r }, (_, i) => n - i);
55
+ yield indices.slice(0, r).map((i) => pool[i]);
56
+ if (n === 0) return;
57
+ outer: while (true) {
58
+ for (let i = r - 1; i >= 0; i--) {
59
+ cycles[i] -= 1;
60
+ if (cycles[i] === 0) {
61
+ const tail = indices.slice(i + 1).concat(indices.slice(i, i + 1));
62
+ indices.splice(i, indices.length - i, ...tail);
63
+ cycles[i] = n - i;
64
+ } else {
65
+ const j = cycles[i];
66
+ const a = indices[i];
67
+ const b = indices[n - j];
68
+ indices[i] = b;
69
+ indices[n - j] = a;
70
+ yield indices.slice(0, r).map((idx) => pool[idx]);
71
+ continue outer;
72
+ }
73
+ }
74
+ return;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * r-length permutations of `iterable` (default r = pool length). Mirrors
80
+ * Python's itertools.permutations(iterable, r).
81
+ */
82
+ export function* permutations<T>(iterable: Iterable<T>, r?: number): Generator<T[]> {
83
+ yield* permutationsFromPool([...iterable], r);
84
+ }
85
+
86
+ function* combinationsFromPool<T>(pool: T[], r: number): Generator<T[]> {
87
+ const n = pool.length;
88
+ if (r > n || r < 0) return;
89
+ const indices = Array.from({ length: r }, (_, i) => i);
90
+ yield indices.map((i) => pool[i]);
91
+ while (true) {
92
+ let i = -1;
93
+ for (let k = r - 1; k >= 0; k--) {
94
+ if (indices[k] !== k + n - r) {
95
+ i = k;
96
+ break;
97
+ }
98
+ }
99
+ if (i === -1) return;
100
+ indices[i] += 1;
101
+ for (let j = i + 1; j < r; j++) {
102
+ indices[j] = indices[j - 1] + 1;
103
+ }
104
+ yield indices.map((idx) => pool[idx]);
105
+ }
106
+ }
107
+
108
+ /**
109
+ * r-length combinations of `iterable`, in sorted (input) order, without
110
+ * replacement. Mirrors Python's itertools.combinations(iterable, r).
111
+ */
112
+ export function* combinations<T>(iterable: Iterable<T>, r: number): Generator<T[]> {
113
+ yield* combinationsFromPool([...iterable], r);
114
+ }
115
+
116
+ function* combinationsWithReplacementFromPool<T>(pool: T[], r: number): Generator<T[]> {
117
+ const n = pool.length;
118
+ if (n === 0 && r > 0) return;
119
+ const indices: number[] = new Array(r).fill(0);
120
+ yield indices.map((i) => pool[i]);
121
+ while (true) {
122
+ let i = -1;
123
+ for (let k = r - 1; k >= 0; k--) {
124
+ if (indices[k] !== n - 1) {
125
+ i = k;
126
+ break;
127
+ }
128
+ }
129
+ if (i === -1) return;
130
+ const value = indices[i] + 1;
131
+ for (let j = i; j < r; j++) {
132
+ indices[j] = value;
133
+ }
134
+ yield indices.map((idx) => pool[idx]);
135
+ }
136
+ }
137
+
138
+ /**
139
+ * r-length combinations of `iterable`, with replacement. Mirrors Python's
140
+ * itertools.combinations_with_replacement(iterable, r).
141
+ */
142
+ export function* combinationsWithReplacement<T>(iterable: Iterable<T>, r: number): Generator<T[]> {
143
+ yield* combinationsWithReplacementFromPool([...iterable], r);
144
+ }
@@ -0,0 +1,162 @@
1
+ import { ComplexNumber } from "./ComplexNumber.ts";
2
+ import { integrateN } from "./RealMath.ts";
3
+ import { Vector } from "./Vector.ts";
4
+ import { VectorUtils } from "./VectorUtils.ts";
5
+
6
+ /**
7
+ * ComplexMath — the remaining complex-valued operations that don't belong on a
8
+ * single {@link ComplexNumber}: vector algebra over `Vector<ComplexNumber>`
9
+ * and probability distributions. Plain exported functions rather than a
10
+ * `static` method bag (same "namespace class" fix as {@link RealMath}).
11
+ *
12
+ * Everything per-value (arithmetic, exponentials/logs, trig, comparisons) is
13
+ * now a fluent {@link ComplexNumber} instance method. Matrix operations were
14
+ * dropped — `Structure.complexField()` already provides generic linear
15
+ * algebra. Order-dependent statistics (median/percentile/variance/etc.) were
16
+ * dropped with them: they had no test coverage here and don't generalize to
17
+ * complex numbers (there's no total order on the complex plane to found
18
+ * percentiles on) — see {@link Statistics} for the real-only equivalents.
19
+ *
20
+ * Ported from Mallory's ActionScript `ComplexMath`; see `ComplexNumber`'s doc
21
+ * comment for the bug fixes inherited from that port (directed-infinity
22
+ * division, zero-base `power`, etc.). `normalDistribution`'s AS3 bug (the
23
+ * reciprocal was taken of the whole PDF expression, flipping the exponential's
24
+ * sign) is fixed here too.
25
+ */
26
+
27
+ type CNInput = ComplexNumber | number;
28
+ type CVec = Vector<ComplexNumber>;
29
+ type CUnary = (x: ComplexNumber) => ComplexNumber;
30
+
31
+ const cn = (x: CNInput): ComplexNumber => (x instanceof ComplexNumber ? x : new ComplexNumber(x));
32
+
33
+ // -- vectors ------------------------------------------------------------------
34
+
35
+ export function addVector(alpha: CVec, beta: CVec): CVec {
36
+ return VectorUtils.combine(alpha, beta, (a, b) => a.add(b)) as CVec;
37
+ }
38
+
39
+ export function scaleVector(alpha: CVec, scalar: CNInput): CVec {
40
+ const s = cn(scalar);
41
+ return VectorUtils.transform(alpha, (x) => s.multiply(x));
42
+ }
43
+
44
+ export function negativeVector(alpha: CVec): CVec {
45
+ return scaleVector(alpha, -1);
46
+ }
47
+
48
+ export function subtractVector(alpha: CVec, beta: CVec): CVec {
49
+ return addVector(alpha, negativeVector(beta));
50
+ }
51
+
52
+ export function dotProduct(alpha: CVec, beta: CVec): ComplexNumber {
53
+ const collapsable = VectorUtils.combine(alpha, beta, (a, b) => a.multiply(b), ComplexNumber.Zero) as CVec;
54
+ return VectorUtils.collapse(collapsable, (a, b) => a.add(b)) as ComplexNumber;
55
+ }
56
+
57
+ /** 3D cross product (bug fix: index 2 for z, presence not truthiness — as in RealMath). */
58
+ export function crossProduct(alpha: CVec, beta: CVec): CVec {
59
+ const a = [ComplexNumber.Zero, ComplexNumber.Zero, ComplexNumber.Zero];
60
+ const b = [ComplexNumber.Zero, ComplexNumber.Zero, ComplexNumber.Zero];
61
+ for (let idx = 0; idx < 3; idx++) {
62
+ if (idx < alpha.length && alpha[idx] != null) a[idx] = alpha[idx] as ComplexNumber;
63
+ if (idx < beta.length && beta[idx] != null) b[idx] = beta[idx] as ComplexNumber;
64
+ }
65
+ const i = a[1].multiply(b[2]).subtract(a[2].multiply(b[1]));
66
+ const j = a[2].multiply(b[0]).subtract(a[0].multiply(b[2]));
67
+ const k = a[0].multiply(b[1]).subtract(a[1].multiply(b[0]));
68
+ return Vector.fromArray([i, j, k]);
69
+ }
70
+
71
+ export function kroneckerProduct(A: CVec, B: CVec): CVec {
72
+ let kronecker = VectorUtils.constantVector(A.length, null as unknown) as Vector<unknown>;
73
+ kronecker = VectorUtils.fillByIndex(kronecker, (i) => scaleVector(B, A[i] as ComplexNumber));
74
+ return VectorUtils.flattenSDLevels(kronecker, 2) as CVec;
75
+ }
76
+
77
+ export function pNorm(alpha: CVec, norm: CNInput = 2): ComplexNumber {
78
+ const p = cn(norm);
79
+ if (p.value === 0) {
80
+ const magnitudes = VectorUtils.transform(alpha, (x) => new ComplexNumber(x.magnitude()));
81
+ return VectorUtils.collapse(magnitudes, (a, b) => (a.magCompare(b) >= 0 ? a : b)) as ComplexNumber;
82
+ }
83
+ const summable = VectorUtils.transform(alpha, (x) => new ComplexNumber(x.magnitude()).power(p.value));
84
+ const sum = VectorUtils.collapse(summable, (a, b) => a.add(b), ComplexNumber.Zero) as ComplexNumber;
85
+ return sum.power(1 / p.value);
86
+ }
87
+
88
+ export function distanceVector(alpha: CVec, beta: CVec, norm: CNInput = 2): ComplexNumber {
89
+ return pNorm(subtractVector(alpha, beta), norm);
90
+ }
91
+
92
+ export function magnitudeVector(alpha: CVec): ComplexNumber {
93
+ return pNorm(alpha, 2);
94
+ }
95
+
96
+ export function angleBetween(alpha: CVec, beta: CVec): ComplexNumber {
97
+ return dotProduct(alpha, beta)
98
+ .divide(magnitudeVector(alpha).multiply(magnitudeVector(beta)))
99
+ .arcCosine();
100
+ }
101
+
102
+ // -- probability ----------------------------------------------------------------
103
+
104
+ export function zScore(x: CNInput, mean: CNInput = 0, sDev: CNInput = 1): ComplexNumber {
105
+ return cn(x).subtract(mean).divide(sDev);
106
+ }
107
+
108
+ export function invertZScore(z: CNInput, mean: CNInput = 0, sDev: CNInput = 1): ComplexNumber {
109
+ return cn(z).multiply(sDev).add(mean);
110
+ }
111
+
112
+ /** Normal PDF closure over complex arguments. */
113
+ export function normalDistribution(mean: CNInput, sDev: CNInput): CUnary {
114
+ return (x: ComplexNumber) =>
115
+ cn(sDev)
116
+ .multiply(Math.sqrt(2 * Math.PI))
117
+ .reciprocal()
118
+ .multiply(ComplexNumber.E.power(zScore(x, mean, sDev).square().multiply(-0.5)));
119
+ }
120
+
121
+ export function standardNormalDistribution(x: CNInput): ComplexNumber {
122
+ return normalDistribution(0, 1)(cn(x));
123
+ }
124
+
125
+ /**
126
+ * Numeric integral of a complex-valued function over a real interval, via the
127
+ * real {@link integrateN} applied component-wise (bug fix: same midpoint-rule
128
+ * fix as `RealMath.integrateN`).
129
+ */
130
+ function integrateComplexN(
131
+ unaryOperation: CUnary,
132
+ lowerLimit: number,
133
+ upperLimit: number,
134
+ interval = 0.0001,
135
+ ): ComplexNumber {
136
+ const re = integrateN((t) => unaryOperation(new ComplexNumber(t, 0)).value, lowerLimit, upperLimit, interval);
137
+ const im = integrateN((t) => unaryOperation(new ComplexNumber(t, 0)).iValue, lowerLimit, upperLimit, interval);
138
+ return new ComplexNumber(re, im);
139
+ }
140
+
141
+ export function normalProbability(
142
+ x: CNInput,
143
+ mean: CNInput,
144
+ sDev: CNInput,
145
+ negativeInfinityApproximation = -5,
146
+ integrationInterval = 0.0001,
147
+ ): ComplexNumber {
148
+ return integrateComplexN(
149
+ normalDistribution(mean, sDev),
150
+ negativeInfinityApproximation,
151
+ cn(x).value,
152
+ integrationInterval,
153
+ );
154
+ }
155
+
156
+ export function standardNormalProbability(
157
+ z: CNInput,
158
+ negativeInfinityApproximation = -5,
159
+ interval = 0.0001,
160
+ ): ComplexNumber {
161
+ return integrateComplexN(standardNormalDistribution, negativeInfinityApproximation, cn(z).value, interval);
162
+ }