190 lines
4.7 KiB
TypeScript
190 lines
4.7 KiB
TypeScript
import { IBitmap } from "./types";
|
|
|
|
const computeBitsArrayLength = (x: number): number => Math.ceil(x / 8);
|
|
const getBitPositionIndex = (x: number): number => Math.floor(x / 8);
|
|
const getBitPosition = (x: number): number => x % 8;
|
|
const getMask = (bitIdx: number): number => 0b00000001 << bitIdx;
|
|
|
|
export class Bitmap implements IBitmap {
|
|
private bits: Uint8Array;
|
|
|
|
constructor(x = 32) {
|
|
this.bits = new Uint8Array(computeBitsArrayLength(x));
|
|
}
|
|
|
|
public grow(x: number): void {
|
|
const arrLength = Math.max(1, computeBitsArrayLength(x));
|
|
if (arrLength <= this.bits.length) return;
|
|
const prev = this.bits;
|
|
this.bits = new Uint8Array(arrLength);
|
|
this.bits.set(prev);
|
|
}
|
|
|
|
public set(x: number): void {
|
|
const idx = getBitPositionIndex(x);
|
|
if (idx >= this.bits.length) this.grow(x);
|
|
this.bits[idx] |= getMask(getBitPosition(x));
|
|
}
|
|
|
|
public remove(x: number): void {
|
|
const idx = getBitPositionIndex(x);
|
|
if (!this.bits[idx]) return;
|
|
const mask = getMask(getBitPosition(x));
|
|
this.bits[idx] &= ~mask;
|
|
}
|
|
|
|
public contains(x: number): boolean {
|
|
const [idx, pos] = [getBitPositionIndex(x), getBitPosition(x)];
|
|
if (!this.bits[idx]) return false;
|
|
const mask = getMask(pos);
|
|
return (this.bits[idx] & mask) === mask;
|
|
}
|
|
|
|
public count(): number {
|
|
let count = 0;
|
|
for (const byte of this.bits) {
|
|
let tmp = byte;
|
|
while (tmp) {
|
|
tmp &= tmp - 1;
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public and(bitmap: IBitmap): IBitmap {
|
|
const otherBits = (bitmap as Bitmap).bits;
|
|
const result = this.clone() as Bitmap;
|
|
const minlen = Math.min(this.bits.length, otherBits.length);
|
|
|
|
for (let i = 0; i < minlen; i++) {
|
|
result.bits[i] &= otherBits[i];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public andNot(bitmap: IBitmap): IBitmap {
|
|
const otherBits = (bitmap as Bitmap).bits;
|
|
const result = this.clone() as Bitmap;
|
|
const minlen = Math.min(this.bits.length, otherBits.length);
|
|
|
|
for (let i = 0; i < minlen; i++) {
|
|
result.bits[i] &= ~otherBits[i];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public or(bitmap: IBitmap): IBitmap {
|
|
const otherBits = (bitmap as Bitmap).bits;
|
|
const result = this.clone() as Bitmap;
|
|
const minlen = Math.min(this.bits.length, otherBits.length);
|
|
|
|
for (let i = 0; i < minlen; i++) {
|
|
result.bits[i] |= otherBits[i];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public xor(bitmap: IBitmap): IBitmap {
|
|
const otherBits = (bitmap as Bitmap).bits;
|
|
const result = this.clone() as Bitmap;
|
|
const minlen = Math.min(this.bits.length, otherBits.length);
|
|
|
|
for (let i = 0; i < minlen; i++) {
|
|
result.bits[i] ^= otherBits[i];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public range(fn: (x: number) => boolean | void): void {
|
|
let needContinueIterating = true;
|
|
for (let i = 0; i < this.bits.length; i++) {
|
|
let x = this.bits[i];
|
|
for (let j = 0; j < 8; j++) {
|
|
const bit = (x >> j) & 1;
|
|
if (bit === 0) continue;
|
|
needContinueIterating = fn(i * 8 + j) ?? true;
|
|
if (!needContinueIterating) break;
|
|
}
|
|
if (!needContinueIterating) break;
|
|
}
|
|
}
|
|
|
|
public filter(fn: (x: number) => boolean): void {
|
|
for (let i = 0; i < this.bits.length; i++) {
|
|
for (let j = 0; j < 8; j++) {
|
|
const bitIndex = i * 8 + j;
|
|
if (this.contains(bitIndex) && !fn(bitIndex)) {
|
|
this.remove(bitIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public clear(): void {
|
|
for (let i = 0; i < this.bits.length; i++) {
|
|
this.bits[i] = 0;
|
|
}
|
|
}
|
|
|
|
public clone(): IBitmap {
|
|
const clonedBitmap = new Bitmap(this.bits.length * 8);
|
|
clonedBitmap.bits.set(this.bits);
|
|
return clonedBitmap;
|
|
}
|
|
|
|
public min(): number {
|
|
for (let i = 0; i < this.bits.length; i++) {
|
|
const byte = this.bits[i];
|
|
if (byte === 0) continue;
|
|
for (let j = 0; j < 8; j++) {
|
|
if ((byte & (1 << j)) !== 0) {
|
|
return i * 8 + j;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public max(): number {
|
|
let x = -1;
|
|
for (let i = this.bits.length - 1; i >= 0; i--) {
|
|
const byte = this.bits[i];
|
|
if (byte === 0) continue;
|
|
x = i * 8 + Math.floor(Math.log2(byte));
|
|
break;
|
|
}
|
|
return x;
|
|
}
|
|
|
|
public minZero(): number {
|
|
for (let i = 0; i < this.bits.length; i++) {
|
|
const byte = this.bits[i];
|
|
if (byte === 0xff) continue;
|
|
for (let j = 0; j < 8; j++) {
|
|
if ((byte & (1 << j)) === 0) {
|
|
return i * 8 + j;
|
|
}
|
|
}
|
|
}
|
|
return this.bits.length * 8;
|
|
}
|
|
|
|
public maxZero(): number {
|
|
for (let i = this.bits.length - 1; i >= 0; i--) {
|
|
const byte = this.bits[i];
|
|
if (byte === 0xff) continue;
|
|
for (let j = 7; j >= 0; j--) {
|
|
if ((byte & (1 << j)) === 0) {
|
|
return i * 8 + j;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
}
|