#region CPL License /* Nuclex Framework Copyright (C) 2002-2014 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion 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(TItem item) { if(this.firstBlockStartIndex > 0) { --this.firstBlockStartIndex; } else { // Need to allocate a new block this.blocks.Insert(0, new TItem[this.blockSize]); this.firstBlockStartIndex = this.blockSize - 1; } this.blocks[0][this.firstBlockStartIndex] = item; ++this.count; #if DEBUG ++this.version; #endif } /// Appends an item to the end of the double-ended queue /// Item that will be appended to the queue public void AddLast(TItem item) { if(this.lastBlockEndIndex < this.blockSize) { ++this.lastBlockEndIndex; } else { // Need to allocate a new block this.blocks.Add(new TItem[this.blockSize]); this.lastBlockEndIndex = 1; } this.blocks[this.blocks.Count - 1][this.lastBlockEndIndex - 1] = item; ++this.count; #if DEBUG ++this.version; #endif } /// Inserts the item at the specified index /// Index the item will be inserted at /// Item that will be inserted public void Insert(int index, TItem 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); } #if DEBUG ++this.version; #endif } /// /// 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, TItem 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 TItem[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, TItem 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 TItem[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