Implemented deque Insert() and RemoveAt() methods with fixed shifting of all items to the right for now - still have to extend the code to shift items to the closest end; added more unit tests to achieve 100% test coverage for the code

git-svn-id: file:///srv/devel/repo-conversion/nusu@159 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2009-07-11 19:55:59 +00:00
parent ff44edcdf1
commit ed62fa85f2
3 changed files with 369 additions and 5 deletions

View File

@ -28,7 +28,8 @@ using NMock2;
namespace Nuclex.Support.Collections { namespace Nuclex.Support.Collections {
#if false #if true
/// <summary>Unit Test for the double ended queue</summary> /// <summary>Unit Test for the double ended queue</summary>
[TestFixture] [TestFixture]
public class DequeTest { public class DequeTest {
@ -93,7 +94,263 @@ namespace Nuclex.Support.Collections {
} }
} }
/// <summary>
/// Validates that an exception is thrown if the 'First' property is accessed
/// in an empty deque
/// </summary>
[Test]
public void TestThrowOnAccessFirstInEmptyDeque() {
Deque<int> intDeque = new Deque<int>();
Assert.Throws<InvalidOperationException>(
delegate() { Console.WriteLine(intDeque.First); }
);
} }
/// <summary>
/// Validates that an exception is thrown if the 'Last' property is accessed
/// in an empty deque
/// </summary>
[Test]
public void TestThrowOnAccessLastInEmptyDeque() {
Deque<int> intDeque = new Deque<int>();
Assert.Throws<InvalidOperationException>(
delegate() { Console.WriteLine(intDeque.Last); }
);
}
/// <summary>
/// Validates that an exception is thrown if the first item is attempted to be
/// removed from an empty deque
/// </summary>
[Test]
public void TestThrowOnRemoveFirstFromEmptyDeque() {
Deque<int> intDeque = new Deque<int>();
Assert.Throws<InvalidOperationException>(
delegate() { intDeque.RemoveFirst(); }
);
}
/// <summary>
/// Validates that an exception is thrown if the last item is attempted to be
/// removed from an empty deque
/// </summary>
[Test]
public void TestThrowOnRemoveLastFromEmptyDeque() {
Deque<int> intDeque = new Deque<int>();
Assert.Throws<InvalidOperationException>(
delegate() { intDeque.RemoveLast(); }
);
}
/// <summary>
/// Tests whether the Insert() method of the deque can insert an item at
/// the end of the deque
/// </summary>
[Test]
public void TestInsertAtEnd() {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 0; item < 48; ++item) {
intDeque.AddLast(item);
}
intDeque.Insert(intDeque.Count, 12345);
Assert.AreEqual(12345, intDeque.Last);
}
/// <summary>
/// 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
/// </summary>
[Test]
public void TestInsertInLastBlock() {
Deque<int> intDeque = new Deque<int>(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]);
}
}
/// <summary>
/// Tests whether the Insert() method of the deque can insert an item into
/// the second-to-last block of the deque
/// </summary>
[Test]
public void TestInsertInSecondToLastBlock() {
Deque<int> intDeque = new Deque<int>(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]);
}
}
/// <summary>
/// Tests whether the Insert() method of the deque can insert an item into
/// the third-to-last block of the deque
/// </summary>
[Test]
public void TestInsertInThirdToLastBlock() {
Deque<int> intDeque = new Deque<int>(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]);
}
}
/// <summary>
/// Tests whether the RemoveAt() method of the deque can remove an item from
/// the end of the deque
/// </summary>
[Test]
public void TestRemoveAtEnd() {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 0; item < 48; ++item) {
intDeque.AddLast(item);
}
intDeque.RemoveAt(intDeque.Count - 1);
Assert.AreEqual(46, intDeque.Last);
}
/// <summary>
/// Tests whether the RemoveAt() method of the deque can remove an item
/// from the last block of the deque
/// </summary>
[Test]
public void TestRemoveFromLastBlock() {
Deque<int> intDeque = new Deque<int>(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]);
}
}
/// <summary>
/// Tests whether the RemoveAt() method of the deque can remove an item from
/// the second-to-last block of the deque
/// </summary>
[Test]
public void TestRemoveFromSecondToLastBlock() {
Deque<int> intDeque = new Deque<int>(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]);
}
}
/// <summary>
/// Tests whether the RemoveAt() method of the deque can remove an item from
/// the third-to-last block of the deque
/// </summary>
[Test]
public void TestRemoveFromThirdToLastBlock() {
Deque<int> intDeque = new Deque<int>(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]);
}
}
/// <summary>
/// Verifies that items can be assigned by their index
/// </summary>
[Test]
public void TestIndexAssignment() {
Deque<int> intDeque = new Deque<int>(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);
}
/// <summary>
/// Verifies that an exception is thrown if an invalid index is accessed
/// </summary>
[Test]
public void TestThrowOnInvalidIndex() {
Deque<int> intDeque = new Deque<int>(16);
for(int item = 0; item < 32; ++item) {
intDeque.AddLast(item);
}
Assert.Throws<ArgumentOutOfRangeException>(
delegate() { Console.WriteLine(intDeque[32]); }
);
}
}
#endif #endif
} // namespace Nuclex.Support.Collections } // namespace Nuclex.Support.Collections

View File

@ -4,7 +4,8 @@ using System.Collections;
namespace Nuclex.Support.Collections { namespace Nuclex.Support.Collections {
#if false #if true
/// <summary>A double-ended queue that allocates memory in blocks</summary> /// <summary>A double-ended queue that allocates memory in blocks</summary>
/// <typeparam name="ItemType">Type of the items being stored in the queue</typeparam> /// <typeparam name="ItemType">Type of the items being stored in the queue</typeparam>
/// <remarks> /// <remarks>
@ -82,6 +83,8 @@ namespace Nuclex.Support.Collections {
throw new InvalidOperationException("Cannot remove items from empty deque"); throw new InvalidOperationException("Cannot remove items from empty deque");
} }
// TODO: Zero removed array entry if array is kept
++this.firstBlockStartIndex; ++this.firstBlockStartIndex;
if(this.firstBlockStartIndex >= this.blockSize) { // Block became empty if(this.firstBlockStartIndex >= this.blockSize) { // Block became empty
if(this.count > 1) { // Still more blocks in queue, remove block if(this.count > 1) { // Still more blocks in queue, remove block
@ -101,6 +104,8 @@ namespace Nuclex.Support.Collections {
throw new InvalidOperationException("Cannot remove items from empty deque"); throw new InvalidOperationException("Cannot remove items from empty deque");
} }
// TODO: Zero removed array entry if array is kept
--this.lastBlockEndIndex; --this.lastBlockEndIndex;
if(this.lastBlockEndIndex == 0) { // Block became empty if(this.lastBlockEndIndex == 0) { // Block became empty
if(this.count > 1) { if(this.count > 1) {
@ -142,17 +147,118 @@ namespace Nuclex.Support.Collections {
++this.count; ++this.count;
} }
/// <summary>Inserts the item at the specified index</summary>
/// <param name="index">Index the item will be inserted at</param>
/// <param name="item">Item that will be inserted</param>
public void Insert(int index, ItemType item) { 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) { if(index == this.count) {
AddLast(item); AddLast(item);
} else { } else {
int blockIndex, subIndex; int blockIndex, subIndex;
findIndex(index, out blockIndex, out 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
/// <summary>Removes the item at the specified index</summary>
/// <param name="index">Index of the item that will be removed</param>
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) { public int IndexOf(ItemType item) {
switch(this.blocks.Count) { switch(this.blocks.Count) {
@ -239,6 +345,7 @@ namespace Nuclex.Support.Collections {
private int lastBlockEndIndex; private int lastBlockEndIndex;
} }
#endif #endif
} // namespace Nuclex.Support.Collections } // namespace Nuclex.Support.Collections

View File

@ -193,7 +193,7 @@ namespace Nuclex.Support.Tracking {
this.mockery = new Mockery(); this.mockery = new Mockery();
} }
/// <summary>Validates that the set transaction properly sums the progress</summary> /// <summary>Validates that the transaction group correctly sums the progress</summary>
[Test] [Test]
public void TestSummedProgress() { public void TestSummedProgress() {
using( using(