import { assert } from 'chai'; import { HashSet } from '../../../lib/containers/HashSet'; type Vec = number[]; function sumVec(vec: Vec) { let ret = 0; const length = vec.length; for (let i = 0; i < length; i++) ret += vec[i]; return ret; } function eqVec(lhs: Vec, rhs: Vec) { const length = lhs.length; if (length !== rhs.length) return false; for (let i = 0; i < length; i++) if (lhs[i] !== rhs[i]) return false; return true; } function compVec(lhs: Vec, rhs: Vec) { const lhsLength = lhs.length; const rhsLength = rhs.length; const minLength = Math.min(lhsLength, rhsLength); for (let i = 0; i < minLength; i++) { const result = lhs[i] - rhs[i]; if (result !== 0) return result; } return lhsLength - rhsLength; } function sortVec(vecArr: Vec[]) { return vecArr.slice().sort(compVec); } describe('containers.HashSet', () => { describe('constructor', () => { it('requires hash and equal functions', () => { const set = new HashSet(sumVec, eqVec); assert.strictEqual(set['_hash'], sumVec); assert.strictEqual(set['_equal'], eqVec); assert.strictEqual(set.size, 0); }); it('should accept optional entries argument', () => { const set = new HashSet(sumVec, eqVec, [[1, 2], [2, 3], [3, 4]]); assert.strictEqual(set['_hash'], sumVec); assert.strictEqual(set['_equal'], eqVec); assert.strictEqual(set.size, 3); assert.deepEqual(sortVec([...set.values()]), [[1, 2], [2, 3], [3, 4]]); }); it('should accept optional iterable entries argument', () => { const set = new HashSet(sumVec, eqVec, (function* () { yield [1, 2]; yield [2, 3]; yield [3, 4]; }())); assert.strictEqual(set['_hash'], sumVec); assert.strictEqual(set['_equal'], eqVec); assert.strictEqual(set.size, 3); assert.deepEqual(sortVec([...set.values()]), [[1, 2], [2, 3], [3, 4]]); }); it('should accept optional array-like entries argument', () => { const set = new HashSet(sumVec, eqVec, { 0: [1, 2], 1: [2, 3], 2: [3, 4], length: 3 }); assert.strictEqual(set['_hash'], sumVec); assert.strictEqual(set['_equal'], eqVec); assert.strictEqual(set.size, 3); assert.deepEqual(sortVec([...set.values()]), [[1, 2], [2, 3], [3, 4]]); }); }); describe('size', () => { it('should provide size property', () => { const set = new HashSet(sumVec, eqVec); assert.strictEqual(set.size, 0); set.add([1, 2]); assert.strictEqual(set.size, 1); set.add([2, 3]); assert.strictEqual(set.size, 2); }); }); describe('add', () => { it('should add different values', () => { const set = new HashSet(sumVec, eqVec); set.add([1, 2]); set.add([2, 3]); set.add([3, 4]); assert.deepEqual(sortVec([...set.values()]), [[1, 2], [2, 3], [3, 4]]); }); it('should not add same values', () => { const set = new HashSet(sumVec, eqVec); set.add([1, 2]); set.add([2, 3]); set.add([1, 2]); set.add([3, 4]); assert.deepEqual(sortVec([...set.values()]), [[1, 2], [2, 3], [3, 4]]); }); it('should add values with hash collisions', () => { const set = new HashSet(sumVec, eqVec); set.add([1, 2]); set.add([0, 3]); set.add([3, 4]); set.add([-1, 4]); set.add([2, 2, 2, 1]); assert.deepEqual(sortVec([...set.values()]), [[-1, 4], [0, 3], [1, 2], [2, 2, 2, 1], [3, 4]]); }); }); describe('has', () => { it('should test items by value', () => { const set = new HashSet(sumVec, eqVec); assert.isFalse(set.has([1, 2])); set.add([1, 2]); assert.isTrue(set.has([1, 2])); assert.isFalse(set.has([2, 3])); assert.isFalse(set.has([0, 3])); assert.isFalse(set.has([2, 1])); set.add([2, 1]); assert.isTrue(set.has([2, 1])); assert.isFalse(set.has([0, 3])); set.add([2, 3]); assert.isTrue(set.has([2, 3])); }); }); describe('delete', () => { it('should delete items by value', () => { const set = new HashSet(sumVec, eqVec); set.add([1, 2]); set.add([2, 3]); set.add([3, 4]); assert.isTrue(set.delete([2, 3])); assert.strictEqual(set.size, 2); assert.deepEqual(sortVec([...set.values()]), [[1, 2], [3, 4]]); assert.isFalse(set.delete([4, 5])); assert.isFalse(set.delete([0, 3])); assert.strictEqual(set.size, 2); assert.deepEqual(sortVec([...set.values()]), [[1, 2], [3, 4]]); set.add([2, 3]); assert.strictEqual(set.size, 3); assert.deepEqual(sortVec([...set.values()]), [[1, 2], [2, 3], [3, 4]]); assert.isTrue(set.delete([2, 3])); assert.strictEqual(set.size, 2); assert.deepEqual(sortVec([...set.values()]), [[1, 2], [3, 4]]); }); it('should delete items with hash collisions', () => { const set = new HashSet(sumVec, eqVec); set.add([1, 2]); set.add([2, 3]); set.add([3, 4]); set.add([0, 3]); set.add([1, 1, 1]); assert.isTrue(set.delete([0, 3])); assert.strictEqual(set.size, 4); assert.deepEqual(sortVec([...set.values()]), [[1, 1, 1], [1, 2], [2, 3], [3, 4]]); assert.isFalse(set.delete([0, 3])); assert.strictEqual(set.size, 4); assert.deepEqual(sortVec([...set.values()]), [[1, 1, 1], [1, 2], [2, 3], [3, 4]]); assert.isTrue(set.delete([1, 1, 1])); assert.strictEqual(set.size, 3); assert.deepEqual(sortVec([...set.values()]), [[1, 2], [2, 3], [3, 4]]); }); it('should delete items with hash collisions', () => { const set = new HashSet(() => 0, (lhs, rhs) => lhs === rhs); set.add('foo'); set.add('bar'); set.add('buzz'); assert.deepEqual([...set].sort(), ['foo', 'bar', 'buzz'].sort()); set.delete('foo'); assert.deepEqual([...set].sort(), ['bar', 'buzz'].sort()); set.delete('buzz'); assert.deepEqual([...set], ['bar']); set.delete('bar'); assert.strictEqual(set.size, 0); }); }); describe('clear', () => { it('should clear empty set', () => { const set = new HashSet(sumVec, eqVec); set.clear(); assert.strictEqual(set.size, 0); assert.deepEqual([...set.values()], []); set.add([1, 2]); set.add([2, 3]); set.add([3, 4]); assert.strictEqual(set.size, 3); assert.deepEqual(sortVec([...set.values()]), [[1, 2], [2, 3], [3, 4]]); }); it('should clear non empty set', () => { const set = new HashSet(sumVec, eqVec); set.add([1, 2]); set.add([2, 3]); set.add([3, 4]); assert.strictEqual(set.size, 3); assert.deepEqual(sortVec([...set.values()]), [[1, 2], [2, 3], [3, 4]]); set.clear(); assert.strictEqual(set.size, 0); assert.deepEqual([...set.values()], []); set.add([3, 4]); set.add([4, 5]); assert.strictEqual(set.size, 2); assert.deepEqual(sortVec([...set.values()]), [[3, 4], [4, 5]]); }); }); describe('values', () => { it(`should support 'values' iteration`, () => { const set = new HashSet(sumVec, eqVec); set.add([1, 2]); set.add([2, 3]); set.add([3, 4]); assert.deepEqual(sortVec([...set.values()]), [[1, 2], [2, 3], [3, 4]]); }); }); describe('entries', () => { it(`should support 'entries' iteration`, () => { const set = new HashSet(sumVec, eqVec); set.add([1, 2]); set.add([2, 3]); assert.deepEqual([...set.entries()], [[[1, 2], [1, 2]], [[2, 3], [2, 3]]]); }); }); describe('Symbol.iterator', () => { it(`should support 'Symbol.iterator' iteration`, () => { const set = new HashSet(sumVec, eqVec); set.add([1, 2]); set.add([2, 3]); set.add([3, 4]); assert.deepEqual(sortVec([...set]), [[1, 2], [2, 3], [3, 4]]); }); }); describe('forEach', () => { it(`should support 'forEach' iteration`, () => { const scope = {}; const entries = [[1, 2], [2, 3], [3, 4]]; const set = new HashSet(sumVec, eqVec, entries); const result: number[][] = []; set.forEach(function (this: any, value, key, subject) { assert.strictEqual(set, subject); assert.strictEqual(this, scope); assert.deepEqual(value, key); result.push(value); }, scope); assert.deepEqual(result, entries); }); }); });