jsketcher/web/app/utils/hashmap.js
2022-08-15 23:47:20 -07:00

165 lines
3.8 KiB
JavaScript

function HashTable(hashCodeF, equalsF) {
this.hashCodeF = hashCodeF;
this.equalsF = equalsF;
this.setTableSize(8);
this.size = 0;
}
HashTable.prototype.hash = function(key) {
return Math.abs(this.hashCodeF(key) % this.table.length);
};
HashTable.prototype.get = function(key) {
const entry = this._findEntry(key, this._findBucket(key));
if (entry == null) return null;
return entry[1];
};
HashTable.prototype.put = function(key, value) {
if (this.size >= 0.75 * this.table.length) {
this.rebuild();
}
this._put(key, value);
};
HashTable.prototype._findBucket = function(key) {
const hash = this.hash(key);
let bucket = this.table[hash];
if (bucket === null) {
bucket = [];
this.table[hash] = bucket;
}
return bucket;
};
HashTable.prototype._findEntry = function(key, bucket) {
for (let i = 0; i < bucket.length; i++) {
if (this.equalsF(bucket[i][0], key)) {
return bucket[i];
}
}
return null;
};
HashTable.prototype._put = function(key, value) {
const bucket = this._findBucket(key);
const entry = this._findEntry(key, bucket);
if (entry == null) {
bucket.push([key, value]);
} else {
entry[1] = value;
}
this.size++;
};
HashTable.prototype.rebuild = function() {
this.size = 0;
const oldTable = this.table;
this.setTableSize(this.table.length * 2);
for (let i = 0; i < oldTable.length; i++) {
const e = oldTable[i];
if (e != null) {
for (let j = 0; j < e.length; j++) {
const bucket = e[j];
this._put(bucket[0], bucket[1]);
}
}
}
};
HashTable.prototype.getKeys = function() {
const keys = [];
this.entries(function(k) {
keys.push(k)
});
return keys;
};
HashTable.prototype.entries = function(callback) {
for (let i = 0; i < this.table.length; i++) {
const e = this.table[i];
if (e != null) {
for (let j = 0; j < e.length; j++) {
const bucket = e[j];
callback(bucket[0], bucket[1]);
}
}
}
};
HashTable.prototype.setTableSize = function(newSize) {
this.table = [];
for (let i = 0; i < newSize; i++) {
this.table[i] = null;
}
};
function DoubleHelper() {
this.dv = new DataView(new ArrayBuffer(8));
}
DoubleHelper.prototype.hash = function(v) {
this.dv.setFloat64(0, v);
return this.dv.getInt32(0) ^ this.dv.getInt32(4);
};
HashTable.forVector3d = function() {
const doubleHelper = new DoubleHelper();
function hash(v) {
return doubleHelper.hash(v.x) ^ doubleHelper.hash(v.y) ^ doubleHelper.hash(v.z);
}
function eq(a, b) {
return a.x === b.x && a.y === b.y && a.z === b.z;
}
return new HashTable(hash, eq);
};
HashTable.forEdge = function() {
const doubleHelper = new DoubleHelper();
function hash(v) {
return doubleHelper.hash(v[0].x) ^ doubleHelper.hash(v[0].y) ^ doubleHelper.hash(v[0].z)
^doubleHelper.hash(v[1].x) ^ doubleHelper.hash(v[1].y) ^ doubleHelper.hash(v[1].z);
}
function veq(a, b) {
return a.x === b.x && a.y === b.y && a.z === b.z;
}
function eq(e1, e2) {
const a1 = e1[0];
const b1 = e1[1];
const a2 = e2[0];
const b2 = e2[1];
return (veq(a1, a2) && veq(b1, b2)) || (veq(a1, b2) && veq(b1, a2));
}
return new HashTable(hash, eq);
};
HashTable.forVector2d = function() {
const doubleHelper = new DoubleHelper();
function hash(v) {
return doubleHelper.hash(v.x) ^ doubleHelper.hash(v.y) ;
}
function eq(a, b) {
return a.x === b.x && a.y === b.y;
}
return new HashTable(hash, eq);
};
HashTable.forDoubleArray = function() {
const doubleHelper = new DoubleHelper();
function hash(v) {
let hash = 0;
for (let i = 0; i < v.length; i++) {
hash ^= v[i];
}
return hash;
}
function eq(a, b) {
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
return new HashTable(hash, eq);
};
export {HashTable, DoubleHelper}