UNPKG

astl

Version:

AssemblyScript-STL (Standard Template Library, migrated from the C++)

218 lines (179 loc) 5.81 kB
import { Vector } from "../../container/Vector"; import { CMath } from "../numeric/CMath"; import { Hasher } from "../functional/Hasher"; import { BinaryPredicator } from "../functional/BinaryPredicator"; export class HashBuckets<Key, Elem> { private readonly hasher_: Hasher<Key>; private readonly predicator_: BinaryPredicator<Key>; private readonly fetcher_: (elem: Elem) => Key; private max_load_factor_: f64; private data_: Vector<Vector<Elem>>; private size_: usize; /* --------------------------------------------------------- CONSTRUCTORS --------------------------------------------------------- */ public constructor(hasher: Hasher<Key>, pred: BinaryPredicator<Key>, fetcher: (elem: Elem) => Key) { this.hasher_ = hasher; this.predicator_ = pred; this.fetcher_ = fetcher; this.max_load_factor_ = DEFAULT_MAX_FACTOR; this.data_ = new Vector(); this.initialize(); } public clear(): void { this.data_.clear(); this.initialize(); } public rehash(length: usize): void { length = CMath.max(length, MIN_BUCKET_COUNT); const log: f64 = Math.log2(<f64>length); if (log !== Math.floor(log)) length = <usize>(Math.pow(2, Math.ceil(log))); // CREATE NEW BUCKET const data: Vector<Vector<Elem>> = new Vector(); data.reserve(length); for (let i: usize = 0; i < length; ++i) data.push_back(new Vector()); // MIGRATE ELEMENTS TO THE NEW BUCKET for (let i: usize = 0; i < this.data_.size(); ++i) { const row: Vector<Elem> = this.data_.at(i); for (let j: usize = 0; j < row.size(); ++j) { const element: Elem = row.at(j); const index: usize = this.hasher_(this.fetcher_(element)) % length; data.at(index).push_back(element); } } // DO CHANGE THE BUCKET this.data_ = data; } public reserve(length: usize): void { if (length > this.capacity()) { length = <usize>(length / this.max_load_factor_); this.rehash(length); } } private initialize(): void { this.size_ = 0; for (let i: usize = 0; i < MIN_BUCKET_COUNT; ++i) this.data_.push_back(new Vector()); } /* --------------------------------------------------------- ACCESSORS --------------------------------------------------------- */ @inline public size(): usize { return this.size_; } @inline public row_size(): usize { return this.data_.size(); } @inline public capacity(): usize { return <usize>(this.row_size() * this.max_load_factor_); } @inline public at(index: usize): Vector<Elem> { return this.data_.at(index); } @inline public load_factor(): f64 { return this.size_ / this.row_size(); } @inline public max_load_factor(): f64 { return this.max_load_factor_; } @inline public set_max_load_factor(z: f64): void { this.max_load_factor_ = z; } @inline public hash_function(): Hasher<Key> { return this.hasher_; } @inline public key_eq(): BinaryPredicator<Key> { return this.predicator_; } /* --------------------------------------------------------- FINDERS --------------------------------------------------------- */ @inline() private index_by_key(key: Key): usize { return this.hash_function()(key) & (this.row_size() - 1); } @inline private index_by_value(elem: Elem): usize { return this.index_by_key(this.fetcher_(elem)); } public find(key: Key): Elem | null { const index: usize = this.index_by_key(key); const bucket: Vector<Elem> = this.at(index); for (let i: usize = 0; i < bucket.size(); ++i) { const it: Elem = bucket.at(i); if (this.predicator_(key, this.fetcher_(it)) === true) return it; } return null; } public count(key: Key): usize { const index: usize = this.index_by_key(key); const bucket: Vector<Elem> = this.at(index); let ret: usize = 0; for (let i: usize = 0; i < bucket.size(); ++i) if (this.key_eq()(key, this.fetcher_(bucket.at(i))) === true) ++ret; return ret; } /* --------------------------------------------------------- ELEMENTS I/O --------------------------------------------------------- */ public insert(val: Elem): void { const capacity: usize = this.capacity(); if (++this.size_ > capacity) this.rehash(capacity * 2); const index: usize = this.index_by_value(val); const bucket: Vector<Elem> = this.data_.at(index); bucket.push_back(val); } public erase(val: Elem): boolean { const index: usize = this.index_by_value(val); const bucket: Vector<Elem> = this.data_.at(index); for (let i: usize = 0; i < bucket.size(); ++i) if (bucket.at(i) === val) { bucket.erase(bucket.nth(i)); --this.size_; return true; } return false; } } const MIN_BUCKET_COUNT: usize = 8; const DEFAULT_MAX_FACTOR: f64 = 1.0;