/Source/PowerCollections/BigList.cs
C# | 2792 lines | 1424 code | 290 blank | 1078 comment | 473 complexity | 9d613785f8b56466664c01e883b7f485 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
Large files files are truncated, but you can click here to view the full file
- //******************************
- // Written by Peter Golde
- // Copyright (c) 2004-2007, Wintellect
- //
- // Use and restribution of this code is subject to the license agreement
- // contained in the file "License.txt" accompanying this file.
- //******************************
-
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
-
- // CONSIDER: provide more efficient implementation of CopyTo.
-
- namespace Wintellect.PowerCollections
- {
- /// <summary>
- /// BigList<T> provides a list of items, in order, with indices of the items ranging from 0 to one less
- /// than the count of items in the collection. BigList<T> is optimized for efficient operations on large (>100 items)
- /// lists, especially for insertions, deletions, copies, and concatinations.
- /// </summary>
- /// <remarks>
- /// <para>BigList<T> class is similar in functionality to the standard List<T> class. Both classes
- /// provide a collection that stores an set of items in order, with indices of the items ranging from 0 to one less
- /// than the count of items in the collection. Both classes provide the ability to add and remove items from any index,
- /// and the get or set the item at any index.</para>
- /// <para>BigList<T> differs significantly from List<T> in the performance of various operations,
- /// especially when the lists become large (several hundred items or more). With List<T>, inserting or removing
- /// elements from anywhere in a large list except the end is very inefficient -- every item after the point of inserting
- /// or deletion has to be moved in the list. The BigList<T> class, however, allows for fast insertions
- /// and deletions anywhere in the list. Furthermore, BigList<T> allows copies of a list, sub-parts
- /// of a list, and concatinations of two lists to be very fast. When a copy is made of part or all of a BigList,
- /// two lists shared storage for the parts of the lists that are the same. Only when one of the lists is changed is additional
- /// memory allocated to store the distinct parts of the lists.</para>
- /// <para>Of course, there is a small price to pay for this extra flexibility. Although still quite efficient, using an
- /// index to get or change one element of a BigList, while still reasonably efficient, is significantly slower than using
- /// a plain List. Because of this, if you want to process every element of a BigList, using a foreach loop is a lot
- /// more efficient than using a for loop and indexing the list.</para>
- /// <para>In general, use a List when the only operations you are using are Add (to the end), foreach,
- /// or indexing, or you are very sure the list will always remain small (less than 100 items). For large (>100 items) lists
- /// that do insertions, removals, copies, concatinations, or sub-ranges, BigList will be more efficient than List.
- /// In almost all cases, BigList is more efficient and easier to use than LinkedList.</para>
- /// </remarks>
- /// <typeparam name="T">The type of items to store in the BigList.</typeparam>
- [Serializable]
- public class BigList<T>: ListBase<T>, ICloneable
- {
- const uint MAXITEMS = int.MaxValue - 1; // maximum number of items in a BigList.
-
- #if DEBUG
- const int MAXLEAF = 8; // Maximum number of elements in a leaf node -- small for debugging purposes.
- #else
- const int MAXLEAF = 120; // Maximum number of elements in a leaf node.
- #endif
- const int BALANCEFACTOR = 6; // how far the root must be in depth from fully balanced to invoke the rebalance operation (min 3).
-
- // The fibonacci numbers. Used in the rebalancing algorithm. Final MaxValue makes sure we don't go off the end.
- static readonly int[] FIBONACCI = {1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584,
- 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040,
- 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986,
- 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, int.MaxValue};
- const int MAXFIB = 44; // maximum index in the above, not counting the final MaxValue.
-
-
-
- // If null, the BigList is empty. If non-null, the list has at least one item.
- private Node root;
-
- // Holds the change stamp for the collection.
- private int changeStamp;
-
- /// <summary>
- /// Must be called whenever there is a structural change in the tree. Causes
- /// changeStamp to be changed, which causes any in-progress enumerations
- /// to throw exceptions.
- /// </summary>
- private void StopEnumerations()
- {
- ++changeStamp;
- }
-
- /// <summary>
- /// Checks the given stamp against the current change stamp. If different, the
- /// collection has changed during enumeration and an InvalidOperationException
- /// must be thrown
- /// </summary>
- /// <param name="startStamp">changeStamp at the start of the enumeration.</param>
- private void CheckEnumerationStamp(int startStamp)
- {
- if (startStamp != changeStamp) {
- throw new InvalidOperationException(Strings.ChangeDuringEnumeration);
- }
- }
-
- /// <summary>
- /// Creates a new BigList. The BigList is initially empty.
- /// </summary>
- /// <remarks>Creating a empty BigList takes constant time and consumes a very small amount of memory.</remarks>
- public BigList()
- {
- root = null;
- }
-
- /// <summary>
- /// Creates a new BigList initialized with the items from <paramref name="collection"/>, in order.
- /// </summary>
- /// <remarks>Initializing the tree list with the elements of collection takes time O(N), where N is the number of
- /// items in <paramref name="collection"/>.</remarks>
- /// <param name="collection">The collection used to initialize the BigList. </param>
- /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
- public BigList(IEnumerable<T> collection)
- {
- if (collection == null)
- throw new ArgumentNullException("collection");
- root = NodeFromEnumerable(collection);
- CheckBalance();
- }
-
- /// <summary>
- /// Creates a new BigList initialized with a given number of copies of the items from <paramref name="collection"/>, in order.
- /// </summary>
- /// <remarks>Initializing the tree list with the elements of collection takes time O(N + log K), where N is the number of
- /// items in <paramref name="collection"/>, and K is the number of copies.</remarks>
- /// <param name="copies">Number of copies of the collection to use.</param>
- /// <param name="collection">The collection used to initialize the BigList. </param>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="copies"/> is negative.</exception>
- /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
- public BigList(IEnumerable<T> collection, int copies)
- {
- if (collection == null)
- throw new ArgumentNullException("collection");
- root = NCopiesOfNode(copies, NodeFromEnumerable(collection));
- CheckBalance();
- }
-
- /// <summary>
- /// Creates a new BigList that is a copy of <paramref name="list"/>.
- /// </summary>
- /// <remarks>Copying a BigList takes constant time, and little
- /// additional memory, since the storage for the items of the two lists is shared. However, changing
- /// either list will take additional time and memory. Portions of the list are copied when they are changed.</remarks>
- /// <param name="list">The BigList to copy. </param>
- /// <exception cref="ArgumentNullException"><paramref name="list"/> is null.</exception>
- public BigList(BigList<T> list)
- {
- if (list == null)
- throw new ArgumentNullException("list");
- if (list.root == null)
- root = null;
- else {
- list.root.MarkShared();
- root = list.root;
- }
- }
-
-
- /// <summary>
- /// Creates a new BigList that is several copies of <paramref name="list"/>.
- /// </summary>
- /// <remarks>Creating K copies of a BigList takes time O(log K), and O(log K)
- /// additional memory, since the storage for the items of the two lists is shared. However, changing
- /// either list will take additional time and memory. Portions of the list are copied when they are changed.</remarks>
- /// <param name="copies">Number of copies of the collection to use.</param>
- /// <param name="list">The BigList to copy. </param>
- /// <exception cref="ArgumentNullException"><paramref name="list"/> is null.</exception>
- public BigList(BigList<T> list, int copies)
- {
- if (list == null)
- throw new ArgumentNullException("list");
- if (list.root == null)
- root = null;
- else {
- list.root.MarkShared();
- root = NCopiesOfNode(copies, list.root);
- }
- }
-
- /// <summary>
- /// Creates a new BigList from the indicated Node.
- /// </summary>
- /// <param name="node">Node that becomes the new root. If null, the new BigList is empty.</param>
- private BigList(Node node)
- {
- this.root = node;
- CheckBalance();
- }
-
- /// <summary>
- /// Gets the number of items stored in the BigList. The indices of the items
- /// range from 0 to Count-1.
- /// </summary>
- /// <remarks>Getting the number of items in the BigList takes constant time.</remarks>
- /// <value>The number of items in the BigList.</value>
- public sealed override int Count
- {
- get
- {
- if (root == null)
- return 0;
- else
- return root.Count;
- }
- }
-
- /// <summary>
- /// Gets or sets an item in the list, by index.
- /// </summary>
- /// <remarks><para> Gettingor setting an item takes time O(log N), where N is the number of items
- /// in the list.</para>
- /// <para>To process each of the items in the list, using GetEnumerator() or a foreach loop is more efficient
- /// that accessing each of the elements by index.</para></remarks>
- /// <param name="index">The index of the item to get or set. The first item in the list
- /// has index 0, the last item has index Count-1.</param>
- /// <returns>The value of the item at the given index.</returns>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than zero or
- /// greater than or equal to Count.</exception>
- public sealed override T this[int index]
- {
- get
- {
- // This could just be a simple call to GetAt on the root.
- // It is recoded as an interative algorithm for performance.
-
- if (root == null || index < 0 || index >= root.Count)
- throw new ArgumentOutOfRangeException("index");
-
- Node current = root;
- ConcatNode curConcat = current as ConcatNode;
-
- while (curConcat != null) {
- int leftCount = curConcat.left.Count;
- if (index < leftCount)
- current = curConcat.left;
- else {
- current = curConcat.right;
- index -= leftCount;
- }
-
- curConcat = current as ConcatNode;
- }
-
- LeafNode curLeaf = (LeafNode)current;
- return curLeaf.items[index];
- }
-
- set
- {
- // This could just be a simple call to SetAtInPlace on the root.
- // It is recoded as an interative algorithm for performance.
-
- if (root == null || index < 0 || index >= root.Count)
- throw new ArgumentOutOfRangeException("index");
-
- // Like List<T>, we stop enumerations after a set operation. This could be made
- // to not happen, but it would be complex, because set operations on a shared node
- // could change the node.
- StopEnumerations();
-
- if (root.Shared)
- root = root.SetAt(index, value);
-
- Node current = root;
- ConcatNode curConcat = current as ConcatNode;
-
- while (curConcat != null) {
- int leftCount = curConcat.left.Count;
- if (index < leftCount) {
- current = curConcat.left;
- if (current.Shared) {
- curConcat.left = current.SetAt(index, value);
- return;
- }
- }
- else {
- current = curConcat.right;
- index -= leftCount;
- if (current.Shared) {
- curConcat.right = current.SetAt(index, value);
- return;
- }
- }
-
- curConcat = current as ConcatNode;
- }
-
- LeafNode curLeaf = (LeafNode)current;
- curLeaf.items[index] = value;
- }
- }
-
- /// <summary>
- /// Removes all of the items from the BigList.
- /// </summary>
- /// <remarks>Clearing a BigList takes constant time.</remarks>
- public sealed override void Clear()
- {
- StopEnumerations();
- root = null;
- }
-
- /// <summary>
- /// Inserts a new item at the given index in the BigList. All items at indexes
- /// equal to or greater than <paramref name="index"/> move up one index.
- /// </summary>
- /// <remarks>The amount of time to insert an item is O(log N), no matter where
- /// in the list the insertion occurs. Inserting an item at the beginning or end of the
- /// list is O(N).
- /// </remarks>
- /// <param name="index">The index to insert the item at. After the
- /// insertion, the inserted item is located at this index. The
- /// first item has index 0.</param>
- /// <param name="item">The item to insert at the given index.</param>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is
- /// less than zero or greater than Count.</exception>
- public sealed override void Insert(int index, T item)
- {
- StopEnumerations();
-
- if ((uint)Count + 1 > MAXITEMS)
- throw new InvalidOperationException(Strings.CollectionTooLarge);
-
- if (index <= 0 || index >= Count) {
- if (index == 0)
- AddToFront(item);
- else if (index == Count)
- Add(item);
- else
- throw new ArgumentOutOfRangeException("index");
- }
- else {
- if (root == null)
- root = new LeafNode(item);
- else {
- Node newRoot = root.InsertInPlace(index, item);
- if (newRoot != root) {
- root = newRoot;
- CheckBalance();
- }
- }
- }
- }
-
- /// <summary>
- /// Inserts a collection of items at the given index in the BigList. All items at indexes
- /// equal to or greater than <paramref name="index"/> increase their indices
- /// by the number of items inserted.
- /// </summary>
- /// <remarks>The amount of time to insert an arbitrary collection in the BigList is O(M + log N),
- /// where M is the number of items inserted, and N is the number of items in the list.
- /// </remarks>
- /// <param name="index">The index to insert the collection at. After the
- /// insertion, the first item of the inserted collection is located at this index. The
- /// first item has index 0.</param>
- /// <param name="collection">The collection of items to insert at the given index.</param>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is
- /// less than zero or greater than Count.</exception>
- /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
- public void InsertRange(int index, IEnumerable<T> collection)
- {
- StopEnumerations();
-
- if (collection == null)
- throw new ArgumentNullException("collection");
-
- if (index <= 0 || index >= Count) {
- if (index == 0)
- AddRangeToFront(collection);
- else if (index == Count)
- AddRange(collection);
- else
- throw new ArgumentOutOfRangeException("index");
- }
- else {
- Node node = NodeFromEnumerable(collection);
- if (node == null)
- return;
- else if (root == null)
- root = node;
- else {
- if ((uint)Count + (uint)node.Count > MAXITEMS)
- throw new InvalidOperationException(Strings.CollectionTooLarge);
-
- Node newRoot = root.InsertInPlace(index, node, true);
- if (newRoot != root) {
- root = newRoot;
- CheckBalance();
- }
- }
- }
- }
-
- /// <summary>
- /// Inserts a BigList of items at the given index in the BigList. All items at indexes
- /// equal to or greater than <paramref name="index"/> increase their indices
- /// by the number of items inserted.
- /// </summary>
- /// <remarks>The amount of time to insert another BigList is O(log N),
- /// where N is the number of items in the list, regardless of the number of items in the
- /// inserted list. Storage is shared between the two lists until one of them is changed.
- /// </remarks>
- /// <param name="index">The index to insert the collection at. After the
- /// insertion, the first item of the inserted collection is located at this index. The
- /// first item has index 0.</param>
- /// <param name="list">The BigList of items to insert at the given index.</param>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is
- /// less than zero or greater than Count.</exception>
- /// <exception cref="ArgumentNullException"><paramref name="list"/> is null.</exception>
- public void InsertRange(int index, BigList<T> list)
- {
- StopEnumerations();
-
- if (list == null)
- throw new ArgumentNullException("list");
- if ((uint)Count + (uint)list.Count > MAXITEMS)
- throw new InvalidOperationException(Strings.CollectionTooLarge);
-
- if (index <= 0 || index >= Count) {
- if (index == 0)
- AddRangeToFront(list);
- else if (index == Count)
- AddRange(list);
- else
- throw new ArgumentOutOfRangeException("index");
- }
- else {
- if (list.Count == 0)
- return;
-
- if (root == null) {
- list.root.MarkShared();
- root = list.root;
- }
- else {
- if (list.root == root)
- root.MarkShared(); // make sure inserting into itself works.
-
- Node newRoot = root.InsertInPlace(index, list.root, false);
- if (newRoot != root) {
- root = newRoot;
- CheckBalance();
- }
- }
- }
- }
-
- /// <summary>
- /// Removes the item at the given index in the BigList. All items at indexes
- /// greater than <paramref name="index"/> move down one index.
- /// </summary>
- /// <remarks>The amount of time to delete an item in the BigList is O(log N),
- /// where N is the number of items in the list.
- /// </remarks>
- /// <param name="index">The index in the list to remove the item at. The
- /// first item in the list has index 0.</param>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is
- /// less than zero or greater than or equal to Count.</exception>
- public sealed override void RemoveAt(int index)
- {
- RemoveRange(index, 1);
- }
-
- /// <summary>
- /// Removes a range of items at the given index in the Deque. All items at indexes
- /// greater than <paramref name="index"/> move down <paramref name="count"/> indices
- /// in the Deque.
- /// </summary>
- /// <remarks>The amount of time to delete <paramref name="count"/> items in the Deque is proportional
- /// to the distance of index from the closest end of the Deque, plus <paramref name="count"/>:
- /// O(count + Min(<paramref name="index"/>, Count - 1 - <paramref name="index"/>)).
- /// </remarks>
- /// <param name="index">The index in the list to remove the range at. The
- /// first item in the list has index 0.</param>
- /// <param name="count">The number of items to remove.</param>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is
- /// less than zero or greater than or equal to Count, or <paramref name="count"/> is less than zero
- /// or too large.</exception>
- public void RemoveRange(int index, int count)
- {
- if (count == 0)
- return; // nothing to do.
- if (index < 0 || index >= Count)
- throw new ArgumentOutOfRangeException("index");
- if (count < 0 || count > Count - index)
- throw new ArgumentOutOfRangeException("count");
-
- StopEnumerations();
-
- Node newRoot = root.RemoveRangeInPlace(index, index + count - 1);
- if (newRoot != root) {
- root = newRoot;
- CheckBalance();
- }
- }
-
- /// <summary>
- /// Adds an item to the end of the BigList. The indices of all existing items
- /// in the Deque are unchanged.
- /// </summary>
- /// <remarks>Adding an item takes, on average, constant time.</remarks>
- /// <param name="item">The item to add.</param>
- public sealed override void Add(T item)
- {
- if ((uint)Count + 1 > MAXITEMS)
- throw new InvalidOperationException(Strings.CollectionTooLarge);
-
- StopEnumerations();
-
- if (root == null)
- root = new LeafNode(item);
- else {
- Node newRoot = root.AppendInPlace(item);
- if (newRoot != root) {
- root = newRoot;
- CheckBalance();
- }
- }
- }
-
- /// <summary>
- /// Adds an item to the beginning of the BigList. The indices of all existing items
- /// in the Deque are increased by one, and the new item has index zero.
- /// </summary>
- /// <remarks>Adding an item takes, on average, constant time.</remarks>
- /// <param name="item">The item to add.</param>
- public void AddToFront(T item)
- {
- if ((uint)Count + 1 > MAXITEMS)
- throw new InvalidOperationException(Strings.CollectionTooLarge);
-
- StopEnumerations();
-
- if (root == null)
- root = new LeafNode(item);
- else {
- Node newRoot = root.PrependInPlace(item);
- if (newRoot != root) {
- root = newRoot;
- CheckBalance();
- }
- }
- }
-
- /// <summary>
- /// Adds a collection of items to the end of BigList. The indices of all existing items
- /// are unchanged. The last item in the added collection becomes the
- /// last item in the BigList.
- /// </summary>
- /// <remarks>This method takes time O(M + log N), where M is the number of items in the
- /// <paramref name="collection"/>, and N is the size of the BigList.</remarks>
- /// <param name="collection">The collection of items to add.</param>
- /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
- public void AddRange(IEnumerable<T> collection)
- {
- if (collection == null)
- throw new ArgumentNullException("collection");
-
- StopEnumerations();
-
- Node node = NodeFromEnumerable(collection);
- if (node == null)
- return;
- else if (root == null) {
- root = node;
- CheckBalance();
- }
- else {
- if ((uint)Count + (uint)node.count > MAXITEMS)
- throw new InvalidOperationException(Strings.CollectionTooLarge);
-
- Node newRoot = root.AppendInPlace(node, true);
- if (newRoot != root) {
- root = newRoot;
- CheckBalance();
- }
- }
- }
-
- /// <summary>
- /// Adds a collection of items to the front of BigList. The indices of all existing items
- /// in the are increased by the number of items in <paramref name="collection"/>.
- /// The first item in the added collection becomes the first item in the BigList.
- /// </summary>
- /// <remarks>This method takes time O(M + log N), where M is the number of items in the
- /// <paramref name="collection"/>, and N is the size of the BigList.</remarks>
- /// <param name="collection">The collection of items to add.</param>
- /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
- public void AddRangeToFront(IEnumerable<T> collection)
- {
- if (collection == null)
- throw new ArgumentNullException("collection");
-
- StopEnumerations();
-
- Node node = NodeFromEnumerable(collection);
- if (node == null)
- return;
- else if (root == null) {
- root = node;
- CheckBalance();
- }
- else {
- if ((uint)Count + (uint)node.Count > MAXITEMS)
- throw new InvalidOperationException(Strings.CollectionTooLarge);
-
- Node newRoot = root.PrependInPlace(node, true);
- if (newRoot != root) {
- root = newRoot;
- CheckBalance();
- }
- }
- }
-
- /// <summary>
- /// Creates a new BigList that is a copy of this list.
- /// </summary>
- /// <remarks>Copying a BigList takes constant time, and little
- /// additional memory, since the storage for the items of the two lists is shared. However, changing
- /// either list will take additional time and memory. Portions of the list are copied when they are changed.</remarks>
- /// <returns>A copy of the current list</returns>
- public BigList<T> Clone()
- {
- if (root == null)
- return new BigList<T>();
- else {
- root.MarkShared();
- return new BigList<T>(root);
- }
- }
-
- /// <summary>
- /// Creates a new BigList that is a copy of this list.
- /// </summary>
- /// <remarks>Copying a BigList takes constant time, and little
- /// additional memory, since the storage for the items of the two lists is shared. However, changing
- /// either list will take additional time and memory. Portions of the list are copied when they are changed.</remarks>
- /// <returns>A copy of the current list</returns>
- object ICloneable.Clone()
- {
- return Clone();
- }
-
- /// <summary>
- /// Makes a deep clone of this BigList. A new BigList is created with a clone of
- /// each element of this set, by calling ICloneable.Clone on each element. If T is
- /// a value type, then this method is the same as Clone.
- /// </summary>
- /// <remarks><para>If T is a reference type, it must implement
- /// ICloneable. Otherwise, an InvalidOperationException is thrown.</para>
- /// <para>If T is a reference type, cloning the list takes time approximate O(N), where N is the number of items in the list.</para></remarks>
- /// <returns>The cloned set.</returns>
- /// <exception cref="InvalidOperationException">T is a reference type that does not implement ICloneable.</exception>
- public BigList<T> CloneContents()
- {
- if (root == null)
- return new BigList<T>();
- else {
- bool itemIsValueType;
- if (!Util.IsCloneableType(typeof(T), out itemIsValueType))
- throw new InvalidOperationException(string.Format(Strings.TypeNotCloneable, typeof(T).FullName));
-
- if (itemIsValueType)
- return Clone();
-
- // Create a new list by converting each item in this list via cloning.
- return new BigList<T>(Algorithms.Convert<T, T>(this, delegate(T item)
- {
- if (item == null)
- return default(T); // Really null, because we know T is a reference type
- else
- return (T)(((ICloneable)item).Clone());
- }));
- }
- }
-
- /// <summary>
- /// Adds a BigList of items to the end of BigList. The indices of all existing items
- /// are unchanged. The last item in <paramref name="list"/> becomes the
- /// last item in this list. The added list <paramref name="list"/> is unchanged.
- /// </summary>
- /// <remarks>This method takes, on average, constant time, regardless of the size
- /// of either list. Although conceptually all of the items in <paramref name="list"/> are
- /// copied, storage is shared between the two lists until changes are made to the
- /// shared sections.</remarks>
- /// <param name="list">The list of items to add.</param>
- /// <exception cref="ArgumentNullException"><paramref name="list"/> is null.</exception>
- public void AddRange(BigList<T> list)
- {
- if (list == null)
- throw new ArgumentNullException("list");
- if ((uint)Count + (uint)list.Count > MAXITEMS)
- throw new InvalidOperationException(Strings.CollectionTooLarge);
-
- if (list.Count == 0)
- return;
-
- StopEnumerations();
-
- if (root == null) {
- list.root.MarkShared();
- root = list.root;
- }
- else {
- Node newRoot = root.AppendInPlace(list.root, false);
- if (newRoot != root) {
- root = newRoot;
- CheckBalance();
- }
- }
- }
-
- /// <summary>
- /// Adds a BigList of items to the front of BigList. The indices of all existing items
- /// are increased by the number of items in <paramref name="list"/>. The first item in <paramref name="list"/>
- /// becomes the first item in this list. The added list <paramref name="list"/> is unchanged.
- /// </summary>
- /// <remarks>This method takes, on average, constant time, regardless of the size
- /// of either list. Although conceptually all of the items in <paramref name="list"/> are
- /// copied, storage is shared between the two lists until changes are made to the
- /// shared sections.</remarks>
- /// <param name="list">The list of items to add.</param>
- /// <exception cref="ArgumentNullException"><paramref name="list"/> is null.</exception>
- public void AddRangeToFront(BigList<T> list)
- {
- if (list == null)
- throw new ArgumentNullException("list");
- if ((uint)Count + (uint)list.Count > MAXITEMS)
- throw new InvalidOperationException(Strings.CollectionTooLarge);
-
- if (list.Count == 0)
- return;
-
- StopEnumerations();
-
- if (root == null) {
- list.root.MarkShared();
- root = list.root;
- }
- else {
- Node newRoot = root.PrependInPlace(list.root, false);
- if (newRoot != root) {
- root = newRoot;
- CheckBalance();
- }
- }
- }
-
- /// <summary>
- /// Concatenates two lists together to create a new list. Both lists being concatenated
- /// are unchanged. The resulting list contains all the items in <paramref name="first"/>, followed
- /// by all the items in <paramref name="second"/>.
- /// </summary>
- /// <remarks>This method takes, on average, constant time, regardless of the size
- /// of either list. Although conceptually all of the items in both lists are
- /// copied, storage is shared until changes are made to the
- /// shared sections.</remarks>
- /// <param name="first">The first list to concatenate.</param>
- /// <param name="second">The second list to concatenate.</param>
- /// <exception cref="ArgumentNullException"><paramref name="first"/> or <paramref name="second"/> is null.</exception>
- public static BigList<T> operator +(BigList<T> first, BigList<T> second)
- {
- if (first == null)
- throw new ArgumentNullException("first");
- if (second == null)
- throw new ArgumentNullException("second");
- if ((uint)first.Count + (uint)second.Count > MAXITEMS)
- throw new InvalidOperationException(Strings.CollectionTooLarge);
-
- if (first.Count == 0)
- return second.Clone();
- else if (second.Count == 0)
- return first.Clone();
- else {
- BigList<T> result = new BigList<T>(first.root.Append(second.root, false));
- result.CheckBalance();
- return result;
- }
- }
-
- /// <summary>
- /// Creates a new list that contains a subrange of elements from this list. The
- /// current list is unchanged.
- /// </summary>
- /// <remarks>This method takes take O(log N), where N is the size of the current list. Although
- /// the sub-range is conceptually copied, storage is shared between the two lists until a change
- /// is made to the shared items.</remarks>
- /// <remarks>If a view of a sub-range is desired, instead of a copy, use the
- /// more efficient <see cref="Range"/> method, which provides a view onto a sub-range of items.</remarks>
- /// <param name="index">The starting index of the sub-range.</param>
- /// <param name="count">The number of items in the sub-range. If this is zero,
- /// the returned list is empty.</param>
- /// <returns>A new list with the <paramref name="count"/> items that start at <paramref name="index"/>.</returns>
- public BigList<T> GetRange(int index, int count)
- {
- if (count == 0)
- return new BigList<T>();
-
- if (index < 0 || index >= Count)
- throw new ArgumentOutOfRangeException("index");
- if (count < 0 || count > Count - index)
- throw new ArgumentOutOfRangeException("count");
-
- return new BigList<T>(root.Subrange(index, index + count - 1));
- }
-
- /// <summary>
- /// Returns a view onto a sub-range of this list. Items are not copied; the
- /// returned IList<T> is simply a different view onto the same underlying items. Changes to this list
- /// are reflected in the view, and vice versa. Insertions and deletions in the view change the size of the
- /// view, but insertions and deletions in the underlying list do not.
- /// </summary>
- /// <remarks>
- /// <para>If a copy of the sub-range is desired, use the <see cref="GetRange"/> method instead.</para>
- /// <para>This method can be used to apply an algorithm to a portion of a list. For example:</para>
- /// <code>Algorithms.ReverseInPlace(list.Range(3, 6))</code>
- /// will reverse the 6 items beginning at index 3.</remarks>
- /// <param name="index">The starting index of the view.</param>
- /// <param name="count">The number of items in the view.</param>
- /// <returns>A list that is a view onto the given sub-list. </returns>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> or <paramref name="count"/> is negative.</exception>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> + <paramref name="count"/> is greater than the
- /// size of this list.</exception>
- public sealed override IList<T> Range(int index, int count)
- {
- if (index < 0 || index > this.Count || (index == this.Count && count != 0))
- throw new ArgumentOutOfRangeException("index");
- if (count < 0 || count > this.Count || count + index > this.Count)
- throw new ArgumentOutOfRangeException("count");
-
- return new BigListRange(this, index, count);
- }
-
- /// <summary>
- /// Enumerates a range of the items in the list, in order. The item at <paramref name="start"/>
- /// is enumerated first, then the next item at index 1, and so on. At most <paramref name="maxItems"/>
- /// items are enumerated.
- /// </summary>
- /// <remarks>Enumerating all of the items in the list take time O(N), where
- /// N is the number of items being enumerated. Using GetEnumerator() or foreach
- /// is much more efficient than accessing all items by index.</remarks>
- /// <param name="start">Index to start enumerating at.</param>
- /// <param name="maxItems">Max number of items to enumerate.</param>
- /// <returns>An IEnumerator<T> that enumerates all the
- /// items in the given range.</returns>
- private IEnumerator<T> GetEnumerator(int start, int maxItems)
- {
- // We could use a recursive enumerator here, but an explicit stack
- // is a lot more efficient, and efficiency matters here.
-
- int startStamp = changeStamp; // to detect changes during enumeration.
-
- if (root != null && maxItems > 0) {
- ConcatNode[] stack = new ConcatNode[root.Depth];
- bool[] leftStack = new bool[root.Depth];
- int stackPtr = 0, startIndex = 0;
- Node current = root;
- LeafNode currentLeaf;
- ConcatNode currentConcat;
-
- if (start != 0) {
- // Set current to the node containing start, and set startIndex to
- // the index within that node.
- if (start < 0 || start >= root.Count)
- throw new ArgumentOutOfRangeException("start");
-
- currentConcat = current as ConcatNode;
- startIndex = start;
- while (currentConcat != null) {
- stack[stackPtr] = currentConcat;
-
- int leftCount = currentConcat.left.Count;
- if (startIndex < leftCount) {
- leftStack[stackPtr] = true;
- current = currentConcat.left;
- }
- else {
- leftStack[stackPtr] = false;
- current = currentConcat.right;
- startIndex -= leftCount;
- }
-
- ++stackPtr;
- currentConcat = current as ConcatNode;
- }
- }
-
- for (; ; ) {
- // If not already at a leaf, walk to the left to find a leaf node.
- while ((currentConcat = current as ConcatNode) != null) {
- stack[stackPtr] = currentConcat;
- leftStack[stackPtr] = true;
- ++stackPtr;
- current = currentConcat.left;
- }
-
- // Iterate the leaf.
- currentLeaf = (LeafNode)current;
-
- int limit = currentLeaf.Count;
- if (limit > startIndex + maxItems)
- limit = startIndex + maxItems;
-
- for (int i = startIndex; i < limit; ++i) {
- yield return currentLeaf.items[i];
- CheckEnumerationStamp(startStamp);
- }
-
- // Update the number of items to interate.
- maxItems -= limit - startIndex;
- if (maxItems <= 0)
- yield break; // Done!
-
- // From now on, start enumerating at 0.
- startIndex = 0;
-
- // Go back up the stack until we find a place to the right
- // we didn't just come from.
- for (; ; ) {
- ConcatNode parent;
- if (stackPtr == 0)
- yield break; // iteration is complete.
-
- parent = stack[--stackPtr];
- if (leftStack[stackPtr]) {
- leftStack[stackPtr] = false;
- ++stackPtr;
- current = parent.right;
- break;
- }
-
- current = parent;
- // And keep going up...
- }
-
- // current is now a new node we need to visit. Loop around to get it.
- }
- }
- }
-
- /// <summary>
- /// Enumerates all of the items in the list, in order. The item at index 0
- /// is enumerated first, then the item at index 1, and so on. Usually, the
- /// foreach statement is used to call this method implicitly.
- /// </summary>
- /// <remarks>Enumerating all of the items in the list take time O(N), where
- /// N is the number of items in the list. Using GetEnumerator() or foreach
- /// is much more efficient than accessing all items by index.</remarks>
- /// <returns>An IEnumerator<T> that enumerates all the
- /// items in the list.</returns>
- public sealed override IEnumerator<T> GetEnumerator()
- {
- return GetEnumerator(0, int.MaxValue);
- }
-
- /// <summary>
- /// Given an IEnumerable<T>, create a new Node with all of the
- /// items in the enumerable. Returns null if the enumerable has no items.
- /// </summary>
- /// <param name="collection">The collection to copy.</param>
- /// <returns>Returns a Node, not shared or with any shared children,
- /// with the items from the collection. If the collection was empty,
- /// null is returned.</returns>
- private static Node NodeFromEnumerable(IEnumerable<T> collection)
- {
- Node node = null;
- LeafNode leaf;
- IEnumerator<T> enumerator = collection.GetEnumerator();
-
- while ((leaf = LeafFromEnumerator(enumerator)) != null) {
- if (node == null)
- node = leaf;
- else {
- if ((uint)(node.count) + (uint)(leaf.count) > MAXITEMS)
- throw new InvalidOperationException(Strings.CollectionTooLarge);
-
- node = node.AppendInPlace(leaf, true);
- }
- }
-
- return node;
- }
-
- /// <summary>
- /// Consumes up to MAXLEAF items from an Enumerator and places them in a leaf
- /// node. If the enumerator is at the end, null is returned.
- /// </summary>
- /// <param name="enumerator">The enumerator to take items from.</param>
- /// <returns>A LeafNode with items taken from the enumerator. </returns>
- private static LeafNode LeafFromEnumerator(IEnumerator<T> enumerator)
- {
- int i = 0;
- T[] items = null;
-
- while (i < MAXLEAF && enumerator.MoveNext()) {
- if (i == 0)
- items = new T[MAXLEAF];
-
- if (items!=null)
- items[i++] = enumerator.Current;
- }
-
- if (items != null)
- return new LeafNode(i, items);
- else
- return null;
- }
-
- /// <summary>
- /// Create a node that has N copies of the given node.
- /// </summary>
- /// <param name="copies">Number of copies. Must be non-negative.</param>
- /// <param name="node">Node to make copies of.</param>
- /// <returns>null if node is null or copies is 0. Otherwise, a node consisting of <paramref name="copies"/> copies
- /// of node.</returns>
- /// <exception cref="ArgumentOutOfRangeException">copies is negative.</exception>
- private static Node NCopiesOfNode(int copies, Node node)
- {
- if (copies < 0)
- throw new ArgumentOutOfRangeException("copies", Strings.ArgMustNotBeNegative);
-
- // Do the simple cases.
- if (copies == 0 || node == null)
- return null;
- if (copies == 1)
- return node;
-
- if (copies * (long)(node.count) > MAXITEMS)
- throw new InvalidOperationException(Strings.CollectionTooLarge);
-
- // Build up the copies by powers of two.
- int n = 1;
- Node power = node, builder = null;
- while (copies > 0) {
- power.MarkShared();
-
- if ((copies & n) != 0) {
- // This power of two is used in the final result.
- copies -= n;
- if (builder == null)
- builder = power;
- else
- builder = builder.Append(power, false);
- }
-
- n *= 2;
- power = power.Append(power, false);
- }
-
- return builder;
- }
-
- /// <summary>
- /// Check the balance of the current tree and rebalance it if it is more than BALANCEFACTOR
- /// levels away from fully balanced. Note that rebalancing a tree may leave it two levels away from
- /// fully balanced.
- /// </summary>
- private void CheckBalance()
- {
- if (root != null &&
- (root.Depth > BALANCEFACTOR && !(root.Depth - BALANCEFACTOR <= MAXFIB && Count >= FIBONACCI[root.Depth - BALANCEFACTOR])))
- {
- Rebalance();
- }
- }
-
- /// <summary>
- /// Rebalance the current tree. Once rebalanced, the depth of the current tree is no more than
- /// two levels from fully balanced, where fully balanced is defined as having Fibonacci(N+2) or more items
- /// in a tree of depth N.
- /// </summary>
- /// <remarks>The rebalancing algorithm is from "Ropes: an Alternative to Strings", by
- /// Boehm, Atkinson, and Plass, in SOFTWARE--PRACTICE AND EXPERIENCE, VOL. 25(12), 1315–1330 (DECEMBER 1995).
- /// </remarks>
- internal void Rebalance()
…
Large files files are truncated, but you can click here to view the full file