diff --git a/Nuclex.Support (Xbox 360).csproj b/Nuclex.Support (Xbox 360).csproj
index d810efe..099dbf5 100644
--- a/Nuclex.Support (Xbox 360).csproj
+++ b/Nuclex.Support (Xbox 360).csproj
@@ -70,6 +70,12 @@
AssertHelper.cs
+
+ Deque.cs
+
+
+ Deque.cs
+
Deque.cs
diff --git a/Nuclex.Support.csproj b/Nuclex.Support.csproj
index 9c60d86..8088796 100644
--- a/Nuclex.Support.csproj
+++ b/Nuclex.Support.csproj
@@ -52,6 +52,12 @@
AssertHelper.cs
+
+ Deque.cs
+
+
+ Deque.cs
+
Deque.cs
diff --git a/Source/Collections/Deque.Insertion.cs b/Source/Collections/Deque.Insertion.cs
new file mode 100644
index 0000000..7d9b99e
--- /dev/null
+++ b/Source/Collections/Deque.Insertion.cs
@@ -0,0 +1,184 @@
+using System;
+using System.Collections.Generic;
+using System.Collections;
+
+namespace Nuclex.Support.Collections {
+
+ partial class Deque {
+
+ /// Inserts an item at the beginning of the double-ended queue
+ /// Item that will be inserted into the queue
+ public void AddFirst(ItemType item) {
+ if(this.firstBlockStartIndex > 0) {
+ --this.firstBlockStartIndex;
+ } else { // Need to allocate a new block
+ this.blocks.Insert(0, new ItemType[this.blockSize]);
+ this.firstBlockStartIndex = this.blockSize - 1;
+ }
+
+ this.blocks[0][this.firstBlockStartIndex] = item;
+ ++this.count;
+ }
+
+ /// Appends an item to the end of the double-ended queue
+ /// Item that will be appended to the queue
+ public void AddLast(ItemType item) {
+ if(this.lastBlockEndIndex < this.blockSize) {
+ ++this.lastBlockEndIndex;
+ } else { // Need to allocate a new block
+ this.blocks.Add(new ItemType[this.blockSize]);
+ this.lastBlockEndIndex = 1;
+ }
+
+ this.blocks[this.blocks.Count - 1][this.lastBlockEndIndex - 1] = item;
+ ++this.count;
+ }
+
+ /// Inserts the item at the specified index
+ /// Index the item will be inserted at
+ /// Item that will be inserted
+ public void Insert(int index, ItemType item) {
+ int distanceToRightEnd = this.count - index;
+ if(index < distanceToRightEnd) { // Are we closer to the left end?
+ shiftLeftAndInsert(index, item);
+ } else { // Nope, we're closer to the right end
+ shiftRightAndInsert(index, item);
+ }
+ }
+
+ ///
+ /// Shifts all items before the insertion point to the left and inserts
+ /// the item at the specified index
+ ///
+ /// Index the item will be inserted at
+ /// Item that will be inserted
+ private void shiftLeftAndInsert(int index, ItemType item) {
+ if(index == 0) {
+ AddFirst(item);
+ } else {
+ int blockIndex, subIndex;
+ findIndex(index, out blockIndex, out subIndex);
+
+ int firstBlock = 0;
+ int blockStart;
+
+ // If the first block is full, we need to add another block
+ if(this.firstBlockStartIndex == 0) {
+ this.blocks.Insert(0, new ItemType[this.blockSize]);
+ this.blocks[0][this.blockSize - 1] = this.blocks[1][0];
+ this.firstBlockStartIndex = this.blockSize - 1;
+
+ blockStart = 1;
+ --subIndex;
+ if(subIndex < 0) {
+ subIndex = this.blockSize - 1;
+ } else {
+ ++blockIndex;
+ }
+ ++firstBlock;
+ } else {
+ blockStart = this.firstBlockStartIndex;
+ --this.firstBlockStartIndex;
+
+ --subIndex;
+ if(subIndex < 0) {
+ subIndex = this.blockSize - 1;
+ --blockIndex;
+ }
+ }
+
+ // If the insertion point is not in the first block
+ if(blockIndex != firstBlock) {
+ Array.Copy(
+ this.blocks[firstBlock], blockStart,
+ this.blocks[firstBlock], blockStart - 1,
+ this.blockSize - blockStart
+ );
+ this.blocks[firstBlock][this.blockSize - 1] = this.blocks[firstBlock + 1][0];
+
+ // Move all the blocks following the insertion point to the right by one item.
+ // If there are no blocks inbetween, this for loop will not run.
+ for(int tempIndex = firstBlock + 1; tempIndex < blockIndex; ++tempIndex) {
+ Array.Copy(
+ this.blocks[tempIndex], 1, this.blocks[tempIndex], 0, this.blockSize - 1
+ );
+ this.blocks[tempIndex][this.blockSize - 1] = this.blocks[tempIndex + 1][0];
+ }
+
+ blockStart = 1;
+ }
+
+ // Finally, move the items in the block the insertion takes place in
+ Array.Copy(
+ this.blocks[blockIndex], blockStart,
+ this.blocks[blockIndex], blockStart - 1,
+ subIndex - blockStart + 1
+ );
+
+ this.blocks[blockIndex][subIndex] = item;
+ ++this.count;
+ }
+ }
+
+ ///
+ /// Shifts all items after the insertion point to the right and inserts
+ /// the item at the specified index
+ ///
+ /// Index the item will be inserted at
+ /// Item that will be inserted
+ private void shiftRightAndInsert(int index, ItemType item) {
+ if(index == this.count) {
+ AddLast(item);
+ } else {
+ int blockIndex, subIndex;
+ findIndex(index, out blockIndex, out subIndex);
+
+ int lastBlock = this.blocks.Count - 1;
+ int blockLength;
+
+ // If the lastmost block is full, we need to add another block
+ if(this.lastBlockEndIndex == this.blockSize) {
+ this.blocks.Add(new ItemType[this.blockSize]);
+ this.blocks[lastBlock + 1][0] = this.blocks[lastBlock][this.blockSize - 1];
+ this.lastBlockEndIndex = 1;
+
+ blockLength = this.blockSize - 1;
+ } else {
+ blockLength = this.lastBlockEndIndex;
+ ++this.lastBlockEndIndex;
+ }
+
+ // If the insertion point is not in the lastmost block
+ if(blockIndex != lastBlock) {
+ Array.Copy(
+ this.blocks[lastBlock], 0, this.blocks[lastBlock], 1, blockLength
+ );
+ this.blocks[lastBlock][0] = this.blocks[lastBlock - 1][this.blockSize - 1];
+
+ // Move all the blocks following the insertion point to the right by one item.
+ // If there are no blocks inbetween, this for loop will not run.
+ for(int tempIndex = lastBlock - 1; tempIndex > blockIndex; --tempIndex) {
+ Array.Copy(
+ this.blocks[tempIndex], 0, this.blocks[tempIndex], 1, this.blockSize - 1
+ );
+ this.blocks[tempIndex][0] = this.blocks[tempIndex - 1][this.blockSize - 1];
+ }
+
+ blockLength = this.blockSize - 1;
+ }
+
+ // Finally, move the items in the block the insertion takes place in
+ Array.Copy(
+ this.blocks[blockIndex], subIndex,
+ this.blocks[blockIndex], subIndex + 1,
+ blockLength - subIndex
+ );
+
+ this.blocks[blockIndex][subIndex] = item;
+ ++this.count;
+ }
+ }
+
+ }
+
+} // namespace Nuclex.Support.Collections
diff --git a/Source/Collections/Deque.Removal.cs b/Source/Collections/Deque.Removal.cs
new file mode 100644
index 0000000..a191c8d
--- /dev/null
+++ b/Source/Collections/Deque.Removal.cs
@@ -0,0 +1,174 @@
+using System;
+using System.Collections.Generic;
+using System.Collections;
+
+namespace Nuclex.Support.Collections {
+
+ partial class Deque {
+
+ /// Removes the first item in the double-ended queue
+ public void RemoveFirst() {
+ if(this.count == 0) {
+ throw new InvalidOperationException("Cannot remove items from empty deque");
+ }
+
+ // TODO: Zero removed array entry if array is kept
+
+ ++this.firstBlockStartIndex;
+ if(this.firstBlockStartIndex >= this.blockSize) { // Block became empty
+ if(this.count > 1) { // Still more blocks in queue, remove block
+ this.blocks.RemoveAt(0);
+ this.firstBlockStartIndex = 0;
+ } else { // Last block - do not remove
+ this.firstBlockStartIndex = 0;
+ this.lastBlockEndIndex = 0;
+ }
+ }
+ --this.count;
+ }
+
+ /// Removes the last item in the double-ended queue
+ public void RemoveLast() {
+ if(this.count == 0) {
+ throw new InvalidOperationException("Cannot remove items from empty deque");
+ }
+
+ // TODO: Zero removed array entry if array is kept
+
+ --this.lastBlockEndIndex;
+ if(this.lastBlockEndIndex == 0) { // Block became empty
+ if(this.count > 1) {
+ this.blocks.RemoveAt(this.blocks.Count - 1);
+ this.lastBlockEndIndex = this.blockSize;
+ } else { // Last block - do not remove
+ this.firstBlockStartIndex = 0;
+ this.lastBlockEndIndex = 0;
+ }
+ }
+ --this.count;
+ }
+
+ /// Removes the item at the specified index
+ /// Index of the item that will be removed
+ public void RemoveAt(int index) {
+ int distanceToRightEnd = this.count - index;
+ if(index < distanceToRightEnd) { // Are we closer to the left end?
+ removeFromLeft(index);
+ } else { // Nope, we're closer to the right end
+ removeFromRight(index);
+ }
+ }
+
+ ///
+ /// Removes an item from the left side of the queue by shifting all items that
+ /// come before it to the right by one
+ ///
+ /// Index of the item that will be removed
+ private void removeFromLeft(int index) {
+ if(index == this.count - 1) {
+ RemoveLast();
+ } else {
+ int blockIndex, subIndex;
+ findIndex(index, out blockIndex, out subIndex);
+
+ int lastBlock = this.blocks.Count - 1;
+ int startIndex;
+
+ if(blockIndex < lastBlock) {
+ Array.Copy(
+ this.blocks[blockIndex], subIndex + 1,
+ this.blocks[blockIndex], subIndex,
+ this.blockSize - subIndex - 1
+ );
+ this.blocks[blockIndex][this.blockSize - 1] = this.blocks[blockIndex + 1][0];
+
+ for(int tempIndex = blockIndex + 1; tempIndex < lastBlock; ++tempIndex) {
+ Array.Copy(
+ this.blocks[tempIndex], 1,
+ this.blocks[tempIndex], 0,
+ this.blockSize - 1
+ );
+ this.blocks[tempIndex][this.blockSize - 1] = this.blocks[tempIndex + 1][0];
+ }
+
+ startIndex = 0;
+ } else {
+ startIndex = subIndex;
+ }
+
+ Array.Copy(
+ this.blocks[lastBlock], startIndex + 1,
+ this.blocks[lastBlock], startIndex,
+ this.lastBlockEndIndex - startIndex - 1
+ );
+
+ if(this.lastBlockEndIndex == 1) {
+ this.blocks.RemoveAt(lastBlock);
+ this.lastBlockEndIndex = this.blockSize;
+ } else {
+ this.blocks[lastBlock][this.lastBlockEndIndex - 1] = default(ItemType);
+ --this.lastBlockEndIndex;
+ }
+
+ --this.count;
+ }
+ }
+
+ ///
+ /// Removes an item from the right side of the queue by shifting all items that
+ /// come after it to the left by one
+ ///
+ /// Index of the item that will be removed
+ private void removeFromRight(int index) {
+ if(index == this.count - 1) {
+ RemoveLast();
+ } else {
+ int blockIndex, subIndex;
+ findIndex(index, out blockIndex, out subIndex);
+
+ int lastBlock = this.blocks.Count - 1;
+ int startIndex;
+
+ if(blockIndex < lastBlock) {
+ Array.Copy(
+ this.blocks[blockIndex], subIndex + 1,
+ this.blocks[blockIndex], subIndex,
+ this.blockSize - subIndex - 1
+ );
+ this.blocks[blockIndex][this.blockSize - 1] = this.blocks[blockIndex + 1][0];
+
+ for(int tempIndex = blockIndex + 1; tempIndex < lastBlock; ++tempIndex) {
+ Array.Copy(
+ this.blocks[tempIndex], 1,
+ this.blocks[tempIndex], 0,
+ this.blockSize - 1
+ );
+ this.blocks[tempIndex][this.blockSize - 1] = this.blocks[tempIndex + 1][0];
+ }
+
+ startIndex = 0;
+ } else {
+ startIndex = subIndex;
+ }
+
+ Array.Copy(
+ this.blocks[lastBlock], startIndex + 1,
+ this.blocks[lastBlock], startIndex,
+ this.lastBlockEndIndex - startIndex - 1
+ );
+
+ if(this.lastBlockEndIndex == 1) {
+ this.blocks.RemoveAt(lastBlock);
+ this.lastBlockEndIndex = this.blockSize;
+ } else {
+ this.blocks[lastBlock][this.lastBlockEndIndex - 1] = default(ItemType);
+ --this.lastBlockEndIndex;
+ }
+
+ --this.count;
+ }
+ }
+
+ }
+
+} // namespace Nuclex.Support.Collections
diff --git a/Source/Collections/Deque.Test.cs b/Source/Collections/Deque.Test.cs
index e0c737e..81be7fe 100644
--- a/Source/Collections/Deque.Test.cs
+++ b/Source/Collections/Deque.Test.cs
@@ -28,8 +28,6 @@ using NMock2;
namespace Nuclex.Support.Collections {
-#if true
-
/// Unit Test for the double ended queue
[TestFixture]
public class DequeTest {
@@ -94,6 +92,111 @@ namespace Nuclex.Support.Collections {
}
}
+
+ /// Verifies that the Insert() method works in all cases
+ ///
+ /// We have several different cases here that will be tested. The deque can
+ /// shift items to the left or right (depending on which end is closer to
+ /// the insertion point) and the insertion point may fall in an only partially
+ /// occupied block, requiring elaborate index calculations
+ ///
+ [Test]
+ public void TestInsert() {
+ for(int testedIndex = 0; testedIndex <= 96; ++testedIndex) {
+ Deque intDeque = new Deque(16);
+ for(int item = 0; item < 96; ++item) {
+ intDeque.AddLast(item);
+ }
+
+ intDeque.Insert(testedIndex, 12345);
+
+ Assert.AreEqual(97, intDeque.Count);
+
+ for(int index = 0; index < testedIndex; ++index) {
+ Assert.AreEqual(index, intDeque[index]);
+ }
+ Assert.AreEqual(12345, intDeque[testedIndex]);
+ for(int index = testedIndex + 1; index < 97; ++index) {
+ Assert.AreEqual(index - 1, intDeque[index]);
+ }
+ }
+ }
+
+ /// Verifies that the Insert() method works in all cases
+ [Test]
+ public void TestInsertNonNormalized() {
+ for(int testedIndex = 0; testedIndex <= 96; ++testedIndex) {
+ Deque intDeque = new Deque(16);
+ for(int item = 4; item < 96; ++item) {
+ intDeque.AddLast(item);
+ }
+ intDeque.AddFirst(3);
+ intDeque.AddFirst(2);
+ intDeque.AddFirst(1);
+ intDeque.AddFirst(0);
+
+ intDeque.Insert(testedIndex, 12345);
+
+ Assert.AreEqual(97, intDeque.Count);
+
+ for(int index = 0; index < testedIndex; ++index) {
+ Assert.AreEqual(index, intDeque[index]);
+ }
+ Assert.AreEqual(12345, intDeque[testedIndex]);
+ for(int index = testedIndex + 1; index < 97; ++index) {
+ Assert.AreEqual(index - 1, intDeque[index]);
+ }
+ }
+ }
+
+ /// Verifies the the RemoveAt() method works in all cases
+ [Test]
+ public void TestRemoveAt() {
+ for(int testedIndex = 0; testedIndex < 96; ++testedIndex) {
+ Deque intDeque = new Deque(16);
+ for(int item = 0; item < 96; ++item) {
+ intDeque.AddLast(item);
+ }
+
+ intDeque.RemoveAt(testedIndex);
+
+ Assert.AreEqual(95, intDeque.Count);
+
+ for(int index = 0; index < testedIndex; ++index) {
+ Assert.AreEqual(index, intDeque[index]);
+ }
+ for(int index = testedIndex; index < 95; ++index) {
+ Assert.AreEqual(index + 1, intDeque[index]);
+ }
+ }
+ }
+
+ /// Verifies the the RemoveAt() method works in all cases
+ [Test]
+ public void TestRemoveAtNonNormalized() {
+ for(int testedIndex = 0; testedIndex < 96; ++testedIndex) {
+ Deque intDeque = new Deque(16);
+ for(int item = 4; item < 96; ++item) {
+ intDeque.AddLast(item);
+ }
+ intDeque.AddFirst(3);
+ intDeque.AddFirst(2);
+ intDeque.AddFirst(1);
+ intDeque.AddFirst(0);
+
+ intDeque.RemoveAt(testedIndex);
+
+ Assert.AreEqual(95, intDeque.Count);
+
+ for(int index = 0; index < testedIndex; ++index) {
+ Assert.AreEqual(index, intDeque[index]);
+ }
+ for(int index = testedIndex; index < 95; ++index) {
+ Assert.AreEqual(index + 1, intDeque[index]);
+ }
+ }
+ }
+
///
/// Validates that an exception is thrown if the 'First' property is accessed
/// in an empty deque
@@ -141,178 +244,7 @@ namespace Nuclex.Support.Collections {
delegate() { intDeque.RemoveLast(); }
);
}
-
- ///
- /// Tests whether the Insert() method of the deque can insert an item at
- /// the end of the deque
- ///
- [Test]
- public void TestInsertAtEnd() {
- Deque intDeque = new Deque(16);
- for(int item = 0; item < 48; ++item) {
- intDeque.AddLast(item);
- }
-
- intDeque.Insert(intDeque.Count, 12345);
- Assert.AreEqual(12345, intDeque.Last);
- }
-
- ///
- /// Tests whether the Insert() method of the deque can insert an item into
- /// the last block of the deque when that last block is already full
- ///
- [Test]
- public void TestInsertInLastBlock() {
- Deque intDeque = new Deque(16);
- for(int item = 0; item < 48; ++item) {
- intDeque.AddLast(item);
- }
-
- intDeque.Insert(45, 12345);
-
- Assert.AreEqual(49, intDeque.Count);
- for(int index = 0; index < 44; ++index) {
- Assert.AreEqual(index, intDeque[index]);
- }
- Assert.AreEqual(12345, intDeque[45]);
- for(int index = 46; index < 49; ++index) {
- Assert.AreEqual(index - 1, intDeque[index]);
- }
- }
-
- ///
- /// Tests whether the Insert() method of the deque can insert an item into
- /// the second-to-last block of the deque
- ///
- [Test]
- public void TestInsertInSecondToLastBlock() {
- Deque intDeque = new Deque(16);
- for(int item = 0; item < 40; ++item) {
- intDeque.AddLast(item);
- }
-
- intDeque.Insert(24, 12345);
-
- Assert.AreEqual(41, intDeque.Count);
- for(int index = 0; index < 24; ++index) {
- Assert.AreEqual(index, intDeque[index]);
- }
- Assert.AreEqual(12345, intDeque[24]);
- for(int index = 25; index < 41; ++index) {
- Assert.AreEqual(index - 1, intDeque[index]);
- }
- }
-
- ///
- /// Tests whether the Insert() method of the deque can insert an item into
- /// the third-to-last block of the deque
- ///
- [Test]
- public void TestInsertInThirdToLastBlock() {
- Deque intDeque = new Deque(16);
- for(int item = 0; item < 40; ++item) {
- intDeque.AddLast(item);
- }
-
- intDeque.Insert(8, 12345);
-
- Assert.AreEqual(41, intDeque.Count);
-
- for(int index = 0; index < 8; ++index) {
- Assert.AreEqual(index, intDeque[index]);
- }
- Assert.AreEqual(12345, intDeque[8]);
- for(int index = 9; index < 41; ++index) {
- Assert.AreEqual(index - 1, intDeque[index]);
- }
- }
-
- ///
- /// Tests whether the RemoveAt() method of the deque can remove an item from
- /// the end of the deque
- ///
- [Test]
- public void TestRemoveAtEnd() {
- Deque intDeque = new Deque(16);
- for(int item = 0; item < 48; ++item) {
- intDeque.AddLast(item);
- }
-
- intDeque.RemoveAt(intDeque.Count - 1);
-
- Assert.AreEqual(46, intDeque.Last);
- }
-
- ///
- /// Tests whether the RemoveAt() method of the deque can remove an item
- /// from the last block of the deque
- ///
- [Test]
- public void TestRemoveFromLastBlock() {
- Deque intDeque = new Deque(16);
- for(int item = 0; item < 48; ++item) {
- intDeque.AddLast(item);
- }
-
- intDeque.RemoveAt(45);
-
- Assert.AreEqual(47, intDeque.Count);
-
- for(int index = 0; index < 45; ++index) {
- Assert.AreEqual(index, intDeque[index]);
- }
- for(int index = 45; index < 47; ++index) {
- Assert.AreEqual(index + 1, intDeque[index]);
- }
- }
-
- ///
- /// Tests whether the RemoveAt() method of the deque can remove an item from
- /// the second-to-last block of the deque
- ///
- [Test]
- public void TestRemoveFromSecondToLastBlock() {
- Deque intDeque = new Deque(16);
- for(int item = 0; item < 40; ++item) {
- intDeque.AddLast(item);
- }
-
- intDeque.RemoveAt(24);
-
- Assert.AreEqual(39, intDeque.Count);
-
- for(int index = 0; index < 24; ++index) {
- Assert.AreEqual(index, intDeque[index]);
- }
- for(int index = 24; index < 39; ++index) {
- Assert.AreEqual(index + 1, intDeque[index]);
- }
- }
-
- ///
- /// Tests whether the RemoveAt() method of the deque can remove an item from
- /// the third-to-last block of the deque
- ///
- [Test]
- public void TestRemoveFromThirdToLastBlock() {
- Deque intDeque = new Deque(16);
- for(int item = 0; item < 33; ++item) {
- intDeque.AddLast(item);
- }
-
- intDeque.RemoveAt(8);
-
- Assert.AreEqual(32, intDeque.Count);
-
- for(int index = 0; index < 8; ++index) {
- Assert.AreEqual(index, intDeque[index]);
- }
- for(int index = 8; index < 32; ++index) {
- Assert.AreEqual(index + 1, intDeque[index]);
- }
- }
-
///
/// Verifies that items can be assigned by their index
///
@@ -324,7 +256,7 @@ namespace Nuclex.Support.Collections {
}
intDeque[16] = 12345;
intDeque[17] = 54321;
-
+
for(int index = 0; index < 16; ++index) {
intDeque.RemoveFirst();
}
@@ -343,15 +275,29 @@ namespace Nuclex.Support.Collections {
for(int item = 0; item < 32; ++item) {
intDeque.AddLast(item);
}
-
+
Assert.Throws(
delegate() { Console.WriteLine(intDeque[32]); }
);
}
- }
+ /// Tests the IndexOf() method
+ [Test, TestCase(0), TestCase(16), TestCase(32), TestCase(48)]
+ public void TestIndexOf(int count) {
+ Deque intDeque = new Deque(16);
+ for(int item = 4; item < count; ++item) {
+ intDeque.AddLast(item);
+ }
+ if(count > 3) { intDeque.AddFirst(3); }
+ if(count > 2) { intDeque.AddFirst(2); }
+ if(count > 1) { intDeque.AddFirst(1); }
+ if(count > 0) { intDeque.AddFirst(0); }
-#endif
+ Assert.AreEqual(count - 1, intDeque.IndexOf(count - 1));
+ Assert.AreEqual(-1, intDeque.IndexOf(count));
+ }
+
+ }
} // namespace Nuclex.Support.Collections
diff --git a/Source/Collections/Deque.cs b/Source/Collections/Deque.cs
index 18430d5..2b453e1 100644
--- a/Source/Collections/Deque.cs
+++ b/Source/Collections/Deque.cs
@@ -4,8 +4,6 @@ using System.Collections;
namespace Nuclex.Support.Collections {
-#if true
-
/// A double-ended queue that allocates memory in blocks
/// Type of the items being stored in the queue
///
@@ -20,7 +18,7 @@ namespace Nuclex.Support.Collections {
/// require items to be copied around and still can be accessed by index.
///
///
- public class Deque /*: IList, IList*/ {
+ public partial class Deque /*: IList, IList*/ {
/// Initializes a new deque
public Deque() : this(512) { }
@@ -77,249 +75,60 @@ namespace Nuclex.Support.Collections {
}
}
- /// Removes the first item in the double-ended queue
- public void RemoveFirst() {
- if(this.count == 0) {
- throw new InvalidOperationException("Cannot remove items from empty deque");
- }
-
- // TODO: Zero removed array entry if array is kept
-
- ++this.firstBlockStartIndex;
- if(this.firstBlockStartIndex >= this.blockSize) { // Block became empty
- if(this.count > 1) { // Still more blocks in queue, remove block
- this.blocks.RemoveAt(0);
- this.firstBlockStartIndex = 0;
- } else { // Last block - do not remove
- this.firstBlockStartIndex = 0;
- this.lastBlockEndIndex = 0;
- }
- }
- --this.count;
- }
-
- /// Removes the last item in the double-ended queue
- public void RemoveLast() {
- if(this.count == 0) {
- throw new InvalidOperationException("Cannot remove items from empty deque");
- }
-
- // TODO: Zero removed array entry if array is kept
-
- --this.lastBlockEndIndex;
- if(this.lastBlockEndIndex == 0) { // Block became empty
- if(this.count > 1) {
- this.blocks.RemoveAt(this.blocks.Count - 1);
- this.lastBlockEndIndex = this.blockSize;
- } else { // Last block - do not remove
- this.firstBlockStartIndex = 0;
- this.lastBlockEndIndex = 0;
- }
- }
- --this.count;
- }
-
- /// Inserts an item at the beginning of the double-ended queue
- /// Item that will be inserted into the queue
- public void AddFirst(ItemType item) {
- if(this.firstBlockStartIndex > 0) {
- --this.firstBlockStartIndex;
- } else { // Need to allocate a new block
- this.blocks.Insert(0, new ItemType[this.blockSize]);
- this.firstBlockStartIndex = this.blockSize - 1;
- }
-
- this.blocks[0][this.firstBlockStartIndex] = item;
- ++this.count;
- }
-
- /// Appends an item to the end of the double-ended queue
- /// Item that will be appended to the queue
- public void AddLast(ItemType item) {
- if(this.lastBlockEndIndex < this.blockSize) {
- ++this.lastBlockEndIndex;
- } else { // Need to allocate a new block
- this.blocks.Add(new ItemType[this.blockSize]);
- this.lastBlockEndIndex = 1;
- }
-
- this.blocks[this.blocks.Count - 1][this.lastBlockEndIndex - 1] = item;
- ++this.count;
- }
-
- /// Inserts the item at the specified index
- /// Index the item will be inserted at
- /// Item that will be inserted
- public void Insert(int index, ItemType item) {
- // TODO: Not perfect. Can shift to left or to right in a deque.
- // Rewrite!
- if(index == this.count) {
- AddLast(item);
- } else {
- int blockIndex, subIndex;
- findIndex(index, out blockIndex, out subIndex);
-
- int lastBlock = this.blocks.Count - 1;
- int blockLength;
-
- // If the lastmost block is full, we need to add another block
- if(this.lastBlockEndIndex == this.blockSize) {
- this.blocks.Add(new ItemType[this.blockSize]);
- this.blocks[lastBlock + 1][0] = this.blocks[lastBlock][this.blockSize - 1];
- this.lastBlockEndIndex = 1;
-
- blockLength = this.blockSize - 1;
- } else {
- blockLength = this.lastBlockEndIndex;
- ++this.lastBlockEndIndex;
- }
-
- // If the insertion point is not in the lastmost block
- if(blockIndex != lastBlock) {
- Array.Copy(
- this.blocks[lastBlock], 0, this.blocks[lastBlock], 1, blockLength
- );
- this.blocks[lastBlock][0] = this.blocks[lastBlock - 1][this.blockSize - 1];
-
- // Move all the blocks following the insertion point to the right by one item.
- // If there are no blocks inbetween, this for loop will not run.
- for(int tempIndex = lastBlock - 1; tempIndex > blockIndex; --tempIndex) {
- Array.Copy(
- this.blocks[tempIndex], 0, this.blocks[tempIndex], 1, this.blockSize - 1
- );
- this.blocks[tempIndex][0] = this.blocks[tempIndex - 1][this.blockSize - 1];
- }
-
- blockLength = this.blockSize - 1;
- }
-
- // Finally, move the items in the block the insertion takes place in
- Array.Copy(
- this.blocks[blockIndex], subIndex,
- this.blocks[blockIndex], subIndex + 1,
- blockLength - subIndex
+ ///
+ /// Determines the index of the first occurence of the specified item in the deque
+ ///
+ /// Item that will be located in the deque
+ /// The index of the item or -1 if it wasn't found
+ public int IndexOf(ItemType item) {
+ if(this.blocks.Count == 1) { // Only one block to scan?
+ int length = this.lastBlockEndIndex - this.firstBlockStartIndex;
+ int index = Array.IndexOf(
+ this.blocks[0], item, this.firstBlockStartIndex, length
);
- this.blocks[blockIndex][subIndex] = item;
- ++this.count;
- }
- }
-
- /// Removes the item at the specified index
- /// Index of the item that will be removed
- public void RemoveAt(int index) {
- // TODO: Not perfect. Can shift to left or to right in a deque.
- // Rewrite!
- if(index == this.count - 1) {
- RemoveLast();
- } else {
- int blockIndex, subIndex;
- findIndex(index, out blockIndex, out subIndex);
-
- int lastBlock = this.blocks.Count - 1;
- int startIndex;
-
- if(blockIndex < lastBlock) {
- Array.Copy(
- this.blocks[blockIndex], subIndex + 1,
- this.blocks[blockIndex], subIndex,
- this.blockSize - subIndex - 1
- );
- this.blocks[blockIndex][this.blockSize - 1] = this.blocks[blockIndex + 1][0];
-
- for(int tempIndex = blockIndex + 1; tempIndex < lastBlock; ++tempIndex) {
- Array.Copy(
- this.blocks[tempIndex], 1,
- this.blocks[tempIndex], 0,
- this.blockSize - 1
- );
- this.blocks[tempIndex][this.blockSize - 1] = this.blocks[tempIndex + 1][0];
- }
-
- startIndex = 0;
+ // If we found something, we need to adjust its index so the first item in
+ // the deque always appears at index 0 to the user
+ if(index != -1) {
+ return (index - this.firstBlockStartIndex);
} else {
- startIndex = subIndex;
+ return -1;
}
+ } else { // At least two blocks exist
- Array.Copy(
- this.blocks[lastBlock], startIndex + 1,
- this.blocks[lastBlock], startIndex,
- this.lastBlockEndIndex - startIndex - 1
+ // Scan the first block for the item and if found, return the index
+ int length = this.blockSize - this.firstBlockStartIndex;
+ int index = Array.IndexOf(
+ this.blocks[0], item, this.firstBlockStartIndex, length
);
- if(this.lastBlockEndIndex == 1) {
- this.blocks.RemoveAt(lastBlock);
- this.lastBlockEndIndex = this.blockSize;
- } else {
- this.blocks[lastBlock][this.lastBlockEndIndex - 1] = default(ItemType);
- --this.lastBlockEndIndex;
+ // If we found something, we need to adjust its index
+ if(index != -1) {
+ return (index - this.firstBlockStartIndex);
}
- --this.count;
+ int lastBlock = this.blocks.Count - 1;
+ for(int tempIndex = 1; tempIndex < lastBlock; ++tempIndex) {
+ index = Array.IndexOf(
+ this.blocks[1], item, 0, this.blockSize
+ );
+ if(index != -1) {
+ return (index - this.firstBlockStartIndex + tempIndex * this.blockSize);
+ }
+ }
+
+ // Nothing found, continue the search in the
+ index = Array.IndexOf(
+ this.blocks[lastBlock], item, 0, this.lastBlockEndIndex
+ );
+ if(index == -1) {
+ return -1;
+ } else {
+ return (index - this.firstBlockStartIndex + lastBlock * this.blockSize);
+ }
}
}
- /*
- public int IndexOf(ItemType item) {
- switch(this.blocks.Count) {
-
- // No block exist, so the item cannot be found
- case 0: {
- return -1;
- }
-
- // Only one block exists, start index and end index apply to the same block
- case 1: {
- int count = this.lastBlockEndIndex - this.firstBlockStartIndex;
- int index = Array.IndexOf(
- this.blocks[0], item, this.firstBlockStartIndex, count
- );
-
- // If we found something, we need to adjust its index so the first item in
- // the deque always appears at index 0 to the user
- if(index != -1) {
- return (index - this.firstBlockStartIndex);
- } else {
- return -1;
- }
- }
-
- // Two blocks exist, start index is in first block, end index in second block
- case 2: {
-
- // Scan the first block for the item and if found, return the index
- int count = this.blockSize - this.firstBlockStartIndex;
- int index = Array.IndexOf(
- this.blocks[0], item, this.firstBlockStartIndex, this.blockSize
- );
-
- // If we found something, we need to adjust its index
- if(index != -1) {
- return (index - this.firstBlockStartIndex);
- }
-
- // Nothing found, continue the search in the
- index = Array.IndexOf(
- this.blocks[1], item, 0, this.lastBlockEndIndex
- );
- if(index == -1) {
- return -1;
- } else {
- return (index - this.firstBlockStartIndex + this.blockSize);
- }
- }
-
- default: {
- int count = this.blockSize - this.firstBlockStartIndex;
- int index = Array.IndexOf(
- this.blocks[0], item, this.firstBlockStartIndex, this.blockSize
- );
- return -1;
- }
-
- }
- }
- */
/// Calculates the block index and local sub index of an entry
/// Index of the entry that will be located
/// Index of the block the entry is contained in
@@ -346,6 +155,4 @@ namespace Nuclex.Support.Collections {
}
-#endif
-
} // namespace Nuclex.Support.Collections