11// @jessie -check
22
33import { Fail , q } from '@endo/errors' ;
4- import { passStyleOf } from '@endo/pass-style' ;
4+ import { passStyleOf , mapIterable } from '@endo/pass-style' ;
55import { isKey , assertKey , mustMatch , matches } from '@endo/patterns' ;
66import {
77 elementsIsSuperset ,
@@ -12,6 +12,93 @@ import {
1212} from '@agoric/store' ;
1313import { 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