├── documentation ├── hamt-logo.ai └── hamt-logo.png ├── .travis.yml ├── .editorconfig ├── .gitignore ├── gulpfile.js ├── tests ├── fold.js ├── forEach.js ├── keys.js ├── has.js ├── values.js ├── tryGet.js ├── modify.js ├── entries.js ├── isEmpty.js ├── count.js ├── set.js ├── mutation.js └── remove.js ├── CHANGELOG.md ├── package.json ├── LICENSE ├── console.html ├── README.md ├── lib └── hamt.js ├── hamt.js └── hamt.js.map /documentation/hamt-logo.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattbierner/hamt_plus/HEAD/documentation/hamt-logo.ai -------------------------------------------------------------------------------- /documentation/hamt-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattbierner/hamt_plus/HEAD/documentation/hamt-logo.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | before_script: 5 | - npm install -g gulp 6 | script: gulp -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.js] 4 | indent_style = space 5 | indent_size = 4 6 | 7 | [*.json] 8 | indent_style = space 9 | indent_size = 4 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project Files # 2 | ################# 3 | .project 4 | node_modules 5 | 6 | # OS Files # 7 | ############ 8 | .DS_Store* 9 | ehthumbs.db 10 | Icon? 11 | Thumbs.db 12 | 13 | # Temp Files # 14 | ############## 15 | *~ 16 | *.tmp* 17 | *.orig 18 | *.cache 19 | *.log 20 | 21 | 22 | # Jekyll # 23 | ########## 24 | _site/ -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var babel = require("gulp-babel"); 2 | var gulp = require("gulp"); 3 | var sourcemaps = require("gulp-sourcemaps"); 4 | var plumber = require('gulp-plumber'); 5 | 6 | gulp.task("default", () => 7 | gulp.src("lib/*.js") 8 | .pipe(plumber()) 9 | .pipe(sourcemaps.init()) 10 | .pipe(babel({ 11 | presets: ['es2015'] 12 | })) 13 | .pipe(sourcemaps.write(".")) 14 | .pipe(gulp.dest("./"))); 15 | 16 | -------------------------------------------------------------------------------- /tests/fold.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const hamt = require('../hamt'); 3 | const assert = require('chai').assert; 4 | 5 | describe('fold', () => { 6 | it('should handle large folds correctly', () => { 7 | let h = hamt.make(); 8 | 9 | let sum = 0; 10 | for (let i = 0; i < 20000; ++i) { 11 | h = h.set(i + '', i); 12 | sum += i; 13 | } 14 | 15 | assert.strictEqual(sum, h.fold((p, c) => p + c, 0)); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ChangeLog # 2 | 3 | ## 1.0.2 - March 27, 2017 4 | * Fixed mutation splice out. Thanks @wshager! 5 | 6 | ## 1.0.1 - January 27, 2016 7 | * Fixed bug when array nodes are packed during removal. 8 | 9 | ## 1.0.0 - January 26, 2016 10 | * Refork from Hamt 2.1 11 | * Duplicates existing Hamt 2.x API. 12 | * Added constant time size queries. 13 | 14 | ## 0.0.6 - September 27, 2014 15 | * Fixed collision nodes on unexpanded branch not being expanded on insertions 16 | further down branch. Thanks raymond-w-ko for reporting this and providing test 17 | data for `hamt`. 18 | 19 | ## 0.0.5 - Aug 22, 2014 20 | * Fixed `CollisionNode` updates not using node values. 21 | 22 | ## 0.0.4 - Aug 21, 2014 23 | * Fixed `CollisionNode` not having an `edit` member. 24 | 25 | ## 0.0.3 - Aug 19, 2014 26 | * Revert `fold` to fix performance degradation. 27 | 28 | ## 0.0.0 - Aug 18, 2014 29 | * Initial release. 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hamt_plus", 3 | "version": "1.0.2", 4 | "description": "Fork of HAMT that adds transient mutation and support for custom key types", 5 | "keywords": [ 6 | "trie", 7 | "hamt", 8 | "map", 9 | "persistent", 10 | "data structure", 11 | "hash" 12 | ], 13 | "author": "Matt Bierner", 14 | "license": "MIT", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/mattbierner/hamt_plus.git" 18 | }, 19 | "main": "hamt.js", 20 | "files": [ 21 | "hamt.js", 22 | "hamt.js.map" 23 | ], 24 | "dependencies": {}, 25 | "devDependencies": { 26 | "babel-preset-es2015": "^6.24.0", 27 | "gulp": "^3.9.0", 28 | "gulp-babel": "^6.1.2", 29 | "gulp-plumber": "^1.1.0", 30 | "gulp-sourcemaps": "^2.4.0", 31 | "chai": "*", 32 | "mocha": "*" 33 | }, 34 | "scripts": { 35 | "compile": "gulp", 36 | "test": "mocha tests" 37 | } 38 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (C) 2016 Matt Bierner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /tests/forEach.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const hamt = require('../hamt'); 3 | const assert = require('chai').assert; 4 | 5 | describe('forEach', () => { 6 | it('should noop for empty map', () => { 7 | hamt.forEach(() => { 8 | assert.ok(false); 9 | }, hamt.make()); 10 | }); 11 | 12 | 13 | it('should be invoked for every element in map', () => { 14 | let h = hamt.make().set('a', 3).set('b', 5); 15 | 16 | let foundSum = 0; 17 | h.forEach(x => { 18 | foundSum += x; 19 | }); 20 | 21 | assert.strictEqual(8, foundSum); 22 | }); 23 | 24 | it('should handle large map correctly', () => { 25 | let h = hamt.make(); 26 | 27 | let sum = 0; 28 | for (let i = 0; i < 20000; ++i) { 29 | h = h.set(i + '', i); 30 | sum += i; 31 | } 32 | 33 | let foundSum = 0; 34 | h.forEach(x => { 35 | foundSum += x; 36 | }); 37 | 38 | assert.strictEqual(sum, foundSum); 39 | }); 40 | }); 41 | 42 | -------------------------------------------------------------------------------- /console.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /tests/keys.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const hamt = require('../hamt'); 3 | const assert = require('chai').assert; 4 | 5 | describe('keys', () => { 6 | it('should return empty for empty map', () => { 7 | assert.deepEqual([], Array.from(hamt.keys(hamt.make()))); 8 | }); 9 | 10 | it('should return single key for single element map', () => { 11 | assert.deepEqual(['a'], Array.from(hamt.keys(hamt.make().set('a', 5)))); 12 | assert.deepEqual(['b'], Array.from(hamt.make().set('b', 5).keys())); 13 | }); 14 | 15 | it('should return all keys for collision', () => { 16 | const h1 = hamt.make() 17 | .setHash(0, 'a', 3) 18 | .setHash(0, 'b', 5); 19 | 20 | assert.sameMembers(['a', 'b'], Array.from(h1.keys())); 21 | }); 22 | 23 | it('return correct keys while items are added', () => { 24 | const insert = [ 25 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g", 26 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r", 27 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s", 28 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P", 29 | "u", "I", "O", "`", "X"]; 30 | 31 | let h = hamt.make(); 32 | insert.forEach(x => { 33 | h = h.set(x, x); 34 | }); 35 | 36 | assert.sameMembers(insert, Array.from(hamt.keys(h))); 37 | }); 38 | }); 39 | 40 | -------------------------------------------------------------------------------- /tests/has.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const hamt = require('../hamt'); 3 | const assert = require('chai').assert; 4 | 5 | describe('has', () => { 6 | it('should return false for empty map', () => { 7 | assert.strictEqual(false, hamt.has('a', hamt.make())); 8 | assert.strictEqual(false, hamt.has('b', hamt.make())); 9 | }); 10 | 11 | it('should return if entry exists for single map', () => { 12 | var h1 = hamt.make().set('a', 3); 13 | 14 | assert.strictEqual(true, h1.has('a')); 15 | assert.strictEqual(false, h1.has('b')); 16 | }); 17 | 18 | it('should not depend on stored value', () => { 19 | assert.strictEqual(true, hamt.has('a', hamt.make().set('a', 3))); 20 | assert.strictEqual(true, hamt.has('a', hamt.make().set('a', false))); 21 | assert.strictEqual(true, hamt.has('a', hamt.make().set('a', null))); 22 | assert.strictEqual(true, hamt.has('a', hamt.make().set('a', undefined))); 23 | }); 24 | 25 | it('should return if entry exists in map', () => { 26 | var h = hamt.make(); 27 | for (let i = 'A'.charCodeAt(0); i < 'z'.charCodeAt(0); i += 2) { 28 | h = h.set(String.fromCharCode(i), i); 29 | } 30 | 31 | for (let i = 'A'.charCodeAt(0); i < 'z'.charCodeAt(0);) { 32 | assert.strictEqual(true, hamt.has(String.fromCharCode(i++), h)); 33 | assert.strictEqual(false, hamt.has(String.fromCharCode(i++), h)); 34 | } 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /tests/values.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const hamt = require('../hamt'); 3 | const assert = require('chai').assert; 4 | 5 | describe('values', () => { 6 | it('should return empty for empty map', () => { 7 | assert.deepEqual([], Array.from(hamt.values(hamt.make()))); 8 | }); 9 | 10 | it('should return single key for single element map', () => { 11 | assert.deepEqual([3], Array.from(hamt.values(hamt.make().set('a', 3)))); 12 | assert.deepEqual([5], Array.from(hamt.make().set('b', 5).values())); 13 | }); 14 | 15 | it('should return all values for collision', () => { 16 | const h1 = hamt.make() 17 | .setHash(0, 'a', 3) 18 | .setHash(0, 'b', 5); 19 | 20 | assert.sameMembers([5, 3], Array.from(h1.values())); 21 | }); 22 | 23 | it('should return duplicate values', () => { 24 | const h = hamt.make().set('b', 3).set('a', 3); 25 | assert.deepEqual([3, 3], Array.from(hamt.values(h))); 26 | }); 27 | 28 | it('return correct values while items are added', () => { 29 | const insert = [ 30 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g", 31 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r", 32 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s", 33 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P", 34 | "u", "I", "O", "`", "X"]; 35 | 36 | let h = hamt.make(); 37 | insert.forEach(x => { 38 | h = h.set(x, x); 39 | }); 40 | 41 | assert.sameMembers(insert, Array.from(hamt.values(h))); 42 | }); 43 | }); 44 | 45 | -------------------------------------------------------------------------------- /tests/tryGet.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const hamt = require('../hamt'); 3 | const assert = require('chai').assert; 4 | 5 | describe('tryGet', () => { 6 | it('should return default for empty map', () => { 7 | assert.strictEqual(10, hamt.tryGet(10, 'a', hamt.make())); 8 | assert.strictEqual(10, hamt.make().tryGet(10, 'a')); 9 | 10 | assert.strictEqual(10, hamt.tryGet(10, 'b', hamt.make())); 11 | assert.strictEqual(false, hamt.tryGet(false, 'a', hamt.make())); 12 | 13 | const a = {}; 14 | assert.strictEqual(a, hamt.tryGet(a, 'b', hamt.make())); 15 | 16 | }); 17 | 18 | it('should return default for non-existant value', () => { 19 | var h1 = hamt.make().set('a', 3); 20 | 21 | assert.strictEqual(3, hamt.tryGet(10, 'a', h1)); 22 | assert.strictEqual(10, hamt.tryGet(10, 'b', h1)); 23 | }); 24 | 25 | it('should uesd custom keyEq', () => { 26 | var h1 = hamt.make({ 27 | keyEq: (x, y) => x.value === y.value 28 | }) 29 | .set({value: 'a'}, 3) 30 | .set({value: 'b'}, 5); 31 | 32 | assert.strictEqual(3, hamt.get({value: 'a'}, h1)); 33 | assert.strictEqual(undefined, hamt.get('a', h1)); 34 | assert.strictEqual(5, hamt.get({value: 'b'}, h1)); 35 | assert.strictEqual(undefined, hamt.get('b', h1)); 36 | }); 37 | 38 | it('should work on array nodes correctly', () => { 39 | const insert = [ 40 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g", 41 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r", 42 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s", 43 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P", 44 | "u", "I", "O", "`", "X"]; 45 | 46 | let h = hamt.make(); 47 | for (let i = 0; i < insert.length; ++i) { 48 | h = h.setHash(i, insert[i], insert[i]); 49 | } 50 | 51 | assert.strictEqual(insert.length, h.count()); 52 | 53 | for (let i = 0; i < insert.length; ++i) { 54 | const x = insert[i]; 55 | assert.strictEqual(x, h.tryGetHash(null, i, x)); 56 | assert.strictEqual(null, h.tryGetHash(null, i, x + x)); 57 | } 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /tests/modify.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const hamt = require('../hamt'); 3 | const assert = require('chai').assert; 4 | 5 | describe('modify', () => { 6 | it('should update entry in single map', () => { 7 | const h = hamt.set('a', 3, hamt.make()); 8 | const h1 = hamt.modify(x => x * 2, 'a', h); 9 | 10 | assert.strictEqual(6, hamt.get('a', h1)); 11 | assert.strictEqual(3, hamt.get('a', h)); 12 | }); 13 | 14 | it('should work on with method calls', () => { 15 | const h = hamt.make().set('a', 3); 16 | const h1 = h.modify('a', x => x * 2); 17 | 18 | assert.strictEqual(6, h1.get('a')); 19 | assert.strictEqual(3, h.get('a')); 20 | }); 21 | 22 | 23 | it('should insert into empty map', () => { 24 | const h = hamt.make().modify('a', _ => 10); 25 | assert.strictEqual(10, hamt.get('a', h)); 26 | }); 27 | 28 | 29 | it('should call `f` with zero args on insert', () => { 30 | const h = hamt.modify(function(x) { 31 | assert.strictEqual(0, arguments.length); 32 | assert.strictEqual(undefined, x); 33 | }, 'a', hamt.make()); 34 | }); 35 | 36 | it('should insert if no value in map matches', () => { 37 | const h = hamt.make().set('a', 3).set('b', 5); 38 | const h1 = hamt.modify(_ => 10, 'c', h); 39 | 40 | assert.strictEqual(3, hamt.get('a', h1)); 41 | assert.strictEqual(5, hamt.get('b', h1)); 42 | assert.strictEqual(10, hamt.get('c', h1)); 43 | 44 | assert.strictEqual(3, hamt.get('a', h)); 45 | assert.strictEqual(5, hamt.get('b', h)); 46 | assert.strictEqual(undefined, hamt.get('c', h)); 47 | }); 48 | 49 | it('should modify collision values correctly', () => { 50 | const h1 = hamt.make() 51 | .modifyHash(0, 'a', () => 3) 52 | .modifyHash(0, 'b', () => 5); 53 | 54 | const h3 = h1.modifyHash(0, 'a', x => x * 2); 55 | assert.strictEqual(6, h3.getHash(0, 'a')); 56 | assert.strictEqual(5, h3.getHash(0, 'b')); 57 | 58 | const h4 = h3.modifyHash(0, 'b', x => x * 2); 59 | assert.strictEqual(6, h4.getHash(0, 'a')); 60 | assert.strictEqual(10, h4.getHash(0, 'b')); 61 | 62 | // Non existant 63 | const h5 = h4.modifyHash(0, 'c', _ => 100); 64 | assert.strictEqual(6, h5.getHash(0, 'a')); 65 | assert.strictEqual(10, h5.getHash(0, 'b')); 66 | assert.strictEqual(100, h5.getHash(0, 'c')); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /tests/entries.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const hamt = require('../hamt'); 3 | const assert = require('chai').assert; 4 | 5 | describe('entries', () => { 6 | it('should be empty for empty map', () => { 7 | const h = hamt.make(); 8 | const it = hamt.entries(h); 9 | 10 | let v = it.next(); 11 | assert.ok(v.done); 12 | 13 | for (let x of h) 14 | assert.isFalse(true); 15 | }); 16 | 17 | it('should visit single child', () => { 18 | const h = hamt.make().set('a', 3); 19 | const it = hamt.entries(h); 20 | let v = it.next(); 21 | 22 | assert.deepEqual(['a', 3], v.value); 23 | assert.notOk(v.done); 24 | v = it.next(); 25 | assert.ok(v.done); 26 | 27 | assert.deepEqual([['a', 3]], Array.from(h)); 28 | }); 29 | 30 | it('should visit all children', () => { 31 | const h = hamt.make() 32 | .set('a', 3) 33 | .set('b', 5) 34 | .set('c', 7); 35 | 36 | const expected = [['a', 3], ['b', 5], ['c', 7]]; 37 | 38 | { 39 | const it = h.entries(); 40 | const results = []; 41 | let v; 42 | for (var i = 0; i < h.count(); ++i) { 43 | v = it.next(); 44 | assert.notOk(v.done); 45 | results.push(v.value) 46 | } 47 | v = it.next(); 48 | assert.isTrue(v.done); 49 | assert.deepEqual(expected, results); 50 | } 51 | assert.deepEqual(expected, Array.from(h)); 52 | }); 53 | 54 | it('should handle collisions', () => { 55 | const h = hamt.make() 56 | .setHash(0, 'a', 3) 57 | .setHash(0, 'b', 5) 58 | .setHash(0, 'c', 7) 59 | 60 | const expected = [['b', 5], ['a', 3], ['c', 7]]; 61 | 62 | { 63 | const it = h.entries(); 64 | const results = []; 65 | let v; 66 | for (var i = 0; i < h.count(); ++i) { 67 | v = it.next(); 68 | assert.notOk(v.done); 69 | results.push(v.value) 70 | } 71 | v = it.next(); 72 | assert.isTrue(v.done); 73 | assert.deepEqual(expected, results); 74 | } 75 | assert.deepEqual(expected, Array.from(h)); 76 | }); 77 | 78 | it('should handle large map correctly', () => { 79 | let h = hamt.make(); 80 | 81 | let sum = 0; 82 | for (let i = 0; i < 20000; ++i) { 83 | h = h.set(i + '', i); 84 | sum += i; 85 | } 86 | 87 | let foundSum = 0; 88 | for (let x of h) 89 | foundSum += x[1]; 90 | assert.strictEqual(sum, foundSum); 91 | }); 92 | }); 93 | 94 | -------------------------------------------------------------------------------- /tests/isEmpty.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const hamt = require('../hamt'); 3 | const assert = require('chai').assert; 4 | 5 | describe('isEmpty', () => { 6 | it('should return true for empty', () => { 7 | assert.isTrue(hamt.isEmpty(hamt.make())); 8 | assert.isTrue(hamt.make().isEmpty()); 9 | }); 10 | 11 | it('should false for single element map', () => { 12 | assert.isFalse(hamt.isEmpty(hamt.make().set('a', 4))); 13 | assert.isFalse(hamt.make().set('a', 4).isEmpty()); 14 | }); 15 | 16 | it('should return false for collision', () => { 17 | const h1 = hamt.make() 18 | .setHash(0, 'a', 3) 19 | .setHash(0, 'b', 5); 20 | 21 | assert.isFalse(hamt.isEmpty(h1)); 22 | assert.isFalse(h1.isEmpty()); 23 | }); 24 | 25 | it('should handle remove correctly', () => { 26 | var h1 = hamt.make() 27 | .set('a', 3) 28 | .set('b', 5); 29 | 30 | var h2 = h1.delete('a'); 31 | var h3 = h2.delete('b'); 32 | 33 | assert.isFalse(hamt.isEmpty(h1)); 34 | assert.isFalse(hamt.isEmpty(h2)); 35 | assert.isTrue(hamt.isEmpty(h3)); 36 | }); 37 | 38 | it('should handle remove collision correctly', () => { 39 | const h1 = hamt.make() 40 | .setHash(0, 'a', 3) 41 | .setHash(0, 'b', 5); 42 | 43 | var h2 = h1.deleteHash(0, 'a'); 44 | var h3 = h2.deleteHash(0, 'b'); 45 | 46 | assert.isFalse(hamt.isEmpty(h1)); 47 | assert.isFalse(hamt.isEmpty(h2)); 48 | assert.isTrue(hamt.isEmpty(h3)); 49 | }); 50 | 51 | it('return correct counts while items are added and removed', () => { 52 | const insert = [ 53 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g", 54 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r", 55 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s", 56 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P", 57 | "u", "I", "O", "`", "X"]; 58 | 59 | const remove = [ 60 | "w", "m", "Q", "R", "i", "K", "P", "Y", "D", "g", "y", "L", 61 | "b", "[", "a", "t", "j", "W", "J", "G", "q", "r", "p", "U", 62 | "v", "h", "S", "_", "d", "x", "I", "F", "f", "n", "B", "\\", 63 | "k", "V", "N", "l", "X", "A", "]", "s", "Z", "O", "^", "o", 64 | "`", "H", "E", "e", "M", "u", "T", "c", "C"]; 65 | 66 | let h = hamt.make(); 67 | 68 | for (let i = 0; i < insert.length; ++i) { 69 | const x = insert[i]; 70 | h = h.set(x, x); 71 | assert.isFalse(hamt.isEmpty(h)); 72 | } 73 | 74 | for (let i = 0; i < remove.length; ++i) { 75 | assert.isFalse(hamt.isEmpty(h)); 76 | h = h.remove(remove[i]); 77 | } 78 | 79 | assert.isTrue(hamt.isEmpty(h)); 80 | }); 81 | }); 82 | 83 | -------------------------------------------------------------------------------- /tests/count.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const hamt = require('../hamt'); 3 | const assert = require('chai').assert; 4 | 5 | describe('count', () => { 6 | it('should return zero for empty map', () => { 7 | assert.strictEqual(0, hamt.count(hamt.make())); 8 | assert.strictEqual(0, hamt.make().count()); 9 | assert.strictEqual(0, hamt.make().size); 10 | }); 11 | 12 | it('should return 1 for single element map', () => { 13 | assert.strictEqual(1, hamt.count(hamt.make().set('a', 5))); 14 | assert.strictEqual(1, hamt.make().set('b', 5).count()); 15 | assert.strictEqual(1, hamt.make().set('b', 5).size); 16 | }); 17 | 18 | it('should handle counts on collisions correctly', () => { 19 | const h1 = hamt.make() 20 | .setHash(0, 'a', 3) 21 | .setHash(0, 'b', 5); 22 | 23 | assert.strictEqual(2, hamt.count(h1)); 24 | }); 25 | 26 | it('return correct counts while items are added and removed', () => { 27 | const insert = [ 28 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g", 29 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r", 30 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s", 31 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P", 32 | "u", "I", "O", "`", "X"]; 33 | 34 | const remove = [ 35 | "w", "m", "Q", "R", "i", "K", "P", "Y", "D", "g", "y", "L", 36 | "b", "[", "a", "t", "j", "W", "J", "G", "q", "r", "p", "U", 37 | "v", "h", "S", "_", "d", "x", "I", "F", "f", "n", "B", "\\", 38 | "k", "V", "N", "l", "X", "A", "]", "s", "Z", "O", "^", "o", 39 | "`", "H", "E", "e", "M", "u", "T", "c", "C"]; 40 | 41 | let h = hamt.make(); 42 | 43 | for (let i = 0; i < insert.length; ++i) { 44 | const x = insert[i]; 45 | h = h.set(x, x); 46 | assert.strictEqual(i + 1, hamt.count(h)); 47 | } 48 | 49 | for (let i = 0; i < remove.length; ++i) { 50 | h = h.remove(remove[i]); 51 | assert.strictEqual(remove.length - i - 1, hamt.count(h)); 52 | } 53 | }); 54 | 55 | it('should work on array nodes correctly', () => { 56 | const insert = [ 57 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g", 58 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r", 59 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s", 60 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P", 61 | "u", "I", "O", "`", "X"]; 62 | 63 | let h = hamt.make(); 64 | for (let i = 0; i < insert.length; ++i) { 65 | h = h.setHash(i, insert[i], insert[i]); 66 | } 67 | 68 | assert.strictEqual(insert.length, h.count()); 69 | 70 | for (let i = 0; i < insert.length; ++i) { 71 | h = h.removeHash(i, insert[i]); 72 | assert.strictEqual(insert.length - i - 1, h.count()); 73 | } 74 | }); 75 | }); 76 | 77 | -------------------------------------------------------------------------------- /tests/set.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const hamt = require('../hamt'); 3 | const assert = require('chai').assert; 4 | 5 | describe('set', () => { 6 | it('should add entry to empty map', () => { 7 | assert.strictEqual(3, hamt.set('a', 3, hamt.make()).get('a')); 8 | assert.strictEqual(3, hamt.make().set('a', 3).get('a')); 9 | }); 10 | 11 | it('should add entry to existing map', () => { 12 | const h = hamt.make().set('a', 3); 13 | const h1 = h.set('b', 5); 14 | 15 | assert.strictEqual(3, hamt.get('a', h1)); 16 | assert.strictEqual(5, hamt.get('b', h1)); 17 | 18 | assert.strictEqual(3, hamt.get('a', h)); 19 | assert.strictEqual(undefined, hamt.get('b', h)); 20 | }); 21 | 22 | 23 | it('should overwrite entry in existing map', () => { 24 | const h1 = hamt.make() 25 | .set('a', 3) 26 | .set('b', 5) 27 | .set('c', 10); 28 | const h2 = h1.set('b', 4); 29 | 30 | assert.strictEqual(3, hamt.get('a', h2)); 31 | assert.strictEqual(4, hamt.get('b', h2)); 32 | assert.strictEqual(10, hamt.get('c', h2)); 33 | 34 | assert.strictEqual(3, hamt.get('a', h1)); 35 | assert.strictEqual(5, hamt.get('b', h1)); 36 | assert.strictEqual(10, hamt.get('c', h1)); 37 | }); 38 | 39 | it('should handle collisions correctly', () => { 40 | const h1 = hamt.make().setHash(0, 'a', 3); 41 | const h2 = h1.setHash(0, 'b', 5); 42 | 43 | assert.strictEqual(3, h2.getHash(0, 'a')); 44 | assert.strictEqual(5, h2.getHash(0, 'b')); 45 | }); 46 | 47 | it('should add to collisions correctly', () => { 48 | const h1 = hamt.make().setHash(0, 'a', 3); 49 | const h2 = h1.setHash(0, 'b', 5); 50 | const h3 = h2.setHash(1, 'c', 7); 51 | 52 | assert.strictEqual(3, h3.getHash(0, 'a')); 53 | assert.strictEqual(5, h3.getHash(0, 'b')); 54 | assert.strictEqual(7, h3.getHash(1, 'c')); 55 | }); 56 | 57 | it('should use custom hash function', () => { 58 | const hash = () => 0; 59 | const h = hamt.make({ hash: hash }) 60 | .set('a', 3) 61 | .set('b', 5); 62 | 63 | assert.strictEqual(3, h.getHash(0, 'a')); 64 | assert.strictEqual(5, h.getHash(0, 'b')); 65 | }); 66 | 67 | it('should use custom key function', () => { 68 | const keyEq = (x, y) => x.value === y.value; 69 | const h = hamt.make({ keyEq: keyEq }) 70 | .set({ value: 'a' }, 3) 71 | .set({ value: 'b' }, 5) 72 | .set({ value: 'a' }, 7); 73 | 74 | assert.strictEqual(2, h.count()); 75 | assert.strictEqual(7, h.get({ value: 'a' })); 76 | assert.strictEqual(undefined, h.get('a')); 77 | assert.strictEqual(5, h.get({ value: 'b' })); 78 | assert.strictEqual(undefined, h.get('b')); 79 | }); 80 | 81 | it('should set values correctly from list with no order', () => { 82 | const arr = [ 83 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g", 84 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r", 85 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s", 86 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P", 87 | "u", "I", "O", "`", "X"]; 88 | 89 | let h = hamt.make(); 90 | arr.forEach(function(x) { 91 | h = h.set(x, x); 92 | }); 93 | 94 | arr.forEach(function(x) { 95 | assert.strictEqual(x, hamt.get(x, h)); 96 | }); 97 | }); 98 | 99 | it('should set values correctly from an ordered list', () => { 100 | let h = hamt.make(); 101 | for (let i = 'A'.charCodeAt(0); i < 'z'.charCodeAt(0); ++i) { 102 | h = h.set(String.fromCharCode(i), i); 103 | } 104 | 105 | for (let i = 'A'.charCodeAt(0); i < 'z'.charCodeAt(0); ++i) { 106 | assert.strictEqual(i, hamt.get(String.fromCharCode(i), h)); 107 | } 108 | }); 109 | 110 | it('should not mutate map if value is same as stored', () => { 111 | { 112 | const h = hamt.make().set('a', 3); 113 | const h1 = h.set('a', 3); 114 | assert.strictEqual(h1, h); 115 | } 116 | { 117 | const h = hamt.make() 118 | .set('a', 3) 119 | .set('b', 5) 120 | .set('c', 10); 121 | const h1 = h 122 | .set('a', 3) 123 | .set('b', 5) 124 | .set('c', 10); 125 | assert.strictEqual(h1, h); 126 | } 127 | }); 128 | 129 | it('should not mutate map if value in collision is same as stored', () => { 130 | const h = hamt.make().setHash(0, 'a', 3).setHash(0, 'b', 3); 131 | const h1 = h.setHash(0, 'a', 3); 132 | assert.strictEqual(h1, h); 133 | }); 134 | }); 135 | -------------------------------------------------------------------------------- /tests/mutation.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const hamt = require('../hamt'); 3 | const assert = require('chai').assert; 4 | 5 | describe('mutate', () => { 6 | it('should insert single element into empty map', () => { 7 | const h1 = hamt.mutate(function (m) { 8 | hamt.set('a', 3, m); 9 | }, hamt.make()); 10 | 11 | assert.strictEqual(1, h1.count()); 12 | assert.strictEqual(3, h1.get('a')); 13 | }); 14 | 15 | it('should insert elements as normal into empty map', () => { 16 | const h1 = hamt.mutate(function (m) { 17 | hamt.set('a', 3, m); 18 | hamt.set('b', 5, m); 19 | }, hamt.make()); 20 | 21 | assert.strictEqual(2, h1.size); 22 | assert.strictEqual(3, h1.get('a')); 23 | assert.strictEqual(5, h1.get('b')); 24 | }); 25 | 26 | it('should allow for nested mutations', () => { 27 | let h1 = hamt.make().beginMutation().beginMutation(); 28 | h1.set('a', 3); 29 | h1.set('b', 5); 30 | h1.endMutation(); 31 | 32 | assert.strictEqual(2, h1.size); 33 | assert.strictEqual(3, h1.get('a')); 34 | assert.strictEqual(5, h1.get('b')); 35 | 36 | h1.set('a', 30); 37 | assert.strictEqual(2, h1.size); 38 | assert.strictEqual(30, h1.get('a')); 39 | assert.strictEqual(5, h1.get('b')); 40 | 41 | h1.endMutation(); 42 | 43 | h1.set('a', 300); 44 | assert.strictEqual(2, h1.size); 45 | assert.strictEqual(30, h1.get('a')); 46 | assert.strictEqual(5, h1.get('b')); 47 | 48 | }); 49 | 50 | it('should not leak mutation to values before scope', () => { 51 | const h = hamt.set('a', 100, hamt.make()); 52 | 53 | const h1 = hamt.mutate(function (m) { 54 | hamt.set('a', 3, m); 55 | hamt.set('b', 5, m); 56 | }, h); 57 | 58 | assert.strictEqual(1, h.size); 59 | assert.strictEqual(100, hamt.get('a', h)); 60 | assert.strictEqual(undefined, hamt.get('b', h)); 61 | 62 | assert.strictEqual(2, h1.size); 63 | assert.strictEqual(3, hamt.get('a', h1)); 64 | assert.strictEqual(5, hamt.get('b', h1)); 65 | }); 66 | 67 | it('should not leak mutation to values after scope', () => { 68 | const h = hamt.mutate(function (m) { 69 | hamt.set('a', 3, m); 70 | hamt.set('b', 5, m); 71 | }, hamt.make()); 72 | 73 | const h1 = hamt.set('a', 100, h); 74 | 75 | assert.strictEqual(2, h.size); 76 | assert.strictEqual(3, hamt.get('a', h)); 77 | assert.strictEqual(5, hamt.get('b', h)); 78 | 79 | assert.strictEqual(2, h1.size); 80 | assert.strictEqual(100, hamt.get('a', h1)); 81 | assert.strictEqual(5, hamt.get('b', h1)); 82 | }); 83 | 84 | it('should not effect other mutations', () => { 85 | const h = hamt.set('a', 100, hamt.make()); 86 | 87 | const h1 = hamt.mutate(function (m) { 88 | hamt.set('a', 3, m); 89 | hamt.set('b', 5, m); 90 | }, h); 91 | 92 | const h2 = hamt.mutate(function (m) { 93 | hamt.set('a', 30, m); 94 | hamt.set('b', 50, m); 95 | }, h1); 96 | 97 | assert.strictEqual(1, h.size); 98 | assert.strictEqual(100, hamt.get('a', h)); 99 | assert.strictEqual(undefined, hamt.get('b', h)); 100 | 101 | assert.strictEqual(2, h1.size); 102 | assert.strictEqual(3, hamt.get('a', h1)); 103 | assert.strictEqual(5, hamt.get('b', h1)); 104 | 105 | assert.strictEqual(2, h2.size); 106 | assert.strictEqual(50, hamt.get('b', h2)); 107 | assert.strictEqual(30, hamt.get('a', h2)); 108 | }); 109 | 110 | it('should handle many insertions correctly', () => { 111 | const insert = [ 112 | "The", "Time", "Traveller", "for", "so", "it", "will", "be", 113 | "convenient", "to", "speak", "of", "him", "was", "expounding", 114 | "a", "recondite", "matter", "us", "His", "grey", "eyes", 115 | "shone", "twinkled", "his", "usually", "pale", 116 | "face", "flushed", "animated"]; 117 | 118 | const h = hamt.mutate(h => 119 | insert.forEach((x, i) => { 120 | h.set(x, x); 121 | assert.strictEqual(i + 1, h.count()); 122 | }), 123 | hamt.empty); 124 | 125 | assert.strictEqual(insert.length, h.size); 126 | insert.forEach(x => 127 | assert.strictEqual(x, h.get(x))); 128 | 129 | const h1 = hamt.mutate(h => { 130 | insert.forEach((x, i) => { 131 | h.set(x + x, x); 132 | assert.strictEqual(insert.length + i + 1, h.count()); 133 | }); 134 | }, h); 135 | 136 | assert.strictEqual(insert.length, h.size); 137 | insert.forEach(x => 138 | assert.strictEqual(x, h.get(x))); 139 | 140 | assert.strictEqual(insert.length * 2, h1.size); 141 | insert.forEach(x => { 142 | assert.strictEqual(x, h1.get(x)); 143 | assert.strictEqual(x, h1.get(x + x)) 144 | }); 145 | }); 146 | 147 | it('should handle many removals correctly', () => { 148 | const insert = [ 149 | "The", "Time", "Traveller", "for", "so", "it", "will", "be", 150 | "convenient", "to", "speak", "of", "him", "was", "expounding", 151 | "a", "recondite", "matter", "us", "His", "grey", "eyes", 152 | "shone", "twinkled", "his", "usually", "pale", 153 | "face", "flushed", "animated"]; 154 | 155 | const h = hamt.mutate(h => { 156 | insert.forEach((x, i) => { 157 | h.set(x, x); 158 | assert.strictEqual(i + 1, h.count()); 159 | }) 160 | insert.forEach((x, i) => { 161 | h.delete(x); 162 | assert.strictEqual(insert.length - i - 1, h.count()); 163 | }) 164 | }, hamt.empty); 165 | 166 | assert.strictEqual(0, h.count()); 167 | }); 168 | 169 | }); 170 | -------------------------------------------------------------------------------- /tests/remove.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const hamt = require('../hamt'); 3 | const assert = require('chai').assert; 4 | 5 | describe('remove', () => { 6 | it('should noop on empty', () => { 7 | assert.strictEqual(0, hamt.count(hamt.remove('a', hamt.make()))); 8 | assert.strictEqual(0, hamt.count(hamt.remove('b', hamt.make()))); 9 | }); 10 | 11 | it('should remove value from single map', () => { 12 | const h1 = hamt.make().set('a', 3); 13 | const h2 = h1.remove('a'); 14 | 15 | assert.strictEqual(0, hamt.count(h2)); 16 | assert.strictEqual(undefined, hamt.get('a', h2)); 17 | 18 | assert.strictEqual(1, hamt.count(h1)); 19 | assert.strictEqual(3, hamt.get('a', h1)); 20 | }); 21 | 22 | it('should only remove a single entry', () => { 23 | const h1 = hamt.make() 24 | .set('a', 3) 25 | .set('b', 5); 26 | const h2 = hamt.remove('a', h1); 27 | 28 | assert.strictEqual(1, hamt.count(h2)); 29 | assert.strictEqual(undefined, hamt.get('a', h2)); 30 | assert.strictEqual(5, hamt.get('b', h2)); 31 | 32 | assert.strictEqual(2, hamt.count(h1)); 33 | assert.strictEqual(3, hamt.get('a', h1)); 34 | assert.strictEqual(5, hamt.get('b', h1)); 35 | }); 36 | 37 | it('should remove collisions correctly a single entry', () => { 38 | const h1 = hamt.make() 39 | .setHash(0, 'a', 3) 40 | .setHash(0, 'b', 5); 41 | 42 | const h2 = h1.deleteHash(0, 'a'); 43 | 44 | assert.strictEqual(1, hamt.count(h2)); 45 | assert.strictEqual(undefined, h2.getHash(0, 'a')); 46 | assert.strictEqual(5, h2.getHash(0, 'b')); 47 | 48 | assert.strictEqual(2, hamt.count(h1)); 49 | assert.strictEqual(3, h1.getHash(0, 'a')); 50 | assert.strictEqual(5, h1.getHash(0, 'b')); 51 | }); 52 | 53 | it('should not remove for a collision that does not match key', () => { 54 | const h1 = hamt.make() 55 | .setHash(0, 'a', 3) 56 | .setHash(0, 'b', 5); 57 | 58 | const h2 = h1.removeHash(0, 'c'); 59 | 60 | assert.strictEqual(3, h2.getHash(0, 'a')); 61 | assert.strictEqual(5, h2.getHash(0, 'b')); 62 | assert.strictEqual(undefined, h2.getHash(0, 'c')); 63 | assert.strictEqual(h1, h2); 64 | }); 65 | 66 | it('should use custom hash function', () => { 67 | const hash = () => 0; 68 | const h1 = hamt.make({ 69 | hash: hash 70 | }) 71 | .setHash(0, 'a', 3) 72 | .setHash(0, 'b', 5); 73 | 74 | const h2 = h1.delete('a'); 75 | assert.strictEqual(undefined, h2.getHash(0, 'a')); 76 | assert.strictEqual(5, h2.getHash(0, 'b')); 77 | }); 78 | 79 | it('should remove correctly from large set', () => { 80 | const insert = [ 81 | "n", "U", "p", "^", "h", "w", "W", "x", "S", "f", "H", "m", "g", 82 | "l", "b", "_", "V", "Z", "G", "o", "F", "Q", "a", "k", "j", "r", 83 | "B", "A", "y", "\\", "R", "D", "i", "c", "]", "C", "[", "e", "s", 84 | "t", "J", "E", "q", "v", "M", "T", "N", "L", "K", "Y", "d", "P", 85 | "u", "I", "O", "`", "X" 86 | ]; 87 | 88 | const remove = [ 89 | "w", "m", "Q", "R", "i", "K", "P", "Y", "D", "g", "y", "L", 90 | "b", "[", "a", "t", "j", "W", "J", "G", "q", "r", "p", "U", 91 | "v", "h", "S", "_", "d", "x", "I", "F", "f", "n", "B", "\\", 92 | "k", "V", "N", "l", "X", "A", "]", "s", "Z", "O", "^", "o", 93 | "`", "H", "E", "e", "M", "u", "T", "c", "C" 94 | ]; 95 | 96 | let h = hamt.make(); 97 | insert.forEach(function(x) { 98 | h = hamt.set(x, x, h); 99 | }); 100 | 101 | for (let i = 0; i < remove.length; ++i) { 102 | h = hamt.remove(remove[i], h); 103 | 104 | for (let g = 0; g <= i; ++g) { 105 | assert.strictEqual( 106 | hamt.get(remove[g], h), 107 | undefined); 108 | } 109 | for (let g = i + 1; g < remove.length; ++g) { 110 | assert.strictEqual( 111 | hamt.get(remove[g], h), 112 | remove[g]); 113 | } 114 | } 115 | }); 116 | 117 | it('should not mutate for noop remove', () => { 118 | const h1 = hamt.make() 119 | .set('a', 3) 120 | .set('b', 5); 121 | const h2 = hamt.remove('none', h1); 122 | 123 | assert.strictEqual(h1, h2); 124 | }); 125 | 126 | it('should when all elements are removed', () => { 127 | const keys = ['hiymmhdhq', 'hzyghyg', 'hzieut', 'mjnaup', 'tinjxpys', 'kwpcqm', 128 | 'vxeusxcg', 'faybuua', 'lycfxflwft', 'tnwtzj', 'lrvycc', 'flaqdhqkj', 129 | 'ngkmhejrm', 'jkqotnew', 'tvnxhguhn', 'frisdgmgwk', 'xqhakqug', 'cncoahk', 130 | 'zczoqcfqy', 'czlfnbl', 'comrfarx', 'xkxedunf', 'szzmuwuuu', 'mcqhmf', 131 | 'zqyjwwjba', 'kqoxvzky', 'mihnuv', 'shydgsfmpp', 'rdokftl', 'hzkpejjor', 132 | 'uuwfazpud', 'wauyyr', 'nhfzckr', 'kmfdpcdgwi', 'twhbuhpgp', 'eyzbrtjwa', 133 | 'aqdohkac', 'mteeptl', 'lmyxutoqg', 'ijqumqzsq', 'qwpqnsp', 'yklnknl', 134 | 'byjxqzl', 'ryptefqr', 'fhplnoi', 'uvflmypxsa', 'xsenqm', 'kpquygdx', 135 | 'ztsfcuy', 'xjrtyl', 'elzgmbcsfs', 'tksobwth', 'nxfktmbn', 'qsiqzdl', 136 | 'ztfxghd', 'blekwtpzg', 'ogtwty', 'jvwzhjwmnl', 'xiqset', 'yaeazzw', 137 | 'megtbspnvy', 'afjowwuv', 'ysaldydgvx', 'vrejaghyy', 'ogwjrroeiu', 'alvdrg', 138 | 'lytqpgdnt', 'yaiedb', 'czrtsqh', 'bfvxsoxvql', 'vpfaam', 'kbiyel', 'vwwubrdqx', 139 | 'iwhibpcqm', 'jrhkkzw', 'ajezbycwg', 'asubyu', 'ctdjnchw', 'gbzbbmdug', 'njxcfr', 140 | 'mddyfqb', 'xgtthksh', 'cocjuvwrjm', 'xnwuoczjnh', 'nqehrflx', 'szyfto', 141 | 'vpmynbgdo', 'zourijqabw', 'olujjqqkxy', 'rifpiaoqrj', 'ahurel', 'rqfdytylz', 142 | 'ymgpnp', 'qevprue', 'sjttddstx', 'uqjuyyu', 'mkwxsgg', 'aesdxulaw', 'nwtfbe', 143 | 'duyksei' 144 | ]; 145 | const order = [62, 6, 52, 79, 89, 1, 94, 16, 10, 21, 70, 99, 7, 81, 63, 146 | 73, 53, 50, 25, 26, 78, 29, 28, 13, 35, 77, 84, 51, 15, 36, 96, 92, 69, 147 | 18, 88, 12, 20, 58, 14, 48, 17, 4, 82, 76, 42, 5, 9, 86, 87, 43, 61, 2, 148 | 66, 41, 68, 56, 54, 74, 22, 34, 3, 57, 31, 72, 91, 95, 23, 90, 32, 71, 149 | 85, 80, 60, 11, 0, 55, 46, 40, 37, 67, 75, 64, 97, 27, 49, 93, 47, 38, 150 | 83, 59, 65, 30, 98, 8, 44, 24, 19, 39, 45, 33 151 | ]; 152 | 153 | let h = hamt.empty; 154 | keys.forEach(function(x) { 155 | h = hamt.set(x, x, h); 156 | }); 157 | 158 | order.forEach(function(x) { 159 | h = hamt.remove(keys[x], h); 160 | }); 161 | }); 162 | }); 163 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | H.A.M.T.+ 3 |
4 | 5 | Fork of the [Hamt][hamt] ([hash array mapped trie][hash-array-mapped-trie]) library. This fork adds a few important features in exchange for very slightly degraded performance: 6 | 7 | * Transient mutation. This allows efficient mass operations, while retaining the safety of a persistent data structure. 8 | * Supports using a custom key comparision function. 9 | * Supports using a custom hash function. 10 | 11 | The Hamt+ Api is a superset of Hamt's Api. Hamt+ supports any key type using the custom hash and key comparision functions. 12 | 13 | ## Install 14 | Source code is in `hamt.js` and generated from `lib/hamt.js`. The library supports node, AMD, and use as a global. 15 | 16 | ### Node 17 | ``` sh 18 | $ npm install hamt 19 | ``` 20 | 21 | ``` javascript 22 | var hamt = require('hamt_plus'); 23 | 24 | var h = hamt.empty.set('key', 'value'); 25 | 26 | ... 27 | ``` 28 | 29 | 30 | ### AMD 31 | ``` javascript 32 | requirejs.config({ 33 | paths: { 34 | 'hamt': 'path/to/hamt_plus/' 35 | } 36 | }); 37 | 38 | require(['hamt'], function(hamt) { 39 | var h = hamt.empty.set('key', 'value'); 40 | ... 41 | }); 42 | ``` 43 | 44 | 45 | # Usage 46 | Hamt+ provides a method chaining interface and free functions for updating and querying the map. Both APIs provide identical functionality, but the free functions are designed for binding and composition, while the method chaining API is more legible and more Javascripty. 47 | 48 | HAMTs are is persistent, so operations always return a modified copy of the map instead of altering the original. 49 | 50 | ## Custom Hash Values 51 | Most update and lookup methods have two versions: one that takes a key and uses an internal hash function to compute its hash, and a version that takes a custom computed hash value. 52 | 53 | 54 | ``` javascript 55 | var h = hamt.empty.set('key', 'value'); 56 | var h2 = hamt.empty.setHash(5, 'key', 'value'); 57 | 58 | 59 | h.get('key') === 'value' 60 | h2.getHash(5, 'key') === 'value' 61 | ``` 62 | 63 | If using a custom hash, you must only use the `*Hash` variant of functions to interact with the map. 64 | 65 | 66 | ``` javascript 67 | // Because the internally computed hash of `key` is not `5`, a direct 68 | // look will not work. 69 | h2.get('key') === undefined 70 | 71 | // You must use `getHash` with the same hash value originally passed in. 72 | h2.getHash(5, 'key') === 'value' 73 | ``` 74 | 75 | 76 | ## API 77 | 78 | #### `hamt.make(config)` 79 | Create a new, empty map. 80 | 81 | * `config` – Optional. Holds the custom hash and key compare functions: `{ hash: myHashFunction, keyEq: myKeyCompareFunction }` 82 | 83 | ```js 84 | const Vec2 = (x, y) => ({ x: x, y: y }); 85 | 86 | const vecMap = hamt.make({ 87 | hash: (value) => hamt.hash(value.x + ',' + value.y), 88 | keyEq: (a, b) => a.x === b.x && a.y === b.y 89 | }); 90 | 91 | vecMap = vecMap.set(Vec2(1, 2), 'value'); 92 | 93 | vecMap.get(Vec2(1, 2)) === 'value' 94 | ``` 95 | 96 | #### `hamt.empty` 97 | An empty map. 98 | 99 | Uses default key compare function and hash functions. 100 | 101 | ---- 102 | 103 | #### `hamt.isEmpty(map)` 104 | #### `map.isEmpty()` 105 | Is a map empty? 106 | 107 | This is the correct method to check if a map is empty. Direct comparisons to `hamt.empty` will not work. 108 | 109 | ---- 110 | 111 | #### `hamt.get(key, map)` 112 | #### `map.get(key)` 113 | Lookup the value for `key` in `map`. 114 | 115 | * `key` - String key. 116 | * `map` - Hamt map. 117 | 118 | ``` javascript 119 | var h = hamt.empty.set('key', 'value'); 120 | 121 | h.get('key') === 'value' 122 | hamt.get('key', k) === 'value' 123 | 124 | h.get('no such key') === undefined 125 | ``` 126 | 127 | ---- 128 | 129 | #### `hamt.getHash(hash, key, map)` 130 | #### `map.getHash(hash, key)` 131 | Same as `get` but uses a custom hash value. 132 | 133 | ---- 134 | 135 | #### `hamt.tryGet(alt, key, map)` 136 | #### `map.tryGet(alt, key)` 137 | Same as `get` but returns `alt` if no value for `key` exists. 138 | 139 | * `alt` - Value returned if no such key exists in the map. 140 | * `key` - String key. 141 | * `map` - Hamt map. 142 | 143 | ---- 144 | 145 | #### `hamt.has(key, map)` 146 | #### `map.has(key)` 147 | Does an entry for `key` exist in `map`? 148 | 149 | * `key` - String key. 150 | * `map` - Hamt map. 151 | 152 | ``` javascript 153 | var h = hamt.empty.set('key', 'value'); 154 | 155 | h.has('key') === true 156 | h.has('no such key') === false 157 | ``` 158 | 159 | ---- 160 | 161 | #### `hamt.tryGetHash(alt, hash, key, map)` 162 | #### `map.tryGetHash(alt, hash, key)` 163 | Same as `tryGet` but uses a custom hash value. 164 | 165 | ---- 166 | 167 | #### `hamt.set(key, value, map)` 168 | #### `map.set(key, value)` 169 | Set the value for `key` in `map`. 170 | 171 | * `value` - Value to store. Hamt supports all value types, including: literals, objects, falsy values, null, and undefined. Keep in mind that only the map data structure itself is guaranteed to be immutable. Using immutable values is recommended but not required. 172 | * `key` - String key. 173 | * `map` - Hamt map. 174 | 175 | Returns a new map with the value set. Does not alter the original. 176 | 177 | ``` javascript 178 | var h = hamt.empty 179 | .set('key', 'value'); 180 | .set('key2', 'value2'); 181 | 182 | var h2 = h.set('key3', 'value3'); 183 | 184 | h2.get('key') === 'value' 185 | h2.get('key2') === 'value2' 186 | h2.get('key3') === 'value3' 187 | 188 | // original `h` was not modified 189 | h.get('key') === 'value' 190 | h.get('key2') === 'value2' 191 | h.get('key3') === undefined 192 | ``` 193 | 194 | ---- 195 | 196 | #### `hamt.setHash(hash, key, value, map)` 197 | #### `map.setHash(hash, key, value)` 198 | Same as `set` but uses a custom hash value. 199 | 200 | ---- 201 | 202 | #### `hamt.modify(f, key, map)` 203 | #### `map.modify(key, f)` 204 | Update the value stored for `key` in `map`. 205 | 206 | * `f` - Function mapping the current value to the new value. If no current value exists, the function is invoked with no arguments. 207 | * `key` - String key. 208 | * `map` - Hamt map. 209 | 210 | Returns a new map with the modified value. Does not alter the original. 211 | 212 | ``` javascript 213 | var h = hamt.empty 214 | .set('i', 2); 215 | 216 | var h2 = h.modify('i', x => x * x); 217 | 218 | h2.get('i') === 4 219 | h.get('i') === 2 220 | h2.count() === 1 221 | h.count() === 1 222 | 223 | // Operate on value that does not exist 224 | var h3 = h.modify('new', x => { 225 | if (x === undefined) { 226 | return 10; 227 | } 228 | return -x; 229 | }); 230 | 231 | h3.get('new') === 10 232 | h3.count() === 2 233 | ``` 234 | 235 | ---- 236 | 237 | #### `hamt.modifyHash(f, hash, key, map)` 238 | #### `map.modifyHash(hash, key, f)` 239 | Same as `modify` but uses a custom hash value. 240 | 241 | ---- 242 | 243 | #### `hamt.remove(key, map)` 244 | #### `map.remove(key)` 245 | #### `map.delete(key)` 246 | Remove `key` from `map`. 247 | 248 | * `key` - String key. 249 | * `map` - Hamt map. 250 | 251 | Returns a new map with the value removed. Does not alter the original. 252 | 253 | ``` javascript 254 | var h = hamt.empty 255 | .set('a', 1) 256 | .set('b', 2) 257 | .set('c', 3); 258 | 259 | var h2 = h.remove('b'); 260 | 261 | h2.count() === 2; 262 | h2.get('a') === 1 263 | h2.get('b') === undefined 264 | h2.get('c') === 3 265 | ``` 266 | 267 | ---- 268 | 269 | #### `hamt.removeHash(hash, key, map)` 270 | #### `map.removeHash(hash, key)` 271 | #### `map.deleteHash(hash, key)` 272 | Same as `remove` but uses a custom hash value. 273 | 274 | ---- 275 | 276 | #### `hamt.count(map)` 277 | #### `map.count()` 278 | #### `map.size` 279 | Get number of elements in `map`. 280 | 281 | * `map` - Hamt map. 282 | 283 | 284 | ``` javascript 285 | hamt.empty.count() === 0; 286 | hamt.empty.set('a', 3).count() === 1; 287 | hamt.empty.set('a', 3).set('b', 3).count() === 2; 288 | ``` 289 | 290 | ---- 291 | 292 | #### `hamt.fold(f, z, map)` 293 | #### `map.fold(f, z)` 294 | Fold over the map, accumulating result value. 295 | 296 | * `f` - Function invoked with accumulated value, current value, and current key. 297 | * `z` - Initial value. 298 | * `map` - Hamt map. 299 | 300 | Order is not guaranteed. 301 | 302 | ``` javascript 303 | var max = hamt.fold.bind(null, 304 | (acc, value, key) => Math.max(acc, value), 305 | 0); 306 | 307 | max(hamt.empty.set('key', 3).set('key', 4)) === 4; 308 | ``` 309 | 310 | ---- 311 | 312 | #### `hamt.entries(map)` 313 | #### `map.entries()` 314 | Get an Javascript iterator to all key value pairs in `map`. 315 | 316 | * `map` - Hamt map. 317 | 318 | Order is not guaranteed. 319 | 320 | ``` javascript 321 | Array.from(hamt.empty.entries()) === []; 322 | Array.from(hamt.empty.set('a', 3).entries()) === [['a', 3]]; 323 | Array.from(hamt.empty.set('a', 3).set('b', 3).entries()) === [['a', 3], ['b', 3]]; 324 | ``` 325 | 326 | You can also iterated directly over a map with ES6: 327 | 328 | ```javascript 329 | const h = hamt.empty.set('a', 3).set('b', 3); 330 | 331 | for (let [key, value] of h) 332 | ... 333 | 334 | Array.from(h) === [['a', 3], ['b', 3]]; 335 | ``` 336 | 337 | ---- 338 | 339 | #### `hamt.key(map)` 340 | #### `map.keys()` 341 | Get an Javascript iterator to all keys in `map`. 342 | 343 | * `map` - Hamt map. 344 | 345 | Order is not guaranteed. 346 | 347 | ``` javascript 348 | Array.from(hamt.empty.keys()) === []; 349 | Array.from(hamt.empty.set('a', 3).keys()) === ['a']; 350 | Array.from(hamt.empty.set('a', 3).set('b', 3).keys()) === ['a', 'b']; 351 | ``` 352 | 353 | ---- 354 | 355 | #### `hamt.values(map)` 356 | #### `map.values()` 357 | Get an Javascript iterator to all values in `map`. 358 | 359 | * `map` - Hamt map. 360 | 361 | Order is not guaranteed. Duplicate entries may exist. 362 | 363 | ``` javascript 364 | Array.from(hamt.empty.values()) === []; 365 | Array.from(hamt.empty.set('a', 3).values()) === [3]; 366 | Array.from(hamt.empty.set('a', 3).values('b', 3).values()) === [3, 3]; 367 | ``` 368 | 369 | ---- 370 | 371 | #### `hamt.forEach(f, map)` 372 | #### `map.forEach(f)` 373 | Invoke function `f` for each value in the map. 374 | 375 | * `f` - Function invoked with `(value, key, map)`. 376 | * `map` - Hamt map. 377 | 378 | Order is not guaranteed. 379 | 380 | ---- 381 | 382 | #### `hamt.beginMutation(map)` 383 | #### `map.beginMutation()` 384 | Start the mutation of `map`. The number of calls to `beginMutation` is counted, but mutation itself is binary: the map is either mutable or immutable. Mutation cannot leak before the first call to `beginMutation` or after the matching call to `endMutation.` 385 | 386 | ---- 387 | 388 | #### `hamt.endMutation(map)` 389 | #### `map.endMutation()` 390 | End the mutation of `map`. 391 | 392 | ---- 393 | 394 | #### `hamt.mutate(f, map)` 395 | #### `map.mutate(f)` 396 | Mutate `map` within the context of function `f`. 397 | 398 | ```js 399 | const insert = ['a', 'b', 'c']; 400 | 401 | const h = hamt.mutate(h => 402 | // any operations within this block may mutate `h` internally. 403 | insert.forEach((x, i) => { 404 | h.set(x, i); 405 | }), 406 | hamt.empty); 407 | 408 | h.count() === 3; 409 | h.get('b') === 2; 410 | ``` 411 | 412 | ## Development 413 | Any contributions to Hamt+ are welcome. Feel free to open an [issues](https://github.com/mattbierner/hamt_plus/issues) if you run into problems or have a suggested improvement. 414 | 415 | To develop Hamt, fork the repo and install the development node packages: 416 | 417 | ```bash 418 | cd hamt_plus 419 | $ npm install 420 | ``` 421 | 422 | The source is written in ES6 and lives in `lib/hamt.js`. Gulp and Bable are used to translate the ES6 code to an ES5 distribution found in `hamt.js`. To start the compiler: 423 | 424 | ```bash 425 | $ gulp default 426 | ``` 427 | 428 | Tests are written in Mocha and found in `tests/*`. To run the tests: 429 | 430 | ```js 431 | $ mocha tests 432 | ``` 433 | 434 | [hamt]: https://github.com/mattbierner/hamt 435 | [benchmarks]: http://github.com/mattbierner/js-hashtrie-benchmark 436 | [pdata]: https://github.com/exclipy/pdata 437 | [hash-array-mapped-trie]: http://en.wikipedia.org/wiki/Hash_array_mapped_trie 438 | [persistent]: http://en.wikipedia.org/wiki/Persistent_data_structure 439 | -------------------------------------------------------------------------------- /lib/hamt.js: -------------------------------------------------------------------------------- 1 | /** 2 | @fileOverview Hash Array Mapped Trie. 3 | 4 | Code based on: https://github.com/exclipy/pdata 5 | */ 6 | const hamt = {}; // export 7 | 8 | /* Configuration 9 | ******************************************************************************/ 10 | const SIZE = 5; 11 | 12 | const BUCKET_SIZE = Math.pow(2, SIZE); 13 | 14 | const MASK = BUCKET_SIZE - 1; 15 | 16 | const MAX_INDEX_NODE = BUCKET_SIZE / 2; 17 | 18 | const MIN_ARRAY_NODE = BUCKET_SIZE / 4; 19 | 20 | /* 21 | ******************************************************************************/ 22 | const nothing = ({}); 23 | 24 | const constant = x => () => x; 25 | 26 | /** 27 | Get 32 bit hash of string. 28 | 29 | Based on: 30 | http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery 31 | */ 32 | const hash = hamt.hash = str => { 33 | const type = typeof str; 34 | if (type === 'number') 35 | return str; 36 | if (type !== 'string') 37 | str += ''; 38 | 39 | let hash = 0; 40 | for (let i = 0, len = str.length; i < len; ++i) { 41 | const c = str.charCodeAt(i); 42 | hash = (((hash << 5) - hash) + c) | 0; 43 | } 44 | return hash; 45 | }; 46 | 47 | /* Bit Ops 48 | ******************************************************************************/ 49 | /** 50 | Hamming weight. 51 | 52 | Taken from: http://jsperf.com/hamming-weight 53 | */ 54 | const popcount = (x) => { 55 | x -= ((x >> 1) & 0x55555555); 56 | x = (x & 0x33333333) + ((x >> 2) & 0x33333333); 57 | x = (x + (x >> 4)) & 0x0f0f0f0f; 58 | x += (x >> 8); 59 | x += (x >> 16); 60 | return (x & 0x7f); 61 | }; 62 | 63 | const hashFragment = (shift, h) => 64 | (h >>> shift) & MASK; 65 | 66 | const toBitmap = x => 67 | 1 << x; 68 | 69 | const fromBitmap = (bitmap, bit) => 70 | popcount(bitmap & (bit - 1)); 71 | 72 | /* Array Ops 73 | ******************************************************************************/ 74 | /** 75 | Set a value in an array. 76 | 77 | @param mutate Should the input array be mutated? 78 | @param at Index to change. 79 | @param v New value 80 | @param arr Array. 81 | */ 82 | const arrayUpdate = (mutate, at, v, arr) => { 83 | let out = arr; 84 | if (!mutate) { 85 | const len = arr.length; 86 | out = new Array(len); 87 | for (let i = 0; i < len; ++i) 88 | out[i] = arr[i]; 89 | } 90 | out[at] = v; 91 | return out; 92 | }; 93 | 94 | /** 95 | Remove a value from an array. 96 | 97 | @param mutate Should the input array be mutated? 98 | @param at Index to remove. 99 | @param arr Array. 100 | */ 101 | const arraySpliceOut = (mutate, at, arr) => { 102 | const newLen = arr.length - 1; 103 | let i = 0; 104 | let g = 0; 105 | let out = arr; 106 | if (mutate) { 107 | i = g = at; 108 | } else { 109 | out = new Array(newLen); 110 | while (i < at) 111 | out[g++] = arr[i++]; 112 | } 113 | ++i; 114 | while (i <= newLen) 115 | out[g++] = arr[i++]; 116 | if (mutate) { 117 | out.length = newLen; 118 | } 119 | return out; 120 | }; 121 | 122 | /** 123 | Insert a value into an array. 124 | 125 | @param mutate Should the input array be mutated? 126 | @param at Index to insert at. 127 | @param v Value to insert, 128 | @param arr Array. 129 | */ 130 | const arraySpliceIn = (mutate, at, v, arr) => { 131 | const len = arr.length; 132 | if (mutate) { 133 | let i = len; 134 | while (i >= at) 135 | arr[i--] = arr[i]; 136 | arr[at] = v; 137 | return arr; 138 | } 139 | let i = 0, g = 0; 140 | const out = new Array(len + 1); 141 | while (i < at) 142 | out[g++] = arr[i++]; 143 | out[at] = v; 144 | while (i < len) 145 | out[++g] = arr[i++]; 146 | return out; 147 | }; 148 | 149 | /* Node Structures 150 | ******************************************************************************/ 151 | const LEAF = 1; 152 | const COLLISION = 2; 153 | const INDEX = 3; 154 | const ARRAY = 4; 155 | 156 | /** 157 | Empty node. 158 | */ 159 | const empty = ({ 160 | __hamt_isEmpty: true 161 | }); 162 | 163 | const isEmptyNode = x => 164 | x === empty || (x && x.__hamt_isEmpty); 165 | 166 | /** 167 | Leaf holding a value. 168 | 169 | @member edit Edit of the node. 170 | @member hash Hash of key. 171 | @member key Key. 172 | @member value Value stored. 173 | */ 174 | const Leaf = (edit, hash, key, value) => ({ 175 | type: LEAF, 176 | edit: edit, 177 | hash: hash, 178 | key: key, 179 | value: value, 180 | _modify: Leaf__modify 181 | }); 182 | 183 | /** 184 | Leaf holding multiple values with the same hash but different keys. 185 | 186 | @member edit Edit of the node. 187 | @member hash Hash of key. 188 | @member children Array of collision children node. 189 | */ 190 | const Collision = (edit, hash, children) => ({ 191 | type: COLLISION, 192 | edit: edit, 193 | hash: hash, 194 | children: children, 195 | _modify: Collision__modify 196 | }); 197 | 198 | /** 199 | Internal node with a sparse set of children. 200 | 201 | Uses a bitmap and array to pack children. 202 | 203 | @member edit Edit of the node. 204 | @member mask Bitmap that encode the positions of children in the array. 205 | @member children Array of child nodes. 206 | */ 207 | const IndexedNode = (edit, mask, children) => ({ 208 | type: INDEX, 209 | edit: edit, 210 | mask: mask, 211 | children: children, 212 | _modify: IndexedNode__modify 213 | }); 214 | 215 | /** 216 | Internal node with many children. 217 | 218 | @member edit Edit of the node. 219 | @member size Number of children. 220 | @member children Array of child nodes. 221 | */ 222 | const ArrayNode = (edit, size, children) => ({ 223 | type: ARRAY, 224 | edit: edit, 225 | size: size, 226 | children: children, 227 | _modify: ArrayNode__modify 228 | }); 229 | 230 | /** 231 | Is `node` a leaf node? 232 | */ 233 | const isLeaf = node => 234 | (node === empty || node.type === LEAF || node.type === COLLISION); 235 | 236 | /* Internal node operations. 237 | ******************************************************************************/ 238 | /** 239 | Expand an indexed node into an array node. 240 | 241 | @param edit Current edit. 242 | @param frag Index of added child. 243 | @param child Added child. 244 | @param mask Index node mask before child added. 245 | @param subNodes Index node children before child added. 246 | */ 247 | const expand = (edit, frag, child, bitmap, subNodes) => { 248 | const arr = []; 249 | let bit = bitmap; 250 | let count = 0; 251 | for (let i = 0; bit; ++i) { 252 | if (bit & 1) 253 | arr[i] = subNodes[count++]; 254 | bit >>>= 1; 255 | } 256 | arr[frag] = child; 257 | return ArrayNode(edit, count + 1, arr); 258 | }; 259 | 260 | /** 261 | Collapse an array node into a indexed node. 262 | 263 | @param edit Current edit. 264 | @param count Number of elements in new array. 265 | @param removed Index of removed element. 266 | @param elements Array node children before remove. 267 | */ 268 | const pack = (edit, count, removed, elements) => { 269 | const children = new Array(count - 1); 270 | let g = 0; 271 | let bitmap = 0; 272 | for (let i = 0, len = elements.length; i < len; ++i) { 273 | if (i !== removed) { 274 | const elem = elements[i]; 275 | if (elem && !isEmptyNode(elem)) { 276 | children[g++] = elem; 277 | bitmap |= 1 << i; 278 | } 279 | } 280 | } 281 | return IndexedNode(edit, bitmap, children); 282 | }; 283 | 284 | /** 285 | Merge two leaf nodes. 286 | 287 | @param shift Current shift. 288 | @param h1 Node 1 hash. 289 | @param n1 Node 1. 290 | @param h2 Node 2 hash. 291 | @param n2 Node 2. 292 | */ 293 | const mergeLeaves = (edit, shift, h1, n1, h2, n2) => { 294 | if (h1 === h2) 295 | return Collision(edit, h1, [n2, n1]); 296 | 297 | const subH1 = hashFragment(shift, h1); 298 | const subH2 = hashFragment(shift, h2); 299 | return IndexedNode(edit, toBitmap(subH1) | toBitmap(subH2), 300 | subH1 === subH2 301 | ? [mergeLeaves(edit, shift + SIZE, h1, n1, h2, n2)] 302 | : subH1 < subH2 ? [n1, n2] : [n2, n1]); 303 | }; 304 | 305 | /** 306 | Update an entry in a collision list. 307 | 308 | @param mutate Should mutation be used? 309 | @param edit Current edit. 310 | @param keyEq Key compare function. 311 | @param hash Hash of collision. 312 | @param list Collision list. 313 | @param f Update function. 314 | @param k Key to update. 315 | @param size Size ref. 316 | */ 317 | const updateCollisionList = (mutate, edit, keyEq, h, list, f, k, size) => { 318 | const len = list.length; 319 | for (let i = 0; i < len; ++i) { 320 | const child = list[i]; 321 | if (keyEq(k, child.key)) { 322 | const value = child.value; 323 | const newValue = f(value); 324 | if (newValue === value) 325 | return list; 326 | 327 | if (newValue === nothing) { 328 | --size.value; 329 | return arraySpliceOut(mutate, i, list); 330 | } 331 | return arrayUpdate(mutate, i, Leaf(edit, h, k, newValue), list); 332 | } 333 | } 334 | 335 | const newValue = f(); 336 | if (newValue === nothing) 337 | return list; 338 | ++size.value; 339 | return arrayUpdate(mutate, len, Leaf(edit, h, k, newValue), list); 340 | }; 341 | 342 | const canEditNode = (edit, node) => edit === node.edit; 343 | 344 | /* Editing 345 | ******************************************************************************/ 346 | const Leaf__modify = function (edit, keyEq, shift, f, h, k, size) { 347 | if (keyEq(k, this.key)) { 348 | const v = f(this.value); 349 | if (v === this.value) 350 | return this; 351 | else if (v === nothing) { 352 | --size.value; 353 | return empty; 354 | } 355 | if (canEditNode(edit, this)) { 356 | this.value = v; 357 | return this; 358 | } 359 | return Leaf(edit, h, k, v); 360 | } 361 | const v = f(); 362 | if (v === nothing) 363 | return this; 364 | ++size.value; 365 | return mergeLeaves(edit, shift, this.hash, this, h, Leaf(edit, h, k, v)); 366 | }; 367 | 368 | const Collision__modify = function (edit, keyEq, shift, f, h, k, size) { 369 | if (h === this.hash) { 370 | const canEdit = canEditNode(edit, this); 371 | const list = updateCollisionList(canEdit, edit, keyEq, this.hash, this.children, f, k, size); 372 | if (list === this.children) 373 | return this; 374 | 375 | return list.length > 1 376 | ? Collision(edit, this.hash, list) 377 | : list[0]; // collapse single element collision list 378 | } 379 | const v = f(); 380 | if (v === nothing) 381 | return this; 382 | ++size.value; 383 | return mergeLeaves(edit, shift, this.hash, this, h, Leaf(edit, h, k, v)); 384 | }; 385 | 386 | const IndexedNode__modify = function (edit, keyEq, shift, f, h, k, size) { 387 | const mask = this.mask; 388 | const children = this.children; 389 | const frag = hashFragment(shift, h); 390 | const bit = toBitmap(frag); 391 | const indx = fromBitmap(mask, bit); 392 | const exists = mask & bit; 393 | const current = exists ? children[indx] : empty; 394 | const child = current._modify(edit, keyEq, shift + SIZE, f, h, k, size); 395 | 396 | if (current === child) 397 | return this; 398 | 399 | const canEdit = canEditNode(edit, this); 400 | let bitmap = mask; 401 | let newChildren; 402 | if (exists && isEmptyNode(child)) { // remove 403 | bitmap &= ~bit; 404 | if (!bitmap) 405 | return empty; 406 | if (children.length <= 2 && isLeaf(children[indx ^ 1])) 407 | return children[indx ^ 1] // collapse 408 | 409 | newChildren = arraySpliceOut(canEdit, indx, children); 410 | } else if (!exists && !isEmptyNode(child)) { // add 411 | if (children.length >= MAX_INDEX_NODE) 412 | return expand(edit, frag, child, mask, children); 413 | 414 | bitmap |= bit; 415 | newChildren = arraySpliceIn(canEdit, indx, child, children); 416 | } else { // modify 417 | newChildren = arrayUpdate(canEdit, indx, child, children); 418 | } 419 | 420 | if (canEdit) { 421 | this.mask = bitmap; 422 | this.children = newChildren; 423 | return this; 424 | } 425 | return IndexedNode(edit, bitmap, newChildren); 426 | }; 427 | 428 | const ArrayNode__modify = function (edit, keyEq, shift, f, h, k, size) { 429 | let count = this.size; 430 | const children = this.children; 431 | const frag = hashFragment(shift, h); 432 | const child = children[frag]; 433 | const newChild = (child || empty)._modify(edit, keyEq, shift + SIZE, f, h, k, size); 434 | 435 | if (child === newChild) 436 | return this; 437 | 438 | const canEdit = canEditNode(edit, this); 439 | let newChildren; 440 | if (isEmptyNode(child) && !isEmptyNode(newChild)) { // add 441 | ++count; 442 | newChildren = arrayUpdate(canEdit, frag, newChild, children); 443 | } else if (!isEmptyNode(child) && isEmptyNode(newChild)) { // remove 444 | --count; 445 | if (count <= MIN_ARRAY_NODE) 446 | return pack(edit, count, frag, children); 447 | newChildren = arrayUpdate(canEdit, frag, empty, children); 448 | } else { // modify 449 | newChildren = arrayUpdate(canEdit, frag, newChild, children); 450 | } 451 | 452 | if (canEdit) { 453 | this.size = count; 454 | this.children = newChildren; 455 | return this; 456 | } 457 | return ArrayNode(edit, count, newChildren); 458 | }; 459 | 460 | empty._modify = (edit, keyEq, shift, f, h, k, size) => { 461 | const v = f(); 462 | if (v === nothing) 463 | return empty; 464 | ++size.value; 465 | return Leaf(edit, h, k, v); 466 | }; 467 | 468 | /* 469 | ******************************************************************************/ 470 | function Map(editable, edit, config, root, size) { 471 | this._editable = editable; 472 | this._edit = edit; 473 | this._config = config; 474 | this._root = root; 475 | this._size = size; 476 | }; 477 | 478 | Map.prototype.setTree = function (newRoot, newSize) { 479 | if (this._editable) { 480 | this._root = newRoot; 481 | this._size = newSize; 482 | return this; 483 | } 484 | return newRoot === this._root 485 | ? this 486 | : new Map(this._editable, this._edit, this._config, newRoot, newSize); 487 | }; 488 | 489 | /* Queries 490 | ******************************************************************************/ 491 | /** 492 | Lookup the value for `key` in `map` using a custom `hash`. 493 | 494 | Returns the value or `alt` if none. 495 | */ 496 | const tryGetHash = hamt.tryGetHash = (alt, hash, key, map) => { 497 | let node = map._root; 498 | let shift = 0; 499 | const keyEq = map._config.keyEq; 500 | while (true) switch (node.type) { 501 | case LEAF: 502 | { 503 | return keyEq(key, node.key) ? node.value : alt; 504 | } 505 | case COLLISION: 506 | { 507 | if (hash === node.hash) { 508 | const children = node.children; 509 | for (let i = 0, len = children.length; i < len; ++i) { 510 | const child = children[i]; 511 | if (keyEq(key, child.key)) 512 | return child.value; 513 | } 514 | } 515 | return alt; 516 | } 517 | case INDEX: 518 | { 519 | const frag = hashFragment(shift, hash); 520 | const bit = toBitmap(frag); 521 | if (node.mask & bit) { 522 | node = node.children[fromBitmap(node.mask, bit)] 523 | shift += SIZE; 524 | break; 525 | } 526 | return alt; 527 | } 528 | case ARRAY: 529 | { 530 | node = node.children[hashFragment(shift, hash)]; 531 | if (node) { 532 | shift += SIZE; 533 | break; 534 | } 535 | return alt; 536 | } 537 | default: 538 | return alt; 539 | } 540 | }; 541 | 542 | Map.prototype.tryGetHash = function (alt, hash, key) { 543 | return tryGetHash(alt, hash, key, this); 544 | }; 545 | 546 | /** 547 | Lookup the value for `key` in `map` using internal hash function. 548 | 549 | @see `tryGetHash` 550 | */ 551 | const tryGet = hamt.tryGet = (alt, key, map) => 552 | tryGetHash(alt, map._config.hash(key), key, map); 553 | 554 | Map.prototype.tryGet = function (alt, key) { 555 | return tryGet(alt, key, this); 556 | }; 557 | 558 | /** 559 | Lookup the value for `key` in `map` using a custom `hash`. 560 | 561 | Returns the value or `undefined` if none. 562 | */ 563 | const getHash = hamt.getHash = (hash, key, map) => 564 | tryGetHash(undefined, hash, key, map); 565 | 566 | Map.prototype.getHash = function (hash, key) { 567 | return getHash(hash, key, this); 568 | }; 569 | 570 | /** 571 | Lookup the value for `key` in `map` using internal hash function. 572 | 573 | @see `get` 574 | */ 575 | const get = hamt.get = (key, map) => 576 | tryGetHash(undefined, map._config.hash(key), key, map); 577 | 578 | Map.prototype.get = function (key, alt) { 579 | return tryGet(alt, key, this); 580 | }; 581 | 582 | /** 583 | Does an entry exist for `key` in `map`? Uses custom `hash`. 584 | */ 585 | const hasHash = hamt.has = (hash, key, map) => 586 | tryGetHash(nothing, hash, key, map) !== nothing; 587 | 588 | Map.prototype.hasHash = function (hash, key) { 589 | return hasHash(hash, key, this); 590 | }; 591 | 592 | /** 593 | Does an entry exist for `key` in `map`? Uses internal hash function. 594 | */ 595 | const has = hamt.has = (key, map) => 596 | hasHash(map._config.hash(key), key, map); 597 | 598 | Map.prototype.has = function (key) { 599 | return has(key, this); 600 | }; 601 | 602 | const defKeyCompare = (x, y) => x === y; 603 | 604 | /** 605 | Create an empty map. 606 | 607 | @param config Configuration. 608 | */ 609 | hamt.make = (config) => 610 | new Map(0, 0, { 611 | keyEq: (config && config.keyEq) || defKeyCompare, 612 | hash: (config && config.hash) || hash 613 | }, empty, 0); 614 | 615 | /** 616 | Empty map. 617 | */ 618 | hamt.empty = hamt.make(); 619 | 620 | /** 621 | Does `map` contain any elements? 622 | */ 623 | const isEmpty = hamt.isEmpty = (map) => 624 | map && !!isEmptyNode(map._root); 625 | 626 | Map.prototype.isEmpty = function () { 627 | return isEmpty(this); 628 | }; 629 | 630 | /* Updates 631 | ******************************************************************************/ 632 | /** 633 | Alter the value stored for `key` in `map` using function `f` using 634 | custom hash. 635 | 636 | `f` is invoked with the current value for `k` if it exists, 637 | or no arguments if no such value exists. `modify` will always either 638 | update or insert a value into the map. 639 | 640 | Returns a map with the modified value. Does not alter `map`. 641 | */ 642 | const modifyHash = hamt.modifyHash = (f, hash, key, map) => { 643 | const size = { value: map._size }; 644 | const newRoot = map._root._modify( 645 | map._editable ? map._edit : NaN, 646 | map._config.keyEq, 647 | 0, 648 | f, 649 | hash, 650 | key, 651 | size); 652 | return map.setTree(newRoot, size.value); 653 | }; 654 | 655 | Map.prototype.modifyHash = function (hash, key, f) { 656 | return modifyHash(f, hash, key, this); 657 | }; 658 | 659 | /** 660 | Alter the value stored for `key` in `map` using function `f` using 661 | internal hash function. 662 | 663 | @see `modifyHash` 664 | */ 665 | const modify = hamt.modify = (f, key, map) => 666 | modifyHash(f, map._config.hash(key), key, map); 667 | 668 | Map.prototype.modify = function (key, f) { 669 | return modify(f, key, this); 670 | }; 671 | 672 | /** 673 | Store `value` for `key` in `map` using custom `hash`. 674 | 675 | Returns a map with the modified value. Does not alter `map`. 676 | */ 677 | const setHash = hamt.setHash = (hash, key, value, map) => 678 | modifyHash(constant(value), hash, key, map); 679 | 680 | Map.prototype.setHash = function (hash, key, value) { 681 | return setHash(hash, key, value, this); 682 | }; 683 | 684 | /** 685 | Store `value` for `key` in `map` using internal hash function. 686 | 687 | @see `setHash` 688 | */ 689 | const set = hamt.set = (key, value, map) => 690 | setHash(map._config.hash(key), key, value, map); 691 | 692 | Map.prototype.set = function (key, value) { 693 | return set(key, value, this); 694 | }; 695 | 696 | /** 697 | Remove the entry for `key` in `map`. 698 | 699 | Returns a map with the value removed. Does not alter `map`. 700 | */ 701 | const del = constant(nothing); 702 | const removeHash = hamt.removeHash = (hash, key, map) => 703 | modifyHash(del, hash, key, map); 704 | 705 | Map.prototype.removeHash = Map.prototype.deleteHash = function (hash, key) { 706 | return removeHash(hash, key, this); 707 | }; 708 | 709 | /** 710 | Remove the entry for `key` in `map` using internal hash function. 711 | 712 | @see `removeHash` 713 | */ 714 | const remove = hamt.remove = (key, map) => 715 | removeHash(map._config.hash(key), key, map); 716 | 717 | Map.prototype.remove = Map.prototype.delete = function (key) { 718 | return remove(key, this); 719 | }; 720 | 721 | /* Mutation 722 | ******************************************************************************/ 723 | /** 724 | Mark `map` as mutable. 725 | */ 726 | const beginMutation = hamt.beginMutation = (map) => 727 | new Map( 728 | map._editable + 1, 729 | map._edit + 1, 730 | map._config, 731 | map._root, 732 | map._size); 733 | 734 | Map.prototype.beginMutation = function () { 735 | return beginMutation(this); 736 | }; 737 | 738 | /** 739 | Mark `map` as immutable. 740 | */ 741 | const endMutation = hamt.endMutation = (map) => { 742 | map._editable = map._editable && map._editable - 1; 743 | return map; 744 | }; 745 | 746 | Map.prototype.endMutation = function () { 747 | return endMutation(this); 748 | }; 749 | 750 | /** 751 | Mutate `map` within the context of `f`. 752 | @param f 753 | @param map HAMT 754 | */ 755 | const mutate = hamt.mutate = (f, map) => { 756 | const transient = beginMutation(map); 757 | f(transient); 758 | return endMutation(transient); 759 | }; 760 | 761 | Map.prototype.mutate = function (f) { 762 | return mutate(f, this); 763 | }; 764 | 765 | /* Traversal 766 | ******************************************************************************/ 767 | /** 768 | Apply a continuation. 769 | */ 770 | const appk = k => 771 | k && lazyVisitChildren(k[0], k[1], k[2], k[3], k[4]); 772 | 773 | /** 774 | Recursively visit all values stored in an array of nodes lazily. 775 | */ 776 | var lazyVisitChildren = (len, children, i, f, k) => { 777 | while (i < len) { 778 | const child = children[i++]; 779 | if (child && !isEmptyNode(child)) 780 | return lazyVisit(child, f, [len, children, i, f, k]); 781 | } 782 | return appk(k); 783 | }; 784 | 785 | /** 786 | Recursively visit all values stored in `node` lazily. 787 | */ 788 | const lazyVisit = (node, f, k) => { 789 | switch (node.type) { 790 | case LEAF: 791 | return { 792 | value: f(node), 793 | rest: k 794 | }; 795 | 796 | case COLLISION: 797 | case ARRAY: 798 | case INDEX: 799 | const children = node.children; 800 | return lazyVisitChildren(children.length, children, 0, f, k); 801 | 802 | default: 803 | return appk(k); 804 | } 805 | }; 806 | 807 | const DONE = { 808 | done: true 809 | }; 810 | 811 | /** 812 | Javascript iterator over a map. 813 | */ 814 | function MapIterator(v) { 815 | this.v = v; 816 | }; 817 | 818 | MapIterator.prototype.next = function () { 819 | if (!this.v) 820 | return DONE; 821 | const v0 = this.v; 822 | this.v = appk(v0.rest); 823 | return v0; 824 | }; 825 | 826 | MapIterator.prototype[Symbol.iterator] = function () { 827 | return this; 828 | }; 829 | 830 | /** 831 | Lazily visit each value in map with function `f`. 832 | */ 833 | const visit = (map, f) => 834 | new MapIterator(lazyVisit(map._root, f)); 835 | 836 | /** 837 | Get a Javascsript iterator of `map`. 838 | 839 | Iterates over `[key, value]` arrays. 840 | */ 841 | const buildPairs = (x) => [x.key, x.value]; 842 | const entries = hamt.entries = (map) => 843 | visit(map, buildPairs); 844 | 845 | Map.prototype.entries = Map.prototype[Symbol.iterator] = function () { 846 | return entries(this); 847 | }; 848 | 849 | /** 850 | Get array of all keys in `map`. 851 | 852 | Order is not guaranteed. 853 | */ 854 | const buildKeys = (x) => x.key; 855 | const keys = hamt.keys = (map) => 856 | visit(map, buildKeys); 857 | 858 | Map.prototype.keys = function () { 859 | return keys(this); 860 | } 861 | 862 | /** 863 | Get array of all values in `map`. 864 | 865 | Order is not guaranteed, duplicates are preserved. 866 | */ 867 | const buildValues = x => x.value; 868 | const values = hamt.values = Map.prototype.values = map => 869 | visit(map, buildValues); 870 | 871 | Map.prototype.values = function () { 872 | return values(this); 873 | }; 874 | 875 | /* Fold 876 | ******************************************************************************/ 877 | /** 878 | Visit every entry in the map, aggregating data. 879 | 880 | Order of nodes is not guaranteed. 881 | 882 | @param f Function mapping accumulated value, value, and key to new value. 883 | @param z Starting value. 884 | @param m HAMT 885 | */ 886 | const fold = hamt.fold = (f, z, m) => { 887 | const root = m._root; 888 | if (root.type === LEAF) 889 | return f(z, root.value, root.key); 890 | 891 | const toVisit = [root.children]; 892 | let children; 893 | while (children = toVisit.pop()) { 894 | for (let i = 0, len = children.length; i < len;) { 895 | const child = children[i++]; 896 | if (child && child.type) { 897 | if (child.type === LEAF) 898 | z = f(z, child.value, child.key); 899 | else 900 | toVisit.push(child.children); 901 | } 902 | } 903 | } 904 | return z; 905 | }; 906 | 907 | Map.prototype.fold = function (f, z) { 908 | return fold(f, z, this); 909 | }; 910 | 911 | /** 912 | Visit every entry in the map, aggregating data. 913 | 914 | Order of nodes is not guaranteed. 915 | 916 | @param f Function invoked with value and key 917 | @param map HAMT 918 | */ 919 | const forEach = hamt.forEach = (f, map) => 920 | fold((_, value, key) => f(value, key, map), null, map); 921 | 922 | Map.prototype.forEach = function (f) { 923 | return forEach(f, this); 924 | }; 925 | 926 | /* Aggregate 927 | ******************************************************************************/ 928 | /** 929 | Get the number of entries in `map`. 930 | */ 931 | const count = hamt.count = map => 932 | map._size; 933 | 934 | Map.prototype.count = function () { 935 | return count(this); 936 | }; 937 | 938 | Object.defineProperty(Map.prototype, 'size', { 939 | get: Map.prototype.count 940 | }); 941 | 942 | /* Export 943 | ******************************************************************************/ 944 | if (typeof module !== 'undefined' && module.exports) { 945 | module.exports = hamt; 946 | } else if (typeof define === 'function' && define.amd) { 947 | define('hamt', [], () => hamt); 948 | } else { 949 | this.hamt = hamt; 950 | } 951 | -------------------------------------------------------------------------------- /hamt.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 4 | 5 | /** 6 | @fileOverview Hash Array Mapped Trie. 7 | 8 | Code based on: https://github.com/exclipy/pdata 9 | */ 10 | var hamt = {}; // export 11 | 12 | /* Configuration 13 | ******************************************************************************/ 14 | var SIZE = 5; 15 | 16 | var BUCKET_SIZE = Math.pow(2, SIZE); 17 | 18 | var MASK = BUCKET_SIZE - 1; 19 | 20 | var MAX_INDEX_NODE = BUCKET_SIZE / 2; 21 | 22 | var MIN_ARRAY_NODE = BUCKET_SIZE / 4; 23 | 24 | /* 25 | ******************************************************************************/ 26 | var nothing = {}; 27 | 28 | var constant = function constant(x) { 29 | return function () { 30 | return x; 31 | }; 32 | }; 33 | 34 | /** 35 | Get 32 bit hash of string. 36 | 37 | Based on: 38 | http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery 39 | */ 40 | var hash = hamt.hash = function (str) { 41 | var type = typeof str === 'undefined' ? 'undefined' : _typeof(str); 42 | if (type === 'number') return str; 43 | if (type !== 'string') str += ''; 44 | 45 | var hash = 0; 46 | for (var i = 0, len = str.length; i < len; ++i) { 47 | var c = str.charCodeAt(i); 48 | hash = (hash << 5) - hash + c | 0; 49 | } 50 | return hash; 51 | }; 52 | 53 | /* Bit Ops 54 | ******************************************************************************/ 55 | /** 56 | Hamming weight. 57 | 58 | Taken from: http://jsperf.com/hamming-weight 59 | */ 60 | var popcount = function popcount(x) { 61 | x -= x >> 1 & 0x55555555; 62 | x = (x & 0x33333333) + (x >> 2 & 0x33333333); 63 | x = x + (x >> 4) & 0x0f0f0f0f; 64 | x += x >> 8; 65 | x += x >> 16; 66 | return x & 0x7f; 67 | }; 68 | 69 | var hashFragment = function hashFragment(shift, h) { 70 | return h >>> shift & MASK; 71 | }; 72 | 73 | var toBitmap = function toBitmap(x) { 74 | return 1 << x; 75 | }; 76 | 77 | var fromBitmap = function fromBitmap(bitmap, bit) { 78 | return popcount(bitmap & bit - 1); 79 | }; 80 | 81 | /* Array Ops 82 | ******************************************************************************/ 83 | /** 84 | Set a value in an array. 85 | 86 | @param mutate Should the input array be mutated? 87 | @param at Index to change. 88 | @param v New value 89 | @param arr Array. 90 | */ 91 | var arrayUpdate = function arrayUpdate(mutate, at, v, arr) { 92 | var out = arr; 93 | if (!mutate) { 94 | var len = arr.length; 95 | out = new Array(len); 96 | for (var i = 0; i < len; ++i) { 97 | out[i] = arr[i]; 98 | } 99 | } 100 | out[at] = v; 101 | return out; 102 | }; 103 | 104 | /** 105 | Remove a value from an array. 106 | 107 | @param mutate Should the input array be mutated? 108 | @param at Index to remove. 109 | @param arr Array. 110 | */ 111 | var arraySpliceOut = function arraySpliceOut(mutate, at, arr) { 112 | var newLen = arr.length - 1; 113 | var i = 0; 114 | var g = 0; 115 | var out = arr; 116 | if (mutate) { 117 | i = g = at; 118 | } else { 119 | out = new Array(newLen); 120 | while (i < at) { 121 | out[g++] = arr[i++]; 122 | } 123 | } 124 | ++i; 125 | while (i <= newLen) { 126 | out[g++] = arr[i++]; 127 | }if (mutate) { 128 | out.length = newLen; 129 | } 130 | return out; 131 | }; 132 | 133 | /** 134 | Insert a value into an array. 135 | 136 | @param mutate Should the input array be mutated? 137 | @param at Index to insert at. 138 | @param v Value to insert, 139 | @param arr Array. 140 | */ 141 | var arraySpliceIn = function arraySpliceIn(mutate, at, v, arr) { 142 | var len = arr.length; 143 | if (mutate) { 144 | var _i = len; 145 | while (_i >= at) { 146 | arr[_i--] = arr[_i]; 147 | }arr[at] = v; 148 | return arr; 149 | } 150 | var i = 0, 151 | g = 0; 152 | var out = new Array(len + 1); 153 | while (i < at) { 154 | out[g++] = arr[i++]; 155 | }out[at] = v; 156 | while (i < len) { 157 | out[++g] = arr[i++]; 158 | }return out; 159 | }; 160 | 161 | /* Node Structures 162 | ******************************************************************************/ 163 | var LEAF = 1; 164 | var COLLISION = 2; 165 | var INDEX = 3; 166 | var ARRAY = 4; 167 | 168 | /** 169 | Empty node. 170 | */ 171 | var empty = { 172 | __hamt_isEmpty: true 173 | }; 174 | 175 | var isEmptyNode = function isEmptyNode(x) { 176 | return x === empty || x && x.__hamt_isEmpty; 177 | }; 178 | 179 | /** 180 | Leaf holding a value. 181 | 182 | @member edit Edit of the node. 183 | @member hash Hash of key. 184 | @member key Key. 185 | @member value Value stored. 186 | */ 187 | var Leaf = function Leaf(edit, hash, key, value) { 188 | return { 189 | type: LEAF, 190 | edit: edit, 191 | hash: hash, 192 | key: key, 193 | value: value, 194 | _modify: Leaf__modify 195 | }; 196 | }; 197 | 198 | /** 199 | Leaf holding multiple values with the same hash but different keys. 200 | 201 | @member edit Edit of the node. 202 | @member hash Hash of key. 203 | @member children Array of collision children node. 204 | */ 205 | var Collision = function Collision(edit, hash, children) { 206 | return { 207 | type: COLLISION, 208 | edit: edit, 209 | hash: hash, 210 | children: children, 211 | _modify: Collision__modify 212 | }; 213 | }; 214 | 215 | /** 216 | Internal node with a sparse set of children. 217 | 218 | Uses a bitmap and array to pack children. 219 | 220 | @member edit Edit of the node. 221 | @member mask Bitmap that encode the positions of children in the array. 222 | @member children Array of child nodes. 223 | */ 224 | var IndexedNode = function IndexedNode(edit, mask, children) { 225 | return { 226 | type: INDEX, 227 | edit: edit, 228 | mask: mask, 229 | children: children, 230 | _modify: IndexedNode__modify 231 | }; 232 | }; 233 | 234 | /** 235 | Internal node with many children. 236 | 237 | @member edit Edit of the node. 238 | @member size Number of children. 239 | @member children Array of child nodes. 240 | */ 241 | var ArrayNode = function ArrayNode(edit, size, children) { 242 | return { 243 | type: ARRAY, 244 | edit: edit, 245 | size: size, 246 | children: children, 247 | _modify: ArrayNode__modify 248 | }; 249 | }; 250 | 251 | /** 252 | Is `node` a leaf node? 253 | */ 254 | var isLeaf = function isLeaf(node) { 255 | return node === empty || node.type === LEAF || node.type === COLLISION; 256 | }; 257 | 258 | /* Internal node operations. 259 | ******************************************************************************/ 260 | /** 261 | Expand an indexed node into an array node. 262 | 263 | @param edit Current edit. 264 | @param frag Index of added child. 265 | @param child Added child. 266 | @param mask Index node mask before child added. 267 | @param subNodes Index node children before child added. 268 | */ 269 | var expand = function expand(edit, frag, child, bitmap, subNodes) { 270 | var arr = []; 271 | var bit = bitmap; 272 | var count = 0; 273 | for (var i = 0; bit; ++i) { 274 | if (bit & 1) arr[i] = subNodes[count++]; 275 | bit >>>= 1; 276 | } 277 | arr[frag] = child; 278 | return ArrayNode(edit, count + 1, arr); 279 | }; 280 | 281 | /** 282 | Collapse an array node into a indexed node. 283 | 284 | @param edit Current edit. 285 | @param count Number of elements in new array. 286 | @param removed Index of removed element. 287 | @param elements Array node children before remove. 288 | */ 289 | var pack = function pack(edit, count, removed, elements) { 290 | var children = new Array(count - 1); 291 | var g = 0; 292 | var bitmap = 0; 293 | for (var i = 0, len = elements.length; i < len; ++i) { 294 | if (i !== removed) { 295 | var elem = elements[i]; 296 | if (elem && !isEmptyNode(elem)) { 297 | children[g++] = elem; 298 | bitmap |= 1 << i; 299 | } 300 | } 301 | } 302 | return IndexedNode(edit, bitmap, children); 303 | }; 304 | 305 | /** 306 | Merge two leaf nodes. 307 | 308 | @param shift Current shift. 309 | @param h1 Node 1 hash. 310 | @param n1 Node 1. 311 | @param h2 Node 2 hash. 312 | @param n2 Node 2. 313 | */ 314 | var mergeLeaves = function mergeLeaves(edit, shift, h1, n1, h2, n2) { 315 | if (h1 === h2) return Collision(edit, h1, [n2, n1]); 316 | 317 | var subH1 = hashFragment(shift, h1); 318 | var subH2 = hashFragment(shift, h2); 319 | return IndexedNode(edit, toBitmap(subH1) | toBitmap(subH2), subH1 === subH2 ? [mergeLeaves(edit, shift + SIZE, h1, n1, h2, n2)] : subH1 < subH2 ? [n1, n2] : [n2, n1]); 320 | }; 321 | 322 | /** 323 | Update an entry in a collision list. 324 | 325 | @param mutate Should mutation be used? 326 | @param edit Current edit. 327 | @param keyEq Key compare function. 328 | @param hash Hash of collision. 329 | @param list Collision list. 330 | @param f Update function. 331 | @param k Key to update. 332 | @param size Size ref. 333 | */ 334 | var updateCollisionList = function updateCollisionList(mutate, edit, keyEq, h, list, f, k, size) { 335 | var len = list.length; 336 | for (var i = 0; i < len; ++i) { 337 | var child = list[i]; 338 | if (keyEq(k, child.key)) { 339 | var value = child.value; 340 | var _newValue = f(value); 341 | if (_newValue === value) return list; 342 | 343 | if (_newValue === nothing) { 344 | --size.value; 345 | return arraySpliceOut(mutate, i, list); 346 | } 347 | return arrayUpdate(mutate, i, Leaf(edit, h, k, _newValue), list); 348 | } 349 | } 350 | 351 | var newValue = f(); 352 | if (newValue === nothing) return list; 353 | ++size.value; 354 | return arrayUpdate(mutate, len, Leaf(edit, h, k, newValue), list); 355 | }; 356 | 357 | var canEditNode = function canEditNode(edit, node) { 358 | return edit === node.edit; 359 | }; 360 | 361 | /* Editing 362 | ******************************************************************************/ 363 | var Leaf__modify = function Leaf__modify(edit, keyEq, shift, f, h, k, size) { 364 | if (keyEq(k, this.key)) { 365 | var _v = f(this.value); 366 | if (_v === this.value) return this;else if (_v === nothing) { 367 | --size.value; 368 | return empty; 369 | } 370 | if (canEditNode(edit, this)) { 371 | this.value = _v; 372 | return this; 373 | } 374 | return Leaf(edit, h, k, _v); 375 | } 376 | var v = f(); 377 | if (v === nothing) return this; 378 | ++size.value; 379 | return mergeLeaves(edit, shift, this.hash, this, h, Leaf(edit, h, k, v)); 380 | }; 381 | 382 | var Collision__modify = function Collision__modify(edit, keyEq, shift, f, h, k, size) { 383 | if (h === this.hash) { 384 | var canEdit = canEditNode(edit, this); 385 | var list = updateCollisionList(canEdit, edit, keyEq, this.hash, this.children, f, k, size); 386 | if (list === this.children) return this; 387 | 388 | return list.length > 1 ? Collision(edit, this.hash, list) : list[0]; // collapse single element collision list 389 | } 390 | var v = f(); 391 | if (v === nothing) return this; 392 | ++size.value; 393 | return mergeLeaves(edit, shift, this.hash, this, h, Leaf(edit, h, k, v)); 394 | }; 395 | 396 | var IndexedNode__modify = function IndexedNode__modify(edit, keyEq, shift, f, h, k, size) { 397 | var mask = this.mask; 398 | var children = this.children; 399 | var frag = hashFragment(shift, h); 400 | var bit = toBitmap(frag); 401 | var indx = fromBitmap(mask, bit); 402 | var exists = mask & bit; 403 | var current = exists ? children[indx] : empty; 404 | var child = current._modify(edit, keyEq, shift + SIZE, f, h, k, size); 405 | 406 | if (current === child) return this; 407 | 408 | var canEdit = canEditNode(edit, this); 409 | var bitmap = mask; 410 | var newChildren = void 0; 411 | if (exists && isEmptyNode(child)) { 412 | // remove 413 | bitmap &= ~bit; 414 | if (!bitmap) return empty; 415 | if (children.length <= 2 && isLeaf(children[indx ^ 1])) return children[indx ^ 1]; // collapse 416 | 417 | newChildren = arraySpliceOut(canEdit, indx, children); 418 | } else if (!exists && !isEmptyNode(child)) { 419 | // add 420 | if (children.length >= MAX_INDEX_NODE) return expand(edit, frag, child, mask, children); 421 | 422 | bitmap |= bit; 423 | newChildren = arraySpliceIn(canEdit, indx, child, children); 424 | } else { 425 | // modify 426 | newChildren = arrayUpdate(canEdit, indx, child, children); 427 | } 428 | 429 | if (canEdit) { 430 | this.mask = bitmap; 431 | this.children = newChildren; 432 | return this; 433 | } 434 | return IndexedNode(edit, bitmap, newChildren); 435 | }; 436 | 437 | var ArrayNode__modify = function ArrayNode__modify(edit, keyEq, shift, f, h, k, size) { 438 | var count = this.size; 439 | var children = this.children; 440 | var frag = hashFragment(shift, h); 441 | var child = children[frag]; 442 | var newChild = (child || empty)._modify(edit, keyEq, shift + SIZE, f, h, k, size); 443 | 444 | if (child === newChild) return this; 445 | 446 | var canEdit = canEditNode(edit, this); 447 | var newChildren = void 0; 448 | if (isEmptyNode(child) && !isEmptyNode(newChild)) { 449 | // add 450 | ++count; 451 | newChildren = arrayUpdate(canEdit, frag, newChild, children); 452 | } else if (!isEmptyNode(child) && isEmptyNode(newChild)) { 453 | // remove 454 | --count; 455 | if (count <= MIN_ARRAY_NODE) return pack(edit, count, frag, children); 456 | newChildren = arrayUpdate(canEdit, frag, empty, children); 457 | } else { 458 | // modify 459 | newChildren = arrayUpdate(canEdit, frag, newChild, children); 460 | } 461 | 462 | if (canEdit) { 463 | this.size = count; 464 | this.children = newChildren; 465 | return this; 466 | } 467 | return ArrayNode(edit, count, newChildren); 468 | }; 469 | 470 | empty._modify = function (edit, keyEq, shift, f, h, k, size) { 471 | var v = f(); 472 | if (v === nothing) return empty; 473 | ++size.value; 474 | return Leaf(edit, h, k, v); 475 | }; 476 | 477 | /* 478 | ******************************************************************************/ 479 | function Map(editable, edit, config, root, size) { 480 | this._editable = editable; 481 | this._edit = edit; 482 | this._config = config; 483 | this._root = root; 484 | this._size = size; 485 | }; 486 | 487 | Map.prototype.setTree = function (newRoot, newSize) { 488 | if (this._editable) { 489 | this._root = newRoot; 490 | this._size = newSize; 491 | return this; 492 | } 493 | return newRoot === this._root ? this : new Map(this._editable, this._edit, this._config, newRoot, newSize); 494 | }; 495 | 496 | /* Queries 497 | ******************************************************************************/ 498 | /** 499 | Lookup the value for `key` in `map` using a custom `hash`. 500 | 501 | Returns the value or `alt` if none. 502 | */ 503 | var tryGetHash = hamt.tryGetHash = function (alt, hash, key, map) { 504 | var node = map._root; 505 | var shift = 0; 506 | var keyEq = map._config.keyEq; 507 | while (true) { 508 | switch (node.type) { 509 | case LEAF: 510 | { 511 | return keyEq(key, node.key) ? node.value : alt; 512 | } 513 | case COLLISION: 514 | { 515 | if (hash === node.hash) { 516 | var children = node.children; 517 | for (var i = 0, len = children.length; i < len; ++i) { 518 | var child = children[i]; 519 | if (keyEq(key, child.key)) return child.value; 520 | } 521 | } 522 | return alt; 523 | } 524 | case INDEX: 525 | { 526 | var frag = hashFragment(shift, hash); 527 | var bit = toBitmap(frag); 528 | if (node.mask & bit) { 529 | node = node.children[fromBitmap(node.mask, bit)]; 530 | shift += SIZE; 531 | break; 532 | } 533 | return alt; 534 | } 535 | case ARRAY: 536 | { 537 | node = node.children[hashFragment(shift, hash)]; 538 | if (node) { 539 | shift += SIZE; 540 | break; 541 | } 542 | return alt; 543 | } 544 | default: 545 | return alt; 546 | } 547 | } 548 | }; 549 | 550 | Map.prototype.tryGetHash = function (alt, hash, key) { 551 | return tryGetHash(alt, hash, key, this); 552 | }; 553 | 554 | /** 555 | Lookup the value for `key` in `map` using internal hash function. 556 | 557 | @see `tryGetHash` 558 | */ 559 | var tryGet = hamt.tryGet = function (alt, key, map) { 560 | return tryGetHash(alt, map._config.hash(key), key, map); 561 | }; 562 | 563 | Map.prototype.tryGet = function (alt, key) { 564 | return tryGet(alt, key, this); 565 | }; 566 | 567 | /** 568 | Lookup the value for `key` in `map` using a custom `hash`. 569 | 570 | Returns the value or `undefined` if none. 571 | */ 572 | var getHash = hamt.getHash = function (hash, key, map) { 573 | return tryGetHash(undefined, hash, key, map); 574 | }; 575 | 576 | Map.prototype.getHash = function (hash, key) { 577 | return getHash(hash, key, this); 578 | }; 579 | 580 | /** 581 | Lookup the value for `key` in `map` using internal hash function. 582 | 583 | @see `get` 584 | */ 585 | var get = hamt.get = function (key, map) { 586 | return tryGetHash(undefined, map._config.hash(key), key, map); 587 | }; 588 | 589 | Map.prototype.get = function (key, alt) { 590 | return tryGet(alt, key, this); 591 | }; 592 | 593 | /** 594 | Does an entry exist for `key` in `map`? Uses custom `hash`. 595 | */ 596 | var hasHash = hamt.has = function (hash, key, map) { 597 | return tryGetHash(nothing, hash, key, map) !== nothing; 598 | }; 599 | 600 | Map.prototype.hasHash = function (hash, key) { 601 | return hasHash(hash, key, this); 602 | }; 603 | 604 | /** 605 | Does an entry exist for `key` in `map`? Uses internal hash function. 606 | */ 607 | var has = hamt.has = function (key, map) { 608 | return hasHash(map._config.hash(key), key, map); 609 | }; 610 | 611 | Map.prototype.has = function (key) { 612 | return has(key, this); 613 | }; 614 | 615 | var defKeyCompare = function defKeyCompare(x, y) { 616 | return x === y; 617 | }; 618 | 619 | /** 620 | Create an empty map. 621 | 622 | @param config Configuration. 623 | */ 624 | hamt.make = function (config) { 625 | return new Map(0, 0, { 626 | keyEq: config && config.keyEq || defKeyCompare, 627 | hash: config && config.hash || hash 628 | }, empty, 0); 629 | }; 630 | 631 | /** 632 | Empty map. 633 | */ 634 | hamt.empty = hamt.make(); 635 | 636 | /** 637 | Does `map` contain any elements? 638 | */ 639 | var isEmpty = hamt.isEmpty = function (map) { 640 | return map && !!isEmptyNode(map._root); 641 | }; 642 | 643 | Map.prototype.isEmpty = function () { 644 | return isEmpty(this); 645 | }; 646 | 647 | /* Updates 648 | ******************************************************************************/ 649 | /** 650 | Alter the value stored for `key` in `map` using function `f` using 651 | custom hash. 652 | 653 | `f` is invoked with the current value for `k` if it exists, 654 | or no arguments if no such value exists. `modify` will always either 655 | update or insert a value into the map. 656 | 657 | Returns a map with the modified value. Does not alter `map`. 658 | */ 659 | var modifyHash = hamt.modifyHash = function (f, hash, key, map) { 660 | var size = { value: map._size }; 661 | var newRoot = map._root._modify(map._editable ? map._edit : NaN, map._config.keyEq, 0, f, hash, key, size); 662 | return map.setTree(newRoot, size.value); 663 | }; 664 | 665 | Map.prototype.modifyHash = function (hash, key, f) { 666 | return modifyHash(f, hash, key, this); 667 | }; 668 | 669 | /** 670 | Alter the value stored for `key` in `map` using function `f` using 671 | internal hash function. 672 | 673 | @see `modifyHash` 674 | */ 675 | var modify = hamt.modify = function (f, key, map) { 676 | return modifyHash(f, map._config.hash(key), key, map); 677 | }; 678 | 679 | Map.prototype.modify = function (key, f) { 680 | return modify(f, key, this); 681 | }; 682 | 683 | /** 684 | Store `value` for `key` in `map` using custom `hash`. 685 | 686 | Returns a map with the modified value. Does not alter `map`. 687 | */ 688 | var setHash = hamt.setHash = function (hash, key, value, map) { 689 | return modifyHash(constant(value), hash, key, map); 690 | }; 691 | 692 | Map.prototype.setHash = function (hash, key, value) { 693 | return setHash(hash, key, value, this); 694 | }; 695 | 696 | /** 697 | Store `value` for `key` in `map` using internal hash function. 698 | 699 | @see `setHash` 700 | */ 701 | var set = hamt.set = function (key, value, map) { 702 | return setHash(map._config.hash(key), key, value, map); 703 | }; 704 | 705 | Map.prototype.set = function (key, value) { 706 | return set(key, value, this); 707 | }; 708 | 709 | /** 710 | Remove the entry for `key` in `map`. 711 | 712 | Returns a map with the value removed. Does not alter `map`. 713 | */ 714 | var del = constant(nothing); 715 | var removeHash = hamt.removeHash = function (hash, key, map) { 716 | return modifyHash(del, hash, key, map); 717 | }; 718 | 719 | Map.prototype.removeHash = Map.prototype.deleteHash = function (hash, key) { 720 | return removeHash(hash, key, this); 721 | }; 722 | 723 | /** 724 | Remove the entry for `key` in `map` using internal hash function. 725 | 726 | @see `removeHash` 727 | */ 728 | var remove = hamt.remove = function (key, map) { 729 | return removeHash(map._config.hash(key), key, map); 730 | }; 731 | 732 | Map.prototype.remove = Map.prototype.delete = function (key) { 733 | return remove(key, this); 734 | }; 735 | 736 | /* Mutation 737 | ******************************************************************************/ 738 | /** 739 | Mark `map` as mutable. 740 | */ 741 | var beginMutation = hamt.beginMutation = function (map) { 742 | return new Map(map._editable + 1, map._edit + 1, map._config, map._root, map._size); 743 | }; 744 | 745 | Map.prototype.beginMutation = function () { 746 | return beginMutation(this); 747 | }; 748 | 749 | /** 750 | Mark `map` as immutable. 751 | */ 752 | var endMutation = hamt.endMutation = function (map) { 753 | map._editable = map._editable && map._editable - 1; 754 | return map; 755 | }; 756 | 757 | Map.prototype.endMutation = function () { 758 | return endMutation(this); 759 | }; 760 | 761 | /** 762 | Mutate `map` within the context of `f`. 763 | @param f 764 | @param map HAMT 765 | */ 766 | var mutate = hamt.mutate = function (f, map) { 767 | var transient = beginMutation(map); 768 | f(transient); 769 | return endMutation(transient); 770 | }; 771 | 772 | Map.prototype.mutate = function (f) { 773 | return mutate(f, this); 774 | }; 775 | 776 | /* Traversal 777 | ******************************************************************************/ 778 | /** 779 | Apply a continuation. 780 | */ 781 | var appk = function appk(k) { 782 | return k && lazyVisitChildren(k[0], k[1], k[2], k[3], k[4]); 783 | }; 784 | 785 | /** 786 | Recursively visit all values stored in an array of nodes lazily. 787 | */ 788 | var lazyVisitChildren = function lazyVisitChildren(len, children, i, f, k) { 789 | while (i < len) { 790 | var child = children[i++]; 791 | if (child && !isEmptyNode(child)) return lazyVisit(child, f, [len, children, i, f, k]); 792 | } 793 | return appk(k); 794 | }; 795 | 796 | /** 797 | Recursively visit all values stored in `node` lazily. 798 | */ 799 | var lazyVisit = function lazyVisit(node, f, k) { 800 | switch (node.type) { 801 | case LEAF: 802 | return { 803 | value: f(node), 804 | rest: k 805 | }; 806 | 807 | case COLLISION: 808 | case ARRAY: 809 | case INDEX: 810 | var children = node.children; 811 | return lazyVisitChildren(children.length, children, 0, f, k); 812 | 813 | default: 814 | return appk(k); 815 | } 816 | }; 817 | 818 | var DONE = { 819 | done: true 820 | }; 821 | 822 | /** 823 | Javascript iterator over a map. 824 | */ 825 | function MapIterator(v) { 826 | this.v = v; 827 | }; 828 | 829 | MapIterator.prototype.next = function () { 830 | if (!this.v) return DONE; 831 | var v0 = this.v; 832 | this.v = appk(v0.rest); 833 | return v0; 834 | }; 835 | 836 | MapIterator.prototype[Symbol.iterator] = function () { 837 | return this; 838 | }; 839 | 840 | /** 841 | Lazily visit each value in map with function `f`. 842 | */ 843 | var visit = function visit(map, f) { 844 | return new MapIterator(lazyVisit(map._root, f)); 845 | }; 846 | 847 | /** 848 | Get a Javascsript iterator of `map`. 849 | 850 | Iterates over `[key, value]` arrays. 851 | */ 852 | var buildPairs = function buildPairs(x) { 853 | return [x.key, x.value]; 854 | }; 855 | var entries = hamt.entries = function (map) { 856 | return visit(map, buildPairs); 857 | }; 858 | 859 | Map.prototype.entries = Map.prototype[Symbol.iterator] = function () { 860 | return entries(this); 861 | }; 862 | 863 | /** 864 | Get array of all keys in `map`. 865 | 866 | Order is not guaranteed. 867 | */ 868 | var buildKeys = function buildKeys(x) { 869 | return x.key; 870 | }; 871 | var keys = hamt.keys = function (map) { 872 | return visit(map, buildKeys); 873 | }; 874 | 875 | Map.prototype.keys = function () { 876 | return keys(this); 877 | }; 878 | 879 | /** 880 | Get array of all values in `map`. 881 | 882 | Order is not guaranteed, duplicates are preserved. 883 | */ 884 | var buildValues = function buildValues(x) { 885 | return x.value; 886 | }; 887 | var values = hamt.values = Map.prototype.values = function (map) { 888 | return visit(map, buildValues); 889 | }; 890 | 891 | Map.prototype.values = function () { 892 | return values(this); 893 | }; 894 | 895 | /* Fold 896 | ******************************************************************************/ 897 | /** 898 | Visit every entry in the map, aggregating data. 899 | 900 | Order of nodes is not guaranteed. 901 | 902 | @param f Function mapping accumulated value, value, and key to new value. 903 | @param z Starting value. 904 | @param m HAMT 905 | */ 906 | var fold = hamt.fold = function (f, z, m) { 907 | var root = m._root; 908 | if (root.type === LEAF) return f(z, root.value, root.key); 909 | 910 | var toVisit = [root.children]; 911 | var children = void 0; 912 | while (children = toVisit.pop()) { 913 | for (var i = 0, len = children.length; i < len;) { 914 | var child = children[i++]; 915 | if (child && child.type) { 916 | if (child.type === LEAF) z = f(z, child.value, child.key);else toVisit.push(child.children); 917 | } 918 | } 919 | } 920 | return z; 921 | }; 922 | 923 | Map.prototype.fold = function (f, z) { 924 | return fold(f, z, this); 925 | }; 926 | 927 | /** 928 | Visit every entry in the map, aggregating data. 929 | 930 | Order of nodes is not guaranteed. 931 | 932 | @param f Function invoked with value and key 933 | @param map HAMT 934 | */ 935 | var forEach = hamt.forEach = function (f, map) { 936 | return fold(function (_, value, key) { 937 | return f(value, key, map); 938 | }, null, map); 939 | }; 940 | 941 | Map.prototype.forEach = function (f) { 942 | return forEach(f, this); 943 | }; 944 | 945 | /* Aggregate 946 | ******************************************************************************/ 947 | /** 948 | Get the number of entries in `map`. 949 | */ 950 | var count = hamt.count = function (map) { 951 | return map._size; 952 | }; 953 | 954 | Map.prototype.count = function () { 955 | return count(this); 956 | }; 957 | 958 | Object.defineProperty(Map.prototype, 'size', { 959 | get: Map.prototype.count 960 | }); 961 | 962 | /* Export 963 | ******************************************************************************/ 964 | if (typeof module !== 'undefined' && module.exports) { 965 | module.exports = hamt; 966 | } else if (typeof define === 'function' && define.amd) { 967 | define('hamt', [], function () { 968 | return hamt; 969 | }); 970 | } else { 971 | undefined.hamt = hamt; 972 | } 973 | //# sourceMappingURL=hamt.js.map 974 | -------------------------------------------------------------------------------- /hamt.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["hamt.js"],"names":["hamt","SIZE","BUCKET_SIZE","Math","pow","MASK","MAX_INDEX_NODE","MIN_ARRAY_NODE","nothing","constant","x","hash","type","str","i","len","length","c","charCodeAt","popcount","hashFragment","shift","h","toBitmap","fromBitmap","bitmap","bit","arrayUpdate","mutate","at","v","arr","out","Array","arraySpliceOut","newLen","g","arraySpliceIn","LEAF","COLLISION","INDEX","ARRAY","empty","__hamt_isEmpty","isEmptyNode","Leaf","edit","key","value","_modify","Leaf__modify","Collision","children","Collision__modify","IndexedNode","mask","IndexedNode__modify","ArrayNode","size","ArrayNode__modify","isLeaf","node","expand","frag","child","subNodes","count","pack","removed","elements","elem","mergeLeaves","h1","n1","h2","n2","subH1","subH2","updateCollisionList","keyEq","list","f","k","newValue","canEditNode","canEdit","indx","exists","current","newChildren","newChild","Map","editable","config","root","_editable","_edit","_config","_root","_size","prototype","setTree","newRoot","newSize","tryGetHash","alt","map","tryGet","getHash","undefined","get","hasHash","has","defKeyCompare","y","make","isEmpty","modifyHash","NaN","modify","setHash","set","del","removeHash","deleteHash","remove","delete","beginMutation","endMutation","transient","appk","lazyVisitChildren","lazyVisit","rest","DONE","done","MapIterator","next","v0","Symbol","iterator","visit","buildPairs","entries","buildKeys","keys","buildValues","values","fold","z","m","toVisit","pop","push","forEach","_","Object","defineProperty","module","exports","define","amd"],"mappings":"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","file":"hamt.js","sourcesContent":["/**\n @fileOverview Hash Array Mapped Trie.\n\n Code based on: https://github.com/exclipy/pdata\n*/\nconst hamt = {}; // export\n\n/* Configuration\n ******************************************************************************/\nconst SIZE = 5;\n\nconst BUCKET_SIZE = Math.pow(2, SIZE);\n\nconst MASK = BUCKET_SIZE - 1;\n\nconst MAX_INDEX_NODE = BUCKET_SIZE / 2;\n\nconst MIN_ARRAY_NODE = BUCKET_SIZE / 4;\n\n/*\n ******************************************************************************/\nconst nothing = ({});\n\nconst constant = x => () => x;\n\n/**\n Get 32 bit hash of string.\n\n Based on:\n http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery\n*/\nconst hash = hamt.hash = str => {\n const type = typeof str;\n if (type === 'number')\n return str;\n if (type !== 'string')\n str += '';\n\n let hash = 0;\n for (let i = 0, len = str.length; i < len; ++i) {\n const c = str.charCodeAt(i);\n hash = (((hash << 5) - hash) + c) | 0;\n }\n return hash;\n};\n\n/* Bit Ops\n ******************************************************************************/\n/**\n Hamming weight.\n\n Taken from: http://jsperf.com/hamming-weight\n*/\nconst popcount = (x) => {\n x -= ((x >> 1) & 0x55555555);\n x = (x & 0x33333333) + ((x >> 2) & 0x33333333);\n x = (x + (x >> 4)) & 0x0f0f0f0f;\n x += (x >> 8);\n x += (x >> 16);\n return (x & 0x7f);\n};\n\nconst hashFragment = (shift, h) =>\n (h >>> shift) & MASK;\n\nconst toBitmap = x =>\n 1 << x;\n\nconst fromBitmap = (bitmap, bit) =>\n popcount(bitmap & (bit - 1));\n\n/* Array Ops\n ******************************************************************************/\n/**\n Set a value in an array.\n\n @param mutate Should the input array be mutated?\n @param at Index to change.\n @param v New value\n @param arr Array.\n*/\nconst arrayUpdate = (mutate, at, v, arr) => {\n let out = arr;\n if (!mutate) {\n const len = arr.length;\n out = new Array(len);\n for (let i = 0; i < len; ++i)\n out[i] = arr[i];\n }\n out[at] = v;\n return out;\n};\n\n/**\n Remove a value from an array.\n\n @param mutate Should the input array be mutated?\n @param at Index to remove.\n @param arr Array.\n*/\nconst arraySpliceOut = (mutate, at, arr) => {\n const newLen = arr.length - 1;\n let i = 0;\n let g = 0;\n let out = arr;\n if (mutate) {\n i = g = at;\n } else {\n out = new Array(newLen);\n while (i < at)\n out[g++] = arr[i++];\n }\n ++i;\n while (i <= newLen)\n out[g++] = arr[i++];\n if (mutate) {\n out.length = newLen;\n }\n return out;\n};\n\n/**\n Insert a value into an array.\n\n @param mutate Should the input array be mutated?\n @param at Index to insert at.\n @param v Value to insert,\n @param arr Array.\n*/\nconst arraySpliceIn = (mutate, at, v, arr) => {\n const len = arr.length;\n if (mutate) {\n let i = len;\n while (i >= at)\n arr[i--] = arr[i];\n arr[at] = v;\n return arr;\n }\n let i = 0, g = 0;\n const out = new Array(len + 1);\n while (i < at)\n out[g++] = arr[i++];\n out[at] = v;\n while (i < len)\n out[++g] = arr[i++];\n return out;\n};\n\n/* Node Structures\n ******************************************************************************/\nconst LEAF = 1;\nconst COLLISION = 2;\nconst INDEX = 3;\nconst ARRAY = 4;\n\n/**\n Empty node.\n*/\nconst empty = ({\n __hamt_isEmpty: true\n});\n\nconst isEmptyNode = x =>\n x === empty || (x && x.__hamt_isEmpty);\n\n/**\n Leaf holding a value.\n\n @member edit Edit of the node.\n @member hash Hash of key.\n @member key Key.\n @member value Value stored.\n*/\nconst Leaf = (edit, hash, key, value) => ({\n type: LEAF,\n edit: edit,\n hash: hash,\n key: key,\n value: value,\n _modify: Leaf__modify\n});\n\n/**\n Leaf holding multiple values with the same hash but different keys.\n\n @member edit Edit of the node.\n @member hash Hash of key.\n @member children Array of collision children node.\n*/\nconst Collision = (edit, hash, children) => ({\n type: COLLISION,\n edit: edit,\n hash: hash,\n children: children,\n _modify: Collision__modify\n});\n\n/**\n Internal node with a sparse set of children.\n\n Uses a bitmap and array to pack children.\n\n @member edit Edit of the node.\n @member mask Bitmap that encode the positions of children in the array.\n @member children Array of child nodes.\n*/\nconst IndexedNode = (edit, mask, children) => ({\n type: INDEX,\n edit: edit,\n mask: mask,\n children: children,\n _modify: IndexedNode__modify\n});\n\n/**\n Internal node with many children.\n\n @member edit Edit of the node.\n @member size Number of children.\n @member children Array of child nodes.\n*/\nconst ArrayNode = (edit, size, children) => ({\n type: ARRAY,\n edit: edit,\n size: size,\n children: children,\n _modify: ArrayNode__modify\n});\n\n/**\n Is `node` a leaf node?\n*/\nconst isLeaf = node =>\n (node === empty || node.type === LEAF || node.type === COLLISION);\n\n/* Internal node operations.\n ******************************************************************************/\n/**\n Expand an indexed node into an array node.\n\n @param edit Current edit.\n @param frag Index of added child.\n @param child Added child.\n @param mask Index node mask before child added.\n @param subNodes Index node children before child added.\n*/\nconst expand = (edit, frag, child, bitmap, subNodes) => {\n const arr = [];\n let bit = bitmap;\n let count = 0;\n for (let i = 0; bit; ++i) {\n if (bit & 1)\n arr[i] = subNodes[count++];\n bit >>>= 1;\n }\n arr[frag] = child;\n return ArrayNode(edit, count + 1, arr);\n};\n\n/**\n Collapse an array node into a indexed node.\n\n @param edit Current edit.\n @param count Number of elements in new array.\n @param removed Index of removed element.\n @param elements Array node children before remove.\n*/\nconst pack = (edit, count, removed, elements) => {\n const children = new Array(count - 1);\n let g = 0;\n let bitmap = 0;\n for (let i = 0, len = elements.length; i < len; ++i) {\n if (i !== removed) {\n const elem = elements[i];\n if (elem && !isEmptyNode(elem)) {\n children[g++] = elem;\n bitmap |= 1 << i;\n }\n }\n }\n return IndexedNode(edit, bitmap, children);\n};\n\n/**\n Merge two leaf nodes.\n\n @param shift Current shift.\n @param h1 Node 1 hash.\n @param n1 Node 1.\n @param h2 Node 2 hash.\n @param n2 Node 2.\n*/\nconst mergeLeaves = (edit, shift, h1, n1, h2, n2) => {\n if (h1 === h2)\n return Collision(edit, h1, [n2, n1]);\n\n const subH1 = hashFragment(shift, h1);\n const subH2 = hashFragment(shift, h2);\n return IndexedNode(edit, toBitmap(subH1) | toBitmap(subH2),\n subH1 === subH2\n ? [mergeLeaves(edit, shift + SIZE, h1, n1, h2, n2)]\n : subH1 < subH2 ? [n1, n2] : [n2, n1]);\n};\n\n/**\n Update an entry in a collision list.\n\n @param mutate Should mutation be used?\n @param edit Current edit.\n @param keyEq Key compare function.\n @param hash Hash of collision.\n @param list Collision list.\n @param f Update function.\n @param k Key to update.\n @param size Size ref.\n*/\nconst updateCollisionList = (mutate, edit, keyEq, h, list, f, k, size) => {\n const len = list.length;\n for (let i = 0; i < len; ++i) {\n const child = list[i];\n if (keyEq(k, child.key)) {\n const value = child.value;\n const newValue = f(value);\n if (newValue === value)\n return list;\n\n if (newValue === nothing) {\n --size.value;\n return arraySpliceOut(mutate, i, list);\n }\n return arrayUpdate(mutate, i, Leaf(edit, h, k, newValue), list);\n }\n }\n\n const newValue = f();\n if (newValue === nothing)\n return list;\n ++size.value;\n return arrayUpdate(mutate, len, Leaf(edit, h, k, newValue), list);\n};\n\nconst canEditNode = (edit, node) => edit === node.edit;\n\n/* Editing\n ******************************************************************************/\nconst Leaf__modify = function (edit, keyEq, shift, f, h, k, size) {\n if (keyEq(k, this.key)) {\n const v = f(this.value);\n if (v === this.value)\n return this;\n else if (v === nothing) {\n --size.value;\n return empty;\n }\n if (canEditNode(edit, this)) {\n this.value = v;\n return this;\n }\n return Leaf(edit, h, k, v);\n }\n const v = f();\n if (v === nothing)\n return this;\n ++size.value;\n return mergeLeaves(edit, shift, this.hash, this, h, Leaf(edit, h, k, v));\n};\n\nconst Collision__modify = function (edit, keyEq, shift, f, h, k, size) {\n if (h === this.hash) {\n const canEdit = canEditNode(edit, this);\n const list = updateCollisionList(canEdit, edit, keyEq, this.hash, this.children, f, k, size);\n if (list === this.children)\n return this;\n\n return list.length > 1\n ? Collision(edit, this.hash, list)\n : list[0]; // collapse single element collision list\n }\n const v = f();\n if (v === nothing)\n return this;\n ++size.value;\n return mergeLeaves(edit, shift, this.hash, this, h, Leaf(edit, h, k, v));\n};\n\nconst IndexedNode__modify = function (edit, keyEq, shift, f, h, k, size) {\n const mask = this.mask;\n const children = this.children;\n const frag = hashFragment(shift, h);\n const bit = toBitmap(frag);\n const indx = fromBitmap(mask, bit);\n const exists = mask & bit;\n const current = exists ? children[indx] : empty;\n const child = current._modify(edit, keyEq, shift + SIZE, f, h, k, size);\n\n if (current === child)\n return this;\n\n const canEdit = canEditNode(edit, this);\n let bitmap = mask;\n let newChildren;\n if (exists && isEmptyNode(child)) { // remove\n bitmap &= ~bit;\n if (!bitmap)\n return empty;\n if (children.length <= 2 && isLeaf(children[indx ^ 1]))\n return children[indx ^ 1] // collapse\n\n newChildren = arraySpliceOut(canEdit, indx, children);\n } else if (!exists && !isEmptyNode(child)) { // add\n if (children.length >= MAX_INDEX_NODE)\n return expand(edit, frag, child, mask, children);\n\n bitmap |= bit;\n newChildren = arraySpliceIn(canEdit, indx, child, children);\n } else { // modify\n newChildren = arrayUpdate(canEdit, indx, child, children);\n }\n\n if (canEdit) {\n this.mask = bitmap;\n this.children = newChildren;\n return this;\n }\n return IndexedNode(edit, bitmap, newChildren);\n};\n\nconst ArrayNode__modify = function (edit, keyEq, shift, f, h, k, size) {\n let count = this.size;\n const children = this.children;\n const frag = hashFragment(shift, h);\n const child = children[frag];\n const newChild = (child || empty)._modify(edit, keyEq, shift + SIZE, f, h, k, size);\n\n if (child === newChild)\n return this;\n\n const canEdit = canEditNode(edit, this);\n let newChildren;\n if (isEmptyNode(child) && !isEmptyNode(newChild)) { // add\n ++count;\n newChildren = arrayUpdate(canEdit, frag, newChild, children);\n } else if (!isEmptyNode(child) && isEmptyNode(newChild)) { // remove\n --count;\n if (count <= MIN_ARRAY_NODE)\n return pack(edit, count, frag, children);\n newChildren = arrayUpdate(canEdit, frag, empty, children);\n } else { // modify\n newChildren = arrayUpdate(canEdit, frag, newChild, children);\n }\n\n if (canEdit) {\n this.size = count;\n this.children = newChildren;\n return this;\n }\n return ArrayNode(edit, count, newChildren);\n};\n\nempty._modify = (edit, keyEq, shift, f, h, k, size) => {\n const v = f();\n if (v === nothing)\n return empty;\n ++size.value;\n return Leaf(edit, h, k, v);\n};\n\n/*\n ******************************************************************************/\nfunction Map(editable, edit, config, root, size) {\n this._editable = editable;\n this._edit = edit;\n this._config = config;\n this._root = root;\n this._size = size;\n};\n\nMap.prototype.setTree = function (newRoot, newSize) {\n if (this._editable) {\n this._root = newRoot;\n this._size = newSize;\n return this;\n }\n return newRoot === this._root\n ? this\n : new Map(this._editable, this._edit, this._config, newRoot, newSize);\n};\n\n/* Queries\n ******************************************************************************/\n/**\n Lookup the value for `key` in `map` using a custom `hash`.\n\n Returns the value or `alt` if none.\n*/\nconst tryGetHash = hamt.tryGetHash = (alt, hash, key, map) => {\n let node = map._root;\n let shift = 0;\n const keyEq = map._config.keyEq;\n while (true) switch (node.type) {\n case LEAF:\n {\n return keyEq(key, node.key) ? node.value : alt;\n }\n case COLLISION:\n {\n if (hash === node.hash) {\n const children = node.children;\n for (let i = 0, len = children.length; i < len; ++i) {\n const child = children[i];\n if (keyEq(key, child.key))\n return child.value;\n }\n }\n return alt;\n }\n case INDEX:\n {\n const frag = hashFragment(shift, hash);\n const bit = toBitmap(frag);\n if (node.mask & bit) {\n node = node.children[fromBitmap(node.mask, bit)]\n shift += SIZE;\n break;\n }\n return alt;\n }\n case ARRAY:\n {\n node = node.children[hashFragment(shift, hash)];\n if (node) {\n shift += SIZE;\n break;\n }\n return alt;\n }\n default:\n return alt;\n }\n};\n\nMap.prototype.tryGetHash = function (alt, hash, key) {\n return tryGetHash(alt, hash, key, this);\n};\n\n/**\n Lookup the value for `key` in `map` using internal hash function.\n\n @see `tryGetHash`\n*/\nconst tryGet = hamt.tryGet = (alt, key, map) =>\n tryGetHash(alt, map._config.hash(key), key, map);\n\nMap.prototype.tryGet = function (alt, key) {\n return tryGet(alt, key, this);\n};\n\n/**\n Lookup the value for `key` in `map` using a custom `hash`.\n\n Returns the value or `undefined` if none.\n*/\nconst getHash = hamt.getHash = (hash, key, map) =>\n tryGetHash(undefined, hash, key, map);\n\nMap.prototype.getHash = function (hash, key) {\n return getHash(hash, key, this);\n};\n\n/**\n Lookup the value for `key` in `map` using internal hash function.\n\n @see `get`\n*/\nconst get = hamt.get = (key, map) =>\n tryGetHash(undefined, map._config.hash(key), key, map);\n\nMap.prototype.get = function (key, alt) {\n return tryGet(alt, key, this);\n};\n\n/**\n Does an entry exist for `key` in `map`? Uses custom `hash`.\n*/\nconst hasHash = hamt.has = (hash, key, map) =>\n tryGetHash(nothing, hash, key, map) !== nothing;\n\nMap.prototype.hasHash = function (hash, key) {\n return hasHash(hash, key, this);\n};\n\n/**\n Does an entry exist for `key` in `map`? Uses internal hash function.\n*/\nconst has = hamt.has = (key, map) =>\n hasHash(map._config.hash(key), key, map);\n\nMap.prototype.has = function (key) {\n return has(key, this);\n};\n\nconst defKeyCompare = (x, y) => x === y;\n\n/**\n Create an empty map.\n\n @param config Configuration.\n*/\nhamt.make = (config) =>\n new Map(0, 0, {\n keyEq: (config && config.keyEq) || defKeyCompare,\n hash: (config && config.hash) || hash\n }, empty, 0);\n\n/**\n Empty map.\n*/\nhamt.empty = hamt.make();\n\n/**\n Does `map` contain any elements?\n*/\nconst isEmpty = hamt.isEmpty = (map) =>\n map && !!isEmptyNode(map._root);\n\nMap.prototype.isEmpty = function () {\n return isEmpty(this);\n};\n\n/* Updates\n ******************************************************************************/\n/**\n Alter the value stored for `key` in `map` using function `f` using\n custom hash.\n\n `f` is invoked with the current value for `k` if it exists,\n or no arguments if no such value exists. `modify` will always either\n update or insert a value into the map.\n\n Returns a map with the modified value. Does not alter `map`.\n*/\nconst modifyHash = hamt.modifyHash = (f, hash, key, map) => {\n const size = { value: map._size };\n const newRoot = map._root._modify(\n map._editable ? map._edit : NaN,\n map._config.keyEq,\n 0,\n f,\n hash,\n key,\n size);\n return map.setTree(newRoot, size.value);\n};\n\nMap.prototype.modifyHash = function (hash, key, f) {\n return modifyHash(f, hash, key, this);\n};\n\n/**\n Alter the value stored for `key` in `map` using function `f` using\n internal hash function.\n\n @see `modifyHash`\n*/\nconst modify = hamt.modify = (f, key, map) =>\n modifyHash(f, map._config.hash(key), key, map);\n\nMap.prototype.modify = function (key, f) {\n return modify(f, key, this);\n};\n\n/**\n Store `value` for `key` in `map` using custom `hash`.\n\n Returns a map with the modified value. Does not alter `map`.\n*/\nconst setHash = hamt.setHash = (hash, key, value, map) =>\n modifyHash(constant(value), hash, key, map);\n\nMap.prototype.setHash = function (hash, key, value) {\n return setHash(hash, key, value, this);\n};\n\n/**\n Store `value` for `key` in `map` using internal hash function.\n\n @see `setHash`\n*/\nconst set = hamt.set = (key, value, map) =>\n setHash(map._config.hash(key), key, value, map);\n\nMap.prototype.set = function (key, value) {\n return set(key, value, this);\n};\n\n/**\n Remove the entry for `key` in `map`.\n\n Returns a map with the value removed. Does not alter `map`.\n*/\nconst del = constant(nothing);\nconst removeHash = hamt.removeHash = (hash, key, map) =>\n modifyHash(del, hash, key, map);\n\nMap.prototype.removeHash = Map.prototype.deleteHash = function (hash, key) {\n return removeHash(hash, key, this);\n};\n\n/**\n Remove the entry for `key` in `map` using internal hash function.\n\n @see `removeHash`\n*/\nconst remove = hamt.remove = (key, map) =>\n removeHash(map._config.hash(key), key, map);\n\nMap.prototype.remove = Map.prototype.delete = function (key) {\n return remove(key, this);\n};\n\n/* Mutation\n ******************************************************************************/\n/**\n Mark `map` as mutable.\n */\nconst beginMutation = hamt.beginMutation = (map) =>\n new Map(\n map._editable + 1,\n map._edit + 1,\n map._config,\n map._root,\n map._size);\n\nMap.prototype.beginMutation = function () {\n return beginMutation(this);\n};\n\n/**\n Mark `map` as immutable.\n */\nconst endMutation = hamt.endMutation = (map) => {\n map._editable = map._editable && map._editable - 1;\n return map;\n};\n\nMap.prototype.endMutation = function () {\n return endMutation(this);\n};\n\n/**\n Mutate `map` within the context of `f`.\n @param f\n @param map HAMT\n*/\nconst mutate = hamt.mutate = (f, map) => {\n const transient = beginMutation(map);\n f(transient);\n return endMutation(transient);\n};\n\nMap.prototype.mutate = function (f) {\n return mutate(f, this);\n};\n\n/* Traversal\n ******************************************************************************/\n/**\n Apply a continuation.\n*/\nconst appk = k =>\n k && lazyVisitChildren(k[0], k[1], k[2], k[3], k[4]);\n\n/**\n Recursively visit all values stored in an array of nodes lazily.\n*/\nvar lazyVisitChildren = (len, children, i, f, k) => {\n while (i < len) {\n const child = children[i++];\n if (child && !isEmptyNode(child))\n return lazyVisit(child, f, [len, children, i, f, k]);\n }\n return appk(k);\n};\n\n/**\n Recursively visit all values stored in `node` lazily.\n*/\nconst lazyVisit = (node, f, k) => {\n switch (node.type) {\n case LEAF:\n return {\n value: f(node),\n rest: k\n };\n\n case COLLISION:\n case ARRAY:\n case INDEX:\n const children = node.children;\n return lazyVisitChildren(children.length, children, 0, f, k);\n\n default:\n return appk(k);\n }\n};\n\nconst DONE = {\n done: true\n};\n\n/**\n Javascript iterator over a map.\n*/\nfunction MapIterator(v) {\n this.v = v;\n};\n\nMapIterator.prototype.next = function () {\n if (!this.v)\n return DONE;\n const v0 = this.v;\n this.v = appk(v0.rest);\n return v0;\n};\n\nMapIterator.prototype[Symbol.iterator] = function () {\n return this;\n};\n\n/**\n Lazily visit each value in map with function `f`.\n*/\nconst visit = (map, f) =>\n new MapIterator(lazyVisit(map._root, f));\n\n/**\n Get a Javascsript iterator of `map`.\n\n Iterates over `[key, value]` arrays.\n*/\nconst buildPairs = (x) => [x.key, x.value];\nconst entries = hamt.entries = (map) =>\n visit(map, buildPairs);\n\nMap.prototype.entries = Map.prototype[Symbol.iterator] = function () {\n return entries(this);\n};\n\n/**\n Get array of all keys in `map`.\n\n Order is not guaranteed.\n*/\nconst buildKeys = (x) => x.key;\nconst keys = hamt.keys = (map) =>\n visit(map, buildKeys);\n\nMap.prototype.keys = function () {\n return keys(this);\n}\n\n/**\n Get array of all values in `map`.\n\n Order is not guaranteed, duplicates are preserved.\n*/\nconst buildValues = x => x.value;\nconst values = hamt.values = Map.prototype.values = map =>\n visit(map, buildValues);\n\nMap.prototype.values = function () {\n return values(this);\n};\n\n/* Fold\n ******************************************************************************/\n/**\n Visit every entry in the map, aggregating data.\n\n Order of nodes is not guaranteed.\n\n @param f Function mapping accumulated value, value, and key to new value.\n @param z Starting value.\n @param m HAMT\n*/\nconst fold = hamt.fold = (f, z, m) => {\n const root = m._root;\n if (root.type === LEAF)\n return f(z, root.value, root.key);\n\n const toVisit = [root.children];\n let children;\n while (children = toVisit.pop()) {\n for (let i = 0, len = children.length; i < len;) {\n const child = children[i++];\n if (child && child.type) {\n if (child.type === LEAF)\n z = f(z, child.value, child.key);\n else\n toVisit.push(child.children);\n }\n }\n }\n return z;\n};\n\nMap.prototype.fold = function (f, z) {\n return fold(f, z, this);\n};\n\n/**\n Visit every entry in the map, aggregating data.\n\n Order of nodes is not guaranteed.\n\n @param f Function invoked with value and key\n @param map HAMT\n*/\nconst forEach = hamt.forEach = (f, map) =>\n fold((_, value, key) => f(value, key, map), null, map);\n\nMap.prototype.forEach = function (f) {\n return forEach(f, this);\n};\n\n/* Aggregate\n ******************************************************************************/\n/**\n Get the number of entries in `map`.\n*/\nconst count = hamt.count = map =>\n map._size;\n\nMap.prototype.count = function () {\n return count(this);\n};\n\nObject.defineProperty(Map.prototype, 'size', {\n get: Map.prototype.count\n});\n\n/* Export\n ******************************************************************************/\nif (typeof module !== 'undefined' && module.exports) {\n module.exports = hamt;\n} else if (typeof define === 'function' && define.amd) {\n define('hamt', [], () => hamt);\n} else {\n this.hamt = hamt;\n}\n"]} --------------------------------------------------------------------------------