Skip to content

Commit b8ebc19

Browse files
committed
fixup! abstract helper iterations
1 parent 4426a4a commit b8ebc19

File tree

1 file changed

+100
-39
lines changed

1 file changed

+100
-39
lines changed
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)