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