├── .gitignore ├── tslint.json ├── SECURITY.md ├── .editorconfig ├── .travis.yml ├── tsconfig.json ├── LICENSE ├── benchmark ├── construct.ts └── rotate.ts ├── package.json ├── README.md └── src ├── index.ts └── index.spec.ts /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vscode 3 | coverage/ 4 | node_modules/ 5 | npm-debug.log 6 | dist/ 7 | typings/ 8 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint-config-standard", 4 | "tslint-config-prettier" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Security contact information 4 | 5 | To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_size = 2 7 | indent_style = space 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | 4 | notifications: 5 | email: 6 | on_success: never 7 | on_failure: change 8 | 9 | node_js: 10 | - '6' 11 | - stable 12 | 13 | after_script: "npm install coveralls@2 && cat ./coverage/lcov.info | coveralls" 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "lib": ["es2015"], 5 | "rootDir": "src", 6 | "outDir": "dist", 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "strict": true, 10 | "declaration": true, 11 | "sourceMap": true, 12 | "inlineSources": true, 13 | "noUnusedLocals": true 14 | }, 15 | "include": [ 16 | "src/**/*" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Blake Embrey (hello@blakeembrey.com) 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /benchmark/construct.ts: -------------------------------------------------------------------------------- 1 | import Benchmark = require('benchmark') 2 | 3 | import { Deque } from '../src' 4 | 5 | const DoubleEndedQueue = require('double-ended-queue') 6 | const Denque = require('denque') 7 | 8 | const suite = new Benchmark.Suite() 9 | 10 | const length = 2000000 11 | const items = Array.from({ length }, (x, i) => i) 12 | 13 | suite 14 | .add('@blakeembrey/deque', () => { 15 | const deque = new Deque(items) 16 | 17 | for (let i = 0; i < length; i++) deque.pop() 18 | }) 19 | .add('denque', () => { 20 | const deque = new Denque(items) 21 | 22 | for (let i = 0; i < length; i++) deque.pop() 23 | }) 24 | .add('double-ended-queue', () => { 25 | const deque = new DoubleEndedQueue(items) 26 | 27 | for (let i = 0; i < length; i++) deque.pop() 28 | }) 29 | .on('cycle', (e: any) => { 30 | console.log('' + e.target) 31 | }) 32 | .run() 33 | -------------------------------------------------------------------------------- /benchmark/rotate.ts: -------------------------------------------------------------------------------- 1 | import Benchmark = require('benchmark') 2 | 3 | import { Deque } from '../src' 4 | 5 | const DoubleEndedQueue = require('double-ended-queue') 6 | const Denque = require('denque') 7 | 8 | const suite = new Benchmark.Suite() 9 | 10 | const length = 2000000 11 | const items = Array.from({ length }, (x, i) => i) 12 | 13 | const deque = new Deque(items) 14 | const dequeRotate = new Deque(items) 15 | const denque = new Denque(items) 16 | const doubleEndedQueue = new DoubleEndedQueue(items) 17 | 18 | suite 19 | .add('@blakeembrey/deque', () => { 20 | deque.push(deque.popLeft()) 21 | }) 22 | .add('@blakeembrey/deque rotate', () => { 23 | dequeRotate.rotate(-1) 24 | }) 25 | .add('denque', () => { 26 | denque.push(denque.shift()) 27 | }) 28 | .add('double-ended-queue', () => { 29 | doubleEndedQueue.push(doubleEndedQueue.shift()) 30 | }) 31 | .on('cycle', (e: any) => { 32 | console.log('' + e.target) 33 | }) 34 | .run() 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@blakeembrey/deque", 3 | "version": "1.0.5", 4 | "description": "Deques are a generalization of stacks and queues", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "files": [ 8 | "dist/" 9 | ], 10 | "scripts": { 11 | "prettier": "prettier --single-quote --no-semi --write", 12 | "lint": "tslint \"src/**/*.ts\" --project tsconfig.json", 13 | "format": "npm run prettier -- \"src/**/*.ts\"", 14 | "build": "rimraf dist && tsc", 15 | "specs": "jest --coverage", 16 | "test": "npm run -s lint && npm run -s build && npm run -s specs", 17 | "prepare": "npm run build", 18 | "benchmark": "ts-node benchmark/construct.ts && ts-node benchmark/rotate.ts" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git://github.com/blakeembrey/deque.git" 23 | }, 24 | "keywords": [ 25 | "deque", 26 | "fifo", 27 | "lifo", 28 | "queue", 29 | "stack", 30 | "double", 31 | "linked", 32 | "list", 33 | "iterator", 34 | "iteration" 35 | ], 36 | "author": { 37 | "name": "Blake Embrey", 38 | "email": "hello@blakeembrey.com", 39 | "url": "http://blakeembrey.me" 40 | }, 41 | "license": "Apache-2.0", 42 | "bugs": { 43 | "url": "https://github.com/blakeembrey/deque/issues" 44 | }, 45 | "homepage": "https://github.com/blakeembrey/deque", 46 | "jest": { 47 | "roots": [ 48 | "/src/" 49 | ], 50 | "transform": { 51 | "\\.tsx?$": "ts-jest" 52 | }, 53 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(tsx?|jsx?)$", 54 | "moduleFileExtensions": [ 55 | "ts", 56 | "tsx", 57 | "js", 58 | "jsx", 59 | "json", 60 | "node" 61 | ] 62 | }, 63 | "husky": { 64 | "hooks": { 65 | "pre-commit": "lint-staged" 66 | } 67 | }, 68 | "lint-staged": { 69 | "*.{js,json,css,md}": [ 70 | "npm run prettier", 71 | "git add" 72 | ] 73 | }, 74 | "publishConfig": { 75 | "access": "public" 76 | }, 77 | "devDependencies": { 78 | "@types/benchmark": "^1.0.31", 79 | "@types/jest": "^24.0.6", 80 | "@types/node": "^11.9.4", 81 | "benchmark": "^2.1.4", 82 | "denque": "^1.3.0", 83 | "double-ended-queue": "^2.1.0-0", 84 | "husky": "^1.2.0", 85 | "jest": "^24.1.0", 86 | "lint-staged": "^8.1.0", 87 | "prettier": "^1.14.2", 88 | "rimraf": "^2.5.4", 89 | "ts-jest": "^23.1.4", 90 | "ts-node": "^8.0.2", 91 | "tslint": "^5.0.0", 92 | "tslint-config-prettier": "^1.15.0", 93 | "tslint-config-standard": "^8.0.0", 94 | "typescript": "^3.0.3" 95 | }, 96 | "dependencies": {} 97 | } 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deque 2 | 3 | [![NPM version][npm-image]][npm-url] 4 | [![NPM downloads][downloads-image]][downloads-url] 5 | [![Build status][travis-image]][travis-url] 6 | [![Test coverage][coveralls-image]][coveralls-url] 7 | 8 | > Deques are a generalization of stacks and queues (the name is pronounced "deck" and is short for "double-ended queue"). 9 | > -- [Python `collections`](https://docs.python.org/3/library/collections.html#collections.deque). 10 | 11 | ## Installation 12 | 13 | ``` 14 | npm install @blakeembrey/deque --save 15 | ``` 16 | 17 | ## Usage 18 | 19 | - `size` Returns the number of elements in the deque. 20 | - `push(x)` Add `x` to right side of the deque. 21 | - `pushLeft(x)` Add `x` to the left side of the deque. 22 | - `clear()` Remove all elements from the deque leaving it with length 0. 23 | - `extend(iterable)` Extend the right side of the deque by appending elements from iterable. 24 | - `extendLeft(iterable)` Extend the left side of the deque by appending elements from iterable. 25 | - `peek(i)` Return the element at index `i` in the deque. 26 | - `indexOf(x, start?)` Return the position of `x` in the deque. 27 | - `has(x)` Return a boolean indicating whether `x` is in the deque. 28 | - `insert(i, x)` Insert `x` into the deque at position `i`. 29 | - `pop()` Remove and return an element from the right side of the deque. If no elements are present, throws `RangeError`. 30 | - `popLeft()` Return and return an element from the left side of the deque. If no elements are present, throws `RangeError`. 31 | - `delete(i)` Delete the value at position `i`. 32 | - `reverse()` Reverse the elements of the deque in-place. 33 | - `rotate(n=1)` Rotate the deque `n` steps to the right. 34 | - `entries()` Return an iterable of deque. 35 | - `@@iterator()` Return an iterable of deque. 36 | 37 | ```js 38 | import { Deque } from '@blakeembrey/deque' 39 | 40 | const d = new Deque('ghi') 41 | 42 | for (const value of d) { 43 | console.log(value.toUpperCase()) //=> G H I 44 | } 45 | 46 | d.push('j') 47 | d.pushLeft('f') 48 | d //=> Deque(['f', 'g', 'h', 'i', 'j']) 49 | 50 | d.pop() //=> 'j' 51 | d.popLeft() //=> 'f' 52 | 53 | Array.from(d) //=> ['g', 'h', 'i'] 54 | 55 | d.peek(0) //=> 'g' 56 | d.peek(-1) //=> 'i' 57 | 58 | d.extend('jkl') 59 | d //=> Deque(['g', 'h', 'i', 'j', 'k', 'l']) 60 | 61 | d.rotate(1) 62 | d //=> Deque(['l', 'g', 'h', 'i', 'j', 'k']) 63 | 64 | d.rotate(-1) 65 | d //=> Deque(['g', 'h', 'i', 'j', 'k', 'l']) 66 | 67 | const d2 = new Deque(d) 68 | 69 | d2 //=> Deque(['g', 'h', 'i', 'j', 'k', 'l']) 70 | ``` 71 | 72 | ## TypeScript 73 | 74 | This project uses [TypeScript](https://github.com/Microsoft/TypeScript) and publishes definitions on NPM. 75 | 76 | ## Reference 77 | 78 | Circular array implementation originally based on [`denque`](https://github.com/Salakar/denque) with additional optimizations. 79 | 80 | ## License 81 | 82 | Apache 2.0 83 | 84 | [npm-image]: https://img.shields.io/npm/v/@blakeembrey/deque.svg?style=flat 85 | [npm-url]: https://npmjs.org/package/@blakeembrey/deque 86 | [downloads-image]: https://img.shields.io/npm/dm/@blakeembrey/deque.svg?style=flat 87 | [downloads-url]: https://npmjs.org/package/@blakeembrey/deque 88 | [travis-image]: https://img.shields.io/travis/blakeembrey/deque.svg?style=flat 89 | [travis-url]: https://travis-ci.org/blakeembrey/deque 90 | [coveralls-image]: https://img.shields.io/coveralls/blakeembrey/deque.svg?style=flat 91 | [coveralls-url]: https://coveralls.io/r/blakeembrey/deque?branch=master 92 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export class Deque { 2 | private head = 0 3 | private tail = 0 4 | private mask = 1 5 | private list = new Array(2) 6 | 7 | constructor(values?: Iterable) { 8 | if (values) this.extend(values) 9 | } 10 | 11 | private _resize(size: number, length: number) { 12 | const { head, mask } = this 13 | 14 | this.head = 0 15 | this.tail = size 16 | this.mask = length - 1 17 | 18 | // Optimize resize when list is already sorted. 19 | if (head === 0) { 20 | this.list.length = length 21 | return 22 | } 23 | 24 | const sorted = new Array(length) 25 | for (let i = 0; i < size; i++) sorted[i] = this.list[(head + i) & mask] 26 | this.list = sorted 27 | } 28 | 29 | push(value: T): this { 30 | this.list[this.tail] = value 31 | this.tail = (this.tail + 1) & this.mask 32 | if (this.head === this.tail) this._resize(this.list.length, this.list.length << 1) 33 | return this 34 | } 35 | 36 | pushLeft(value: T): this { 37 | this.head = (this.head - 1) & this.mask 38 | this.list[this.head] = value 39 | if (this.head === this.tail) this._resize(this.list.length, this.list.length << 1) 40 | return this 41 | } 42 | 43 | clear() { 44 | this.head = 0 45 | this.tail = 0 46 | } 47 | 48 | extend(values: Iterable) { 49 | for (const value of values) this.push(value) 50 | return this 51 | } 52 | 53 | extendLeft(values: Iterable) { 54 | for (const value of values) this.pushLeft(value) 55 | return this 56 | } 57 | 58 | peek(index: number) { 59 | const { head, size, tail, list } = this 60 | 61 | if ((index | 0) !== index || index >= size || index < -size) { 62 | throw new RangeError('deque index out of range') 63 | } 64 | 65 | const pos = ((index >= 0 ? head : tail) + index) & this.mask 66 | return list[pos] as T 67 | } 68 | 69 | indexOf(needle: T, start = 0) { 70 | const { head, list, size, mask } = this 71 | const offset = start >= 0 ? start : start < -size ? 0 : size + start 72 | 73 | for (let i = offset; i < size; i++) { 74 | if (list[(head + i) & mask] === needle) return i 75 | } 76 | 77 | return -1 78 | } 79 | 80 | has(needle: T) { 81 | const { head, list, size, mask } = this 82 | 83 | for (let i = 0; i < size; i++) { 84 | if (list[(head + i) & mask] === needle) return true 85 | } 86 | 87 | return false 88 | } 89 | 90 | insert(index: number, value: T) { 91 | const pos = (this.head + index) & this.mask 92 | let cur = this.tail 93 | 94 | // Increase tail position by 1. 95 | this.tail = (this.tail + 1) & this.mask 96 | 97 | // Shift items forward 1 to make space for insert. 98 | while (cur !== pos) { 99 | const prev = (cur - 1) & this.mask 100 | this.list[cur] = this.list[prev] 101 | cur = prev 102 | } 103 | 104 | this.list[pos] = value 105 | if (this.head === this.tail) this._resize(this.list.length, this.list.length << 1) 106 | return this 107 | } 108 | 109 | get size() { 110 | return (this.tail - this.head) & this.mask 111 | } 112 | 113 | pop() { 114 | if (this.head === this.tail) throw new RangeError('pop from an empty deque') 115 | 116 | this.tail = (this.tail - 1) & this.mask 117 | const value = this.list[this.tail] as T 118 | this.list[this.tail] = undefined 119 | if (this.size < this.mask >>> 1) this._resize(this.size, this.list.length >>> 1) 120 | return value 121 | } 122 | 123 | popLeft() { 124 | if (this.head === this.tail) throw new RangeError('pop from an empty deque') 125 | 126 | const value = this.list[this.head] as T 127 | this.list[this.head] = undefined 128 | this.head = (this.head + 1) & this.mask 129 | if (this.size < this.mask >>> 1) this._resize(this.size, this.list.length >>> 1) 130 | return value 131 | } 132 | 133 | delete(index: number) { 134 | if (index >= this.size || index < 0) { 135 | throw new RangeError('deque index out of range') 136 | } 137 | 138 | const pos = (this.head + index) & this.mask 139 | let cur = pos 140 | 141 | // Shift items backward 1 to erase position. 142 | while (cur !== this.tail) { 143 | const next = (cur + 1) & this.mask 144 | this.list[cur] = this.list[next] 145 | cur = next 146 | } 147 | 148 | // Decrease tail position by 1. 149 | this.tail = (this.tail - 1) & this.mask 150 | 151 | if (this.size < this.mask >>> 1) this._resize(this.size, this.list.length >>> 1) 152 | 153 | return this 154 | } 155 | 156 | reverse() { 157 | const { head, tail, size, mask } = this 158 | 159 | for (let i = 0; i < ~~(size / 2); i++) { 160 | const a = (tail - i - 1) & mask 161 | const b = (head + i) & mask 162 | 163 | const temp = this.list[a] 164 | this.list[a] = this.list[b] 165 | this.list[b] = temp 166 | } 167 | 168 | return this 169 | } 170 | 171 | rotate(n = 1) { 172 | const { head, tail } = this 173 | 174 | if (n === 0 || head === tail) return this 175 | 176 | this.head = (head - n) & this.mask 177 | this.tail = (tail - n) & this.mask 178 | 179 | if (n > 0) { 180 | for (let i = 1; i <= n; i++) { 181 | const a = (head - i) & this.mask 182 | const b = (tail - i) & this.mask 183 | 184 | this.list[a] = this.list[b] 185 | this.list[b] = undefined 186 | } 187 | } else { 188 | for (let i = 0; i > n; i--) { 189 | const a = (tail - i) & this.mask 190 | const b = (head - i) & this.mask 191 | 192 | this.list[a] = this.list[b] 193 | this.list[b] = undefined 194 | } 195 | } 196 | 197 | return this 198 | } 199 | 200 | *entries(): IterableIterator { 201 | const { head, size, list, mask } = this 202 | 203 | for (let i = 0; i < size; i++) yield list[(head + i) & mask] as T 204 | } 205 | 206 | keys() { 207 | return this.entries() 208 | } 209 | 210 | values() { 211 | return this.entries() 212 | } 213 | 214 | [Symbol.iterator]() { 215 | return this.entries() 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /src/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { Deque } from './index' 2 | 3 | describe('values', () => { 4 | it('should create an iterator of values', () => { 5 | const d = new Deque('abc') 6 | const values = Array.from(d) 7 | 8 | expect(values).toEqual(Array.from('abc')) 9 | }) 10 | 11 | it('should support `Set`-like iterable methods', () => { 12 | const d = new Deque('abc') 13 | 14 | expect(Array.from(d.entries())).toEqual(Array.from('abc')) 15 | expect(Array.from(d.keys())).toEqual(Array.from('abc')) 16 | expect(Array.from(d.values())).toEqual(Array.from('abc')) 17 | }) 18 | }) 19 | 20 | describe('push', () => { 21 | it('should push a value on the right', () => { 22 | const d = new Deque() 23 | 24 | d.push('a') 25 | expect(Array.from(d)).toEqual(Array.from('a')) 26 | d.push('b') 27 | expect(Array.from(d)).toEqual(Array.from('ab')) 28 | d.push('c') 29 | expect(Array.from(d)).toEqual(Array.from('abc')) 30 | }) 31 | }) 32 | 33 | describe('pushLeft', () => { 34 | it('should push a value on the left', () => { 35 | const d = new Deque() 36 | 37 | d.pushLeft('a') 38 | expect(Array.from(d)).toEqual(Array.from('a')) 39 | d.pushLeft('b') 40 | expect(Array.from(d)).toEqual(Array.from('ba')) 41 | d.pushLeft('c') 42 | expect(Array.from(d)).toEqual(Array.from('cba')) 43 | }) 44 | }) 45 | 46 | describe('extend', () => { 47 | it('should extend to the right', () => { 48 | const d = new Deque('abc') 49 | 50 | d.extend('def') 51 | 52 | expect(Array.from(d)).toEqual(Array.from('abcdef')) 53 | }) 54 | }) 55 | 56 | describe('extendLeft', () => { 57 | it('should extend to the left', () => { 58 | const d = new Deque('def') 59 | 60 | d.extendLeft('cba') 61 | 62 | expect(Array.from(d)).toEqual(Array.from('abcdef')) 63 | }) 64 | }) 65 | 66 | describe('pop', () => { 67 | it('should pop right value', () => { 68 | const d = new Deque('abcde') 69 | 70 | expect(Array.from(d)).toEqual(['a', 'b', 'c', 'd', 'e']) 71 | expect(d.pop()).toEqual('e') 72 | expect(Array.from(d)).toEqual(['a', 'b', 'c', 'd']) 73 | expect(d.pop()).toEqual('d') 74 | expect(Array.from(d)).toEqual(['a', 'b', 'c']) 75 | expect(d.pop()).toEqual('c') 76 | expect(Array.from(d)).toEqual(['a', 'b']) 77 | expect(d.pop()).toEqual('b') 78 | expect(Array.from(d)).toEqual(['a']) 79 | expect(d.pop()).toEqual('a') 80 | }) 81 | 82 | it('should not pop empty deque', () => { 83 | const d = new Deque() 84 | 85 | expect(() => d.pop()).toThrow(RangeError) 86 | }) 87 | }) 88 | 89 | describe('popLeft', () => { 90 | it('should pop left value', () => { 91 | const d = new Deque('abcde') 92 | 93 | expect(Array.from(d)).toEqual(['a', 'b', 'c', 'd', 'e']) 94 | expect(d.popLeft()).toEqual('a') 95 | expect(Array.from(d)).toEqual(['b', 'c', 'd', 'e']) 96 | expect(d.popLeft()).toEqual('b') 97 | expect(Array.from(d)).toEqual(['c', 'd', 'e']) 98 | expect(d.popLeft()).toEqual('c') 99 | expect(Array.from(d)).toEqual(['d', 'e']) 100 | expect(d.popLeft()).toEqual('d') 101 | expect(Array.from(d)).toEqual(['e']) 102 | expect(d.popLeft()).toEqual('e') 103 | }) 104 | 105 | it('should not pop left empty deque', () => { 106 | const d = new Deque() 107 | 108 | expect(() => d.popLeft()).toThrow(RangeError) 109 | }) 110 | }) 111 | 112 | describe('peek', () => { 113 | it('should peek values', () => { 114 | const d = new Deque('abc') 115 | 116 | expect(d.peek(0)).toEqual('a') 117 | expect(d.peek(1)).toEqual('b') 118 | expect(d.peek(2)).toEqual('c') 119 | expect(d.peek(-1)).toEqual('c') 120 | expect(d.peek(-2)).toEqual('b') 121 | expect(d.peek(-3)).toEqual('a') 122 | 123 | expect(() => d.peek(3)).toThrow(RangeError) 124 | expect(() => d.peek(-4)).toThrow(RangeError) 125 | }) 126 | 127 | it('should throw on non range', () => { 128 | const d = new Deque('abc') 129 | 130 | expect(() => d.peek('a' as any)).toThrow(RangeError) 131 | }) 132 | 133 | it('should throw on empty deque', () => { 134 | const d = new Deque() 135 | 136 | expect(() => d.peek(0)).toThrow(RangeError) 137 | }) 138 | }) 139 | 140 | describe('clear', () => { 141 | it('should clear a deque', () => { 142 | const d = new Deque('abc') 143 | 144 | expect(Array.from(d)).toEqual(Array.from('abc')) 145 | 146 | d.clear() 147 | 148 | expect(Array.from(d)).toEqual([]) 149 | expect(() => d.peek(0)).toThrow(RangeError) 150 | }) 151 | }) 152 | 153 | describe('indexOf', () => { 154 | it('should search for the position of a value', () => { 155 | const d = new Deque('abc') 156 | 157 | expect(d.indexOf('a')).toEqual(0) 158 | expect(d.indexOf('b')).toEqual(1) 159 | expect(d.indexOf('c')).toEqual(2) 160 | expect(d.indexOf('d')).toEqual(-1) 161 | }) 162 | 163 | it('should search from offset', () => { 164 | const d = new Deque('abcdef') 165 | 166 | expect(d.indexOf('a', 2)).toEqual(-1) 167 | expect(d.indexOf('a', 1)).toEqual(-1) 168 | expect(d.indexOf('a', 0)).toEqual(0) 169 | 170 | expect(d.indexOf('b', 1)).toEqual(1) 171 | expect(d.indexOf('c', 2)).toEqual(2) 172 | expect(d.indexOf('d', 3)).toEqual(3) 173 | expect(d.indexOf('e', 4)).toEqual(4) 174 | expect(d.indexOf('f', 5)).toEqual(5) 175 | 176 | expect(d.indexOf('a', 6)).toEqual(-1) 177 | expect(d.indexOf('a', -1)).toEqual(-1) 178 | expect(d.indexOf('a', -5)).toEqual(-1) 179 | expect(d.indexOf('f', -1)).toEqual(5) 180 | expect(d.indexOf('a', -6)).toEqual(0) 181 | expect(d.indexOf('a', -10)).toEqual(0) 182 | }) 183 | }) 184 | 185 | describe('has', () => { 186 | it('should search for a value', () => { 187 | const d = new Deque('abc') 188 | 189 | expect(d.has('a')).toEqual(true) 190 | expect(d.has('b')).toEqual(true) 191 | expect(d.has('c')).toEqual(true) 192 | expect(d.has('d')).toEqual(false) 193 | }) 194 | }) 195 | 196 | describe('insert', () => { 197 | it('should insert an element at position', () => { 198 | const d = new Deque() 199 | 200 | d.insert(0, 'b') 201 | d.insert(1, 'd') 202 | d.insert(1, 'c') 203 | d.insert(0, 'a') 204 | 205 | expect(Array.from(d)).toEqual(Array.from('abcd')) 206 | }) 207 | 208 | it('should insert at the tail each time', () => { 209 | const d = new Deque() 210 | 211 | d.insert(0, 'a') 212 | d.insert(1, 'b') 213 | d.insert(2, 'c') 214 | d.insert(3, 'd') 215 | d.insert(4, 'e') 216 | 217 | expect(Array.from(d)).toEqual(Array.from('abcde')) 218 | }) 219 | }) 220 | 221 | describe('delete', () => { 222 | it('should delete value at index', () => { 223 | const d = new Deque('abc') 224 | 225 | d.delete(1) 226 | 227 | expect(Array.from(d)).toEqual(Array.from('ac')) 228 | expect(() => d.delete(-1)).toThrowError(RangeError) 229 | 230 | d.delete(0) 231 | 232 | expect(Array.from(d)).toEqual(Array.from('c')) 233 | expect(() => d.delete(1)).toThrowError(RangeError) 234 | 235 | d.delete(0) 236 | 237 | expect(Array.from(d)).toEqual([]) 238 | expect(() => d.delete(0)).toThrowError(RangeError) 239 | }) 240 | 241 | it('should throw when deleting empty deque', () => { 242 | const d = new Deque() 243 | 244 | expect(() => d.delete(0)).toThrow(RangeError) 245 | }) 246 | }) 247 | 248 | describe('reverse', () => { 249 | it('should reverse a deque', () => { 250 | const d = new Deque('abcde') 251 | 252 | d.reverse() 253 | 254 | expect(Array.from(d)).toEqual(Array.from('edcba')) 255 | }) 256 | 257 | it('should reverse an empty deque', () => { 258 | const d = new Deque() 259 | 260 | d.reverse() 261 | 262 | expect(Array.from(d)).toEqual([]) 263 | }) 264 | 265 | it('should reverse a deque with one element', () => { 266 | const d = new Deque('a') 267 | 268 | d.reverse() 269 | 270 | expect(Array.from(d)).toEqual(['a']) 271 | }) 272 | 273 | it('should reverse a rotated deque', () => { 274 | const d = new Deque('abcdefghi') 275 | 276 | d.rotate(5) 277 | expect(Array.from(d)).toEqual(Array.from('efghiabcd')) 278 | 279 | d.reverse() 280 | expect(Array.from(d)).toEqual(Array.from('dcbaihgfe')) 281 | }) 282 | }) 283 | 284 | describe('rotate', () => { 285 | it('should rotate a deque', () => { 286 | const d = new Deque('abcde') 287 | 288 | d.rotate(2) 289 | 290 | expect(Array.from(d)).toEqual(Array.from('deabc')) 291 | 292 | d.rotate(-3) 293 | 294 | expect(Array.from(d)).toEqual(Array.from('bcdea')) 295 | 296 | d.rotate(6) 297 | 298 | expect(Array.from(d)).toEqual(Array.from('abcde')) 299 | }) 300 | 301 | it('should rotate on empty deque', () => { 302 | const d = new Deque() 303 | 304 | d.rotate() 305 | 306 | expect(Array.from(d)).toEqual([]) 307 | }) 308 | }) 309 | 310 | describe('size', () => { 311 | it('should give the deque size', () => { 312 | const d = new Deque('abcdef') 313 | 314 | expect(d.size).toEqual(6) 315 | 316 | d.rotate(3) 317 | 318 | expect(d.size).toEqual(6) 319 | }) 320 | }) 321 | 322 | describe('array resize', () => { 323 | it('should resize array as required', () => { 324 | const d = new Deque() 325 | d.extend('abcd') 326 | 327 | expect(d.pop()).toEqual('d') 328 | expect(d.pop()).toEqual('c') 329 | expect(d.pop()).toEqual('b') 330 | expect(d.pop()).toEqual('a') 331 | expect(() => d.pop()).toThrow(RangeError) 332 | 333 | d.extend('efgh') 334 | d.extend('ijkl') 335 | 336 | expect(d.popLeft()).toEqual('e') 337 | expect(d.popLeft()).toEqual('f') 338 | expect(d.popLeft()).toEqual('g') 339 | expect(d.popLeft()).toEqual('h') 340 | expect(d.popLeft()).toEqual('i') 341 | expect(d.popLeft()).toEqual('j') 342 | expect(d.popLeft()).toEqual('k') 343 | expect(d.popLeft()).toEqual('l') 344 | expect(() => d.popLeft()).toThrow(RangeError) 345 | }) 346 | }) 347 | --------------------------------------------------------------------------------