├── .gitignore ├── index.js ├── webpack.config.js ├── test ├── setup-mocha.js ├── queue.spec.js ├── node.spec.js └── max-heap.spec.js ├── index.html ├── src ├── node.js ├── queue.js └── max-heap.js ├── package.json ├── score-weight.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | app.bundle.js 4 | app.bundle.js.map 5 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const MaxHeap = require('./src/max-heap'); 2 | 3 | const h = new MaxHeap(); 4 | window.h = h; 5 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: { app: './index.js' }, 3 | output: { 4 | path: './', 5 | filename: 'app.bundle.js' 6 | }, 7 | devtool: 'source-map' 8 | }; 9 | -------------------------------------------------------------------------------- /test/setup-mocha.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const sinon = require('sinon'); 3 | const sinonChai = require('sinon-chai'); 4 | 5 | global.expect = chai.expect; 6 | global.sinon = sinon; 7 | chai.use(sinonChai); 8 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/node.js: -------------------------------------------------------------------------------- 1 | class Node { 2 | constructor(data, priority) { 3 | 4 | } 5 | 6 | appendChild(node) { 7 | 8 | } 9 | 10 | removeChild(node) { 11 | 12 | } 13 | 14 | remove() { 15 | 16 | } 17 | 18 | swapWithParent() { 19 | 20 | } 21 | } 22 | 23 | module.exports = Node; 24 | -------------------------------------------------------------------------------- /src/queue.js: -------------------------------------------------------------------------------- 1 | const MaxHeap = require('./max-heap.js'); 2 | 3 | class PriorityQueue { 4 | constructor(maxSize) { 5 | 6 | } 7 | 8 | push(data, priority) { 9 | 10 | } 11 | 12 | shift() { 13 | 14 | } 15 | 16 | size() { 17 | 18 | } 19 | 20 | isEmpty() { 21 | 22 | } 23 | } 24 | 25 | module.exports = PriorityQueue; 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "priority-queue", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "test": "mocha -r ./test/setup-mocha.js", 7 | "start": "webpack-dev-server" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "chai": "^3.5.0", 13 | "mocha": "^3.0.2", 14 | "sinon": "^1.17.5", 15 | "sinon-chai": "^2.8.0", 16 | "webpack": "^1.13.1", 17 | "webpack-dev-server": "^1.14.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/max-heap.js: -------------------------------------------------------------------------------- 1 | const Node = require('./node'); 2 | 3 | class MaxHeap { 4 | constructor() { 5 | 6 | } 7 | 8 | push(data, priority) { 9 | 10 | } 11 | 12 | pop() { 13 | 14 | } 15 | 16 | detachRoot() { 17 | 18 | } 19 | 20 | restoreRootFromLastInsertedNode(detached) { 21 | 22 | } 23 | 24 | size() { 25 | 26 | } 27 | 28 | isEmpty() { 29 | 30 | } 31 | 32 | clear() { 33 | 34 | } 35 | 36 | insertNode(node) { 37 | 38 | } 39 | 40 | shiftNodeUp(node) { 41 | 42 | } 43 | 44 | shiftNodeDown(node) { 45 | 46 | } 47 | } 48 | 49 | module.exports = MaxHeap; 50 | -------------------------------------------------------------------------------- /score-weight.json: -------------------------------------------------------------------------------- 1 | { 2 | "Node": { 3 | "weight": 10, 4 | "suitesWeights": { 5 | "#constructor": 10, 6 | "#appendChild": 25, 7 | "#removeChild": 25, 8 | "#remove": 10, 9 | "#swapWithParent": 30 10 | } 11 | }, 12 | "MaxHeap": { 13 | "weight": 30, 14 | "suitesWeights": { 15 | "#constructor": 10, 16 | "#push": 10, 17 | "#insertNode": 10, 18 | "#shiftNodeUp": 30, 19 | "#clear": 5, 20 | "#pop": 10, 21 | "#detachRoot": 10, 22 | "#restoreRootFromLastInsertedNode": 10, 23 | "#shiftNodeDown": 30, 24 | "#size": 5, 25 | "#isEmpty": 5 26 | } 27 | }, 28 | "PriorityQueue": { 29 | "weight": 60, 30 | "suitesWeights": { 31 | "#constructor": 1, 32 | "#push": 10, 33 | "#shift": 50, 34 | "#size": 1, 35 | "#isEmpty": 1 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Priority queue task 2 | 3 | :warning: DO NOT SUBMIT PRS TO THIS REPO :warning: 4 | 5 | ### Task 6 | 7 | The task should be implemeted using [tdd](https://en.wikipedia.org/wiki/Test-driven_development) 8 | 9 | All requirements are defined in the tests. 10 | 11 | P S A partial solution is also a solution. The task is very long ) 12 | 13 | ### Run tests 14 | ```sh 15 | npm test 16 | ``` 17 | 18 | ### Run in browser 19 | ```sh 20 | npm start 21 | ``` 22 | 23 | open http://localhost:8080 24 | 25 | --- 26 | 27 | © [R1ZZU](https://github.com/R1ZZU) 28 | 29 | ### Prerequisites 30 | 1. Install [Node.js](https://nodejs.org/en/download/) 31 | 2. Fork this repository: https://github.com/rolling-scopes-school/priority-queue/ 32 | 3. Clone your newly created repo: https://github.com/<%your_github_username%>/priority-queue/ 33 | 4. Go to folder `priority-queue` 34 | 5. To install all dependencies use [`npm install`](https://docs.npmjs.com/cli/install) 35 | 6. Run `npm test` in command line or you can run tests in browser with `npm start` and open http://localhost:8080 36 | 7. You will see the number of passing and failing tests. 100% of passing tests is equal to 100p in score 37 | 38 | --- 39 | 40 | ### Submit to [rs app](https://app.rs.school) 41 | 1. Open [rs app](https://app.rs.school) and login 42 | 2. Go to [submit task page](https://app.rs.school/course/submit-task?course=rs-2019-q3) 43 | 3. Select your task (priority-queue) 44 | 4. Press the submit button and enjoy 45 | 46 | --- 47 | 48 | ### Notes 49 | 1. We recommend you to use nodejs of version 10 or lower. If you using any of features that does not supported by node v10, score won't be submitted. 50 | 2. Please be sure that each of your test in limit of 30sec. 51 | -------------------------------------------------------------------------------- /test/queue.spec.js: -------------------------------------------------------------------------------- 1 | const Queue = require('../src/queue'); 2 | const MaxHeap = require('../src/max-heap'); 3 | 4 | describe('PriorityQueue', () => { 5 | describe('#constructor', () => { 6 | it('assings passed maxSize or set it to default value 30', () => { 7 | const q = new Queue(10); 8 | const qWithDefaultMaxSize = new Queue(); 9 | 10 | expect(q.maxSize).to.equal(10); 11 | expect(qWithDefaultMaxSize.maxSize).to.equal(30); 12 | }); 13 | 14 | it('assings new MaxHeap to this.heap', () => { 15 | const q = new Queue(); 16 | 17 | expect(q.heap).to.be.instanceof(MaxHeap); 18 | }); 19 | }); 20 | 21 | describe('#push', () => { 22 | let q; 23 | 24 | beforeEach(() => { 25 | q = new Queue(3); 26 | }); 27 | 28 | it('calls heap.push with passed data and priority', () => { 29 | sinon.spy(q.heap, 'push'); 30 | 31 | q.push(0, 1); 32 | expect(q.heap.push).to.have.been.calledWith(0, 1); 33 | }); 34 | 35 | it('throws an error if queue has max size', () => { 36 | q.push(0, 1); 37 | q.push(1, 2); 38 | q.push(2, 3); 39 | 40 | expect(() => { 41 | q.push(3, 4); 42 | }).to.throw(); 43 | }); 44 | }); 45 | 46 | describe('#shift', () => { 47 | let q; 48 | 49 | beforeEach(() => { 50 | q = new Queue(); 51 | }); 52 | 53 | it('calls heap.pop', () => { 54 | q.push(0, 1); 55 | 56 | sinon.spy(q.heap, 'pop'); 57 | q.shift(); 58 | expect(q.heap.pop).to.have.been.called; 59 | }); 60 | 61 | it('returns value of removed node', () => { 62 | q.push(0, 1); 63 | expect(q.shift()).to.equal(0); 64 | }); 65 | 66 | it('throws an error if queue is empty', () => { 67 | expect(() => { 68 | q.shift() 69 | }).to.throw(); 70 | }); 71 | 72 | it('should return items sorted by priority', () => { 73 | const nodes = [ 74 | { priority: 10, data: 1 }, 75 | { priority: 20, data: 2 }, 76 | { priority: 5, data: 3 }, 77 | { priority: 0, data: 4 }, 78 | { priority: 8, data: 5 }, 79 | { priority: 12, data: 6 }, 80 | { priority: 17, data: 7 }, 81 | { priority: 15, data: 8 }, 82 | ]; 83 | 84 | const expectedData = [2, 7, 8, 6, 1, 5, 3, 4] 85 | 86 | nodes.forEach(node => q.push(node.data, node.priority)); 87 | expectedData.forEach(d => expect(q.shift()).to.equal(d)); 88 | }); 89 | 90 | it('should handle items with same priority (return in the same order this items have been added)', () => { 91 | const expectedData = [3, 5, 1, 0, 4, 2]; 92 | 93 | q.push(0, 10); 94 | q.push(1, 15); 95 | q.push(2, 4); 96 | q.push(3, 17); 97 | q.push(4, 6); 98 | q.push(5, 17); 99 | 100 | for (var i = 0; i < 6; i++) { 101 | expect(q.shift()).to.equal(expectedData[i]); 102 | } 103 | }); 104 | }); 105 | 106 | describe('#size', () => { 107 | it('returns current size of queue', () => { 108 | const q = new Queue(); 109 | 110 | q.push(0, 1); 111 | expect(q.size()).to.equal(1); 112 | 113 | q.push(1, 2); 114 | expect(q.size()).to.equal(2); 115 | 116 | q.push(2, 3); 117 | expect(q.size()).to.equal(3); 118 | 119 | q.shift(); 120 | q.shift(); 121 | 122 | expect(q.size()).to.equal(1); 123 | 124 | q.shift(); 125 | expect(q.size()).to.equal(0); 126 | }); 127 | }); 128 | 129 | describe('#isEmpty', () => { 130 | it('return true if queue is empty', () => { 131 | const q = new Queue(); 132 | 133 | expect(q.isEmpty()).to.equal(true); 134 | 135 | q.push(0, 1); 136 | expect(q.isEmpty()).to.equal(false); 137 | 138 | q.shift(); 139 | expect(q.isEmpty()).to.equal(true); 140 | }); 141 | }); 142 | }); 143 | -------------------------------------------------------------------------------- /test/node.spec.js: -------------------------------------------------------------------------------- 1 | const Node = require('../src/node'); 2 | 3 | describe('Node', () => { 4 | describe('#constructor', () => { 5 | const node = new Node(42, 15); 6 | 7 | it('assigns passed data and priority to this', () => { 8 | expect(node.data).to.equal(42); 9 | expect(node.priority).to.equal(15); 10 | }); 11 | 12 | it('assigns this.parent, this.left and this.right to null', () => { 13 | expect(node.parent).to.equal(null); 14 | expect(node.left).to.equal(null); 15 | expect(node.right).to.equal(null); 16 | }); 17 | }); 18 | 19 | describe('#appendChild', () => { 20 | let parent, leftChild, rightChild; 21 | 22 | beforeEach(() => { 23 | parent = new Node(42, 15); 24 | leftChild = new Node(13, 20); 25 | rightChild = new Node(98, 69); 26 | }); 27 | 28 | it('assigns passed child to this.left', () => { 29 | parent.appendChild(leftChild); 30 | 31 | expect(parent.left).to.equal(leftChild); 32 | expect(parent.right).to.equal(null); 33 | }); 34 | 35 | it('assigns passed child to this.right if this.left exists', () => { 36 | parent.appendChild(leftChild); 37 | parent.appendChild(rightChild); 38 | 39 | expect(parent.left).to.equal(leftChild); 40 | expect(parent.right).to.equal(rightChild); 41 | }); 42 | 43 | it('does nothing if this.left and this.right exist', () => { 44 | parent.appendChild(leftChild); 45 | parent.appendChild(rightChild); 46 | parent.appendChild(new Node(42, 15)); 47 | 48 | expect(parent.left).to.equal(leftChild); 49 | expect(parent.right).to.equal(rightChild); 50 | }); 51 | }); 52 | 53 | describe('#removeChild', () => { 54 | let parent, leftChild, rightChild; 55 | 56 | beforeEach(() => { 57 | parent = new Node(42, 15); 58 | leftChild = new Node(13, 20); 59 | rightChild = new Node(98, 69); 60 | 61 | parent.appendChild(leftChild); 62 | parent.appendChild(rightChild); 63 | }); 64 | 65 | it('assing null to this.left if passed node is left child', () => { 66 | parent.removeChild(leftChild); 67 | expect(parent.left).to.equal(null); 68 | }); 69 | 70 | it('assing null to this.right if passed node is right child', () => { 71 | parent.removeChild(rightChild); 72 | expect(parent.right).to.equal(null); 73 | }); 74 | 75 | it('throws error if passed node is not a child of this node', () => { 76 | expect(() => { 77 | parent.removeChild(new Node(15, 42)); 78 | }).to.throw(); 79 | 80 | expect(parent.left).to.equal(leftChild); 81 | expect(parent.right).to.equal(rightChild); 82 | }); 83 | 84 | it('assigns null to child.parent', () => { 85 | parent.removeChild(leftChild); 86 | 87 | expect(leftChild.parent).to.equal(null); 88 | }) 89 | }); 90 | 91 | describe('#remove', () => { 92 | it('does nothing if node does not have parent', () => { 93 | const node = new Node(42, 15); 94 | 95 | expect(() => { 96 | node.remove(); 97 | }).not.to.throw(); 98 | }); 99 | 100 | it('calls child.parent.removeChild with child as arg', () => { 101 | const parent = new Node(42, 15); 102 | const child = new Node(15, 42); 103 | 104 | parent.appendChild(child); 105 | 106 | sinon.spy(parent, 'removeChild'); 107 | 108 | child.remove(); 109 | 110 | expect(parent.removeChild).to.have.been.calledOnce; 111 | expect(parent.removeChild).to.have.been.calledWith(child); 112 | }); 113 | }); 114 | 115 | describe('#swapWithParent', () => { 116 | it('does nothing if node does not have parent', () => { 117 | const node = new Node(15, 42); 118 | 119 | expect(() => { 120 | node.swapWithParent(); 121 | }).not.to.throw(); 122 | }); 123 | 124 | it('updates parent.parent', () => { 125 | const parent = new Node(15, 42); 126 | const child = new Node(42, 15); 127 | 128 | parent.appendChild(child); 129 | child.swapWithParent(); 130 | 131 | expect(parent.parent).to.equal(child); 132 | }); 133 | 134 | it('updates parent.parent.parent', () => { 135 | const root = new Node(8, 8); 136 | const child = new Node(4, 4); 137 | const grandson = new Node(2, 2); 138 | 139 | root.appendChild(child); 140 | child.appendChild(grandson); 141 | 142 | grandson.swapWithParent(); 143 | 144 | expect(child.parent).to.equal(grandson); 145 | expect(grandson.parent).to.equal(root); 146 | }); 147 | 148 | it('updates child.parent', () => { 149 | const parentOfParent = new Node(100, 500); 150 | const parent = new Node(15, 42); 151 | const child = new Node(42, 15); 152 | 153 | parentOfParent.appendChild(parent); 154 | parent.appendChild(child); 155 | child.swapWithParent(); 156 | 157 | expect(child.parent).to.equal(parentOfParent); 158 | }); 159 | 160 | it('updates parent.child.parent', () => { 161 | const root = new Node(1, 2); 162 | const left = new Node(3, 4); 163 | const right = new Node(5, 6); 164 | 165 | root.appendChild(left); 166 | root.appendChild(right); 167 | 168 | right.swapWithParent(); 169 | 170 | expect(left.parent).to.equal(right); 171 | }) 172 | 173 | it('updates children of node and parent node', () => { 174 | const root = new Node(42, 15); 175 | const left = new Node(13, 42); 176 | const right = new Node(0, 1); 177 | const childOfLeft = new Node(0, 15); 178 | 179 | root.appendChild(left); 180 | root.appendChild(right); 181 | left.appendChild(childOfLeft); 182 | 183 | left.swapWithParent(); 184 | 185 | expect(left.right).to.equal(right); 186 | expect(left.left).to.equal(root); 187 | expect(root.left).to.equal(childOfLeft); 188 | }); 189 | 190 | it('maintains correct state of parent.parent.left and parent.parent.right', () => { 191 | const root = new Node(15, 42); 192 | const left = new Node(42, 15); 193 | const right = new Node(13, 42); 194 | const childOfLeft = new Node(13, 34); 195 | const childOfRight = new Node(0, 1); 196 | 197 | root.appendChild(left); 198 | root.appendChild(right); 199 | left.appendChild(childOfLeft); 200 | right.appendChild(childOfRight); 201 | 202 | childOfLeft.swapWithParent(); 203 | childOfRight.swapWithParent(); 204 | 205 | expect(root.left).to.equal(childOfLeft); 206 | expect(root.right).to.equal(childOfRight); 207 | }); 208 | }); 209 | }); 210 | -------------------------------------------------------------------------------- /test/max-heap.spec.js: -------------------------------------------------------------------------------- 1 | const Node = require('../src/node'); 2 | const MaxHeap = require('../src/max-heap'); 3 | 4 | describe('MaxHeap', () => { 5 | describe('#constructor', () => { 6 | const h = new MaxHeap(); 7 | 8 | it('assigns null to this.root', () => { 9 | expect(h.root).to.equal(null); 10 | }); 11 | 12 | it('assigns [] to this.parentNodes', () => { 13 | expect(h.parentNodes).to.deep.equal([]); 14 | }); 15 | }); 16 | 17 | describe('#push', () => { 18 | let h; 19 | 20 | beforeEach(() => { 21 | h = new MaxHeap(); 22 | }); 23 | 24 | it('calls insertNode with new node having passed data and priority', () => { 25 | sinon.spy(h, 'insertNode'); 26 | 27 | h.push(42, 15); 28 | 29 | expect(h.insertNode).to.have.been.calledOnce; 30 | expect(h.insertNode.firstCall.args[0]).to.be.an.instanceof(Node); 31 | expect(h.insertNode.firstCall.args[0].data).to.equal(42); 32 | expect(h.insertNode.firstCall.args[0].priority).to.equal(15); 33 | }); 34 | 35 | it('calls shiftNodeUp with new node having passed data and priority', () => { 36 | sinon.spy(h, 'shiftNodeUp'); 37 | 38 | h.push(42, 15); 39 | 40 | expect(h.shiftNodeUp).to.have.been.calledOnce; 41 | expect(h.shiftNodeUp.firstCall.args[0]).to.be.an.instanceof(Node); 42 | expect(h.shiftNodeUp.firstCall.args[0].data).to.equal(42); 43 | expect(h.shiftNodeUp.firstCall.args[0].priority).to.equal(15); 44 | }); 45 | }); 46 | 47 | describe('#insertNode', () => { 48 | let h; 49 | 50 | beforeEach(() => { 51 | h = new MaxHeap(); 52 | }); 53 | 54 | it('assings passed node to this.root if heap is empty', () => { 55 | const node = new Node(42, 15); 56 | 57 | h.insertNode(node); 58 | expect(h.root).to.equal(node); 59 | }); 60 | 61 | it('inserts nodes to correct places', () => { 62 | const nodes = [ 63 | new Node(0, 0), 64 | new Node(1, 1), 65 | new Node(2, 2), 66 | new Node(3, 3), 67 | new Node(4, 4), 68 | new Node(5, 5), 69 | new Node(6, 6), 70 | ]; 71 | 72 | nodes.forEach(node => { 73 | h.insertNode(node); 74 | }); 75 | 76 | expect(h.root).to.equal(nodes[0]); 77 | expect(h.root.left).to.equal(nodes[1]); 78 | expect(h.root.right).to.equal(nodes[2]); 79 | expect(h.root.left.left).to.equal(nodes[3]); 80 | expect(h.root.left.right).to.equal(nodes[4]); 81 | }); 82 | 83 | it('maintains this.parentNodes in correct state', () => { 84 | const nodes = [ 85 | new Node(0, 0), 86 | new Node(1, 1), 87 | new Node(2, 2), 88 | new Node(3, 3), 89 | new Node(4, 4), 90 | new Node(5, 5), 91 | new Node(6, 6), 92 | ]; 93 | 94 | h.insertNode(nodes[0]); 95 | expect(h.parentNodes[0]).to.equal(nodes[0]); 96 | 97 | h.insertNode(nodes[1]); 98 | expect(h.parentNodes[0]).to.equal(nodes[0]); 99 | expect(h.parentNodes[1]).to.equal(nodes[1]); 100 | 101 | h.insertNode(nodes[2]); 102 | expect(h.parentNodes[0]).to.equal(nodes[1]); 103 | expect(h.parentNodes[1]).to.equal(nodes[2]); 104 | 105 | h.insertNode(nodes[3]); 106 | expect(h.parentNodes[0]).to.equal(nodes[1]); 107 | expect(h.parentNodes[1]).to.equal(nodes[2]); 108 | expect(h.parentNodes[2]).to.equal(nodes[3]); 109 | 110 | h.insertNode(nodes[4]); 111 | expect(h.parentNodes[0]).to.equal(nodes[2]); 112 | expect(h.parentNodes[1]).to.equal(nodes[3]); 113 | expect(h.parentNodes[2]).to.equal(nodes[4]); 114 | 115 | h.insertNode(nodes[5]); 116 | expect(h.parentNodes[0]).to.equal(nodes[2]); 117 | expect(h.parentNodes[1]).to.equal(nodes[3]); 118 | expect(h.parentNodes[2]).to.equal(nodes[4]); 119 | expect(h.parentNodes[3]).to.equal(nodes[5]); 120 | 121 | h.insertNode(nodes[6]); 122 | expect(h.parentNodes[0]).to.equal(nodes[3]); 123 | expect(h.parentNodes[1]).to.equal(nodes[4]); 124 | expect(h.parentNodes[2]).to.equal(nodes[5]); 125 | expect(h.parentNodes[3]).to.equal(nodes[6]); 126 | }); 127 | }); 128 | 129 | describe('#shiftNodeUp', () => { 130 | let h; 131 | 132 | beforeEach(() => { 133 | h = new MaxHeap(); 134 | 135 | h.root = new Node(0, 10); 136 | h.root.appendChild(new Node(1, 5)); 137 | h.root.appendChild(new Node(2, 7)); 138 | h.root.left.appendChild(new Node(3, 20)); 139 | 140 | /** 141 | 10 20 142 | / \ / \ 143 | 5 7 - shift up -> 10 7 144 | / / 145 | 20 5 146 | **/ 147 | 148 | h.parentNodes = [ 149 | h.root.left, 150 | h.root.right, 151 | h.root.left.left, 152 | ]; 153 | }); 154 | 155 | it('shifts node up until heap property is valid', () => { 156 | const newRoot = h.root.left.left; 157 | h.shiftNodeUp(h.root.left.left); 158 | expect(h.root).to.equal(newRoot); 159 | }); 160 | 161 | it('maintants parentNodes in correct state', () => { 162 | const correctParentNodesOrderAfterShiftUp = [ 163 | h.root, 164 | h.root.right, 165 | h.root.left 166 | ] 167 | 168 | h.shiftNodeUp(h.root.left.left); 169 | 170 | expect(h.parentNodes[0]).to.equal(correctParentNodesOrderAfterShiftUp[0]); 171 | expect(h.parentNodes[1]).to.equal(correctParentNodesOrderAfterShiftUp[1]); 172 | expect(h.parentNodes[2]).to.equal(correctParentNodesOrderAfterShiftUp[2]); 173 | }); 174 | 175 | it('calls Node.swapWithParent', () => { 176 | const nodeToShiftUp = h.root.left.left; 177 | sinon.spy(nodeToShiftUp, 'swapWithParent'); 178 | 179 | h.shiftNodeUp(nodeToShiftUp); 180 | 181 | expect(nodeToShiftUp.swapWithParent).to.have.been.calledTwice; 182 | }); 183 | 184 | it('calls itself recursively', () => { 185 | const nodeToShiftUp = h.root.left.left; 186 | sinon.spy(h, 'shiftNodeUp'); 187 | 188 | h.shiftNodeUp(nodeToShiftUp); 189 | 190 | expect(h.shiftNodeUp).to.have.been.calledThrice; 191 | expect(h.shiftNodeUp.firstCall.args[0]).to.equal(nodeToShiftUp); 192 | expect(h.shiftNodeUp.secondCall.args[0]).to.equal(nodeToShiftUp); 193 | expect(h.shiftNodeUp.thirdCall.args[0]).to.equal(nodeToShiftUp); 194 | }); 195 | }); 196 | 197 | describe('#clear', () => { 198 | it('assigns null to root and [] to parentNodes', () => { 199 | const h = new MaxHeap(); 200 | h.push(0, 0); 201 | h.push(15, 2); 202 | h.push(42, 13); 203 | 204 | h.clear(); 205 | 206 | expect(h.root).to.equal(null); 207 | expect(h.parentNodes).to.deep.equal([]); 208 | }); 209 | }); 210 | 211 | describe('#pop', () => { 212 | it('does nothing if heap is empty', () => { 213 | const h = new MaxHeap(); 214 | expect(() => { 215 | h.pop(); 216 | }).not.to.throw(); 217 | }); 218 | 219 | it('returns data associated with root', () => { 220 | const h = new MaxHeap(); 221 | h.push(42, 15); 222 | h.push(15, 14); 223 | h.push(0, 16); 224 | h.push(100, 100); 225 | 226 | expect(h.pop()).to.equal(100); 227 | expect(h.pop()).to.equal(0); 228 | expect(h.pop()).to.equal(42); 229 | expect(h.pop()).to.equal(15); 230 | }); 231 | 232 | it('calls detachRoot', () => { 233 | const h = new MaxHeap(); 234 | h.push(42, 15); 235 | 236 | sinon.spy(h, 'detachRoot'); 237 | 238 | h.pop(); 239 | 240 | expect(h.detachRoot).to.have.been.called; 241 | }); 242 | 243 | it('calls restoreRootFromLastInsertedNode with detached root', () => { 244 | const fakeDetachedNode = {}; 245 | 246 | const h = new MaxHeap(); 247 | h.push(42, 15); 248 | 249 | sinon.stub(h, 'detachRoot').returns(fakeDetachedNode); 250 | sinon.spy(h, 'restoreRootFromLastInsertedNode'); 251 | 252 | h.pop(); 253 | 254 | expect(h.restoreRootFromLastInsertedNode).to.have.been.calledWith(fakeDetachedNode); 255 | }); 256 | 257 | it('calls shiftNodeDown with current heap root', () => { 258 | const h = new MaxHeap(); 259 | h.push(42, 15); 260 | h.push(15, 42); 261 | h.push(100, 100); 262 | 263 | const expectedNodeToShiftDown = h.root.right; 264 | 265 | sinon.spy(h, 'shiftNodeDown'); 266 | 267 | h.pop(); 268 | expect(h.shiftNodeDown).to.have.been.calledWith(expectedNodeToShiftDown); 269 | }); 270 | }); 271 | 272 | describe('#detachRoot', () => { 273 | let h; 274 | 275 | beforeEach(() => { 276 | h = new MaxHeap(); 277 | }); 278 | 279 | it('assigns null to this.root', () => { 280 | h.push(42, 15); 281 | h.detachRoot(); 282 | 283 | expect(h.root).to.equal(null); 284 | }); 285 | 286 | it('removes root from parentNodes', () => { 287 | h.push(42, 15); 288 | h.push(15, 42); 289 | 290 | h.detachRoot(); 291 | 292 | expect(h.parentNodes[0].data).to.equal(42); 293 | expect(h.parentNodes[0].priority).to.equal(15); 294 | }); 295 | 296 | it('returns detached root', () => { 297 | h.push(42, 15); 298 | 299 | const expected = h.root; 300 | const actual = h.detachRoot(); 301 | 302 | expect(actual).to.equal(expected); 303 | }); 304 | }); 305 | 306 | describe('#restoreRootFromLastInsertedNode', () => { 307 | let h; 308 | 309 | beforeEach(() => { 310 | h = new MaxHeap(); 311 | 312 | h.push(42, 15); 313 | h.push(14, 32); 314 | h.push(0, 0); 315 | }); 316 | 317 | it('should remove last inserted node and assing it to root', () => { 318 | const lastInsertedNode = h.root.right; 319 | const left = h.root.left; 320 | 321 | const detached = h.detachRoot(); 322 | h.restoreRootFromLastInsertedNode(detached); 323 | 324 | expect(h.root).to.equal(lastInsertedNode); 325 | expect(h.root.left).to.equal(left); 326 | expect(left.parent).to.equal(lastInsertedNode); 327 | }); 328 | 329 | it('should maintain correct state of parentNodes', () => { 330 | const root = h.root; 331 | const left = h.root.left; 332 | const lastInsertedNode = h.root.right; 333 | 334 | const detached = h.detachRoot(); 335 | h.restoreRootFromLastInsertedNode(detached); 336 | 337 | expect(h.parentNodes.indexOf(root)).to.equal(-1); 338 | expect(h.parentNodes[0]).to.equal(lastInsertedNode); 339 | expect(h.parentNodes[1]).to.equal(left); 340 | }); 341 | 342 | it('should maintain correct order of parentNodes when right child is moved', () => { 343 | h.push(14,14); 344 | h.push(13,13); 345 | h.push(16,16); 346 | h.push(12,12); 347 | 348 | /** 349 | 32 12 350 | / \ / \ 351 | 15 16 - restoreRoot -> 15 16 352 | / \ / \ / \ / 353 | 14 13 0 12 14 13 0 354 | **/ 355 | 356 | const detached = h.detachRoot(); 357 | h.restoreRootFromLastInsertedNode(detached); 358 | 359 | expect(h.parentNodes.map(n=>n.priority)).to.deep.equal([16,14,13,0]); 360 | }); 361 | 362 | }); 363 | 364 | describe('#shiftNodeDown', () => { 365 | let h; 366 | 367 | beforeEach(() => { 368 | h = new MaxHeap(); 369 | 370 | h.root = new Node(0, 3); 371 | h.root.appendChild(new Node(1, 20)); 372 | h.root.appendChild(new Node(2, 7)); 373 | h.root.left.appendChild(new Node(3, 5)); 374 | 375 | /** 376 | 3 20 377 | / \ / \ 378 | 20 7 - shift down -> 5 7 379 | / / 380 | 5 3 381 | **/ 382 | 383 | h.parentNodes = [ 384 | h.root.left, 385 | h.root.right, 386 | h.root.left.left, 387 | ]; 388 | }); 389 | 390 | it('shifts node down until heap property is valid', () => { 391 | const newRoot = h.root.left; 392 | const newDeepest = h.root; 393 | 394 | h.shiftNodeDown(h.root); 395 | expect(h.root).to.equal(newRoot); 396 | expect(h.root.left.left).to.equal(newDeepest); 397 | }); 398 | 399 | it('maintants parentNodes in correct state', () => { 400 | const correctParentNodesOrderAfterShiftUp = [ 401 | h.root.left.left, 402 | h.root.right, 403 | h.root 404 | ] 405 | 406 | h.shiftNodeDown(h.root); 407 | 408 | expect(h.parentNodes[0]).to.equal(correctParentNodesOrderAfterShiftUp[0]); 409 | expect(h.parentNodes[1]).to.equal(correctParentNodesOrderAfterShiftUp[1]); 410 | expect(h.parentNodes[2]).to.equal(correctParentNodesOrderAfterShiftUp[2]); 411 | }); 412 | 413 | it('shifts node down in right direction', () => { 414 | h = new MaxHeap(); 415 | 416 | let newRoot = new Node(20, 20); 417 | let newDeepest = new Node(1, 1); 418 | 419 | h.root = newDeepest; 420 | h.root.appendChild(new Node(10, 10)); 421 | h.root.appendChild(newRoot); 422 | h.root.left.appendChild(new Node(5, 5)); 423 | h.root.left.appendChild(new Node(8, 8)); 424 | h.root.right.appendChild(new Node(11, 11)); 425 | h.root.right.appendChild(new Node(6, 6)); 426 | 427 | h.parentNodes = [h.root.left.left, h.root.left.right, h.root.right.left, h.root.right.right]; 428 | 429 | /** 430 | 1 20 431 | / \ / \ 432 | 10 20 - shiftDown -> 10 11 433 | / \ / \ / \ / \ 434 | 5 8 11 6 5 8 1 6 435 | **/ 436 | 437 | h.shiftNodeDown(h.root); 438 | 439 | expect(h.root).to.equal(newRoot); 440 | expect(h.root.right.left).to.equal(newDeepest); 441 | expect(h.parentNodes.map(n=>n.priority)).to.deep.equal([5,8,1,6]); 442 | }); 443 | 444 | it('calls Node.swapWithParent', () => { 445 | const firstNodeToSwapWith = h.root.left; 446 | sinon.spy(firstNodeToSwapWith, 'swapWithParent'); 447 | 448 | h.shiftNodeDown(h.root); 449 | 450 | expect(firstNodeToSwapWith.swapWithParent).to.have.been.calledOnce; 451 | }); 452 | 453 | it('calls itself recursively', () => { 454 | const nodeToShiftDown = h.root; 455 | sinon.spy(h, 'shiftNodeDown'); 456 | 457 | h.shiftNodeDown(nodeToShiftDown); 458 | 459 | expect(h.shiftNodeDown).to.have.been.calledThrice; 460 | expect(h.shiftNodeDown.firstCall.args[0]).to.equal(nodeToShiftDown); 461 | expect(h.shiftNodeDown.secondCall.args[0]).to.equal(nodeToShiftDown); 462 | expect(h.shiftNodeDown.thirdCall.args[0]).to.equal(nodeToShiftDown); 463 | }); 464 | }); 465 | 466 | describe('#size', () => { 467 | it('returns current size of heap', () => { 468 | const h = new MaxHeap(); 469 | expect(h.size()).to.equal(0); 470 | 471 | h.push(15, 42); 472 | h.push(13, 0); 473 | expect(h.size()).to.equal(2); 474 | 475 | h.push(14, 100); 476 | expect(h.size()).to.equal(3); 477 | 478 | h.pop(); 479 | h.pop(); 480 | expect(h.size()).to.equal(1); 481 | 482 | h.clear(); 483 | expect(h.size()).to.equal(0); 484 | }); 485 | }); 486 | 487 | describe('#isEmpty', () => { 488 | it('reutrns true if heap is empty', () => { 489 | const h = new MaxHeap(); 490 | expect(h.isEmpty()).to.equal(true); 491 | 492 | h.push(100, 500); 493 | expect(h.isEmpty()).to.equal(false); 494 | 495 | h.clear(); 496 | expect(h.isEmpty()).to.equal(true); 497 | }); 498 | }); 499 | }); 500 | --------------------------------------------------------------------------------