refactor BitArray to be interface with hidden implementations
This commit is contained in:
264
src/bitarray.ts
264
src/bitarray.ts
@@ -1,7 +1,34 @@
|
||||
import { join } from "./collector.js";
|
||||
import { arrayLike, sequence } from "./sync.js";
|
||||
import { Enumerable, arrayLike, sequence } from "./sync.js";
|
||||
import { asArray, isDefined } from "./utils.js";
|
||||
|
||||
export interface BitArray extends Iterable<boolean> {
|
||||
readonly length: number;
|
||||
|
||||
isFull(): boolean;
|
||||
isEmpty(): boolean;
|
||||
|
||||
get(index: number): boolean;
|
||||
set(index: number, value: boolean): void;
|
||||
fill(value: boolean): void;
|
||||
|
||||
and(other: BitArray): BitArray;
|
||||
or(other: BitArray): BitArray;
|
||||
xor(other: BitArray): BitArray;
|
||||
not(): BitArray;
|
||||
|
||||
contains(other: BitArray): boolean;
|
||||
intersects(other: BitArray): boolean;
|
||||
|
||||
slice(offset: number, length: number): BitArray;
|
||||
copy(): BitArray;
|
||||
toArray(): boolean[];
|
||||
|
||||
equals(other: BitArray): boolean;
|
||||
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
const BYTE_SIZE = Uint8Array.BYTES_PER_ELEMENT * 8;
|
||||
const FULL_BYTE = getMask(BYTE_SIZE);
|
||||
|
||||
@@ -54,7 +81,29 @@ function* getBytes(bits: Iterable<boolean>) {
|
||||
}
|
||||
}
|
||||
|
||||
export class BitArray implements Iterable<boolean> {
|
||||
export namespace BitArray {
|
||||
export function create(length: number): BitArray {
|
||||
return new BitArrayImpl(length);
|
||||
}
|
||||
|
||||
export function from(bits: Iterable<boolean>): BitArray {
|
||||
const arr = asArray(bits);
|
||||
const result = create(arr.length);
|
||||
|
||||
let i = 0;
|
||||
for (const bit of arr) {
|
||||
result.set(i++, bit);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function of(...bits: boolean[]): BitArray {
|
||||
return from(bits);
|
||||
}
|
||||
}
|
||||
|
||||
class BitArrayImpl implements BitArray {
|
||||
readonly #length: number;
|
||||
readonly #bits: Uint8Array;
|
||||
readonly #wholeBytes: number;
|
||||
@@ -72,22 +121,6 @@ export class BitArray implements Iterable<boolean> {
|
||||
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);
|
||||
@@ -149,9 +182,16 @@ export class BitArray implements Iterable<boolean> {
|
||||
public and(other: BitArray) {
|
||||
this.#ensureSameSize(other);
|
||||
|
||||
if (other instanceof BitArrayImpl) {
|
||||
for (let i = 0; i < this.#length; i++) {
|
||||
this.#bits[i] &= other.#bits[i];
|
||||
}
|
||||
} else {
|
||||
let i = 0;
|
||||
for (const byte of getBytes(other)) {
|
||||
this.#bits[i++] &= byte;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -159,9 +199,16 @@ export class BitArray implements Iterable<boolean> {
|
||||
public or(other: BitArray) {
|
||||
this.#ensureSameSize(other);
|
||||
|
||||
if (other instanceof BitArrayImpl) {
|
||||
for (let i = 0; i < this.#length; i++) {
|
||||
this.#bits[i] |= other.#bits[i];
|
||||
}
|
||||
} else {
|
||||
let i = 0;
|
||||
for (const byte of getBytes(other)) {
|
||||
this.#bits[i++] |= byte;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -169,9 +216,16 @@ export class BitArray implements Iterable<boolean> {
|
||||
public xor(other: BitArray) {
|
||||
this.#ensureSameSize(other);
|
||||
|
||||
if (other instanceof BitArrayImpl) {
|
||||
for (let i = 0; i < this.#length; i++) {
|
||||
this.#bits[i] ^= other.#bits[i];
|
||||
}
|
||||
} else {
|
||||
let i = 0;
|
||||
for (const byte of getBytes(other)) {
|
||||
this.#bits[i++] ^= byte;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -188,30 +242,28 @@ export class BitArray implements Iterable<boolean> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public andNot(other: BitArray) {
|
||||
this.#ensureSameSize(other);
|
||||
|
||||
for (let i = 0; i < this.#length; i++) {
|
||||
this.#bits[i] &= ~other.#bits[i];
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public contains(other: BitArray) {
|
||||
this.#ensureSameSize(other);
|
||||
|
||||
return arrayLike(this.#bits).zip(arrayLike(other.#bits)).all(([a, b]) => (a & b) === b);
|
||||
return other instanceof BitArrayImpl ?
|
||||
arrayLike(this.#bits).zip(arrayLike(other.#bits)).all(([a, b]) => (a & b) === b) :
|
||||
sequence(this).zip(sequence(other)).where(([, b]) => b).all(([a, b]) => a && b);
|
||||
}
|
||||
|
||||
public intersects(other: BitArray) {
|
||||
this.#ensureSameSize(other);
|
||||
|
||||
return arrayLike(this.#bits).zip(arrayLike(other.#bits)).any(([a, b]) => (a & b) !== 0);
|
||||
return other instanceof BitArrayImpl ?
|
||||
arrayLike(this.#bits).zip(arrayLike(other.#bits)).any(([a, b]) => (a & b) !== 0) :
|
||||
sequence(this).zip(sequence(other)).any(([a, b]) => a && b);
|
||||
}
|
||||
|
||||
public slice(offset: number, length: number) {
|
||||
return new BitArraySlice(this, offset, length);
|
||||
}
|
||||
|
||||
public copy() {
|
||||
const copy = new BitArray(this.#length);
|
||||
const copy = new BitArrayImpl(this.#length);
|
||||
copy.#bits.set(this.#bits);
|
||||
return copy;
|
||||
}
|
||||
@@ -221,7 +273,155 @@ export class BitArray implements Iterable<boolean> {
|
||||
}
|
||||
|
||||
public equals(other: BitArray) {
|
||||
return other === this || isDefined(other) && arrayLike(this.#bits).equals(arrayLike(other.#bits));
|
||||
return other === this || isDefined(other) && (other instanceof BitArrayImpl ? arrayLike(this.#bits).equals(arrayLike(other.#bits)) : sequence(this).equals(sequence(other)));
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return sequence(this).select(bit => bit ? '1' : '0').collect(join());
|
||||
}
|
||||
}
|
||||
|
||||
class BitArraySlice implements BitArray {
|
||||
readonly #parent: BitArray;
|
||||
readonly #offset: number;
|
||||
readonly #length: number;
|
||||
|
||||
public constructor(parent: BitArray, offset: number, length: number) {
|
||||
this.#parent = parent;
|
||||
this.#offset = offset;
|
||||
this.#length = length;
|
||||
}
|
||||
|
||||
*[Symbol.iterator]() {
|
||||
for (let i = 0; i < this.#length; i++) {
|
||||
yield this.#parent.get(i + this.#offset);
|
||||
}
|
||||
}
|
||||
|
||||
#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 isFull() {
|
||||
for (const bit of this) {
|
||||
if (!bit) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public isEmpty() {
|
||||
for (const bit of this) {
|
||||
if (bit) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public get(index: number) {
|
||||
this.#ensureValidIndex(index);
|
||||
|
||||
return this.#parent.get(index + this.#offset);
|
||||
}
|
||||
|
||||
public set(index: number, value: boolean) {
|
||||
this.#ensureValidIndex(index);
|
||||
|
||||
this.#parent.set(index + this.#offset, value);
|
||||
}
|
||||
|
||||
public fill(value: boolean) {
|
||||
for (let i = 0; i < this.#length; i++) {
|
||||
this.#parent.set(i + this.#offset, value);
|
||||
}
|
||||
}
|
||||
|
||||
public and(other: BitArray) {
|
||||
this.#ensureSameSize(other);
|
||||
|
||||
for (let i = 0; i < this.#length; i++) {
|
||||
this.set(i, this.get(i) && other.get(i));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public or(other: BitArray) {
|
||||
this.#ensureSameSize(other);
|
||||
|
||||
for (let i = 0; i < this.#length; i++) {
|
||||
this.set(i, this.get(i) || other.get(i));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public xor(other: BitArray) {
|
||||
this.#ensureSameSize(other);
|
||||
|
||||
for (let i = 0; i < this.#length; i++) {
|
||||
this.set(i, this.get(i) !== other.get(i));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public not() {
|
||||
for (let i = 0; i < this.#length; i++) {
|
||||
this.set(i, !this.get(i));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public contains(other: BitArray) {
|
||||
this.#ensureSameSize(other);
|
||||
|
||||
return sequence(this).zip(sequence(other)).where(([, b]) => b).all(([a, b]) => a && b);
|
||||
}
|
||||
|
||||
public intersects(other: BitArray) {
|
||||
this.#ensureSameSize(other);
|
||||
|
||||
return sequence(this).zip(sequence(other)).any(([a, b]) => a && b);
|
||||
}
|
||||
|
||||
public slice(offset: number, length: number) {
|
||||
return new BitArraySlice(this.#parent, offset + this.#offset, length);
|
||||
}
|
||||
|
||||
public copy() {
|
||||
const copy = new BitArrayImpl(this.#length);
|
||||
|
||||
for (let i = 0; i < this.#length; i++) {
|
||||
copy.set(i, this.get(i));
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
public toArray() {
|
||||
return sequence(this).toArray();
|
||||
}
|
||||
|
||||
public equals(other: BitArray) {
|
||||
return other === this || isDefined(other) && sequence(this).equals(sequence(other));
|
||||
}
|
||||
|
||||
public toString() {
|
||||
|
||||
@@ -82,6 +82,10 @@ export function getRandomElement<T>(sequence: Iterable<T>, options?: RandomOptio
|
||||
return { element: result, index: resultIndex };
|
||||
}
|
||||
|
||||
export function getRandomElement<T>(sequence: Iterable<T>, options?: RandomOptions<T>) {
|
||||
return _getRandomElement(sequence, withDefaultOptions(options));
|
||||
}
|
||||
|
||||
export class RandomPicker<T> {
|
||||
readonly #elements: Iterable<T>;
|
||||
readonly #flags: BitArray;
|
||||
@@ -89,7 +93,7 @@ export class RandomPicker<T> {
|
||||
|
||||
public constructor(elements: T[], options?: RandomOptions<T>) {
|
||||
this.#elements = elements;
|
||||
this.#flags = new BitArray(elements.length);
|
||||
this.#flags = BitArray.create(elements.length);
|
||||
this.#options = withDefaultOptions(mergeOptions({ predicate: i => this.#flags.get(i) }, options));
|
||||
|
||||
this.reset();
|
||||
|
||||
Reference in New Issue
Block a user