diff --git a/Source/Collections/Deque.Test.cs b/Source/Collections/Deque.Test.cs
index 04f1f5d..e0c737e 100644
--- a/Source/Collections/Deque.Test.cs
+++ b/Source/Collections/Deque.Test.cs
@@ -28,7 +28,8 @@ using NMock2;
namespace Nuclex.Support.Collections {
-#if false
+#if true
+
/// Unit Test for the double ended queue
[TestFixture]
public class DequeTest {
@@ -93,7 +94,263 @@ namespace Nuclex.Support.Collections {
}
}
+ ///
+ /// Validates that an exception is thrown if the 'First' property is accessed
+ /// in an empty deque
+ ///
+ [Test]
+ public void TestThrowOnAccessFirstInEmptyDeque() {
+ Deque intDeque = new Deque();
+ Assert.Throws(
+ delegate() { Console.WriteLine(intDeque.First); }
+ );
+ }
+
+ ///
+ /// Validates that an exception is thrown if the 'Last' property is accessed
+ /// in an empty deque
+ ///
+ [Test]
+ public void TestThrowOnAccessLastInEmptyDeque() {
+ Deque intDeque = new Deque();
+ Assert.Throws(
+ delegate() { Console.WriteLine(intDeque.Last); }
+ );
+ }
+
+ ///
+ /// Validates that an exception is thrown if the first item is attempted to be
+ /// removed from an empty deque
+ ///
+ [Test]
+ public void TestThrowOnRemoveFirstFromEmptyDeque() {
+ Deque intDeque = new Deque();
+ Assert.Throws(
+ delegate() { intDeque.RemoveFirst(); }
+ );
+ }
+
+ ///
+ /// Validates that an exception is thrown if the last item is attempted to be
+ /// removed from an empty deque
+ ///
+ [Test]
+ public void TestThrowOnRemoveLastFromEmptyDeque() {
+ Deque intDeque = new Deque();
+ Assert.Throws(
+ 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
+ ///
+ [Test]
+ public void TestIndexAssignment() {
+ Deque intDeque = new Deque(16);
+ for(int item = 0; item < 32; ++item) {
+ intDeque.AddLast(item);
+ }
+ intDeque[16] = 12345;
+ intDeque[17] = 54321;
+
+ for(int index = 0; index < 16; ++index) {
+ intDeque.RemoveFirst();
+ }
+
+ Assert.AreEqual(12345, intDeque.First);
+ intDeque.RemoveFirst();
+ Assert.AreEqual(54321, intDeque.First);
+ }
+
+ ///
+ /// Verifies that an exception is thrown if an invalid index is accessed
+ ///
+ [Test]
+ public void TestThrowOnInvalidIndex() {
+ Deque intDeque = new Deque(16);
+ for(int item = 0; item < 32; ++item) {
+ intDeque.AddLast(item);
+ }
+
+ Assert.Throws(
+ delegate() { Console.WriteLine(intDeque[32]); }
+ );
+ }
+
}
+
#endif
} // namespace Nuclex.Support.Collections
diff --git a/Source/Collections/Deque.cs b/Source/Collections/Deque.cs
index 32ead50..18430d5 100644
--- a/Source/Collections/Deque.cs
+++ b/Source/Collections/Deque.cs
@@ -4,7 +4,8 @@ using System.Collections;
namespace Nuclex.Support.Collections {
-#if false
+#if true
+
/// A double-ended queue that allocates memory in blocks
/// Type of the items being stored in the queue
///
@@ -81,6 +82,8 @@ namespace Nuclex.Support.Collections {
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
@@ -101,6 +104,8 @@ namespace Nuclex.Support.Collections {
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) {
@@ -142,17 +147,118 @@ namespace Nuclex.Support.Collections {
++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);
- //for(int block = this.blocks.Count - 1; block >
+ 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;
}
}
- // http://www.codeproject.com/KB/dotnet/ccnetsandcastle.aspx
+
+ /// 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;
+ } 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;
+ }
+ }
+
/*
public int IndexOf(ItemType item) {
switch(this.blocks.Count) {
@@ -239,6 +345,7 @@ namespace Nuclex.Support.Collections {
private int lastBlockEndIndex;
}
+
#endif
} // namespace Nuclex.Support.Collections
diff --git a/Source/Tracking/TransactionGroup.Test.cs b/Source/Tracking/TransactionGroup.Test.cs
index 6488237..2ee58eb 100644
--- a/Source/Tracking/TransactionGroup.Test.cs
+++ b/Source/Tracking/TransactionGroup.Test.cs
@@ -193,7 +193,7 @@ namespace Nuclex.Support.Tracking {
this.mockery = new Mockery();
}
- /// Validates that the set transaction properly sums the progress
+ /// Validates that the transaction group correctly sums the progress
[Test]
public void TestSummedProgress() {
using(