@stevenvo780/st-lang 4.9.0 → 4.10.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/logic/profiles/natural-deduction-nk/formula.d.ts +18 -0
- package/dist/logic/profiles/natural-deduction-nk/formula.d.ts.map +1 -0
- package/dist/logic/profiles/natural-deduction-nk/formula.js +102 -0
- package/dist/logic/profiles/natural-deduction-nk/formula.js.map +1 -0
- package/dist/logic/profiles/natural-deduction-nk/index.d.ts +5 -0
- package/dist/logic/profiles/natural-deduction-nk/index.d.ts.map +1 -0
- package/dist/logic/profiles/natural-deduction-nk/index.js +28 -0
- package/dist/logic/profiles/natural-deduction-nk/index.js.map +1 -0
- package/dist/logic/profiles/natural-deduction-nk/prover.d.ts +49 -0
- package/dist/logic/profiles/natural-deduction-nk/prover.d.ts.map +1 -0
- package/dist/logic/profiles/natural-deduction-nk/prover.js +557 -0
- package/dist/logic/profiles/natural-deduction-nk/prover.js.map +1 -0
- package/dist/logic/profiles/natural-deduction-nk/types.d.ts +48 -0
- package/dist/logic/profiles/natural-deduction-nk/types.d.ts.map +1 -0
- package/dist/logic/profiles/natural-deduction-nk/types.js +35 -0
- package/dist/logic/profiles/natural-deduction-nk/types.js.map +1 -0
- package/dist/reasoning/automata/dfa.d.ts +17 -0
- package/dist/reasoning/automata/dfa.d.ts.map +1 -0
- package/dist/reasoning/automata/dfa.js +276 -0
- package/dist/reasoning/automata/dfa.js.map +1 -0
- package/dist/reasoning/automata/index.d.ts +8 -0
- package/dist/reasoning/automata/index.d.ts.map +1 -0
- package/dist/reasoning/automata/index.js +64 -0
- package/dist/reasoning/automata/index.js.map +1 -0
- package/dist/reasoning/automata/languages.d.ts +10 -0
- package/dist/reasoning/automata/languages.d.ts.map +1 -0
- package/dist/reasoning/automata/languages.js +78 -0
- package/dist/reasoning/automata/languages.js.map +1 -0
- package/dist/reasoning/automata/nfa.d.ts +8 -0
- package/dist/reasoning/automata/nfa.d.ts.map +1 -0
- package/dist/reasoning/automata/nfa.js +122 -0
- package/dist/reasoning/automata/nfa.js.map +1 -0
- package/dist/reasoning/automata/pda.d.ts +10 -0
- package/dist/reasoning/automata/pda.d.ts.map +1 -0
- package/dist/reasoning/automata/pda.js +169 -0
- package/dist/reasoning/automata/pda.js.map +1 -0
- package/dist/reasoning/automata/regex.d.ts +6 -0
- package/dist/reasoning/automata/regex.d.ts.map +1 -0
- package/dist/reasoning/automata/regex.js +259 -0
- package/dist/reasoning/automata/regex.js.map +1 -0
- package/dist/reasoning/automata/types.d.ts +69 -0
- package/dist/reasoning/automata/types.d.ts.map +1 -0
- package/dist/reasoning/automata/types.js +29 -0
- package/dist/reasoning/automata/types.js.map +1 -0
- package/dist/reasoning/computability/index.d.ts +239 -0
- package/dist/reasoning/computability/index.d.ts.map +1 -0
- package/dist/reasoning/computability/index.js +851 -0
- package/dist/reasoning/computability/index.js.map +1 -0
- package/dist/reasoning/number-theory/crt.d.ts +9 -0
- package/dist/reasoning/number-theory/crt.d.ts.map +1 -0
- package/dist/reasoning/number-theory/crt.js +39 -0
- package/dist/reasoning/number-theory/crt.js.map +1 -0
- package/dist/reasoning/number-theory/diophantine.d.ts +10 -0
- package/dist/reasoning/number-theory/diophantine.d.ts.map +1 -0
- package/dist/reasoning/number-theory/diophantine.js +87 -0
- package/dist/reasoning/number-theory/diophantine.js.map +1 -0
- package/dist/reasoning/number-theory/factorization.d.ts +12 -0
- package/dist/reasoning/number-theory/factorization.d.ts.map +1 -0
- package/dist/reasoning/number-theory/factorization.js +136 -0
- package/dist/reasoning/number-theory/factorization.js.map +1 -0
- package/dist/reasoning/number-theory/gcd.d.ts +8 -0
- package/dist/reasoning/number-theory/gcd.d.ts.map +1 -0
- package/dist/reasoning/number-theory/gcd.js +51 -0
- package/dist/reasoning/number-theory/gcd.js.map +1 -0
- package/dist/reasoning/number-theory/index.d.ts +9 -0
- package/dist/reasoning/number-theory/index.d.ts.map +1 -0
- package/dist/reasoning/number-theory/index.js +46 -0
- package/dist/reasoning/number-theory/index.js.map +1 -0
- package/dist/reasoning/number-theory/modular.d.ts +6 -0
- package/dist/reasoning/number-theory/modular.d.ts.map +1 -0
- package/dist/reasoning/number-theory/modular.js +75 -0
- package/dist/reasoning/number-theory/modular.js.map +1 -0
- package/dist/reasoning/number-theory/primality.d.ts +6 -0
- package/dist/reasoning/number-theory/primality.d.ts.map +1 -0
- package/dist/reasoning/number-theory/primality.js +144 -0
- package/dist/reasoning/number-theory/primality.js.map +1 -0
- package/dist/reasoning/number-theory/symbols.d.ts +3 -0
- package/dist/reasoning/number-theory/symbols.d.ts.map +1 -0
- package/dist/reasoning/number-theory/symbols.js +57 -0
- package/dist/reasoning/number-theory/symbols.js.map +1 -0
- package/dist/reasoning/real-analysis/index.d.ts +127 -0
- package/dist/reasoning/real-analysis/index.d.ts.map +1 -0
- package/dist/reasoning/real-analysis/index.js +638 -0
- package/dist/reasoning/real-analysis/index.js.map +1 -0
- package/dist/reasoning/topology/index.d.ts +41 -0
- package/dist/reasoning/topology/index.d.ts.map +1 -0
- package/dist/reasoning/topology/index.js +739 -0
- package/dist/reasoning/topology/index.js.map +1 -0
- package/dist/tests/logic/profiles/natural-deduction-nk/nk.test.d.ts +2 -0
- package/dist/tests/logic/profiles/natural-deduction-nk/nk.test.d.ts.map +1 -0
- package/dist/tests/logic/profiles/natural-deduction-nk/nk.test.js +288 -0
- package/dist/tests/logic/profiles/natural-deduction-nk/nk.test.js.map +1 -0
- package/dist/tests/reasoning/automata/automata.test.d.ts +2 -0
- package/dist/tests/reasoning/automata/automata.test.d.ts.map +1 -0
- package/dist/tests/reasoning/automata/automata.test.js +310 -0
- package/dist/tests/reasoning/automata/automata.test.js.map +1 -0
- package/dist/tests/reasoning/computability/computability.test.d.ts +2 -0
- package/dist/tests/reasoning/computability/computability.test.d.ts.map +1 -0
- package/dist/tests/reasoning/computability/computability.test.js +246 -0
- package/dist/tests/reasoning/computability/computability.test.js.map +1 -0
- package/dist/tests/reasoning/number-theory/number-theory.test.d.ts +2 -0
- package/dist/tests/reasoning/number-theory/number-theory.test.d.ts.map +1 -0
- package/dist/tests/reasoning/number-theory/number-theory.test.js +170 -0
- package/dist/tests/reasoning/number-theory/number-theory.test.js.map +1 -0
- package/dist/tests/reasoning/real-analysis/real-analysis.test.d.ts +2 -0
- package/dist/tests/reasoning/real-analysis/real-analysis.test.d.ts.map +1 -0
- package/dist/tests/reasoning/real-analysis/real-analysis.test.js +197 -0
- package/dist/tests/reasoning/real-analysis/real-analysis.test.js.map +1 -0
- package/dist/tests/reasoning/topology/topology.test.d.ts +2 -0
- package/dist/tests/reasoning/topology/topology.test.d.ts.map +1 -0
- package/dist/tests/reasoning/topology/topology.test.js +327 -0
- package/dist/tests/reasoning/topology/topology.test.js.map +1 -0
- package/dist/tests/tooling/exporters/lean4/lean4-exporter.test.d.ts +2 -0
- package/dist/tests/tooling/exporters/lean4/lean4-exporter.test.d.ts.map +1 -0
- package/dist/tests/tooling/exporters/lean4/lean4-exporter.test.js +473 -0
- package/dist/tests/tooling/exporters/lean4/lean4-exporter.test.js.map +1 -0
- package/dist/tooling/exporters/lean4/index.d.ts +47 -0
- package/dist/tooling/exporters/lean4/index.d.ts.map +1 -0
- package/dist/tooling/exporters/lean4/index.js +423 -0
- package/dist/tooling/exporters/lean4/index.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,851 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// ST Computability theory — Turing machines + undecidability
|
|
4
|
+
// ============================================================
|
|
5
|
+
// Cuatro piezas que aterrizan los resultados clásicos de Turing,
|
|
6
|
+
// Kleene y Rice:
|
|
7
|
+
//
|
|
8
|
+
// 1. Máquina de Turing determinista de una cinta (TM): estados,
|
|
9
|
+
// alfabeto de cinta, transición parcial, cabeza con movimientos
|
|
10
|
+
// L/R/S (stay), aceptación/rechazo por estado. Trazas y pasos
|
|
11
|
+
// explícitos. `run` y `trace` son simulación pura — sin efectos.
|
|
12
|
+
//
|
|
13
|
+
// 2. Halting acotado: dado un budget `maxSteps`, devolvemos
|
|
14
|
+
// `boolean | 'unknown'`. Esto refleja el hecho de que el problema
|
|
15
|
+
// de la parada es semi-decidible: si la máquina para dentro del
|
|
16
|
+
// budget sabemos sí/no; si no, no podemos concluir (no es lo
|
|
17
|
+
// mismo que "no para").
|
|
18
|
+
//
|
|
19
|
+
// 3. Funciones primitivas recursivas (PRF): cero, sucesor,
|
|
20
|
+
// proyección, composición y recursión primitiva. `evalPR` corre
|
|
21
|
+
// la semántica estándar. Constructores PR_ADD, PR_MUL, PR_POW,
|
|
22
|
+
// PR_FACT, PR_PREDECESSOR para chequear que la maquinaria
|
|
23
|
+
// compila funciones de aritmética básica. Ackermann queda fuera
|
|
24
|
+
// (es μ-recursiva, no PR), incluida como tope de potencia.
|
|
25
|
+
//
|
|
26
|
+
// 4. Witness de Rice: cualquier propiedad no trivial sobre el
|
|
27
|
+
// lenguaje aceptado por una TM es indecidible. Aquí no hay
|
|
28
|
+
// decisión real (ningún algoritmo puede tener éxito); damos un
|
|
29
|
+
// explicador que sólo verifica el predicado "es no trivial" sobre
|
|
30
|
+
// una muestra y devuelve la justificación clásica.
|
|
31
|
+
//
|
|
32
|
+
// Convenciones:
|
|
33
|
+
// • Cinta = array de símbolos; expansión perezosa con `blank`
|
|
34
|
+
// hacia ambos lados (se inserta blank cuando la cabeza pasa el
|
|
35
|
+
// borde). Para no mutar configuraciones previas devolvemos una
|
|
36
|
+
// copia (`step` y `trace` no comparten estructura).
|
|
37
|
+
// • Strings de entrada/salida son la concatenación de símbolos.
|
|
38
|
+
// Para `tmReverseString`, `tmCopy`, etc. el resultado se lee de
|
|
39
|
+
// la cinta en el rango no-blank al final.
|
|
40
|
+
// • PRF: usamos `number` con guardas (negativos → error; NaN →
|
|
41
|
+
// error). No intentamos manejar BigInt aquí — los tests caben en
|
|
42
|
+
// `number` cómodamente.
|
|
43
|
+
// ============================================================
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.PR_FACT = exports.PR_PREDECESSOR = exports.PR_POW = exports.PR_MUL = exports.PR_ADD = void 0;
|
|
46
|
+
exports.initialConfig = initialConfig;
|
|
47
|
+
exports.step = step;
|
|
48
|
+
exports.run = run;
|
|
49
|
+
exports.trace = trace;
|
|
50
|
+
exports.readTape = readTape;
|
|
51
|
+
exports.boundedHalts = boundedHalts;
|
|
52
|
+
exports.tmBinaryIncrement = tmBinaryIncrement;
|
|
53
|
+
exports.tmUnaryParity = tmUnaryParity;
|
|
54
|
+
exports.tmReverseString = tmReverseString;
|
|
55
|
+
exports.tmCopy = tmCopy;
|
|
56
|
+
exports.tmAddBinary = tmAddBinary;
|
|
57
|
+
exports.evalPR = evalPR;
|
|
58
|
+
exports.ackermann = ackermann;
|
|
59
|
+
exports.isInPR = isInPR;
|
|
60
|
+
exports.riceWitness = riceWitness;
|
|
61
|
+
/**
|
|
62
|
+
* Inicializa una configuración a partir de la entrada. La cinta arranca
|
|
63
|
+
* con los símbolos de `input`, cabeza en 0. Si la entrada es vacía la
|
|
64
|
+
* cinta arranca con un blank.
|
|
65
|
+
*/
|
|
66
|
+
function initialConfig(M, input) {
|
|
67
|
+
const tape = input.length === 0 ? [M.blank] : Array.from(input);
|
|
68
|
+
return { state: M.initialState, tape, head: 0, step: 0 };
|
|
69
|
+
}
|
|
70
|
+
function findTransition(M, state, symbol) {
|
|
71
|
+
return M.transitions.find((t) => t.fromState === state && t.readSymbol === symbol);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Un paso de la TM. Devuelve la próxima configuración o un terminal.
|
|
75
|
+
* • Si el estado actual ya es de aceptación → 'halted-accept'.
|
|
76
|
+
* • Si es de rechazo → 'halted-reject'.
|
|
77
|
+
* • Si no hay transición desde (state, leído) → 'no-transition'.
|
|
78
|
+
*/
|
|
79
|
+
function step(M, config) {
|
|
80
|
+
if (M.acceptStates.has(config.state))
|
|
81
|
+
return 'halted-accept';
|
|
82
|
+
if (M.rejectStates?.has(config.state))
|
|
83
|
+
return 'halted-reject';
|
|
84
|
+
// Expansión perezosa de la cinta a la izquierda
|
|
85
|
+
let tape = config.tape;
|
|
86
|
+
let head = config.head;
|
|
87
|
+
if (head < 0) {
|
|
88
|
+
const pad = new Array(-head).fill(M.blank);
|
|
89
|
+
tape = pad.concat(tape);
|
|
90
|
+
head = 0;
|
|
91
|
+
}
|
|
92
|
+
if (head >= tape.length) {
|
|
93
|
+
const extra = head - tape.length + 1;
|
|
94
|
+
tape = tape.concat(new Array(extra).fill(M.blank));
|
|
95
|
+
}
|
|
96
|
+
const read = tape[head] ?? M.blank;
|
|
97
|
+
const tr = findTransition(M, config.state, read);
|
|
98
|
+
if (!tr)
|
|
99
|
+
return 'no-transition';
|
|
100
|
+
const newTape = tape.slice();
|
|
101
|
+
newTape[head] = tr.writeSymbol;
|
|
102
|
+
let newHead = head;
|
|
103
|
+
if (tr.direction === 'L')
|
|
104
|
+
newHead -= 1;
|
|
105
|
+
else if (tr.direction === 'R')
|
|
106
|
+
newHead += 1;
|
|
107
|
+
// direction 'S' = stay
|
|
108
|
+
return {
|
|
109
|
+
state: tr.toState,
|
|
110
|
+
tape: newTape,
|
|
111
|
+
head: newHead,
|
|
112
|
+
step: config.step + 1,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Corre la TM hasta aceptar, rechazar, agotarse o quedar sin transición.
|
|
117
|
+
* `maxSteps` defaultea a 10_000.
|
|
118
|
+
*/
|
|
119
|
+
function run(M, input, maxSteps = 10_000) {
|
|
120
|
+
let cfg = initialConfig(M, input);
|
|
121
|
+
for (let i = 0; i < maxSteps; i += 1) {
|
|
122
|
+
const next = step(M, cfg);
|
|
123
|
+
if (next === 'halted-accept')
|
|
124
|
+
return { result: 'accept', steps: cfg.step, finalConfig: cfg };
|
|
125
|
+
if (next === 'halted-reject')
|
|
126
|
+
return { result: 'reject', steps: cfg.step, finalConfig: cfg };
|
|
127
|
+
if (next === 'no-transition')
|
|
128
|
+
return { result: 'no-transition', steps: cfg.step, finalConfig: cfg };
|
|
129
|
+
cfg = next;
|
|
130
|
+
}
|
|
131
|
+
return { result: 'timeout', steps: cfg.step, finalConfig: cfg };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Lista todas las configuraciones generadas, hasta `maxSteps`.
|
|
135
|
+
* Incluye la configuración inicial. No incluye un sentinel para terminal.
|
|
136
|
+
*/
|
|
137
|
+
function trace(M, input, maxSteps) {
|
|
138
|
+
const out = [];
|
|
139
|
+
let cfg = initialConfig(M, input);
|
|
140
|
+
out.push(cfg);
|
|
141
|
+
for (let i = 0; i < maxSteps; i += 1) {
|
|
142
|
+
const next = step(M, cfg);
|
|
143
|
+
if (typeof next === 'string')
|
|
144
|
+
return out;
|
|
145
|
+
out.push(next);
|
|
146
|
+
cfg = next;
|
|
147
|
+
}
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Lectura útil de la cinta: la subcadena no-blank más larga centrada en
|
|
152
|
+
* la región explorada. Sirve para validar máquinas que escriben output
|
|
153
|
+
* en la cinta (binary increment, reverse, copy, etc.).
|
|
154
|
+
*/
|
|
155
|
+
function readTape(M, config) {
|
|
156
|
+
let start = 0;
|
|
157
|
+
let end = config.tape.length;
|
|
158
|
+
while (start < end && config.tape[start] === M.blank)
|
|
159
|
+
start += 1;
|
|
160
|
+
while (end > start && config.tape[end - 1] === M.blank)
|
|
161
|
+
end -= 1;
|
|
162
|
+
return config.tape.slice(start, end).join('');
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Halting acotado: ¿la TM para en ≤ `maxSteps` pasos?
|
|
166
|
+
* • `true` si para por aceptación, rechazo o falta de transición.
|
|
167
|
+
* • `false` técnicamente nunca se devuelve aquí — para devolver
|
|
168
|
+
* `false` con certeza haría falta resolver el halting problem, que
|
|
169
|
+
* es indecidible. Cuando agotamos el budget devolvemos `'unknown'`.
|
|
170
|
+
* Esta función es semi-decidible: reconoce las máquinas que paran,
|
|
171
|
+
* pero no decide el lenguaje complemento (las que no paran).
|
|
172
|
+
*/
|
|
173
|
+
function boundedHalts(M, input, maxSteps) {
|
|
174
|
+
const r = run(M, input, maxSteps);
|
|
175
|
+
if (r.result === 'timeout')
|
|
176
|
+
return 'unknown';
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
// ── Máquinas estándar ───────────────────────────────────────
|
|
180
|
+
/**
|
|
181
|
+
* Incrementa un número binario (MSB first) en la cinta.
|
|
182
|
+
* Algoritmo: ir al final, retroceder convirtiendo 1→0 mientras haya
|
|
183
|
+
* acarreo; al primer 0 escribir 1 y aceptar. Si todo era 1, escribir
|
|
184
|
+
* 1 al borde izquierdo y aceptar.
|
|
185
|
+
*/
|
|
186
|
+
function tmBinaryIncrement() {
|
|
187
|
+
return {
|
|
188
|
+
states: new Set(['q0', 'q1', 'q2', 'qa']),
|
|
189
|
+
alphabet: new Set(['0', '1']),
|
|
190
|
+
tapeAlphabet: new Set(['0', '1', 'B']),
|
|
191
|
+
blank: 'B',
|
|
192
|
+
initialState: 'q0',
|
|
193
|
+
acceptStates: new Set(['qa']),
|
|
194
|
+
transitions: [
|
|
195
|
+
// q0: ir a la derecha hasta encontrar blank
|
|
196
|
+
{ fromState: 'q0', readSymbol: '0', toState: 'q0', writeSymbol: '0', direction: 'R' },
|
|
197
|
+
{ fromState: 'q0', readSymbol: '1', toState: 'q0', writeSymbol: '1', direction: 'R' },
|
|
198
|
+
{ fromState: 'q0', readSymbol: 'B', toState: 'q1', writeSymbol: 'B', direction: 'L' },
|
|
199
|
+
// q1: propagar acarreo
|
|
200
|
+
{ fromState: 'q1', readSymbol: '0', toState: 'q2', writeSymbol: '1', direction: 'L' },
|
|
201
|
+
{ fromState: 'q1', readSymbol: '1', toState: 'q1', writeSymbol: '0', direction: 'L' },
|
|
202
|
+
{ fromState: 'q1', readSymbol: 'B', toState: 'qa', writeSymbol: '1', direction: 'S' },
|
|
203
|
+
// q2: terminar moviéndose al final
|
|
204
|
+
{ fromState: 'q2', readSymbol: '0', toState: 'q2', writeSymbol: '0', direction: 'L' },
|
|
205
|
+
{ fromState: 'q2', readSymbol: '1', toState: 'q2', writeSymbol: '1', direction: 'L' },
|
|
206
|
+
{ fromState: 'q2', readSymbol: 'B', toState: 'qa', writeSymbol: 'B', direction: 'R' },
|
|
207
|
+
],
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Acepta sii la cantidad de 1s en la entrada (sobre alfabeto {1}) es par.
|
|
212
|
+
* Estados q0 = par hasta ahora, q1 = impar.
|
|
213
|
+
*/
|
|
214
|
+
function tmUnaryParity() {
|
|
215
|
+
return {
|
|
216
|
+
states: new Set(['q0', 'q1', 'qa', 'qr']),
|
|
217
|
+
alphabet: new Set(['1']),
|
|
218
|
+
tapeAlphabet: new Set(['1', 'B']),
|
|
219
|
+
blank: 'B',
|
|
220
|
+
initialState: 'q0',
|
|
221
|
+
acceptStates: new Set(['qa']),
|
|
222
|
+
rejectStates: new Set(['qr']),
|
|
223
|
+
transitions: [
|
|
224
|
+
{ fromState: 'q0', readSymbol: '1', toState: 'q1', writeSymbol: '1', direction: 'R' },
|
|
225
|
+
{ fromState: 'q0', readSymbol: 'B', toState: 'qa', writeSymbol: 'B', direction: 'S' },
|
|
226
|
+
{ fromState: 'q1', readSymbol: '1', toState: 'q0', writeSymbol: '1', direction: 'R' },
|
|
227
|
+
{ fromState: 'q1', readSymbol: 'B', toState: 'qr', writeSymbol: 'B', direction: 'S' },
|
|
228
|
+
],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Revierte una cadena sobre {a, b}. Algoritmo:
|
|
233
|
+
* 1. Marcar inicio (Sa, Sb) con un símbolo distintivo.
|
|
234
|
+
* 2. Encontrar el final y desplazar el símbolo del frente al fondo.
|
|
235
|
+
* 3. Repetir hasta agotar.
|
|
236
|
+
* Output: la cinta termina con el reverso de la entrada original.
|
|
237
|
+
*
|
|
238
|
+
* Implementación con cinta auxiliar a la derecha (más simple):
|
|
239
|
+
* Caminar a la derecha mientras se copian símbolos en orden inverso
|
|
240
|
+
* tras un separador. Al terminar borrar la entrada original.
|
|
241
|
+
*
|
|
242
|
+
* Para evitar complicaciones, esta versión usa una técnica de
|
|
243
|
+
* "shift left after marking": tras leer un símbolo, lo borra y lo
|
|
244
|
+
* reescribe al final del bloque restante.
|
|
245
|
+
*
|
|
246
|
+
* Nota: este algoritmo es O(n²) en pasos pero correcto.
|
|
247
|
+
*/
|
|
248
|
+
function tmReverseString() {
|
|
249
|
+
// Estrategia:
|
|
250
|
+
// 1. init: pasamos a la derecha y escribimos un marcador '|' justo
|
|
251
|
+
// tras el último símbolo del input.
|
|
252
|
+
// 2. pickRight: vamos a la izquierda desde '|' hasta el primer
|
|
253
|
+
// símbolo del input por la derecha (el ÚLTIMO símbolo de la
|
|
254
|
+
// entrada original). Lo borramos y lo "cargamos" en el estado.
|
|
255
|
+
// Si en lugar de un símbolo encontramos el marcador izquierdo
|
|
256
|
+
// '^' → done. Si encontramos sólo blanks entre símbolos, los
|
|
257
|
+
// saltamos.
|
|
258
|
+
// 3. carry_X: caminamos a la derecha, pasando '|' y los símbolos
|
|
259
|
+
// ya escritos a la derecha del marcador, hasta encontrar el
|
|
260
|
+
// primer blank tras el output. Allí escribimos X. Esto pone el
|
|
261
|
+
// símbolo cargado al final del bloque de output → orden inverso.
|
|
262
|
+
// 4. rewind: volvemos al marcador '|' (atravesando los símbolos
|
|
263
|
+
// output) y desde ahí relanzamos pickRight.
|
|
264
|
+
// 5. Cuando pickRight ve '^' contiguo a '|' (input agotado) →
|
|
265
|
+
// borramos '^' y '|' y aceptamos.
|
|
266
|
+
//
|
|
267
|
+
// El marcador '^' se inserta al inicio extendiendo la cinta una
|
|
268
|
+
// posición a la izquierda. Como la TM no puede insertar fácilmente,
|
|
269
|
+
// arrancamos en init1 que escribe '^' en la posición 0 y desplaza
|
|
270
|
+
// todo a la derecha. Para evitar la sub-rutina de shift, optamos
|
|
271
|
+
// por una codificación más simple: el marcador izquierdo se pone
|
|
272
|
+
// a la primera posición *blank* a la izquierda. Como la cinta es
|
|
273
|
+
// ilimitada por la izquierda con blanks padded, podemos ir un paso
|
|
274
|
+
// L del primer símbolo y escribir '^' allí (eso reserva 1 blank a
|
|
275
|
+
// la izquierda del input).
|
|
276
|
+
const symbols = ['a', 'b', 'c'];
|
|
277
|
+
const transitions = [];
|
|
278
|
+
// start: ir UNA posición a la izquierda y escribir '^', luego volver a la derecha.
|
|
279
|
+
transitions.push({
|
|
280
|
+
fromState: 'start',
|
|
281
|
+
readSymbol: 'a',
|
|
282
|
+
toState: 'mark',
|
|
283
|
+
writeSymbol: 'a',
|
|
284
|
+
direction: 'L',
|
|
285
|
+
});
|
|
286
|
+
transitions.push({
|
|
287
|
+
fromState: 'start',
|
|
288
|
+
readSymbol: 'b',
|
|
289
|
+
toState: 'mark',
|
|
290
|
+
writeSymbol: 'b',
|
|
291
|
+
direction: 'L',
|
|
292
|
+
});
|
|
293
|
+
transitions.push({
|
|
294
|
+
fromState: 'start',
|
|
295
|
+
readSymbol: 'c',
|
|
296
|
+
toState: 'mark',
|
|
297
|
+
writeSymbol: 'c',
|
|
298
|
+
direction: 'L',
|
|
299
|
+
});
|
|
300
|
+
transitions.push({
|
|
301
|
+
fromState: 'start',
|
|
302
|
+
readSymbol: 'B',
|
|
303
|
+
toState: 'mark',
|
|
304
|
+
writeSymbol: 'B',
|
|
305
|
+
direction: 'L',
|
|
306
|
+
});
|
|
307
|
+
transitions.push({
|
|
308
|
+
fromState: 'mark',
|
|
309
|
+
readSymbol: 'B',
|
|
310
|
+
toState: 'init',
|
|
311
|
+
writeSymbol: '^',
|
|
312
|
+
direction: 'R',
|
|
313
|
+
});
|
|
314
|
+
// init: pasar a la derecha y poner '|' al final del input
|
|
315
|
+
for (const s of symbols) {
|
|
316
|
+
transitions.push({
|
|
317
|
+
fromState: 'init',
|
|
318
|
+
readSymbol: s,
|
|
319
|
+
toState: 'init',
|
|
320
|
+
writeSymbol: s,
|
|
321
|
+
direction: 'R',
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
transitions.push({
|
|
325
|
+
fromState: 'init',
|
|
326
|
+
readSymbol: 'B',
|
|
327
|
+
toState: 'pickRight',
|
|
328
|
+
writeSymbol: '|',
|
|
329
|
+
direction: 'L',
|
|
330
|
+
});
|
|
331
|
+
// pickRight: ir a la izquierda desde '|' buscando el último símbolo.
|
|
332
|
+
for (const s of symbols) {
|
|
333
|
+
transitions.push({
|
|
334
|
+
fromState: 'pickRight',
|
|
335
|
+
readSymbol: s,
|
|
336
|
+
toState: `carry_${s}`,
|
|
337
|
+
writeSymbol: 'B',
|
|
338
|
+
direction: 'R',
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
transitions.push({
|
|
342
|
+
fromState: 'pickRight',
|
|
343
|
+
readSymbol: 'B',
|
|
344
|
+
toState: 'pickRight',
|
|
345
|
+
writeSymbol: 'B',
|
|
346
|
+
direction: 'L',
|
|
347
|
+
});
|
|
348
|
+
// si pickRight ve '^' (input agotado) → done
|
|
349
|
+
transitions.push({
|
|
350
|
+
fromState: 'pickRight',
|
|
351
|
+
readSymbol: '^',
|
|
352
|
+
toState: 'done',
|
|
353
|
+
writeSymbol: 'B',
|
|
354
|
+
direction: 'R',
|
|
355
|
+
});
|
|
356
|
+
// carry_X: caminar a la derecha cruzando blanks ya borrados, '|', y
|
|
357
|
+
// los símbolos output existentes, hasta el primer blank tras output.
|
|
358
|
+
for (const s of symbols) {
|
|
359
|
+
const carry = `carry_${s}`;
|
|
360
|
+
transitions.push({
|
|
361
|
+
fromState: carry,
|
|
362
|
+
readSymbol: 'B',
|
|
363
|
+
toState: carry,
|
|
364
|
+
writeSymbol: 'B',
|
|
365
|
+
direction: 'R',
|
|
366
|
+
});
|
|
367
|
+
transitions.push({
|
|
368
|
+
fromState: carry,
|
|
369
|
+
readSymbol: '|',
|
|
370
|
+
toState: `carry_${s}_post`,
|
|
371
|
+
writeSymbol: '|',
|
|
372
|
+
direction: 'R',
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
for (const s of symbols) {
|
|
376
|
+
const post = `carry_${s}_post`;
|
|
377
|
+
for (const t of symbols) {
|
|
378
|
+
transitions.push({
|
|
379
|
+
fromState: post,
|
|
380
|
+
readSymbol: t,
|
|
381
|
+
toState: post,
|
|
382
|
+
writeSymbol: t,
|
|
383
|
+
direction: 'R',
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
transitions.push({
|
|
387
|
+
fromState: post,
|
|
388
|
+
readSymbol: 'B',
|
|
389
|
+
toState: 'rewind',
|
|
390
|
+
writeSymbol: s,
|
|
391
|
+
direction: 'L',
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
// rewind: volver hasta '|' atravesando los símbolos output.
|
|
395
|
+
for (const s of symbols) {
|
|
396
|
+
transitions.push({
|
|
397
|
+
fromState: 'rewind',
|
|
398
|
+
readSymbol: s,
|
|
399
|
+
toState: 'rewind',
|
|
400
|
+
writeSymbol: s,
|
|
401
|
+
direction: 'L',
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
transitions.push({
|
|
405
|
+
fromState: 'rewind',
|
|
406
|
+
readSymbol: '|',
|
|
407
|
+
toState: 'pickRight',
|
|
408
|
+
writeSymbol: '|',
|
|
409
|
+
direction: 'L',
|
|
410
|
+
});
|
|
411
|
+
// done: borrar '|' y aceptar (cuando pickRight vio '^' la cabeza fue R 1).
|
|
412
|
+
for (const s of symbols) {
|
|
413
|
+
transitions.push({
|
|
414
|
+
fromState: 'done',
|
|
415
|
+
readSymbol: s,
|
|
416
|
+
toState: 'done',
|
|
417
|
+
writeSymbol: s,
|
|
418
|
+
direction: 'R',
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
transitions.push({
|
|
422
|
+
fromState: 'done',
|
|
423
|
+
readSymbol: 'B',
|
|
424
|
+
toState: 'done',
|
|
425
|
+
writeSymbol: 'B',
|
|
426
|
+
direction: 'R',
|
|
427
|
+
});
|
|
428
|
+
transitions.push({
|
|
429
|
+
fromState: 'done',
|
|
430
|
+
readSymbol: '|',
|
|
431
|
+
toState: 'qa',
|
|
432
|
+
writeSymbol: 'B',
|
|
433
|
+
direction: 'S',
|
|
434
|
+
});
|
|
435
|
+
const stateSet = new Set(['start', 'mark', 'init', 'pickRight', 'rewind', 'done', 'qa']);
|
|
436
|
+
for (const s of symbols) {
|
|
437
|
+
stateSet.add(`carry_${s}`);
|
|
438
|
+
stateSet.add(`carry_${s}_post`);
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
states: stateSet,
|
|
442
|
+
alphabet: new Set(symbols),
|
|
443
|
+
tapeAlphabet: new Set([...symbols, '|', '^', 'B']),
|
|
444
|
+
blank: 'B',
|
|
445
|
+
initialState: 'start',
|
|
446
|
+
acceptStates: new Set(['qa']),
|
|
447
|
+
transitions,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Copia w → w#w sobre {a, b}. El '#' se inserta automáticamente y la
|
|
452
|
+
* copia queda a la derecha. Algoritmo análogo a reverse pero copiando
|
|
453
|
+
* en orden directo (marcamos cada símbolo procesado con mayúscula).
|
|
454
|
+
*/
|
|
455
|
+
function tmCopy() {
|
|
456
|
+
return {
|
|
457
|
+
states: new Set([
|
|
458
|
+
'scan',
|
|
459
|
+
'next',
|
|
460
|
+
'copyA',
|
|
461
|
+
'copyB',
|
|
462
|
+
'placeA',
|
|
463
|
+
'placeB',
|
|
464
|
+
'back',
|
|
465
|
+
'restore',
|
|
466
|
+
'done',
|
|
467
|
+
'qa',
|
|
468
|
+
]),
|
|
469
|
+
alphabet: new Set(['a', 'b']),
|
|
470
|
+
tapeAlphabet: new Set(['a', 'b', 'A', 'B0', '#', 'B']),
|
|
471
|
+
blank: 'B',
|
|
472
|
+
initialState: 'scan',
|
|
473
|
+
acceptStates: new Set(['qa']),
|
|
474
|
+
transitions: [
|
|
475
|
+
// scan: ir al final y poner '#'
|
|
476
|
+
{ fromState: 'scan', readSymbol: 'a', toState: 'scan', writeSymbol: 'a', direction: 'R' },
|
|
477
|
+
{ fromState: 'scan', readSymbol: 'b', toState: 'scan', writeSymbol: 'b', direction: 'R' },
|
|
478
|
+
{ fromState: 'scan', readSymbol: 'B', toState: 'back', writeSymbol: '#', direction: 'L' },
|
|
479
|
+
// back: volver al inicio (pasando por #, output, marcas y blanks dentro del segmento)
|
|
480
|
+
{ fromState: 'back', readSymbol: 'a', toState: 'back', writeSymbol: 'a', direction: 'L' },
|
|
481
|
+
{ fromState: 'back', readSymbol: 'b', toState: 'back', writeSymbol: 'b', direction: 'L' },
|
|
482
|
+
{ fromState: 'back', readSymbol: 'A', toState: 'back', writeSymbol: 'A', direction: 'L' },
|
|
483
|
+
{ fromState: 'back', readSymbol: 'B0', toState: 'back', writeSymbol: 'B0', direction: 'L' },
|
|
484
|
+
{ fromState: 'back', readSymbol: '#', toState: 'back', writeSymbol: '#', direction: 'L' },
|
|
485
|
+
{ fromState: 'back', readSymbol: 'B', toState: 'next', writeSymbol: 'B', direction: 'R' },
|
|
486
|
+
// next: si encuentras a/b, marcarlo y empezar copy
|
|
487
|
+
{ fromState: 'next', readSymbol: 'a', toState: 'copyA', writeSymbol: 'A', direction: 'R' },
|
|
488
|
+
{ fromState: 'next', readSymbol: 'b', toState: 'copyB', writeSymbol: 'B0', direction: 'R' },
|
|
489
|
+
{ fromState: 'next', readSymbol: 'A', toState: 'next', writeSymbol: 'A', direction: 'R' },
|
|
490
|
+
{ fromState: 'next', readSymbol: 'B0', toState: 'next', writeSymbol: 'B0', direction: 'R' },
|
|
491
|
+
{ fromState: 'next', readSymbol: '#', toState: 'restore', writeSymbol: '#', direction: 'L' },
|
|
492
|
+
// copyA: ir a la derecha hasta pasar '#' y escribir 'a' donde haya blank
|
|
493
|
+
{ fromState: 'copyA', readSymbol: 'a', toState: 'copyA', writeSymbol: 'a', direction: 'R' },
|
|
494
|
+
{ fromState: 'copyA', readSymbol: 'b', toState: 'copyA', writeSymbol: 'b', direction: 'R' },
|
|
495
|
+
{ fromState: 'copyA', readSymbol: '#', toState: 'placeA', writeSymbol: '#', direction: 'R' },
|
|
496
|
+
{ fromState: 'placeA', readSymbol: 'a', toState: 'placeA', writeSymbol: 'a', direction: 'R' },
|
|
497
|
+
{ fromState: 'placeA', readSymbol: 'b', toState: 'placeA', writeSymbol: 'b', direction: 'R' },
|
|
498
|
+
{ fromState: 'placeA', readSymbol: 'B', toState: 'back', writeSymbol: 'a', direction: 'L' },
|
|
499
|
+
// copyB
|
|
500
|
+
{ fromState: 'copyB', readSymbol: 'a', toState: 'copyB', writeSymbol: 'a', direction: 'R' },
|
|
501
|
+
{ fromState: 'copyB', readSymbol: 'b', toState: 'copyB', writeSymbol: 'b', direction: 'R' },
|
|
502
|
+
{ fromState: 'copyB', readSymbol: '#', toState: 'placeB', writeSymbol: '#', direction: 'R' },
|
|
503
|
+
{ fromState: 'placeB', readSymbol: 'a', toState: 'placeB', writeSymbol: 'a', direction: 'R' },
|
|
504
|
+
{ fromState: 'placeB', readSymbol: 'b', toState: 'placeB', writeSymbol: 'b', direction: 'R' },
|
|
505
|
+
{ fromState: 'placeB', readSymbol: 'B', toState: 'back', writeSymbol: 'b', direction: 'L' },
|
|
506
|
+
// restore: ir a la izquierda y devolver A→a, B0→b
|
|
507
|
+
{
|
|
508
|
+
fromState: 'restore',
|
|
509
|
+
readSymbol: 'A',
|
|
510
|
+
toState: 'restore',
|
|
511
|
+
writeSymbol: 'a',
|
|
512
|
+
direction: 'L',
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
fromState: 'restore',
|
|
516
|
+
readSymbol: 'B0',
|
|
517
|
+
toState: 'restore',
|
|
518
|
+
writeSymbol: 'b',
|
|
519
|
+
direction: 'L',
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
fromState: 'restore',
|
|
523
|
+
readSymbol: 'a',
|
|
524
|
+
toState: 'restore',
|
|
525
|
+
writeSymbol: 'a',
|
|
526
|
+
direction: 'L',
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
fromState: 'restore',
|
|
530
|
+
readSymbol: 'b',
|
|
531
|
+
toState: 'restore',
|
|
532
|
+
writeSymbol: 'b',
|
|
533
|
+
direction: 'L',
|
|
534
|
+
},
|
|
535
|
+
{ fromState: 'restore', readSymbol: 'B', toState: 'qa', writeSymbol: 'B', direction: 'R' },
|
|
536
|
+
],
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
// Patch: la transición `back → next` necesita un estado adicional `next`.
|
|
540
|
+
// Agregamos `'next'` al set de estados arriba.
|
|
541
|
+
/**
|
|
542
|
+
* Suma con `a` en binario y `b` en unario, formato "<a>+<b>" donde
|
|
543
|
+
* a ∈ {0,1}* (MSB-first), b ∈ {1}*.
|
|
544
|
+
*
|
|
545
|
+
* Mantener `b` unario evita la coreografía de decremento binario (que
|
|
546
|
+
* agrega ~10 estados sin enseñar nada nuevo) y muestra claramente la
|
|
547
|
+
* técnica de "incrementar `a` una vez por cada token de `b`".
|
|
548
|
+
*
|
|
549
|
+
* Algoritmo:
|
|
550
|
+
* 1. Ir al final.
|
|
551
|
+
* 2. Si la última posición es '1' (un token de b), borrarlo y entrar
|
|
552
|
+
* en `incA`: incrementar `a` y luego volver al final.
|
|
553
|
+
* 3. Si la última posición es '+' (b vacío), borrarlo: la cinta queda
|
|
554
|
+
* con sólo `a` (el resultado).
|
|
555
|
+
*/
|
|
556
|
+
function tmAddBinary() {
|
|
557
|
+
return {
|
|
558
|
+
states: new Set(['s0', 'check', 'incA', 'carryLeft', 'returnEnd', 'qa']),
|
|
559
|
+
alphabet: new Set(['0', '1', '+']),
|
|
560
|
+
tapeAlphabet: new Set(['0', '1', '+', 'B']),
|
|
561
|
+
blank: 'B',
|
|
562
|
+
initialState: 's0',
|
|
563
|
+
acceptStates: new Set(['qa']),
|
|
564
|
+
transitions: [
|
|
565
|
+
// s0: ir a la derecha hasta blank
|
|
566
|
+
{ fromState: 's0', readSymbol: '0', toState: 's0', writeSymbol: '0', direction: 'R' },
|
|
567
|
+
{ fromState: 's0', readSymbol: '1', toState: 's0', writeSymbol: '1', direction: 'R' },
|
|
568
|
+
{ fromState: 's0', readSymbol: '+', toState: 's0', writeSymbol: '+', direction: 'R' },
|
|
569
|
+
{ fromState: 's0', readSymbol: 'B', toState: 'check', writeSymbol: 'B', direction: 'L' },
|
|
570
|
+
// check: ¿es '1' (un token b) o '+' (terminamos)?
|
|
571
|
+
{ fromState: 'check', readSymbol: '1', toState: 'incA', writeSymbol: 'B', direction: 'L' },
|
|
572
|
+
{ fromState: 'check', readSymbol: '+', toState: 'qa', writeSymbol: 'B', direction: 'S' },
|
|
573
|
+
// incA: caminar a la izquierda saltando '1's de b hasta '+', luego al LSB de a.
|
|
574
|
+
{ fromState: 'incA', readSymbol: '1', toState: 'incA', writeSymbol: '1', direction: 'L' },
|
|
575
|
+
{
|
|
576
|
+
fromState: 'incA',
|
|
577
|
+
readSymbol: '+',
|
|
578
|
+
toState: 'carryLeft',
|
|
579
|
+
writeSymbol: '+',
|
|
580
|
+
direction: 'L',
|
|
581
|
+
},
|
|
582
|
+
// carryLeft: propagar acarreo binario de derecha a izquierda dentro de a.
|
|
583
|
+
{
|
|
584
|
+
fromState: 'carryLeft',
|
|
585
|
+
readSymbol: '0',
|
|
586
|
+
toState: 'returnEnd',
|
|
587
|
+
writeSymbol: '1',
|
|
588
|
+
direction: 'R',
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
fromState: 'carryLeft',
|
|
592
|
+
readSymbol: '1',
|
|
593
|
+
toState: 'carryLeft',
|
|
594
|
+
writeSymbol: '0',
|
|
595
|
+
direction: 'L',
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
fromState: 'carryLeft',
|
|
599
|
+
readSymbol: 'B',
|
|
600
|
+
toState: 'returnEnd',
|
|
601
|
+
writeSymbol: '1',
|
|
602
|
+
direction: 'R',
|
|
603
|
+
},
|
|
604
|
+
// returnEnd: ir al final (al blank derecho) y volver a check.
|
|
605
|
+
{
|
|
606
|
+
fromState: 'returnEnd',
|
|
607
|
+
readSymbol: '0',
|
|
608
|
+
toState: 'returnEnd',
|
|
609
|
+
writeSymbol: '0',
|
|
610
|
+
direction: 'R',
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
fromState: 'returnEnd',
|
|
614
|
+
readSymbol: '1',
|
|
615
|
+
toState: 'returnEnd',
|
|
616
|
+
writeSymbol: '1',
|
|
617
|
+
direction: 'R',
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
fromState: 'returnEnd',
|
|
621
|
+
readSymbol: '+',
|
|
622
|
+
toState: 'returnEnd',
|
|
623
|
+
writeSymbol: '+',
|
|
624
|
+
direction: 'R',
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
fromState: 'returnEnd',
|
|
628
|
+
readSymbol: 'B',
|
|
629
|
+
toState: 'check',
|
|
630
|
+
writeSymbol: 'B',
|
|
631
|
+
direction: 'L',
|
|
632
|
+
},
|
|
633
|
+
],
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Semántica de PRF:
|
|
638
|
+
* • zero() = 0
|
|
639
|
+
* • succ(x) = x + 1
|
|
640
|
+
* • U^n_i(x1..xn) = x_i
|
|
641
|
+
* • comp(h, g1..gk)(x1..xn) = h(g1(x1..xn), ..., gk(x1..xn))
|
|
642
|
+
* • rec(base, step):
|
|
643
|
+
* f(0, x1..xn) = base(x1..xn)
|
|
644
|
+
* f(y+1, x1..xn) = step(y, f(y, x1..xn), x1..xn)
|
|
645
|
+
*
|
|
646
|
+
* El argumento "iterado" es el primero (convención común). `args[0]` es
|
|
647
|
+
* el contador en `rec`.
|
|
648
|
+
*/
|
|
649
|
+
function evalPR(f, args) {
|
|
650
|
+
for (const a of args) {
|
|
651
|
+
if (!Number.isFinite(a) || a < 0 || !Number.isInteger(a)) {
|
|
652
|
+
throw new Error(`evalPR: argumento inválido ${String(a)}`);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
switch (f.kind) {
|
|
656
|
+
case 'zero':
|
|
657
|
+
return 0;
|
|
658
|
+
case 'succ': {
|
|
659
|
+
const x = args[0];
|
|
660
|
+
if (x === undefined)
|
|
661
|
+
throw new Error('succ: falta argumento');
|
|
662
|
+
return x + 1;
|
|
663
|
+
}
|
|
664
|
+
case 'proj': {
|
|
665
|
+
if (f.i < 1 || f.i > f.n)
|
|
666
|
+
throw new Error('proj: índice fuera de rango');
|
|
667
|
+
if (args.length < f.n)
|
|
668
|
+
throw new Error('proj: faltan argumentos');
|
|
669
|
+
const v = args[f.i - 1];
|
|
670
|
+
if (v === undefined)
|
|
671
|
+
throw new Error('proj: undefined');
|
|
672
|
+
return v;
|
|
673
|
+
}
|
|
674
|
+
case 'comp': {
|
|
675
|
+
const innerVals = f.inner.map((g) => evalPR(g, args));
|
|
676
|
+
return evalPR(f.outer, innerVals);
|
|
677
|
+
}
|
|
678
|
+
case 'rec': {
|
|
679
|
+
const y = args[0];
|
|
680
|
+
if (y === undefined)
|
|
681
|
+
throw new Error('rec: falta contador');
|
|
682
|
+
const rest = args.slice(1);
|
|
683
|
+
let acc = evalPR(f.base, rest);
|
|
684
|
+
for (let k = 0; k < y; k += 1) {
|
|
685
|
+
acc = evalPR(f.step, [k, acc, ...rest]);
|
|
686
|
+
}
|
|
687
|
+
return acc;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
// Helpers para construir PRFn más legibles.
|
|
692
|
+
const zero = { kind: 'zero' };
|
|
693
|
+
const succ = { kind: 'succ' };
|
|
694
|
+
const proj = (n, i) => ({ kind: 'proj', n, i });
|
|
695
|
+
const comp = (outer, ...inner) => ({ kind: 'comp', outer, inner });
|
|
696
|
+
const rec = (base, step) => ({ kind: 'rec', base, step });
|
|
697
|
+
/**
|
|
698
|
+
* Suma: add(0, y) = y, add(x+1, y) = succ(add(x, y))
|
|
699
|
+
* base = U^1_1 (identidad en y)
|
|
700
|
+
* step = succ ∘ U^3_2 (toma f(x,y) y le aplica succ)
|
|
701
|
+
*
|
|
702
|
+
* En nuestra convención args[0] es el contador → add(x, y).
|
|
703
|
+
*/
|
|
704
|
+
exports.PR_ADD = rec(proj(1, 1), comp(succ, proj(3, 2)));
|
|
705
|
+
/**
|
|
706
|
+
* Multiplicación: mul(0, y) = 0, mul(x+1, y) = add(mul(x,y), y)
|
|
707
|
+
* base = zero (después de proyectar y fuera)
|
|
708
|
+
* step = add(f(x,y), y)
|
|
709
|
+
*
|
|
710
|
+
* base: el caso 0 → 0. Es la función constante 0 sobre 1 argumento:
|
|
711
|
+
* const0(y) = zero ∘ U^1_1, pero zero ignora sus argumentos.
|
|
712
|
+
* En este eval, zero retorna 0 sin importar args.
|
|
713
|
+
*/
|
|
714
|
+
exports.PR_MUL = rec(comp(zero, proj(1, 1)), comp(exports.PR_ADD, proj(3, 2), proj(3, 3)));
|
|
715
|
+
/**
|
|
716
|
+
* Potencia: pow(x, y) = y^x (iterando sobre primer argumento).
|
|
717
|
+
* pow(0, y) = 1
|
|
718
|
+
* pow(x+1, y) = mul(pow(x,y), y)
|
|
719
|
+
*/
|
|
720
|
+
const ONE_FN = comp(succ, comp(zero, proj(1, 1))); // succ(0) = 1
|
|
721
|
+
exports.PR_POW = rec(ONE_FN, comp(exports.PR_MUL, proj(3, 2), proj(3, 3)));
|
|
722
|
+
/**
|
|
723
|
+
* Predecesor: pred(0) = 0, pred(x+1) = x.
|
|
724
|
+
* rec con base = zero, step = U^2_1 (devuelve y, el contador previo).
|
|
725
|
+
*/
|
|
726
|
+
exports.PR_PREDECESSOR = rec(zero, proj(2, 1));
|
|
727
|
+
/**
|
|
728
|
+
* Factorial: fact(0) = 1, fact(x+1) = mul(succ(x), fact(x)).
|
|
729
|
+
*
|
|
730
|
+
* En recursión: contador y, acumulador f(y), sin args extra. Step recibe
|
|
731
|
+
* (y, f(y)) y debe devolver mul(succ(y), f(y)).
|
|
732
|
+
*
|
|
733
|
+
* Cuidado: aquí args[0]=y es el "k" del bucle (0-indexed), por lo que
|
|
734
|
+
* succ(y) = y+1 = el siguiente número a multiplicar.
|
|
735
|
+
*/
|
|
736
|
+
exports.PR_FACT = rec(comp(succ, zero), // base = 1
|
|
737
|
+
comp(exports.PR_MUL, comp(succ, proj(2, 1)), proj(2, 2)));
|
|
738
|
+
// ── Ackermann (no PR) ───────────────────────────────────────
|
|
739
|
+
/**
|
|
740
|
+
* Función de Ackermann (Peter):
|
|
741
|
+
* A(0, n) = n + 1
|
|
742
|
+
* A(m+1, 0) = A(m, 1)
|
|
743
|
+
* A(m+1, n+1) = A(m, A(m+1, n))
|
|
744
|
+
*
|
|
745
|
+
* Crece más rápido que toda PRF: A(m, n) es la prueba canónica de que
|
|
746
|
+
* existen funciones recursivas totales no primitivas recursivas.
|
|
747
|
+
*
|
|
748
|
+
* Implementación iterativa por stack para esquivar el call-stack JS.
|
|
749
|
+
*/
|
|
750
|
+
function ackermann(m, n) {
|
|
751
|
+
if (!Number.isInteger(m) || !Number.isInteger(n) || m < 0 || n < 0) {
|
|
752
|
+
throw new Error('ackermann: m,n deben ser enteros ≥ 0');
|
|
753
|
+
}
|
|
754
|
+
const stack = [m];
|
|
755
|
+
let curN = n;
|
|
756
|
+
while (stack.length > 0) {
|
|
757
|
+
const curM = stack.pop();
|
|
758
|
+
if (curM === 0) {
|
|
759
|
+
curN = curN + 1;
|
|
760
|
+
}
|
|
761
|
+
else if (curN === 0) {
|
|
762
|
+
stack.push(curM - 1);
|
|
763
|
+
curN = 1;
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
stack.push(curM - 1);
|
|
767
|
+
stack.push(curM);
|
|
768
|
+
curN = curN - 1;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return curN;
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Test heurístico de "esta función podría ser PR": evalúa la función
|
|
775
|
+
* sobre `samples` puntos pequeños y la compara con el patrón de
|
|
776
|
+
* crecimiento de Ackermann (que escapa a PR). Es estrictamente una
|
|
777
|
+
* heurística — no es un decisor.
|
|
778
|
+
*
|
|
779
|
+
* • Si la función supera a A(samples, samples) en algún punto pequeño
|
|
780
|
+
* → likely = false (probablemente no es PR).
|
|
781
|
+
* • Si nunca crece más rápido que cuadrático/exponencial moderado →
|
|
782
|
+
* likely = true.
|
|
783
|
+
*/
|
|
784
|
+
function isInPR(f, samples = 4) {
|
|
785
|
+
let lastRatio = 1;
|
|
786
|
+
for (let n = 1; n <= samples; n += 1) {
|
|
787
|
+
const v = f(n);
|
|
788
|
+
if (!Number.isFinite(v) || v < 0)
|
|
789
|
+
return { likely: false };
|
|
790
|
+
const prevV = f(n - 1);
|
|
791
|
+
const ratio = prevV === 0 ? v : v / prevV;
|
|
792
|
+
// Comparar con un "horizonte de Ackermann": si f(n) > A(2, n+3), lo
|
|
793
|
+
// tomamos como señal de crecimiento súper-PR moderado. Esto es
|
|
794
|
+
// intencionadamente generoso porque casi todo lo "normal" cae aquí.
|
|
795
|
+
const ack = ackermann(2, Math.min(n + 3, 8));
|
|
796
|
+
if (v > ack * 1_000_000)
|
|
797
|
+
return { likely: false, estimate: ratio };
|
|
798
|
+
lastRatio = ratio;
|
|
799
|
+
}
|
|
800
|
+
return { likely: true, estimate: lastRatio };
|
|
801
|
+
}
|
|
802
|
+
// ── Witness de Rice ─────────────────────────────────────────
|
|
803
|
+
/**
|
|
804
|
+
* Rice (1953): toda propiedad **no trivial** sobre el lenguaje aceptado
|
|
805
|
+
* por una TM (es decir, sobre el comportamiento input/output observable)
|
|
806
|
+
* es **indecidible**.
|
|
807
|
+
*
|
|
808
|
+
* No podemos *decidir* la propiedad — eso es justo lo que el teorema
|
|
809
|
+
* niega. Lo que sí podemos es **verificar el predicado de Rice**: la
|
|
810
|
+
* propiedad debe ser
|
|
811
|
+
*
|
|
812
|
+
* 1. extensional (depende sólo del lenguaje, no del código),
|
|
813
|
+
* 2. no vacía (alguna TM la satisface),
|
|
814
|
+
* 3. no total (alguna TM no la satisface).
|
|
815
|
+
*
|
|
816
|
+
* Esta función toma el predicado, lo evalúa sobre un muestreo finito de
|
|
817
|
+
* TMs conocidas y, si encuentra una `M0` que la satisface y una `M1`
|
|
818
|
+
* que no, devuelve `undecidable = true` con explicación. Si todas las
|
|
819
|
+
* TMs de la muestra dan la misma respuesta no podemos concluir nada
|
|
820
|
+
* (la propiedad podría ser trivial, o la muestra puede ser muy chica).
|
|
821
|
+
*/
|
|
822
|
+
function riceWitness(property, sampleSize = 5) {
|
|
823
|
+
const samples = [
|
|
824
|
+
tmBinaryIncrement(),
|
|
825
|
+
tmUnaryParity(),
|
|
826
|
+
tmReverseString(),
|
|
827
|
+
tmCopy(),
|
|
828
|
+
tmAddBinary(),
|
|
829
|
+
].slice(0, Math.max(2, sampleSize));
|
|
830
|
+
let sawTrue = false;
|
|
831
|
+
let sawFalse = false;
|
|
832
|
+
for (const m of samples) {
|
|
833
|
+
if (property(m))
|
|
834
|
+
sawTrue = true;
|
|
835
|
+
else
|
|
836
|
+
sawFalse = true;
|
|
837
|
+
if (sawTrue && sawFalse) {
|
|
838
|
+
return {
|
|
839
|
+
undecidable: true,
|
|
840
|
+
explanation: 'La propiedad es no trivial (alguna TM la satisface y otra no). Por el teorema de Rice (1953), su decisión sobre todas las TMs es indecidible: no existe algoritmo que reciba una codificación de TM y responda sí/no para la propiedad.',
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
return {
|
|
845
|
+
undecidable: false,
|
|
846
|
+
explanation: sawTrue
|
|
847
|
+
? 'La muestra es uniformemente positiva: la propiedad podría ser trivialmente verdadera (todas las TMs la satisfacen). Sin más evidencia no podemos invocar Rice.'
|
|
848
|
+
: 'La muestra es uniformemente negativa: la propiedad podría ser trivialmente falsa. Sin más evidencia no podemos invocar Rice.',
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
//# sourceMappingURL=index.js.map
|