Skip to content

Commit a2c8801

Browse files
committed
fixup! abstract helper iterations
1 parent 039744c commit a2c8801

File tree

3 files changed

+320
-281
lines changed

3 files changed

+320
-281
lines changed

package.json

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,40 @@
4242
"resolutions": {
4343
"**/protobufjs": "^7.2.6",
4444
"**/@types/estree": "^1.0.0",
45-
"@endo/eslint-plugin/typescript-eslint": "^8.26.0"
45+
"@endo/eslint-plugin/typescript-eslint": "^8.26.0",
46+
"**/@endo/base64": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/base64/endo-base64-v1.0.9-e26aa4bbfc1648962912233c4662b3d766048751.tgz",
47+
"**/@endo/bundle-source": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/bundle-source/endo-bundle-source-v3.5.1-8bf2e7563dc773515430234b23ee5abce2e34706.tgz",
48+
"**/@endo/captp": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/captp/endo-captp-v4.4.4-ac12a4160437053851cc5d4d971c264c7c882ff4.tgz",
49+
"**/@endo/check-bundle": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/check-bundle/endo-check-bundle-v1.0.13-2b7f8665e52f5be8f1218d69a61ee6c2a7a4a4e7.tgz",
50+
"**/@endo/cjs-module-analyzer": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/cjs-module-analyzer/endo-cjs-module-analyzer-v1.0.9-2de795e14e7aa39b54f00adf6f8e93d2d1196af3.tgz",
51+
"**/@endo/common": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/common/endo-common-v1.2.9-e72af313e0670174573e6a6ab7b9f1a5374dfbf1.tgz",
52+
"**/@endo/compartment-mapper": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/compartment-mapper/endo-compartment-mapper-v1.5.0-197fb32137aef5ab6a6f602eec0e3f49a3fca944.tgz",
53+
"**/@endo/env-options": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/env-options/endo-env-options-v1.1.8-e7695ab0eb7c84d86f908afacea2389ee6f3741d.tgz",
54+
"**/@endo/errors": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/errors/endo-errors-v1.2.9-5c06ab3cec1f018b829cb9fd7d62ea435c224057.tgz",
55+
"**/@endo/eslint-plugin": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/eslint-plugin/endo-eslint-plugin-v2.3.0-9fe4cae97ff010fe808ca3099839fa6bc1646160.tgz",
56+
"**/@endo/evasive-transform": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/evasive-transform/endo-evasive-transform-v1.3.4-774571d28864a9f73650991f3b3a2c35ff33621a.tgz",
57+
"**/@endo/eventual-send": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/eventual-send/endo-eventual-send-v1.3.0-abdc631c8655da23dfaa4dbfc26b3cb2d6c6a51c.tgz",
58+
"**/@endo/exo": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/exo/endo-exo-v1.5.8-a5c11be4edc89aa1ca73b73db4181c3a15c9df73.tgz",
59+
"**/@endo/far": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/far/endo-far-v1.1.10-948b31f5bba69fad597ca8cb1437b101b3213f9a.tgz",
60+
"**/@endo/import-bundle": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/import-bundle/endo-import-bundle-v1.3.3-e0d677e8a53dbb2a65ebcb28e3a8dd15c49ba828.tgz",
61+
"**/@endo/init": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/init/endo-init-v1.1.8-60ddfa368d5f93af4eabb8ec98de4c796c449969.tgz",
62+
"**/@endo/lockdown": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/lockdown/endo-lockdown-v1.0.14-0f1f749f97f404c5ca831ac946a91cfd7ce7b4d5.tgz",
63+
"**/@endo/lp32": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/lp32/endo-lp32-v1.1.9-6b0229f6650ec52b2ddd7e57d1547fa60234530f.tgz",
64+
"**/@endo/marshal": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/marshal/endo-marshal-v1.6.3-2f4193f3327868c7f9f6c381a4e7a6ac1650ff0a.tgz",
65+
"**/@endo/memoize": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/memoize/endo-memoize-v1.1.9-f2a9040164ef0f5619c1c1d126dfba330e347c76.tgz",
66+
"**/@endo/module-source": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/module-source/endo-module-source-v1.2.0-bda274bf3c7b53254fe8fb5eb37f258807e4d9c2.tgz",
67+
"**/@endo/nat": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/nat/endo-nat-v5.0.14-11087c79c7feb8bca47c4e503cf88f207e88f248.tgz",
68+
"**/@endo/netstring": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/netstring/endo-netstring-v1.0.14-825c7c74144cbbba2a860400fd76acf0f0cf87a4.tgz",
69+
"**/@endo/pass-style": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/pass-style/endo-pass-style-v1.4.8-e470dddbd3c6abf116063b5c3122f7313dbdef9a.tgz",
70+
"**/@endo/patterns": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/patterns/endo-patterns-v1.4.8-b4becca9b450c72930e4aae641b10b1b19b27a08.tgz",
71+
"**/@endo/promise-kit": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/promise-kit/endo-promise-kit-v1.1.9-fbc822b82dfe94b9ce1a8d11e2b124ddee718731.tgz",
72+
"**/@endo/ses-ava": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/ses-ava/endo-ses-ava-v1.2.9-26b941fc3cf03f00c8a09edd368e37ff8262c5ff.tgz",
73+
"**/@endo/stream": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/stream/endo-stream-v1.2.9-71bc177f61f891630e1f87202d70bf3aa553e736.tgz",
74+
"**/@endo/stream-node": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/stream-node/endo-stream-node-v1.1.9-f4493191fea41d01b6c142ad0081216ac372e816.tgz",
75+
"**/@endo/trampoline": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/trampoline/endo-trampoline-v1.0.3-5c71361c31df46075baec608a4bfd148fb4b456f.tgz",
76+
"**/@endo/where": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/where/endo-where-v1.0.9-ce045881698fb212c011438382d30996ca585bd8.tgz",
77+
"**/@endo/zip": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/zip/endo-zip-v1.0.9-302a93648d33179d9f93999878badb36db2ac763.tgz",
78+
"**/ses": "file:/Users/markmiller/src/ongithub/endojs/endo/packages/ses/ses-v1.11.0-fa558b6fb7d0a17b696deeb9b3a5ee70c4a0466c.tgz"
4679
},
4780
"engines": {
4881
"node": "^18.12 || ^20.9"
Lines changed: 100 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// @jessie-check
22

33
import { Fail, q } from '@endo/errors';
4-
import { passStyleOf } from '@endo/pass-style';
4+
import { passStyleOf, mapIterable } from '@endo/pass-style';
55
import { isKey, assertKey, mustMatch, matches } from '@endo/patterns';
66
import {
77
elementsIsSuperset,
@@ -12,6 +12,93 @@ import {
1212
} from '@agoric/store';
1313
import { AmountValueHasBoundShape } from '../typeGuards.js';
1414

15+
/**
16+
* @import {Passable} from '@endo/pass-style'
17+
* @import {AmountValueHasBound} from '../types.js'
18+
*/
19+
20+
/**
21+
* @param {Iterable<[Passable, bigint]>} leftEntries
22+
* @param {AmountValueHasBound} rightBound
23+
* @returns {boolean}
24+
*/
25+
export const entriesIsGTEHasBound = (leftEntries, rightBound) => {
26+
mustMatch(rightBound, AmountValueHasBoundShape, 'right value bound');
27+
const {
28+
payload: [elementPatt, bound],
29+
} = rightBound;
30+
if (bound === 0n) {
31+
return true;
32+
}
33+
let count = 0n;
34+
for (const [element, num] of leftEntries) {
35+
if (matches(element, elementPatt)) {
36+
count += num;
37+
}
38+
if (count >= bound) {
39+
return true;
40+
}
41+
}
42+
return false;
43+
};
44+
45+
/**
46+
* @param {Iterable<[Passable, bigint]>} leftEntries
47+
* @param {AmountValueHasBound} rightBound
48+
* @returns {Iterable<[Passable, bigint]>}
49+
*/
50+
export const entriesMinusHasBound = (leftEntries, rightBound) => {
51+
mustMatch(rightBound, AmountValueHasBoundShape, 'right value bound');
52+
const {
53+
payload: [elementPatt, bound],
54+
} = rightBound;
55+
if (bound === 0n) {
56+
return leftEntries;
57+
}
58+
return harden({
59+
// eslint-disable-next-line no-restricted-globals
60+
[Symbol.iterator]() {
61+
// eslint-disable-next-line no-restricted-syntax, no-restricted-globals
62+
const entriesIterator = leftEntries[Symbol.iterator]();
63+
let count = 0n;
64+
return harden({
65+
next() {
66+
for (;;) {
67+
const { done, value } =
68+
/** @type {IteratorResult<[Passable, bigint]>} */ (
69+
entriesIterator.next()
70+
);
71+
if (done) {
72+
if (count !== bound) {
73+
if (count < bound) {
74+
throw Fail`Only has ${q(count)} matches, but needs at least ${bound}`;
75+
}
76+
throw Fail`internal: ${q(count)} should not get ahead of ${q(bound)}`;
77+
}
78+
return harden({ done: true, value: undefined });
79+
}
80+
const [element, num] = value;
81+
if (count === bound) {
82+
return harden({ done: false, value: [element, num] });
83+
}
84+
if (matches(element, elementPatt)) {
85+
const numRest = bound - count;
86+
if (num <= numRest) {
87+
count += num;
88+
} else {
89+
count = bound;
90+
return harden({ done: false, value: [element, num - numRest] });
91+
}
92+
} else {
93+
return harden({ done: false, value: [element, num] });
94+
}
95+
}
96+
},
97+
});
98+
},
99+
});
100+
};
101+
15102
/**
16103
* @import {Key} from '@endo/patterns'
17104
* @import {MathHelpers, SetValue} from '../types.js'
@@ -42,52 +129,26 @@ export const setMathHelpers = harden({
42129
if (isKey(rightBound)) {
43130
return elementsIsSuperset(left, rightBound);
44131
}
45-
mustMatch(rightBound, AmountValueHasBoundShape, 'right value bound');
46-
const {
47-
payload: [elementPatt, bound],
48-
} = rightBound;
49-
if (bound === 0n) {
50-
return true;
51-
}
52-
let count = 0n;
53-
for (const element of left) {
54-
if (matches(element, elementPatt)) {
55-
count += 1n;
56-
}
57-
if (count >= bound) {
58-
return true;
59-
}
60-
}
61-
return false;
132+
const leftEntries = mapIterable(left, el =>
133+
harden(/** @type {[Passable, bigint]} */ ([el, 1n])),
134+
);
135+
return entriesIsGTEHasBound(leftEntries, rightBound);
62136
},
63137
doIsEqual: (x, y) => elementsCompare(x, y) === 0,
64138
doAdd: elementsDisjointUnion,
65139
doSubtract: (left, rightBound) => {
66140
if (isKey(rightBound)) {
67141
return elementsDisjointSubtract(left, rightBound);
68142
}
69-
mustMatch(rightBound, AmountValueHasBoundShape, 'right value bound');
70-
const {
71-
payload: [elementPatt, bound],
72-
} = rightBound;
73-
if (bound === 0n) {
74-
return left;
75-
}
76-
let count = 0n;
143+
const leftEntries = mapIterable(left, el =>
144+
harden(/** @type {[Passable, bigint]} */ ([el, 1n])),
145+
);
146+
const remainingEntries = entriesMinusHasBound(leftEntries, rightBound);
77147
const result = [];
78-
for (const element of left) {
79-
if (count < bound) {
80-
if (matches(element, elementPatt)) {
81-
count += 1n;
82-
} else {
83-
result.push(element);
84-
}
85-
} else {
86-
result.push(element);
87-
}
148+
for (const [element, num] of remainingEntries) {
149+
num === 1n || Fail`Set subtract elements can only occur once`;
150+
result.push(element);
88151
}
89-
count >= bound ||
90-
Fail`Only has ${q(count)} matches, but needs at least ${bound}`;
91-
return result;
152+
return harden(result);
92153
},
93154
});

0 commit comments

Comments
 (0)