@cavelang/solver 0.35.0 → 0.36.1

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.
Files changed (119) hide show
  1. package/README.md +2155 -3
  2. package/dist/src/adapter.d.ts +6 -0
  3. package/dist/src/adapter.d.ts.map +1 -1
  4. package/dist/src/adapter.js +3 -0
  5. package/dist/src/adapter.js.map +1 -1
  6. package/dist/src/balanced-reduce.d.ts +3 -0
  7. package/dist/src/balanced-reduce.d.ts.map +1 -0
  8. package/dist/src/balanced-reduce.js +10 -0
  9. package/dist/src/balanced-reduce.js.map +1 -0
  10. package/dist/src/canonical-owned.d.ts +5 -0
  11. package/dist/src/canonical-owned.d.ts.map +1 -0
  12. package/dist/src/canonical-owned.js +206 -0
  13. package/dist/src/canonical-owned.js.map +1 -0
  14. package/dist/src/canonical.d.ts +3 -2
  15. package/dist/src/canonical.d.ts.map +1 -1
  16. package/dist/src/canonical.js +12 -85
  17. package/dist/src/canonical.js.map +1 -1
  18. package/dist/src/capability.d.ts.map +1 -1
  19. package/dist/src/capability.js +43 -33
  20. package/dist/src/capability.js.map +1 -1
  21. package/dist/src/clone.d.ts +3 -0
  22. package/dist/src/clone.d.ts.map +1 -0
  23. package/dist/src/clone.js +54 -0
  24. package/dist/src/clone.js.map +1 -0
  25. package/dist/src/constant-sign.d.ts +5 -0
  26. package/dist/src/constant-sign.d.ts.map +1 -0
  27. package/dist/src/constant-sign.js +54 -0
  28. package/dist/src/constant-sign.js.map +1 -0
  29. package/dist/src/context.d.ts +6 -0
  30. package/dist/src/context.d.ts.map +1 -0
  31. package/dist/src/context.js +151 -0
  32. package/dist/src/context.js.map +1 -0
  33. package/dist/src/diagnostic-text.d.ts +3 -0
  34. package/dist/src/diagnostic-text.d.ts.map +1 -0
  35. package/dist/src/diagnostic-text.js +5 -0
  36. package/dist/src/diagnostic-text.js.map +1 -0
  37. package/dist/src/evaluate-expression.d.ts +4 -0
  38. package/dist/src/evaluate-expression.d.ts.map +1 -0
  39. package/dist/src/evaluate-expression.js +24 -0
  40. package/dist/src/evaluate-expression.js.map +1 -0
  41. package/dist/src/exact.d.ts +2 -0
  42. package/dist/src/exact.d.ts.map +1 -1
  43. package/dist/src/exact.js +125 -28
  44. package/dist/src/exact.js.map +1 -1
  45. package/dist/src/explain.d.ts +4 -0
  46. package/dist/src/explain.d.ts.map +1 -1
  47. package/dist/src/explain.js +330 -91
  48. package/dist/src/explain.js.map +1 -1
  49. package/dist/src/explanation-budget.d.ts +14 -0
  50. package/dist/src/explanation-budget.d.ts.map +1 -0
  51. package/dist/src/explanation-budget.js +46 -0
  52. package/dist/src/explanation-budget.js.map +1 -0
  53. package/dist/src/expression-key.d.ts +8 -0
  54. package/dist/src/expression-key.d.ts.map +1 -0
  55. package/dist/src/expression-key.js +54 -0
  56. package/dist/src/expression-key.js.map +1 -0
  57. package/dist/src/fraction-product.d.ts +3 -0
  58. package/dist/src/fraction-product.d.ts.map +1 -0
  59. package/dist/src/fraction-product.js +13 -0
  60. package/dist/src/fraction-product.js.map +1 -0
  61. package/dist/src/fraction-sum.d.ts +3 -0
  62. package/dist/src/fraction-sum.d.ts.map +1 -0
  63. package/dist/src/fraction-sum.js +23 -0
  64. package/dist/src/fraction-sum.js.map +1 -0
  65. package/dist/src/integer-gcd.d.ts +3 -0
  66. package/dist/src/integer-gcd.d.ts.map +1 -0
  67. package/dist/src/integer-gcd.js +50 -0
  68. package/dist/src/integer-gcd.js.map +1 -0
  69. package/dist/src/linear.d.ts +2 -1
  70. package/dist/src/linear.d.ts.map +1 -1
  71. package/dist/src/linear.js +85 -17
  72. package/dist/src/linear.js.map +1 -1
  73. package/dist/src/numeric-size.d.ts +2 -0
  74. package/dist/src/numeric-size.d.ts.map +1 -0
  75. package/dist/src/numeric-size.js +33 -0
  76. package/dist/src/numeric-size.js.map +1 -0
  77. package/dist/src/options.d.ts +4 -0
  78. package/dist/src/options.d.ts.map +1 -0
  79. package/dist/src/options.js +18 -0
  80. package/dist/src/options.js.map +1 -0
  81. package/dist/src/result.d.ts +4 -0
  82. package/dist/src/result.d.ts.map +1 -0
  83. package/dist/src/result.js +29 -0
  84. package/dist/src/result.js.map +1 -0
  85. package/dist/src/solve.d.ts.map +1 -1
  86. package/dist/src/solve.js +34 -3
  87. package/dist/src/solve.js.map +1 -1
  88. package/dist/src/validate.d.ts +5 -0
  89. package/dist/src/validate.d.ts.map +1 -1
  90. package/dist/src/validate.js +323 -65
  91. package/dist/src/validate.js.map +1 -1
  92. package/dist/src/workflow.d.ts.map +1 -1
  93. package/dist/src/workflow.js +98 -26
  94. package/dist/src/workflow.js.map +1 -1
  95. package/package.json +2 -2
  96. package/src/adapter.ts +9 -0
  97. package/src/balanced-reduce.ts +9 -0
  98. package/src/canonical-owned.ts +186 -0
  99. package/src/canonical.ts +14 -90
  100. package/src/capability.ts +41 -30
  101. package/src/clone.ts +47 -0
  102. package/src/constant-sign.ts +59 -0
  103. package/src/context.ts +114 -0
  104. package/src/diagnostic-text.ts +4 -0
  105. package/src/evaluate-expression.ts +21 -0
  106. package/src/exact.ts +122 -27
  107. package/src/explain.ts +342 -94
  108. package/src/explanation-budget.ts +47 -0
  109. package/src/expression-key.ts +45 -0
  110. package/src/fraction-product.ts +10 -0
  111. package/src/fraction-sum.ts +20 -0
  112. package/src/integer-gcd.ts +47 -0
  113. package/src/linear.ts +82 -16
  114. package/src/numeric-size.ts +26 -0
  115. package/src/options.ts +18 -0
  116. package/src/result.ts +28 -0
  117. package/src/solve.ts +34 -3
  118. package/src/validate.ts +312 -66
  119. package/src/workflow.ts +92 -26
@@ -1 +1 @@
1
- {"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../../src/explain.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../../src/explain.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,30 @@
1
- import * as Canonical from "./canonical.js";
1
+ import { ExplanationBudget } from "./explanation-budget.js";
2
+ import { expressionKey } from "./expression-key.js";
3
+ import { constantSign } from "./constant-sign.js";
4
+ import { captureResult } from "./result.js";
5
+ import { balancedReduce } from "./balanced-reduce.js";
6
+ import { sum } from "./fraction-sum.js";
7
+ import { product } from "./fraction-product.js";
8
+ import { gcd } from "./integer-gcd.js";
9
+ import { clone } from "./clone.js";
10
+ import { stringifyInput, validateContext } from "./context.js";
11
+ import { mergeLimits } from "./validate.js";
12
+ import { digestOwned } from "./canonical-owned.js";
2
13
  import * as Exact from "./exact.js";
3
14
  export const schema = 'cave.solver/explanation@1';
4
15
  const compareText = (left, right) => left < right ? -1 : left > right ? 1 : 0;
5
16
  const normalize = (numerator, denominator) => {
6
17
  if (denominator === 0n)
7
18
  throw new TypeError('cannot explain division by zero');
8
- const exact = Exact.rational({ numerator: String(numerator), denominator: String(denominator) });
9
- return { kind: 'number', numerator: BigInt(exact.numerator), denominator: BigInt(exact.denominator) };
19
+ if (denominator < 0n) {
20
+ numerator = -numerator;
21
+ denominator = -denominator;
22
+ }
23
+ const divisor = gcd(numerator, denominator);
24
+ return { kind: 'number', numerator: numerator / divisor, denominator: denominator / divisor };
10
25
  };
11
- const number = (value) => {
26
+ const number = (value, budget) => {
27
+ budget.input(value);
12
28
  const exact = Exact.rational(value);
13
29
  return { kind: 'number', numerator: BigInt(exact.numerator), denominator: BigInt(exact.denominator) };
14
30
  };
@@ -22,15 +38,59 @@ const bool = (value) => {
22
38
  throw new TypeError(`expected a Boolean explanation value, received ${value.kind}`);
23
39
  return value.value;
24
40
  };
25
- const add = (left, right) => normalize(left.numerator * right.denominator + right.numerator * left.denominator, left.denominator * right.denominator);
26
- const multiply = (left, right) => normalize(left.numerator * right.numerator, left.denominator * right.denominator);
27
- const compare = (left, right) => {
28
- const difference = left.numerator * right.denominator - right.numerator * left.denominator;
29
- return difference < 0n ? -1 : difference > 0n ? 1 : 0;
41
+ const add = (left, right, budget) => {
42
+ if (left.numerator === 0n)
43
+ return right;
44
+ if (right.numerator === 0n)
45
+ return left;
46
+ if (left.denominator === right.denominator) {
47
+ budget.check(budget.sum(left.numerator, right.numerator), budget.bits(left.denominator));
48
+ }
49
+ else {
50
+ budget.check(Math.max(budget.product(left.numerator, right.denominator), budget.product(right.numerator, left.denominator)) + 1, budget.product(left.denominator, right.denominator));
51
+ }
52
+ // Adding an integer preserves gcd(numerator, denominator) = 1.
53
+ if (left.denominator === 1n)
54
+ return { kind: 'number',
55
+ numerator: left.numerator * right.denominator + right.numerator, denominator: right.denominator };
56
+ if (right.denominator === 1n)
57
+ return { kind: 'number',
58
+ numerator: left.numerator + right.numerator * left.denominator, denominator: left.denominator };
59
+ return normalize(...sum(left.numerator, left.denominator, right.numerator, right.denominator));
60
+ };
61
+ const multiply = (left, right, budget) => {
62
+ budget.check(budget.product(left.numerator, right.numerator), budget.product(left.denominator, right.denominator));
63
+ // Evaluation values are reduced with positive denominators. Squaring preserves
64
+ // coprimality; otherwise cross-cancellation leaves a reduced product directly.
65
+ const [numerator, denominator] = left.numerator === right.numerator && left.denominator === right.denominator
66
+ ? [left.numerator * right.numerator, left.denominator * right.denominator]
67
+ : product(left.numerator, left.denominator, right.numerator, right.denominator);
68
+ return { kind: 'number', numerator, denominator };
69
+ };
70
+ const negate = (value) => ({ kind: 'number', numerator: -value.numerator, denominator: value.denominator });
71
+ const compare = (left, right, budget) => {
72
+ // Evaluation values are normalized, so both denominators are positive.
73
+ if (left.denominator === right.denominator || left.numerator === 0n || right.numerator === 0n ||
74
+ (left.numerator < 0n) !== (right.numerator < 0n)) {
75
+ return left.numerator < right.numerator ? -1 : left.numerator > right.numerator ? 1 : 0;
76
+ }
77
+ if (left.numerator === right.numerator) {
78
+ return (left.denominator < right.denominator) === (left.numerator > 0n) ? 1 : -1;
79
+ }
80
+ if ((left.numerator < right.numerator) ===
81
+ (left.numerator > 0n ? left.denominator > right.denominator : left.denominator < right.denominator)) {
82
+ return left.numerator < right.numerator ? -1 : 1;
83
+ }
84
+ budget.check(budget.product(left.numerator, right.denominator), budget.product(right.numerator, left.denominator));
85
+ const a = left.numerator * right.denominator;
86
+ const b = right.numerator * left.denominator;
87
+ return a < b ? -1 : a > b ? 1 : 0;
30
88
  };
31
89
  const equal = (left, right) => {
32
- if (left.kind === 'number' && right.kind === 'number')
33
- return compare(left, right) === 0;
90
+ if (left.kind === 'number' && right.kind === 'number') {
91
+ // Every evaluated number is reduced with a positive denominator.
92
+ return left.numerator === right.numerator && left.denominator === right.denominator;
93
+ }
34
94
  if (left.kind !== right.kind)
35
95
  return false;
36
96
  if (left.kind === 'bool' && right.kind === 'bool')
@@ -39,57 +99,163 @@ const equal = (left, right) => {
39
99
  return left.domain === right.domain && left.value === right.value;
40
100
  return false;
41
101
  };
42
- const assigned = (value) => {
102
+ const assigned = (value, budget) => {
43
103
  switch (value.sort) {
44
- case 'bool': return { kind: 'bool', value: value.value };
45
- case 'int': return number({ numerator: value.value, denominator: '1' });
46
- case 'real': return number({ numerator: value.numerator, denominator: value.denominator });
47
- case 'enum': return { kind: 'enum', domain: value.domain, value: value.value };
104
+ case 'bool':
105
+ if (typeof value.value !== 'boolean')
106
+ throw new TypeError('boolean assignment value must be a boolean');
107
+ return { kind: 'bool', value: value.value };
108
+ case 'int': return number({ numerator: value.value, denominator: '1' }, budget);
109
+ case 'real': return number({ numerator: value.numerator, denominator: value.denominator }, budget);
110
+ case 'enum':
111
+ if (typeof value.domain !== 'string' || typeof value.value !== 'string')
112
+ throw new TypeError('enum assignment domain and value must be strings');
113
+ return { kind: 'enum', domain: value.domain, value: value.value };
48
114
  }
49
115
  };
50
- const evaluate = (expression, assignment) => {
51
- const run = (value) => evaluate(value, assignment);
116
+ /** A captured assignment has one validation result for the whole report. */
117
+ const evaluateAssignment = (id, context) => {
118
+ const cached = context.assigned.get(id);
119
+ if (cached !== undefined) {
120
+ if ('error' in cached)
121
+ throw cached.error;
122
+ return cached.value;
123
+ }
124
+ try {
125
+ const value = context.assignment[id];
126
+ if (value === undefined)
127
+ throw new TypeError(`assignment omits ${JSON.stringify(id)}`);
128
+ const evaluated = assigned(value, context.budget);
129
+ const variable = context.variables.get(id);
130
+ const kind = variable.sort === 'int' || variable.sort === 'real' ? 'number' : variable.sort;
131
+ if (evaluated.kind !== kind)
132
+ throw new TypeError('assignment kind must match the declared variable');
133
+ if (variable.sort === 'int' && evaluated.kind === 'number' && evaluated.denominator !== 1n) {
134
+ throw new TypeError('integer variable assignment must be integral');
135
+ }
136
+ if (evaluated.kind === 'number' && (variable.sort === 'int' || variable.sort === 'real')) {
137
+ const min = variable.min === undefined ? undefined : variable.sort === 'int'
138
+ ? number({ numerator: variable.min, denominator: '1' }, context.budget) : number(variable.min, context.budget);
139
+ const max = variable.max === undefined ? undefined : variable.sort === 'int'
140
+ ? number({ numerator: variable.max, denominator: '1' }, context.budget) : number(variable.max, context.budget);
141
+ if ((min !== undefined && compare(evaluated, min, context.budget) < 0) || (max !== undefined && compare(evaluated, max, context.budget) > 0)) {
142
+ throw new TypeError('numeric assignment must lie within the declared bounds');
143
+ }
144
+ }
145
+ if (variable.sort === 'enum' && (evaluated.kind !== 'enum' || evaluated.domain !== variable.domain || !context.enumDomains.get(variable.domain).has(evaluated.value))) {
146
+ throw new TypeError('enum assignment must belong to the declared domain');
147
+ }
148
+ context.assigned.set(id, { value: evaluated });
149
+ return evaluated;
150
+ }
151
+ catch (error) {
152
+ context.assigned.set(id, { error });
153
+ throw error;
154
+ }
155
+ };
156
+ function* evaluateNode(expression, context) {
157
+ const signOf = (value) => constantSign(value, context.signs, literal => {
158
+ if (literal.kind !== 'literal' || (literal.sort !== 'int' && literal.sort !== 'real'))
159
+ throw new TypeError('expected a numeric literal');
160
+ const input = literal.sort === 'int' ? { numerator: literal.value, denominator: '1' } : literal.value;
161
+ context.budget.input(input);
162
+ return Exact.rational(input);
163
+ });
164
+ if (expression.kind === 'eq' || expression.kind === 'neq' || expression.kind === 'lt' ||
165
+ expression.kind === 'lte' || expression.kind === 'gt' || expression.kind === 'gte') {
166
+ const zero = (value) => value.kind === 'literal' &&
167
+ (value.sort === 'int' || value.sort === 'real') && signOf(value) === 0;
168
+ // A proof reads only captured constants. Unknown signs (including undefined
169
+ // arithmetic or any variable) retain ordinary assignment/error evaluation.
170
+ const sign = zero(expression.right) ? signOf(expression.left)
171
+ : zero(expression.left) ? signOf(expression.right) : undefined;
172
+ if (sign !== undefined) {
173
+ const order = zero(expression.right) ? sign : -sign;
174
+ return { kind: 'bool', value: expression.kind === 'eq' ? order === 0
175
+ : expression.kind === 'neq' ? order !== 0 : expression.kind === 'lt' ? order < 0
176
+ : expression.kind === 'lte' ? order <= 0 : expression.kind === 'gt' ? order > 0 : order >= 0 };
177
+ }
178
+ }
52
179
  switch (expression.kind) {
53
180
  case 'literal':
54
181
  switch (expression.sort) {
55
182
  case 'bool': return { kind: 'bool', value: expression.value };
56
- case 'int': return number({ numerator: String(Exact.integer(expression.value)), denominator: '1' });
57
- case 'real': return number(expression.value);
183
+ case 'int': return number({ numerator: expression.value, denominator: '1' }, context.budget);
184
+ case 'real': return number(expression.value, context.budget);
58
185
  case 'enum': return { kind: 'enum', domain: expression.domain, value: expression.value };
59
186
  }
60
- case 'variable': {
61
- const value = assignment[expression.id];
62
- if (value === undefined)
63
- throw new TypeError(`assignment omits ${JSON.stringify(expression.id)}`);
64
- return assigned(value);
187
+ case 'variable': return evaluateAssignment(expression.id, context);
188
+ case 'not': return { kind: 'bool', value: !bool((yield expression.value)) };
189
+ case 'and':
190
+ for (const value of expression.operands)
191
+ if (!bool(yield value))
192
+ return { kind: 'bool', value: false };
193
+ return { kind: 'bool', value: true };
194
+ case 'or':
195
+ for (const value of expression.operands)
196
+ if (bool(yield value))
197
+ return { kind: 'bool', value: true };
198
+ return { kind: 'bool', value: false };
199
+ case 'implies': return { kind: 'bool', value: !bool((yield expression.left)) || bool((yield expression.right)) };
200
+ case 'eq': return { kind: 'bool', value: equal((yield expression.left), (yield expression.right)) };
201
+ case 'neq': return { kind: 'bool', value: !equal((yield expression.left), (yield expression.right)) };
202
+ case 'lt': return { kind: 'bool', value: compare(numeric((yield expression.left)), numeric((yield expression.right)), context.budget) < 0 };
203
+ case 'lte': return { kind: 'bool', value: compare(numeric((yield expression.left)), numeric((yield expression.right)), context.budget) <= 0 };
204
+ case 'gt': return { kind: 'bool', value: compare(numeric((yield expression.left)), numeric((yield expression.right)), context.budget) > 0 };
205
+ case 'gte': return { kind: 'bool', value: compare(numeric((yield expression.left)), numeric((yield expression.right)), context.budget) >= 0 };
206
+ case 'add':
207
+ case 'multiply': {
208
+ const values = [];
209
+ for (const value of expression.operands)
210
+ values.push(yield value);
211
+ const numbers = values.map(numeric);
212
+ // Evaluate and type-check every operand before applying the zero identity:
213
+ // zero must not hide undefined arithmetic in another operand.
214
+ if (expression.kind === 'multiply' && numbers.some(value => value.numerator === 0n)) {
215
+ return normalize(0n, 1n);
216
+ }
217
+ return balancedReduce(numbers, (left, right) => expression.kind === 'add'
218
+ ? add(left, right, context.budget) : multiply(left, right, context.budget));
65
219
  }
66
- case 'not': return { kind: 'bool', value: !bool(run(expression.value)) };
67
- case 'and': return { kind: 'bool', value: expression.operands.every(value => bool(run(value))) };
68
- case 'or': return { kind: 'bool', value: expression.operands.some(value => bool(run(value))) };
69
- case 'implies': return { kind: 'bool', value: !bool(run(expression.left)) || bool(run(expression.right)) };
70
- case 'eq': return { kind: 'bool', value: equal(run(expression.left), run(expression.right)) };
71
- case 'neq': return { kind: 'bool', value: !equal(run(expression.left), run(expression.right)) };
72
- case 'lt': return { kind: 'bool', value: compare(numeric(run(expression.left)), numeric(run(expression.right))) < 0 };
73
- case 'lte': return { kind: 'bool', value: compare(numeric(run(expression.left)), numeric(run(expression.right))) <= 0 };
74
- case 'gt': return { kind: 'bool', value: compare(numeric(run(expression.left)), numeric(run(expression.right))) > 0 };
75
- case 'gte': return { kind: 'bool', value: compare(numeric(run(expression.left)), numeric(run(expression.right))) >= 0 };
76
- case 'add': return expression.operands.map(run).map(numeric).reduce(add);
77
- case 'multiply': return expression.operands.map(run).map(numeric).reduce(multiply);
78
220
  case 'subtract': {
79
- const left = numeric(run(expression.left));
80
- const right = numeric(run(expression.right));
81
- return add(left, normalize(-right.numerator, right.denominator));
221
+ const left = numeric((yield expression.left));
222
+ const right = numeric((yield expression.right));
223
+ return add(left, negate(right), context.budget);
82
224
  }
83
225
  case 'divide': {
84
- const left = numeric(run(expression.left));
85
- const right = numeric(run(expression.right));
86
- return normalize(left.numerator * right.denominator, left.denominator * right.numerator);
226
+ const left = numeric((yield expression.left));
227
+ const right = numeric((yield expression.right));
228
+ if (right.numerator === 0n)
229
+ throw new TypeError('cannot explain division by zero');
230
+ context.budget.check(context.budget.product(left.numerator, right.denominator), context.budget.product(left.denominator, right.numerator));
231
+ // Cross-cancellation preserves reduction; only denominator sign can change.
232
+ const [numerator, denominator] = product(left.numerator, left.denominator, right.denominator, right.numerator);
233
+ return denominator < 0n
234
+ ? { kind: 'number', numerator: -numerator, denominator: -denominator }
235
+ : { kind: 'number', numerator, denominator };
87
236
  }
88
237
  case 'negate': {
89
- const value = numeric(run(expression.value));
90
- return normalize(-value.numerator, value.denominator);
238
+ const value = numeric((yield expression.value));
239
+ return negate(value);
240
+ }
241
+ case 'if': return bool((yield expression.condition)) ? (yield expression.then) : (yield expression.else);
242
+ }
243
+ }
244
+ const evaluate = (expression, context) => {
245
+ const stack = [evaluateNode(expression, context)];
246
+ let step = stack[0].next();
247
+ while (true) {
248
+ if (step.done) {
249
+ stack.pop();
250
+ if (stack.length === 0)
251
+ return step.value;
252
+ step = stack[stack.length - 1].next(step.value);
253
+ }
254
+ else {
255
+ const child = evaluateNode(step.value, context);
256
+ stack.push(child);
257
+ step = child.next();
91
258
  }
92
- case 'if': return bool(run(expression.condition)) ? run(expression.then) : run(expression.else);
93
259
  }
94
260
  };
95
261
  const sorted = (values) => [...new Set(values ?? [])].sort();
@@ -100,26 +266,45 @@ const element = (value) => ({
100
266
  evidenceRowIds: sorted(value.evidenceRowIds),
101
267
  scenarioInputIds: sorted(value.scenarioInputIds)
102
268
  });
103
- const constraint = (value, assignment) => {
104
- let evaluation = 'indeterminate';
269
+ const evaluation = (expression, context) => {
270
+ const repeated = context.expressionResults.get(expression);
271
+ if (repeated !== undefined)
272
+ return repeated;
273
+ const key = expressionKey(expression, context.expressionStrings);
274
+ const cached = context.evaluations.get(key);
275
+ if (cached !== undefined) {
276
+ context.expressionResults.set(expression, cached);
277
+ return cached;
278
+ }
279
+ let result;
105
280
  try {
106
- evaluation = bool(evaluate(value.expression, assignment)) ? 'satisfied' : 'violated';
281
+ result = { value: bool(evaluate(expression, context)) };
282
+ }
283
+ catch (error) {
284
+ // Local arithmetic/assignment failure does not reclassify the backend result.
285
+ result = { evaluationReason: error instanceof Error ? error.message : 'constraint evaluation failed' };
107
286
  }
108
- catch { /* Keep the solver result usable when a backend uses totalized arithmetic. */ }
109
- return { ...element(value), evaluation };
287
+ context.evaluations.set(key, result);
288
+ context.expressionResults.set(expression, result);
289
+ return result;
290
+ };
291
+ const constraint = (value, context) => {
292
+ const result = evaluation(value.expression, context);
293
+ return {
294
+ ...element(value),
295
+ evaluation: result.value === undefined ? 'indeterminate' : result.value ? 'satisfied' : 'violated',
296
+ ...(result.evaluationReason === undefined ? {} : { evaluationReason: result.evaluationReason })
297
+ };
298
+ };
299
+ const softConstraint = (value, context) => {
300
+ const result = evaluation(value.expression, context);
301
+ return {
302
+ ...element(value),
303
+ evaluation: result.value === undefined ? 'indeterminate' : result.value ? 'accepted' : 'violated',
304
+ ...(result.evaluationReason === undefined ? {} : { evaluationReason: result.evaluationReason }),
305
+ weight: Exact.rational(value.weight)
306
+ };
110
307
  };
111
- const softConstraint = (value, assignment) => ({
112
- ...element(value),
113
- evaluation: (() => {
114
- try {
115
- return bool(evaluate(value.expression, assignment)) ? 'accepted' : 'violated';
116
- }
117
- catch {
118
- return 'indeterminate';
119
- }
120
- })(),
121
- weight: Exact.rational(value.weight)
122
- });
123
308
  const assignments = (model, assignment) => {
124
309
  const variables = new Map(model.variables.map(variable => [variable.id, variable]));
125
310
  return Object.entries(assignment)
@@ -129,11 +314,19 @@ const assignments = (model, assignment) => {
129
314
  return { ...element(variable ?? { id }), declared: variable !== undefined, value };
130
315
  });
131
316
  };
132
- const feasible = (model, assignment) => ({
133
- assignments: assignments(model, assignment),
134
- hardConstraints: model.constraints.map(value => constraint(value, assignment)),
135
- softConstraints: (model.softConstraints ?? []).map(value => softConstraint(value, assignment))
136
- });
317
+ const feasible = (model, assignment, limits) => {
318
+ // The report owns a captured assignment; normalized values live only for this report.
319
+ const context = {
320
+ assignment, budget: new ExplanationBudget(limits.maxExplanationBits, limits.maxExplanationWork), assigned: new Map(), signs: new WeakMap(), expressionResults: new WeakMap(), evaluations: new Map(), expressionStrings: new Map(),
321
+ variables: new Map(model.variables.map(variable => [variable.id, variable])),
322
+ enumDomains: new Map((model.enums ?? []).map(domain => [domain.id, new Set(domain.values)]))
323
+ };
324
+ return {
325
+ assignments: assignments(model, assignment),
326
+ hardConstraints: model.constraints.map(value => constraint(value, context)),
327
+ softConstraints: (model.softConstraints ?? []).map(value => softConstraint(value, context))
328
+ };
329
+ };
137
330
  const objective = (declaration, result) => ({
138
331
  ...element(declaration ?? { id: result.objectiveId }),
139
332
  declared: declaration !== undefined,
@@ -141,14 +334,14 @@ const objective = (declaration, result) => ({
141
334
  value: result.value,
142
335
  ...(result.bound === undefined ? {} : { bound: result.bound })
143
336
  });
144
- const outcome = (model, result) => {
337
+ const outcome = (model, result, limits) => {
145
338
  switch (result.status) {
146
- case 'satisfied': return { status: result.status, ...feasible(model, result.assignment) };
339
+ case 'satisfied': return { status: result.status, ...feasible(model, result.assignment, limits) };
147
340
  case 'optimal': {
148
341
  const declarations = new Map((model.objectives ?? []).map(value => [value.id, value]));
149
342
  return {
150
343
  status: result.status,
151
- ...feasible(model, result.assignment),
344
+ ...feasible(model, result.assignment, limits),
152
345
  objectives: result.objectives.map(value => objective(declarations.get(value.objectiveId), value)),
153
346
  optimalityProved: result.optimalityProved
154
347
  };
@@ -172,10 +365,22 @@ const outcome = (model, result) => {
172
365
  };
173
366
  /** Build stable JSON data without mutating the model, scenario, or CAVE store. */
174
367
  export const report = (model, result, limits, context = {}) => {
175
- const modelDigest = Canonical.digest(model);
368
+ limits = mergeLimits(limits);
369
+ model = clone(model);
370
+ result = captureResult(result);
371
+ validateContext(context);
372
+ context = clone({
373
+ modelDigest: context.modelDigest,
374
+ scenario: context.scenario,
375
+ snapshot: context.snapshot,
376
+ inputs: context.inputs,
377
+ });
378
+ validateContext(context);
379
+ const modelDigest = digestOwned(model, limits);
176
380
  if (context.modelDigest !== undefined && context.modelDigest !== modelDigest) {
177
381
  throw new TypeError(`scenario model digest ${context.modelDigest} does not match ${modelDigest}`);
178
382
  }
383
+ // All referenced data is owned by the captures above or constructed in this call.
179
384
  return {
180
385
  schema,
181
386
  run: {
@@ -188,67 +393,101 @@ export const report = (model, result, limits, context = {}) => {
188
393
  ...(context.snapshot === undefined ? {} : { snapshot: context.snapshot }),
189
394
  inputs: [...(context.inputs ?? [])].sort((left, right) => compareText(left.id, right.id))
190
395
  },
191
- outcome: outcome(model, result)
396
+ outcome: outcome(model, result, limits)
192
397
  };
193
398
  };
399
+ const sourceLocation = (declaration) => {
400
+ if (declaration.line !== undefined) {
401
+ return `${inlineText(declaration.uri)}:${declaration.line}${declaration.column === undefined ? '' : `:${declaration.column}`}`;
402
+ }
403
+ return `${inlineText(declaration.uri)}${declaration.column === undefined ? '' : ` (column ${declaration.column})`}`;
404
+ };
194
405
  const evidence = (value) => {
195
406
  const parts = [
196
- value.declaration === undefined ? undefined : `${value.declaration.uri}${value.declaration.line === undefined ? '' : `:${value.declaration.line}${value.declaration.column === undefined ? '' : `:${value.declaration.column}`}`}`,
197
- value.evidenceRowIds.length === 0 ? undefined : `rows ${value.evidenceRowIds.join(', ')}`,
198
- value.scenarioInputIds.length === 0 ? undefined : `inputs ${value.scenarioInputIds.join(', ')}`
407
+ value.declaration === undefined ? undefined : sourceLocation(value.declaration),
408
+ value.evidenceRowIds.length === 0 ? undefined : `rows ${value.evidenceRowIds.map(inlineText).join(', ')}`,
409
+ value.scenarioInputIds.length === 0 ? undefined : `inputs ${value.scenarioInputIds.map(inlineText).join(', ')}`
199
410
  ].filter((part) => part !== undefined);
200
411
  return parts.length === 0 ? '' : ` — ${parts.join('; ')}`;
201
412
  };
202
413
  const valueText = (value) => {
414
+ const invalid = '(invalid backend value)';
415
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
416
+ return invalid;
203
417
  switch (value.sort) {
204
- case 'bool': return String(value.value);
205
- case 'int': return value.value;
206
- case 'real': return `${value.numerator}/${value.denominator}`;
207
- case 'enum': return value.value;
418
+ case 'bool': return typeof value.value === 'boolean' ? String(value.value) : invalid;
419
+ case 'int': return typeof value.value === 'string' ? inlineText(value.value) : invalid;
420
+ case 'real': return typeof value.numerator === 'string' && typeof value.denominator === 'string'
421
+ ? inlineText(`${value.numerator}/${value.denominator}`) : invalid;
422
+ case 'enum': return typeof value.domain === 'string' && typeof value.value === 'string' ? inlineText(value.value) : invalid;
423
+ default: return invalid;
208
424
  }
209
425
  };
426
+ const jsonControlEscapes = new Map([...Array.from({ length: 33 }, (_, index) => index + 0x7f), 0x2028, 0x2029]
427
+ .map(code => [String.fromCharCode(code), `\\u${code.toString(16).padStart(4, '0')}`]));
428
+ const escapeJsonControls = (text) => text.replace(/[\u007f-\u009f\u2028\u2029]/g, character => jsonControlEscapes.get(character));
429
+ const quotedText = (message) => escapeJsonControls(JSON.stringify(message));
430
+ const inlineText = (message) => /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/.test(message) ? quotedText(message) : message;
210
431
  /** Render the same report as concise, deterministic plain text. */
211
432
  export const render = (value) => {
212
433
  const lines = [
213
434
  `Solver result: ${value.outcome.status}`,
214
- `Model: ${value.run.modelDigest}`,
215
- `Backend: ${value.run.backend.name} ${value.run.backend.version}`,
435
+ `Model: ${inlineText(value.run.modelDigest)}`,
436
+ `Backend: ${inlineText(value.run.backend.name)} ${inlineText(value.run.backend.version)}`,
216
437
  `Elapsed: ${value.run.elapsedMs} ms`
217
438
  ];
218
439
  if (value.run.snapshot !== undefined) {
219
- lines.push(`Snapshot: transaction ${value.run.snapshot.transactionTime ?? 'empty'}${value.run.snapshot.validTime === undefined ? '' : `, valid ${value.run.snapshot.validTime}`}`);
440
+ const snapshot = value.run.snapshot;
441
+ const policies = [];
442
+ if (snapshot.aliases !== undefined)
443
+ policies.push(`aliases ${snapshot.aliases}`);
444
+ if (snapshot.resolution !== undefined)
445
+ policies.push(`resolution ${snapshot.resolution}`);
446
+ if (snapshot.minimumConfidence !== undefined)
447
+ policies.push(`minimum confidence ${snapshot.minimumConfidence}`);
448
+ lines.push(`Snapshot: transaction ${inlineText(snapshot.transactionTime ?? 'empty')}${snapshot.validTime === undefined ? '' : `, valid ${inlineText(snapshot.validTime)}`}${policies.length === 0 ? '' : `; ${policies.join('; ')}`}`);
220
449
  }
221
450
  if (value.run.scenario !== undefined)
222
- lines.push(`Scenario: ${value.run.scenario.id} (${value.run.scenario.inputDigest})`);
451
+ lines.push(`Scenario: ${inlineText(value.run.scenario.id)} (input ${inlineText(value.run.scenario.inputDigest)}; overlay ${inlineText(value.run.scenario.overlayDigest)})`);
223
452
  for (const input of value.run.inputs) {
224
- lines.push(`Input ${input.id}: ${JSON.stringify(input.value)}${input.query === undefined ? '' : ` via ${input.query}`}`);
453
+ const values = [];
454
+ if (input.value !== undefined)
455
+ values.push(escapeJsonControls(stringifyInput(input.value)));
456
+ if (input.authoredValue !== undefined)
457
+ values.push(`authored ${escapeJsonControls(stringifyInput(input.authoredValue))}`);
458
+ const sources = [];
459
+ if (input.evidenceRowIds.length > 0)
460
+ sources.push(`rows ${sorted(input.evidenceRowIds).map(inlineText).join(', ')}`);
461
+ if (input.scenarioClaimIds.length > 0)
462
+ sources.push(`scenario claims ${sorted(input.scenarioClaimIds).map(inlineText).join(', ')}`);
463
+ lines.push(`Input ${inlineText(input.id)}: ${values.length === 0 ? '(no value)' : values.join('; ')}${input.query === undefined ? '' : ` via ${inlineText(input.query)}`}${sources.length === 0 ? '' : ` — ${sources.join('; ')}`}`);
225
464
  }
226
465
  switch (value.outcome.status) {
227
466
  case 'satisfied':
228
467
  case 'optimal':
229
468
  for (const assignment of value.outcome.assignments) {
230
- lines.push(`Assignment ${assignment.id} = ${valueText(assignment.value)}${assignment.declared ? '' : ' (undeclared backend ID)'}${evidence(assignment)}`);
469
+ lines.push(`Assignment ${inlineText(assignment.id)} = ${valueText(assignment.value)}${assignment.declared ? '' : ' (undeclared backend ID)'}${evidence(assignment)}`);
231
470
  }
232
471
  for (const constraint of value.outcome.hardConstraints) {
233
- lines.push(`Hard constraint ${constraint.id}: ${constraint.evaluation}${evidence(constraint)}`);
472
+ lines.push(`Hard constraint ${inlineText(constraint.id)}: ${constraint.evaluation}${constraint.evaluationReason === undefined ? '' : ` (${quotedText(constraint.evaluationReason)})`}${evidence(constraint)}`);
234
473
  }
235
474
  for (const constraint of value.outcome.softConstraints) {
236
- lines.push(`Soft constraint ${constraint.id}: ${constraint.evaluation}, weight ${constraint.weight.numerator}/${constraint.weight.denominator}${evidence(constraint)}`);
475
+ lines.push(`Soft constraint ${inlineText(constraint.id)}: ${constraint.evaluation}${constraint.evaluationReason === undefined ? '' : ` (${quotedText(constraint.evaluationReason)})`}, weight ${constraint.weight.numerator}/${constraint.weight.denominator}${evidence(constraint)}`);
237
476
  }
238
477
  if (value.outcome.status === 'optimal') {
239
478
  for (const objective of value.outcome.objectives) {
240
- lines.push(`Objective ${objective.id} (${objective.direction}) = ${valueText(objective.value)}${objective.declared ? '' : ' (undeclared backend ID)'}${evidence(objective)}`);
479
+ lines.push(`Objective ${inlineText(objective.id)} (${objective.direction}) = ${valueText(objective.value)}${objective.declared ? '' : ' (undeclared backend ID)'}${evidence(objective)}`);
241
480
  }
242
481
  }
243
482
  break;
244
483
  case 'unsatisfied':
245
484
  lines.push('Infeasibility proved. Unsatisfiable cores are not necessarily minimal.');
246
485
  for (const constraint of value.outcome.core ?? []) {
247
- lines.push(`Core constraint ${constraint.id}${constraint.declared ? '' : ' (undeclared backend ID)'}${evidence(constraint)}`);
486
+ lines.push(`Core constraint ${inlineText(constraint.id)}${constraint.declared ? '' : ' (undeclared backend ID)'}${evidence(constraint)}`);
248
487
  }
249
488
  break;
250
489
  case 'unknown':
251
- lines.push(`Unknown: ${value.outcome.reason.kind} — ${value.outcome.reason.message}`);
490
+ lines.push(`Unknown: ${value.outcome.reason.kind} — ${inlineText(value.outcome.reason.message)}`);
252
491
  break;
253
492
  }
254
493
  return `${lines.join('\n')}\n`;