@stevenvo780/st-lang 4.12.0 → 4.14.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/dist/reasoning/datalog/index.d.ts +131 -0
- package/dist/reasoning/datalog/index.d.ts.map +1 -0
- package/dist/reasoning/datalog/index.js +706 -0
- package/dist/reasoning/datalog/index.js.map +1 -0
- package/dist/reasoning/galois-fields/index.d.ts +29 -0
- package/dist/reasoning/galois-fields/index.d.ts.map +1 -0
- package/dist/reasoning/galois-fields/index.js +522 -0
- package/dist/reasoning/galois-fields/index.js.map +1 -0
- package/dist/reasoning/hoare-logic/index.d.ts +130 -0
- package/dist/reasoning/hoare-logic/index.d.ts.map +1 -0
- package/dist/reasoning/hoare-logic/index.js +535 -0
- package/dist/reasoning/hoare-logic/index.js.map +1 -0
- package/dist/reasoning/lattice/index.d.ts +165 -0
- package/dist/reasoning/lattice/index.d.ts.map +1 -0
- package/dist/reasoning/lattice/index.js +587 -0
- package/dist/reasoning/lattice/index.js.map +1 -0
- package/dist/reasoning/model-checking/index.d.ts +113 -0
- package/dist/reasoning/model-checking/index.d.ts.map +1 -0
- package/dist/reasoning/model-checking/index.js +786 -0
- package/dist/reasoning/model-checking/index.js.map +1 -0
- package/dist/reasoning/polynomial-ring/index.d.ts +30 -0
- package/dist/reasoning/polynomial-ring/index.d.ts.map +1 -0
- package/dist/reasoning/polynomial-ring/index.js +797 -0
- package/dist/reasoning/polynomial-ring/index.js.map +1 -0
- package/dist/reasoning/separation-logic/index.d.ts +190 -0
- package/dist/reasoning/separation-logic/index.d.ts.map +1 -0
- package/dist/reasoning/separation-logic/index.js +758 -0
- package/dist/reasoning/separation-logic/index.js.map +1 -0
- package/dist/reasoning/universal-algebra/index.d.ts +196 -0
- package/dist/reasoning/universal-algebra/index.d.ts.map +1 -0
- package/dist/reasoning/universal-algebra/index.js +865 -0
- package/dist/reasoning/universal-algebra/index.js.map +1 -0
- package/dist/tests/reasoning/datalog/datalog.test.d.ts +2 -0
- package/dist/tests/reasoning/datalog/datalog.test.d.ts.map +1 -0
- package/dist/tests/reasoning/datalog/datalog.test.js +333 -0
- package/dist/tests/reasoning/datalog/datalog.test.js.map +1 -0
- package/dist/tests/reasoning/galois-fields/galois-fields.test.d.ts +2 -0
- package/dist/tests/reasoning/galois-fields/galois-fields.test.d.ts.map +1 -0
- package/dist/tests/reasoning/galois-fields/galois-fields.test.js +226 -0
- package/dist/tests/reasoning/galois-fields/galois-fields.test.js.map +1 -0
- package/dist/tests/reasoning/hoare-logic/hoare-logic.test.d.ts +2 -0
- package/dist/tests/reasoning/hoare-logic/hoare-logic.test.d.ts.map +1 -0
- package/dist/tests/reasoning/hoare-logic/hoare-logic.test.js +340 -0
- package/dist/tests/reasoning/hoare-logic/hoare-logic.test.js.map +1 -0
- package/dist/tests/reasoning/lattice/lattice.test.d.ts +2 -0
- package/dist/tests/reasoning/lattice/lattice.test.d.ts.map +1 -0
- package/dist/tests/reasoning/lattice/lattice.test.js +238 -0
- package/dist/tests/reasoning/lattice/lattice.test.js.map +1 -0
- package/dist/tests/reasoning/model-checking/model-checking.test.d.ts +2 -0
- package/dist/tests/reasoning/model-checking/model-checking.test.d.ts.map +1 -0
- package/dist/tests/reasoning/model-checking/model-checking.test.js +222 -0
- package/dist/tests/reasoning/model-checking/model-checking.test.js.map +1 -0
- package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.d.ts +2 -0
- package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.d.ts.map +1 -0
- package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.js +230 -0
- package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.js.map +1 -0
- package/dist/tests/reasoning/separation-logic/separation-logic.test.d.ts +2 -0
- package/dist/tests/reasoning/separation-logic/separation-logic.test.d.ts.map +1 -0
- package/dist/tests/reasoning/separation-logic/separation-logic.test.js +311 -0
- package/dist/tests/reasoning/separation-logic/separation-logic.test.js.map +1 -0
- package/dist/tests/reasoning/universal-algebra/universal-algebra.test.d.ts +2 -0
- package/dist/tests/reasoning/universal-algebra/universal-algebra.test.d.ts.map +1 -0
- package/dist/tests/reasoning/universal-algebra/universal-algebra.test.js +289 -0
- package/dist/tests/reasoning/universal-algebra/universal-algebra.test.js.map +1 -0
- package/dist/tests/type-theory/lambda-cube/lambda-cube.test.d.ts +2 -0
- package/dist/tests/type-theory/lambda-cube/lambda-cube.test.d.ts.map +1 -0
- package/dist/tests/type-theory/lambda-cube/lambda-cube.test.js +266 -0
- package/dist/tests/type-theory/lambda-cube/lambda-cube.test.js.map +1 -0
- package/dist/type-theory/lambda-cube/erase.d.ts +26 -0
- package/dist/type-theory/lambda-cube/erase.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/erase.js +68 -0
- package/dist/type-theory/lambda-cube/erase.js.map +1 -0
- package/dist/type-theory/lambda-cube/examples.d.ts +59 -0
- package/dist/type-theory/lambda-cube/examples.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/examples.js +110 -0
- package/dist/type-theory/lambda-cube/examples.js.map +1 -0
- package/dist/type-theory/lambda-cube/index.d.ts +11 -0
- package/dist/type-theory/lambda-cube/index.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/index.js +64 -0
- package/dist/type-theory/lambda-cube/index.js.map +1 -0
- package/dist/type-theory/lambda-cube/normalize.d.ts +17 -0
- package/dist/type-theory/lambda-cube/normalize.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/normalize.js +134 -0
- package/dist/type-theory/lambda-cube/normalize.js.map +1 -0
- package/dist/type-theory/lambda-cube/rules.d.ts +26 -0
- package/dist/type-theory/lambda-cube/rules.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/rules.js +67 -0
- package/dist/type-theory/lambda-cube/rules.js.map +1 -0
- package/dist/type-theory/lambda-cube/typecheck.d.ts +20 -0
- package/dist/type-theory/lambda-cube/typecheck.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/typecheck.js +168 -0
- package/dist/type-theory/lambda-cube/typecheck.js.map +1 -0
- package/dist/type-theory/lambda-cube/types.d.ts +40 -0
- package/dist/type-theory/lambda-cube/types.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/types.js +192 -0
- package/dist/type-theory/lambda-cube/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,797 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// ST Polynomial Ring — polinomios sobre Z, Q (via Z) y Z/pZ.
|
|
4
|
+
// ============================================================
|
|
5
|
+
// Representación densa: `coefficients[i]` es el coeficiente de x^i,
|
|
6
|
+
// con `coefficients[0]` como término constante. El array se mantiene
|
|
7
|
+
// normalizado (sin ceros finales) salvo el polinomio cero `[0n]`.
|
|
8
|
+
//
|
|
9
|
+
// Cuando `modulus` está definido, todas las operaciones reducen
|
|
10
|
+
// coeficientes mod p (p primo) — útil para Berlekamp.
|
|
11
|
+
//
|
|
12
|
+
// Convenciones:
|
|
13
|
+
// • Factorización `factor(p)` devuelve factores con contenido
|
|
14
|
+
// entero positivo (primitive part) más una constante líder si
|
|
15
|
+
// la hay; el producto reconstruye `p`.
|
|
16
|
+
// • `gcd` sobre Z[x] devuelve un polinomio primitivo con líder
|
|
17
|
+
// positivo (gcd "monic-like" sobre Q[x]).
|
|
18
|
+
// • Resultantes y discriminantes se calculan vía subresultant
|
|
19
|
+
// prs cuando `modulus` está ausente.
|
|
20
|
+
// ============================================================
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.poly = poly;
|
|
23
|
+
exports.degree = degree;
|
|
24
|
+
exports.leadingCoefficient = leadingCoefficient;
|
|
25
|
+
exports.isZero = isZero;
|
|
26
|
+
exports.add = add;
|
|
27
|
+
exports.sub = sub;
|
|
28
|
+
exports.multiply = multiply;
|
|
29
|
+
exports.divmod = divmod;
|
|
30
|
+
exports.gcd = gcd;
|
|
31
|
+
exports.derivative = derivative;
|
|
32
|
+
exports.evaluate = evaluate;
|
|
33
|
+
exports.compose = compose;
|
|
34
|
+
exports.rationalRoots = rationalRoots;
|
|
35
|
+
exports.squareFree = squareFree;
|
|
36
|
+
exports.factor = factor;
|
|
37
|
+
exports.isIrreducible = isIrreducible;
|
|
38
|
+
exports.factorInZp = factorInZp;
|
|
39
|
+
exports.resultant = resultant;
|
|
40
|
+
exports.discriminant = discriminant;
|
|
41
|
+
// --- helpers internos --------------------------------------------------
|
|
42
|
+
function absBig(x) {
|
|
43
|
+
return x < 0n ? -x : x;
|
|
44
|
+
}
|
|
45
|
+
function gcdBig(a, b) {
|
|
46
|
+
let x = absBig(a);
|
|
47
|
+
let y = absBig(b);
|
|
48
|
+
while (y !== 0n) {
|
|
49
|
+
const t = y;
|
|
50
|
+
y = x % y;
|
|
51
|
+
x = t;
|
|
52
|
+
}
|
|
53
|
+
return x;
|
|
54
|
+
}
|
|
55
|
+
function modBig(a, m) {
|
|
56
|
+
const r = a % m;
|
|
57
|
+
return r < 0n ? r + m : r;
|
|
58
|
+
}
|
|
59
|
+
// Inverso modular (m primo) vía Fermat — usamos exponenciación rápida.
|
|
60
|
+
function modInvPrime(a, p) {
|
|
61
|
+
let base = modBig(a, p);
|
|
62
|
+
if (base === 0n) {
|
|
63
|
+
throw new Error('modInvPrime: 0 no tiene inverso');
|
|
64
|
+
}
|
|
65
|
+
let exp = p - 2n;
|
|
66
|
+
let result = 1n;
|
|
67
|
+
while (exp > 0n) {
|
|
68
|
+
if (exp & 1n)
|
|
69
|
+
result = (result * base) % p;
|
|
70
|
+
base = (base * base) % p;
|
|
71
|
+
exp >>= 1n;
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
function trim(coeffs, modulus) {
|
|
76
|
+
const out = coeffs.slice();
|
|
77
|
+
if (modulus !== undefined) {
|
|
78
|
+
for (let i = 0; i < out.length; i++) {
|
|
79
|
+
out[i] = modBig(out[i], modulus);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
while (out.length > 1 && out[out.length - 1] === 0n) {
|
|
83
|
+
out.pop();
|
|
84
|
+
}
|
|
85
|
+
// si todo cero, mantener [0n].
|
|
86
|
+
if (out.length === 0)
|
|
87
|
+
out.push(0n);
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
function makePoly(coeffs, modulus) {
|
|
91
|
+
const result = { coefficients: trim(coeffs, modulus) };
|
|
92
|
+
if (modulus !== undefined)
|
|
93
|
+
result.modulus = modulus;
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
function sameModulus(a, b) {
|
|
97
|
+
if (a.modulus === undefined && b.modulus === undefined)
|
|
98
|
+
return undefined;
|
|
99
|
+
if (a.modulus === undefined)
|
|
100
|
+
return b.modulus;
|
|
101
|
+
if (b.modulus === undefined)
|
|
102
|
+
return a.modulus;
|
|
103
|
+
if (a.modulus !== b.modulus) {
|
|
104
|
+
throw new Error('polynomial-ring: módulos incompatibles');
|
|
105
|
+
}
|
|
106
|
+
return a.modulus;
|
|
107
|
+
}
|
|
108
|
+
// --- API base ----------------------------------------------------------
|
|
109
|
+
function poly(coefficients, modulus) {
|
|
110
|
+
const asBig = coefficients.map((c) => (typeof c === 'bigint' ? c : BigInt(c)));
|
|
111
|
+
if (asBig.length === 0)
|
|
112
|
+
asBig.push(0n);
|
|
113
|
+
return makePoly(asBig, modulus);
|
|
114
|
+
}
|
|
115
|
+
function degree(p) {
|
|
116
|
+
// polinomio cero: degree -1 por convención (algunos usan -Infinity, elegimos -1
|
|
117
|
+
// para que sea un número finito en TS strict).
|
|
118
|
+
if (p.coefficients.length === 1 && p.coefficients[0] === 0n)
|
|
119
|
+
return -1;
|
|
120
|
+
return p.coefficients.length - 1;
|
|
121
|
+
}
|
|
122
|
+
function leadingCoefficient(p) {
|
|
123
|
+
const d = degree(p);
|
|
124
|
+
if (d < 0)
|
|
125
|
+
return 0n;
|
|
126
|
+
return p.coefficients[d];
|
|
127
|
+
}
|
|
128
|
+
function isZero(p) {
|
|
129
|
+
return degree(p) < 0;
|
|
130
|
+
}
|
|
131
|
+
// --- aritmética --------------------------------------------------------
|
|
132
|
+
function add(a, b) {
|
|
133
|
+
const m = sameModulus(a, b);
|
|
134
|
+
const len = Math.max(a.coefficients.length, b.coefficients.length);
|
|
135
|
+
const out = Array.from({ length: len }, () => 0n);
|
|
136
|
+
for (let i = 0; i < len; i++) {
|
|
137
|
+
const av = a.coefficients[i] ?? 0n;
|
|
138
|
+
const bv = b.coefficients[i] ?? 0n;
|
|
139
|
+
out[i] = av + bv;
|
|
140
|
+
}
|
|
141
|
+
return makePoly(out, m);
|
|
142
|
+
}
|
|
143
|
+
function sub(a, b) {
|
|
144
|
+
const m = sameModulus(a, b);
|
|
145
|
+
const len = Math.max(a.coefficients.length, b.coefficients.length);
|
|
146
|
+
const out = Array.from({ length: len }, () => 0n);
|
|
147
|
+
for (let i = 0; i < len; i++) {
|
|
148
|
+
const av = a.coefficients[i] ?? 0n;
|
|
149
|
+
const bv = b.coefficients[i] ?? 0n;
|
|
150
|
+
out[i] = av - bv;
|
|
151
|
+
}
|
|
152
|
+
return makePoly(out, m);
|
|
153
|
+
}
|
|
154
|
+
function multiply(a, b) {
|
|
155
|
+
const m = sameModulus(a, b);
|
|
156
|
+
if (isZero(a) || isZero(b))
|
|
157
|
+
return makePoly([0n], m);
|
|
158
|
+
const out = Array.from({ length: a.coefficients.length + b.coefficients.length - 1 }, () => 0n);
|
|
159
|
+
for (let i = 0; i < a.coefficients.length; i++) {
|
|
160
|
+
const ai = a.coefficients[i];
|
|
161
|
+
if (ai === 0n)
|
|
162
|
+
continue;
|
|
163
|
+
for (let j = 0; j < b.coefficients.length; j++) {
|
|
164
|
+
const bj = b.coefficients[j];
|
|
165
|
+
out[i + j] = out[i + j] + ai * bj;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return makePoly(out, m);
|
|
169
|
+
}
|
|
170
|
+
function scalarMul(p, k) {
|
|
171
|
+
return makePoly(p.coefficients.map((c) => c * k), p.modulus);
|
|
172
|
+
}
|
|
173
|
+
// --- división euclidiana ----------------------------------------------
|
|
174
|
+
// Sobre Z/pZ es exacta (campo). Sobre Z, sólo lo hacemos si el divisor
|
|
175
|
+
// es monic (líder ±1) o si la división resulta exacta sobre Z.
|
|
176
|
+
function divmod(a, b) {
|
|
177
|
+
const m = sameModulus(a, b);
|
|
178
|
+
if (isZero(b)) {
|
|
179
|
+
throw new Error('divmod: divisor cero');
|
|
180
|
+
}
|
|
181
|
+
if (degree(a) < degree(b)) {
|
|
182
|
+
return { quotient: makePoly([0n], m), remainder: makePoly(a.coefficients.slice(), m) };
|
|
183
|
+
}
|
|
184
|
+
const rem = a.coefficients.slice();
|
|
185
|
+
const divisor = b.coefficients;
|
|
186
|
+
const divisorDeg = degree(b);
|
|
187
|
+
const divisorLead = divisor[divisorDeg];
|
|
188
|
+
const quotient = Array.from({ length: degree(a) - divisorDeg + 1 }, () => 0n);
|
|
189
|
+
if (m !== undefined) {
|
|
190
|
+
const leadInv = modInvPrime(divisorLead, m);
|
|
191
|
+
for (let i = degree(a); i >= divisorDeg; i--) {
|
|
192
|
+
const coef = rem[i] !== undefined ? modBig(rem[i], m) : 0n;
|
|
193
|
+
if (coef === 0n)
|
|
194
|
+
continue;
|
|
195
|
+
const factor = modBig(coef * leadInv, m);
|
|
196
|
+
quotient[i - divisorDeg] = factor;
|
|
197
|
+
for (let j = 0; j <= divisorDeg; j++) {
|
|
198
|
+
rem[i - divisorDeg + j] = modBig((rem[i - divisorDeg + j] ?? 0n) - factor * divisor[j], m);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
quotient: makePoly(quotient, m),
|
|
203
|
+
remainder: makePoly(rem, m),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
// Sobre Z: requerir divisor monic (lead = ±1) o división exacta.
|
|
207
|
+
// Implementamos como sobre Q (con bigint racional), pero comprobamos
|
|
208
|
+
// que el cociente termine en Z; si no, lanzamos.
|
|
209
|
+
// Para evitar fracciones: si lead es ±1 funciona directo; si no,
|
|
210
|
+
// multiplicamos rem por la potencia adecuada y verificamos cero.
|
|
211
|
+
if (divisorLead !== 1n && divisorLead !== -1n) {
|
|
212
|
+
// Pseudo-división: multiplicar a por lead^(deg(a)-deg(b)+1) primero,
|
|
213
|
+
// luego dividir. Pero divmod debe devolver el cociente "real" sólo
|
|
214
|
+
// cuando es exacta. Si no es exacta, dejar remainder = a, quotient = 0.
|
|
215
|
+
// Caso típico: gcd usa pseudo-remainder.
|
|
216
|
+
const { quotient: q, remainder: r } = pseudoDivmodZ(a, b);
|
|
217
|
+
return { quotient: q, remainder: r };
|
|
218
|
+
}
|
|
219
|
+
// monic ±1 → divisor exacto entero.
|
|
220
|
+
for (let i = degree(a); i >= divisorDeg; i--) {
|
|
221
|
+
const coef = rem[i] ?? 0n;
|
|
222
|
+
if (coef === 0n)
|
|
223
|
+
continue;
|
|
224
|
+
const factor = coef / divisorLead;
|
|
225
|
+
quotient[i - divisorDeg] = factor;
|
|
226
|
+
for (let j = 0; j <= divisorDeg; j++) {
|
|
227
|
+
rem[i - divisorDeg + j] = (rem[i - divisorDeg + j] ?? 0n) - factor * divisor[j];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
quotient: makePoly(quotient),
|
|
232
|
+
remainder: makePoly(rem),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
// Pseudo-división sobre Z: encuentra q, r tales que lead(b)^d * a = q*b + r
|
|
236
|
+
// con deg(r) < deg(b). Devuelve r tal cual; el cociente se descarta para
|
|
237
|
+
// usos que sólo necesitan el remainder en cadena de Euclides.
|
|
238
|
+
function pseudoDivmodZ(a, b) {
|
|
239
|
+
if (isZero(b))
|
|
240
|
+
throw new Error('pseudoDivmodZ: divisor cero');
|
|
241
|
+
if (degree(a) < degree(b)) {
|
|
242
|
+
return { quotient: makePoly([0n]), remainder: makePoly(a.coefficients.slice()) };
|
|
243
|
+
}
|
|
244
|
+
const da = degree(a);
|
|
245
|
+
const db = degree(b);
|
|
246
|
+
const lb = leadingCoefficient(b);
|
|
247
|
+
const power = lb ** BigInt(da - db + 1);
|
|
248
|
+
// Multiplicar a por power.
|
|
249
|
+
const aScaled = scalarMul(a, power);
|
|
250
|
+
const rem = aScaled.coefficients.slice();
|
|
251
|
+
const divisor = b.coefficients;
|
|
252
|
+
const quotient = Array.from({ length: da - db + 1 }, () => 0n);
|
|
253
|
+
for (let i = da; i >= db; i--) {
|
|
254
|
+
const coef = rem[i] ?? 0n;
|
|
255
|
+
if (coef === 0n)
|
|
256
|
+
continue;
|
|
257
|
+
if (coef % lb !== 0n) {
|
|
258
|
+
// No exacto bajo este pseudo-power: significa que la versión
|
|
259
|
+
// estándar requeriría más escalado. Para nuestro uso (gcd) basta
|
|
260
|
+
// con que el remainder sea reducido módulo lead; devolvemos rem
|
|
261
|
+
// tal cual sin avanzar más para no inflar coeficientes.
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
const factor = coef / lb;
|
|
265
|
+
quotient[i - db] = factor;
|
|
266
|
+
for (let j = 0; j <= db; j++) {
|
|
267
|
+
rem[i - db + j] = (rem[i - db + j] ?? 0n) - factor * divisor[j];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return { quotient: makePoly(quotient), remainder: makePoly(rem) };
|
|
271
|
+
}
|
|
272
|
+
// --- GCD ---------------------------------------------------------------
|
|
273
|
+
function contentZ(p) {
|
|
274
|
+
if (isZero(p))
|
|
275
|
+
return 0n;
|
|
276
|
+
let c = 0n;
|
|
277
|
+
for (const coef of p.coefficients) {
|
|
278
|
+
c = gcdBig(c, coef);
|
|
279
|
+
}
|
|
280
|
+
return c === 0n ? 1n : c;
|
|
281
|
+
}
|
|
282
|
+
function primitivePart(p) {
|
|
283
|
+
if (isZero(p))
|
|
284
|
+
return p;
|
|
285
|
+
const c = contentZ(p);
|
|
286
|
+
if (c === 1n) {
|
|
287
|
+
// Garantizar líder positivo: si el líder es negativo, negar todo.
|
|
288
|
+
if (leadingCoefficient(p) < 0n) {
|
|
289
|
+
return makePoly(p.coefficients.map((x) => -x));
|
|
290
|
+
}
|
|
291
|
+
return p;
|
|
292
|
+
}
|
|
293
|
+
const sign = leadingCoefficient(p) < 0n ? -1n : 1n;
|
|
294
|
+
return makePoly(p.coefficients.map((x) => (x / c) * sign));
|
|
295
|
+
}
|
|
296
|
+
function gcd(a, b) {
|
|
297
|
+
const m = sameModulus(a, b);
|
|
298
|
+
if (m !== undefined) {
|
|
299
|
+
// Euclides sobre Z/pZ — campo, división euclidiana exacta.
|
|
300
|
+
let x = makePoly(a.coefficients.slice(), m);
|
|
301
|
+
let y = makePoly(b.coefficients.slice(), m);
|
|
302
|
+
while (!isZero(y)) {
|
|
303
|
+
const { remainder } = divmod(x, y);
|
|
304
|
+
x = y;
|
|
305
|
+
y = remainder;
|
|
306
|
+
}
|
|
307
|
+
if (isZero(x))
|
|
308
|
+
return x;
|
|
309
|
+
// Hacer monic.
|
|
310
|
+
const leadInv = modInvPrime(leadingCoefficient(x), m);
|
|
311
|
+
return scalarMul(x, leadInv);
|
|
312
|
+
}
|
|
313
|
+
// Sobre Z[x]: usar subresultant PRS simplificada. Garantiza coeficientes
|
|
314
|
+
// razonables y termina. Devolvemos la parte primitiva con líder ≥ 0.
|
|
315
|
+
if (isZero(a))
|
|
316
|
+
return primitivePart(b);
|
|
317
|
+
if (isZero(b))
|
|
318
|
+
return primitivePart(a);
|
|
319
|
+
let x = makePoly(a.coefficients.slice());
|
|
320
|
+
let y = makePoly(b.coefficients.slice());
|
|
321
|
+
while (!isZero(y)) {
|
|
322
|
+
const { remainder } = pseudoDivmodZ(x, y);
|
|
323
|
+
x = y;
|
|
324
|
+
// Sacar contenido del remainder para evitar blow-up.
|
|
325
|
+
if (!isZero(remainder)) {
|
|
326
|
+
y = primitivePart(remainder);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
y = remainder;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return primitivePart(x);
|
|
333
|
+
}
|
|
334
|
+
// --- derivada, evaluación, composición --------------------------------
|
|
335
|
+
function derivative(p) {
|
|
336
|
+
if (degree(p) <= 0)
|
|
337
|
+
return makePoly([0n], p.modulus);
|
|
338
|
+
const out = Array.from({ length: p.coefficients.length - 1 }, () => 0n);
|
|
339
|
+
for (let i = 1; i < p.coefficients.length; i++) {
|
|
340
|
+
out[i - 1] = p.coefficients[i] * BigInt(i);
|
|
341
|
+
}
|
|
342
|
+
return makePoly(out, p.modulus);
|
|
343
|
+
}
|
|
344
|
+
function evaluate(p, x) {
|
|
345
|
+
// Horner.
|
|
346
|
+
let result = 0n;
|
|
347
|
+
for (let i = p.coefficients.length - 1; i >= 0; i--) {
|
|
348
|
+
result = result * x + p.coefficients[i];
|
|
349
|
+
if (p.modulus !== undefined)
|
|
350
|
+
result = modBig(result, p.modulus);
|
|
351
|
+
}
|
|
352
|
+
return result;
|
|
353
|
+
}
|
|
354
|
+
function compose(a, b) {
|
|
355
|
+
// a(b(x)) via Horner sobre b.
|
|
356
|
+
const m = sameModulus(a, b);
|
|
357
|
+
let result = makePoly([0n], m);
|
|
358
|
+
for (let i = a.coefficients.length - 1; i >= 0; i--) {
|
|
359
|
+
result = multiply(result, b);
|
|
360
|
+
result = add(result, makePoly([a.coefficients[i]], m));
|
|
361
|
+
}
|
|
362
|
+
return result;
|
|
363
|
+
}
|
|
364
|
+
// --- raíces racionales y factorización Q[x] ---------------------------
|
|
365
|
+
function divisorsAbs(n) {
|
|
366
|
+
const x = absBig(n);
|
|
367
|
+
if (x === 0n)
|
|
368
|
+
return [];
|
|
369
|
+
const out = [];
|
|
370
|
+
let d = 1n;
|
|
371
|
+
while (d * d <= x) {
|
|
372
|
+
if (x % d === 0n) {
|
|
373
|
+
out.push(d);
|
|
374
|
+
const q = x / d;
|
|
375
|
+
if (q !== d)
|
|
376
|
+
out.push(q);
|
|
377
|
+
}
|
|
378
|
+
d++;
|
|
379
|
+
}
|
|
380
|
+
out.sort((p, q) => (p < q ? -1 : p > q ? 1 : 0));
|
|
381
|
+
return out;
|
|
382
|
+
}
|
|
383
|
+
function reduceFraction(num, den) {
|
|
384
|
+
if (den === 0n)
|
|
385
|
+
throw new Error('reduceFraction: denominador cero');
|
|
386
|
+
const g = gcdBig(num, den);
|
|
387
|
+
let n = num / g;
|
|
388
|
+
let d = den / g;
|
|
389
|
+
if (d < 0n) {
|
|
390
|
+
n = -n;
|
|
391
|
+
d = -d;
|
|
392
|
+
}
|
|
393
|
+
return { num: n, den: d };
|
|
394
|
+
}
|
|
395
|
+
function rationalRoots(p) {
|
|
396
|
+
if (p.modulus !== undefined) {
|
|
397
|
+
throw new Error('rationalRoots: no aplica con módulo definido');
|
|
398
|
+
}
|
|
399
|
+
if (isZero(p) || degree(p) === 0)
|
|
400
|
+
return [];
|
|
401
|
+
// Sacar factor x^k si la constante es 0.
|
|
402
|
+
let working = p;
|
|
403
|
+
let zeroRoot = false;
|
|
404
|
+
while (working.coefficients[0] === 0n && degree(working) > 0) {
|
|
405
|
+
working = makePoly(working.coefficients.slice(1));
|
|
406
|
+
zeroRoot = true;
|
|
407
|
+
}
|
|
408
|
+
const a0 = working.coefficients[0];
|
|
409
|
+
const an = leadingCoefficient(working);
|
|
410
|
+
const seen = new Set();
|
|
411
|
+
const out = [];
|
|
412
|
+
if (zeroRoot) {
|
|
413
|
+
out.push({ num: 0n, den: 1n });
|
|
414
|
+
seen.add('0/1');
|
|
415
|
+
}
|
|
416
|
+
if (degree(working) === 0)
|
|
417
|
+
return out;
|
|
418
|
+
const pDivs = divisorsAbs(a0);
|
|
419
|
+
const qDivs = divisorsAbs(an);
|
|
420
|
+
for (const num of pDivs) {
|
|
421
|
+
for (const den of qDivs) {
|
|
422
|
+
for (const sign of [1n, -1n]) {
|
|
423
|
+
const candidate = reduceFraction(sign * num, den);
|
|
424
|
+
const key = `${candidate.num}/${candidate.den}`;
|
|
425
|
+
if (seen.has(key))
|
|
426
|
+
continue;
|
|
427
|
+
// Evaluar p(num/den): multiplicar por den^deg para enteros.
|
|
428
|
+
let value = 0n;
|
|
429
|
+
const denPow = 1n;
|
|
430
|
+
const coefs = working.coefficients;
|
|
431
|
+
const dg = coefs.length - 1;
|
|
432
|
+
// p(n/d) = sum c_i (n/d)^i. Multiplicamos por d^dg: sum c_i n^i d^(dg - i).
|
|
433
|
+
for (let i = 0; i <= dg; i++) {
|
|
434
|
+
// n^i:
|
|
435
|
+
let npow = 1n;
|
|
436
|
+
for (let k = 0; k < i; k++)
|
|
437
|
+
npow *= candidate.num;
|
|
438
|
+
// d^(dg - i):
|
|
439
|
+
let dpow = 1n;
|
|
440
|
+
for (let k = 0; k < dg - i; k++)
|
|
441
|
+
dpow *= candidate.den;
|
|
442
|
+
value += coefs[i] * npow * dpow;
|
|
443
|
+
}
|
|
444
|
+
void denPow;
|
|
445
|
+
if (value === 0n) {
|
|
446
|
+
seen.add(key);
|
|
447
|
+
out.push(candidate);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
// Orden estable: por (num, den) lexicográfico.
|
|
453
|
+
out.sort((x, y) => {
|
|
454
|
+
if (x.num !== y.num)
|
|
455
|
+
return x.num < y.num ? -1 : 1;
|
|
456
|
+
if (x.den !== y.den)
|
|
457
|
+
return x.den < y.den ? -1 : 1;
|
|
458
|
+
return 0;
|
|
459
|
+
});
|
|
460
|
+
return out;
|
|
461
|
+
}
|
|
462
|
+
function squareFree(p) {
|
|
463
|
+
if (p.modulus !== undefined) {
|
|
464
|
+
// Sobre Z/p: squarefree = p / gcd(p, p').
|
|
465
|
+
if (isZero(p) || degree(p) === 0)
|
|
466
|
+
return p;
|
|
467
|
+
const dp = derivative(p);
|
|
468
|
+
if (isZero(dp))
|
|
469
|
+
return p;
|
|
470
|
+
const g = gcd(p, dp);
|
|
471
|
+
if (degree(g) === 0)
|
|
472
|
+
return p;
|
|
473
|
+
const { quotient } = divmod(p, g);
|
|
474
|
+
return quotient;
|
|
475
|
+
}
|
|
476
|
+
if (isZero(p) || degree(p) === 0)
|
|
477
|
+
return p;
|
|
478
|
+
const dp = derivative(p);
|
|
479
|
+
if (isZero(dp))
|
|
480
|
+
return p;
|
|
481
|
+
const g = gcd(p, dp);
|
|
482
|
+
if (degree(g) <= 0)
|
|
483
|
+
return primitivePart(p);
|
|
484
|
+
// squarefree part = p / g. Sobre Z usamos pseudo-division y luego primitivePart.
|
|
485
|
+
const { remainder } = pseudoDivmodZ(p, g);
|
|
486
|
+
if (!isZero(remainder)) {
|
|
487
|
+
// No divisible exacto: fallback — devolver primitivePart(p).
|
|
488
|
+
return primitivePart(p);
|
|
489
|
+
}
|
|
490
|
+
// Recalcular cociente real con división exacta.
|
|
491
|
+
const result = exactDivideZ(p, g);
|
|
492
|
+
return primitivePart(result);
|
|
493
|
+
}
|
|
494
|
+
// División exacta en Z[x] cuando se sabe que b | a (usado en factor()).
|
|
495
|
+
function exactDivideZ(a, b) {
|
|
496
|
+
if (isZero(b))
|
|
497
|
+
throw new Error('exactDivideZ: divisor cero');
|
|
498
|
+
if (isZero(a))
|
|
499
|
+
return makePoly([0n]);
|
|
500
|
+
const da = degree(a);
|
|
501
|
+
const db = degree(b);
|
|
502
|
+
if (da < db) {
|
|
503
|
+
if (!isZero(a))
|
|
504
|
+
throw new Error('exactDivideZ: división no exacta (grado)');
|
|
505
|
+
return makePoly([0n]);
|
|
506
|
+
}
|
|
507
|
+
const rem = a.coefficients.slice();
|
|
508
|
+
const divisor = b.coefficients;
|
|
509
|
+
const lb = leadingCoefficient(b);
|
|
510
|
+
const quotient = Array.from({ length: da - db + 1 }, () => 0n);
|
|
511
|
+
for (let i = da; i >= db; i--) {
|
|
512
|
+
const coef = rem[i] ?? 0n;
|
|
513
|
+
if (coef === 0n)
|
|
514
|
+
continue;
|
|
515
|
+
if (coef % lb !== 0n) {
|
|
516
|
+
throw new Error('exactDivideZ: división no exacta');
|
|
517
|
+
}
|
|
518
|
+
const f = coef / lb;
|
|
519
|
+
quotient[i - db] = f;
|
|
520
|
+
for (let j = 0; j <= db; j++) {
|
|
521
|
+
rem[i - db + j] = (rem[i - db + j] ?? 0n) - f * divisor[j];
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
for (let i = 0; i < db; i++) {
|
|
525
|
+
if ((rem[i] ?? 0n) !== 0n) {
|
|
526
|
+
throw new Error('exactDivideZ: división no exacta (resto)');
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return makePoly(quotient);
|
|
530
|
+
}
|
|
531
|
+
function factor(p) {
|
|
532
|
+
if (p.modulus !== undefined) {
|
|
533
|
+
throw new Error('factor: usa factorInZp para módulos primos');
|
|
534
|
+
}
|
|
535
|
+
if (isZero(p))
|
|
536
|
+
return [makePoly([0n])];
|
|
537
|
+
if (degree(p) === 0)
|
|
538
|
+
return [makePoly([p.coefficients[0]])];
|
|
539
|
+
// Extraer contenido.
|
|
540
|
+
let working = p;
|
|
541
|
+
const c = contentZ(working);
|
|
542
|
+
const factors = [];
|
|
543
|
+
if (c !== 1n) {
|
|
544
|
+
factors.push(makePoly([c]));
|
|
545
|
+
working = exactDivideZ(working, makePoly([c]));
|
|
546
|
+
}
|
|
547
|
+
// Sacar factores x sucesivos si constant=0.
|
|
548
|
+
while (working.coefficients[0] === 0n && degree(working) > 0) {
|
|
549
|
+
factors.push(makePoly([0n, 1n]));
|
|
550
|
+
working = makePoly(working.coefficients.slice(1));
|
|
551
|
+
}
|
|
552
|
+
// Extraer todas las raíces racionales (con multiplicidad).
|
|
553
|
+
let changed = true;
|
|
554
|
+
while (changed && degree(working) > 0) {
|
|
555
|
+
changed = false;
|
|
556
|
+
const roots = rationalRoots(working);
|
|
557
|
+
for (const r of roots) {
|
|
558
|
+
// Factor lineal: den*x - num.
|
|
559
|
+
let linear = makePoly([-r.num, r.den]);
|
|
560
|
+
// Dividir mientras lo divida.
|
|
561
|
+
// Pero como pseudo-division puede no ser exacta, usamos exactDivideZ
|
|
562
|
+
// tras chequear que evaluate=0.
|
|
563
|
+
while (degree(working) > 0 && evaluateRational(working, r.num, r.den) === 0n) {
|
|
564
|
+
try {
|
|
565
|
+
working = exactDivideZ(working, linear);
|
|
566
|
+
}
|
|
567
|
+
catch {
|
|
568
|
+
break;
|
|
569
|
+
}
|
|
570
|
+
factors.push(linear);
|
|
571
|
+
changed = true;
|
|
572
|
+
linear = makePoly([-r.num, r.den]);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (degree(working) > 0) {
|
|
577
|
+
factors.push(primitivePart(working));
|
|
578
|
+
// Si la parte primitiva se quitó un signo o constante, registrarlo.
|
|
579
|
+
const leftover = exactDivideZ(working, primitivePart(working));
|
|
580
|
+
if (degree(leftover) === 0 && leftover.coefficients[0] !== 1n) {
|
|
581
|
+
factors.push(leftover);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
else if (degree(working) === 0 && working.coefficients[0] !== 1n) {
|
|
585
|
+
factors.push(makePoly([working.coefficients[0]]));
|
|
586
|
+
}
|
|
587
|
+
return factors;
|
|
588
|
+
}
|
|
589
|
+
// Evalúa p en num/den retornando 0n si la fracción es raíz (sin construir Q).
|
|
590
|
+
function evaluateRational(p, num, den) {
|
|
591
|
+
const dg = degree(p);
|
|
592
|
+
if (dg < 0)
|
|
593
|
+
return 0n;
|
|
594
|
+
let acc = 0n;
|
|
595
|
+
for (let i = 0; i <= dg; i++) {
|
|
596
|
+
let npow = 1n;
|
|
597
|
+
for (let k = 0; k < i; k++)
|
|
598
|
+
npow *= num;
|
|
599
|
+
let dpow = 1n;
|
|
600
|
+
for (let k = 0; k < dg - i; k++)
|
|
601
|
+
dpow *= den;
|
|
602
|
+
acc += p.coefficients[i] * npow * dpow;
|
|
603
|
+
}
|
|
604
|
+
return acc;
|
|
605
|
+
}
|
|
606
|
+
function isIrreducible(p, samples = 30) {
|
|
607
|
+
if (p.modulus !== undefined) {
|
|
608
|
+
// Sobre Z/pZ: irreducible ⇔ factorInZp da un sólo factor (lineal-monic).
|
|
609
|
+
const fs = factorInZp(p, p.modulus);
|
|
610
|
+
return fs.length === 1 && degree(fs[0]) === degree(p);
|
|
611
|
+
}
|
|
612
|
+
if (isZero(p))
|
|
613
|
+
return false;
|
|
614
|
+
const d = degree(p);
|
|
615
|
+
if (d <= 0)
|
|
616
|
+
return false;
|
|
617
|
+
if (d === 1)
|
|
618
|
+
return true;
|
|
619
|
+
// Si hay raíz racional, reducible.
|
|
620
|
+
if (rationalRoots(p).length > 0)
|
|
621
|
+
return false;
|
|
622
|
+
if (d <= 3)
|
|
623
|
+
return true; // grados 2 y 3 sin raíz racional ⇒ irreducible en Q.
|
|
624
|
+
// d >= 4: usamos factor() y vemos si quedó un único factor no constante.
|
|
625
|
+
const fs = factor(p);
|
|
626
|
+
const nonTrivial = fs.filter((f) => degree(f) > 0);
|
|
627
|
+
if (nonTrivial.length === 1 && degree(nonTrivial[0]) === d)
|
|
628
|
+
return true;
|
|
629
|
+
if (nonTrivial.length === 0)
|
|
630
|
+
return false;
|
|
631
|
+
// Si factor() no pudo dividir más (no encontró raíz racional), nuestro
|
|
632
|
+
// factorizador devuelve el polinomio entero como único factor no trivial.
|
|
633
|
+
// Como heurística: si nonTrivial.length === 1 con su grado === d, ok.
|
|
634
|
+
void samples;
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
637
|
+
// --- Factorización en Z/pZ vía búsqueda exhaustiva + Berlekamp simplificado
|
|
638
|
+
// ---------------------------------------------------------------------
|
|
639
|
+
// Implementación pragmática: para p pequeño basta probar todas las raíces
|
|
640
|
+
// en {0, ..., p-1}, dividir por (x - r) hasta agotar, y para grado ≥ 2
|
|
641
|
+
// restante se hace una búsqueda por factores de grado 2 (suficiente para
|
|
642
|
+
// los casos de test). Es una versión "lite" de Berlekamp.
|
|
643
|
+
function factorInZp(p, prime) {
|
|
644
|
+
if (prime <= 1n)
|
|
645
|
+
throw new Error('factorInZp: prime debe ser > 1');
|
|
646
|
+
let working = makePoly(p.coefficients.slice(), prime);
|
|
647
|
+
if (isZero(working))
|
|
648
|
+
return [makePoly([0n], prime)];
|
|
649
|
+
const factors = [];
|
|
650
|
+
// Hacer monic.
|
|
651
|
+
const lead = leadingCoefficient(working);
|
|
652
|
+
if (lead !== 1n) {
|
|
653
|
+
const inv = modInvPrime(lead, prime);
|
|
654
|
+
factors.push(makePoly([lead], prime));
|
|
655
|
+
working = scalarMul(working, inv);
|
|
656
|
+
}
|
|
657
|
+
// 1) Sacar raíces (factores lineales).
|
|
658
|
+
let foundRoot = true;
|
|
659
|
+
while (foundRoot && degree(working) > 0) {
|
|
660
|
+
foundRoot = false;
|
|
661
|
+
for (let r = 0n; r < prime; r++) {
|
|
662
|
+
if (evaluate(working, r) === 0n) {
|
|
663
|
+
const linear = makePoly([modBig(-r, prime), 1n], prime);
|
|
664
|
+
const { quotient, remainder } = divmod(working, linear);
|
|
665
|
+
if (!isZero(remainder))
|
|
666
|
+
continue;
|
|
667
|
+
factors.push(linear);
|
|
668
|
+
working = quotient;
|
|
669
|
+
foundRoot = true;
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
// 2) Búsqueda exhaustiva de factores cuadráticos monic.
|
|
675
|
+
while (degree(working) >= 2) {
|
|
676
|
+
let found = null;
|
|
677
|
+
outer: for (let a = 0n; a < prime; a++) {
|
|
678
|
+
for (let b = 0n; b < prime; b++) {
|
|
679
|
+
const candidate = makePoly([b, a, 1n], prime); // x^2 + a x + b
|
|
680
|
+
const { quotient, remainder } = divmod(working, candidate);
|
|
681
|
+
if (isZero(remainder) && degree(quotient) < degree(working)) {
|
|
682
|
+
// Asegurar que el candidato sea irreducible (sin raíces en Zp).
|
|
683
|
+
let hasRoot = false;
|
|
684
|
+
for (let r = 0n; r < prime; r++) {
|
|
685
|
+
if (evaluate(candidate, r) === 0n) {
|
|
686
|
+
hasRoot = true;
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
if (hasRoot)
|
|
691
|
+
continue;
|
|
692
|
+
found = candidate;
|
|
693
|
+
working = quotient;
|
|
694
|
+
break outer;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
if (found) {
|
|
699
|
+
factors.push(found);
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
break;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
if (degree(working) > 0) {
|
|
706
|
+
factors.push(working);
|
|
707
|
+
}
|
|
708
|
+
else if (degree(working) === 0 && working.coefficients[0] !== 1n) {
|
|
709
|
+
factors.push(working);
|
|
710
|
+
}
|
|
711
|
+
return factors;
|
|
712
|
+
}
|
|
713
|
+
// --- resultante y discriminante ---------------------------------------
|
|
714
|
+
function resultant(a, b) {
|
|
715
|
+
if (a.modulus !== undefined || b.modulus !== undefined) {
|
|
716
|
+
throw new Error('resultant: implementación sólo Z');
|
|
717
|
+
}
|
|
718
|
+
if (isZero(a) || isZero(b))
|
|
719
|
+
return 0n;
|
|
720
|
+
// Resultante vía cadena de Euclides con signos: usamos Sylvester
|
|
721
|
+
// directamente, simple y robusto.
|
|
722
|
+
return sylvesterDeterminant(a, b);
|
|
723
|
+
}
|
|
724
|
+
function sylvesterDeterminant(a, b) {
|
|
725
|
+
const da = degree(a);
|
|
726
|
+
const db = degree(b);
|
|
727
|
+
const n = da + db;
|
|
728
|
+
if (n <= 0)
|
|
729
|
+
return 1n;
|
|
730
|
+
// Matriz n×n.
|
|
731
|
+
const M = Array.from({ length: n }, () => Array.from({ length: n }, () => 0n));
|
|
732
|
+
for (let i = 0; i < db; i++) {
|
|
733
|
+
for (let j = 0; j <= da; j++) {
|
|
734
|
+
M[i][i + (da - j)] = a.coefficients[j];
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
for (let i = 0; i < da; i++) {
|
|
738
|
+
for (let j = 0; j <= db; j++) {
|
|
739
|
+
M[db + i][i + (db - j)] = b.coefficients[j];
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
return bigDeterminant(M);
|
|
743
|
+
}
|
|
744
|
+
function bigDeterminant(M) {
|
|
745
|
+
// Gauss con bigint usando eliminación fraction-free Bareiss.
|
|
746
|
+
const n = M.length;
|
|
747
|
+
if (n === 0)
|
|
748
|
+
return 1n;
|
|
749
|
+
const A = M.map((row) => row.slice());
|
|
750
|
+
let sign = 1n;
|
|
751
|
+
let prev = 1n;
|
|
752
|
+
for (let i = 0; i < n; i++) {
|
|
753
|
+
// pivot.
|
|
754
|
+
if (A[i][i] === 0n) {
|
|
755
|
+
let swap = -1;
|
|
756
|
+
for (let k = i + 1; k < n; k++) {
|
|
757
|
+
if (A[k][i] !== 0n) {
|
|
758
|
+
swap = k;
|
|
759
|
+
break;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
if (swap === -1)
|
|
763
|
+
return 0n;
|
|
764
|
+
const tmp = A[i];
|
|
765
|
+
A[i] = A[swap];
|
|
766
|
+
A[swap] = tmp;
|
|
767
|
+
sign = -sign;
|
|
768
|
+
}
|
|
769
|
+
for (let k = i + 1; k < n; k++) {
|
|
770
|
+
for (let j = i + 1; j < n; j++) {
|
|
771
|
+
const numerator = A[k][j] * A[i][i] - A[k][i] * A[i][j];
|
|
772
|
+
A[k][j] = numerator / prev;
|
|
773
|
+
}
|
|
774
|
+
A[k][i] = 0n;
|
|
775
|
+
}
|
|
776
|
+
prev = A[i][i];
|
|
777
|
+
}
|
|
778
|
+
return sign * A[n - 1][n - 1];
|
|
779
|
+
}
|
|
780
|
+
function discriminant(p) {
|
|
781
|
+
if (p.modulus !== undefined) {
|
|
782
|
+
throw new Error('discriminant: implementación sólo Z');
|
|
783
|
+
}
|
|
784
|
+
const n = degree(p);
|
|
785
|
+
if (n < 1)
|
|
786
|
+
return 0n;
|
|
787
|
+
const an = leadingCoefficient(p);
|
|
788
|
+
const dp = derivative(p);
|
|
789
|
+
const res = resultant(p, dp);
|
|
790
|
+
// disc(p) = (-1)^{n(n-1)/2} * res(p, p') / lead(p).
|
|
791
|
+
const halfExp = (n * (n - 1)) / 2;
|
|
792
|
+
const signFactor = halfExp % 2 === 0 ? 1n : -1n;
|
|
793
|
+
// res / an puede no ser exacto sólo si an no divide res — para polinomios
|
|
794
|
+
// enteros sí lo es.
|
|
795
|
+
return signFactor * (res / an);
|
|
796
|
+
}
|
|
797
|
+
//# sourceMappingURL=index.js.map
|