From 8526d668f77dc9a75474b337a82a5ae583df1c56 Mon Sep 17 00:00:00 2001 From: Salakar Date: Sat, 2 Jul 2016 05:19:05 +0100 Subject: [PATCH] initial commit --- .editorconfig | 11 ++ .gitignore | 124 ++++++++++++++ .travis.yml | 25 +++ README.md | 21 +++ benchmark/print.js | 24 +++ benchmark/thousand.js | 43 +++++ benchmark/two_million.js | 43 +++++ index.js | 257 ++++++++++++++++++++++++++++ package.json | 47 +++++ test/denque.js | 359 +++++++++++++++++++++++++++++++++++++++ 10 files changed, 954 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 README.md create mode 100644 benchmark/print.js create mode 100644 benchmark/thousand.js create mode 100644 benchmark/two_million.js create mode 100644 index.js create mode 100644 package.json create mode 100644 test/denque.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f68e97b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +# http://editorconfig.org + +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..687d476 --- /dev/null +++ b/.gitignore @@ -0,0 +1,124 @@ +/dist +/lib +################################################ +############### .gitignore ################## +################################################ +# +# This file is only relevant if you are using git. +# +# Files which match the splat patterns below will +# be ignored by git. This keeps random crap and +# sensitive credentials from being uploaded to +# your repository. It allows you to configure your +# app for your machine without accidentally +# committing settings which will smash the local +# settings of other developers on your team. +# +# Some reasonable defaults are included below, +# but, of course, you should modify/extend/prune +# to fit your needs! +################################################ + + + + +################################################ +# Local Configuration +# +# Explicitly ignore files which contain: +# +# 1. Sensitive information you'd rather not push to +# your git repository. +# e.g., your personal API keys or passwords. +# +# 2. Environment-specific configuration +# Basically, anything that would be annoying +# to have to change every time you do a +# `git pull` +# e.g., your local development database, or +# the S3 bucket you're using for file uploads +# development. +# +################################################ + +config/local.js + + + + + +################################################ +# Dependencies +# +# When releasing a production app, you may +# consider including your node_modules and +# bower_components directory in your git repo, +# but during development, its best to exclude it, +# since different developers may be working on +# different kernels, where dependencies would +# need to be recompiled anyway. +# +# More on that here about node_modules dir: +# http://www.futurealoof.com/posts/nodemodules-in-git.html +# (credit Mikeal Rogers, @mikeal) +# +# About bower_components dir, you can see this: +# http://addyosmani.com/blog/checking-in-front-end-dependencies/ +# (credit Addy Osmani, @addyosmani) +# +################################################ + +node_modules +bower_components + + + + +################################################ +# Sails.js / Waterline / Grunt +# +# Files generated by Sails and Grunt, or related +# tasks and adapters. +################################################ +.tmp +dump.rdb + + + + + +################################################ +# Node.js / NPM +# +# Common files generated by Node, NPM, and the +# related ecosystem. +################################################ +lib-cov +*.seed +*.log +*.out +*.pid +npm-debug.log + + + + + +################################################ +# Miscellaneous +# +# Common files generated by text editors, +# operating systems, file systems, etc. +################################################ + +*~ +*# +.DS_STORE +.netbeans +nbproject +.idea +.node_history + + +# Coverage directory used by tools like istanbul +coverage diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7229e8a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +language: node_js + +node_js: +- '4' +- '5' +- '6' + +script: +- npm run test + +env: +- CXX=g++-4.8 + + +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.8 + code_climate: + repo_token: 4cee2f60edbf31acac6ddff823f0b93e2e9882c3e5c55130049e0fd878549f84 + +after_success: +- npm run coverage diff --git a/README.md b/README.md new file mode 100644 index 0000000..dbf6655 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# DENQUE + +[![Coverage Status](https://coveralls.io/repos/github/Salakar/denque/badge.svg?branch=master)](https://coveralls.io/github/Salakar/denque?branch=master) +![Downloads](https://img.shields.io/npm/dt/denque.svg) +[![npm version](https://img.shields.io/npm/v/denque.svg)](https://www.npmjs.com/package/denque) +[![dependencies](https://img.shields.io/david/Salakar/denque.svg)](https://david-dm.org/Salakar/denque) +[![build](https://travis-ci.org/Salakar/denque.svg)](https://travis-ci.org/Salakar/denque) +[![License](https://img.shields.io/npm/l/denque.svg)](/LICENSE) + + +## Benchmarks + +### 1000 items in queue + + denque x 31,015,027 ops/sec ±1.52% (86 runs sampled) + double-ended-queue x 21,350,509 ops/sec ±1.21% (86 runs sampled) + +### 2 million items in queue + + denque x 28,710,051 ops/sec ±0.95% (87 runs sampled) + double-ended-queue x 20,531,490 ops/sec ±1.04% (89 runs sampled) diff --git a/benchmark/print.js b/benchmark/print.js new file mode 100644 index 0000000..77585d1 --- /dev/null +++ b/benchmark/print.js @@ -0,0 +1,24 @@ +'use strict'; + +console.log("Platform info:"); + +const os = require("os"); +const v8 = process.versions.v8; +const node = process.versions.node; +const plat = os.type() + " " + os.release() + " " + os.arch() + "\nNode.JS " + node + "\nV8 " + v8; + +let cpus = os.cpus().map(function (cpu) { + return cpu.model; +}).reduce(function (o, model) { + if (!o[model]) o[model] = 0; + o[model]++; + return o; +}, {}); + +cpus = Object.keys(cpus).map(function (key) { + return key + " \u00d7 " + cpus[key]; +}).join("\n"); + +console.log(plat + "\n" + cpus + "\n"); + +module.exports = {}; diff --git a/benchmark/thousand.js b/benchmark/thousand.js new file mode 100644 index 0000000..e35a829 --- /dev/null +++ b/benchmark/thousand.js @@ -0,0 +1,43 @@ +'use strict'; + +require('./print'); + +const Benchmark = require('benchmark'); +const suite = new Benchmark.Suite(); + +const Denque = require('./../'); +const DoubleEndedQueue = require('double-ended-queue'); + +const denque = new Denque(); +const doubleEndedQueue = new DoubleEndedQueue(); + +let l = 1000; + +while (--l) { + denque.push(l); + doubleEndedQueue.push(l); +} + +suite + .add('denque', function () { + const a = denque.shift(); + const b = denque.shift(); + const c = denque.shift(); + + denque.push(a); + denque.push(b); + denque.push(c); + }) + .add('double-ended-queue', function () { + const a = doubleEndedQueue.shift(); + const b = doubleEndedQueue.shift(); + const c = doubleEndedQueue.shift(); + + doubleEndedQueue.push(a); + doubleEndedQueue.push(b); + doubleEndedQueue.push(c); + }) + .on('cycle', function (e) { + console.log('' + e.target); + }) + .run(); diff --git a/benchmark/two_million.js b/benchmark/two_million.js new file mode 100644 index 0000000..9ba4a7f --- /dev/null +++ b/benchmark/two_million.js @@ -0,0 +1,43 @@ +'use strict'; + +require('./print'); + +const Benchmark = require('benchmark'); +const suite = new Benchmark.Suite(); + +const Denque = require('./../'); +const DoubleEndedQueue = require('double-ended-queue'); + +const denque = new Denque(); +const doubleEndedQueue = new DoubleEndedQueue(); + +let l = 2000000; + +while (--l) { + denque.push(l); + doubleEndedQueue.push(l); +} + +suite + .add('denque', function () { + const a = denque.shift(); + const b = denque.shift(); + const c = denque.shift(); + + denque.push(a); + denque.push(b); + denque.push(c); + }) + .add('double-ended-queue', function () { + const a = doubleEndedQueue.shift(); + const b = doubleEndedQueue.shift(); + const c = doubleEndedQueue.shift(); + + doubleEndedQueue.push(a); + doubleEndedQueue.push(b); + doubleEndedQueue.push(c); + }) + .on('cycle', function (e) { + console.log('' + e.target); + }) + .run(); diff --git a/index.js b/index.js new file mode 100644 index 0000000..393d451 --- /dev/null +++ b/index.js @@ -0,0 +1,257 @@ +'use strict'; + +/** + * Custom implementation of a double ended queue. + * This is almost double the performance of petkaantonov's double-ended-queue lib. ¯\_(ツ)_/¯ + * 24 mil ops/s compared to 14 mil + * + */ +class QueueList { + constructor(array) { + // circular buffer + this._list = new Array(4); + // bit mask + this._capacityMask = 0x3; + // next unread item + this._head = 0; + // next empty slot + this._tail = 0; + + if (Array.isArray(array)) { + this._fromArray(array); + } + } + + /** + * ------------- + * PUBLIC API + * ------------- + */ + + /** + * Returns the item at the specified index from the list. + * 0 is the first element, 1 is the second, and so on... + * Elements at negative values are that many from the end: -1 is one before the end + * (the last element), -2 is two before the end (one before last), etc. + * @param index + * @returns {*} + */ + peekAt(index) { + let i = index; + // expect a number or return undefined + if ((i !== (i | 0))) { + return void 0; + } + const len = this.size(); + if (i >= len || i < -len) return undefined; + if (i < 0) i += len; + i = (this._head + i) & this._capacityMask; + return this._list[i]; + } + + /** + * Alias for peakAt() + * @param i + * @returns {*} + */ + get(i) { + return this.peekAt(i); + } + + /** + * Returns the first item in the list without removing it. + * @returns {*} + */ + peek() { + if (this._head === this._tail) return undefined; + return this._list[this._head]; + } + + /** + * Alias for peek() + * @returns {*} + */ + peekFront() { + return this.peek(); + } + + /** + * Returns the item that is at the back of the queue without removing it. + * Uses peekAt(-1) + */ + peekBack() { + return this.peekAt(-1); + } + + /** + * Return the number of items on the list, or 0 if empty. + * @returns {number} + */ + size() { + if (this._head === this._tail) return 0; + if (this._head < this._tail) return this._tail - this._head; + else return this._capacityMask + 1 - (this._head - this._tail); + } + + /** + * Add an item at the beginning of the list. + * @param item + */ + unshift(item) { + if (!item) return this.length; + const len = this._list.length; + this._head = (this._head - 1 + len) & this._capacityMask; + this._list[this._head] = item; + if (this._tail === this._head) this._growArray(); + if (this._head < this._tail) return this._tail - this._head; + else return this._capacityMask + 1 - (this._head - this._tail); + } + + /** + * Remove and return the first item on the list, + * Returns undefined if the list is empty. + * @returns {*} + */ + shift() { + const head = this._head; + if (head === this._tail) return undefined; + const item = this._list[head]; + this._head = (head + 1) & this._capacityMask; + if (head < 2 && this._tail > 10000 && this._tail <= this._list.length >>> 2) this._shrinkArray(); + return item; + } + + /** + * Add an item to the bottom of the list. + * @param item + */ + push(item) { + if (!item) return this.length; + const tail = this._tail; + this._list[tail] = item; + this._tail = (tail + 1) & this._capacityMask; + if (this._tail === this._head) { + this._growArray(); + } + + if (this._head < this._tail) return this._tail - this._head; + else return this._capacityMask + 1 - (this._head - this._tail); + } + + /** + * Remove and return the last item on the list. + * Returns undefined if the list is empty. + * @returns {*} + */ + pop() { + const tail = this._tail; + if (tail === this._head) return undefined; + const len = this._list.length; + this._tail = (tail - 1 + len) & this._capacityMask; + const item = this._list[this._tail]; + if (this._head < 2 && tail > 10000 && tail <= len >>> 2) this._shrinkArray(); + return item; + } + + /** + * Soft clear - does not reset capacity. + */ + clear() { + this._head = 0; + this._tail = 0; + } + + /** + * Returns true or false whether the list is empty. + * @returns {boolean} + */ + isEmpty() { + return this._head === this._tail; + } + + /** + * Returns an array of all queue items. + * @returns {Array} + */ + toArray() { + return this._copyArray(false); + } + + /** + * ------------- + * INTERNALS + * ------------- + */ + + /** + * Fills the queue with items from an array + * For use in the constructor + * @param array + * @private + */ + _fromArray(array) { + // const list = this._list = new Array(0); + // while (list.length < array.length) this._growArray(); + for (let i = 0; i < array.length; i++) this.push(array[i]); + // console.log(this._list); + // this._tail = array.length & this._capacityMask; + } + + /** + * + * @param fullCopy + * @returns {Array} + * @private + */ + _copyArray(fullCopy) { + const newArray = []; + const list = this._list; + const len = list.length; + let i; + if (fullCopy || this._head > this._tail) { + for (i = this._head; i < len; i++) newArray.push(list[i]); + for (i = 0; i < this._tail; i++) newArray.push(list[i]); + } else { + for (i = this._head; i < this._tail; i++) newArray.push(list[i]); + } + return newArray; + } + + /** + * Grows the internal list array. + * @private + */ + _growArray() { + if (this._head) { + // copy existing data, head to end, then beginning to tail. + this._list = this._copyArray(true); + this._head = 0; + } + + // head is at 0 and array is now full, safe to extend + this._tail = this._list.length; + // console.log(this._list.length); + this._list.length *= 2; + this._capacityMask = (this._capacityMask << 1) | 1; + } + + /** + * Shrinks the internal list array. + * @private + */ + _shrinkArray() { + this._list.length >>>= 1; + this._capacityMask >>>= 1; + } + +} + +Object.defineProperty(QueueList.prototype, 'length', { + get() { + if (this._head === this._tail) return 0; + if (this._head < this._tail) return this._tail - this._head; + else return this._capacityMask + 1 - (this._head - this._tail); + } +}); + +module.exports = QueueList; diff --git a/package.json b/package.json new file mode 100644 index 0000000..384563e --- /dev/null +++ b/package.json @@ -0,0 +1,47 @@ +{ + "name": "denque", + "version": "1.0.0", + "description": "The fastest javascript implementation of a double-ended queue. Maintains compatability with deque.", + "main": "index.js", + "engines": { + "node": ">=4.0" + }, + "keywords": [ + "data-structure", + "data-structures", + "queue", + "double", + "end", + "ended", + "deque", + "denque", + "double-ended-queue" + ], + "scripts": { + "test": "istanbul cover --report lcov _mocha", + "coveralls": "cat ./coverage/lcov.info | coveralls", + "benchmark_thousand": "node benchmark/thousand", + "benchmark_two_million": "node benchmark/two_million" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Salakar/denque.git" + }, + "author": { + "name": "Mike Diarmid", + "email": "mike.diarmid@teamfa.com", + "url": "http://github.com/Salakar/" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/Salakar/denque/issues" + }, + "homepage": "https://github.com/Salakar/denque#readme", + "devDependencies": { + "benchmark": "^2.1.0", + "coveralls": "^2.11.9", + "double-ended-queue": "^2.1.0-0", + "istanbul": "^0.4.4", + "mocha": "^2.5.3" + } +} diff --git a/test/denque.js b/test/denque.js new file mode 100644 index 0000000..7267364 --- /dev/null +++ b/test/denque.js @@ -0,0 +1,359 @@ +'use strict'; + +const assert = require('assert'); +const Denque = require('../'); + +describe('Denque.prototype.constructor', function () { + it("should take no argument", function () { + const a = new Denque(); + assert(a._capacityMask === 3); + assert(a._list.length === 4); + assert(a.size() === 0); + assert(a.length === 0); + }); + + it("should take array argument", function () { + const a = new Denque([1, 2, 3, 4]); + const b = new Denque([]); + + assert(a.length >= 4); + assert.deepEqual(a.toArray(), [1, 2, 3, 4]); + assert(b.length === 0); + assert.deepEqual(b.toArray(), []); + }); + + it("should handle a high volume with no out of memory exception", function () { + this.timeout(20000); + const denque = new Denque(); + let l = 250000; + + while (--l) { + denque.push(l); + denque.unshift(l); + } + + l = 125000; + while (--l) { + const a = denque.shift(); + denque.pop(); + denque.shift(); + denque.push(a); + denque.shift(); + denque.shift(); + } + + // console.log(denque._list.length); + // console.log(denque.length); + // console.log(denque._head); + // console.log(denque._tail); + + denque.clear(); + l = 100000; + + while (--l) { + denque.push(l); + } + + l = 100000; + while (--l) { + denque.shift(); + denque.shift(); + denque.shift(); + if (l=== 25000) denque.clear(); + denque.pop(); + denque.pop(); + denque.pop(); + } + + // console.log(denque._list.length); + // console.log(denque.length); + // console.log(denque._head); + // console.log(denque._tail); + + }); +}); + +describe('Denque.prototype.toArray', function () { + it("should return an array", function () { + const a = new Denque([1, 2, 3, 4]); + assert.deepEqual(a.toArray(), [1, 2, 3, 4]); + }); +}); + +describe('Denque.prototype.push', function () { + it("Should do nothing if no arguments", function () { + const a = new Denque(); + const before = a.length; + const ret = a.push(); + assert(ret === before); + assert(a.length === ret); + assert(ret === 0); + }); + + it("Should add single argument - plenty of capacity", function () { + const a = new Denque([1, 2, 3, 4, 5]); + assert(a._list.length - a.length > 1); + const before = a.length; + const ret = a.push(1); + assert(ret === before + 1); + assert(a.length === ret); + assert(ret === 6); + assert.deepEqual(a.toArray(), [1, 2, 3, 4, 5, 1]); + }); + + it("Should add single argument - exact capacity", function () { + const a = new Denque([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + assert(a._list.length - a.length === 1); + const before = a.length; + const ret = a.push(1); + assert(ret === before + 1); + assert(a.length === ret); + assert(ret === 16); + assert.deepEqual(a.toArray(), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1]); + }); + + it("Should add single argument - over capacity", function () { + const a = new Denque([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + assert(a._list.length / a.length === 2); + const before = a.length; + const ret = a.push(1); + assert(ret === before + 1); + assert(a.length === ret); + assert(ret === 17); + assert.deepEqual(a.toArray(), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1]); + }); + +}); + +describe('Denque.prototype.unshift', function () { + + it("Should do nothing if no arguments", function () { + const a = new Denque(); + const before = a.length; + const ret = a.unshift(); + assert(ret === before); + assert(a.length === ret); + assert(ret === 0); + }); + + it("Should add single argument - plenty of capacity", function () { + const a = new Denque([1, 2, 3, 4, 5]); + assert(a._list.length - a.length > 1); + const before = a.length; + const ret = a.unshift(1); + assert(ret === before + 1); + assert(a.length === ret); + assert(ret === 6); + assert.deepEqual(a.toArray(), [1, 1, 2, 3, 4, 5]); + }); + + it("Should add single argument - exact capacity", function () { + const a = new Denque([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + assert(a._list.length - a.length === 1); + const before = a.length; + const ret = a.unshift(1); + assert(ret === before + 1); + assert(a.length === ret); + assert(ret === 16); + assert.deepEqual(a.toArray(), [1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + }); + + it("Should add single argument - over capacity", function () { + const a = new Denque([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + assert(a._list.length / a.length === 2); + const before = a.length; + const ret = a.unshift(1); + assert(ret === before + 1); + assert(a.length === ret); + assert(ret === 17); + assert.deepEqual(a.toArray(), [1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + }); + +}); + +describe('Denque.prototype.pop', function () { + it("Should return undefined when empty denque", function () { + const a = new Denque(); + assert(a.length === 0); + assert(a.pop() === void 0); + assert(a.pop() === void 0); + assert(a.length === 0); + }); + + it("Should return the item at the back of the denque", function () { + const a = new Denque([1, 2, 3, 4, 5, 6, 7, 8, 9]); + const b = []; + + b.push(1, 2, 3, 4, 5, 6, 7, 8, 9); + + assert(a.pop() === 9); + assert(a.pop() === 8); + b.pop(); + b.pop(); + assert.deepEqual(a.toArray(), b); + a.unshift(5); + a.unshift(4); + a.unshift(3); + a.unshift(2); + a.unshift(1); + a.push(1); + a.push(2); + a.push(3); + a.push(4); + a.push(5); + a.unshift(3); + a.unshift(2); + a.unshift(1); + a.pop(); + b.unshift(1, 2, 3, 4, 5); + b.push(1, 2, 3, 4, 5); + b.unshift(1, 2, 3); + b.pop(); + assert.deepEqual(a.toArray(), b); + assert(a.pop() === b.pop()); + assert.deepEqual(a.toArray(), b); + }); +}); + +describe('Deque.prototype.shift', function () { + it("Should return undefined when empty denque", function () { + const a = new Denque(); + assert(a.length === 0); + assert(a.shift() === void 0); + assert(a.shift() === void 0); + assert(a.length === 0); + }); + + it("Should return the item at the front of the denque", function () { + const a = new Denque([1, 2, 3, 4, 5, 6, 7, 8, 9]); + const b = []; + + b.push(1, 2, 3, 4, 5, 6, 7, 8, 9); + + assert(a.shift() === 1); + assert(a.shift() === 2); + b.shift(); + b.shift(); + assert.deepEqual(a.toArray(), b); + a.unshift(5); + a.unshift(4); + a.unshift(3); + a.unshift(2); + a.unshift(1); + a.push(1); + a.push(2); + a.push(3); + a.push(4); + a.push(5); + a.unshift(3); + a.unshift(2); + a.unshift(1); + a.shift(); + b.unshift(1, 2, 3, 4, 5); + b.push(1, 2, 3, 4, 5); + b.unshift(1, 2, 3); + b.shift(); + assert.deepEqual(a.toArray(), b); + assert(a.shift() === b.shift()); + assert.deepEqual(a.toArray(), b); + }); +}); + +describe('Denque.prototype.get', function () { + it("should return undefined on nonsensical argument", function () { + const a = new Denque([1, 2, 3, 4]); + assert(a.get(-5) === void 0); + assert(a.get(-100) === void 0); + assert(a.get(void 0) === void 0); + assert(a.get("1") === void 0); + assert(a.get(NaN) === void 0); + assert(a.get(Infinity) === void 0); + assert(a.get(-Infinity) === void 0); + assert(a.get(1.5) === void 0); + assert(a.get(4) === void 0); + }); + + it("should support positive indexing", function () { + const a = new Denque([1, 2, 3, 4]); + assert(a.get(0) === 1); + assert(a.get(1) === 2); + assert(a.get(2) === 3); + assert(a.get(3) === 4); + }); + + it("should support negative indexing", function () { + const a = new Denque([1, 2, 3, 4]); + assert(a.get(-1) === 4); + assert(a.get(-2) === 3); + assert(a.get(-3) === 2); + assert(a.get(-4) === 1); + }); +}); + +describe('Denque.prototype.isEmpty', function () { + it("should return true on empty denque", function () { + const a = new Denque(); + assert(a.isEmpty()); + }); + + it("should return false on denque with items", function () { + const a = new Denque([1]); + assert(!a.isEmpty()); + }); +}); + +describe('Denque.prototype.peekFront', function () { + it("Should return undefined when queue is empty", function() { + const a = new Denque(); + assert(a.length === 0); + assert(a.peekFront() === void 0); + assert(a.peekFront() === void 0); + assert(a.length === 0); + }); + + it("should return the item at the front of the denque", function () { + const a = new Denque([1, 2, 3, 4, 5, 6, 7, 8, 9]); + assert(a.peekFront() === 1); + + let l = 5; + while (l--) a.pop(); + + assert.deepEqual(a.toArray(), [1, 2, 3, 4]); + + assert(a.peekFront() === 1); + }); +}); + +describe('Denque.prototype.peekBack', function () { + it("Should return undefined when queue is empty", function() { + const a = new Denque(); + assert(a.length === 0); + assert(a.peekBack() === void 0); + assert(a.peekBack() === void 0); + assert(a.length === 0); + }); + + it("should return the item at the back of the denque", function () { + const a = new Denque([1, 2, 3, 4, 5, 6, 7, 8, 9]); + assert(a.peekBack() === 9); + + let l = 5; + while (l--) a.pop(); + + assert.deepEqual(a.toArray(), [1, 2, 3, 4]); + + assert(a.peekBack() === 4); + }); +}); + + +describe('Denque.prototype.clear', function () { + it("should clear the denque", function () { + const a = new Denque([1, 2, 3, 4]); + assert(!a.isEmpty()); + a.clear(); + assert(a.isEmpty()); + }); +}); + +