Implemented more efficient Insert() method for the deque; split RemoveAt() into left-shifting and right-shifting specializations (right-shifting still is a copy of the left-shifting one, will fix it soon); improved unit tests to catch any and all border cases; made the IndexOf() method workable; wrote unit tests for the IndexOf() method; moved removal and insertion functions into the own sub-files to avoid having huge source files
git-svn-id: file:///srv/devel/repo-conversion/nusu@160 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
ed62fa85f2
commit
58c3254260
|
@ -70,6 +70,12 @@
|
||||||
<DependentUpon>AssertHelper.cs</DependentUpon>
|
<DependentUpon>AssertHelper.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\Collections\Deque.cs" />
|
<Compile Include="Source\Collections\Deque.cs" />
|
||||||
|
<Compile Include="Source\Collections\Deque.Insertion.cs">
|
||||||
|
<DependentUpon>Deque.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\Collections\Deque.Removal.cs">
|
||||||
|
<DependentUpon>Deque.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Source\Collections\Deque.Test.cs">
|
<Compile Include="Source\Collections\Deque.Test.cs">
|
||||||
<DependentUpon>Deque.cs</DependentUpon>
|
<DependentUpon>Deque.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
@ -52,6 +52,12 @@
|
||||||
<DependentUpon>AssertHelper.cs</DependentUpon>
|
<DependentUpon>AssertHelper.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\Collections\Deque.cs" />
|
<Compile Include="Source\Collections\Deque.cs" />
|
||||||
|
<Compile Include="Source\Collections\Deque.Insertion.cs">
|
||||||
|
<DependentUpon>Deque.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Source\Collections\Deque.Removal.cs">
|
||||||
|
<DependentUpon>Deque.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Source\Collections\Deque.Test.cs">
|
<Compile Include="Source\Collections\Deque.Test.cs">
|
||||||
<DependentUpon>Deque.cs</DependentUpon>
|
<DependentUpon>Deque.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
184
Source/Collections/Deque.Insertion.cs
Normal file
184
Source/Collections/Deque.Insertion.cs
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
|
partial class Deque<ItemType> {
|
||||||
|
|
||||||
|
/// <summary>Inserts an item at the beginning of the double-ended queue</summary>
|
||||||
|
/// <param name="item">Item that will be inserted into the queue</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Appends an item to the end of the double-ended queue</summary>
|
||||||
|
/// <param name="item">Item that will be appended to the queue</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shifts all items before the insertion point to the left and 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>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shifts all items after the insertion point to the right and 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>
|
||||||
|
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
|
174
Source/Collections/Deque.Removal.cs
Normal file
174
Source/Collections/Deque.Removal.cs
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
|
partial class Deque<ItemType> {
|
||||||
|
|
||||||
|
/// <summary>Removes the first item in the double-ended queue</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Removes the last item in the double-ended queue</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes an item from the left side of the queue by shifting all items that
|
||||||
|
/// come before it to the right by one
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">Index of the item that will be removed</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes an item from the right side of the queue by shifting all items that
|
||||||
|
/// come after it to the left by one
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">Index of the item that will be removed</param>
|
||||||
|
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
|
|
@ -28,8 +28,6 @@ using NMock2;
|
||||||
|
|
||||||
namespace Nuclex.Support.Collections {
|
namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
#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 {
|
||||||
|
@ -94,6 +92,111 @@ namespace Nuclex.Support.Collections {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Verifies that the Insert() method works in all cases</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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
|
||||||
|
/// </remarks>
|
||||||
|
[Test]
|
||||||
|
public void TestInsert() {
|
||||||
|
for(int testedIndex = 0; testedIndex <= 96; ++testedIndex) {
|
||||||
|
Deque<int> intDeque = new Deque<int>(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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that the Insert() method works in all cases</summary>
|
||||||
|
[Test]
|
||||||
|
public void TestInsertNonNormalized() {
|
||||||
|
for(int testedIndex = 0; testedIndex <= 96; ++testedIndex) {
|
||||||
|
Deque<int> intDeque = new Deque<int>(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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies the the RemoveAt() method works in all cases</summary>
|
||||||
|
[Test]
|
||||||
|
public void TestRemoveAt() {
|
||||||
|
for(int testedIndex = 0; testedIndex < 96; ++testedIndex) {
|
||||||
|
Deque<int> intDeque = new Deque<int>(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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies the the RemoveAt() method works in all cases</summary>
|
||||||
|
[Test]
|
||||||
|
public void TestRemoveAtNonNormalized() {
|
||||||
|
for(int testedIndex = 0; testedIndex < 96; ++testedIndex) {
|
||||||
|
Deque<int> intDeque = new Deque<int>(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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates that an exception is thrown if the 'First' property is accessed
|
/// Validates that an exception is thrown if the 'First' property is accessed
|
||||||
/// in an empty deque
|
/// in an empty deque
|
||||||
|
@ -141,178 +244,7 @@ namespace Nuclex.Support.Collections {
|
||||||
delegate() { intDeque.RemoveLast(); }
|
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>
|
/// <summary>
|
||||||
/// Verifies that items can be assigned by their index
|
/// Verifies that items can be assigned by their index
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -324,7 +256,7 @@ namespace Nuclex.Support.Collections {
|
||||||
}
|
}
|
||||||
intDeque[16] = 12345;
|
intDeque[16] = 12345;
|
||||||
intDeque[17] = 54321;
|
intDeque[17] = 54321;
|
||||||
|
|
||||||
for(int index = 0; index < 16; ++index) {
|
for(int index = 0; index < 16; ++index) {
|
||||||
intDeque.RemoveFirst();
|
intDeque.RemoveFirst();
|
||||||
}
|
}
|
||||||
|
@ -343,15 +275,29 @@ namespace Nuclex.Support.Collections {
|
||||||
for(int item = 0; item < 32; ++item) {
|
for(int item = 0; item < 32; ++item) {
|
||||||
intDeque.AddLast(item);
|
intDeque.AddLast(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.Throws<ArgumentOutOfRangeException>(
|
Assert.Throws<ArgumentOutOfRangeException>(
|
||||||
delegate() { Console.WriteLine(intDeque[32]); }
|
delegate() { Console.WriteLine(intDeque[32]); }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/// <summary>Tests the IndexOf() method</summary>
|
||||||
|
[Test, TestCase(0), TestCase(16), TestCase(32), TestCase(48)]
|
||||||
|
public void TestIndexOf(int count) {
|
||||||
|
Deque<int> intDeque = new Deque<int>(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
|
} // namespace Nuclex.Support.Collections
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,6 @@ using System.Collections;
|
||||||
|
|
||||||
namespace Nuclex.Support.Collections {
|
namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
#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>
|
||||||
|
@ -20,7 +18,7 @@ namespace Nuclex.Support.Collections {
|
||||||
/// require items to be copied around and still can be accessed by index.
|
/// require items to be copied around and still can be accessed by index.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class Deque<ItemType> /*: IList<ItemType>, IList*/ {
|
public partial class Deque<ItemType> /*: IList<ItemType>, IList*/ {
|
||||||
|
|
||||||
/// <summary>Initializes a new deque</summary>
|
/// <summary>Initializes a new deque</summary>
|
||||||
public Deque() : this(512) { }
|
public Deque() : this(512) { }
|
||||||
|
@ -77,249 +75,60 @@ namespace Nuclex.Support.Collections {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Removes the first item in the double-ended queue</summary>
|
/// <summary>
|
||||||
public void RemoveFirst() {
|
/// Determines the index of the first occurence of the specified item in the deque
|
||||||
if(this.count == 0) {
|
/// </summary>
|
||||||
throw new InvalidOperationException("Cannot remove items from empty deque");
|
/// <param name="item">Item that will be located in the deque</param>
|
||||||
}
|
/// <returns>The index of the item or -1 if it wasn't found</returns>
|
||||||
|
public int IndexOf(ItemType item) {
|
||||||
// TODO: Zero removed array entry if array is kept
|
if(this.blocks.Count == 1) { // Only one block to scan?
|
||||||
|
int length = this.lastBlockEndIndex - this.firstBlockStartIndex;
|
||||||
++this.firstBlockStartIndex;
|
int index = Array.IndexOf<ItemType>(
|
||||||
if(this.firstBlockStartIndex >= this.blockSize) { // Block became empty
|
this.blocks[0], item, this.firstBlockStartIndex, length
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Removes the last item in the double-ended queue</summary>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Inserts an item at the beginning of the double-ended queue</summary>
|
|
||||||
/// <param name="item">Item that will be inserted into the queue</param>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Appends an item to the end of the double-ended queue</summary>
|
|
||||||
/// <param name="item">Item that will be appended to the queue</param>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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) {
|
|
||||||
// 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
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.blocks[blockIndex][subIndex] = item;
|
// If we found something, we need to adjust its index so the first item in
|
||||||
++this.count;
|
// the deque always appears at index 0 to the user
|
||||||
}
|
if(index != -1) {
|
||||||
}
|
return (index - this.firstBlockStartIndex);
|
||||||
|
|
||||||
/// <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 {
|
} else {
|
||||||
startIndex = subIndex;
|
return -1;
|
||||||
}
|
}
|
||||||
|
} else { // At least two blocks exist
|
||||||
|
|
||||||
Array.Copy(
|
// Scan the first block for the item and if found, return the index
|
||||||
this.blocks[lastBlock], startIndex + 1,
|
int length = this.blockSize - this.firstBlockStartIndex;
|
||||||
this.blocks[lastBlock], startIndex,
|
int index = Array.IndexOf<ItemType>(
|
||||||
this.lastBlockEndIndex - startIndex - 1
|
this.blocks[0], item, this.firstBlockStartIndex, length
|
||||||
);
|
);
|
||||||
|
|
||||||
if(this.lastBlockEndIndex == 1) {
|
// If we found something, we need to adjust its index
|
||||||
this.blocks.RemoveAt(lastBlock);
|
if(index != -1) {
|
||||||
this.lastBlockEndIndex = this.blockSize;
|
return (index - this.firstBlockStartIndex);
|
||||||
} else {
|
|
||||||
this.blocks[lastBlock][this.lastBlockEndIndex - 1] = default(ItemType);
|
|
||||||
--this.lastBlockEndIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
--this.count;
|
int lastBlock = this.blocks.Count - 1;
|
||||||
|
for(int tempIndex = 1; tempIndex < lastBlock; ++tempIndex) {
|
||||||
|
index = Array.IndexOf<ItemType>(
|
||||||
|
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<ItemType>(
|
||||||
|
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<ItemType>(
|
|
||||||
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<ItemType>(
|
|
||||||
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<ItemType>(
|
|
||||||
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<ItemType>(
|
|
||||||
this.blocks[0], item, this.firstBlockStartIndex, this.blockSize
|
|
||||||
);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/// <summary>Calculates the block index and local sub index of an entry</summary>
|
/// <summary>Calculates the block index and local sub index of an entry</summary>
|
||||||
/// <param name="index">Index of the entry that will be located</param>
|
/// <param name="index">Index of the entry that will be located</param>
|
||||||
/// <param name="blockIndex">Index of the block the entry is contained in</param>
|
/// <param name="blockIndex">Index of the block the entry is contained in</param>
|
||||||
|
@ -346,6 +155,4 @@ namespace Nuclex.Support.Collections {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace Nuclex.Support.Collections
|
} // namespace Nuclex.Support.Collections
|
||||||
|
|
Loading…
Reference in New Issue
Block a user