@the-w1zard/dx 1.0.8 → 1.0.9
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.html +823 -121
- package/README.md +288 -107
- package/dist/index.js +12967 -0
- package/package.json +21 -6
- package/src/dx.d.ts +2 -10
- package/src/array.js +0 -221
- package/src/array.ts +0 -301
- package/src/boolean_byte.js +0 -22
- package/src/boolean_byte.ts +0 -23
- package/src/class.js +0 -1
- package/src/class.ts +0 -0
- package/src/date.js +0 -649
- package/src/date.ts +0 -660
- package/src/error.js +0 -60
- package/src/error.ts +0 -81
- package/src/index.js +0 -18
- package/src/index.ts +0 -18
- package/src/iterator.js +0 -386
- package/src/iterator.ts +0 -407
- package/src/map.js +0 -495
- package/src/map.ts +0 -513
- package/src/map_byte.js +0 -483
- package/src/map_byte.ts +0 -422
- package/src/number.js +0 -178
- package/src/number.ts +0 -212
- package/src/object.js +0 -217
- package/src/object.ts +0 -291
- package/src/promise.js +0 -206
- package/src/promise.ts +0 -309
- package/src/reg_ex.js +0 -479
- package/src/reg_ex.ts +0 -423
- package/src/set.js +0 -227
- package/src/set.ts +0 -198
- package/src/set_byte.js +0 -261
- package/src/set_byte.ts +0 -223
- package/src/soa_codec.js +0 -28
- package/src/soa_codec.ts +0 -27
- package/src/string.js +0 -133
- package/src/string.ts +0 -175
- package/src/unit8array.js +0 -272
- package/src/unit8array.ts +0 -374
- package/src/url_search_params.js +0 -172
- package/src/url_search_params.ts +0 -187
- package/src/weak_map.js +0 -136
- package/src/weak_map.ts +0 -155
- package/src/weak_set.js +0 -92
- package/src/weak_set.ts +0 -101
package/src/map_byte.ts
DELETED
|
@@ -1,422 +0,0 @@
|
|
|
1
|
-
type F64 = Float64Array;
|
|
2
|
-
type V128 = N | F64; // a "value" is either scalar (stride=1) or a stride-length Float64 view
|
|
3
|
-
|
|
4
|
-
type Derived = {
|
|
5
|
-
deps: AR<N>;
|
|
6
|
-
fn: (...vals: AR<V128>) => V128;
|
|
7
|
-
dirty: B;
|
|
8
|
-
has: B;
|
|
9
|
-
value?: V128;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export type M2BEvent = {
|
|
13
|
-
op: "set" | "upd" | "del" | "dirty";
|
|
14
|
-
pid: N;
|
|
15
|
-
v?: V128;
|
|
16
|
-
prev?: V128;
|
|
17
|
-
has: B;
|
|
18
|
-
batch?: B;
|
|
19
|
-
};
|
|
20
|
-
type Sub = (e: M2BEvent) => V;
|
|
21
|
-
|
|
22
|
-
const sameValue128 = (a: V128 | UD, b: V128 | UD): B => {
|
|
23
|
-
if (a === b) return true;
|
|
24
|
-
if (a instanceof Float64Array && b instanceof Float64Array) {
|
|
25
|
-
if (a.length !== b.length) return false;
|
|
26
|
-
for (let i = 0; i < a.length; i++) if (!Object.is(a[i], b[i])) return false;
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
return Object.is(a, b);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export class M2BI {
|
|
33
|
-
readonly cap: N;
|
|
34
|
-
readonly stride: N;
|
|
35
|
-
|
|
36
|
-
readonly pidToSlot: I32;
|
|
37
|
-
readonly slotToPid: I32;
|
|
38
|
-
readonly vals: F64;
|
|
39
|
-
|
|
40
|
-
#size = 0;
|
|
41
|
-
#subs?: Set<Sub>;
|
|
42
|
-
#hist?: AR<M2BEvent>;
|
|
43
|
-
#histLimit = 0;
|
|
44
|
-
#strict = false;
|
|
45
|
-
#iterating = 0;
|
|
46
|
-
#batching = 0;
|
|
47
|
-
#pending?: Map<N, M2BEvent>;
|
|
48
|
-
#derived?: Map<N, Derived>;
|
|
49
|
-
#dependents?: Map<N, Set<N>>;
|
|
50
|
-
|
|
51
|
-
constructor(maxParticles: N, stride: N = 1, seed?: I<readonly [N, V128]>) {
|
|
52
|
-
this.cap = maxParticles;
|
|
53
|
-
this.stride = stride;
|
|
54
|
-
this.pidToSlot = new Int32Array(maxParticles).fill(-1);
|
|
55
|
-
this.slotToPid = new Int32Array(maxParticles).fill(-1);
|
|
56
|
-
this.vals = new Float64Array(maxParticles * stride);
|
|
57
|
-
if (seed) for (const [pid, v] of seed) this.s(v, pid);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
get size(): N { return this.#size; }
|
|
61
|
-
|
|
62
|
-
// ---------------------------------------------------------------- internals
|
|
63
|
-
#emit(e: M2BEvent): V {
|
|
64
|
-
if (this.#batching > 0) {
|
|
65
|
-
this.#pending ??= new Map();
|
|
66
|
-
const pending = this.#pending.get(e.pid);
|
|
67
|
-
this.#pending.set(e.pid, pending ? { ...e, prev: pending.prev, batch: true } : { ...e, batch: true });
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
this.#record(e);
|
|
71
|
-
this.#subs?.forEach((sub) => { try { sub(e); } catch (err: A) { process.emitWarning(err); } });
|
|
72
|
-
}
|
|
73
|
-
#record(e: M2BEvent): V {
|
|
74
|
-
if (!this.#hist) return;
|
|
75
|
-
this.#hist.push(e);
|
|
76
|
-
if (this.#hist.length > this.#histLimit) this.#hist.splice(0, this.#hist.length - this.#histLimit);
|
|
77
|
-
}
|
|
78
|
-
#invalidate(pid: N): V {
|
|
79
|
-
const deps = this.#dependents?.get(pid);
|
|
80
|
-
if (!deps) return;
|
|
81
|
-
for (const dk of deps) {
|
|
82
|
-
const d = this.#derived?.get(dk);
|
|
83
|
-
if (!d || d.dirty) continue;
|
|
84
|
-
d.dirty = true;
|
|
85
|
-
this.#emit({ op: "dirty", pid: dk, has: d.has });
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
#computeDerived(pid: N, d: Derived): V128 {
|
|
89
|
-
const vals = d.deps.map((dep) => this.g(dep) as V128);
|
|
90
|
-
const next = d.fn(...vals);
|
|
91
|
-
const had = d.has;
|
|
92
|
-
const prev = d.value;
|
|
93
|
-
d.value = next; d.has = true; d.dirty = false;
|
|
94
|
-
if (!had || !sameValue128(prev, next)) {
|
|
95
|
-
this.#emit({ op: had ? "upd" : "set", pid, v: next, prev, has: true });
|
|
96
|
-
}
|
|
97
|
-
return next;
|
|
98
|
-
}
|
|
99
|
-
#assertNotIterating(op: S): V {
|
|
100
|
-
if (this.#strict && this.#iterating > 0) {
|
|
101
|
-
throw new Error(`M2BI.${op}() during iteration. Snapshot keys first, or disable $strict().`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
#readSlot(slot: N): V128 {
|
|
105
|
-
return this.stride === 1 ? this.vals[slot] : this.vals.subarray(slot * this.stride, slot * this.stride + this.stride);
|
|
106
|
-
}
|
|
107
|
-
#writeSlot(slot: N, v: V128): V {
|
|
108
|
-
if (this.stride === 1) this.vals[slot] = v as N;
|
|
109
|
-
else this.vals.set(v as F64, slot * this.stride);
|
|
110
|
-
}
|
|
111
|
-
#fresh(): M2BI { return new M2BI(this.cap, this.stride); } // [sibling-store factory]
|
|
112
|
-
|
|
113
|
-
// ---------------------------------------------------------------- subs / batch
|
|
114
|
-
public on(fn: Sub): () => V { (this.#subs ??= new Set()).add(fn); return () => this.off(fn); } // [ON]
|
|
115
|
-
public off(fn: Sub): V { this.#subs?.delete(fn); } // [OFF]
|
|
116
|
-
public batch(fn: () => V): this { // [BATCH]
|
|
117
|
-
this.#batching++;
|
|
118
|
-
this.#pending ??= new Map();
|
|
119
|
-
try { fn(); }
|
|
120
|
-
finally {
|
|
121
|
-
this.#batching--;
|
|
122
|
-
if (this.#batching === 0) {
|
|
123
|
-
const events = [...this.#pending.values()];
|
|
124
|
-
this.#pending = undefined;
|
|
125
|
-
for (const e of events) { this.#record(e); this.#subs?.forEach(sub => sub(e)); }
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return this;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// ---------------------------------------------------------------- core CRUD
|
|
132
|
-
public s(v: V128, pid: N): this { // [SET]
|
|
133
|
-
this.#assertNotIterating("s");
|
|
134
|
-
let slot = this.pidToSlot[pid];
|
|
135
|
-
const has = slot !== -1;
|
|
136
|
-
const prev = has ? this.#readSlot(slot) : undefined;
|
|
137
|
-
if (!has) {
|
|
138
|
-
slot = this.#size;
|
|
139
|
-
this.pidToSlot[pid] = slot;
|
|
140
|
-
this.slotToPid[slot] = pid;
|
|
141
|
-
this.#size++;
|
|
142
|
-
}
|
|
143
|
-
this.#writeSlot(slot, v);
|
|
144
|
-
this.#invalidate(pid);
|
|
145
|
-
this.#emit({ op: has ? "upd" : "set", pid, v, prev, has });
|
|
146
|
-
return this;
|
|
147
|
-
}
|
|
148
|
-
public mutate(pid: N, fn: (old: V128, has: true) => V128): V128 | UD { // [MUTATE]
|
|
149
|
-
this.#assertNotIterating("mutate");
|
|
150
|
-
if (!this.h(pid)) return undefined;
|
|
151
|
-
const next = fn(this.g(pid) as V128, true);
|
|
152
|
-
this.s(next, pid);
|
|
153
|
-
return next;
|
|
154
|
-
}
|
|
155
|
-
public d(pid: N): B { // [DELETE]
|
|
156
|
-
this.#assertNotIterating("delete");
|
|
157
|
-
const slot = this.pidToSlot[pid];
|
|
158
|
-
if (slot === -1) return false;
|
|
159
|
-
const prev = this.#readSlot(slot);
|
|
160
|
-
const lastSlot = this.#size - 1;
|
|
161
|
-
const lastPid = this.slotToPid[lastSlot];
|
|
162
|
-
if (slot !== lastSlot) {
|
|
163
|
-
this.#writeSlot(slot, this.#readSlot(lastSlot));
|
|
164
|
-
this.slotToPid[slot] = lastPid;
|
|
165
|
-
this.pidToSlot[lastPid] = slot;
|
|
166
|
-
}
|
|
167
|
-
this.pidToSlot[pid] = -1;
|
|
168
|
-
this.slotToPid[lastSlot] = -1;
|
|
169
|
-
this.#size--;
|
|
170
|
-
this.#invalidate(pid);
|
|
171
|
-
this.#emit({ op: "del", pid, prev, has: false });
|
|
172
|
-
return true;
|
|
173
|
-
}
|
|
174
|
-
public c(): V { // [CLEAR]
|
|
175
|
-
this.#assertNotIterating("clear");
|
|
176
|
-
for (let i = this.#size - 1; i >= 0; i--) {
|
|
177
|
-
const pid = this.slotToPid[i];
|
|
178
|
-
const prev = this.#readSlot(i);
|
|
179
|
-
this.pidToSlot[pid] = -1;
|
|
180
|
-
this.slotToPid[i] = -1;
|
|
181
|
-
this.#invalidate(pid);
|
|
182
|
-
this.#emit({ op: "del", pid, prev, has: false });
|
|
183
|
-
}
|
|
184
|
-
this.#size = 0;
|
|
185
|
-
}
|
|
186
|
-
public dv(pid: N, deps: AR<N>, fn: (...vals: AR<V128>) => V128): this { // [DERIVED]
|
|
187
|
-
(this.#derived ??= new Map()).set(pid, { deps, fn, dirty: true, has: false });
|
|
188
|
-
for (const dep of deps) {
|
|
189
|
-
const set = (this.#dependents ??= new Map());
|
|
190
|
-
if (!set.has(dep)) set.set(dep, new Set());
|
|
191
|
-
set.get(dep)!.add(pid);
|
|
192
|
-
}
|
|
193
|
-
return this;
|
|
194
|
-
}
|
|
195
|
-
public dirty(...pids: AR<N>): this { // [DIRTY]
|
|
196
|
-
for (const pid of pids) {
|
|
197
|
-
const d = this.#derived?.get(pid);
|
|
198
|
-
if (d && !d.dirty) { d.dirty = true; this.#emit({ op: "dirty", pid, has: d.has }); }
|
|
199
|
-
}
|
|
200
|
-
return this;
|
|
201
|
-
}
|
|
202
|
-
public g(pid: N): V128 | UD { // [GET]
|
|
203
|
-
const d = this.#derived?.get(pid);
|
|
204
|
-
if (d) return d.dirty || !d.has ? this.#computeDerived(pid, d) : d.value!;
|
|
205
|
-
const slot = this.pidToSlot[pid];
|
|
206
|
-
return slot === -1 ? undefined : this.#readSlot(slot);
|
|
207
|
-
}
|
|
208
|
-
public h(pid: N): B { // [HAS]
|
|
209
|
-
const d = this.#derived?.get(pid);
|
|
210
|
-
if (d) { if (d.dirty || !d.has) this.#computeDerived(pid, d); return true; }
|
|
211
|
-
return this.pidToSlot[pid] !== -1;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// ---------------------------------------------------------------- debug
|
|
215
|
-
public $debug(limit: N = 256): this { this.#hist ??= []; this.#histLimit = limit; return this; } // [$DEBUG]
|
|
216
|
-
public $strict(): this { this.#strict = true; return this; } // [$STRICT]
|
|
217
|
-
public $history(): AR<M2BEvent> { return this.#hist ? [...this.#hist] : []; } // [$HISTORY]
|
|
218
|
-
public $why(pid: N): M2BEvent | UD { // [$WHY]
|
|
219
|
-
if (!this.#hist) return undefined;
|
|
220
|
-
for (let i = this.#hist.length - 1; i >= 0; i--) if (this.#hist[i].pid === pid) return this.#hist[i];
|
|
221
|
-
return undefined;
|
|
222
|
-
}
|
|
223
|
-
public $diff(other: M2BI) { // [$DIFF]
|
|
224
|
-
const add: AR<[N, V128]> = [];
|
|
225
|
-
const del: AR<N> = [];
|
|
226
|
-
const chg: AR<[N, V128, V128]> = [];
|
|
227
|
-
for (const [pid, v] of this) {
|
|
228
|
-
if (!other.has(pid)) add.push([pid, v]);
|
|
229
|
-
else if (!sameValue128(other.get(pid), v)) chg.push([pid, v, other.get(pid)!]);
|
|
230
|
-
}
|
|
231
|
-
for (const pid of other.keys()) if (!this.has(pid)) del.push(pid);
|
|
232
|
-
return { add, del, chg };
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// ---------------------------------------------------------------- iteration (dense slots, no hashing)
|
|
236
|
-
public *entries(): Generator<[N, V128]> { // [*ENTRIES]
|
|
237
|
-
this.#iterating++;
|
|
238
|
-
try { for (let i = 0; i < this.#size; i++) yield [this.slotToPid[i], this.#readSlot(i)]; }
|
|
239
|
-
finally { this.#iterating--; }
|
|
240
|
-
}
|
|
241
|
-
public *keys(): Generator<N> { // [*KEYS]
|
|
242
|
-
this.#iterating++;
|
|
243
|
-
try { for (let i = 0; i < this.#size; i++) yield this.slotToPid[i]; }
|
|
244
|
-
finally { this.#iterating--; }
|
|
245
|
-
}
|
|
246
|
-
public *values(): Generator<V128> { // [*VALUES]
|
|
247
|
-
this.#iterating++;
|
|
248
|
-
try { for (let i = 0; i < this.#size; i++) yield this.#readSlot(i); }
|
|
249
|
-
finally { this.#iterating--; }
|
|
250
|
-
}
|
|
251
|
-
public [Symbol.iterator](): Generator<[N, V128]> { return this.entries(); }
|
|
252
|
-
|
|
253
|
-
// ---------------------------------------------------------------- alias / has/has-not helpers
|
|
254
|
-
public has = this.h; // [has alias]
|
|
255
|
-
public get = this.g; // [get alias]
|
|
256
|
-
public delete = this.d; // [delete alias]
|
|
257
|
-
public clear = this.c; // [clear alias]
|
|
258
|
-
public f = this.forEach.bind(this); // [FOR EACH]
|
|
259
|
-
public forEach(fn: (v: V128, pid: N, map: this) => V): V { for (const [pid, v] of this) fn(v, pid, this); }
|
|
260
|
-
|
|
261
|
-
// ---------------------------------------------------------------- sugar
|
|
262
|
-
public swap(value: V128, pid: N): V128 | UD { // [SWAP]
|
|
263
|
-
const previous = this.has(pid) ? this.get(pid) : value;
|
|
264
|
-
this.s(value, pid);
|
|
265
|
-
return previous;
|
|
266
|
-
}
|
|
267
|
-
public take(pid: N): V128 | UD { const value = this.g(pid); this.d(pid); return value; } // [TAKE]
|
|
268
|
-
public peek(pid: N): V128 | false { return this.h(pid) ? (this.g(pid) as V128) : false; } // [PEEK]
|
|
269
|
-
public getOrI(value: V128, pid: N): V128 { // [GETORI]
|
|
270
|
-
if (this.h(pid)) return this.g(pid) as V128;
|
|
271
|
-
this.s(value, pid);
|
|
272
|
-
return value;
|
|
273
|
-
}
|
|
274
|
-
public getOrM(make: () => V128, pid: N): V128 { // [GETORM]
|
|
275
|
-
if (this.h(pid)) return this.g(pid) as V128;
|
|
276
|
-
const value = make();
|
|
277
|
-
this.s(value, pid);
|
|
278
|
-
return value;
|
|
279
|
-
}
|
|
280
|
-
public update(pid: N, fn: (old: V128, has: B) => V128, initial?: V128): V128 { // [UPDATE]
|
|
281
|
-
const has = this.h(pid);
|
|
282
|
-
const old = has ? (this.g(pid) as V128) : (initial === undefined ? (0 as V128) : initial);
|
|
283
|
-
const next = fn(old, has);
|
|
284
|
-
this.s(next, pid);
|
|
285
|
-
return next;
|
|
286
|
-
}
|
|
287
|
-
public setIfAbsent(value: V128, pid: N): B { // [SETIFABSENT]
|
|
288
|
-
if (this.h(pid)) return false;
|
|
289
|
-
this.s(value, pid);
|
|
290
|
-
return true;
|
|
291
|
-
}
|
|
292
|
-
public setIfPresent(value: V128, pid: N): B { // [SETIFPRESENT]
|
|
293
|
-
if (!this.h(pid)) return false;
|
|
294
|
-
this.s(value, pid);
|
|
295
|
-
return true;
|
|
296
|
-
}
|
|
297
|
-
public replace(value: V128, pid: N): B { return this.setIfPresent(value, pid); } // [REPLACE]
|
|
298
|
-
public inc(pid: N, by: N = 1): N { // [INC]
|
|
299
|
-
return this.update(pid, (old) => (((old as N) ?? 0) + by) as V128, 0) as N;
|
|
300
|
-
}
|
|
301
|
-
public touch(pid: N): B { return this.setIfAbsent(0 as V128, pid); } // [SWTOUCHAP]
|
|
302
|
-
|
|
303
|
-
// ---------------------------------------------------------------- transforms (produce new M2BI, same cap/stride)
|
|
304
|
-
public mapValues(fn: (v: V128, pid: N) => V128): M2BI { // [MAPVALUES]
|
|
305
|
-
const out = this.#fresh();
|
|
306
|
-
for (const [pid, v] of this) out.s(fn(v, pid), pid);
|
|
307
|
-
return out;
|
|
308
|
-
}
|
|
309
|
-
public mapKeys(fn: (v: V128, pid: N) => N): M2BI { // [MAPKEYS]
|
|
310
|
-
const out = this.#fresh(); // target pids must still fit within cap
|
|
311
|
-
for (const [pid, v] of this) out.s(v, fn(v, pid));
|
|
312
|
-
return out;
|
|
313
|
-
}
|
|
314
|
-
public filterKV(pred: (v: V128, pid: N) => B): this { // [FILTERKV]
|
|
315
|
-
for (const pid of [...this.keys()]) if (!pred(this.g(pid) as V128, pid)) this.d(pid);
|
|
316
|
-
return this;
|
|
317
|
-
}
|
|
318
|
-
public filterKeys(pred: (v: V128, pid: N) => B): this { return this.filterKV(pred); } // [FILTERKEYS]
|
|
319
|
-
public partition(pred: (v: V128, pid: N) => B): [M2BI, M2BI] { // [PARTITION]
|
|
320
|
-
const yes = this.#fresh(), no = this.#fresh();
|
|
321
|
-
for (const [pid, v] of this) (pred(v, pid) ? yes : no).s(v, pid);
|
|
322
|
-
return [yes, no];
|
|
323
|
-
}
|
|
324
|
-
public find(pred: (v: V128, pid: N) => B): [N, V128] | UD { // [FIND]
|
|
325
|
-
for (const [pid, v] of this) if (pred(v, pid)) return [pid, v];
|
|
326
|
-
return undefined;
|
|
327
|
-
}
|
|
328
|
-
public findKey(pred: (v: V128, pid: N) => B): N | UD { // [FINDKEY]
|
|
329
|
-
for (const [pid, v] of this) if (pred(v, pid)) return pid;
|
|
330
|
-
return undefined;
|
|
331
|
-
}
|
|
332
|
-
public findValue(pred: (v: V128, pid: N) => B): V128 | UD { // [FINDVALUE]
|
|
333
|
-
for (const [pid, v] of this) if (pred(v, pid)) return v;
|
|
334
|
-
return undefined;
|
|
335
|
-
}
|
|
336
|
-
public some(pred: (v: V128, pid: N) => B): B { // [SOME]
|
|
337
|
-
for (const [pid, v] of this) if (pred(v, pid)) return true;
|
|
338
|
-
return false;
|
|
339
|
-
}
|
|
340
|
-
public every(pred: (v: V128, pid: N) => B): B { // [EVERY]
|
|
341
|
-
for (const [pid, v] of this) if (!pred(v, pid)) return false;
|
|
342
|
-
return true;
|
|
343
|
-
}
|
|
344
|
-
public reduce<G>(fn: (acc: G, v: V128, pid: N) => G, initial: G): G { // [REDUCE]
|
|
345
|
-
let acc = initial;
|
|
346
|
-
for (const [pid, v] of this) acc = fn(acc, v, pid);
|
|
347
|
-
return acc;
|
|
348
|
-
}
|
|
349
|
-
public forEachKV(fn: (v: V128, pid: N, map: this) => V): V { for (const [pid, v] of this) fn(v, pid, this); } // [FOREACH]
|
|
350
|
-
public invert(): M2<V128, N> { // [INVERT]
|
|
351
|
-
const out = new Map<S, N>(); // values aren't valid pids/Int32 slots in general -> plain Map, not M2BI
|
|
352
|
-
for (const [pid, v] of this) out.set(this.stride === 1 ? String(v) : Array.from(v as F64).join(","), pid);
|
|
353
|
-
return out as A;
|
|
354
|
-
}
|
|
355
|
-
public unique(fn?: (v: V128, pid: N) => N): M2BI { // [UNIQUE]
|
|
356
|
-
const out = this.#fresh();
|
|
357
|
-
const seen = new Set<S>();
|
|
358
|
-
for (const [pid, v] of this) {
|
|
359
|
-
const keyFor = fn ? fn(v, pid) : pid;
|
|
360
|
-
const dedupeKey = this.stride === 1 ? String(v) : Array.from(v as F64).join(",");
|
|
361
|
-
if (!seen.has(dedupeKey)) { seen.add(dedupeKey); out.s(v, keyFor); }
|
|
362
|
-
}
|
|
363
|
-
return out;
|
|
364
|
-
}
|
|
365
|
-
public pick(pids: Iterable<N>): M2BI { // [PICK]
|
|
366
|
-
const out = this.#fresh();
|
|
367
|
-
for (const pid of pids) if (this.h(pid)) out.s(this.g(pid) as V128, pid);
|
|
368
|
-
return out;
|
|
369
|
-
}
|
|
370
|
-
public omit(pids: Iterable<N>): M2BI { // [OMIT]
|
|
371
|
-
const omitted = new Set(pids);
|
|
372
|
-
const out = this.#fresh();
|
|
373
|
-
for (const [pid, v] of this) if (!omitted.has(pid)) out.s(v, pid);
|
|
374
|
-
return out;
|
|
375
|
-
}
|
|
376
|
-
public merge(other: Iterable<readonly [N, V128]>): this { // [MERGE]
|
|
377
|
-
for (const [pid, v] of other) this.s(v, pid);
|
|
378
|
-
return this;
|
|
379
|
-
}
|
|
380
|
-
public clearAndSet(entries: Iterable<readonly [N, V128]>): this { // [CLEARANDSET]
|
|
381
|
-
return this.batch(() => { this.clear(); for (const [pid, v] of entries) this.s(v, pid); });
|
|
382
|
-
}
|
|
383
|
-
public toObject(): OB<V128> { // [TOPOBJECT]
|
|
384
|
-
const out = {} as OB<V128>;
|
|
385
|
-
for (const [pid, v] of this) out[pid as unknown as S] = v;
|
|
386
|
-
return out;
|
|
387
|
-
}
|
|
388
|
-
public toValues(): AR<V128> { return [...this.values()]; } // [TOVALUES]
|
|
389
|
-
public toKeys(): AR<N> { return [...this.keys()]; } // [TOKEYS]
|
|
390
|
-
public toArray(): AR<[N, V128]> { return [...this.entries()]; } // [TOARRAY]
|
|
391
|
-
public group<G>(fn: (v: V128, pid: N) => G): Map<G, AR<V128>> { // [GROUP]
|
|
392
|
-
const out = new Map<G, AR<V128>>();
|
|
393
|
-
for (const [pid, v] of this) {
|
|
394
|
-
const k = fn(v, pid);
|
|
395
|
-
if (!out.has(k)) out.set(k, []);
|
|
396
|
-
out.get(k)!.push(v);
|
|
397
|
-
}
|
|
398
|
-
return out;
|
|
399
|
-
}
|
|
400
|
-
public clone(): this { // [CLONE]
|
|
401
|
-
return new M2BI(this.cap, this.stride, this.entries()) as this;
|
|
402
|
-
}
|
|
403
|
-
public equals(other: M2BI): B { // [EQUALS]
|
|
404
|
-
if (other === this) return true;
|
|
405
|
-
if (this.size !== other.size) return false;
|
|
406
|
-
for (const [pid, v] of this) if (!other.has(pid) || !sameValue128(other.get(pid), v)) return false;
|
|
407
|
-
return true;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// ---------------------------------------------------------------- statics
|
|
411
|
-
static fromObject(obj: Record<N, V128>, maxParticles: N, stride: N = 1): M2BI { // [FROMOBBJ]
|
|
412
|
-
return new M2BI(maxParticles, stride, Object.entries(obj).map(([k, v]) => [Number(k), v] as [N, V128]));
|
|
413
|
-
}
|
|
414
|
-
static fromEntries(entries: I<readonly [N, V128]>, maxParticles: N, stride: N = 1): M2BI { // [FROMENTIRES]
|
|
415
|
-
return new M2BI(maxParticles, stride, entries);
|
|
416
|
-
}
|
|
417
|
-
static from(map: RM<N, V128>, maxParticles: N, stride: N = 1): M2BI { // [FROM]
|
|
418
|
-
return new M2BI(maxParticles, stride, map as A);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
globalThis.M2B = M2BI;
|
package/src/number.js
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
export class N2I extends Number {
|
|
2
|
-
abs() {
|
|
3
|
-
return new N2I(Math.abs(this.valueOf()));
|
|
4
|
-
}
|
|
5
|
-
sign() {
|
|
6
|
-
return new N2I(Math.sign(this.valueOf()));
|
|
7
|
-
}
|
|
8
|
-
neg() {
|
|
9
|
-
return new N2I(-this.valueOf());
|
|
10
|
-
}
|
|
11
|
-
inv() {
|
|
12
|
-
return new N2I(1 / this.valueOf());
|
|
13
|
-
}
|
|
14
|
-
clamp(min, max) {
|
|
15
|
-
return new N2I(Math.min(Math.max(this.valueOf(), min), max));
|
|
16
|
-
}
|
|
17
|
-
wrap(min, max) {
|
|
18
|
-
const range = max - min;
|
|
19
|
-
if (range <= 0)
|
|
20
|
-
throw new RangeError("max must be greater than min");
|
|
21
|
-
return new N2I((((this.valueOf() - min) % range) + range) % range + min);
|
|
22
|
-
}
|
|
23
|
-
lerp(to, t) {
|
|
24
|
-
return new N2I(this.valueOf() + (to - this.valueOf()) * t);
|
|
25
|
-
}
|
|
26
|
-
map(inMin, inMax, outMin, outMax) {
|
|
27
|
-
if (inMax === inMin)
|
|
28
|
-
throw new RangeError("Input range cannot be zero");
|
|
29
|
-
return new N2I(outMin + ((this.valueOf() - inMin) / (inMax - inMin)) * (outMax - outMin));
|
|
30
|
-
}
|
|
31
|
-
remap(inMin, inMax, outMin, outMax) {
|
|
32
|
-
return this.map(inMin, inMax, outMin, outMax);
|
|
33
|
-
}
|
|
34
|
-
roundTo(precision = 0) {
|
|
35
|
-
const factor = 10 ** precision;
|
|
36
|
-
return new N2I(Math.round((this.valueOf() + Number.EPSILON) * factor) / factor);
|
|
37
|
-
}
|
|
38
|
-
floorTo(precision = 0) {
|
|
39
|
-
const factor = 10 ** precision;
|
|
40
|
-
return new N2I(Math.floor(this.valueOf() * factor) / factor);
|
|
41
|
-
}
|
|
42
|
-
ceilTo(precision = 0) {
|
|
43
|
-
const factor = 10 ** precision;
|
|
44
|
-
return new N2I(Math.ceil(this.valueOf() * factor) / factor);
|
|
45
|
-
}
|
|
46
|
-
truncTo(precision = 0) {
|
|
47
|
-
const factor = 10 ** precision;
|
|
48
|
-
return new N2I(Math.trunc(this.valueOf() * factor) / factor);
|
|
49
|
-
}
|
|
50
|
-
isFinite() {
|
|
51
|
-
return Number.isFinite(this.valueOf());
|
|
52
|
-
}
|
|
53
|
-
isInteger() {
|
|
54
|
-
return Number.isInteger(this.valueOf());
|
|
55
|
-
}
|
|
56
|
-
isSafeInteger() {
|
|
57
|
-
return Number.isSafeInteger(this.valueOf());
|
|
58
|
-
}
|
|
59
|
-
isNaN() {
|
|
60
|
-
return Number.isNaN(this.valueOf());
|
|
61
|
-
}
|
|
62
|
-
isBetween(min, max, inclusive = true) {
|
|
63
|
-
const value = this.valueOf();
|
|
64
|
-
return inclusive ? value >= min && value <= max : value > min && value < max;
|
|
65
|
-
}
|
|
66
|
-
isPositive() {
|
|
67
|
-
return this.valueOf() > 0;
|
|
68
|
-
}
|
|
69
|
-
isNegative() {
|
|
70
|
-
return this.valueOf() < 0;
|
|
71
|
-
}
|
|
72
|
-
isZero() {
|
|
73
|
-
return this.valueOf() === 0;
|
|
74
|
-
}
|
|
75
|
-
isEven() {
|
|
76
|
-
const value = this.valueOf();
|
|
77
|
-
return Number.isInteger(value) && value % 2 === 0;
|
|
78
|
-
}
|
|
79
|
-
isOdd() {
|
|
80
|
-
const value = this.valueOf();
|
|
81
|
-
return Number.isInteger(value) && Math.abs(value % 2) === 1;
|
|
82
|
-
}
|
|
83
|
-
squared() {
|
|
84
|
-
return new N2I(this.valueOf() ** 2);
|
|
85
|
-
}
|
|
86
|
-
cubed() {
|
|
87
|
-
return new N2I(this.valueOf() ** 3);
|
|
88
|
-
}
|
|
89
|
-
sqrt() {
|
|
90
|
-
return new N2I(Math.sqrt(this.valueOf()));
|
|
91
|
-
}
|
|
92
|
-
cbrt() {
|
|
93
|
-
return new N2I(Math.cbrt(this.valueOf()));
|
|
94
|
-
}
|
|
95
|
-
pow(exponent) {
|
|
96
|
-
return new N2I(this.valueOf() ** exponent);
|
|
97
|
-
}
|
|
98
|
-
root(degree) {
|
|
99
|
-
if (degree === 0)
|
|
100
|
-
throw new RangeError("Root degree cannot be zero");
|
|
101
|
-
const value = this.valueOf();
|
|
102
|
-
if (value < 0 && Number.isInteger(degree) && Math.abs(degree % 2) === 1) {
|
|
103
|
-
return new N2I(-((-value) ** (1 / degree)));
|
|
104
|
-
}
|
|
105
|
-
return new N2I(value ** (1 / degree));
|
|
106
|
-
}
|
|
107
|
-
mod(divisor) {
|
|
108
|
-
if (divisor === 0)
|
|
109
|
-
throw new RangeError("Divisor cannot be zero");
|
|
110
|
-
return new N2I(((this.valueOf() % divisor) + divisor) % divisor);
|
|
111
|
-
}
|
|
112
|
-
percentOf(total) {
|
|
113
|
-
return new N2I((this.valueOf() / 100) * total);
|
|
114
|
-
}
|
|
115
|
-
percentChange(from) {
|
|
116
|
-
if (from === 0)
|
|
117
|
-
throw new RangeError("Cannot calculate percentage change from zero");
|
|
118
|
-
return new N2I(((this.valueOf() - from) / Math.abs(from)) * 100);
|
|
119
|
-
}
|
|
120
|
-
toRadians() {
|
|
121
|
-
return new N2I(this.valueOf() * (Math.PI / 180));
|
|
122
|
-
}
|
|
123
|
-
toDegrees() {
|
|
124
|
-
return new N2I(this.valueOf() * (180 / Math.PI));
|
|
125
|
-
}
|
|
126
|
-
toOrdinal() {
|
|
127
|
-
const value = this.valueOf();
|
|
128
|
-
const integer = Math.trunc(value);
|
|
129
|
-
const abs = Math.abs(integer);
|
|
130
|
-
const suffix = abs % 100 >= 11 && abs % 100 <= 13
|
|
131
|
-
? "th"
|
|
132
|
-
: { 1: "st", 2: "nd", 3: "rd" }[abs % 10] ?? "th";
|
|
133
|
-
return `${value}${suffix}`;
|
|
134
|
-
}
|
|
135
|
-
toBytes(unit, decimals = 2) {
|
|
136
|
-
const value = this.valueOf();
|
|
137
|
-
const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB"];
|
|
138
|
-
const index = unit === undefined
|
|
139
|
-
? value === 0 ? 0 : Math.min(Math.floor(Math.log(Math.abs(value)) / Math.log(1024)), units.length - 1)
|
|
140
|
-
: Math.max(0, Math.min(Math.trunc(unit), units.length - 1));
|
|
141
|
-
return `${(value / 1024 ** index).toFixed(decimals)} ${units[index]}`;
|
|
142
|
-
}
|
|
143
|
-
toCompact(locales) {
|
|
144
|
-
return new Intl.NumberFormat(locales, { notation: "compact", maximumFractionDigits: 2 }).format(this.valueOf());
|
|
145
|
-
}
|
|
146
|
-
toCurrency(currency = "USD", locales) {
|
|
147
|
-
return new Intl.NumberFormat(locales, { style: "currency", currency }).format(this.valueOf());
|
|
148
|
-
}
|
|
149
|
-
toPercent(decimals = 0, locales) {
|
|
150
|
-
return new Intl.NumberFormat(locales, {
|
|
151
|
-
style: "percent",
|
|
152
|
-
maximumFractionDigits: decimals,
|
|
153
|
-
}).format(this.valueOf());
|
|
154
|
-
}
|
|
155
|
-
times(fn) {
|
|
156
|
-
const count = this.valueOf();
|
|
157
|
-
for (let i = 0; i < count; i++)
|
|
158
|
-
fn(i);
|
|
159
|
-
return this;
|
|
160
|
-
}
|
|
161
|
-
upTo(end, step = 1) {
|
|
162
|
-
if (step <= 0)
|
|
163
|
-
throw new RangeError("step must be positive");
|
|
164
|
-
const out = [];
|
|
165
|
-
for (let value = this.valueOf(); value <= end; value += step)
|
|
166
|
-
out.push(value);
|
|
167
|
-
return out;
|
|
168
|
-
}
|
|
169
|
-
downTo(end, step = 1) {
|
|
170
|
-
if (step <= 0)
|
|
171
|
-
throw new RangeError("step must be positive");
|
|
172
|
-
const out = [];
|
|
173
|
-
for (let value = this.valueOf(); value >= end; value -= step)
|
|
174
|
-
out.push(value);
|
|
175
|
-
return out;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
globalThis.N2 = N2I;
|