From 969a2e953c976178af177c71d9900940933cec0c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 21 Dec 2016 03:18:30 -0800 Subject: [PATCH] Implement CircularList.shiftElements --- src/utils/CircularList.test.ts | 96 ++++++++++++++++++++++++++++++++++ src/utils/CircularList.ts | 30 +++++++++++ 2 files changed, 126 insertions(+) diff --git a/src/utils/CircularList.test.ts b/src/utils/CircularList.test.ts index 41bb8299..aaa4a0d9 100644 --- a/src/utils/CircularList.test.ts +++ b/src/utils/CircularList.test.ts @@ -156,4 +156,100 @@ describe('CircularList', () => { assert.equal(list.length, 0); }); }); + + describe('shiftElements', () => { + it('should not mutate the list when count is 0', () => { + const list = new CircularList(5); + list.push(1); + list.push(2); + list.shiftElements(0, 0, 1); + assert.equal(list.length, 2); + assert.equal(list.get(0), 1); + assert.equal(list.get(1), 2); + }); + + it('should throw for invalid args', () => { + const list = new CircularList(5); + list.push(1); + assert.throws(() => list.shiftElements(-1, 1, 1), 'start argument out of range'); + assert.throws(() => list.shiftElements(1, 1, 1), 'start argument out of range'); + assert.throws(() => list.shiftElements(0, 1, -1), 'Cannot shift elements in list beyond index 0'); + }); + + it('should shift an element forward', () => { + const list = new CircularList(5); + list.push(1); + list.push(2); + list.shiftElements(0, 1, 1); + assert.equal(list.length, 2); + assert.equal(list.get(0), 1); + assert.equal(list.get(1), 1); + }); + + it('should shift elements forward', () => { + const list = new CircularList(5); + list.push(1); + list.push(2); + list.push(3); + list.push(4); + list.shiftElements(0, 2, 2); + assert.equal(list.length, 4); + assert.equal(list.get(0), 1); + assert.equal(list.get(1), 2); + assert.equal(list.get(2), 1); + assert.equal(list.get(3), 2); + }); + + it('should shift elements forward, expanding the list if needed', () => { + const list = new CircularList(5); + list.push(1); + list.push(2); + list.shiftElements(0, 2, 2); + assert.equal(list.length, 4); + assert.equal(list.get(0), 1); + assert.equal(list.get(1), 2); + assert.equal(list.get(2), 1); + assert.equal(list.get(3), 2); + }); + + it('should shift elements forward, wrapping the list if needed', () => { + const list = new CircularList(5); + list.push(1); + list.push(2); + list.push(3); + list.push(4); + list.push(5); + list.shiftElements(2, 2, 3); + assert.equal(list.length, 5); + assert.equal(list.get(0), 3); + assert.equal(list.get(1), 4); + assert.equal(list.get(2), 5); + assert.equal(list.get(3), 3); + assert.equal(list.get(4), 4); + }); + + it('should shift an element backwards', () => { + const list = new CircularList(5); + list.push(1); + list.push(2); + list.shiftElements(1, 1, -1); + assert.equal(list.length, 2); + assert.equal(list.get(0), 2); + assert.equal(list.get(1), 2); + }); + + it('should shift elements backwards', () => { + const list = new CircularList(5); + list.push(1); + list.push(2); + list.push(3); + list.push(4); + list.shiftElements(2, 2, -2); + assert.equal(list.length, 4); + assert.equal(list.get(0), 3); + assert.equal(list.get(1), 4); + assert.equal(list.get(2), 3); + assert.equal(list.get(3), 4); + }); + }); }); diff --git a/src/utils/CircularList.ts b/src/utils/CircularList.ts index 1056c21e..a84a98eb 100644 --- a/src/utils/CircularList.ts +++ b/src/utils/CircularList.ts @@ -145,6 +145,36 @@ export class CircularList { this._length -= count; } + public shiftElements(start: number, count: number, offset: number): void { + if (count <= 0) { + return; + } + if (start < 0 || start >= this._length) { + throw new Error('start argument out of range'); + } + if (start + offset < 0) { + throw new Error('Cannot shift elements in list beyond index 0'); + } + + if (offset > 0) { + for (let i = count - 1; i >= 0; i--) { + this.set(start + i + offset, this.get(start + i)); + } + const expandListBy = (start + count + offset) - this._length; + if (expandListBy > 0) { + this._length += expandListBy; + while (this._length > this.maxLength) { + this._length--; + this._startIndex++; + } + } + } else { + for (let i = 0; i < count; i++) { + this.set(start + i + offset, this.get(start + i)); + } + } + } + /** * Gets the cyclic index for the specified regular index. The cyclic index can then be used on the * backing array to get the element associated with the regular index.