initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/node_modules
|
||||||
213
package-lock.json
generated
Normal file
213
package-lock.json
generated
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
{
|
||||||
|
"name": "enumerable",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "enumerable",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.12.7",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.4.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@cspotcode/source-map-support": {
|
||||||
|
"version": "0.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||||
|
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/trace-mapping": "0.3.9"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
|
"version": "1.4.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||||
|
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
|
"version": "0.3.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||||
|
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/resolve-uri": "^3.0.3",
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tsconfig/node10": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@tsconfig/node12": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@tsconfig/node14": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@tsconfig/node16": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "20.12.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz",
|
||||||
|
"integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/acorn": {
|
||||||
|
"version": "8.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||||
|
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"acorn": "bin/acorn"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/acorn-walk": {
|
||||||
|
"version": "8.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
|
||||||
|
"integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/arg": {
|
||||||
|
"version": "4.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||||
|
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/create-require": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/diff": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/make-error": {
|
||||||
|
"version": "1.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||||
|
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/ts-node": {
|
||||||
|
"version": "10.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||||
|
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@cspotcode/source-map-support": "^0.8.0",
|
||||||
|
"@tsconfig/node10": "^1.0.7",
|
||||||
|
"@tsconfig/node12": "^1.0.7",
|
||||||
|
"@tsconfig/node14": "^1.0.0",
|
||||||
|
"@tsconfig/node16": "^1.0.2",
|
||||||
|
"acorn": "^8.4.1",
|
||||||
|
"acorn-walk": "^8.1.1",
|
||||||
|
"arg": "^4.1.0",
|
||||||
|
"create-require": "^1.1.0",
|
||||||
|
"diff": "^4.0.1",
|
||||||
|
"make-error": "^1.1.1",
|
||||||
|
"v8-compile-cache-lib": "^3.0.1",
|
||||||
|
"yn": "3.1.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"ts-node": "dist/bin.js",
|
||||||
|
"ts-node-cwd": "dist/bin-cwd.js",
|
||||||
|
"ts-node-esm": "dist/bin-esm.js",
|
||||||
|
"ts-node-script": "dist/bin-script.js",
|
||||||
|
"ts-node-transpile-only": "dist/bin-transpile.js",
|
||||||
|
"ts-script": "dist/bin-script-deprecated.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@swc/core": ">=1.2.50",
|
||||||
|
"@swc/wasm": ">=1.2.50",
|
||||||
|
"@types/node": "*",
|
||||||
|
"typescript": ">=2.7"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@swc/core": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@swc/wasm": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
|
||||||
|
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/v8-compile-cache-lib": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/yn": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
package.json
Normal file
18
package.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "enumerable",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Implementation of Enumerable API from .NET",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node --import ./ts-loader.js src/index.ts",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "Hervé BECHER",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.12.7",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.4.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
2093
src/async.ts
Normal file
2093
src/async.ts
Normal file
File diff suppressed because it is too large
Load Diff
254
src/bitarray.ts
Normal file
254
src/bitarray.ts
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
import { join } from "./collector.js";
|
||||||
|
import { arrayLike, sequence } from "./sync.js";
|
||||||
|
import { asArray, isDefined } from "./utils.js";
|
||||||
|
|
||||||
|
const BYTE_SIZE = Uint8Array.BYTES_PER_ELEMENT * 8;
|
||||||
|
const FULL_BYTE = getMask(BYTE_SIZE);
|
||||||
|
|
||||||
|
function getMask(bitCount: number) {
|
||||||
|
return ~(~0b0 << bitCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getByteIndex(index: number) {
|
||||||
|
return Math.floor(index / BYTE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBitIndex(index: number) {
|
||||||
|
return index % BYTE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBit(byte: number, bitIndex: number, value: boolean) {
|
||||||
|
const mask = 0b1 << bitIndex;
|
||||||
|
return value ? (byte | mask) : (byte & ~mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
function* yieldBits(byte: number, bitCount: number) {
|
||||||
|
for (let i = 0; i < bitCount; i++) {
|
||||||
|
yield Boolean((byte >>> i) & 0b1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function* getBytes(bits: Iterable<boolean>) {
|
||||||
|
const bitIterator = bits[Symbol.iterator]();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
let next = bitIterator.next();
|
||||||
|
|
||||||
|
if (next.done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let byte = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < BYTE_SIZE; i++) {
|
||||||
|
byte |= Number(next.value) << i;
|
||||||
|
|
||||||
|
next = bitIterator.next();
|
||||||
|
|
||||||
|
if (next.done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield byte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BitArray implements Iterable<boolean> {
|
||||||
|
readonly #length: number;
|
||||||
|
readonly #bits: Uint8Array;
|
||||||
|
readonly #wholeBytes: number;
|
||||||
|
readonly #remainingBits: number;
|
||||||
|
readonly #remainingBitsMask: number;
|
||||||
|
|
||||||
|
public constructor(length: number) {
|
||||||
|
const exactBytes = length / BYTE_SIZE;
|
||||||
|
const remainingBits = length % BYTE_SIZE;
|
||||||
|
|
||||||
|
this.#length = length;
|
||||||
|
this.#bits = new Uint8Array(Math.ceil(exactBytes));
|
||||||
|
this.#wholeBytes = Math.floor(exactBytes);
|
||||||
|
this.#remainingBits = remainingBits;
|
||||||
|
this.#remainingBitsMask = getMask(remainingBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static from(bits: Iterable<boolean>) {
|
||||||
|
const arr = asArray(bits);
|
||||||
|
const result = new this(arr.length);
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
for (const bit of arr) {
|
||||||
|
result.set(i++, bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static of(...bits: boolean[]) {
|
||||||
|
return this.from(bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
*[Symbol.iterator]() {
|
||||||
|
for (let i = 0; i < this.#wholeBytes; i++) {
|
||||||
|
yield* yieldBits(this.#bits[i], BYTE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#remainingBits > 0) {
|
||||||
|
yield* yieldBits(this.#bits[this.#wholeBytes], this.#remainingBits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ensureValidIndex(index: number) {
|
||||||
|
if (index < 0 || index >= this.#length) {
|
||||||
|
throw new RangeError("The index is outside the BitArray range.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ensureSameSize(other: BitArray) {
|
||||||
|
if (this.#length !== other.length) {
|
||||||
|
throw new TypeError("The BitArrays do not have the same length.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get length() {
|
||||||
|
return this.#length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(index: number) {
|
||||||
|
this.#ensureValidIndex(index);
|
||||||
|
|
||||||
|
return Boolean((this.#bits[getByteIndex(index)] >>> getBitIndex(index)) & 0b1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set(index: number, value: boolean) {
|
||||||
|
this.#ensureValidIndex(index);
|
||||||
|
|
||||||
|
const byteIndex = getByteIndex(index);
|
||||||
|
this.#bits[byteIndex] = setBit(this.#bits[byteIndex], getBitIndex(index), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isFull() {
|
||||||
|
// return enumerable.sequence(this).all(bit => bit);
|
||||||
|
return this.#bits.subarray(0, this.#wholeBytes).every(byte => byte === FULL_BYTE) && (this.#remainingBits === 0 || this.#bits[this.#wholeBytes] === this.#remainingBitsMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
public isEmpty() {
|
||||||
|
// return enumerable.sequence(this).none(bit => bit);
|
||||||
|
return this.#bits.every(byte => byte === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public fill(value: boolean) {
|
||||||
|
const b = value ? FULL_BYTE : 0;
|
||||||
|
for (let i = 0; i < this.#wholeBytes; i++) {
|
||||||
|
this.#bits[i] = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#remainingBits > 0) {
|
||||||
|
this.#bits[this.#wholeBytes] = value ? this.#remainingBitsMask : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public and(other: BitArray) {
|
||||||
|
this.#ensureSameSize(other);
|
||||||
|
|
||||||
|
// let i = 0;
|
||||||
|
// for (const byte of getBytes(other)) {
|
||||||
|
// this.#bits[i++] &= byte;
|
||||||
|
// }
|
||||||
|
|
||||||
|
for (let i = 0; i < this.#length; i++) {
|
||||||
|
this.#bits[i] &= other.#bits[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public or(other: BitArray) {
|
||||||
|
this.#ensureSameSize(other);
|
||||||
|
|
||||||
|
// let i = 0;
|
||||||
|
// for (const byte of getBytes(other)) {
|
||||||
|
// this.#bits[i++] |= byte;
|
||||||
|
// }
|
||||||
|
|
||||||
|
for (let i = 0; i < this.#length; i++) {
|
||||||
|
this.#bits[i] |= other.#bits[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public xor(other: BitArray) {
|
||||||
|
this.#ensureSameSize(other);
|
||||||
|
|
||||||
|
// let i = 0;
|
||||||
|
// for (const byte of getBytes(other)) {
|
||||||
|
// this.#bits[i++] ^= byte;
|
||||||
|
// }
|
||||||
|
|
||||||
|
for (let i = 0; i < this.#length; i++) {
|
||||||
|
this.#bits[i] ^= other.#bits[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public not() {
|
||||||
|
for (let i = 0; i < this.#wholeBytes; i++) {
|
||||||
|
this.#bits[i] = ~this.#bits[i] & FULL_BYTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#remainingBits > 0) {
|
||||||
|
this.#bits[this.#wholeBytes] = ~this.#bits[this.#wholeBytes] & this.#remainingBitsMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public andNot(other: BitArray) {
|
||||||
|
this.#ensureSameSize(other);
|
||||||
|
|
||||||
|
// let i = 0;
|
||||||
|
// for (const byte of getBytes(other)) {
|
||||||
|
// this.#bits[i++] &= ~byte;
|
||||||
|
// }
|
||||||
|
|
||||||
|
for (let i = 0; i < this.#length; i++) {
|
||||||
|
this.#bits[i] &= ~other.#bits[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public contains(other: BitArray) {
|
||||||
|
this.#ensureSameSize(other);
|
||||||
|
|
||||||
|
// return enumerable.sequence(this).zip(enumerable.sequence(other)).where(([, b]) => b).all(([a, b]) => a && b);
|
||||||
|
return arrayLike(this.#bits).zip(arrayLike(other.#bits)).all(([a, b]) => (a & b) === b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public intersects(other: BitArray) {
|
||||||
|
this.#ensureSameSize(other);
|
||||||
|
|
||||||
|
// return enumerable.sequence(this).zip(enumerable.sequence(other)).any(([a, b]) => a && b);
|
||||||
|
return arrayLike(this.#bits).zip(arrayLike(other.#bits)).any(([a, b]) => (a & b) !== 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public copy() {
|
||||||
|
const copy = new BitArray(this.#length);
|
||||||
|
copy.#bits.set(this.#bits);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toArray() {
|
||||||
|
return sequence(this).toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public equals(other: BitArray) {
|
||||||
|
return other === this || isDefined(other) && arrayLike(this.#bits).equals(arrayLike(other.#bits));
|
||||||
|
}
|
||||||
|
|
||||||
|
public toString() {
|
||||||
|
return sequence(this).select(bit => bit ? '1' : '0').collect(join());
|
||||||
|
}
|
||||||
|
}
|
||||||
250
src/collector.ts
Normal file
250
src/collector.ts
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
import { Converter } from "./sync.js";
|
||||||
|
|
||||||
|
export interface Collector<TElement, TAccumulator, TResult> {
|
||||||
|
initialize(): TAccumulator;
|
||||||
|
accumulate(accumulator: TAccumulator, element: TElement): void;
|
||||||
|
finalize(accumulator: TAccumulator): TResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
// export interface Collector2<TElement, TResult> {
|
||||||
|
// accumulate(element: TElement): void;
|
||||||
|
// finalize(): TResult;
|
||||||
|
// }
|
||||||
|
|
||||||
|
class SimpleCollector<TElement, TAccumulator, TResult> implements Collector<TElement, TAccumulator, TResult> {
|
||||||
|
readonly #initialize: () => TAccumulator;
|
||||||
|
readonly #accumulate: (accumulator: TAccumulator, element: TElement) => void;
|
||||||
|
readonly #finalize: (accumulator: TAccumulator) => TResult;
|
||||||
|
|
||||||
|
constructor(initialize: () => TAccumulator, accumulate: (accumulator: TAccumulator, element: TElement) => void,
|
||||||
|
finalize: (accumulator: TAccumulator) => TResult) {
|
||||||
|
this.#initialize = initialize;
|
||||||
|
this.#accumulate = accumulate;
|
||||||
|
this.#finalize = finalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
return this.#initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
accumulate(accumulator: TAccumulator, element: TElement) {
|
||||||
|
this.#accumulate(accumulator, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize(accumulator: TAccumulator): TResult {
|
||||||
|
return this.#finalize(accumulator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function create<TElement, TAccumulator, TResult>(initialize: () => TAccumulator, accumulate: (accumulator: TAccumulator, element: TElement) => void,
|
||||||
|
finalize: (accumulator: TAccumulator) => TResult): Collector<TElement, TAccumulator, TResult> {
|
||||||
|
return new SimpleCollector(initialize, accumulate, finalize);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ToArrayCollector<TElement> implements Collector<TElement, TElement[], TElement[]> {
|
||||||
|
initialize(): TElement[] {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
accumulate(accumulator: TElement[], element: TElement) {
|
||||||
|
accumulator.push(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize(accumulator: TElement[]) {
|
||||||
|
return accumulator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const toArrayCollector = new ToArrayCollector<any>();
|
||||||
|
|
||||||
|
export function toArray<TElement>(): Collector<TElement, any, TElement[]> {
|
||||||
|
return toArrayCollector;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ToObjectCollector<TElement, TKey extends PropertyKey, TValue> implements Collector<TElement, Record<TKey, TValue>, Record<TKey, TValue>> {
|
||||||
|
readonly #keySelector: Converter<TElement, TKey>;
|
||||||
|
readonly #valueSelector: Converter<TElement, TValue>;
|
||||||
|
|
||||||
|
constructor(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>) {
|
||||||
|
this.#keySelector = keySelector;
|
||||||
|
this.#valueSelector = valueSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
return {} as Record<TKey, TValue>;
|
||||||
|
}
|
||||||
|
|
||||||
|
accumulate(accumulator: Record<TKey, TValue>, element: TElement) {
|
||||||
|
const key = this.#keySelector(element);
|
||||||
|
const value = this.#valueSelector(element);
|
||||||
|
|
||||||
|
accumulator[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize(accumulator: Record<TKey, TValue>) {
|
||||||
|
return accumulator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toObject<TElement, TKey extends PropertyKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): Collector<TElement, any, Record<TKey, TValue>> {
|
||||||
|
return new ToObjectCollector<TElement, TKey, TValue>(keySelector, valueSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ToMapCollector<TElement, TKey, TValue> implements Collector<TElement, Map<TKey, TValue>, Map<TKey, TValue>> {
|
||||||
|
readonly #keySelector: Converter<TElement, TKey>;
|
||||||
|
readonly #valueSelector: Converter<TElement, TValue>;
|
||||||
|
|
||||||
|
constructor(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>) {
|
||||||
|
this.#keySelector = keySelector;
|
||||||
|
this.#valueSelector = valueSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
return new Map<TKey, TValue>();
|
||||||
|
}
|
||||||
|
|
||||||
|
accumulate(accumulator: Map<TKey, TValue>, element: TElement) {
|
||||||
|
const key = this.#keySelector(element);
|
||||||
|
const value = this.#valueSelector(element);
|
||||||
|
|
||||||
|
accumulator.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize(accumulator: Map<TKey, TValue>) {
|
||||||
|
return accumulator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toMap<TElement, TKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): Collector<TElement, any, Map<TKey, TValue>> {
|
||||||
|
return new ToMapCollector<TElement, TKey, TValue>(keySelector, valueSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ToSetCollector<TElement> implements Collector<TElement, Set<TElement>, Set<TElement>> {
|
||||||
|
initialize() {
|
||||||
|
return new Set<TElement>();
|
||||||
|
}
|
||||||
|
|
||||||
|
accumulate(accumulator: Set<TElement>, element: TElement) {
|
||||||
|
accumulator.add(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize(accumulator: Set<TElement>) {
|
||||||
|
return accumulator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const toSetCollector = new ToSetCollector<any>();
|
||||||
|
|
||||||
|
export function toSet<TElement>(): Collector<TElement, any, Set<TElement>> {
|
||||||
|
return toSetCollector;
|
||||||
|
}
|
||||||
|
|
||||||
|
class JoinCollector implements Collector<any, any[], string> {
|
||||||
|
readonly #delimiter: string;
|
||||||
|
readonly #prefix: string;
|
||||||
|
readonly #suffix: string;
|
||||||
|
|
||||||
|
constructor(delimiter?: string, prefix?: string, suffix?: string) {
|
||||||
|
this.#delimiter = delimiter ?? "";
|
||||||
|
this.#prefix = prefix ?? "";
|
||||||
|
this.#suffix = suffix ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize(): any[] {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
accumulate(accumulator: any[], element: string) {
|
||||||
|
accumulator.push(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize(accumulator: any[]) {
|
||||||
|
return this.#prefix + accumulator.join(this.#delimiter) + this.#suffix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function join(delimiter?: string, prefix?: string, suffix?: string): Collector<any, any, string> {
|
||||||
|
return new JoinCollector(delimiter, prefix, suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SumCollector implements Collector<number, { sum: number }, number> {
|
||||||
|
initialize() {
|
||||||
|
return { sum: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
accumulate(accumulator: { sum: number }, element: number) {
|
||||||
|
accumulator.sum += element;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize(accumulator: { sum: number }) {
|
||||||
|
return accumulator.sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sumCollector = new SumCollector();
|
||||||
|
|
||||||
|
export function sum(): Collector<number, any, number> {
|
||||||
|
return sumCollector;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BigIntSumCollector implements Collector<bigint, { sum: bigint }, bigint> {
|
||||||
|
initialize() {
|
||||||
|
return { sum: 0n, };
|
||||||
|
}
|
||||||
|
|
||||||
|
accumulate(accumulator: { sum: bigint }, element: bigint) {
|
||||||
|
accumulator.sum += element;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize(accumulator: { sum: bigint }) {
|
||||||
|
return accumulator.sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bigintSumCollector = new BigIntSumCollector();
|
||||||
|
|
||||||
|
export function bigintSum(): Collector<bigint, any, bigint> {
|
||||||
|
return bigintSumCollector;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AverageCollector implements Collector<number, { count: number, sum: number }, number> {
|
||||||
|
initialize() {
|
||||||
|
return { count: 0, sum: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
accumulate(accumulator: { count: number, sum: number }, element: number) {
|
||||||
|
accumulator.count++;
|
||||||
|
accumulator.sum += element;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize(accumulator: { count: number, sum: number }) {
|
||||||
|
return accumulator.count === 0 ? 0 : accumulator.sum / accumulator.count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const averageCollector = new AverageCollector();
|
||||||
|
|
||||||
|
export function average(): Collector<number, any, number> {
|
||||||
|
return averageCollector;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BigIntAverageCollector implements Collector<bigint, { count: number, sum: bigint }, bigint> {
|
||||||
|
initialize() {
|
||||||
|
return { count: 0, sum: 0n };
|
||||||
|
}
|
||||||
|
|
||||||
|
accumulate(accumulator: { count: number, sum: bigint }, element: bigint) {
|
||||||
|
accumulator.count++;
|
||||||
|
accumulator.sum += element;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize(accumulator: { count: number, sum: bigint }) {
|
||||||
|
return accumulator.count === 0 ? 0n : accumulator.sum / BigInt(accumulator.count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bigintAverageCollector = new BigIntAverageCollector();
|
||||||
|
|
||||||
|
export function bigintAverage(): Collector<bigint, any, bigint> {
|
||||||
|
return bigintAverageCollector;
|
||||||
|
}
|
||||||
219
src/equality-map.ts
Normal file
219
src/equality-map.ts
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
import { Equater } from "./sync.js";
|
||||||
|
import { MaybeAsyncEquater } from "./async.js";
|
||||||
|
import { asAsyncGenerator } from "./utils.js";
|
||||||
|
|
||||||
|
export interface EqualityMap<K, V> extends Iterable<[K, V]> {
|
||||||
|
get(key: K): V | undefined;
|
||||||
|
set(key: K, value: V): V | undefined;
|
||||||
|
contains(key: K): boolean;
|
||||||
|
remove(key: K): V | undefined;
|
||||||
|
clear(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class NativeEqualityMap<K, V> implements EqualityMap<K, V> {
|
||||||
|
readonly #map = new Map<K, V>();
|
||||||
|
|
||||||
|
get(key: K) {
|
||||||
|
return this.#map.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key: K, value: V) {
|
||||||
|
const existing = this.get(key);
|
||||||
|
this.#map.set(key, value);
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
contains(key: K) {
|
||||||
|
return this.#map.has(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(key: K) {
|
||||||
|
const existing = this.get(key);
|
||||||
|
this.#map.delete(key);
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.#map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return this.#map[Symbol.iterator]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomEqualityMap<K, V> implements EqualityMap<K, V> {
|
||||||
|
readonly #keyComparer: Equater<K>;
|
||||||
|
readonly #list = new Array<[K, V]>();
|
||||||
|
|
||||||
|
constructor(keyComparer: Equater<K>) {
|
||||||
|
this.#keyComparer = keyComparer;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key: K) {
|
||||||
|
for (const entry of this.#list) {
|
||||||
|
if (this.#keyComparer(key, entry[0])) {
|
||||||
|
return entry[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key: K, value: V) {
|
||||||
|
for (const entry of this.#list) {
|
||||||
|
if (this.#keyComparer(key, entry[0])) {
|
||||||
|
const previous = entry[1];
|
||||||
|
entry[1] = value;
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#list.push([key, value]);
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
contains(key: K) {
|
||||||
|
for (const entry of this.#list) {
|
||||||
|
if (this.#keyComparer(key, entry[0])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(key: K) {
|
||||||
|
const length = this.#list.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
if (this.#keyComparer(key, this.#list[i][0])) {
|
||||||
|
const removed = this.#list.splice(i, 1);
|
||||||
|
return removed[0][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.#list.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return this.#list[Symbol.iterator]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createEqualityMap<K, V>(keyComparer?: Equater<K>): EqualityMap<K, V> {
|
||||||
|
return keyComparer ? new CustomEqualityMap<K, V>(keyComparer) : new NativeEqualityMap<K, V>();
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AsyncEqualityMap<K, V> extends AsyncIterable<[K, V]> {
|
||||||
|
get(key: K): Promise<V | undefined>;
|
||||||
|
set(key: K, value: V): Promise<V | undefined>;
|
||||||
|
contains(key: K): Promise<boolean>;
|
||||||
|
remove(key: K): Promise<V | undefined>;
|
||||||
|
clear(): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
class NativeAsyncEqualityMap<K, V> implements AsyncEqualityMap<K, V> {
|
||||||
|
readonly #map = new Map<K, V>();
|
||||||
|
|
||||||
|
async get(key: K) {
|
||||||
|
return this.#map.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
async set(key: K, value: V) {
|
||||||
|
const existing = await this.get(key);
|
||||||
|
this.#map.set(key, value);
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
async contains(key: K) {
|
||||||
|
return this.#map.has(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(key: K) {
|
||||||
|
const existing = await this.get(key);
|
||||||
|
this.#map.delete(key);
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear() {
|
||||||
|
this.#map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.asyncIterator]() {
|
||||||
|
return asAsyncGenerator(this.#map[Symbol.iterator]());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomAsyncEqualityMap<K, V> implements AsyncEqualityMap<K, V> {
|
||||||
|
readonly #keyComparer: MaybeAsyncEquater<K>;
|
||||||
|
readonly #list = new Array<[K, V]>();
|
||||||
|
|
||||||
|
constructor(keyComparer: MaybeAsyncEquater<K>) {
|
||||||
|
this.#keyComparer = keyComparer;
|
||||||
|
}
|
||||||
|
|
||||||
|
async get(key: K) {
|
||||||
|
for (const entry of this.#list) {
|
||||||
|
if (await this.#keyComparer(key, entry[0])) {
|
||||||
|
return entry[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async set(key: K, value: V) {
|
||||||
|
for (const entry of this.#list) {
|
||||||
|
if (await this.#keyComparer(key, entry[0])) {
|
||||||
|
const previous = entry[1];
|
||||||
|
entry[1] = value;
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#list.push([key, value]);
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async contains(key: K) {
|
||||||
|
for (const entry of this.#list) {
|
||||||
|
if (await this.#keyComparer(key, entry[0])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(key: K) {
|
||||||
|
const length = this.#list.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
if (await this.#keyComparer(key, this.#list[i][0])) {
|
||||||
|
const removed = this.#list.splice(i, 1);
|
||||||
|
return removed[0][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear() {
|
||||||
|
this.#list.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.asyncIterator]() {
|
||||||
|
return asAsyncGenerator(this.#list[Symbol.iterator]());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createAsyncEqualityMap<K, V>(keyComparer?: MaybeAsyncEquater<K>): AsyncEqualityMap<K, V> {
|
||||||
|
return keyComparer ? new CustomAsyncEqualityMap<K, V>(keyComparer) : new NativeAsyncEqualityMap<K, V>();
|
||||||
|
}
|
||||||
195
src/equality-set.ts
Normal file
195
src/equality-set.ts
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
import { Equater } from "./sync.js";
|
||||||
|
import { MaybeAsyncEquater } from "./async.js";
|
||||||
|
import { asAsyncGenerator } from "./utils.js";
|
||||||
|
|
||||||
|
export interface EqualitySet<T> extends Iterable<T> {
|
||||||
|
readonly size: number;
|
||||||
|
add(obj: T): boolean;
|
||||||
|
contains(obj: T): boolean;
|
||||||
|
remove(obj: T): boolean;
|
||||||
|
clear(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class NativeEqualitySet<T> implements EqualitySet<T> {
|
||||||
|
readonly #set = new Set<T>();
|
||||||
|
|
||||||
|
get size() {
|
||||||
|
return this.#set.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(obj: T) {
|
||||||
|
const exists = this.contains(obj);
|
||||||
|
this.#set.add(obj);
|
||||||
|
return !exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
contains(obj: T) {
|
||||||
|
return this.#set.has(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(obj: T) {
|
||||||
|
return this.#set.delete(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.#set.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return this.#set[Symbol.iterator]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomEqualitySet<T> implements EqualitySet<T> {
|
||||||
|
readonly #equater: Equater<T>;
|
||||||
|
readonly #list: T[] = [];
|
||||||
|
|
||||||
|
constructor(equater: Equater<T>) {
|
||||||
|
this.#equater = equater;
|
||||||
|
}
|
||||||
|
|
||||||
|
get size() {
|
||||||
|
return this.#list.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(obj: T) {
|
||||||
|
if (this.contains(obj)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#list.push(obj);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
contains(obj: T) {
|
||||||
|
for (const val of this.#list) {
|
||||||
|
if (this.#equater(obj, val)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(obj: T) {
|
||||||
|
const length = this.#list.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
if (this.#equater(obj, this.#list[i])) {
|
||||||
|
this.#list.splice(i, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.#list.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return this.#list[Symbol.iterator]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createEqualitySet<T>(equater?: Equater<T>): EqualitySet<T> {
|
||||||
|
return equater ? new CustomEqualitySet(equater) : new NativeEqualitySet<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AsyncEqualitySet<T> extends AsyncIterable<T> {
|
||||||
|
readonly size: number;
|
||||||
|
add(obj: T): Promise<boolean>;
|
||||||
|
contains(obj: T): Promise<boolean>;
|
||||||
|
remove(obj: T): Promise<boolean>;
|
||||||
|
clear(): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
class NativeAsyncEqualitySet<T> implements AsyncEqualitySet<T> {
|
||||||
|
readonly #set = new Set<T>();
|
||||||
|
|
||||||
|
get size() {
|
||||||
|
return this.#set.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
async add(obj: T) {
|
||||||
|
const exists = await this.contains(obj);
|
||||||
|
this.#set.add(obj);
|
||||||
|
return !exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
async contains(obj: T) {
|
||||||
|
return this.#set.has(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(obj: T) {
|
||||||
|
return this.#set.delete(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear() {
|
||||||
|
this.#set.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.asyncIterator]() {
|
||||||
|
return asAsyncGenerator(this.#set[Symbol.iterator]());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomAsyncEqualitySet<T> implements AsyncEqualitySet<T> {
|
||||||
|
readonly #equater: MaybeAsyncEquater<T>;
|
||||||
|
readonly #list: T[] = [];
|
||||||
|
|
||||||
|
constructor(equater: MaybeAsyncEquater<T>) {
|
||||||
|
this.#equater = equater;
|
||||||
|
}
|
||||||
|
|
||||||
|
get size() {
|
||||||
|
return this.#list.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
async add(obj: T) {
|
||||||
|
if (await this.contains(obj)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#list.push(obj);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async contains(obj: T) {
|
||||||
|
for (const val of this.#list) {
|
||||||
|
if (await this.#equater(obj, val)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(obj: T) {
|
||||||
|
const length = this.#list.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
if (await this.#equater(obj, this.#list[i])) {
|
||||||
|
this.#list.splice(i, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear() {
|
||||||
|
this.#list.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.asyncIterator]() {
|
||||||
|
return asAsyncGenerator(this.#list[Symbol.iterator]());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createAsyncEqualitySet<T>(equater?: MaybeAsyncEquater<T>): AsyncEqualitySet<T> {
|
||||||
|
return equater ? new CustomAsyncEqualitySet(equater) : new NativeAsyncEqualitySet<T>();
|
||||||
|
}
|
||||||
4
src/index.ts
Normal file
4
src/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export * from "./sync.js";
|
||||||
|
export * as async from "./async.js";
|
||||||
|
export * as collectors from "./collector.js";
|
||||||
|
export * as random from "./random.js";
|
||||||
204
src/queue.ts
Normal file
204
src/queue.ts
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
export interface Queue<T> extends Iterable<T> {
|
||||||
|
get capacity(): number;
|
||||||
|
get length(): number;
|
||||||
|
get free(): number;
|
||||||
|
|
||||||
|
isEmpty(): boolean;
|
||||||
|
isFull(): boolean;
|
||||||
|
|
||||||
|
enqueue(obj: T): void;
|
||||||
|
dequeue(): T | undefined;
|
||||||
|
|
||||||
|
clear(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class EmptyQueue<T> implements Queue<T> {
|
||||||
|
get capacity() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get length() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get free() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmpty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFull() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
enqueue() { }
|
||||||
|
|
||||||
|
dequeue(): T | undefined {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() { }
|
||||||
|
|
||||||
|
*[Symbol.iterator]() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
class BoundedArrayQueue<T> implements Queue<T> {
|
||||||
|
readonly #capacity: number;
|
||||||
|
readonly #buffer: T[];
|
||||||
|
#addIndex = 0;
|
||||||
|
#getIndex = 0;
|
||||||
|
|
||||||
|
constructor(capacity: number) {
|
||||||
|
this.#capacity = capacity;
|
||||||
|
this.#buffer = new Array<T>(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
get capacity() {
|
||||||
|
return this.#capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
get length() {
|
||||||
|
const n = this.#diff();
|
||||||
|
return n < 0 ? n + this.#capacity : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
get free() {
|
||||||
|
return this.#capacity - this.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmpty() {
|
||||||
|
return this.#getIndex === this.#addIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFull() {
|
||||||
|
return this.#diff() === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#diff() {
|
||||||
|
return this.#offset(this.#addIndex, -this.#getIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
#nextIndex(index: number) {
|
||||||
|
return this.#offset(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#offset(index: number, offset: number) {
|
||||||
|
return (index + offset) % this.#capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
#enqueue(obj: T) {
|
||||||
|
this.#buffer[this.#addIndex] = obj;
|
||||||
|
this.#addIndex = this.#nextIndex(this.#addIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
enqueue(obj: T) {
|
||||||
|
this.#enqueue(obj);
|
||||||
|
|
||||||
|
if (this.isEmpty()) {
|
||||||
|
this.#getIndex = this.#nextIndex(this.#getIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#dequeue() {
|
||||||
|
const obj = this.#buffer[this.#getIndex];
|
||||||
|
delete this.#buffer[this.#getIndex];
|
||||||
|
this.#getIndex = this.#nextIndex(this.#getIndex);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
dequeue() {
|
||||||
|
if (this.isEmpty()) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.#dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.#buffer.length = 0;
|
||||||
|
this.#addIndex = 0;
|
||||||
|
this.#getIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*[Symbol.iterator]() {
|
||||||
|
while (!this.isEmpty()) {
|
||||||
|
yield this.#dequeue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnboundedArrayQueue<T> implements Queue<T> {
|
||||||
|
readonly #buffer: T[] = [];
|
||||||
|
#addIndex = 0;
|
||||||
|
#getIndex = 0;
|
||||||
|
|
||||||
|
get capacity() {
|
||||||
|
return Infinity;
|
||||||
|
}
|
||||||
|
|
||||||
|
get length() {
|
||||||
|
return this.#addIndex - this.#getIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
get free() {
|
||||||
|
return Infinity;
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmpty() {
|
||||||
|
return this.#getIndex === this.#addIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFull() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
enqueue(obj: T) {
|
||||||
|
this.#buffer[this.#addIndex] = obj;
|
||||||
|
this.#addIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
dequeue() {
|
||||||
|
if (this.isEmpty()) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = this.#buffer[this.#getIndex];
|
||||||
|
delete this.#buffer[this.#getIndex];
|
||||||
|
this.#getIndex++;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.#buffer.length = 0;
|
||||||
|
this.#addIndex = 0;
|
||||||
|
this.#getIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*[Symbol.iterator]() {
|
||||||
|
for (let i = this.#getIndex; i < this.#addIndex; i++) {
|
||||||
|
yield this.#buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const emptyQueue = new EmptyQueue<any>();
|
||||||
|
|
||||||
|
export function createQueue<T>(capacity?: number): Queue<T> {
|
||||||
|
if (capacity === undefined) {
|
||||||
|
return new UnboundedArrayQueue<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capacity < 0) {
|
||||||
|
throw new Error("Capacity must be greater than or equal to zero.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capacity === 0) {
|
||||||
|
return emptyQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BoundedArrayQueue<T>(capacity);
|
||||||
|
}
|
||||||
137
src/random.ts
Normal file
137
src/random.ts
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
import { BitArray } from "./bitarray.js";
|
||||||
|
import { asArray } from "./utils.js";
|
||||||
|
|
||||||
|
export interface ElementPredicate<T = any> {
|
||||||
|
(index: number, obj: T): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ElementWeight<T = any> {
|
||||||
|
(index: number, obj: T): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RandomGenerator {
|
||||||
|
(): number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RandomOptions<T> {
|
||||||
|
predicate?: ElementPredicate<T>;
|
||||||
|
weight?: ElementWeight<T>;
|
||||||
|
random?: RandomGenerator;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const alwaysTrue: ElementPredicate = () => true;
|
||||||
|
export const weightOfOne: ElementWeight = () => 1.0;
|
||||||
|
export const mathRandom: RandomGenerator = () => Math.random();
|
||||||
|
|
||||||
|
const defaultOptions = Object.freeze<Required<RandomOptions<any>>>({
|
||||||
|
predicate: alwaysTrue,
|
||||||
|
weight: weightOfOne,
|
||||||
|
random: mathRandom
|
||||||
|
});
|
||||||
|
|
||||||
|
function mergeOptions<T>(first: RandomOptions<T> | undefined, second: RandomOptions<T> | undefined): RandomOptions<T> | undefined {
|
||||||
|
if (!first) {
|
||||||
|
return second;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!second) {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstPredicate = first.predicate;
|
||||||
|
const secondPredicate = second.predicate;
|
||||||
|
|
||||||
|
return {
|
||||||
|
predicate: firstPredicate ? secondPredicate ? (i, o) => firstPredicate(i, o) && secondPredicate(i, o) : firstPredicate : secondPredicate,
|
||||||
|
weight: first.weight ?? second.weight,
|
||||||
|
random: first.random ?? second.random
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function withDefaultOptions<T>(options: RandomOptions<T> | undefined): Required<RandomOptions<T>> {
|
||||||
|
if (!options) {
|
||||||
|
return { ...defaultOptions };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
predicate: options.predicate ?? defaultOptions.predicate,
|
||||||
|
weight: options.weight ?? defaultOptions.weight,
|
||||||
|
random: options.random ?? defaultOptions.random
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRandomElement<T>(sequence: Iterable<T>, options?: RandomOptions<T>) {
|
||||||
|
const { predicate, weight, random } = withDefaultOptions(options);
|
||||||
|
|
||||||
|
let result: T | undefined = undefined;
|
||||||
|
let resultIndex = -1;
|
||||||
|
let index = 0;
|
||||||
|
let weightAcc = 0.0;
|
||||||
|
|
||||||
|
for (const element of sequence) {
|
||||||
|
const currentIndex = index++;
|
||||||
|
|
||||||
|
if (predicate(currentIndex, element)) {
|
||||||
|
const w = weight(currentIndex, element);
|
||||||
|
|
||||||
|
if (w <= 0.0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
weightAcc += w;
|
||||||
|
|
||||||
|
if (random() * weightAcc < w) {
|
||||||
|
result = element;
|
||||||
|
resultIndex = currentIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { element: result, index: resultIndex };
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RandomPicker<T> {
|
||||||
|
readonly #elements: Iterable<T>;
|
||||||
|
readonly #flags: BitArray;
|
||||||
|
readonly #options: RandomOptions<T>;
|
||||||
|
|
||||||
|
public constructor(elements: T[], options?: RandomOptions<T>) {
|
||||||
|
this.#elements = elements;
|
||||||
|
this.#flags = new BitArray(elements.length);
|
||||||
|
this.#options = withDefaultOptions(mergeOptions({ predicate: (i, o) => this.#flags.get(i) }, options));
|
||||||
|
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static from<T>(iterable: Iterable<T>, options?: RandomOptions<T>) {
|
||||||
|
return new this<T>(asArray(iterable), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static of<T>(options?: RandomOptions<T>, ...values: T[]) {
|
||||||
|
return new this<T>(values, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get state() {
|
||||||
|
return this.#flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset() {
|
||||||
|
this.#flags.fill(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public next() {
|
||||||
|
const result = getRandomElement(this.#elements, this.#options);
|
||||||
|
|
||||||
|
if (result.index < 0) {
|
||||||
|
this.reset();
|
||||||
|
} else {
|
||||||
|
this.#flags.set(result.index, false);
|
||||||
|
|
||||||
|
if (this.#flags.isEmpty()) {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.element;
|
||||||
|
}
|
||||||
|
}
|
||||||
88
src/sorting.ts
Normal file
88
src/sorting.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import { MaybeAsyncComparer } from "./async.js";
|
||||||
|
import { reverseAsyncComparer } from "./utils.js";
|
||||||
|
|
||||||
|
export interface AsyncSorter {
|
||||||
|
sort<T>(array: T[], descending: boolean, comparer: MaybeAsyncComparer<T>): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function swap(array: any[], i: number, j: number) {
|
||||||
|
const tmp = array[i];
|
||||||
|
array[i] = array[j];
|
||||||
|
array[j] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class BaseAsyncSorter implements AsyncSorter {
|
||||||
|
async sort<T>(array: T[], descending: boolean, comparer: MaybeAsyncComparer<T>) {
|
||||||
|
await this._sort(array, descending ? reverseAsyncComparer(comparer) : comparer);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract _sort<T>(array: T[], comparer: MaybeAsyncComparer<T>): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
class InsertionSorter extends BaseAsyncSorter {
|
||||||
|
protected override async _sort<T>(array: T[], comparer: MaybeAsyncComparer<T>) {
|
||||||
|
for (let i = 1; i < array.length; i++) {
|
||||||
|
const obj = array[i];
|
||||||
|
|
||||||
|
for (let j = i - 1; j >= 0; j--) {
|
||||||
|
if (await comparer(obj, array[j]) < 0) {
|
||||||
|
for (let k = i; k > j; k--) {
|
||||||
|
swap(array, k - 1, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SelectionSorter extends BaseAsyncSorter {
|
||||||
|
protected async _sort<T>(array: T[], comparer: MaybeAsyncComparer<T>) {
|
||||||
|
for (let i = 0; i < array.length; i++) {
|
||||||
|
let smallest = array[i];
|
||||||
|
let smallestIndex = i;
|
||||||
|
|
||||||
|
for (let j = i; j < array.length; j++) {
|
||||||
|
const current = array[j];
|
||||||
|
|
||||||
|
if (await comparer(current, smallest) < 0) {
|
||||||
|
smallest = current;
|
||||||
|
smallestIndex = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smallestIndex !== i) {
|
||||||
|
swap(array, smallestIndex, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BubbleSorter extends BaseAsyncSorter {
|
||||||
|
protected async _sort<T>(array: T[], comparer: MaybeAsyncComparer<T>) {
|
||||||
|
const length = array.length;
|
||||||
|
|
||||||
|
for (let k = length - 2; k >= 0; k--) {
|
||||||
|
let hasSwaped = false;
|
||||||
|
|
||||||
|
for (let i = 0; i <= k; i++) {
|
||||||
|
const j = i + 1;
|
||||||
|
const a = array[i], b = array[j];
|
||||||
|
|
||||||
|
if (await comparer(a, b) > 0) {
|
||||||
|
swap(array, i, j);
|
||||||
|
hasSwaped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasSwaped) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const insertionSorter: AsyncSorter = new InsertionSorter();
|
||||||
|
export const selectionSorter: AsyncSorter = new SelectionSorter();
|
||||||
|
export const bubbleSorter: AsyncSorter = new BubbleSorter();
|
||||||
2689
src/sync.ts
Normal file
2689
src/sync.ts
Normal file
File diff suppressed because it is too large
Load Diff
121
src/utils.ts
Normal file
121
src/utils.ts
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import { AsyncComparer, MaybeAsyncComparer, MaybeAsyncIterator } from "./async.js";
|
||||||
|
import { Comparer } from "./sync.js";
|
||||||
|
|
||||||
|
export type AsyncFunction<TFunc extends (...args: any) => any> = TFunc extends (...args: infer P) => infer R ? (...args: P) => Promise<Awaited<R>> : never;
|
||||||
|
export type MaybePromise<T> = T | Promise<T>;
|
||||||
|
export type MaybeAsyncFunction<TFunc extends (...args: any) => any> = TFunc extends (...args: infer P) => infer R ? (...args: P) => MaybePromise<R> : never;
|
||||||
|
|
||||||
|
export function isDefined<T = any>(obj: T): obj is NonNullable<T> {
|
||||||
|
return obj !== undefined && obj !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isIterable<T = any>(obj: any): obj is Iterable<T> {
|
||||||
|
return isDefined(obj) && typeof obj[Symbol.iterator] === "function";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAsyncIterable<T = any>(obj: any): obj is AsyncIterable<T> {
|
||||||
|
return isDefined(obj) && typeof obj[Symbol.asyncIterator] === "function";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function identity<T>(obj: T) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function looseEquals<T>(a: T, b: T) {
|
||||||
|
return a == b;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function strictEquals<T>(a: T, b: T) {
|
||||||
|
return a === b;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isEquals<T>(a: T, b: T) {
|
||||||
|
return Object.is(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function numberCompare<T extends number | bigint>(a: T, b: T) {
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function operatorCompare(a: any, b: any) {
|
||||||
|
return a < b ? -1 : a > b ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function defaultArrayComparer<T>(a: T, b: T) {
|
||||||
|
if (a === undefined) {
|
||||||
|
if (b === undefined) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b === undefined) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const aStr = a === null ? "null" : a.toString();
|
||||||
|
const bStr = b === null ? "null" : b.toString();
|
||||||
|
|
||||||
|
return aStr > bStr ? 1 : aStr < bStr ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function combineComparers<T>(first: Comparer<T>, second: Comparer<T>): Comparer<T> {
|
||||||
|
return (a, b) => first(a, b) || second(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function combineAsyncComparers<T>(first: MaybeAsyncComparer<T>, second: MaybeAsyncComparer<T>): AsyncComparer<T> {
|
||||||
|
return async (a, b) => await first(a, b) || await second(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reverseComparer<T>(comparer: Comparer<T>): Comparer<T> {
|
||||||
|
return (a: T, b: T) => comparer(b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reverseAsyncComparer<T>(comparer: MaybeAsyncComparer<T>): AsyncComparer<T> {
|
||||||
|
return async (a: T, b: T) => await comparer(b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* asGenerator<T>(iterator: Iterator<T>) {
|
||||||
|
while (true) {
|
||||||
|
const next = iterator.next();
|
||||||
|
|
||||||
|
if (next.done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield next.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function* asAsyncGenerator<T>(iterator: MaybeAsyncIterator<T>) {
|
||||||
|
while (true) {
|
||||||
|
const next = await iterator.next();
|
||||||
|
|
||||||
|
if (next.done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield next.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function asArray<T>(iterable: Iterable<T>) {
|
||||||
|
return Array.isArray(iterable) ? <T[]>iterable : Array.from(iterable);
|
||||||
|
}
|
||||||
|
|
||||||
|
class WrappedIterator<T> implements Iterable<T> {
|
||||||
|
readonly #iterator: Iterator<T>;
|
||||||
|
|
||||||
|
constructor(iterator: Iterator<T>) {
|
||||||
|
this.#iterator = iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return this.#iterator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wrapAsIterable<T>(iterator: Iterator<T>): Iterable<T> {
|
||||||
|
return new WrappedIterator(iterator);
|
||||||
|
}
|
||||||
4
ts-loader.js
Normal file
4
ts-loader.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { register } from "node:module";
|
||||||
|
import { pathToFileURL } from "node:url";
|
||||||
|
|
||||||
|
register("ts-node/esm", pathToFileURL("./"));
|
||||||
24
tsconfig.json
Normal file
24
tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "Node16",
|
||||||
|
"moduleResolution": "Node16",
|
||||||
|
"alwaysStrict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noErrorTruncation": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"allowUnreachableCode": true,
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
|
"ts-node": {
|
||||||
|
"esm": true,
|
||||||
|
"experimentalSpecifierResolution": "node"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user