gdp-wired 0.7.0 → 0.9.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 CHANGED
@@ -61,6 +61,12 @@ All of these are proven automatically, and each proof is also a Lean theorem:
61
61
  - **Refinement**: an implementation behaves like a simpler spec, step for step (`examples/composition.ts`).
62
62
  - **Functions**: division, remainder, and your own functions, known by their facts; a function defined in Lean brings
63
63
  facts proven there (`examples/functions.ts`).
64
+ - **Lemmas you state**: a function defined by a recurrence (`recur`), of one argument or several, reading other
65
+ functions, and lemmas about it proven by induction, at one point or between two, which every rule that reads it may
66
+ then use: a backoff delay is at most 64 at every attempt and never shrinks (`examples/lemmas.ts`).
67
+ - **Records and variants**: inputs, state, results, map cells and list items made of named fields, or one of several
68
+ cases each with fields of its own (a payment by card or in cash, a seat free, held or sold), every case covered
69
+ (`examples/datatypes.ts`).
64
70
  - **Pure functions and two-run laws**: laws about what a function returns, and laws comparing two calls, like "the
65
71
  secret never changes the answer". `examples/jev-fabric/` ports a Bend project's policy core and proves all 59 laws its
66
72
  Bend proofs state.
@@ -71,7 +77,7 @@ synthesizer finds one and the kernel proves it.
71
77
  **What it leaves to Lean or Bend.** Two things, and neither is a gap the next feature closes: proofs the prover would
72
78
  have to invent (induction over a recursive structure whose proof needs a lemma nobody stated, and arithmetic with
73
79
  products of unknowns beyond multiplying two known facts; integer arithmetic with induction or with products is
74
- undecidable), and shipping an arbitrary algorithm as its proved source (gdp-wired ships the rule as JavaScript; Bend
80
+ undecidable; a lemma you state about a recurrence is proven, but a recursive datatype as a value is Lean's), and shipping an arbitrary algorithm as its proved source (gdp-wired ships the rule as JavaScript; Bend
75
81
  compiles the proved source itself). The full list of what is and isn't in scope is in
76
82
  [`docs/comparison.md`](docs/comparison.md#gdp-wireds-scope-in-detail).
77
83
 
@@ -145,7 +151,7 @@ The builders (`core/dsl.ts`) hand out typed values for every name you declare; e
145
151
  the kernel checks, so nothing in them is trusted.
146
152
 
147
153
  ```ts
148
- import {bool, count, forall, maybe, nat, oneOf, proveSteps, relation, rule, yes} from 'gdp-wired'
154
+ import {bool, count, forall, fun, maybe, min, nat, no, oneOf, proveSteps, record, recur, relation, rule, variant, when, yes} from 'gdp-wired'
149
155
 
150
156
  // Maps and flags: a grant is decided once per step; each map is written once; a map law holds at every key.
151
157
  const caps = rule('caps')
@@ -180,12 +186,39 @@ const window = rule('window limit')
180
186
  const trial = rule('trial').inputs({plan: oneOf('free', 'pro'), spent: maybe(nat), allowance: maybe(nat)})
181
187
  const failsClosed = trial.decide('a missing fact means no trial', ({spent}) => spent.isNone,
182
188
  ({plan, spent, allowance}) => plan.match({free: () => spent.match({none: () => yes, some: s => allowance.match({none: () => yes, some: a => s.atLeast(a)})}), pro: () => yes}))
189
+
190
+ // Records and variants: a payment is by card or in cash, each with fields of its own, read only in `match`.
191
+ const Pay = variant({card: {amount: nat, verified: bool}, cash: {amount: nat}})
192
+ const approve = fun('approve').inputs({pay: Pay, who: record({limit: nat, blocked: bool})})
193
+ .returns(({pay, who}) => ({ok: who.blocked.not().and(pay.match({card: c => c.verified.and(c.amount.atMost(who.limit)), cash: c => c.amount.atMost(200)}))}))
194
+ const unverified = approve.law('an unverified card is never approved', ({pay}, out) => [pay.match({card: c => c.verified.not(), cash: () => no}), out.ok], () => no)
195
+
196
+ // A lemma you state, proven by induction: the backoff delay is defined by its recurrence, and its lemmas are facts
197
+ // every rule that reads it may use (its two equations alone don't bound it at an unknown attempt).
198
+ const delay = recur('delay', {zero: 1, next: (_, d) => min(d.times(2), 64)}, {'at least 1': (_, d) => d.atLeast(1), 'at most 64': (_, d) => d.atMost(64)})
199
+ const retry = rule('retry').inputs({failed: bool, now: nat}).state({tries: 0, next: 0, last: 0}).fns({delay})
200
+ .assume(({now, last}) => now.atLeast(last))
201
+ .step(({failed, now, tries, next, delay}) => ({tries: when(failed, tries.plus(1), 0), next: when(failed.and(now.atLeast(next)), now.plus(delay(tries)), next), last: now}))
202
+ const soon = retry.law('the next try is at most 64 after the last request', ({next, last}) => next.atMost(last.plus(64)))
183
203
  ```
184
204
 
185
205
 
186
- - **Kinds**: `nat`, `upTo(k)`, `bool`, `oneOf(...)`, `maybe(kind)`, `int`, `decimal(places)`, `str({like, maxLength})`;
187
- their assumptions (a bit is 0 or 1, a choice is in range) go to the kernel by themselves, and
188
- `encodeInput`/`encodeParams` turn runtime values into the numbers the machine reads.
206
+ - **Kinds**: `nat`, `upTo(k)`, `bool`, `oneOf(...)`, `maybe(kind)`, `int`, `decimal(places)`, `str({like, maxLength})`,
207
+ `record({...})`, `variant({...})`; their assumptions (a bit is 0 or 1, a choice is in range) go to the kernel by
208
+ themselves, and `encodeInput`/`encodeParams` turn runtime values into the numbers the machine reads.
209
+ - **Records and variants**: `record({limit: nat, blocked: bool})` hands out an object of its fields' values;
210
+ `variant({card: {...}, cash: {...}})` a value with `is(case)` and `match({card: c => ..., cash: c => ...})`, the only
211
+ place a case's fields are read. `Pay.make({tag: 'card', amount, verified})` builds a case to write or to pass to
212
+ `when`, and `same(a, b)` compares two values of one kind part by part; `optional(c, value)` is a value present where c
213
+ holds (a result that may be missing). At runtime a record is an object of its fields and a variant `{tag: 'card',
214
+ ...fields}` (or the key given, `variant({...}, {tag: 'kind'})`); a field or case the kind doesn't declare is refused.
215
+ What a kind keeps true of the values a step writes (a case in range, a flag 0 or 1) is a law of its own, `r.kindLaws()`,
216
+ which proveRule, proveSteps and proveLeadsTo prove beside the laws they are given. Both are their fields'
217
+ variables underneath (`pay.card.amount`, and the case's index as `pay`), so the kernel, both runtimes and the Lean
218
+ twin see nothing new. Maps and lists of them: `.maps({Acct: record({...}).from({...})})` (`Acct.at(k)`,
219
+ `Acct.set(k, value)`, `Acct.each(k => value)`) and `.items({Queue: Job})` (`push`, `pop`, `set`, `insertAt`,
220
+ `removeAt`, `apply`, `has`, `within`, `increasing(by)`), one map per variable of the kind; `rule.decode` reads them back
221
+ as objects and arrays. Not as values: recursive types (a tree).
189
222
  - **State of any kind**: `.state({open: bool.from(true), status: oneOf('new', 'done').from('new'), owner: str().from(''), note: maybe(nat).from(null)})`;
190
223
  a step writes each kind's own values, `when(c, a, b)` chooses between any two values of a kind, and `rule.decode(state)`
191
224
  reads them back as runtime values.
@@ -206,6 +239,18 @@ const failsClosed = trial.decide('a missing fact means no trial', ({spent}) => s
206
239
  take arguments, `perMinute(t)`; the kernel knows each only by its facts (`.facts(...)`, and a library function's
207
240
  own), and the Lean export takes them as the hypothesis they are, proving them where the function has a Lean
208
241
  definition. A function defined and proven in Lean is how a rule reaches past what the kernel decides: `examples/functions.ts`.
242
+ - **Recurrences and their lemmas** (`recur(name, {args, fns, zero, next}, lemmas, between)`): f(0, a..) = zero and
243
+ f(x + 1, a..) = next(x, f(x, a..), a..), the further arguments `args` (up to four) fixed along the recursion, each term
244
+ the kernel's own or a read of a function in `fns` (`modBy(7)`, another recurrence), the start's too. Each lemma is a law about every x
245
+ and f(x), proven when the function is made, all of them together by induction (the recurrence run as a machine, each
246
+ lemma a law of it, so one lemma may lean on another, and on the facts of the functions it reads); each law `between`
247
+ is about every x <= y and f(x), f(y) ("never shrinks"), proven by induction on y from x, with the lemmas. One that
248
+ isn't proven throws `Unproven` with the induction step it breaks at. The two equations, the lemmas and the laws
249
+ between are the function's facts. The runtimes compute f by the recurrence, continuing from the last value computed at
250
+ the same further arguments (a rule that reads f at a growing count takes one step a request); the Lean export defines
251
+ `Recur.<name>` by it (a function of several arguments through an injective pairing, proven so in Lean) and checks the
252
+ kernel's own proof of every lemma and law between: the machine it was proven by, with its trees, and the run of it that
253
+ reaches x and f(x), so a lemma with a product (squares: f(x) = x·x) needs no tactic of Lean's (`examples/lemmas.ts`).
209
254
  - **Liveness** (`rule.leadsTo(name, {from, to, rank, helpful})`, `proveLeadsTo`): whenever `from` holds, `to` holds later in
210
255
  every run that takes each helpful request infinitely often; proven by a rank no step raises while waiting and each
211
256
  helpful request lowers in its region. `rank` may return several terms (compared in order) and `helpful` may be a list
@@ -268,7 +313,7 @@ Lean, compiled natively, wins where the state stays small. Details in [`docs/com
268
313
  | `core/` | Machines, the interpreter and JS compiler, the builders, composition (`compose.ts`), the linear solver and its checker, Houdini invariants, models, the first Lean export |
269
314
  | `kernel/` | The trusted kernel and the untrusted prover, with soundness fuzz (`kernel.test.ts`, `history.test.ts`) |
270
315
  | `lean-kernel/` | The kernel's Lean twin, its soundness proofs, and the exported trees |
271
- | `examples/` | The builders at work: `domains/` (187 rules across 21 domains, each with laws and broken variants), `harness.ts` (the triple check), `readme.ts`, `budget.ts`, `gdp.ts` (gdp-ts's example, its checks proven), `composition.ts`, `sets.ts`, `quantifiers.ts`, `functions.ts`, `lists.ts`, `sizes.ts`, `liveness.ts`, `steps.ts` (laws over up to 16 steps), and `jev-fabric/` (a Bend project's 59 proven laws, ported) |
316
+ | `examples/` | The builders at work: `domains/` (187 rules across 21 domains, each with laws and broken variants), `harness.ts` (the triple check), `readme.ts`, `budget.ts`, `gdp.ts` (gdp-ts's example, its checks proven), `datatypes.ts` (records and variants), `lemmas.ts` (recurrences and their lemmas), `composition.ts`, `sets.ts`, `quantifiers.ts`, `functions.ts`, `lists.ts`, `sizes.ts`, `liveness.ts`, `steps.ts` (laws over up to 16 steps), and `jev-fabric/` (a Bend project's 59 proven laws, ported) |
272
317
  | `docs/` | The comparison with Bend and Lean, and the full scope |
273
318
  | `compare/` | The same three rules in gdp-wired, Bend and plain Lean, checked and measured |
274
319
  | `research/` | Everything around the library ([`research/README.md`](research/README.md)): the GPU searches (`search/`, `hw/`, `modal-gpu/`), invariant and rank synthesis (`synth/`), relational laws and guard synthesis (`next/`), bit-vector laws (`sat/`), the enumeration and benchmarks (`enum/`, `gpu/`, `perf/`), the first Lean checker (`lean/`) and the first prototype, the audit path (`audit/`) |
@@ -1,4 +1,4 @@
1
- import { type C, type E, type Law, type Machine, type Request, type State } from './machine.js';
1
+ import { type C, type E, type Law, type Recurrence, type Machine, type Request, type State } from './machine.js';
2
2
  import { type Goal, type Liveness, type Loop, type StepLaw, type Tree } from '../kernel/kernel.js';
3
3
  export type TermLike = Term | number;
4
4
  export declare const termOf: (t: TermLike) => E;
@@ -43,11 +43,17 @@ export declare function when<K extends string>(c: Cond, then: Choice<K>, otherwi
43
43
  export declare function when(c: Cond, then: Str, otherwise: Str): Str;
44
44
  export declare function when<V>(c: Cond, then: Maybe<V>, otherwise: Maybe<V>): Maybe<V>;
45
45
  export declare function when(c: Cond, then: Cond, otherwise: Cond): Cond;
46
+ export declare function when<C extends Cases, T extends string>(c: Cond, then: Variant<C, T>, otherwise: Variant<C, T>): Variant<C, T>;
47
+ export declare function when<T extends Record<string, unknown>>(c: Cond, then: T, otherwise: T): T;
46
48
  export declare const choose: (c: Cond, then: Cond, otherwise: Cond) => Cond;
49
+ export declare function same(a: unknown, b: unknown): Cond;
47
50
  export declare const count: (c: Cond) => Term;
48
51
  export declare const sum: (first: TermLike, ...rest: TermLike[]) => Term;
49
52
  type Rand = (k: number) => number;
50
- type Raw = number | boolean | string | null | undefined;
53
+ export type Raw = number | boolean | string | null | undefined | RawObject | readonly Raw[];
54
+ export type RawObject = {
55
+ readonly [field: string]: Raw;
56
+ };
51
57
  export type At = string | ((name: string) => string);
52
58
  export type Init<V> = {
53
59
  readonly kind: Kind<V>;
@@ -57,7 +63,9 @@ export declare abstract class Kind<V> {
57
63
  readonly _value: V;
58
64
  abstract vars(name: string): string[];
59
65
  abstract assume(name: string): Cond[];
60
- abstract value(name: string, at?: At): V;
66
+ abstract build(name: string, get: (part: string) => Term): V;
67
+ value(name: string, at?: At): V;
68
+ invariant(_value: V): Cond[];
61
69
  abstract encode(name: string, raw: Raw): Record<string, number>;
62
70
  abstract random(name: string, rand: Rand, bound: number): Record<string, number>;
63
71
  abstract store(name: string, value: unknown): Record<string, TermLike>;
@@ -145,6 +153,66 @@ export declare class Maybe<V> {
145
153
  }): Term;
146
154
  }
147
155
  export declare const maybe: <V>(of: Kind<V>) => Kind<Maybe<V>>;
156
+ export declare const optional: <V>(c: Cond, value: V) => Maybe<V>;
157
+ type Fields = Record<string, Kind<unknown>>;
158
+ export declare const record: <const K extends Fields>(fields: K) => Kind<ValuesOf<K>>;
159
+ type Cases = Record<string, Fields>;
160
+ type CaseValues<C extends Cases> = {
161
+ [k in keyof C]: ValuesOf<C[k]>;
162
+ };
163
+ type MatchCases<C extends Cases, T> = {
164
+ [k in keyof C]: (fields: ValuesOf<C[k]>) => T;
165
+ };
166
+ export declare class Variant<C extends Cases = Cases, T extends string = 'tag'> {
167
+ readonly tag: Term;
168
+ readonly cases: CaseValues<C>;
169
+ readonly kind: VariantKind<C, T>;
170
+ constructor(tag: Term, cases: CaseValues<C>, kind: VariantKind<C, T>);
171
+ is(k: keyof C & string): Cond;
172
+ match(cases: MatchCases<C, Cond>): Cond;
173
+ match(cases: MatchCases<C, TermLike>): Term;
174
+ match<T>(cases: MatchCases<C, T>): T;
175
+ }
176
+ export type VariantWrite<C extends Cases, T extends string = 'tag'> = {
177
+ [k in keyof C]: {
178
+ [f in T | keyof C[k]]: f extends T ? k : C[k][f] extends Kind<infer V> ? Writes<V> : never;
179
+ };
180
+ }[keyof C];
181
+ export declare class VariantKind<C extends Cases, T extends string = 'tag'> extends Kind<Variant<C, T>> {
182
+ readonly cases: C;
183
+ readonly tagKey: T;
184
+ readonly names: (keyof C & string)[];
185
+ constructor(cases: C, tagKey: T);
186
+ index(k: string): number;
187
+ private payload;
188
+ private filled;
189
+ private at;
190
+ vars(name: string): string[];
191
+ assume(name: string): Cond[];
192
+ build(name: string, get: (part: string) => Term): Variant<C, T>;
193
+ invariant(x: Variant<C, T>): Cond[];
194
+ make(write: VariantWrite<C, T>): Variant<C, T>;
195
+ store(name: string, value: unknown): Record<string, TermLike>;
196
+ decode(name: string, vars: Record<string, number>): {
197
+ [x: string]: (keyof C & string) | Raw;
198
+ };
199
+ empty(): {
200
+ [x: string]: (keyof C & string) | Raw;
201
+ };
202
+ encode(name: string, raw: Raw): {
203
+ [x: string]: number;
204
+ };
205
+ random(name: string, rand: Rand, bound: number): {
206
+ [x: string]: number;
207
+ };
208
+ sample(rand: Rand, bound: number): {
209
+ [x: string]: (keyof C & string) | Raw;
210
+ };
211
+ }
212
+ export declare function variant<const C extends Cases>(cases: C): VariantKind<C>;
213
+ export declare function variant<const C extends Cases, const T extends string>(cases: C, opts: {
214
+ tag: T;
215
+ }): VariantKind<C, T>;
148
216
  export declare const intern: ((text: string) => number) & {
149
217
  load(entries: [string, number][]): void;
150
218
  };
@@ -225,10 +293,46 @@ export declare class ListRef {
225
293
  within(): Forall;
226
294
  increasing(): Forall;
227
295
  }
296
+ export type PartsWrite = {
297
+ readonly parts: Record<string, MapWrite>;
298
+ };
299
+ export declare class CellsRef<V> {
300
+ readonly name: string;
301
+ readonly kind: Kind<V>;
302
+ private readonly map;
303
+ constructor(name: string, kind: Kind<V>, map: (part: string) => MapRef);
304
+ at(key: TermLike): V;
305
+ set(key: TermLike, value: Writes<V>): PartsWrite;
306
+ each(f: (key: Term) => Writes<V>): PartsWrite;
307
+ }
308
+ export type ItemsOp = {
309
+ readonly when: Cond;
310
+ readonly parts: Record<string, ListOp>;
311
+ readonly len: TermLike;
312
+ };
313
+ export declare class ItemsRef<V> {
314
+ readonly name: string;
315
+ readonly kind: Kind<V>;
316
+ readonly len: Term;
317
+ private readonly list;
318
+ constructor(name: string, kind: Kind<V>, len: Term, list: (part: string) => ListRef);
319
+ private get names();
320
+ at(i: TermLike): V;
321
+ has(x: V): Cond;
322
+ private op;
323
+ push(x: Writes<V>, when?: Cond): ItemsOp;
324
+ pop(when?: Cond): ItemsOp;
325
+ set(i: TermLike, x: Writes<V>, when?: Cond): ItemsOp;
326
+ insertAt(i: TermLike, x: Writes<V>, when?: Cond): ItemsOp;
327
+ removeAt(i: TermLike, when?: Cond): ItemsOp;
328
+ apply(...ops: ItemsOp[]): Record<string, TermLike | PartsWrite>;
329
+ within(): Forall;
330
+ increasing(by: (x: V) => Term): Forall;
331
+ }
228
332
  export type Forall = {
229
333
  readonly forall: (key: Term, ...more: Term[]) => Cond;
230
334
  };
231
- export declare function forall(f: (key: Term, key2: Term, key3: Term, key4: Term) => Cond): Forall;
335
+ export declare function forall(f: (key: Term, key2: Term, key3: Term, key4: Term, key5: Term, key6: Term) => Cond): Forall;
232
336
  export type Relation = {
233
337
  readonly when: Cond;
234
338
  readonly holds: Cond;
@@ -239,7 +343,9 @@ export type FnDef = {
239
343
  arity: number;
240
344
  js: string | ((...args: number[]) => number);
241
345
  lean?: string;
242
- facts?: (f: (...args: TermLike[]) => Term) => Record<string, Cond | Forall>;
346
+ facts?: (f: (...args: TermLike[]) => Term, uses?: Record<string, (...args: TermLike[]) => Term>) => Record<string, Cond | Forall>;
347
+ recur?: Recurrence;
348
+ uses?: Record<string, FnDef>;
243
349
  };
244
350
  export declare const fn: (arity: number, js: FnDef["js"], opts?: {
245
351
  lean?: string;
@@ -247,16 +353,33 @@ export declare const fn: (arity: number, js: FnDef["js"], opts?: {
247
353
  }) => FnDef;
248
354
  export declare const divBy: (c: number) => FnDef;
249
355
  export declare const modBy: (c: number) => FnDef;
356
+ type FnTerm = (...args: TermLike[]) => Term;
357
+ type RecurEnv<A extends string, U> = {
358
+ [a in A]: Term;
359
+ } & {
360
+ [u in keyof U]: FnTerm;
361
+ };
362
+ export type RecurDef<A extends string, U extends Record<string, FnDef>> = {
363
+ args?: readonly A[];
364
+ fns?: U;
365
+ zero: TermLike | ((env: RecurEnv<A, U>) => TermLike);
366
+ next: (x: Term, prev: Term, env: RecurEnv<A, U>) => TermLike;
367
+ };
368
+ export declare const recur: <const A extends string = never, U extends Record<string, FnDef> = {}>(name: string, def: RecurDef<A, U>, lemmas?: Record<string, (x: Term, fx: Term, env: RecurEnv<A, U>) => Cond>, between?: Record<string, (x: Term, fx: Term, y: Term, fy: Term, env: RecurEnv<A, U>) => Cond>) => FnDef & {
369
+ verdict: Verdict;
370
+ };
250
371
  type Kinds = Record<string, Kind<unknown>>;
251
372
  type ValuesOf<K extends Kinds> = {
252
373
  [n in keyof K]: K[n] extends Kind<infer V> ? V : never;
253
374
  };
254
375
  type Obj = object;
255
- type Writes<V> = V extends Decimal ? DecimalLike | string : V extends Int ? IntLike : V extends Cond ? Cond | boolean : V extends Choice<infer K> ? Choice<K> | K : V extends Maybe<infer W> ? Maybe<W> | Writes<W> | null : V extends Str ? Str | string : TermLike;
376
+ type Writes<V> = V extends Decimal ? DecimalLike | string : V extends Int ? IntLike : V extends Cond ? Cond | boolean : V extends Choice<infer K> ? Choice<K> | K : V extends Maybe<infer W> ? Maybe<W> | Writes<W> | null : V extends Str ? Str | string : V extends Variant<infer C, infer T> ? Variant<C, T> | VariantWrite<C, T> : V extends Term ? TermLike : V extends Record<string, unknown> ? {
377
+ [k in keyof V]: Writes<V[k]>;
378
+ } : TermLike;
256
379
  type Update<S extends Obj, M extends Obj> = Partial<{
257
380
  [k in keyof S]: Writes<S[k]>;
258
381
  } & {
259
- [k in keyof M]: MapWrite;
382
+ [k in keyof M]: M[k] extends CellsRef<unknown> | ItemsRef<unknown> ? PartsWrite : MapWrite;
260
383
  }>;
261
384
  type Spec = {
262
385
  name: string;
@@ -265,6 +388,8 @@ type Spec = {
265
388
  state: [string, number | ((p: never) => TermLike)][];
266
389
  maps: [string, number][];
267
390
  lists?: string[];
391
+ cells?: [string, Kind<unknown>][];
392
+ items?: [string, Kind<unknown>][];
268
393
  kinded?: [string, Kind<unknown>][];
269
394
  carried?: string[];
270
395
  fns?: [string, FnDef][];
@@ -293,8 +418,8 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
293
418
  state<T extends Record<string, number | ((p: P) => TermLike) | Init<unknown>>>(init: T): Rule<P, I, S & {
294
419
  [k in keyof T]: T[k] extends Init<infer V> ? V : Term;
295
420
  }, M, F, R, Su, Q>;
296
- maps<T extends Record<string, number>>(init: T): Rule<P, I, S, M & {
297
- [k in keyof T]: MapRef;
421
+ maps<T extends Record<string, number | Init<unknown>>>(init: T): Rule<P, I, S, M & {
422
+ [k in keyof T]: T[k] extends Init<infer V> ? CellsRef<V> : MapRef;
298
423
  }, F, R, Su, Q>;
299
424
  ints<T extends Record<string, number | ((p: P) => IntLike)>>(init: T): Rule<P, I, S & {
300
425
  [k in keyof T]: Int;
@@ -307,6 +432,11 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
307
432
  }, M & {
308
433
  [k in N]: ListRef;
309
434
  }, F, R, Su, Q>;
435
+ items<T extends Record<string, Kind<unknown>>>(kinds: T): Rule<P, I, S & {
436
+ [k in keyof T & string as `${k}_len`]: Term;
437
+ }, M & {
438
+ [k in keyof T]: ItemsRef<T[k] extends Kind<infer V> ? V : never>;
439
+ }, F, R, Su, Q>;
310
440
  fns<T extends Record<string, FnDef>>(defs: T): Rule<P & {
311
441
  [k in keyof T]: (...args: TermLike[]) => Term;
312
442
  }, I, S, M, F, R, Su, Q>;
@@ -332,6 +462,7 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
332
462
  prune(order: keyof R & string, after: (p: P) => TermLike): Rule<P, I, S, M, F, R, Su, Q>;
333
463
  law(name: string, f: (env: P & S & M & Su) => Cond | Forall): Law;
334
464
  private lawOf;
465
+ kindLaws(): Law[];
335
466
  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;
336
467
  stepsLaw(name: string, k: number, then: (first: P & I & S & M & F & Su & Q, last: S & M & Su, requests: (I & Q)[]) => Cond | Forall, when?: (before: P & I & S & M & F & Su & Q, j: number) => Cond | undefined): StepLaw;
337
468
  leadsTo(name: string, spec: {
@@ -348,6 +479,7 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
348
479
  private kindAssumptions;
349
480
  private afterEnv;
350
481
  private env;
482
+ private composites;
351
483
  private names;
352
484
  private whatIs;
353
485
  private distinct;
@@ -363,7 +495,7 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
363
495
  };
364
496
  private verdict;
365
497
  private crossStep;
366
- compare<St extends Record<string, number>>(reference: {
498
+ compare<St extends Record<string, Raw>>(reference: {
367
499
  init: (params: {
368
500
  [k in keyof P]: Raw;
369
501
  }) => St;
@@ -425,13 +557,28 @@ export type Decision = {
425
557
  then: C;
426
558
  goal: Goal;
427
559
  };
560
+ type ResultOf<V> = 0 extends 1 & V ? Term : [V] extends [Cond] ? Cond : [V] extends [Decimal] ? Decimal : [V] extends [Int] ? Int : [V] extends [Choice<infer K>] ? Choice<K> : [V] extends [Str] ? Str : [V] extends [Variant<infer C, infer T>] ? Variant<C, T> : [V] extends [Maybe<infer W>] ? Maybe<ResultOf<W>> : [V] extends [Term | number] ? Term : [V] extends [Record<string, unknown>] ? {
561
+ [f in keyof V]: ResultOf<V[f]>;
562
+ } : Term;
428
563
  type Result<T> = {
429
- [k in keyof T]: T[k] extends Cond ? Cond : T[k] extends Decimal ? Decimal : T[k] extends Int ? Int : T[k] extends Choice<infer K> ? Choice<K> : T[k] extends Str ? Str : Term;
564
+ [k in keyof T]: ResultOf<T[k]>;
430
565
  };
566
+ type ValueOf<V> = 0 extends 1 & V ? number : [V] extends [Cond] ? boolean : [V] extends [Choice<infer K>] ? K : [V] extends [Str] ? string : [V] extends [Variant<infer C, infer T>] ? CaseRaw<C, T> : [V] extends [Maybe<infer W>] ? ValueOf<W> | null : [V] extends [Term | number | Int | Decimal] ? number : [V] extends [Record<string, unknown>] ? {
567
+ [f in keyof V]: ValueOf<V[f]>;
568
+ } : number;
569
+ type CaseRaw<C extends Cases, T extends string = 'tag'> = {
570
+ [k in keyof C]: {
571
+ [t in T]: k;
572
+ } & {
573
+ [f in keyof C[k]]: C[k][f] extends Kind<infer V> ? ValueOf<V> : never;
574
+ };
575
+ }[keyof C];
431
576
  type Value<T> = {
432
- [k in keyof T]: T[k] extends Cond ? boolean : T[k] extends Choice<infer K> ? K : T[k] extends Str ? string : number;
577
+ [k in keyof T]: ValueOf<T[k]>;
578
+ };
579
+ type Field = Term | Cond | Int | Decimal | Choice<string> | Str | Variant<any, any> | Maybe<any> | {
580
+ [f: string]: Field | number;
433
581
  };
434
- type Field = Term | Cond | Int | Decimal | Choice<string> | Str;
435
582
  export declare class Fun<P extends Obj = {}, I extends Obj = {}, O extends Obj = {}> {
436
583
  private readonly r;
437
584
  private readonly out?;
@@ -524,6 +671,10 @@ export type Verdict = {
524
671
  why?: string;
525
672
  trees?: Record<string, Tree>;
526
673
  };
674
+ export declare class Unproven extends Error {
675
+ readonly verdict: Verdict;
676
+ constructor(what: string, verdict: Verdict);
677
+ }
527
678
  export declare function proveRule(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj>, laws: Law[]): Verdict;
528
679
  export declare function proveSteps(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj>, laws: Law[], stepLaws: StepLaw[]): Verdict;
529
680
  export type { StepLaw };