( expr: Expression, variable: string )
| 970 | |
| 971 | for (let j = 0; j < cols; j++) { |
| 972 | m[row][j] = m[row][j] * pivotVal - factor * m[currentRow][j]; |
| 973 | } |
| 974 | } |
| 975 | |
| 976 | currentRow++; |
| 977 | } |
| 978 | |
| 979 | // Consistency check: after elimination, a row whose coefficient entries are |
| 980 | // all zero but whose RHS is nonzero encodes 0 = c ≠ 0 — the system has no |
| 981 | // solution. Without this the back-substitution below silently ignores such |
| 982 | // rows and returns a spurious (often all-zero) "solution", which made |
| 983 | // partialFraction collapse e.g. 1/(x·(x²+x)) to 0 (the factors x and x²+x |
| 984 | // share the root 0, so the irreducible-factor template is inconsistent). |
| 985 | for (let row = 0; row < rows; row++) { |
| 986 | let allZero = true; |
| 987 | for (let col = 0; col < numVars; col++) { |
| 988 | if (m[row][col] !== 0n) { |
| 989 | allZero = false; |
| 990 | break; |
| 991 | } |
| 992 | } |
| 993 | if (allZero && m[row][cols - 1] !== 0n) return null; // inconsistent |
| 994 | } |
| 995 | |
| 996 | // Back substitution: extract solutions as reduced [numerator, denominator] |
| 997 | const solution: [bigint, bigint][] = new Array(numVars); |
| 998 | for (let col = 0; col < numVars; col++) { |
| 999 | const pr = pivotRow[col]; |
| 1000 | if (pr === -1) { |
| 1001 | // Free variable — set to 0 |
| 1002 | solution[col] = [0n, 1n]; |
| 1003 | continue; |
| 1004 | } |
| 1005 | |
| 1006 | let num = m[pr][cols - 1]; |
| 1007 | let den = m[pr][col]; |
| 1008 | if (den === 0n) return null; // Inconsistent |
| 1009 | |
| 1010 | // Normalize sign onto the numerator and reduce the fraction |
| 1011 | if (den < 0n) { |
| 1012 | num = -num; |
| 1013 | den = -den; |
| 1014 | } |
| 1015 | const g = gcd(num < 0n ? -num : num, den); |
| 1016 | solution[col] = [num / g, den / g]; |
| 1017 | } |
| 1018 | |
| 1019 | return solution; |
| 1020 | } |
| 1021 | |
| 1022 | /** GCD of two non-negative bigints (1 for gcd(0, 0), to avoid /0). */ |
| 1023 | function gcd(a: bigint, b: bigint): bigint { |
| 1024 | while (b) { |
| 1025 | [a, b] = [b, a % b]; |
| 1026 | } |
| 1027 | return a || 1n; |
| 1028 | } |
| 1029 |
no test coverage detected