gdp-wired 0.2.0 → 0.3.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.
@@ -6,8 +6,14 @@
6
6
  // Arithmetic is linear: +, −, × by a constant, min, max, if. Maps are naturals indexed by naturals, every cell starting
7
7
  // at the same value; a law about a map holds for every key x, and is proven at one arbitrary x (with the law assumed at
8
8
  // every other key the step reads).
9
- import { historyMachine, scope } from "../kernel/kernel.js";
9
+ import { historyMachine, keyTerm, plainKey, scope } from "../kernel/kernel.js";
10
10
  import { add, eq, k, le, lt, nat, scale, show, solve, sub, v } from "./solver.js";
11
+ // A write at one key, for the parts that take no write at every key (the symbolic prover, the GPU synthesizers, the
12
+ // hand-written shader, the first Lean export): they refuse one by name.
13
+ export const pointWrite = (w, where) => { if ('each' in w)
14
+ throw new Error(`${where} takes no write at every key`); return w; };
15
+ // The same for a quantified flag.
16
+ export const noQuantifier = (where) => { throw new Error(`${where} takes no quantified flag`); };
11
17
  export { unreachable } from "../kernel/kernel.js";
12
18
  // Builders.
13
19
  export const x = (name) => ({ op: 'var', name });
@@ -27,6 +33,76 @@ export const all = (...xs) => ({ op: 'and', xs });
27
33
  export const any = (...xs) => ({ op: 'or', xs });
28
34
  export const not = (c) => ({ op: 'not', x: c });
29
35
  export const bit = (name) => any(same(x(name), n(0)), same(x(name), n(1)));
36
+ // A condition at every key, or a value at every key. Where the bound key is read only as a map's key, each key a map
37
+ // read holds a cell at, and one key past them all (where every map reads its default), are every case there is.
38
+ const mapsIn = (t) => [...new Set([...JSON.stringify(t).matchAll(/"op":"get","map":"([^"]+)"/g)].map(x => x[1]))];
39
+ function keysAt(st, maps) {
40
+ const keys = new Set();
41
+ let past = 0;
42
+ for (const cells of Object.values(st.maps))
43
+ for (const key of cells.keys())
44
+ past = Math.max(past, key + 1);
45
+ for (const map of maps)
46
+ for (const key of st.maps[map]?.keys() ?? [])
47
+ keys.add(key);
48
+ return [...keys, past];
49
+ }
50
+ const breaksCache = new WeakMap();
51
+ function breaksOf(t, key) {
52
+ const hit = breaksCache.get(t);
53
+ if (hit)
54
+ return hit;
55
+ const out = { reads: [], cmps: [] };
56
+ const termOf = (e) => (e.op === 'var' ? { minus: false } : e.op === 'add' ? { off: e.a.op === 'var' && e.a.name === key ? e.b : e.a, minus: false } : { off: e.b, minus: true });
57
+ const walk = (u) => {
58
+ if (u.op === 'get' && keyTerm(u.key, key)) {
59
+ out.reads.push({ map: u.map, ...termOf(u.key) });
60
+ return;
61
+ }
62
+ if ((u.op === '<' || u.op === '<=' || u.op === '=') && (keyTerm(u.a, key) || keyTerm(u.b, key))) {
63
+ const [kt, other] = keyTerm(u.a, key) ? [u.a, u.b] : [u.b, u.a];
64
+ out.cmps.push({ value: other, ...termOf(kt) });
65
+ walk(other);
66
+ return;
67
+ }
68
+ for (const v of Object.values(u))
69
+ if (v && typeof v === 'object')
70
+ (Array.isArray(v) ? v : [v]).forEach(y => walk(y));
71
+ };
72
+ walk(t);
73
+ breaksCache.set(t, out);
74
+ return out;
75
+ }
76
+ function breakKeys(b, env, st, m) {
77
+ const points = new Set([0]);
78
+ for (const r of b.reads) {
79
+ const d = r.off ? evalE(r.off, env, st, m) : 0;
80
+ for (const s of st.maps[r.map]?.keys() ?? []) {
81
+ if (r.minus)
82
+ points.add(s + d);
83
+ else if (s >= d)
84
+ points.add(s - d);
85
+ }
86
+ if (r.minus)
87
+ points.add(d);
88
+ }
89
+ for (const c of b.cmps) {
90
+ const v = evalE(c.value, env, st, m), d = c.off ? evalE(c.off, env, st, m) : 0;
91
+ if (c.minus) {
92
+ points.add(v + d);
93
+ points.add(d);
94
+ }
95
+ else if (v >= d)
96
+ points.add(v - d);
97
+ }
98
+ const keys = new Set();
99
+ for (const p of points)
100
+ for (let i = -1; i <= 2; i++)
101
+ if (p + i >= 0)
102
+ keys.add(p + i);
103
+ const sorted = [...keys].sort((a, b) => a - b);
104
+ return [...sorted, sorted.at(-1) + 1];
105
+ }
30
106
  export const naturalIn = (v, what) => {
31
107
  if (typeof v !== 'number' || !Number.isSafeInteger(v) || v < 0)
32
108
  throw new RangeError(`${what} must be a natural below 2^53, not ${String(v)}`);
@@ -53,9 +129,24 @@ export function evalE(e, env, st, m) {
53
129
  case 'min': return Math.min(evalE(e.a, env, st, m), evalE(e.b, env, st, m));
54
130
  case 'max': return Math.max(evalE(e.a, env, st, m), evalE(e.b, env, st, m));
55
131
  case 'if': return evalC(e.c, env, st, m) ? evalE(e.then, env, st, m) : evalE(e.else, env, st, m);
56
- case 'get': return st.maps[e.map].get(evalE(e.key, env, st, m)) ?? m.maps[e.map];
132
+ case 'get': {
133
+ const key = evalE(e.key, env, st, m), fn = m.fns?.[e.map];
134
+ return fn ? naturalIn(fnOf(fn)(key), `${e.map}(${key})`) : st.maps[e.map].get(key) ?? st.defaults?.[e.map] ?? m.maps[e.map];
135
+ }
57
136
  }
58
137
  }
138
+ // A function's value at a key (the GPU search tabulates functions with it).
139
+ export const fnValue = (fn, key) => naturalIn(fnOf(fn)(key), `a function at ${key}`);
140
+ // A function's JavaScript, compiled once.
141
+ const compiledFns = new WeakMap();
142
+ const fnOf = (fn) => {
143
+ let f = compiledFns.get(fn);
144
+ if (!f) {
145
+ f = compileFunction(['k'], `return (${fn.js})(k)`);
146
+ compiledFns.set(fn, f);
147
+ }
148
+ return f;
149
+ };
59
150
  export function evalC(c, env, st, m) {
60
151
  switch (c.op) {
61
152
  case '<': return evalE(c.a, env, st, m) < evalE(c.b, env, st, m);
@@ -65,7 +156,33 @@ export function evalC(c, env, st, m) {
65
156
  case 'or': return c.xs.some(y => evalC(y, env, st, m));
66
157
  case 'not': return !evalC(c.x, env, st, m);
67
158
  case 'flag': return env[`?${c.name}`] === 1;
159
+ case 'all': return (plainKey(c.c, c.key) ? keysAt(st, mapsIn(c.c)) : breakKeys(breaksOf(c.c, c.key), env, st, m)).every(key => evalC(c.c, { ...env, [c.key]: key }, st, m));
160
+ }
161
+ }
162
+ // A write at every key: its value at each key the maps it reads hold a cell at, and its new default past them.
163
+ // With the key in key terms, every key up to the last breakpoint is computed (the stretches between breakpoints hold the
164
+ // same value, which need not be the default), and past it the value is the new default.
165
+ function writeEvery(w, env, st, m) {
166
+ if (!plainKey(w.value, w.each)) {
167
+ const top = breakKeys(breaksOf(w.value, w.each), env, st, m).at(-1);
168
+ if (top > 1_000_000)
169
+ throw new RangeError(`a write at every key reads its key in arithmetic past a million keys (${top})`);
170
+ const fallback = evalE(w.value, { ...env, [w.each]: top }, st, m), cells = new Map();
171
+ for (let key = 0; key < top; key++) {
172
+ const v = evalE(w.value, { ...env, [w.each]: key }, st, m);
173
+ if (v !== fallback)
174
+ cells.set(key, v);
175
+ }
176
+ return { cells, default: fallback };
177
+ }
178
+ const keys = keysAt(st, mapsIn(w.value)), past = keys.at(-1);
179
+ const fallback = evalE(w.value, { ...env, [w.each]: past }, st, m), cells = new Map();
180
+ for (const key of keys.slice(0, -1)) {
181
+ const v = evalE(w.value, { ...env, [w.each]: key }, st, m);
182
+ if (v !== fallback)
183
+ cells.set(key, v);
68
184
  }
185
+ return { cells, default: fallback };
69
186
  }
70
187
  // The machine a runtime steps (with a history, kernel.ts's plain machine), its queries, and the inputs a request carries.
71
188
  function runnable(m) {
@@ -77,6 +194,26 @@ function runnable(m) {
77
194
  return { h, plain, queries, carried: m.inputs.filter(i => !queries.includes(i)) };
78
195
  }
79
196
  const empty = { scalars: {}, maps: {} };
197
+ // The state as a step reads it: with the maps the request carries (cells past them read 0), each checked natural.
198
+ export function carriedView(m, st, input, rename = map => map) {
199
+ if (!m.inputMaps?.length)
200
+ return st;
201
+ const maps = { ...st.maps }, defaults = { ...st.defaults };
202
+ for (const map of m.inputMaps) {
203
+ const cells = input[map];
204
+ if (!(cells instanceof Map))
205
+ throw new RangeError(`input ${map} must be a Map of naturals`);
206
+ for (const [key, v] of cells) {
207
+ naturalIn(key, `a key of ${map}`);
208
+ naturalIn(v, `a cell of ${map}`);
209
+ }
210
+ maps[rename(map)] = cells;
211
+ defaults[rename(map)] = 0;
212
+ }
213
+ return { ...st, maps, defaults };
214
+ }
215
+ // A request's numbers (its inputs), without the maps it carries.
216
+ export const numbersOf = (input) => Object.fromEntries(Object.entries(input).filter((e) => typeof e[1] === 'number'));
80
217
  // The interpreter: persistent (a step leaves the state it was given as it was), so searches can branch from any state.
81
218
  export function interpret(m) {
82
219
  const { h, plain, carried } = runnable(m);
@@ -89,6 +226,7 @@ export function interpret(m) {
89
226
  env[i] = naturalIn(input[i], `input ${i}`);
90
227
  return env;
91
228
  };
229
+ const viewed = (st, input) => carriedView(m, st, input);
92
230
  // The records a request's queries read: with pruning, those not yet spent at its order.
93
231
  const live = (env, st) => {
94
232
  const records = st.history;
@@ -134,8 +272,8 @@ export function interpret(m) {
134
272
  ordered(env, st);
135
273
  return queriesOf(env, live(env, st));
136
274
  },
137
- step: (params, st, input) => {
138
- const env = args(params, input);
275
+ step: (params, given, input) => {
276
+ const env = args(params, input), st = viewed(given, input);
139
277
  let records = [];
140
278
  if (h) {
141
279
  ordered(env, st);
@@ -147,12 +285,19 @@ export function interpret(m) {
147
285
  const scalars = { ...st.scalars };
148
286
  for (const [name, e] of Object.entries(plain.step.state ?? {}))
149
287
  scalars[name] = evalE(e, env, st, plain);
150
- const writes = Object.entries(plain.step.maps ?? {}).map(([name, { key, value }]) => [name, evalE(key, env, st, plain), evalE(value, env, st, plain)]);
151
- const maps = Object.fromEntries(Object.entries(st.maps).map(([name, cells]) => [name, new Map(cells)]));
152
- for (const [name, key, value] of writes)
153
- maps[name].set(key, value);
288
+ const writes = Object.entries(plain.step.maps ?? {}).map(([name, w]) => [name, 'each' in w ? writeEvery(w, env, st, plain) : { key: evalE(w.key, env, st, plain), value: evalE(w.value, env, st, plain) }]);
289
+ const maps = Object.fromEntries(Object.entries(given.maps).map(([name, cells]) => [name, new Map(cells)]));
290
+ const defaults = { ...given.defaults };
291
+ for (const [name, w] of writes)
292
+ if ('cells' in w) {
293
+ maps[name] = w.cells;
294
+ defaults[name] = w.default;
295
+ }
296
+ else
297
+ maps[name].set(w.key, w.value);
298
+ const withDefaults = Object.keys(defaults).length ? { defaults } : {};
154
299
  if (!h)
155
- return { scalars, maps };
300
+ return { scalars, maps, ...withDefaults };
156
301
  const kept = evalC(h.keep, env, st, plain);
157
302
  return { scalars, maps, history: kept ? [...records, Object.fromEntries(h.record.map(f => [f, env[f]]))] : records, ...(h.prune ? { last: env[h.prune.order] } : {}) };
158
303
  },
@@ -164,10 +309,12 @@ export function interpret(m) {
164
309
  export function compileJs(m) {
165
310
  const { h, plain, queries, carried } = runnable(m);
166
311
  const id = (name) => JSON.stringify(name);
167
- const flags = Object.keys(plain.step.flags ?? {});
312
+ const flags = Object.keys(plain.step.flags ?? {}), carriedMaps = m.inputMaps ?? [], fns = Object.keys(m.fns ?? {});
313
+ // A key bound by a quantified flag or a write at every key is the JS parameter k.
314
+ let bound;
168
315
  const ex = (e) => {
169
316
  switch (e.op) {
170
- case 'var': return e.name.startsWith('r.') ? `r[${id(e.name.slice(2))}]` : plain.params.includes(e.name) || plain.inputs.includes(e.name) ? `env[${id(e.name)}]` : `s[${id(e.name)}]`;
317
+ case 'var': return e.name === bound ? 'k' : e.name.startsWith('r.') ? `r[${id(e.name.slice(2))}]` : plain.params.includes(e.name) || plain.inputs.includes(e.name) ? `env[${id(e.name)}]` : `s[${id(e.name)}]`;
171
318
  case 'const': return String(e.value);
172
319
  case 'add': return `$x(${ex(e.a)} + ${ex(e.b)})`;
173
320
  case 'sub': return `Math.max(0, ${ex(e.a)} - ${ex(e.b)})`;
@@ -176,7 +323,7 @@ export function compileJs(m) {
176
323
  case 'min': return `Math.min(${ex(e.a)}, ${ex(e.b)})`;
177
324
  case 'max': return `Math.max(${ex(e.a)}, ${ex(e.b)})`;
178
325
  case 'if': return `(${cx(e.c)} ? ${ex(e.then)} : ${ex(e.else)})`;
179
- case 'get': return `(maps[${id(e.map)}].get(${ex(e.key)}) ?? ${plain.maps[e.map]})`;
326
+ case 'get': return fns.includes(e.map) ? `$n(fn${fns.indexOf(e.map)}(${ex(e.key)}), ${id(`${e.map}()`)})` : `(maps[${id(e.map)}].get(${ex(e.key)}) ?? ${carriedMaps.includes(e.map) ? '0' : `dft[${id(e.map)}] ?? ${plain.maps[e.map]}`})`;
180
327
  }
181
328
  };
182
329
  const cx = (c) => {
@@ -188,11 +335,27 @@ export function compileJs(m) {
188
335
  case 'or': return `(${c.xs.map(cx).join(' || ')})`;
189
336
  case 'not': return `(!${cx(c.x)})`;
190
337
  case 'flag': return `flag${flags.indexOf(c.name)}`;
338
+ case 'all': {
339
+ const keys = keysJs(c.c, c.key), outer = bound;
340
+ bound = c.key;
341
+ const body = cx(c.c);
342
+ bound = outer;
343
+ return `${keys}.every(k => ${body})`;
344
+ }
191
345
  }
192
346
  };
347
+ // The keys a quantifier or a write at every key is decided on (the interpreter's keysAt, or its breakKeys).
348
+ const keysJs = (t, key) => {
349
+ if (plainKey(t, key))
350
+ return `$keys(maps, ${JSON.stringify(mapsIn(t).filter(mp => !fns.includes(mp)))})`;
351
+ const b = breaksOf(t, key), off = (o) => (o ? ex(o) : '0');
352
+ return `$breaks(maps, [${b.reads.map(r => `[${id(r.map)}, ${off(r.off)}, ${r.minus}]`).join(', ')}], [${b.cmps.map(c => `[${ex(c.value)}, ${off(c.off)}, ${c.minus}]`).join(', ')}])`;
353
+ };
354
+ const every = (w) => { const keys = keysJs(w.value, w.each), dense = !plainKey(w.value, w.each), outer = bound; bound = w.each; const body = ex(w.value); bound = outer; return `${dense ? '$dense' : '$every'}(${keys}, k => ${body})`; };
193
355
  const writes = Object.entries(plain.step.maps ?? {});
194
356
  const env = (input) => `{${[...m.params.map(p => `${id(p)}: $n(params[${id(p)}], ${id(`param ${p}`)})`), ...(input ? carried.map(i => `${id(i)}: $n(input[${id(i)}], ${id(`input ${i}`)})`) : [])].join(', ')}}`;
195
- const lines = [`const env = ${env(true)}, s = state.scalars, maps = state.maps;`];
357
+ // Reads go through `maps` (with the maps the request carries); writes go to the state's own.
358
+ const lines = [`const env = ${env(true)}, s = state.scalars, dft = {...state.defaults}, maps = ${carriedMaps.length ? `{...state.maps, ${carriedMaps.map(mp => `${id(mp)}: $m(input[${id(mp)}], ${id(`input ${mp}`)})`).join(', ')}}` : 'state.maps'};`];
196
359
  if (h) {
197
360
  lines.push('const records = state.history;', 'let head = state.head;');
198
361
  if (h.prune) {
@@ -201,33 +364,39 @@ export function compileJs(m) {
201
364
  }
202
365
  lines.push(`let ${queries.map((_, i) => `q${i} = 0`).join(', ')};`, `for (let i = head; i < records.length; i++) { const r = records[i]; ${queries.map((q, i) => `q${i} = $x(q${i} + ${ex(h.queries[q])});`).join(' ')} }`, ...queries.map((q, i) => `env[${id(q)}] = q${i};`));
203
366
  }
204
- lines.push(...flags.map((name, i) => `const flag${i} = ${cx(plain.step.flags[name])};`), `const scalars = {...s, ${Object.entries(plain.step.state ?? {}).map(([name, e]) => `${id(name)}: ${ex(e)}`).join(', ')}};`, ...writes.map(([, { key, value }], i) => `const key${i} = ${ex(key)}, value${i} = ${ex(value)};`));
367
+ lines.push(...flags.map((name, i) => `const flag${i} = ${cx(plain.step.flags[name])};`), `const scalars = {...s, ${Object.entries(plain.step.state ?? {}).map(([name, e]) => `${id(name)}: ${ex(e)}`).join(', ')}};`, ...writes.map(([, w], i) => ('each' in w ? `const write${i} = ${every(w)};` : `const key${i} = ${ex(w.key)}, value${i} = ${ex(w.value)};`)));
205
368
  if (h)
206
369
  lines.push(`const keep = ${cx(h.keep)};`);
207
- lines.push(...writes.map(([name], i) => `maps[${id(name)}].set(key${i}, value${i});`));
370
+ lines.push(...writes.map(([name, w], i) => ('each' in w ? `state.maps[${id(name)}] = write${i}.cells; dft[${id(name)}] = write${i}.d;` : `state.maps[${id(name)}].set(key${i}, value${i});`)));
208
371
  if (h)
209
372
  lines.push(`if (keep) records.push({${h.record.map(f => `${id(f)}: env[${id(f)}]`).join(', ')}});`,
210
373
  // Drop the spent prefix now and then, not every step: amortized, each record is moved a bounded number of times.
211
- 'if (head > 1024 && 2 * head > records.length) { records.splice(0, head); head = 0; }', `return {scalars, maps, history: records, head${h.prune ? ', last: order' : ''}};`);
374
+ 'if (head > 1024 && 2 * head > records.length) { records.splice(0, head); head = 0; }', `return {scalars, maps: state.maps, history: records, head${h.prune ? ', last: order' : ''}};`);
212
375
  else
213
- lines.push('return {scalars, maps};');
376
+ lines.push('return Object.keys(dft).length ? {scalars, maps: state.maps, defaults: dft} : {scalars, maps: state.maps};');
214
377
  return `// Generated from the machine ${m.name}. step consumes its state: keep the one it returns (fork to branch).
215
378
  function $n(v, what) { if (typeof v !== 'number' || !Number.isSafeInteger(v) || v < 0) throw new RangeError(what + ' must be a natural below 2^53, not ' + v); return v; }
216
379
  function $x(v) { if (v > 9007199254740991) throw new RangeError('a value passed 2^53 - 1, past which the proofs over the naturals no longer describe the run'); return v; }
380
+ function $m(v, what) { if (!(v instanceof Map)) throw new RangeError(what + ' must be a Map of naturals'); for (const [key, x] of v) { $n(key, 'a key of ' + what); $n(x, 'a cell of ' + what); } return v; }
381
+ function $keys(maps, names) { const keys = new Set(); let past = 0; for (const c of Object.values(maps)) for (const key of c.keys()) past = Math.max(past, key + 1); for (const n of names) for (const key of maps[n].keys()) keys.add(key); return [...keys, past]; }
382
+ ${fns.map((name, i) => `const fn${i} = (${m.fns[name].js});\n`).join('')}function $breaks(maps, reads, cmps) { const p = new Set([0]); for (const [n, d, minus] of reads) { for (const s of maps[n].keys()) { if (minus) p.add(s + d); else if (s >= d) p.add(s - d); } if (minus) p.add(d); } for (const [v, d, minus] of cmps) { if (minus) { p.add(v + d); p.add(d); } else if (v >= d) p.add(v - d); } const keys = new Set(); for (const q of p) for (let i = -1; i <= 2; i++) if (q + i >= 0) keys.add(q + i); const sorted = [...keys].sort((a, b) => a - b); sorted.push(sorted[sorted.length - 1] + 1); return sorted; }
383
+ function $dense(keys, f) { const top = keys[keys.length - 1]; if (top > 1000000) throw new RangeError('a write at every key reads its key in arithmetic past a million keys (' + top + ')'); const d = f(top), cells = new Map(); for (let k = 0; k < top; k++) { const v = f(k); if (v !== d) cells.set(k, v); } return {cells, d}; }
384
+ function $every(keys, f) { const past = keys[keys.length - 1], d = f(past), cells = new Map(); for (let i = 0; i < keys.length - 1; i++) { const v = f(keys[i]); if (v !== d) cells.set(keys[i], v); } return {cells, d}; }
217
385
  export function init(params) {
218
386
  const env = ${env(false)}, s = {};
219
387
  return {scalars: {${Object.entries(plain.state).map(([name, e]) => `${id(name)}: ${ex(e)}`).join(', ')}}, maps: {${Object.keys(plain.maps ?? {}).map(name => `${id(name)}: new Map()`).join(', ')}}${h ? ', history: [], head: 0' : ''}};
220
388
  }
221
389
  export function fork(state) {
222
- return {scalars: {...state.scalars}, maps: Object.fromEntries(Object.entries(state.maps).map(([name, cells]) => [name, new Map(cells)]))${h ? ', history: state.history.slice(state.head), head: 0, last: state.last' : ''}};
390
+ return {scalars: {...state.scalars}, maps: Object.fromEntries(Object.entries(state.maps).map(([name, cells]) => [name, new Map(cells)])), ...(state.defaults ? {defaults: {...state.defaults}} : {})${h ? ', history: state.history.slice(state.head), head: 0, last: state.last' : ''}};
223
391
  }
224
392
  export function step(params, state, input) {
225
393
  ${lines.join('\n ')}
226
394
  }
227
395
  `;
228
396
  }
229
- // Generated code as a function. Every caller passes source this library generated from its own terms, never from data,
230
- // which is what makes compiling it safe; the lint's implied-eval rule is waived here and nowhere else.
397
+ // Generated code as a function. Every caller passes source this library generated from its own terms, or a function's
398
+ // `js` the rule's author wrote (code, as the rule itself is), never data; the lint's implied-eval rule is waived here
399
+ // and nowhere else.
231
400
  export function compileFunction(params, body) {
232
401
  // oxlint-disable-next-line typescript/no-implied-eval
233
402
  return new Function(...params, body);
@@ -279,6 +448,7 @@ function symE(e, s) {
279
448
  // The cases in which c has the given truth value.
280
449
  function symC(c, s, truth) {
281
450
  switch (c.op) {
451
+ case 'all': throw new Error('the symbolic prover takes no quantified flag (the kernel does)');
282
452
  case '<':
283
453
  case '<=':
284
454
  case '=':
@@ -318,7 +488,7 @@ function feasible(cases) {
318
488
  function readsOf(c) {
319
489
  const inE = (e) => e.op === 'get' ? [{ map: e.map, key: e.key }, ...inE(e.key)]
320
490
  : e.op === 'if' ? [...readsOfC(e.c), ...inE(e.then), ...inE(e.else)] : 'a' in e ? [...inE(e.a), ...('b' in e ? inE(e.b) : [])] : [];
321
- const readsOfC = (y) => y.op === 'flag' ? [] : y.op === 'not' ? readsOfC(y.x) : y.op === 'and' || y.op === 'or' ? y.xs.flatMap(readsOfC) : [...inE(y.a), ...inE(y.b)];
491
+ const readsOfC = (y) => y.op === 'flag' || y.op === 'all' ? [] : y.op === 'not' ? readsOfC(y.x) : y.op === 'and' || y.op === 'or' ? y.xs.flatMap(readsOfC) : [...inE(y.a), ...inE(y.b)];
322
492
  return readsOfC(c);
323
493
  }
324
494
  const keyLin = (e, names) => symE(e, { names, reads: noReads })[0].value;
@@ -389,7 +559,7 @@ export function prove(m, goal, invariants = [goal]) {
389
559
  const stepReads = [
390
560
  ...Object.values(m.step.flags ?? {}).flatMap(readsOf),
391
561
  ...Object.values(m.step.state ?? {}).flatMap(e => readsOf({ op: '=', a: e, b: e })),
392
- ...Object.entries(m.step.maps ?? {}).flatMap(([map, w]) => [{ map, key: w.key }, ...readsOf({ op: '=', a: w.value, b: w.value })]),
562
+ ...Object.entries(m.step.maps ?? {}).flatMap(([map, w]) => [...('each' in w ? [] : [{ map, key: w.key }]), ...readsOf({ op: '=', a: w.value, b: w.value })]),
393
563
  ];
394
564
  const pairs = new Map();
395
565
  for (const r of stepReads) {
@@ -424,6 +594,8 @@ export function prove(m, goal, invariants = [goal]) {
424
594
  cases = feasible(cross(cases, acc => {
425
595
  if (write === undefined)
426
596
  return [{ path: [], value: { ...acc, [`${f.cell}'`]: v(f.cell) } }];
597
+ if ('each' in write)
598
+ throw new Error('the symbolic prover takes no write at every key (the kernel does)');
427
599
  return cross(symE(write.key, preSymF), key => {
428
600
  const decided = preSymF.decided?.[`${show(key)}|${show(f.key)}`];
429
601
  const written = () => symE(write.value, preSymF).map(val => ({ path: [...val.path, ...(decided ? [] : eq(key, f.key))], value: { ...acc, [`${f.cell}'`]: val.value } }));
@@ -25,14 +25,17 @@ export declare function facts(c: C, path: Path, positive?: boolean): Lin[];
25
25
  export declare function leafConstraints(goal: Goal, path: Path): Constraint[];
26
26
  export declare function withProducts(rows: Constraint[], pairs: [number, number][]): Constraint[];
27
27
  export declare function check(goal: Goal, tree: Tree, path?: Path): boolean;
28
- type Subst = {
28
+ export type Subst = {
29
29
  vars: Record<string, E>;
30
30
  maps: Record<string, (key: E) => E>;
31
31
  flags: Record<string, C>;
32
32
  };
33
+ export declare function substE(e: E, s: Subst): E;
33
34
  export declare function substC(c: C, s: Subst): C;
34
35
  export type Instances = Record<string, E[]>;
35
36
  export declare function scope(m: Machine, laws?: Law[]): void;
37
+ export declare const keyTerm: (e: E, key: string) => boolean;
38
+ export declare const plainKey: (t: E | C, key: string) => boolean;
36
39
  export declare function historyMachine(m: Machine): {
37
40
  machine: Machine;
38
41
  goals: Goal[];
@@ -59,11 +62,35 @@ export declare function progressObligations(m: Machine, laws: Law[], instances:
59
62
  then: C;
60
63
  request: Record<string, E>;
61
64
  }): Goal[];
65
+ export type Helpful = {
66
+ when?: C;
67
+ request: Record<string, E>;
68
+ };
69
+ export type Liveness = {
70
+ name: string;
71
+ from: C;
72
+ to: C;
73
+ rank: E | E[];
74
+ request?: Record<string, E>;
75
+ helpful?: Helpful[];
76
+ };
77
+ export declare const trueC: C, falseC: C;
78
+ export declare const livenessRanks: (l: Liveness) => E[];
79
+ export declare const livenessHelpful: (l: Liveness) => {
80
+ when: C;
81
+ request: Record<string, E>;
82
+ }[];
83
+ export declare function livenessParts(m: Machine, l: Liveness): {
84
+ stay: StepLaw;
85
+ cover: StepLaw;
86
+ helps: StepLaw[];
87
+ };
62
88
  export type StepLaw = {
63
89
  name: string;
64
90
  when?: C | C[];
65
91
  then: C;
66
92
  forall?: string;
93
+ keys?: string[];
67
94
  steps?: number;
68
95
  };
69
96
  export declare function stepLawObligations(machine: Machine, laws: Law[], instances: Instances, sl: StepLaw): Goal[];
@@ -73,6 +100,7 @@ export declare function checkStepLaws(m: Machine, laws: Law[], stepLaws: StepLaw
73
100
  trees: Record<string, Tree>;
74
101
  };
75
102
  instances: Instances;
103
+ perStep?: Record<string, Instances>;
76
104
  trees: Record<string, Tree>;
77
105
  }): boolean;
78
106
  export type Fold = {
@@ -85,4 +113,3 @@ export type Fold = {
85
113
  assume?: C;
86
114
  };
87
115
  export declare function foldObligations(f: Fold, laws: Law[]): Goal[];
88
- export {};