gdp-wired 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tom X Nguyen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,230 @@
1
- # Temporary Holding Version
1
+ <img src="https://raw.githubusercontent.com/monotykamary/gdp-wired/main/static/banner-ink.svg" alt="gdp-wired" width="100%" />
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ # gdp-wired
4
+
5
+ Laws about stateful rules (budgets, caps, rate limits, access policies), proven in TypeScript by a small trusted
6
+ kernel, with an untrusted prover that writes the proofs, an optional Lean twin that checks the kernel itself, and GPU
7
+ searches that find counterexamples or the invariants a proof needs.
8
+
9
+ You write the rule once with typed builders and state what must always hold. The library proves it by induction (or
10
+ shows a real run that breaks it), and the same rule runs as an interpreter or ships as generated JavaScript.
11
+
12
+ ```sh
13
+ npm install gdp-wired # or bun add gdp-wired; plain JavaScript with types, Node 20+, Bun or a browser
14
+ ```
15
+
16
+ ```ts
17
+ import {min, nat, proveRule, rule, when} from 'gdp-wired'
18
+
19
+ // A request of cost c is admitted while spend s is under budget b; each request is capped at m.
20
+ const budget = rule('budget')
21
+ .params({b: nat, m: nat})
22
+ .inputs({c: nat})
23
+ .state({s: 0})
24
+ .step(({b, m, c, s}) => ({s: when(s.lt(b), s.plus(min(c, m)), s)}))
25
+ const bounded = budget.law('spend stays within b + m', ({s, b, m}) => s.atMost(b.plus(m)))
26
+
27
+ proveRule(budget, [bounded]) // {proven: true, checked: true}
28
+ ```
29
+
30
+ Names are typed: `({s, bb}) => …` or a law that reads the request `c` is a TypeScript error, not a runtime surprise.
31
+ Forget the cap (`s.plus(c)`) and the kernel refuses the proof; a search from the start turns it into a real run:
32
+ `{params: {b: 1, m: 0}, trace: [{c: 2}]}`. `bun examples/budget.ts` runs all of it, including the generated JavaScript,
33
+ and `examples/domains/` holds 92 rules from nine domains (finance, access control, public services, operations,
34
+ protocols, commerce, supply chains, games, education), each checked three ways.
35
+
36
+ ## Scope: where it replaces Bend, and where it doesn't
37
+
38
+ gdp-wired is meant to replace Bend where it matters in practice, not to be a language: laws about **stateful rules whose
39
+ state is counters, totals, maps of counters and logs summed over time**, proven automatically, with the rule shipped as
40
+ code. If the checker side finds new frontiers the scope may grow; until then:
41
+
42
+ | It expresses and proves | It does not (today) |
43
+ | --- | --- |
44
+ | Step machines over naturals: scalars, maps from naturals to naturals (one write per map per step), named step conditions, preconditions on params, assumptions per request | Lists, sets or strings as values: membership, subsets, equality of lists of names |
45
+ | Invariants of the state and params, laws about every key of a map; step laws relating one step's before and after (`s'`, `M'[k]`: "only increases", "stays retired") | Laws about runs of two or more steps ("a refund never follows a release" needs a state flag), or orderings beyond progress |
46
+ | Laws about one key of a map at a time, or about scalars | Totals across a map's keys ("the sum of every client's holdings equals in-use") |
47
+ | Histories: sums and queries over a log of requests, relations between them proven one record at a time, pruning | Unbounded recursion over data, quantifiers over sequences other than a machine's own history |
48
+ | Decisions over facts: flags, one-of choices, optional facts, bounded naturals (`match` covers every case) | Datatypes beyond those encodings; division, modulo, reals |
49
+ | Loops that halt by a rank or a tuple of ranks; folds over binary trees; progress laws ("some request makes this happen") | Arbitrary algorithms: there is no general recursion to prove things about |
50
+ | Arithmetic: `+`, truncated `−`, `×` by a constant, `min`, `max`, `if`, products of two terms (proven by the kernel, less well by the prover) | Generated code for decisions alone (`compileJs` emits machines; a predicate emitter is planned) |
51
+
52
+ Keep Bend where the specification is about **lists or sets of names** (resource scopes, capability subsets, normalization
53
+ that must refine its input), where a proof needs **induction over data structures**, or where the shipped code must be
54
+ the compiled proved source of an arbitrary algorithm. Use gdp-wired where the rule is a budget, a cap, a window, a
55
+ workflow or a policy decision: it proves those without proof files, finds counterexample runs when they're false, and
56
+ checks its own kernel in Lean.
57
+
58
+ For a sense of the split: in one real codebase with 98 Bend laws and about 1,600 lines of hand proofs, the policy
59
+ decisions, the compare-and-swap storage plans and the state/provider plans fit gdp-wired as it is (with names interned
60
+ to numbers), and gdp-wired can prove more about storage than its Bend proofs state (no revision reuse across keys); the
61
+ lifecycle machine's before/after laws are step laws (as in `release lifecycle` in `examples/`); the resource and authority checks, built on lists of names as
62
+ sets, stay in Bend. `compare/` puts three rules side by side in gdp-wired, Bend and plain Lean, with line counts and
63
+ check times.
64
+
65
+ ## What's trusted
66
+
67
+ | Part | Trusted? | What it does |
68
+ | --- | --- | --- |
69
+ | `kernel/kernel.ts`, `core/check.ts` | **Yes** | Turns a machine and its laws into goals (the induction rules: step machines with maps and flags, step laws, loops with ranks, progress, folds over trees, histories) and checks a proof tree for each: splits on linear atoms, a Fourier–Motzkin derivation at each leaf. Refuses names a machine doesn't declare |
70
+ | `kernel/prove.ts` | No | Grows the trees. If it's wrong, the kernel rejects the tree |
71
+ | `lean-kernel/` | Optional | The kernel's twin in Lean 4.34.0, each rule proven sound (`Kern.sound`, `machine_sound`, `mmachine_sound`, `fmmachine_sound`, `loop_sound`, `fold_sound`, `progress_sound`, `steplaw_sound`, `hmachine_sound`), axioms `propext`, `Classical.choice`, `Quot.sound` only. The exporter sends the kernel's real trees; tampered ones compute to false |
72
+ | `search/`, `synth/`, `hw/` | No | GPU work: counterexample search over every short history, and invariant/rank synthesis. Every hit is replayed by the interpreter or proven by the kernel before it counts |
73
+ | `core/dsl.ts`, the runtimes | No | The builders compile to the kernel's plain values; the interpreter and generated JavaScript are held to the reference evaluator by the conformance test |
74
+
75
+ How the untrusted parts are held to the trusted ones, automatically:
76
+
77
+ - **Types**: the whole repo type-checks strictly (`noImplicitReturns`, `noFallthroughCasesInSwitch`,
78
+ `noUncheckedIndexedAccess`), and a type-aware lint denies warnings; every switch over term and condition operations is
79
+ exhaustive and ends in `unreachable()`, so a new operation fails to compile wherever it's missing.
80
+ - **Conformance** (`core/conformance.test.ts`): random terms over every operation through every consumer (interpreter,
81
+ generated JS, the kernel on fixed values, the GPU search's u32 twin, the old symbolic prover) against the reference
82
+ evaluator, and the same cases through Lean's own `evalE` (`lean-kernel/conformance.ts`).
83
+ - **Examples** (`examples/`): every law proven by the kernel and re-checked by Lean, every rule run at random through
84
+ both runtimes, and every broken variant broken by a real run (a refused proof alone could be the prover's weakness).
85
+ - **Tampering and mutation** (`kernel/tamper.test.ts`, `kernel/refusals.test.ts`, `kernel/mutants.ts`): real proofs
86
+ broken structurally or offered for weakened goals that have a model must be rejected, every refusal the kernel makes is
87
+ exercised, and every one-operator mutation of `kernel.ts` and `check.ts` is run against the tests to find what they
88
+ don't notice: 222 of 241 mutants are caught, and the 19 left are equivalent (`kernel/MUTANTS.md`).
89
+ - **Fuzz**: the kernel never accepts a refutation of something with a model (`kernel/kernel.test.ts`), and laws proven
90
+ over histories hold on every run (`kernel/history.test.ts`).
91
+
92
+ ## Writing laws
93
+
94
+ The builders (`core/dsl.ts`) hand out typed values for every name you declare; everything compiles to the plain machine
95
+ the kernel checks, so nothing in them is trusted.
96
+
97
+ ```ts
98
+ import {bool, count, forall, maybe, nat, oneOf, proveSteps, relation, rule, yes} from 'gdp-wired'
99
+
100
+ // Maps and flags: a grant is decided once per step; each map is written once; a map law holds at every key.
101
+ const caps = rule('caps')
102
+ .params({d: nat})
103
+ .inputs({acct: nat, day: nat, ok: bool})
104
+ .maps({Acct: 0, Day: 0})
105
+ .flags(({ok, acct, day, d, Acct, Day}) => ({granted: ok.and(Acct.at(acct).lt(1), Day.at(day).lt(d))}))
106
+ .step(({acct, day, granted, Acct, Day}) => ({Acct: Acct.bump(acct, granted), Day: Day.bump(day, granted)}))
107
+ const dayCap = caps.law('day cap', ({Day, d}) => forall(k => Day.at(k).atMost(d)))
108
+
109
+ // Step laws: about one step, for every request the rule allows; `after` reads the state and maps after it.
110
+ const onlyUp = caps.stepLaw('no count goes down', ({Day}, after) => forall(k => Day.at(k).atMost(after.Day.at(k))))
111
+ const once = caps.stepLaw('a grant adds one to its day', ({day, Day}, after) => after.Day.at(day).eq(Day.at(day).plus(1)), ({granted}) => granted)
112
+ proveSteps(caps, [dayCap], [onlyUp, once]) // {proven: true, checked: true}
113
+
114
+ // Histories: a log of requests, a sum over it (state), a query over it at the request's time (an input the runtime
115
+ // computes), and a relation between them proven one record at a time.
116
+ const window = rule('window limit')
117
+ .params({w: nat, c: nat, now: nat})
118
+ .inputs({forced: bool, t: nat})
119
+ .log('forced', 't')
120
+ .sums({ours: (r, {now, w}) => count(r.forced.not().and(r.t.atMost(now), now.lt(r.t.plus(w))))})
121
+ .queries({seen: (r, {t, w}) => count(t.lt(r.t.plus(w)))})
122
+ .flags(({forced, seen, c}) => ({admitted: forced.not().and(seen.plus(1).atMost(c))}))
123
+ .keep(({forced, admitted}) => forced.or(admitted))
124
+ .relations(({t, now, ours, seen}) => [relation(ours.atMost(seen), t.atMost(now))])
125
+ .prune('t', ({w}) => w) // requests in time order: records older than the window are dropped
126
+
127
+ // Decisions over facts: optional facts and choices, every case covered.
128
+ const trial = rule('trial').inputs({plan: oneOf('free', 'pro'), spent: maybe(nat), allowance: maybe(nat)})
129
+ const failsClosed = trial.decide('a missing fact means no trial', ({spent}) => spent.isNone,
130
+ ({plan, spent, allowance}) => plan.match({free: () => spent.match({none: () => yes, some: s => allowance.match({none: () => yes, some: a => s.atLeast(a)})}), pro: () => yes}))
131
+ ```
132
+
133
+ - **Kinds**: `nat`, `upTo(k)`, `bool`, `oneOf(...)`, `maybe(kind)`; their assumptions (a bit is 0 or 1, a choice is in
134
+ range) go to the kernel by themselves, and `encodeInput`/`encodeParams` turn runtime values into the numbers the
135
+ machine reads.
136
+ - **Arithmetic** over the naturals: `plus`, `minus` (truncated), `times(k)`, `mul` (a product of two terms), `min`,
137
+ `max`, `when(cond, a, b)`, `count(cond)`; conditions `lt`, `atMost`, `eq`, `and`, `or`, `not`, `implies`.
138
+ - **Loops** (`loop(...)`): state, `while`, `body`, a `rank` (or a tuple of ranks) that must drop each turn.
139
+ - **Running it**: `rule.interpreter()` is persistent (searches branch from any state); `rule.compiled()` is what an app
140
+ ships: maps and the log updated in place, so a step costs the same however long the history. Both refuse params and
141
+ inputs that aren't naturals below 2^53 and any value past 2^53 − 1, where the proofs over the naturals stop describing
142
+ the run. The GPU searches refuse a space whose values could pass 2^32 − 1.
143
+
144
+ ## Numbers
145
+
146
+ On Modal CPUs (2 vCPU), generated JavaScript, the answers checked against the interpreter:
147
+
148
+ | Machine | 1k | 10k | 100k | 1M |
149
+ | --- | --- | --- | --- | --- |
150
+ | Per-key caps (`perf/grant-scale.ts`), requests | 0.3 ms | 2.5 ms | 29 ms | 267 ms |
151
+ | Window limit, pruned (`perf/window-scale.ts`), events | 0.8 ms | 3.1 ms | 16 ms | 136 ms |
152
+
153
+ Pruned, the window limit holds about 35 records whatever the event count; unpruned, each request scans every record kept
154
+ (76 ms at 10k events). The interpreter copies its maps each step (it's the reference, and searches branch from it):
155
+ 1.6 s for 10k requests of the caps rule.
156
+
157
+ A brute-force enumeration of 2^31 worlds (`enum/`, `perf/bend-enum/`), the same predicate as Bend programs and as a
158
+ Handwired word program: on an M2, Bend takes 985 ms sequential, 221 ms parallel and 153 ms on Metal (whole process,
159
+ about 130 ms of it device setup); ours takes 33 ms warm on WebGPU, with about 115 ms of device setup first. On an NVIDIA
160
+ L4 ours takes 5 ms warm; Bend's CUDA build compiled, but its GPU runs hung in our tests (its CPU builds ran: 109 ms
161
+ parallel).
162
+
163
+ ## Compared with Bend and Lean
164
+
165
+ `compare/` writes three rules (a budget, per-key caps, a sliding-window limit) in gdp-wired, in Bend 2 and in plain Lean
166
+ 4, each checked by its own tool on Modal, each also handed the same bug to reject. From `compare/README.md`:
167
+
168
+ | | Rule lines | Law lines | Proof lines | Check (median, whole command) |
169
+ | --- | --- | --- | --- | --- |
170
+ | gdp-wired | 5–7 | 1–4 | 1–2 (`proveRule`) | 25–44 ms |
171
+ | Bend 2 | 39–98 (25–50 of them proof evidence in the rule) | 5–26 | 74–117 | 137–161 ms (`--verdict` 167–178 ms) |
172
+ | Lean 4 (by hand) | 4–15 | 1–4 | 8–30 | 488–571 ms |
173
+
174
+ Most of each time is the tool's startup (17, 134 and 453 ms). gdp-wired's kernel trees for the same rules re-check in
175
+ its Lean twin in 1.35 s. What each proves, what each ships at runtime, and where each is the better tool are in
176
+ `compare/README.md`.
177
+
178
+ ## Layout
179
+
180
+ | Path | What |
181
+ | --- | --- |
182
+ | `core/` | Machines, the interpreter and JS compiler, the linear solver and its checker, Houdini invariants, models, the first Lean export |
183
+ | `kernel/` | The trusted kernel and the untrusted prover, with soundness fuzz (`kernel.test.ts`, `history.test.ts`) |
184
+ | `lean/`, `lean-kernel/` | Lean: the derivation checker `Cert.lean`, and the kernel's twin with its exported trees |
185
+ | `search/`, `hw/`, `modal-gpu/` | Counterexample search on the GPU: a hand-written WGSL kernel, and the same through the Handwired fork's word stages |
186
+ | `synth/` | Invariants and ranks found on the GPU, proven by the kernel |
187
+ | `next/` | Guards synthesized from laws, relational laws (migrations, tenant isolation), a ReBAC audit |
188
+ | `sat/` | Bit-vector laws: bit-blasted, solved, LRAT-checked in TypeScript, optionally by Lean |
189
+ | `enum/`, `perf/` | The enumeration, and the benchmarks |
190
+ | `examples/` | The builders at work: `domains/` (92 rules across nine domains, each with laws and broken variants), `harness.ts` (the triple check), `readme.ts`, `budget.ts` |
191
+ | `compare/` | The same three rules in gdp-wired, Bend and plain Lean, checked and measured |
192
+ | `spec.ts`, `gdp.ts`, `circuit.ts`, `laws.ts` | The audit path: gdp-ts predicates written once, as trusted provers per request and as a Handwired circuit that decides every (user, project) pair at once |
193
+ | `modal/` | Everything above as Modal jobs: `suite.json` is the regression suite |
194
+ | `vendor/handwired` | [monotykamary/handwired](https://github.com/monotykamary/handwired), branch `u32-words`: Cheng Lou's Handwired with exact u32 word circuits and word stages in loop programs |
195
+ | `vendor/gdp-ts` | [rauchg/gdp-ts](https://github.com/rauchg/gdp-ts) |
196
+
197
+ ## Running
198
+
199
+ The npm package is the library: the builders (`gdp-wired`), the runtimes (`gdp-wired/machine`), the kernel
200
+ (`gdp-wired/kernel`), the prover (`gdp-wired/prove`), the derivation checker and solver (`gdp-wired/check`,
201
+ `gdp-wired/solver`). Everything else here (examples, comparisons, the Lean twin, the GPU searches) runs from a clone and
202
+ needs [Bun](https://bun.sh); Lean 4.34.0 for `lean/` and `lean-kernel/`; a WebGPU runtime (Chrome, or Dawn's node
203
+ bindings) for the GPU parts. Releases: bump `version` in `package.json` and push to main;
204
+ `.github/workflows/publish.yml` publishes it with npm provenance.
205
+
206
+ ```sh
207
+ git clone --recursive https://github.com/monotykamary/gdp-wired && cd gdp-wired
208
+ bun examples/budget.ts
209
+ bun test core/compile.test.ts core/runtime.test.ts kernel/history.test.ts
210
+ cd kernel && bun run-kernel.ts && bun test kernel.test.ts
211
+ ```
212
+
213
+ The whole suite (107 jobs, about 15 minutes of wall time) runs on [Modal](https://modal.com):
214
+
215
+ ```sh
216
+ modal run modal/run.py --jobs modal/suite.json --out runs/modal/suite && sh modal/reap.sh
217
+ ```
218
+
219
+ Each directory's README has its own runs and results.
220
+
221
+ ## Credits
222
+
223
+ [gdp-ts](https://github.com/rauchg/gdp-ts) by Guillermo Rauch (Ghosts of Departed Proofs for TypeScript) is where the
224
+ audit path starts; [Handwired](https://github.com/chenglou/handwired) by Cheng Lou is the circuit and GPU runtime this
225
+ builds on, through a fork; [Bend](https://github.com/bendlang/bend) is the language the laws were first written in,
226
+ and the yardstick for the benchmarks.
227
+
228
+ ## License
229
+
230
+ MIT
@@ -0,0 +1,3 @@
1
+ import type { Constraint, Step } from './solver.js';
2
+ export declare function checkRefutation(constraints: Constraint[], steps: Step[]): boolean;
3
+ export declare function checkWitness(constraints: Constraint[], witness: Record<string, number>): boolean;
@@ -0,0 +1,31 @@
1
+ const vars = (lin) => Object.keys(lin).filter(key => key !== '1');
2
+ export function checkRefutation(constraints, steps) {
3
+ const rows = [];
4
+ for (const [i, s] of steps.entries()) {
5
+ if (s.kind === 'input') {
6
+ if (!(s.index >= 0 && s.index < constraints.length))
7
+ return false;
8
+ rows.push(constraints[s.index].lin);
9
+ }
10
+ else if (s.kind === 'combine') {
11
+ if (!(s.a < i && s.b < i && Number.isInteger(s.ma) && Number.isInteger(s.mb) && s.ma > 0 && s.mb > 0))
12
+ return false;
13
+ const out = {};
14
+ for (const [lin, m] of [[rows[s.a], s.ma], [rows[s.b], s.mb]])
15
+ for (const [key, c] of Object.entries(lin))
16
+ out[key] = (out[key] ?? 0) + m * c;
17
+ rows.push(out);
18
+ }
19
+ else {
20
+ const row = rows[s.of];
21
+ if (!(row !== undefined && s.of < i && Number.isInteger(s.g) && s.g > 0 && vars(row).every(key => row[key] % s.g === 0)))
22
+ return false;
23
+ rows.push({ ...Object.fromEntries(vars(row).map(key => [key, row[key] / s.g])), 1: Math.ceil((row['1'] ?? 0) / s.g) });
24
+ }
25
+ }
26
+ const last = rows.at(-1);
27
+ return last !== undefined && vars(last).every(key => last[key] === 0) && (last['1'] ?? 0) > 0;
28
+ }
29
+ export function checkWitness(constraints, witness) {
30
+ return constraints.every(c => Object.entries(c.lin).reduce((sum, [key, a]) => sum + a * (key === '1' ? 1 : (witness[key] ?? NaN)), 0) <= 0);
31
+ }
@@ -0,0 +1,267 @@
1
+ import { type C, type E, type Law, type Machine, type State } from './machine.js';
2
+ import { type Goal, type Loop, type StepLaw, type Tree } from '../kernel/kernel.js';
3
+ export type TermLike = Term | number;
4
+ export declare const termOf: (t: TermLike) => E;
5
+ export declare class Term {
6
+ readonly e: E;
7
+ constructor(e: E);
8
+ plus(b: TermLike): Term;
9
+ minus(b: TermLike): Term;
10
+ times(k: number): Term;
11
+ mul(b: TermLike): Term;
12
+ lt(b: TermLike): Cond;
13
+ atMost(b: TermLike): Cond;
14
+ gt(b: TermLike): Cond;
15
+ atLeast(b: TermLike): Cond;
16
+ eq(b: TermLike): Cond;
17
+ ne(b: TermLike): Cond;
18
+ }
19
+ export declare class Cond {
20
+ readonly c: C;
21
+ constructor(c: C);
22
+ and(...xs: Cond[]): Cond;
23
+ or(...xs: Cond[]): Cond;
24
+ not(): Cond;
25
+ implies(b: Cond): Cond;
26
+ iff(b: Cond): Cond;
27
+ }
28
+ export declare const lit: (v: number) => Term;
29
+ export declare const yes: Cond;
30
+ export declare const no: Cond;
31
+ export declare const all: (...cs: Cond[]) => Cond;
32
+ export declare const any: (...cs: Cond[]) => Cond;
33
+ export declare const not: (c: Cond) => Cond;
34
+ export declare const min: (a: TermLike, b: TermLike) => Term;
35
+ export declare const max: (a: TermLike, b: TermLike) => Term;
36
+ export declare const when: (c: Cond, then: TermLike, otherwise: TermLike) => Term;
37
+ export declare const choose: (c: Cond, then: Cond, otherwise: Cond) => Cond;
38
+ export declare const count: (c: Cond) => Term;
39
+ export declare const sum: (first: TermLike, ...rest: TermLike[]) => Term;
40
+ type Rand = (k: number) => number;
41
+ type Raw = number | boolean | string | null | undefined;
42
+ export declare abstract class Kind<V> {
43
+ readonly _value: V;
44
+ abstract vars(name: string): string[];
45
+ abstract assume(name: string): Cond[];
46
+ abstract value(name: string, prefix?: string): V;
47
+ abstract encode(name: string, raw: Raw): Record<string, number>;
48
+ abstract random(name: string, rand: Rand, bound: number): Record<string, number>;
49
+ }
50
+ export declare const nat: Kind<Term>;
51
+ export declare const upTo: (maxValue: number) => Kind<Term>;
52
+ export declare class Bool extends Cond {
53
+ readonly n: Term;
54
+ constructor(n: Term);
55
+ }
56
+ export declare const bool: Kind<Bool>;
57
+ export declare class Choice<K extends string> {
58
+ readonly n: Term;
59
+ readonly values: readonly K[];
60
+ constructor(n: Term, values: readonly K[]);
61
+ is(k: K): Cond;
62
+ match(cases: {
63
+ [k in K]: () => Cond;
64
+ }): Cond;
65
+ match(cases: {
66
+ [k in K]: () => TermLike;
67
+ }): Term;
68
+ }
69
+ export declare const oneOf: <const K extends string>(...values: K[]) => Kind<Choice<K>>;
70
+ export declare class Maybe<V> {
71
+ private readonly present;
72
+ private readonly inner;
73
+ constructor(present: Term, inner: V);
74
+ get isSome(): Cond;
75
+ get isNone(): Cond;
76
+ match(cases: {
77
+ none: () => Cond;
78
+ some: (value: V) => Cond;
79
+ }): Cond;
80
+ match(cases: {
81
+ none: () => TermLike;
82
+ some: (value: V) => TermLike;
83
+ }): Term;
84
+ }
85
+ export declare const maybe: <V>(of: Kind<V>) => Kind<Maybe<V>>;
86
+ export type MapWrite = {
87
+ readonly map: string;
88
+ readonly key: E;
89
+ readonly value: E;
90
+ };
91
+ export declare class MapRef {
92
+ readonly name: string;
93
+ readonly init: number;
94
+ constructor(name: string, init: number);
95
+ at(key: TermLike): Term;
96
+ set(key: TermLike, value: TermLike): MapWrite;
97
+ bump(key: TermLike, c?: Cond, by?: TermLike): MapWrite;
98
+ }
99
+ export type Forall = {
100
+ readonly forall: (key: Term) => Cond;
101
+ };
102
+ export declare const forall: (f: (key: Term) => Cond) => Forall;
103
+ export type Relation = {
104
+ readonly when: Cond;
105
+ readonly holds: Cond;
106
+ };
107
+ export declare const relation: (holds: Cond, whenever?: Cond) => Relation;
108
+ type Kinds = Record<string, Kind<unknown>>;
109
+ type ValuesOf<K extends Kinds> = {
110
+ [n in keyof K]: K[n] extends Kind<infer V> ? V : never;
111
+ };
112
+ type Obj = object;
113
+ type Update<S extends Obj, M extends Obj> = Partial<{
114
+ [k in keyof S]: TermLike;
115
+ } & {
116
+ [k in keyof M]: MapWrite;
117
+ }>;
118
+ type Spec = {
119
+ name: string;
120
+ params: [string, Kind<unknown>][];
121
+ inputs: [string, Kind<unknown>][];
122
+ state: [string, number | ((p: never) => TermLike)][];
123
+ maps: [string, number][];
124
+ flags: ((env: never) => Record<string, Cond>)[];
125
+ step?: (env: never) => Record<string, TermLike | MapWrite | undefined>;
126
+ pre: ((p: never) => Cond)[];
127
+ assume: ((env: never) => Cond)[];
128
+ record?: string[];
129
+ sums: [string, (r: never, p: never) => TermLike][];
130
+ queries: [string, (r: never, v: never) => TermLike][];
131
+ keep?: (env: never) => Cond;
132
+ relations?: (env: never) => Relation[];
133
+ prune?: {
134
+ order: string;
135
+ after: (p: never) => TermLike;
136
+ };
137
+ };
138
+ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj = {}, M extends Obj = {}, F extends Obj = {}, R extends Obj = {}, Su extends Obj = {}, Q extends Obj = {}> {
139
+ private readonly spec;
140
+ constructor(spec: Spec);
141
+ private with;
142
+ get name(): string;
143
+ params<K extends Kinds>(kinds: K): Rule<P & ValuesOf<K>, I, S, M, F, R, Su, Q>;
144
+ inputs<K extends Kinds>(kinds: K): Rule<P, I & ValuesOf<K>, S, M, F, R, Su, Q>;
145
+ state<T extends Record<string, number | ((p: P) => TermLike)>>(init: T): Rule<P, I, S & {
146
+ [k in keyof T]: Term;
147
+ }, M, F, R, Su, Q>;
148
+ maps<T extends Record<string, number>>(init: T): Rule<P, I, S, M & {
149
+ [k in keyof T]: MapRef;
150
+ }, F, R, Su, Q>;
151
+ flags<T extends Record<string, Cond>>(f: (env: P & I & S & M & F & Su & Q) => T): Rule<P, I, S, M, F & {
152
+ [k in keyof T]: Cond;
153
+ }, R, Su, Q>;
154
+ step(f: (env: P & I & S & M & F & Su & Q) => Update<S, M>): Rule<P, I, S, M, F, R, Su, Q>;
155
+ pre(f: (p: P) => Cond): Rule<P, I, S, M, F, R, Su, Q>;
156
+ assume(f: (env: P & I & S & M & F & Su & Q) => Cond): Rule<P, I, S, M, F, R, Su, Q>;
157
+ log<const N extends keyof I & string>(...record: N[]): Rule<P, I, S, M, F, Pick<I, N>, Su, Q>;
158
+ sums<T extends Record<string, (r: R, p: P) => TermLike>>(sums: T): Rule<P, I, S, M, F, R, Su & {
159
+ [k in keyof T]: Term;
160
+ }, Q>;
161
+ queries<T extends Record<string, (r: R, v: P & I) => TermLike>>(queries: T): Rule<P, I, S, M, F, R, Su, Q & {
162
+ [k in keyof T]: Term;
163
+ }>;
164
+ keep(f: (env: P & I & S & M & F & Su & Q) => Cond): Rule<P, I, S, M, F, R, Su, Q>;
165
+ relations(f: (env: P & I & Su & Q) => Relation[]): Rule<P, I, S, M, F, R, Su, Q>;
166
+ prune(order: keyof R & string, after: (p: P) => TermLike): Rule<P, I, S, M, F, R, Su, Q>;
167
+ law(name: string, f: (env: P & S & M & Su) => Cond | Forall): Law;
168
+ stepLaw(name: string, then: (before: P & I & S & M & F & Su & Q, after: S & M & Su) => Cond | Forall, when?: (before: P & I & S & M & F & Su & Q) => Cond): StepLaw;
169
+ decide(name: string, given: (env: P & I) => Cond | Cond[], then: (env: P & I) => Cond): Decision;
170
+ private kindAssumptions;
171
+ private env;
172
+ get machine(): Machine;
173
+ encodeParams(values: {
174
+ [k in keyof P]: Raw;
175
+ }): Record<string, number>;
176
+ encodeInput(values: {
177
+ [k in keyof I]: Raw;
178
+ }): Record<string, number>;
179
+ randomParams(rand: Rand, bound: number): Record<string, number>;
180
+ randomInput(rand: Rand, bound: number): Record<string, number>;
181
+ get orderedBy(): string | undefined;
182
+ interpreter(): {
183
+ init: (params: {
184
+ [x: string]: number;
185
+ }) => State;
186
+ queries: (params: {
187
+ [x: string]: number;
188
+ }, st: State, input: {
189
+ [x: string]: number;
190
+ }) => {
191
+ [x: string]: number;
192
+ };
193
+ step: (params: {
194
+ [x: string]: number;
195
+ }, st: State, input: {
196
+ [x: string]: number;
197
+ }) => State;
198
+ };
199
+ compiled(): {
200
+ init: (params: object) => State & {
201
+ head?: number;
202
+ };
203
+ fork: (st: State) => State;
204
+ step: (params: object, st: State, input: object) => State & {
205
+ head?: number;
206
+ };
207
+ };
208
+ }
209
+ export declare const rule: (name: string) => Rule;
210
+ type AnyOf<R, K extends 'decide' | 'step' | 'law'> = R extends Rule<infer P, infer I, infer S, infer M, infer F, any, infer Su, infer Q> ? K extends 'decide' ? P & I : K extends 'law' ? P & S & M & Su : P & I & S & M & F & Su & Q : never;
211
+ export type DecisionEnv<R> = AnyOf<R, 'decide'>;
212
+ export type StepEnv<R> = AnyOf<R, 'step'>;
213
+ export type LawEnv<R> = AnyOf<R, 'law'>;
214
+ export type Decision = {
215
+ name: string;
216
+ rule: Rule<Obj>;
217
+ given: C[];
218
+ then: C;
219
+ goal: Goal;
220
+ };
221
+ export declare class LoopRule<P extends Obj = {}, I extends Obj = {}, S extends Obj = {}> {
222
+ private readonly spec;
223
+ constructor(spec: {
224
+ name: string;
225
+ params: [string, Kind<unknown>][];
226
+ inputs: [string, Kind<unknown>][];
227
+ state: [string, number | ((p: never) => TermLike)][];
228
+ while?: (env: never) => Cond;
229
+ body?: (env: never) => Record<string, TermLike | undefined>;
230
+ pre: ((p: never) => Cond)[];
231
+ assume: ((env: never) => Cond)[];
232
+ rank?: (env: never) => TermLike | TermLike[];
233
+ });
234
+ private with;
235
+ params<K extends Kinds>(kinds: K): LoopRule<P & ValuesOf<K>, I, S>;
236
+ inputs<K extends Kinds>(kinds: K): LoopRule<P, I & ValuesOf<K>, S>;
237
+ state<T extends Record<string, number | ((p: P) => TermLike)>>(init: T): LoopRule<P, I, S & {
238
+ [k in keyof T]: Term;
239
+ }>;
240
+ while(f: (env: P & S) => Cond): LoopRule<P, I, S>;
241
+ body(f: (env: P & I & S) => Partial<{
242
+ [k in keyof S]: TermLike;
243
+ }>): LoopRule<P, I, S>;
244
+ pre(f: (p: P) => Cond): LoopRule<P, I, S>;
245
+ assume(f: (env: P & I & S) => Cond): LoopRule<P, I, S>;
246
+ rank(f: (env: P & S) => TermLike | TermLike[]): LoopRule<P, I, S>;
247
+ law(name: string, f: (env: P & S) => Cond): Law;
248
+ private env;
249
+ get loop(): Loop;
250
+ get rankTerms(): E[];
251
+ randomParams(rand: Rand, bound: number): Record<string, number>;
252
+ randomInput(rand: Rand, bound: number): Record<string, number>;
253
+ }
254
+ export declare const loop: (name: string) => LoopRule;
255
+ export type Verdict = {
256
+ proven: boolean;
257
+ checked: boolean;
258
+ stuck?: string;
259
+ goal?: string;
260
+ witness?: Record<string, number>;
261
+ trees?: Record<string, Tree>;
262
+ };
263
+ export declare function proveRule(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj>, laws: Law[]): Verdict;
264
+ export declare function proveSteps(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj>, laws: Law[], stepLaws: StepLaw[]): Verdict;
265
+ export type { StepLaw };
266
+ export declare const proveDecision: (d: Decision) => Verdict;
267
+ export declare const proveLoop: (l: LoopRule<Obj, Obj, Obj>, laws: Law[]) => Verdict;