gdp-wired 0.10.0 → 0.11.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/README.md +18 -6
- package/dist/core/dsl.d.ts +106 -8
- package/dist/core/dsl.js +310 -31
- package/dist/core/gdp.js +2 -2
- package/dist/core/machine.d.ts +3 -0
- package/dist/core/machine.js +41 -3
- package/dist/core/solver.d.ts +1 -0
- package/dist/core/solver.js +116 -5
- package/dist/kernel/kernel.d.ts +9 -1
- package/dist/kernel/kernel.js +171 -13
- package/dist/kernel/prove.d.ts +21 -0
- package/dist/kernel/prove.js +128 -22
- package/dist/lean-kernel/emit.js +8 -1
- package/package.json +2 -2
package/dist/kernel/prove.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { add, k, scale, solve, sub, v } from "../core/solver.js";
|
|
2
|
-
import { assumed, canon, columnTerm, decided, stepZone, check, checkLaws, checkStepLaws, historyMachine, leafConstraints, linear, livenessHelpful, livenessParts, obligations, plainKey, progressObligations, stepLawObligations, trueC, truth, unreachable, withProducts } from "./kernel.js";
|
|
1
|
+
import { add, k, refuteFast, scale, solve, sub, v } from "../core/solver.js";
|
|
2
|
+
import { assumed, canon, canonFlip, columnTerm, decided, stepZone, check, checkLaws, checkStepLaws, historyMachine, leafConstraints, leafConstraintsReusing, reusing, linear, livenessHelpful, livenessParts, obligations, plainKey, progressObligations, stepLawObligations, trueC, truth, unreachable, withProducts } from "./kernel.js";
|
|
3
3
|
// The atoms the path leaves open, in evaluation order: deciding any of them makes progress.
|
|
4
4
|
function openE(e, path, out) {
|
|
5
5
|
switch (e.op) {
|
|
@@ -38,17 +38,18 @@ function openC(c, path, out) {
|
|
|
38
38
|
return openC(c.x, path, out);
|
|
39
39
|
if (c.op === 'and' || c.op === 'or')
|
|
40
40
|
return c.xs.forEach(y => openC(y, path, out));
|
|
41
|
-
|
|
42
|
-
const a = linear(c.a, path), b = linear(c.b, path);
|
|
41
|
+
const a = linear(c.a, path), b = linear(c.b, path), own = [];
|
|
43
42
|
if (a && b) {
|
|
44
43
|
const d = sub(a, b);
|
|
45
44
|
if (c.op === '<=')
|
|
46
|
-
|
|
45
|
+
own.push(d);
|
|
47
46
|
else if (c.op === '<')
|
|
48
|
-
|
|
47
|
+
own.push(add(d, k(1)));
|
|
49
48
|
else
|
|
50
|
-
|
|
49
|
+
own.push(d, scale(d, -1));
|
|
51
50
|
}
|
|
51
|
+
openE(c.a, path, out), openE(c.b, path, out);
|
|
52
|
+
out.push(...own);
|
|
52
53
|
}
|
|
53
54
|
// A hypothesis is settled when it gives facts that are all its content: here, when its truth is decided, or it is a
|
|
54
55
|
// conjunction of settled parts / a linear comparison. A disjunction is settled only once one disjunct is left open and
|
|
@@ -85,7 +86,7 @@ function openCells(rows, path) {
|
|
|
85
86
|
if (p.slice(0, p.indexOf('[')) !== q.slice(0, q.indexOf('[')))
|
|
86
87
|
continue;
|
|
87
88
|
const d = sub(parse(keyText(p)), parse(keyText(q)));
|
|
88
|
-
const dec = (lin) => path.get(canon(lin))?.holds ?? (path.has(
|
|
89
|
+
const dec = (lin) => path.get(canon(lin))?.holds ?? (path.has(canonFlip(lin)) ? !path.get(canonFlip(lin)).holds : undefined);
|
|
89
90
|
const a = dec(d), b = dec(scale(d, -1));
|
|
90
91
|
if (a === undefined)
|
|
91
92
|
out.push(d);
|
|
@@ -113,9 +114,40 @@ function parse(text) {
|
|
|
113
114
|
}
|
|
114
115
|
return out;
|
|
115
116
|
}
|
|
117
|
+
// Where a goal's time goes, when asked (research/perf/prover-bench.ts): each root goal's time, nodes, solver calls and
|
|
118
|
+
// the time in the solver and in building rows. Off unless `profile.on`; the counters cost nothing then.
|
|
119
|
+
export const profile = { on: false, goals: [], solves: 0, solveMs: 0, rowsMs: 0, nodes: 0 };
|
|
120
|
+
const timed = (f, add) => { if (!profile.on)
|
|
121
|
+
return f(); const t = performance.now(); try {
|
|
122
|
+
return f();
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
add(performance.now() - t);
|
|
126
|
+
} };
|
|
127
|
+
export const solveKinds = {};
|
|
128
|
+
const solveP = (rows, maxRows) => {
|
|
129
|
+
if (!profile.on)
|
|
130
|
+
return solve(rows, maxRows);
|
|
131
|
+
const t = performance.now(), out = solve(rows, maxRows), ms = performance.now() - t;
|
|
132
|
+
profile.solves++;
|
|
133
|
+
profile.solveMs += ms;
|
|
134
|
+
const s = (solveKinds[out.kind] ??= { n: 0, ms: 0, rows: 0 });
|
|
135
|
+
s.n++;
|
|
136
|
+
s.ms += ms;
|
|
137
|
+
s.rows += rows.length;
|
|
138
|
+
return out;
|
|
139
|
+
};
|
|
116
140
|
// The budget: 200,000 nodes and, by default, a second per hypothesis (at least 20 seconds) per goal; past either the goal
|
|
117
141
|
// is left `unknown`. A goal with many instances (a law about two keys at its pairs) has that many more rows per leaf.
|
|
118
142
|
export function grow(goal, path = new Map(), budget = { nodes: 0, until: performance.now() + Math.max(20_000, 1_000 * goal.hyps.length) }) {
|
|
143
|
+
if (!profile.on || path.size || budget.nodes)
|
|
144
|
+
return growRoot(goal, path, budget);
|
|
145
|
+
const t = performance.now(), s0 = profile.solves, m0 = profile.solveMs, r0 = profile.rowsMs, n0 = profile.nodes;
|
|
146
|
+
const out = growRoot(goal, path, budget);
|
|
147
|
+
profile.goals.push({ name: goal.name, ms: performance.now() - t, nodes: profile.nodes - n0, solves: profile.solves - s0, solveMs: profile.solveMs - m0, rowsMs: profile.rowsMs - r0, proven: 'tree' in out });
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
function growRoot(goal, path, budget) {
|
|
119
151
|
// At the root: plainly first; where the cases multiply past a budget, through cuts. The plain budget halves with each
|
|
120
152
|
// two open choices (what a plain search's cases grow by), so a long run's steps reach the cuts early.
|
|
121
153
|
if (path.size === 0 && budget.nodes === 0) {
|
|
@@ -132,24 +164,47 @@ export function grow(goal, path = new Map(), budget = { nodes: 0, until: perform
|
|
|
132
164
|
function growPlain(goal, path, budget) {
|
|
133
165
|
if (budget.cap !== undefined && budget.nodes >= budget.cap)
|
|
134
166
|
return { stuck: 'unknown', path, why: 'cap' };
|
|
167
|
+
if (profile.on)
|
|
168
|
+
profile.nodes++;
|
|
135
169
|
if (++budget.nodes > 200_000 || (budget.until !== undefined && performance.now() > budget.until))
|
|
136
170
|
return { stuck: 'unknown', path, why: outOfBudget(goal, budget.nodes > 200_000 ? 'nodes' : 'time', path.size) };
|
|
137
171
|
// Rows that read a term column only once a cut has put one on the path; before, the rows a choice-free search uses
|
|
138
172
|
// (what it saw before term columns), renumbered to the whole leaf's when a leaf is found.
|
|
139
|
-
const full =
|
|
173
|
+
const full = timed(() => leafConstraintsReusing(goal, path), ms => { profile.rowsMs += ms; });
|
|
140
174
|
const columns = [...path.values()].some(a => Object.keys(a.lin).some(key => key.startsWith('⟦')));
|
|
141
175
|
const idx = full.flatMap((r, i) => (columns || !Object.keys(r.lin).some(key => key.startsWith('⟦')) ? [i] : []));
|
|
142
176
|
const rows = idx.map(i => full[i]), at = (i) => (i < rows.length ? idx[i] : full.length + i - rows.length);
|
|
143
177
|
const leafOf = (steps, products) => ({ leaf: steps.map(st => (st.kind === 'input' ? { ...st, index: at(st.index) } : st)), ...(products ? { products: products.map(([i, j]) => [at(i), at(j)]) } : {}) });
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return { tree: leafOf(verdict.steps), nodes: budget.nodes };
|
|
178
|
+
// The conclusion's own atoms first (the goal's last hypothesis is the law refuted): a case it doesn't read is often
|
|
179
|
+
// one no refutation needs, and splitting it first multiplies every case after it.
|
|
147
180
|
const open = [];
|
|
148
|
-
|
|
149
|
-
|
|
181
|
+
const hyps = goal.hyps, last = hyps.length - 1;
|
|
182
|
+
let byHyp = openSeen.get(goal);
|
|
183
|
+
if (!byHyp)
|
|
184
|
+
openSeen.set(goal, (byHyp = new Map()));
|
|
185
|
+
const openOf = (h) => { let seen = byHyp.get(h); if (!seen)
|
|
186
|
+
byHyp.set(h, (seen = [])); return reusing(seen, path, p => { const out = []; openH(h, p, out); return out; }); };
|
|
187
|
+
if (last >= 0)
|
|
188
|
+
open.push(...openOf(hyps[last]));
|
|
189
|
+
for (const [i, h] of hyps.entries())
|
|
190
|
+
if (i !== last)
|
|
191
|
+
open.push(...openOf(h));
|
|
150
192
|
open.push(...openCells(rows, path));
|
|
151
|
-
|
|
152
|
-
|
|
193
|
+
const next = open.find(lin => Object.keys(lin).some(key => key !== '1') && !path.has(canon(lin)) && !path.has(canonFlip(lin)));
|
|
194
|
+
// With a case still to split, only "refuted or not" matters (a witness here goes unused): a large system is tried by
|
|
195
|
+
// its Farkas rows alone, and left to the split if they don't refute it. Elimination to the last variable, which a
|
|
196
|
+
// satisfiable system needs to say so, is kept for small systems and for the leaves, where its answer is the outcome.
|
|
197
|
+
// Past the Farkas rows, elimination still runs, bounded (a refutation the rationals miss is an integer one, which
|
|
198
|
+
// elimination's cuts find early; a satisfiable system grows past the bound and is left to the split).
|
|
199
|
+
if (next && rows.length > 60) {
|
|
200
|
+
const fast = timed(() => refuteFast(rows), ms => { profile.solves++; profile.solveMs += ms; });
|
|
201
|
+
if (fast?.kind === 'refuted')
|
|
202
|
+
return { tree: leafOf(fast.steps), nodes: budget.nodes };
|
|
203
|
+
}
|
|
204
|
+
const verdict = next && rows.length > 60 ? solveP(rows, 2 * rows.length) : solveP(rows);
|
|
205
|
+
if (verdict.kind === 'refuted')
|
|
206
|
+
return { tree: leafOf(verdict.steps), nodes: budget.nodes };
|
|
207
|
+
let split = next ?? (verdict.kind === 'unknown' ? bisect(rows, path) ?? fractional(verdict.point, path) : undefined);
|
|
153
208
|
// Products: with monomials about, try every pair of rows multiplied; failing that, split a factor at zero.
|
|
154
209
|
const monomials = rows.some(r => Object.keys(r.lin).some(key => key.includes('*')));
|
|
155
210
|
// A witness that multiplies out (each monomial's value is its factors' product) is a real model: nothing to refute.
|
|
@@ -184,7 +239,7 @@ function growPlain(goal, path, budget) {
|
|
|
184
239
|
return { tree: leafOf(third.steps, [...sq, ...cubes]), nodes: budget.nodes };
|
|
185
240
|
}
|
|
186
241
|
const factors = [...new Set(rows.flatMap(r => Object.keys(r.lin).filter(key => key.includes('*')).flatMap(key => key.split('*'))))];
|
|
187
|
-
const fresh = (lin) => !path.has(canon(lin)) && !path.has(
|
|
242
|
+
const fresh = (lin) => !path.has(canon(lin)) && !path.has(canonFlip(lin));
|
|
188
243
|
// Split a factor at zero, else two factors against each other (so (x - y)² >= 0 becomes a product of rows).
|
|
189
244
|
const zero = factors.find(f => fresh({ [f]: 1 }));
|
|
190
245
|
const apart = factors.flatMap((f, i) => factors.slice(i + 1).map(g => ({ [f]: 1, [g]: -1 }))).find(fresh);
|
|
@@ -199,11 +254,62 @@ function growPlain(goal, path, budget) {
|
|
|
199
254
|
const yes = growPlain(goal, new Map(path).set(id, { lin: split, holds: true }), budget);
|
|
200
255
|
if (!('tree' in yes))
|
|
201
256
|
return yes;
|
|
257
|
+
// A refutation of the yes side that never reads the split refutes the case as it stands: the other side is skipped.
|
|
258
|
+
const lifted = lift(goal, yes.tree, new Map(path).set(id, { lin: split, holds: true }), path);
|
|
259
|
+
if (lifted)
|
|
260
|
+
return { tree: lifted, nodes: budget.nodes };
|
|
202
261
|
const no = growPlain(goal, new Map(path).set(id, { lin: split, holds: false }), budget);
|
|
203
262
|
if (!('tree' in no))
|
|
204
263
|
return no;
|
|
205
264
|
return { tree: { split, yes: yes.tree, no: no.tree }, nodes: budget.nodes };
|
|
206
265
|
}
|
|
266
|
+
// A tree found under one more decision (`from`, the path with it) as a tree under the path without it (`to`), if no
|
|
267
|
+
// leaf reads the decision: each leaf's rows found again among the rows the shorter path gives (the same rows, renumbered:
|
|
268
|
+
// the decision's own row, and any row only it makes, are not there), every split kept. The derivations are the same
|
|
269
|
+
// arithmetic over the same rows, and the kernel checks the whole tree in the end. Undefined at the first leaf that
|
|
270
|
+
// needs the decision.
|
|
271
|
+
function lift(goal, tree, from, to) {
|
|
272
|
+
const own = to.size; // the decision's row: the last of `from`'s path rows, below every later split's
|
|
273
|
+
const reads = (t) => ('leaf' in t ? t.leaf.some(st => st.kind === 'input' && st.index === own) || (t.products ?? []).some(([i, j]) => i === own || j === own) : reads(t.yes) || reads(t.no));
|
|
274
|
+
if (reads(tree))
|
|
275
|
+
return undefined;
|
|
276
|
+
const go = (t, a, b) => {
|
|
277
|
+
if (!('leaf' in t)) {
|
|
278
|
+
const sid = canon(t.split);
|
|
279
|
+
if (b.has(sid) || b.has(canonFlip(t.split)))
|
|
280
|
+
return undefined;
|
|
281
|
+
const yes = go(t.yes, new Map(a).set(sid, { lin: t.split, holds: true }), new Map(b).set(sid, { lin: t.split, holds: true }));
|
|
282
|
+
const no = yes && go(t.no, new Map(a).set(sid, { lin: t.split, holds: false }), new Map(b).set(sid, { lin: t.split, holds: false }));
|
|
283
|
+
return yes && no ? { split: t.split, yes, no } : undefined;
|
|
284
|
+
}
|
|
285
|
+
const ra = leafConstraintsReusing(goal, a), rb = leafConstraintsReusing(goal, b), at = new Map();
|
|
286
|
+
rb.forEach((r, i) => { const key = canon(r.lin); if (!at.has(key))
|
|
287
|
+
at.set(key, i); });
|
|
288
|
+
const map = (i) => (i < ra.length ? at.get(canon(ra[i].lin)) : undefined);
|
|
289
|
+
const leaf = [];
|
|
290
|
+
for (const st of t.leaf) {
|
|
291
|
+
if (st.kind !== 'input') {
|
|
292
|
+
leaf.push(st);
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const j = map(st.index);
|
|
296
|
+
if (j === undefined)
|
|
297
|
+
return undefined;
|
|
298
|
+
leaf.push({ ...st, index: j });
|
|
299
|
+
}
|
|
300
|
+
const products = [];
|
|
301
|
+
for (const [i, j] of t.products ?? []) {
|
|
302
|
+
const pi = map(i), pj = map(j);
|
|
303
|
+
if (pi === undefined || pj === undefined)
|
|
304
|
+
return undefined;
|
|
305
|
+
products.push([pi, pj]);
|
|
306
|
+
}
|
|
307
|
+
return { leaf, ...(t.products ? { products } : {}) };
|
|
308
|
+
};
|
|
309
|
+
return go(tree, from, to);
|
|
310
|
+
}
|
|
311
|
+
// Each hypothesis' open atoms, as at an earlier path that decided the same atoms it reads (kernel.ts's reusing).
|
|
312
|
+
const openSeen = new WeakMap();
|
|
207
313
|
// What ran out, for a goal left unknown: its size, how deep the case splits went, and the two shapes that usually cause it.
|
|
208
314
|
function outOfBudget(goal, limit, depth) {
|
|
209
315
|
const steps = Number(/@(\d+) steps/.exec(goal.name)?.[1] ?? 1);
|
|
@@ -259,7 +365,7 @@ function topAtoms(c, path) {
|
|
|
259
365
|
if (!a || !b)
|
|
260
366
|
return [];
|
|
261
367
|
const atoms = c.op === '<=' ? [sub(a, b)] : c.op === '<' ? [add(sub(a, b), k(1))] : [sub(a, b), sub(b, a)];
|
|
262
|
-
return atoms.filter(l => Object.keys(l).some(key => key !== '1') && !path.has(canon(l)) && !path.has(
|
|
368
|
+
return atoms.filter(l => Object.keys(l).some(key => key !== '1') && !path.has(canon(l)) && !path.has(canonFlip(l)));
|
|
263
369
|
}
|
|
264
370
|
// A refutation that splits only on these atoms (a cut's failing side), within a small budget.
|
|
265
371
|
function growFocused(goal, path, focus, budget, near = focus) {
|
|
@@ -268,7 +374,7 @@ function growFocused(goal, path, focus, budget, near = focus) {
|
|
|
268
374
|
const verdict = solveNear(leafConstraints(goal, path), [...near, ...[...path.values()].slice(-1).map(a => a.lin)]);
|
|
269
375
|
if (verdict.kind === 'refuted')
|
|
270
376
|
return { tree: { leaf: verdict.steps }, nodes: budget.nodes };
|
|
271
|
-
const split = focus.find(l => !path.has(canon(l)) && !path.has(
|
|
377
|
+
const split = focus.find(l => !path.has(canon(l)) && !path.has(canonFlip(l)));
|
|
272
378
|
if (!split)
|
|
273
379
|
return { stuck: verdict.kind === 'sat' ? 'sat' : 'unknown', path };
|
|
274
380
|
const id = canon(split);
|
|
@@ -319,7 +425,7 @@ function bisect(rows, path) {
|
|
|
319
425
|
const mid = Math.floor((lo + hi) / 2);
|
|
320
426
|
for (const c of lo === hi ? [lo, lo - 1] : [mid]) {
|
|
321
427
|
const lin = c === 0 ? { [x]: 1 } : { [x]: 1, 1: -c };
|
|
322
|
-
if (!path.has(canon(lin)) && !path.has(
|
|
428
|
+
if (!path.has(canon(lin)) && !path.has(canonFlip(lin)))
|
|
323
429
|
return lin;
|
|
324
430
|
}
|
|
325
431
|
}
|
|
@@ -331,7 +437,7 @@ function fractional(point, path) {
|
|
|
331
437
|
if (Math.abs(value - Math.round(value)) < 1e-9 || x.includes('*'))
|
|
332
438
|
continue;
|
|
333
439
|
const c = Math.floor(value), lin = c === 0 ? { [x]: 1 } : { [x]: 1, 1: -c };
|
|
334
|
-
if (!path.has(canon(lin)) && !path.has(
|
|
440
|
+
if (!path.has(canon(lin)) && !path.has(canonFlip(lin)))
|
|
335
441
|
return lin;
|
|
336
442
|
}
|
|
337
443
|
return undefined;
|
|
@@ -616,7 +722,7 @@ function proveLawsAsGiven(m, laws) {
|
|
|
616
722
|
// Each law's two goals (start and step) get their own instances, seeded with its own keys.
|
|
617
723
|
const keys = [...readKeys(m, laws), ...witnesses(m)], instances = {};
|
|
618
724
|
laws.forEach((law, i) => {
|
|
619
|
-
instances[law.name] = instancesFor(m, laws, inst => obligations(m, laws, { ...instances, [law.name]: inst }
|
|
725
|
+
instances[law.name] = instancesFor(m, laws, inst => obligations(m, laws, { ...instances, [law.name]: inst }, i), {
|
|
620
726
|
seed: [...keys, ...(law.forall ? [{ op: 'var', name: `${law.forall}#` }] : []), ...(law.keys ?? []).map((name) => ({ op: 'var', name }))],
|
|
621
727
|
focus: [...(law.forall ? [`${law.forall}#`] : []), ...(law.keys ?? [])],
|
|
622
728
|
});
|
package/dist/lean-kernel/emit.js
CHANGED
|
@@ -105,12 +105,19 @@ export function encode(ex, tamper = false, dropStep = true, globalVars, globalMa
|
|
|
105
105
|
else if (key.includes('*'))
|
|
106
106
|
monos.add(key);
|
|
107
107
|
}
|
|
108
|
+
// Each cell a hypothesis reads under the path, kept or not: two reads of one cell cancel in a row (T − T), yet
|
|
109
|
+
// Lean's linear form of each still needs the cell's column to see the comparison decided.
|
|
108
110
|
for (const h of goal.hyps)
|
|
109
|
-
for (const e of subterms(h))
|
|
111
|
+
for (const e of subterms(h)) {
|
|
110
112
|
if (e.op === 'prod')
|
|
111
113
|
for (const key of Object.keys(linear(e, path) ?? {}))
|
|
112
114
|
if (key.includes('*'))
|
|
113
115
|
monos.add(key);
|
|
116
|
+
if (e.op === 'get')
|
|
117
|
+
for (const key of Object.keys(linear(e, path) ?? {}))
|
|
118
|
+
if (key.includes('['))
|
|
119
|
+
addCell(key);
|
|
120
|
+
}
|
|
114
121
|
});
|
|
115
122
|
const depth = (name) => name.split('[').length;
|
|
116
123
|
// Term columns (open choices, each standing for its term's value) before the cells, whose keys may read them.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdp-wired",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "An automatic prover for stateful rules (budgets, caps, windows, workflows, policies): laws proven with no proof to write, checked again in Lean, counterexamples found on the GPU, the rule shipped as code",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"verification",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"build": "bun scripts/build.ts",
|
|
90
90
|
"prepublishOnly": "bun scripts/build.ts",
|
|
91
91
|
"smoke": "sh scripts/smoke.sh",
|
|
92
|
-
"test": "bun scripts/gen-lean-library.ts --check && cd core && bun test solver.test.ts dsl.test.ts conformance.test.ts compose.test.ts quantifiers.test.ts functions.test.ts keyterms.test.ts lists.test.ts liveness.test.ts sizes.test.ts values.test.ts builders.test.ts gdp.test.ts datatypes.test.ts lemmas.test.ts invoices.test.ts folds.test.ts && cd ../kernel && bun test tamper.test.ts refusals.test.ts steplaw.test.ts sets.test.ts && cd ../examples && bun test examples.test.ts && cd .. && bun examples/readme.ts && bun examples/composition.ts && bun examples/sets.ts && bun examples/quantifiers.ts && bun examples/functions.ts && bun examples/datatypes.ts && bun examples/lemmas.ts && bun examples/invoices.ts && bun examples/folds.ts && bun examples/lists.ts && bun examples/sizes.ts && bun examples/liveness.ts && bun examples/budget.ts && bun examples/gdp.ts && bun examples/steps.ts && bun examples/jev-fabric/run.ts"
|
|
92
|
+
"test": "bun scripts/gen-lean-library.ts --check && cd core && bun test solver.test.ts dsl.test.ts conformance.test.ts compose.test.ts quantifiers.test.ts functions.test.ts keyterms.test.ts lists.test.ts liveness.test.ts sizes.test.ts values.test.ts builders.test.ts gdp.test.ts datatypes.test.ts lemmas.test.ts invoices.test.ts folds.test.ts trees.test.ts && cd ../kernel && bun test tamper.test.ts refusals.test.ts steplaw.test.ts sets.test.ts && cd ../examples && bun test examples.test.ts && cd .. && bun examples/readme.ts && bun examples/composition.ts && bun examples/sets.ts && bun examples/quantifiers.ts && bun examples/functions.ts && bun examples/datatypes.ts && bun examples/lemmas.ts && bun examples/invoices.ts && bun examples/folds.ts && bun examples/trees.ts && bun examples/lists.ts && bun examples/sizes.ts && bun examples/liveness.ts && bun examples/budget.ts && bun examples/gdp.ts && bun examples/steps.ts && bun examples/jev-fabric/run.ts"
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
95
|
"@gdp-ts/core": "0.1.0",
|