PageRenderTime 68ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/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
  1. //******************************
  2. // Written by Peter Golde
  3. // Copyright (c) 2004-2007, Wintellect
  4. //
  5. // Use and restribution of this code is subject to the license agreement
  6. // contained in the file "License.txt" accompanying this file.
  7. //******************************
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Diagnostics;
  11. // CONSIDER: provide more efficient implementation of CopyTo.
  12. namespace Wintellect.PowerCollections
  13. {
  14. /// <summary>
  15. /// BigList&lt;T&gt; provides a list of items, in order, with indices of the items ranging from 0 to one less
  16. /// than the count of items in the collection. BigList&lt;T&gt; is optimized for efficient operations on large (&gt;100 items)
  17. /// lists, especially for insertions, deletions, copies, and concatinations.
  18. /// </summary>
  19. /// <remarks>
  20. /// <para>BigList&lt;T&gt; class is similar in functionality to the standard List&lt;T&gt; class. Both classes
  21. /// provide a collection that stores an set of items in order, with indices of the items ranging from 0 to one less
  22. /// than the count of items in the collection. Both classes provide the ability to add and remove items from any index,
  23. /// and the get or set the item at any index.</para>
  24. /// <para>BigList&lt;T&gt; differs significantly from List&lt;T&gt; in the performance of various operations,
  25. /// especially when the lists become large (several hundred items or more). With List&lt;T&gt;, inserting or removing
  26. /// elements from anywhere in a large list except the end is very inefficient -- every item after the point of inserting
  27. /// or deletion has to be moved in the list. The BigList&lt;T&gt; class, however, allows for fast insertions
  28. /// and deletions anywhere in the list. Furthermore, BigList&lt;T&gt; allows copies of a list, sub-parts
  29. /// of a list, and concatinations of two lists to be very fast. When a copy is made of part or all of a BigList,
  30. /// two lists shared storage for the parts of the lists that are the same. Only when one of the lists is changed is additional
  31. /// memory allocated to store the distinct parts of the lists.</para>
  32. /// <para>Of course, there is a small price to pay for this extra flexibility. Although still quite efficient, using an
  33. /// index to get or change one element of a BigList, while still reasonably efficient, is significantly slower than using
  34. /// a plain List. Because of this, if you want to process every element of a BigList, using a foreach loop is a lot
  35. /// more efficient than using a for loop and indexing the list.</para>
  36. /// <para>In general, use a List when the only operations you are using are Add (to the end), foreach,
  37. /// or indexing, or you are very sure the list will always remain small (less than 100 items). For large (&gt;100 items) lists
  38. /// that do insertions, removals, copies, concatinations, or sub-ranges, BigList will be more efficient than List.
  39. /// In almost all cases, BigList is more efficient and easier to use than LinkedList.</para>
  40. /// </remarks>
  41. /// <typeparam name="T">The type of items to store in the BigList.</typeparam>
  42. [Serializable]
  43. public class BigList<T>: ListBase<T>, ICloneable
  44. {
  45. const uint MAXITEMS = int.MaxValue - 1; // maximum number of items in a BigList.
  46. #if DEBUG
  47. const int MAXLEAF = 8; // Maximum number of elements in a leaf node -- small for debugging purposes.
  48. #else
  49. const int MAXLEAF = 120; // Maximum number of elements in a leaf node.
  50. #endif
  51. const int BALANCEFACTOR = 6; // how far the root must be in depth from fully balanced to invoke the rebalance operation (min 3).
  52. // The fibonacci numbers. Used in the rebalancing algorithm. Final MaxValue makes sure we don't go off the end.
  53. static readonly int[] FIBONACCI = {1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584,
  54. 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040,
  55. 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986,
  56. 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, int.MaxValue};
  57. const int MAXFIB = 44; // maximum index in the above, not counting the final MaxValue.
  58. // If null, the BigList is empty. If non-null, the list has at least one item.
  59. private Node root;
  60. // Holds the change stamp for the collection.
  61. private int changeStamp;
  62. /// <summary>
  63. /// Must be called whenever there is a structural change in the tree. Causes
  64. /// changeStamp to be changed, which causes any in-progress enumerations
  65. /// to throw exceptions.
  66. /// </summary>
  67. private void StopEnumerations()
  68. {
  69. ++changeStamp;
  70. }
  71. /// <summary>
  72. /// Checks the given stamp against the current change stamp. If different, the
  73. /// collection has changed during enumeration and an InvalidOperationException
  74. /// must be thrown
  75. /// </summary>
  76. /// <param name="startStamp">changeStamp at the start of the enumeration.</param>
  77. private void CheckEnumerationStamp(int startStamp)
  78. {
  79. if (startStamp != changeStamp) {
  80. throw new InvalidOperationException(Strings.ChangeDuringEnumeration);
  81. }
  82. }
  83. /// <summary>
  84. /// Creates a new BigList. The BigList is initially empty.
  85. /// </summary>
  86. /// <remarks>Creating a empty BigList takes constant time and consumes a very small amount of memory.</remarks>
  87. public BigList()
  88. {
  89. root = null;
  90. }
  91. /// <summary>
  92. /// Creates a new BigList initialized with the items from <paramref name="collection"/>, in order.
  93. /// </summary>
  94. /// <remarks>Initializing the tree list with the elements of collection takes time O(N), where N is the number of
  95. /// items in <paramref name="collection"/>.</remarks>
  96. /// <param name="collection">The collection used to initialize the BigList. </param>
  97. /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
  98. public BigList(IEnumerable<T> collection)
  99. {
  100. if (collection == null)
  101. throw new ArgumentNullException("collection");
  102. root = NodeFromEnumerable(collection);
  103. CheckBalance();
  104. }
  105. /// <summary>
  106. /// Creates a new BigList initialized with a given number of copies of the items from <paramref name="collection"/>, in order.
  107. /// </summary>
  108. /// <remarks>Initializing the tree list with the elements of collection takes time O(N + log K), where N is the number of
  109. /// items in <paramref name="collection"/>, and K is the number of copies.</remarks>
  110. /// <param name="copies">Number of copies of the collection to use.</param>
  111. /// <param name="collection">The collection used to initialize the BigList. </param>
  112. /// <exception cref="ArgumentOutOfRangeException"><paramref name="copies"/> is negative.</exception>
  113. /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
  114. public BigList(IEnumerable<T> collection, int copies)
  115. {
  116. if (collection == null)
  117. throw new ArgumentNullException("collection");
  118. root = NCopiesOfNode(copies, NodeFromEnumerable(collection));
  119. CheckBalance();
  120. }
  121. /// <summary>
  122. /// Creates a new BigList that is a copy of <paramref name="list"/>.
  123. /// </summary>
  124. /// <remarks>Copying a BigList takes constant time, and little
  125. /// additional memory, since the storage for the items of the two lists is shared. However, changing
  126. /// either list will take additional time and memory. Portions of the list are copied when they are changed.</remarks>
  127. /// <param name="list">The BigList to copy. </param>
  128. /// <exception cref="ArgumentNullException"><paramref name="list"/> is null.</exception>
  129. public BigList(BigList<T> list)
  130. {
  131. if (list == null)
  132. throw new ArgumentNullException("list");
  133. if (list.root == null)
  134. root = null;
  135. else {
  136. list.root.MarkShared();
  137. root = list.root;
  138. }
  139. }
  140. /// <summary>
  141. /// Creates a new BigList that is several copies of <paramref name="list"/>.
  142. /// </summary>
  143. /// <remarks>Creating K copies of a BigList takes time O(log K), and O(log K)
  144. /// additional memory, since the storage for the items of the two lists is shared. However, changing
  145. /// either list will take additional time and memory. Portions of the list are copied when they are changed.</remarks>
  146. /// <param name="copies">Number of copies of the collection to use.</param>
  147. /// <param name="list">The BigList to copy. </param>
  148. /// <exception cref="ArgumentNullException"><paramref name="list"/> is null.</exception>
  149. public BigList(BigList<T> list, int copies)
  150. {
  151. if (list == null)
  152. throw new ArgumentNullException("list");
  153. if (list.root == null)
  154. root = null;
  155. else {
  156. list.root.MarkShared();
  157. root = NCopiesOfNode(copies, list.root);
  158. }
  159. }
  160. /// <summary>
  161. /// Creates a new BigList from the indicated Node.
  162. /// </summary>
  163. /// <param name="node">Node that becomes the new root. If null, the new BigList is empty.</param>
  164. private BigList(Node node)
  165. {
  166. this.root = node;
  167. CheckBalance();
  168. }
  169. /// <summary>
  170. /// Gets the number of items stored in the BigList. The indices of the items
  171. /// range from 0 to Count-1.
  172. /// </summary>
  173. /// <remarks>Getting the number of items in the BigList takes constant time.</remarks>
  174. /// <value>The number of items in the BigList.</value>
  175. public sealed override int Count
  176. {
  177. get
  178. {
  179. if (root == null)
  180. return 0;
  181. else
  182. return root.Count;
  183. }
  184. }
  185. /// <summary>
  186. /// Gets or sets an item in the list, by index.
  187. /// </summary>
  188. /// <remarks><para> Gettingor setting an item takes time O(log N), where N is the number of items
  189. /// in the list.</para>
  190. /// <para>To process each of the items in the list, using GetEnumerator() or a foreach loop is more efficient
  191. /// that accessing each of the elements by index.</para></remarks>
  192. /// <param name="index">The index of the item to get or set. The first item in the list
  193. /// has index 0, the last item has index Count-1.</param>
  194. /// <returns>The value of the item at the given index.</returns>
  195. /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than zero or
  196. /// greater than or equal to Count.</exception>
  197. public sealed override T this[int index]
  198. {
  199. get
  200. {
  201. // This could just be a simple call to GetAt on the root.
  202. // It is recoded as an interative algorithm for performance.
  203. if (root == null || index < 0 || index >= root.Count)
  204. throw new ArgumentOutOfRangeException("index");
  205. Node current = root;
  206. ConcatNode curConcat = current as ConcatNode;
  207. while (curConcat != null) {
  208. int leftCount = curConcat.left.Count;
  209. if (index < leftCount)
  210. current = curConcat.left;
  211. else {
  212. current = curConcat.right;
  213. index -= leftCount;
  214. }
  215. curConcat = current as ConcatNode;
  216. }
  217. LeafNode curLeaf = (LeafNode)current;
  218. return curLeaf.items[index];
  219. }
  220. set
  221. {
  222. // This could just be a simple call to SetAtInPlace on the root.
  223. // It is recoded as an interative algorithm for performance.
  224. if (root == null || index < 0 || index >= root.Count)
  225. throw new ArgumentOutOfRangeException("index");
  226. // Like List<T>, we stop enumerations after a set operation. This could be made
  227. // to not happen, but it would be complex, because set operations on a shared node
  228. // could change the node.
  229. StopEnumerations();
  230. if (root.Shared)
  231. root = root.SetAt(index, value);
  232. Node current = root;
  233. ConcatNode curConcat = current as ConcatNode;
  234. while (curConcat != null) {
  235. int leftCount = curConcat.left.Count;
  236. if (index < leftCount) {
  237. current = curConcat.left;
  238. if (current.Shared) {
  239. curConcat.left = current.SetAt(index, value);
  240. return;
  241. }
  242. }
  243. else {
  244. current = curConcat.right;
  245. index -= leftCount;
  246. if (current.Shared) {
  247. curConcat.right = current.SetAt(index, value);
  248. return;
  249. }
  250. }
  251. curConcat = current as ConcatNode;
  252. }
  253. LeafNode curLeaf = (LeafNode)current;
  254. curLeaf.items[index] = value;
  255. }
  256. }
  257. /// <summary>
  258. /// Removes all of the items from the BigList.
  259. /// </summary>
  260. /// <remarks>Clearing a BigList takes constant time.</remarks>
  261. public sealed override void Clear()
  262. {
  263. StopEnumerations();
  264. root = null;
  265. }
  266. /// <summary>
  267. /// Inserts a new item at the given index in the BigList. All items at indexes
  268. /// equal to or greater than <paramref name="index"/> move up one index.
  269. /// </summary>
  270. /// <remarks>The amount of time to insert an item is O(log N), no matter where
  271. /// in the list the insertion occurs. Inserting an item at the beginning or end of the
  272. /// list is O(N).
  273. /// </remarks>
  274. /// <param name="index">The index to insert the item at. After the
  275. /// insertion, the inserted item is located at this index. The
  276. /// first item has index 0.</param>
  277. /// <param name="item">The item to insert at the given index.</param>
  278. /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is
  279. /// less than zero or greater than Count.</exception>
  280. public sealed override void Insert(int index, T item)
  281. {
  282. StopEnumerations();
  283. if ((uint)Count + 1 > MAXITEMS)
  284. throw new InvalidOperationException(Strings.CollectionTooLarge);
  285. if (index <= 0 || index >= Count) {
  286. if (index == 0)
  287. AddToFront(item);
  288. else if (index == Count)
  289. Add(item);
  290. else
  291. throw new ArgumentOutOfRangeException("index");
  292. }
  293. else {
  294. if (root == null)
  295. root = new LeafNode(item);
  296. else {
  297. Node newRoot = root.InsertInPlace(index, item);
  298. if (newRoot != root) {
  299. root = newRoot;
  300. CheckBalance();
  301. }
  302. }
  303. }
  304. }
  305. /// <summary>
  306. /// Inserts a collection of items at the given index in the BigList. All items at indexes
  307. /// equal to or greater than <paramref name="index"/> increase their indices
  308. /// by the number of items inserted.
  309. /// </summary>
  310. /// <remarks>The amount of time to insert an arbitrary collection in the BigList is O(M + log N),
  311. /// where M is the number of items inserted, and N is the number of items in the list.
  312. /// </remarks>
  313. /// <param name="index">The index to insert the collection at. After the
  314. /// insertion, the first item of the inserted collection is located at this index. The
  315. /// first item has index 0.</param>
  316. /// <param name="collection">The collection of items to insert at the given index.</param>
  317. /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is
  318. /// less than zero or greater than Count.</exception>
  319. /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
  320. public void InsertRange(int index, IEnumerable<T> collection)
  321. {
  322. StopEnumerations();
  323. if (collection == null)
  324. throw new ArgumentNullException("collection");
  325. if (index <= 0 || index >= Count) {
  326. if (index == 0)
  327. AddRangeToFront(collection);
  328. else if (index == Count)
  329. AddRange(collection);
  330. else
  331. throw new ArgumentOutOfRangeException("index");
  332. }
  333. else {
  334. Node node = NodeFromEnumerable(collection);
  335. if (node == null)
  336. return;
  337. else if (root == null)
  338. root = node;
  339. else {
  340. if ((uint)Count + (uint)node.Count > MAXITEMS)
  341. throw new InvalidOperationException(Strings.CollectionTooLarge);
  342. Node newRoot = root.InsertInPlace(index, node, true);
  343. if (newRoot != root) {
  344. root = newRoot;
  345. CheckBalance();
  346. }
  347. }
  348. }
  349. }
  350. /// <summary>
  351. /// Inserts a BigList of items at the given index in the BigList. All items at indexes
  352. /// equal to or greater than <paramref name="index"/> increase their indices
  353. /// by the number of items inserted.
  354. /// </summary>
  355. /// <remarks>The amount of time to insert another BigList is O(log N),
  356. /// where N is the number of items in the list, regardless of the number of items in the
  357. /// inserted list. Storage is shared between the two lists until one of them is changed.
  358. /// </remarks>
  359. /// <param name="index">The index to insert the collection at. After the
  360. /// insertion, the first item of the inserted collection is located at this index. The
  361. /// first item has index 0.</param>
  362. /// <param name="list">The BigList of items to insert at the given index.</param>
  363. /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is
  364. /// less than zero or greater than Count.</exception>
  365. /// <exception cref="ArgumentNullException"><paramref name="list"/> is null.</exception>
  366. public void InsertRange(int index, BigList<T> list)
  367. {
  368. StopEnumerations();
  369. if (list == null)
  370. throw new ArgumentNullException("list");
  371. if ((uint)Count + (uint)list.Count > MAXITEMS)
  372. throw new InvalidOperationException(Strings.CollectionTooLarge);
  373. if (index <= 0 || index >= Count) {
  374. if (index == 0)
  375. AddRangeToFront(list);
  376. else if (index == Count)
  377. AddRange(list);
  378. else
  379. throw new ArgumentOutOfRangeException("index");
  380. }
  381. else {
  382. if (list.Count == 0)
  383. return;
  384. if (root == null) {
  385. list.root.MarkShared();
  386. root = list.root;
  387. }
  388. else {
  389. if (list.root == root)
  390. root.MarkShared(); // make sure inserting into itself works.
  391. Node newRoot = root.InsertInPlace(index, list.root, false);
  392. if (newRoot != root) {
  393. root = newRoot;
  394. CheckBalance();
  395. }
  396. }
  397. }
  398. }
  399. /// <summary>
  400. /// Removes the item at the given index in the BigList. All items at indexes
  401. /// greater than <paramref name="index"/> move down one index.
  402. /// </summary>
  403. /// <remarks>The amount of time to delete an item in the BigList is O(log N),
  404. /// where N is the number of items in the list.
  405. /// </remarks>
  406. /// <param name="index">The index in the list to remove the item at. The
  407. /// first item in the list has index 0.</param>
  408. /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is
  409. /// less than zero or greater than or equal to Count.</exception>
  410. public sealed override void RemoveAt(int index)
  411. {
  412. RemoveRange(index, 1);
  413. }
  414. /// <summary>
  415. /// Removes a range of items at the given index in the Deque. All items at indexes
  416. /// greater than <paramref name="index"/> move down <paramref name="count"/> indices
  417. /// in the Deque.
  418. /// </summary>
  419. /// <remarks>The amount of time to delete <paramref name="count"/> items in the Deque is proportional
  420. /// to the distance of index from the closest end of the Deque, plus <paramref name="count"/>:
  421. /// O(count + Min(<paramref name="index"/>, Count - 1 - <paramref name="index"/>)).
  422. /// </remarks>
  423. /// <param name="index">The index in the list to remove the range at. The
  424. /// first item in the list has index 0.</param>
  425. /// <param name="count">The number of items to remove.</param>
  426. /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is
  427. /// less than zero or greater than or equal to Count, or <paramref name="count"/> is less than zero
  428. /// or too large.</exception>
  429. public void RemoveRange(int index, int count)
  430. {
  431. if (count == 0)
  432. return; // nothing to do.
  433. if (index < 0 || index >= Count)
  434. throw new ArgumentOutOfRangeException("index");
  435. if (count < 0 || count > Count - index)
  436. throw new ArgumentOutOfRangeException("count");
  437. StopEnumerations();
  438. Node newRoot = root.RemoveRangeInPlace(index, index + count - 1);
  439. if (newRoot != root) {
  440. root = newRoot;
  441. CheckBalance();
  442. }
  443. }
  444. /// <summary>
  445. /// Adds an item to the end of the BigList. The indices of all existing items
  446. /// in the Deque are unchanged.
  447. /// </summary>
  448. /// <remarks>Adding an item takes, on average, constant time.</remarks>
  449. /// <param name="item">The item to add.</param>
  450. public sealed override void Add(T item)
  451. {
  452. if ((uint)Count + 1 > MAXITEMS)
  453. throw new InvalidOperationException(Strings.CollectionTooLarge);
  454. StopEnumerations();
  455. if (root == null)
  456. root = new LeafNode(item);
  457. else {
  458. Node newRoot = root.AppendInPlace(item);
  459. if (newRoot != root) {
  460. root = newRoot;
  461. CheckBalance();
  462. }
  463. }
  464. }
  465. /// <summary>
  466. /// Adds an item to the beginning of the BigList. The indices of all existing items
  467. /// in the Deque are increased by one, and the new item has index zero.
  468. /// </summary>
  469. /// <remarks>Adding an item takes, on average, constant time.</remarks>
  470. /// <param name="item">The item to add.</param>
  471. public void AddToFront(T item)
  472. {
  473. if ((uint)Count + 1 > MAXITEMS)
  474. throw new InvalidOperationException(Strings.CollectionTooLarge);
  475. StopEnumerations();
  476. if (root == null)
  477. root = new LeafNode(item);
  478. else {
  479. Node newRoot = root.PrependInPlace(item);
  480. if (newRoot != root) {
  481. root = newRoot;
  482. CheckBalance();
  483. }
  484. }
  485. }
  486. /// <summary>
  487. /// Adds a collection of items to the end of BigList. The indices of all existing items
  488. /// are unchanged. The last item in the added collection becomes the
  489. /// last item in the BigList.
  490. /// </summary>
  491. /// <remarks>This method takes time O(M + log N), where M is the number of items in the
  492. /// <paramref name="collection"/>, and N is the size of the BigList.</remarks>
  493. /// <param name="collection">The collection of items to add.</param>
  494. /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
  495. public void AddRange(IEnumerable<T> collection)
  496. {
  497. if (collection == null)
  498. throw new ArgumentNullException("collection");
  499. StopEnumerations();
  500. Node node = NodeFromEnumerable(collection);
  501. if (node == null)
  502. return;
  503. else if (root == null) {
  504. root = node;
  505. CheckBalance();
  506. }
  507. else {
  508. if ((uint)Count + (uint)node.count > MAXITEMS)
  509. throw new InvalidOperationException(Strings.CollectionTooLarge);
  510. Node newRoot = root.AppendInPlace(node, true);
  511. if (newRoot != root) {
  512. root = newRoot;
  513. CheckBalance();
  514. }
  515. }
  516. }
  517. /// <summary>
  518. /// Adds a collection of items to the front of BigList. The indices of all existing items
  519. /// in the are increased by the number of items in <paramref name="collection"/>.
  520. /// The first item in the added collection becomes the first item in the BigList.
  521. /// </summary>
  522. /// <remarks>This method takes time O(M + log N), where M is the number of items in the
  523. /// <paramref name="collection"/>, and N is the size of the BigList.</remarks>
  524. /// <param name="collection">The collection of items to add.</param>
  525. /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
  526. public void AddRangeToFront(IEnumerable<T> collection)
  527. {
  528. if (collection == null)
  529. throw new ArgumentNullException("collection");
  530. StopEnumerations();
  531. Node node = NodeFromEnumerable(collection);
  532. if (node == null)
  533. return;
  534. else if (root == null) {
  535. root = node;
  536. CheckBalance();
  537. }
  538. else {
  539. if ((uint)Count + (uint)node.Count > MAXITEMS)
  540. throw new InvalidOperationException(Strings.CollectionTooLarge);
  541. Node newRoot = root.PrependInPlace(node, true);
  542. if (newRoot != root) {
  543. root = newRoot;
  544. CheckBalance();
  545. }
  546. }
  547. }
  548. /// <summary>
  549. /// Creates a new BigList that is a copy of this list.
  550. /// </summary>
  551. /// <remarks>Copying a BigList takes constant time, and little
  552. /// additional memory, since the storage for the items of the two lists is shared. However, changing
  553. /// either list will take additional time and memory. Portions of the list are copied when they are changed.</remarks>
  554. /// <returns>A copy of the current list</returns>
  555. public BigList<T> Clone()
  556. {
  557. if (root == null)
  558. return new BigList<T>();
  559. else {
  560. root.MarkShared();
  561. return new BigList<T>(root);
  562. }
  563. }
  564. /// <summary>
  565. /// Creates a new BigList that is a copy of this list.
  566. /// </summary>
  567. /// <remarks>Copying a BigList takes constant time, and little
  568. /// additional memory, since the storage for the items of the two lists is shared. However, changing
  569. /// either list will take additional time and memory. Portions of the list are copied when they are changed.</remarks>
  570. /// <returns>A copy of the current list</returns>
  571. object ICloneable.Clone()
  572. {
  573. return Clone();
  574. }
  575. /// <summary>
  576. /// Makes a deep clone of this BigList. A new BigList is created with a clone of
  577. /// each element of this set, by calling ICloneable.Clone on each element. If T is
  578. /// a value type, then this method is the same as Clone.
  579. /// </summary>
  580. /// <remarks><para>If T is a reference type, it must implement
  581. /// ICloneable. Otherwise, an InvalidOperationException is thrown.</para>
  582. /// <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>
  583. /// <returns>The cloned set.</returns>
  584. /// <exception cref="InvalidOperationException">T is a reference type that does not implement ICloneable.</exception>
  585. public BigList<T> CloneContents()
  586. {
  587. if (root == null)
  588. return new BigList<T>();
  589. else {
  590. bool itemIsValueType;
  591. if (!Util.IsCloneableType(typeof(T), out itemIsValueType))
  592. throw new InvalidOperationException(string.Format(Strings.TypeNotCloneable, typeof(T).FullName));
  593. if (itemIsValueType)
  594. return Clone();
  595. // Create a new list by converting each item in this list via cloning.
  596. return new BigList<T>(Algorithms.Convert<T, T>(this, delegate(T item)
  597. {
  598. if (item == null)
  599. return default(T); // Really null, because we know T is a reference type
  600. else
  601. return (T)(((ICloneable)item).Clone());
  602. }));
  603. }
  604. }
  605. /// <summary>
  606. /// Adds a BigList of items to the end of BigList. The indices of all existing items
  607. /// are unchanged. The last item in <paramref name="list"/> becomes the
  608. /// last item in this list. The added list <paramref name="list"/> is unchanged.
  609. /// </summary>
  610. /// <remarks>This method takes, on average, constant time, regardless of the size
  611. /// of either list. Although conceptually all of the items in <paramref name="list"/> are
  612. /// copied, storage is shared between the two lists until changes are made to the
  613. /// shared sections.</remarks>
  614. /// <param name="list">The list of items to add.</param>
  615. /// <exception cref="ArgumentNullException"><paramref name="list"/> is null.</exception>
  616. public void AddRange(BigList<T> list)
  617. {
  618. if (list == null)
  619. throw new ArgumentNullException("list");
  620. if ((uint)Count + (uint)list.Count > MAXITEMS)
  621. throw new InvalidOperationException(Strings.CollectionTooLarge);
  622. if (list.Count == 0)
  623. return;
  624. StopEnumerations();
  625. if (root == null) {
  626. list.root.MarkShared();
  627. root = list.root;
  628. }
  629. else {
  630. Node newRoot = root.AppendInPlace(list.root, false);
  631. if (newRoot != root) {
  632. root = newRoot;
  633. CheckBalance();
  634. }
  635. }
  636. }
  637. /// <summary>
  638. /// Adds a BigList of items to the front of BigList. The indices of all existing items
  639. /// are increased by the number of items in <paramref name="list"/>. The first item in <paramref name="list"/>
  640. /// becomes the first item in this list. The added list <paramref name="list"/> is unchanged.
  641. /// </summary>
  642. /// <remarks>This method takes, on average, constant time, regardless of the size
  643. /// of either list. Although conceptually all of the items in <paramref name="list"/> are
  644. /// copied, storage is shared between the two lists until changes are made to the
  645. /// shared sections.</remarks>
  646. /// <param name="list">The list of items to add.</param>
  647. /// <exception cref="ArgumentNullException"><paramref name="list"/> is null.</exception>
  648. public void AddRangeToFront(BigList<T> list)
  649. {
  650. if (list == null)
  651. throw new ArgumentNullException("list");
  652. if ((uint)Count + (uint)list.Count > MAXITEMS)
  653. throw new InvalidOperationException(Strings.CollectionTooLarge);
  654. if (list.Count == 0)
  655. return;
  656. StopEnumerations();
  657. if (root == null) {
  658. list.root.MarkShared();
  659. root = list.root;
  660. }
  661. else {
  662. Node newRoot = root.PrependInPlace(list.root, false);
  663. if (newRoot != root) {
  664. root = newRoot;
  665. CheckBalance();
  666. }
  667. }
  668. }
  669. /// <summary>
  670. /// Concatenates two lists together to create a new list. Both lists being concatenated
  671. /// are unchanged. The resulting list contains all the items in <paramref name="first"/>, followed
  672. /// by all the items in <paramref name="second"/>.
  673. /// </summary>
  674. /// <remarks>This method takes, on average, constant time, regardless of the size
  675. /// of either list. Although conceptually all of the items in both lists are
  676. /// copied, storage is shared until changes are made to the
  677. /// shared sections.</remarks>
  678. /// <param name="first">The first list to concatenate.</param>
  679. /// <param name="second">The second list to concatenate.</param>
  680. /// <exception cref="ArgumentNullException"><paramref name="first"/> or <paramref name="second"/> is null.</exception>
  681. public static BigList<T> operator +(BigList<T> first, BigList<T> second)
  682. {
  683. if (first == null)
  684. throw new ArgumentNullException("first");
  685. if (second == null)
  686. throw new ArgumentNullException("second");
  687. if ((uint)first.Count + (uint)second.Count > MAXITEMS)
  688. throw new InvalidOperationException(Strings.CollectionTooLarge);
  689. if (first.Count == 0)
  690. return second.Clone();
  691. else if (second.Count == 0)
  692. return first.Clone();
  693. else {
  694. BigList<T> result = new BigList<T>(first.root.Append(second.root, false));
  695. result.CheckBalance();
  696. return result;
  697. }
  698. }
  699. /// <summary>
  700. /// Creates a new list that contains a subrange of elements from this list. The
  701. /// current list is unchanged.
  702. /// </summary>
  703. /// <remarks>This method takes take O(log N), where N is the size of the current list. Although
  704. /// the sub-range is conceptually copied, storage is shared between the two lists until a change
  705. /// is made to the shared items.</remarks>
  706. /// <remarks>If a view of a sub-range is desired, instead of a copy, use the
  707. /// more efficient <see cref="Range"/> method, which provides a view onto a sub-range of items.</remarks>
  708. /// <param name="index">The starting index of the sub-range.</param>
  709. /// <param name="count">The number of items in the sub-range. If this is zero,
  710. /// the returned list is empty.</param>
  711. /// <returns>A new list with the <paramref name="count"/> items that start at <paramref name="index"/>.</returns>
  712. public BigList<T> GetRange(int index, int count)
  713. {
  714. if (count == 0)
  715. return new BigList<T>();
  716. if (index < 0 || index >= Count)
  717. throw new ArgumentOutOfRangeException("index");
  718. if (count < 0 || count > Count - index)
  719. throw new ArgumentOutOfRangeException("count");
  720. return new BigList<T>(root.Subrange(index, index + count - 1));
  721. }
  722. /// <summary>
  723. /// Returns a view onto a sub-range of this list. Items are not copied; the
  724. /// returned IList&lt;T&gt; is simply a different view onto the same underlying items. Changes to this list
  725. /// are reflected in the view, and vice versa. Insertions and deletions in the view change the size of the
  726. /// view, but insertions and deletions in the underlying list do not.
  727. /// </summary>
  728. /// <remarks>
  729. /// <para>If a copy of the sub-range is desired, use the <see cref="GetRange"/> method instead.</para>
  730. /// <para>This method can be used to apply an algorithm to a portion of a list. For example:</para>
  731. /// <code>Algorithms.ReverseInPlace(list.Range(3, 6))</code>
  732. /// will reverse the 6 items beginning at index 3.</remarks>
  733. /// <param name="index">The starting index of the view.</param>
  734. /// <param name="count">The number of items in the view.</param>
  735. /// <returns>A list that is a view onto the given sub-list. </returns>
  736. /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> or <paramref name="count"/> is negative.</exception>
  737. /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> + <paramref name="count"/> is greater than the
  738. /// size of this list.</exception>
  739. public sealed override IList<T> Range(int index, int count)
  740. {
  741. if (index < 0 || index > this.Count || (index == this.Count && count != 0))
  742. throw new ArgumentOutOfRangeException("index");
  743. if (count < 0 || count > this.Count || count + index > this.Count)
  744. throw new ArgumentOutOfRangeException("count");
  745. return new BigListRange(this, index, count);
  746. }
  747. /// <summary>
  748. /// Enumerates a range of the items in the list, in order. The item at <paramref name="start"/>
  749. /// is enumerated first, then the next item at index 1, and so on. At most <paramref name="maxItems"/>
  750. /// items are enumerated.
  751. /// </summary>
  752. /// <remarks>Enumerating all of the items in the list take time O(N), where
  753. /// N is the number of items being enumerated. Using GetEnumerator() or foreach
  754. /// is much more efficient than accessing all items by index.</remarks>
  755. /// <param name="start">Index to start enumerating at.</param>
  756. /// <param name="maxItems">Max number of items to enumerate.</param>
  757. /// <returns>An IEnumerator&lt;T&gt; that enumerates all the
  758. /// items in the given range.</returns>
  759. private IEnumerator<T> GetEnumerator(int start, int maxItems)
  760. {
  761. // We could use a recursive enumerator here, but an explicit stack
  762. // is a lot more efficient, and efficiency matters here.
  763. int startStamp = changeStamp; // to detect changes during enumeration.
  764. if (root != null && maxItems > 0) {
  765. ConcatNode[] stack = new ConcatNode[root.Depth];
  766. bool[] leftStack = new bool[root.Depth];
  767. int stackPtr = 0, startIndex = 0;
  768. Node current = root;
  769. LeafNode currentLeaf;
  770. ConcatNode currentConcat;
  771. if (start != 0) {
  772. // Set current to the node containing start, and set startIndex to
  773. // the index within that node.
  774. if (start < 0 || start >= root.Count)
  775. throw new ArgumentOutOfRangeException("start");
  776. currentConcat = current as ConcatNode;
  777. startIndex = start;
  778. while (currentConcat != null) {
  779. stack[stackPtr] = currentConcat;
  780. int leftCount = currentConcat.left.Count;
  781. if (startIndex < leftCount) {
  782. leftStack[stackPtr] = true;
  783. current = currentConcat.left;
  784. }
  785. else {
  786. leftStack[stackPtr] = false;
  787. current = currentConcat.right;
  788. startIndex -= leftCount;
  789. }
  790. ++stackPtr;
  791. currentConcat = current as ConcatNode;
  792. }
  793. }
  794. for (; ; ) {
  795. // If not already at a leaf, walk to the left to find a leaf node.
  796. while ((currentConcat = current as ConcatNode) != null) {
  797. stack[stackPtr] = currentConcat;
  798. leftStack[stackPtr] = true;
  799. ++stackPtr;
  800. current = currentConcat.left;
  801. }
  802. // Iterate the leaf.
  803. currentLeaf = (LeafNode)current;
  804. int limit = currentLeaf.Count;
  805. if (limit > startIndex + maxItems)
  806. limit = startIndex + maxItems;
  807. for (int i = startIndex; i < limit; ++i) {
  808. yield return currentLeaf.items[i];
  809. CheckEnumerationStamp(startStamp);
  810. }
  811. // Update the number of items to interate.
  812. maxItems -= limit - startIndex;
  813. if (maxItems <= 0)
  814. yield break; // Done!
  815. // From now on, start enumerating at 0.
  816. startIndex = 0;
  817. // Go back up the stack until we find a place to the right
  818. // we didn't just come from.
  819. for (; ; ) {
  820. ConcatNode parent;
  821. if (stackPtr == 0)
  822. yield break; // iteration is complete.
  823. parent = stack[--stackPtr];
  824. if (leftStack[stackPtr]) {
  825. leftStack[stackPtr] = false;
  826. ++stackPtr;
  827. current = parent.right;
  828. break;
  829. }
  830. current = parent;
  831. // And keep going up...
  832. }
  833. // current is now a new node we need to visit. Loop around to get it.
  834. }
  835. }
  836. }
  837. /// <summary>
  838. /// Enumerates all of the items in the list, in order. The item at index 0
  839. /// is enumerated first, then the item at index 1, and so on. Usually, the
  840. /// foreach statement is used to call this method implicitly.
  841. /// </summary>
  842. /// <remarks>Enumerating all of the items in the list take time O(N), where
  843. /// N is the number of items in the list. Using GetEnumerator() or foreach
  844. /// is much more efficient than accessing all items by index.</remarks>
  845. /// <returns>An IEnumerator&lt;T&gt; that enumerates all the
  846. /// items in the list.</returns>
  847. public sealed override IEnumerator<T> GetEnumerator()
  848. {
  849. return GetEnumerator(0, int.MaxValue);
  850. }
  851. /// <summary>
  852. /// Given an IEnumerable&lt;T&gt;, create a new Node with all of the
  853. /// items in the enumerable. Returns null if the enumerable has no items.
  854. /// </summary>
  855. /// <param name="collection">The collection to copy.</param>
  856. /// <returns>Returns a Node, not shared or with any shared children,
  857. /// with the items from the collection. If the collection was empty,
  858. /// null is returned.</returns>
  859. private static Node NodeFromEnumerable(IEnumerable<T> collection)
  860. {
  861. Node node = null;
  862. LeafNode leaf;
  863. IEnumerator<T> enumerator = collection.GetEnumerator();
  864. while ((leaf = LeafFromEnumerator(enumerator)) != null) {
  865. if (node == null)
  866. node = leaf;
  867. else {
  868. if ((uint)(node.count) + (uint)(leaf.count) > MAXITEMS)
  869. throw new InvalidOperationException(Strings.CollectionTooLarge);
  870. node = node.AppendInPlace(leaf, true);
  871. }
  872. }
  873. return node;
  874. }
  875. /// <summary>
  876. /// Consumes up to MAXLEAF items from an Enumerator and places them in a leaf
  877. /// node. If the enumerator is at the end, null is returned.
  878. /// </summary>
  879. /// <param name="enumerator">The enumerator to take items from.</param>
  880. /// <returns>A LeafNode with items taken from the enumerator. </returns>
  881. private static LeafNode LeafFromEnumerator(IEnumerator<T> enumerator)
  882. {
  883. int i = 0;
  884. T[] items = null;
  885. while (i < MAXLEAF && enumerator.MoveNext()) {
  886. if (i == 0)
  887. items = new T[MAXLEAF];
  888. if (items!=null)
  889. items[i++] = enumerator.Current;
  890. }
  891. if (items != null)
  892. return new LeafNode(i, items);
  893. else
  894. return null;
  895. }
  896. /// <summary>
  897. /// Create a node that has N copies of the given node.
  898. /// </summary>
  899. /// <param name="copies">Number of copies. Must be non-negative.</param>
  900. /// <param name="node">Node to make copies of.</param>
  901. /// <returns>null if node is null or copies is 0. Otherwise, a node consisting of <paramref name="copies"/> copies
  902. /// of node.</returns>
  903. /// <exception cref="ArgumentOutOfRangeException">copies is negative.</exception>
  904. private static Node NCopiesOfNode(int copies, Node node)
  905. {
  906. if (copies < 0)
  907. throw new ArgumentOutOfRangeException("copies", Strings.ArgMustNotBeNegative);
  908. // Do the simple cases.
  909. if (copies == 0 || node == null)
  910. return null;
  911. if (copies == 1)
  912. return node;
  913. if (copies * (long)(node.count) > MAXITEMS)
  914. throw new InvalidOperationException(Strings.CollectionTooLarge);
  915. // Build up the copies by powers of two.
  916. int n = 1;
  917. Node power = node, builder = null;
  918. while (copies > 0) {
  919. power.MarkShared();
  920. if ((copies & n) != 0) {
  921. // This power of two is used in the final result.
  922. copies -= n;
  923. if (builder == null)
  924. builder = power;
  925. else
  926. builder = builder.Append(power, false);
  927. }
  928. n *= 2;
  929. power = power.Append(power, false);
  930. }
  931. return builder;
  932. }
  933. /// <summary>
  934. /// Check the balance of the current tree and rebalance it if it is more than BALANCEFACTOR
  935. /// levels away from fully balanced. Note that rebalancing a tree may leave it two levels away from
  936. /// fully balanced.
  937. /// </summary>
  938. private void CheckBalance()
  939. {
  940. if (root != null &&
  941. (root.Depth > BALANCEFACTOR && !(root.Depth - BALANCEFACTOR <= MAXFIB && Count >= FIBONACCI[root.Depth - BALANCEFACTOR])))
  942. {
  943. Rebalance();
  944. }
  945. }
  946. /// <summary>
  947. /// Rebalance the current tree. Once rebalanced, the depth of the current tree is no more than
  948. /// two levels from fully balanced, where fully balanced is defined as having Fibonacci(N+2) or more items
  949. /// in a tree of depth N.
  950. /// </summary>
  951. /// <remarks>The rebalancing algorithm is from "Ropes: an Alternative to Strings", by
  952. /// Boehm, Atkinson, and Plass, in SOFTWARE--PRACTICE AND EXPERIENCE, VOL. 25(12), 1315–1330 (DECEMBER 1995).
  953. /// </remarks>
  954. internal void Rebalance()
  955. {
  956. Node[] rebalanceArray;
  957. int slots;
  958. // The basic rebalancing algorithm is add nodes to a rabalance array, where a node at index K in the
  959. // rebalance array has Fibonacci(K+1) to Fibonacci(K+2) items, and the entire list has the nodes
  960. // from largest to smallest concatenated.
  961. if (root == null)
  962. return;
  963. if (root.Depth <= 1 || (root.Depth-2 <= MAXFIB && Count >= FIBONACCI[root.Depth-2]))
  964. return; // already sufficiently balanced.
  965. // How many slots does the rebalance array need?
  966. for (slots = 0; slots <= MAXFIB; ++slots)
  967. if (root.Count < FIBONACCI[slots])
  968. break;
  969. rebalanceArray = new Node[slots];
  970. // Add all the nodes to the rebalance array.
  971. AddNodeToRebalanceArray(rebalanceArray, root, false);
  972. // Concatinate all the node in the rebalance array.
  973. Node result = null;
  974. for (int slot = 0; slot < slots; ++slot) {
  975. Node n = rebalanceArray[slot];
  976. if (n != null) {
  977. if (result == null)
  978. result = n;
  979. else
  980. result = result.PrependInPlace(n, !n.Shared);
  981. }
  982. }
  983. // And we're done. Check that it worked!
  984. root = result;
  985. Debug.Assert(root.Depth <= 1 || (root.Depth - 2 <= MAXFIB && Count >= FIBONACCI[root.Depth - 2]));
  986. }
  987. /// <summary>
  988. /// Part of the rebalancing algorithm. Adds a node to the rebalance array. If it is already balanced, add it directly, otherwise
  989. /// add its children.
  990. /// </summary>
  991. /// <param name="rebalanceArray">Rebalance array to insert into.</param>
  992. /// <param name="node">Node to add.</param>
  993. /// <param name="shared">If true, mark the node as shared before adding, because one
  994. /// of its parents was shared.</param>
  995. private void AddNodeToRebalanceArray(Node[] rebalanceArray, Node node, bool shared)
  996. {
  997. if (node.Shared)
  998. shared = true;
  999. if (node.IsBalanced()) {
  1000. if (shared)
  1001. node.MarkShared();
  1002. AddBalancedNodeToRebalanceArray(rebalanceArray, node);
  1003. }
  1004. else {
  1005. ConcatNode n = (ConcatNode)node; // leaf nodes are always balanced.
  1006. AddNodeToRebalanceArray(rebalanceArray, n.left, shared);
  1007. AddNodeToRebalanceArray(rebalanceArray, n.right, shared);
  1008. }
  1009. }
  1010. /// <summary>
  1011. /// Part of the rebalancing algorithm. Adds a balanced node to the rebalance array.
  1012. /// </summary>
  1013. /// <param name="rebalanceArray">Rebalance array to insert into.</param>
  1014. /// <param name="balancedNode">Node to add.</param>
  1015. private static void AddBalancedNodeToRebalanceArray(Node[] rebalanceArray, Node balancedNode)
  1016. {
  1017. int slot;
  1018. int count;
  1019. Node accum = null;
  1020. Debug.Assert(balancedNode.IsBalanced());
  1021. count = balancedNode.Count;
  1022. slot = 0;
  1023. while (count >= FIBONACCI[slot + 1]) {
  1024. Node n = rebalanceArray[slot];
  1025. if (n != null) {
  1026. rebalanceArray[slot] = null;
  1027. if (accum == null)
  1028. accum = n;
  1029. else
  1030. accum = accum.PrependInPlace(n, !n.Shared);
  1031. }
  1032. ++slot;
  1033. }
  1034. // slot is the location where balancedNode originally ended up, but possibly
  1035. // not the final resting place.
  1036. if (accum != null)
  1037. balancedNode = balancedNode.PrependInPlace(accum, !accum.Shared);
  1038. for (;;) {
  1039. Node n = rebalanceArray[slot];
  1040. if (n != null) {
  1041. rebalanceArray[slot] = null;
  1042. balancedNode = balancedNode.PrependInPlace(n, !n.Shared);
  1043. }
  1044. if (balancedNode.Count < FIBONACCI[slot + 1]) {
  1045. rebalanceArray[slot] = balancedNode;
  1046. break;
  1047. }
  1048. ++slot;
  1049. }
  1050. #if DEBUG
  1051. // The above operations should ensure that everything in the rebalance array is now almost balanced.
  1052. for (int i = 0; i < rebalanceArray.Length; ++i) {
  1053. if (rebalanceArray[i] != null)
  1054. Debug.Assert(rebalanceArray[i].IsAlmostBalanced());
  1055. }
  1056. #endif //DEBUG
  1057. }
  1058. /// <summary>
  1059. /// Convert the list to a new list by applying a delegate to each item in the collection. The resulting list
  1060. /// contains the result of applying <paramref name="converter"/> to each item in the list, in
  1061. /// order. The current list is unchanged.
  1062. /// </summary>
  1063. /// <typeparam name="TDest">The type each item is being converted to.</typeparam>
  1064. /// <param name="converter">A delegate to the method to call, passing each item in <type name="BigList&lt;T&gt;"/>.</param>
  1065. /// <returns>The resulting BigList from applying <paramref name="converter"/> to each item in this list.</returns>
  1066. /// <exception cref="ArgumentNullException"><paramref name="converter"/> is null.</exception>
  1067. public new BigList<TDest> ConvertAll<TDest>(Converter<T, TDest> converter)
  1068. {
  1069. return new BigList<TDest>(Algorithms.Convert(this, converter));
  1070. }
  1071. /// <summary>
  1072. /// Reverses the current list in place.
  1073. /// </summary>
  1074. public void Reverse()
  1075. {
  1076. Algorithms.ReverseInPlace(this);
  1077. }
  1078. /// <summary>
  1079. /// Reverses the items in the range of <paramref name="count"/> items starting from <paramref name="start"/>, in place.
  1080. /// </summary>
  1081. /// <param name="start">The starting index of the range to reverse.</param>
  1082. /// <param name="count">The number of items in range to reverse.</param>
  1083. public void Reverse(int start, int count)
  1084. {
  1085. Algorithms.ReverseInPlace(Range(start, count));
  1086. }
  1087. /// <summary>
  1088. /// Sorts the list in place.
  1089. /// </summary>
  1090. /// <remarks><para>The Quicksort algorithm is used to sort the items. In virtually all cases,
  1091. /// this takes time O(N log N), where N is the number of items in the list.</para>
  1092. /// <para>Values are compared by using the IComparable or IComparable&lt;T&gt;
  1093. /// interface implementation on the type T.</para></remarks>
  1094. /// <exception cref="InvalidOperationException">The type T does not implement either the IComparable or
  1095. /// IComparable&lt;T&gt; interfaces.</exception>
  1096. public void Sort()
  1097. {
  1098. Sort(Comparers.DefaultComparer<T>());
  1099. }
  1100. /// <summary>
  1101. /// Sorts the list in place. A supplied IComparer&lt;T&gt; is used
  1102. /// to compare the items in the list.
  1103. /// </summary>
  1104. /// <remarks>The Quicksort algorithms is used to sort the items. In virtually all cases,
  1105. /// this takes time O(N log N), where N is the number of items in the list.</remarks>
  1106. /// <param name="comparer">The comparer instance used to compare items in the collection. Only
  1107. /// the Compare method is used.</param>
  1108. public void Sort(IComparer<T> comparer)
  1109. {
  1110. Algorithms.SortInPlace(this, comparer);
  1111. }
  1112. /// <summary>
  1113. /// Sorts the list in place. A supplied Comparison&lt;T&gt; delegate is used
  1114. /// to compare the items in the list.
  1115. /// </summary>
  1116. /// <remarks>The Quicksort algorithms is used to sort the items. In virtually all cases,
  1117. /// this takes time O(N log N), where N is the number of items in the list.</remarks>
  1118. /// <param name="comparison">The comparison delegate used to compare items in the collection.</param>
  1119. public void Sort(Comparison<T> comparison)
  1120. {
  1121. Sort(Comparers.ComparerFromComparison(comparison));
  1122. }
  1123. /// <summary>
  1124. /// Searches a sorted list for an item via binary search. The list must be sorted
  1125. /// in the order defined by the default ordering of the item type; otherwise,
  1126. /// incorrect results will be returned.
  1127. /// </summary>
  1128. /// <param name="item">The item to search for.</param>
  1129. /// <returns>Returns the index of the first occurence of <paramref name="item"/> in the list. If the item does not occur
  1130. /// in the list, the bitwise complement of the first item larger than <paramref name="item"/> in the list is returned. If no item is
  1131. /// larger than <paramref name="item"/>, the bitwise complement of Count is returned.</returns>
  1132. /// <exception cref="InvalidOperationException">The type T does not implement either the IComparable or
  1133. /// IComparable&lt;T&gt; interfaces.</exception>
  1134. public int BinarySearch(T item)
  1135. {
  1136. return BinarySearch(item, Comparers.DefaultComparer<T>());
  1137. }
  1138. /// <summary>
  1139. /// Searches a sorted list for an item via binary search. The list must be sorted
  1140. /// by the ordering defined by the passed IComparer&lt;T&gt; interface; otherwise,
  1141. /// incorrect results will be returned.
  1142. /// </summary>
  1143. /// <param name="item">The item to search for.</param>
  1144. /// <param name="comparer">The IComparer&lt;T&gt; interface used to sort the list.</param>
  1145. /// <returns>Returns the index of the first occurence of <paramref name="item"/> in the list. If the item does not occur
  1146. /// in the list, the bitwise complement of the first item larger than <paramref name="item"/> in the list is returned. If no item is
  1147. /// larger than <paramref name="item"/>, the bitwise complement of Count is returned.</returns>
  1148. public int BinarySearch(T item, IComparer<T> comparer)
  1149. {
  1150. int count, index;
  1151. count = Algorithms.BinarySearch(this, item, comparer, out index);
  1152. if (count == 0)
  1153. return (~index);
  1154. else
  1155. return index;
  1156. }
  1157. /// <summary>
  1158. /// Searches a sorted list for an item via binary search. The list must be sorted
  1159. /// by the ordering defined by the passed Comparison&lt;T&gt; delegate; otherwise,
  1160. /// incorrect results will be returned.
  1161. /// </summary>
  1162. /// <param name="item">The item to search for.</param>
  1163. /// <param name="comparison">The comparison delegate used to sort the list.</param>
  1164. /// <returns>Returns the index of the first occurence of <paramref name="item"/> in the list. If the item does not occur
  1165. /// in the list, the bitwise complement of the first item larger than <paramref name="item"/> in the list is returned. If no item is
  1166. /// larger than <paramref name="item"/>, the bitwise complement of Count is returned.</returns>
  1167. public int BinarySearch(T item, Comparison<T> comparison)
  1168. {
  1169. return BinarySearch(item, Comparers.ComparerFromComparison(comparison));
  1170. }
  1171. #if DEBUG
  1172. /// <summary>
  1173. /// Attempts to validate the internal consistency of the tree.
  1174. /// </summary>
  1175. public void Validate()
  1176. {
  1177. if (root != null) {
  1178. root.Validate();
  1179. Debug.Assert(Count != 0);
  1180. }
  1181. else
  1182. Debug.Assert(Count == 0);
  1183. }
  1184. /// <summary>
  1185. /// Prints out the internal structure of the tree, for debugging purposes.
  1186. /// </summary>
  1187. public void Print()
  1188. {
  1189. Console.WriteLine("SERIES: Count={0}", Count);
  1190. if (Count > 0) {
  1191. Console.Write("ITEMS: ");
  1192. foreach (T item in this) {
  1193. Console.Write("{0} ", item);
  1194. }
  1195. Console.WriteLine();
  1196. Console.WriteLine("TREE:");
  1197. root.Print(" ", " ");
  1198. }
  1199. Console.WriteLine();
  1200. }
  1201. #endif //DEBUG
  1202. /// <summary>
  1203. /// The base class for the two kinds of nodes in the tree: Concat nodes
  1204. /// and Leaf nodes.
  1205. /// </summary>
  1206. [Serializable]
  1207. private abstract class Node
  1208. {
  1209. // Number of items in this node.
  1210. public int count;
  1211. // If true, indicates that this node is referenced by multiple
  1212. // concat nodes or multiple BigList. Neither this node nor
  1213. // nodes below it may be modifed ever again. Never becomes
  1214. // false after being set to true. It's volatile so that accesses
  1215. // from another thread work appropriately -- if shared is set
  1216. // to true, no other thread will attempt to change the node.
  1217. protected volatile bool shared;
  1218. /// <summary>
  1219. /// The number of items stored in the node (or below it).
  1220. /// </summary>
  1221. /// <value>The number of items in the node or below.</value>
  1222. public int Count
  1223. {
  1224. get { return count; }
  1225. }
  1226. /// <summary>
  1227. /// Is this node shared by more that one list (or within a single)
  1228. /// lists. If true, indicates that this node, and any nodes below it,
  1229. /// may never be modified. Never becomes false after being set to
  1230. /// true.
  1231. /// </summary>
  1232. /// <value></value>
  1233. public bool Shared
  1234. {
  1235. get { return shared; }
  1236. }
  1237. /// <summary>
  1238. /// Marks this node as shared by setting the shared variable.
  1239. /// </summary>
  1240. public void MarkShared()
  1241. {
  1242. shared = true;
  1243. }
  1244. /// <summary>
  1245. /// Gets the depth of this node. A leaf node has depth 0,
  1246. /// a concat node with two leaf children has depth 1, etc.
  1247. /// </summary>
  1248. /// <value>The depth of this node.</value>
  1249. public abstract int Depth { get;}
  1250. /// <summary>
  1251. /// Returns the items at the given index in this node.
  1252. /// </summary>
  1253. /// <param name="index">0-based index, relative to this node.</param>
  1254. /// <returns>Item at that index.</returns>
  1255. public abstract T GetAt(int index);
  1256. /// <summary>
  1257. /// Returns a node that has a sub-range of items from this node. The
  1258. /// sub-range may not be empty, but may extend outside the node.
  1259. /// In other words, first might be less than zero or last might be greater
  1260. /// than count. But, last can't be less than zero and first can't be
  1261. /// greater than count. Also, last must be greater than or equal to last.
  1262. /// </summary>
  1263. /// <param name="first">Inclusive first element, relative to this node.</param>
  1264. /// <param name="last">Inclusize last element, relative to this node.</param>
  1265. /// <returns>Node with the given sub-range.</returns>
  1266. public abstract Node Subrange(int first, int last);
  1267. // Any operation that could potentially modify a node exists
  1268. // in two forms -- the "in place" form that possibly modifies the
  1269. // node, and the non-"in place" that returns a new node with
  1270. // the modification. However, even "in-place" operations may return
  1271. // a new node, because a shared node can never be modified, even
  1272. // by an in-place operation.
  1273. /// <summary>
  1274. /// Changes the item at the given index. Never changes this node,
  1275. /// but always returns a new node with the given item changed.
  1276. /// </summary>
  1277. /// <param name="index">Index, relative to this node, to change.</param>
  1278. /// <param name="item">New item to place at the given index.</param>
  1279. /// <returns>A new node with the given item changed.</returns>
  1280. public abstract Node SetAt(int index, T item);
  1281. /// <summary>
  1282. /// Changes the item at the given index. May change this node,
  1283. /// or return a new node with the given item changed.
  1284. /// </summary>
  1285. /// <param name="index">Index, relative to this node, to change.</param>
  1286. /// <param name="item">New item to place at the given index.</param>
  1287. /// <returns>A node with the give item changed. If it can be done in place
  1288. /// then "this" is returned.</returns>
  1289. public abstract Node SetAtInPlace(int index, T item);
  1290. /// <summary>
  1291. /// Append a node after this node. Never changes this node, but returns
  1292. /// a new node with the given appending done.
  1293. /// </summary>
  1294. /// <param name="node">Node to append.</param>
  1295. /// <param name="nodeIsUnused">If true, the given node is not used
  1296. /// in any current list, so it may be change, overwritten, or destroyed
  1297. /// if convenient. If false, the given node is in use. It should be marked
  1298. /// as shared if is is used within the return value.</param>
  1299. /// <returns>A new node with the give node appended to this node.</returns>
  1300. public abstract Node Append(Node node, bool nodeIsUnused);
  1301. /// <summary>
  1302. /// Append a node after this node. May change this node, or return
  1303. /// a new node.
  1304. /// </summary>
  1305. /// <param name="node">Node to append.</param>
  1306. /// <param name="nodeIsUnused">If true, the given node is not used
  1307. /// in any current list, so it may be change, overwritten, or destroyed
  1308. /// if convenient. If false, the given node is in use. It should be marked
  1309. /// as shared if is is used within the return value.</param>
  1310. /// <returns>A node with the give node appended to this node. May be a new
  1311. /// node or the current node.</returns>
  1312. public abstract Node AppendInPlace(Node node, bool nodeIsUnused);
  1313. /// <summary>
  1314. /// Append a item after this node. May change this node, or return
  1315. /// a new node. Equivalent to AppendInPlace(new LeafNode(item), true), but
  1316. /// may be more efficient because a new LeafNode might not be allocated.
  1317. /// </summary>
  1318. /// <param name="item">Item to append.</param>
  1319. /// <returns>A node with the given item appended to this node. May be a new
  1320. /// node or the current node.</returns>
  1321. public abstract Node AppendInPlace(T item);
  1322. /// <summary>
  1323. /// Remove a range of items from this node. Never changes this node, but returns
  1324. /// a new node with the removing done. The
  1325. /// sub-range may not be empty, but may extend outside the node.
  1326. /// In other words, first might be less than zero or last might be greater
  1327. /// than count. But, last can't be less than zero and first can't be
  1328. /// greater than count. Also, last must be greater than or equal to last.
  1329. /// </summary>
  1330. /// <param name="first">Inclusive index of first item in sub-range, relative
  1331. /// to this node.</param>
  1332. /// <param name="last">Inclusize index of last item in sub-range, relative
  1333. /// to this node.</param>
  1334. /// <returns>A new node with the sub-range removed.</returns>
  1335. public abstract Node RemoveRange(int first, int last);
  1336. /// <summary>
  1337. /// Remove a range of items from this node. May change this node, or returns
  1338. /// a new node with the given appending done. The
  1339. /// sub-range may not be empty, but may extend outside the node.
  1340. /// In other words, first might be less than zero or last might be greater
  1341. /// than count. But, last can't be less than zero and first can't be
  1342. /// greater than count. Also, last must be greater than or equal to last.
  1343. /// </summary>
  1344. /// <param name="first">Inclusive index of first item in sub-range, relative
  1345. /// to this node.</param>
  1346. /// <param name="last">Inclusize index of last item in sub-range, relative
  1347. /// to this node.</param>
  1348. /// <returns>A node with the sub-range removed. If done in-place, returns
  1349. /// "this".</returns>
  1350. public abstract Node RemoveRangeInPlace(int first, int last);
  1351. /// <summary>
  1352. /// Inserts a node inside this node. Never changes this node, but returns
  1353. /// a new node with the given appending done.
  1354. /// </summary>
  1355. /// <param name="index">Index, relative to this node, to insert at. Must
  1356. /// be in bounds.</param>
  1357. /// <param name="node">Node to insert.</param>
  1358. /// <param name="nodeIsUnused">If true, the given node is not used
  1359. /// in any current list, so it may be change, overwritten, or destroyed
  1360. /// if convenient. If false, the given node is in use. It should be marked
  1361. /// as shared if is is used within the return value.</param>
  1362. /// <returns>A new node with the give node inserted.</returns>
  1363. public abstract Node Insert(int index, Node node, bool nodeIsUnused);
  1364. /// <summary>
  1365. /// Inserts an item inside this node. May change this node, or return
  1366. /// a new node with the given appending done. Equivalent to
  1367. /// InsertInPlace(new LeafNode(item), true), but may be more efficient.
  1368. /// </summary>
  1369. /// <param name="index">Index, relative to this node, to insert at. Must
  1370. /// be in bounds.</param>
  1371. /// <param name="item">Item to insert.</param>
  1372. /// <returns>A node with the give item inserted. If done in-place, returns
  1373. /// "this".</returns>
  1374. public abstract Node InsertInPlace(int index, T item);
  1375. /// <summary>
  1376. /// Inserts a node inside this node. May change this node, or return
  1377. /// a new node with the given appending done.
  1378. /// </summary>
  1379. /// <param name="index">Index, relative to this node, to insert at. Must
  1380. /// be in bounds.</param>
  1381. /// <param name="node">Node to insert.</param>
  1382. /// <param name="nodeIsUnused">If true, the given node is not used
  1383. /// in any current list, so it may be change, overwritten, or destroyed
  1384. /// if convenient. If false, the given node is in use. It should be marked
  1385. /// as shared if is is used within the return value.</param>
  1386. /// <returns>A node with the given item inserted. If done in-place, returns
  1387. /// "this".</returns>
  1388. public abstract Node InsertInPlace(int index, Node node, bool nodeIsUnused);
  1389. #if DEBUG
  1390. /// <summary>
  1391. /// Validates the node for consistency, as much as possible. Also validates
  1392. /// child nodes, if any.
  1393. /// </summary>
  1394. public abstract void Validate();
  1395. /// <summary>
  1396. /// Print out the contents of this node.
  1397. /// </summary>
  1398. /// <param name="prefixNode">Prefix to use in front of this node.</param>
  1399. /// <param name="prefixChildren">Prefixed to use in front of children of this node.</param>
  1400. public abstract void Print(string prefixNode, string prefixChildren);
  1401. #endif //DEBUG
  1402. /// <summary>
  1403. /// Prefpend a node before this node. Never changes this node, but returns
  1404. /// a new node with the given prepending done.
  1405. /// </summary>
  1406. /// <param name="node">Node to prepend.</param>
  1407. /// <param name="nodeIsUnused">If true, the given node is not used
  1408. /// in any current list, so it may be change, overwritten, or destroyed
  1409. /// if convenient. If false, the given node is in use. It should be marked
  1410. /// as shared if is is used within the return value.</param>
  1411. /// <returns>A new node with the give node prepended to this node.</returns>
  1412. public Node Prepend(Node node, bool nodeIsUnused)
  1413. {
  1414. if (nodeIsUnused)
  1415. return node.AppendInPlace(this, false);
  1416. else
  1417. return node.Append(this, false);
  1418. }
  1419. /// <summary>
  1420. /// Prepend a node before this node. May change this node, or return
  1421. /// a new node.
  1422. /// </summary>
  1423. /// <param name="node">Node to prepend.</param>
  1424. /// <param name="nodeIsUnused">If true, the given node is not used
  1425. /// in any current list, so it may be change, overwritten, or destroyed
  1426. /// if convenient. If false, the given node is in use. It should be marked
  1427. /// as shared if is is used within the return value.</param>
  1428. /// <returns>A node with the give node prepended to this node. May be a new
  1429. /// node or the current node.</returns>
  1430. public Node PrependInPlace(Node node, bool nodeIsUnused)
  1431. {
  1432. if (nodeIsUnused)
  1433. return node.AppendInPlace(this, !this.shared);
  1434. else
  1435. return node.Append(this, !this.shared);
  1436. }
  1437. /// <summary>
  1438. /// Prepend a item before this node. May change this node, or return
  1439. /// a new node. Equivalent to PrependInPlace(new LeafNode(item), true), but
  1440. /// may be more efficient because a new LeafNode might not be allocated.
  1441. /// </summary>
  1442. /// <param name="item">Item to prepend.</param>
  1443. /// <returns>A node with the given item prepended to this node. May be a new
  1444. /// node or the current node.</returns>
  1445. public abstract Node PrependInPlace(T item);
  1446. /// <summary>
  1447. /// Determine if this node is balanced. A node is balanced if the number
  1448. /// of items is greater than
  1449. /// Fibonacci(Depth+2). Balanced nodes are never rebalanced unless
  1450. /// they go out of balance again.
  1451. /// </summary>
  1452. /// <returns>True if the node is balanced by this definition.</returns>
  1453. public bool IsBalanced()
  1454. {
  1455. return (Depth <= MAXFIB && Count >= FIBONACCI[Depth]);
  1456. }
  1457. /// <summary>
  1458. /// Determine if this node is almost balanced. A node is almost balanced if t
  1459. /// its depth is at most one greater than a fully balanced node with the same count.
  1460. /// </summary>
  1461. /// <returns>True if the node is almost balanced by this definition.</returns>
  1462. public bool IsAlmostBalanced()
  1463. {
  1464. return (Depth == 0 || (Depth - 1 <= MAXFIB && Count >= FIBONACCI[Depth - 1]));
  1465. }
  1466. }
  1467. /// <summary>
  1468. /// The LeafNode class is the type of node that lives at the leaf of a tree and holds
  1469. /// the actual items stored in the list. Each leaf holds at least 1, and at most MAXLEAF
  1470. /// items in the items array. The number of items stored is found in "count", which may
  1471. /// be less than "items.Length".
  1472. /// </summary>
  1473. [Serializable]
  1474. private sealed class LeafNode : Node
  1475. {
  1476. /// <summary>
  1477. /// Array that stores the items in the nodes. Always has a least "count" elements,
  1478. /// but may have more as padding.
  1479. /// </summary>
  1480. public T[] items;
  1481. /// <summary>
  1482. /// Creates a LeafNode that holds a single item.
  1483. /// </summary>
  1484. /// <param name="item">Item to place into the leaf node.</param>
  1485. public LeafNode(T item)
  1486. {
  1487. // CONSIDER: is MAXLEAF always the right thing to do? It seems to work well in most cases.
  1488. count = 1;
  1489. items = new T[MAXLEAF];
  1490. items[0] = item;
  1491. }
  1492. /// <summary>
  1493. /// Creates a new leaf node with the indicates count of item and the
  1494. /// </summary>
  1495. /// <param name="count">Number of items. Can't be zero.</param>
  1496. /// <param name="newItems">The array of items. The LeafNode takes
  1497. /// possession of this array.</param>
  1498. public LeafNode(int count, T[] newItems)
  1499. {
  1500. Debug.Assert(count <= newItems.Length && count > 0);
  1501. Debug.Assert(newItems.Length <= MAXLEAF);
  1502. this.count = count;
  1503. items = newItems;
  1504. }
  1505. public override int Depth
  1506. {
  1507. get { return 0; }
  1508. }
  1509. /// <summary>
  1510. /// Returns the items at the given index in this node.
  1511. /// </summary>
  1512. /// <param name="index">0-based index, relative to this node.</param>
  1513. /// <returns>Item at that index.</returns>
  1514. public override T GetAt(int index)
  1515. {
  1516. return items[index];
  1517. }
  1518. /// <summary>
  1519. /// Changes the item at the given index. May change this node,
  1520. /// or return a new node with the given item changed.
  1521. /// </summary>
  1522. /// <param name="index">Index, relative to this node, to change.</param>
  1523. /// <param name="item">New item to place at the given index.</param>
  1524. /// <returns>A node with the give item changed. If it can be done in place
  1525. /// then "this" is returned.</returns>
  1526. public override Node SetAtInPlace(int index, T item)
  1527. {
  1528. if (shared)
  1529. return SetAt(index, item); // Can't update a shared node in place.
  1530. items[index] = item;
  1531. return this;
  1532. }
  1533. /// <summary>
  1534. /// Changes the item at the given index. Never changes this node,
  1535. /// but always returns a new node with the given item changed.
  1536. /// </summary>
  1537. /// <param name="index">Index, relative to this node, to change.</param>
  1538. /// <param name="item">New item to place at the given index.</param>
  1539. /// <returns>A new node with the given item changed.</returns>
  1540. public override Node SetAt(int index, T item)
  1541. {
  1542. T[] newItems = (T[])items.Clone();
  1543. newItems[index] = item;
  1544. return new LeafNode(count, newItems);
  1545. }
  1546. /// <summary>
  1547. /// If other is a leaf node, and the resulting size would be less than MAXLEAF, merge
  1548. /// the other leaf node into this one (after this one) and return true.
  1549. /// </summary>
  1550. /// <param name="other">Other node to possible merge.</param>
  1551. /// <returns>If <paramref name="other"/> could be merged into this node, returns
  1552. /// true. Otherwise returns false and the current node is unchanged.</returns>
  1553. private bool MergeLeafInPlace(Node other)
  1554. {
  1555. Debug.Assert(!shared);
  1556. LeafNode otherLeaf = (other as LeafNode);
  1557. int newCount;
  1558. if (otherLeaf != null && (newCount = otherLeaf.Count + this.count) <= MAXLEAF) {
  1559. // Combine the two leaf nodes into one.
  1560. if (newCount > items.Length) {
  1561. T[] newItems = new T[MAXLEAF];
  1562. Array.Copy(items, 0, newItems, 0, count);
  1563. items = newItems;
  1564. }
  1565. Array.Copy(otherLeaf.items, 0, items, count, otherLeaf.count);
  1566. count = newCount;
  1567. return true;
  1568. }
  1569. return false;
  1570. }
  1571. /// <summary>
  1572. /// If other is a leaf node, and the resulting size would be less than MAXLEAF, merge
  1573. /// the other leaf node with this one (after this one) and return a new node with
  1574. /// the merged items. Does not modify this.
  1575. /// If no merging, return null.
  1576. /// </summary>
  1577. /// <param name="other">Other node to possible merge.</param>
  1578. /// <returns>If the nodes could be merged, returns the new node. Otherwise
  1579. /// returns null.</returns>
  1580. private Node MergeLeaf(Node other)
  1581. {
  1582. LeafNode otherLeaf = (other as LeafNode);
  1583. int newCount;
  1584. if (otherLeaf != null && (newCount = otherLeaf.Count + this.count) <= MAXLEAF) {
  1585. // Combine the two leaf nodes into one.
  1586. T[] newItems = new T[MAXLEAF];
  1587. Array.Copy(items, 0, newItems, 0, count);
  1588. Array.Copy(otherLeaf.items, 0, newItems, count, otherLeaf.count);
  1589. return new LeafNode(newCount, newItems);
  1590. }
  1591. return null;
  1592. }
  1593. /// <summary>
  1594. /// Prepend a item before this node. May change this node, or return
  1595. /// a new node. Equivalent to PrependInPlace(new LeafNode(item), true), but
  1596. /// may be more efficient because a new LeafNode might not be allocated.
  1597. /// </summary>
  1598. /// <param name="item">Item to prepend.</param>
  1599. /// <returns>A node with the given item prepended to this node. May be a new
  1600. /// node or the current node.</returns>
  1601. public override Node PrependInPlace(T item)
  1602. {
  1603. if (shared)
  1604. return Prepend(new LeafNode(item), true); // Can't update a shared node in place.
  1605. // Add into the current leaf, if possible.
  1606. if (count < MAXLEAF) {
  1607. if (count == items.Length) {
  1608. T[] newItems = new T[MAXLEAF];
  1609. Array.Copy(items, 0, newItems, 1, count);
  1610. items = newItems;
  1611. }
  1612. else {
  1613. Array.Copy(items, 0, items, 1, count);
  1614. }
  1615. items[0] = item;
  1616. count += 1;
  1617. return this;
  1618. }
  1619. else {
  1620. return new ConcatNode(new LeafNode(item), this);
  1621. }
  1622. }
  1623. /// <summary>
  1624. /// Append a item after this node. May change this node, or return
  1625. /// a new node. Equivalent to AppendInPlace(new LeafNode(item), true), but
  1626. /// may be more efficient because a new LeafNode might not be allocated.
  1627. /// </summary>
  1628. /// <param name="item">Item to append.</param>
  1629. /// <returns>A node with the given item appended to this node. May be a new
  1630. /// node or the current node.</returns>
  1631. public override Node AppendInPlace(T item)
  1632. {
  1633. if (shared)
  1634. return Append(new LeafNode(item), true); // Can't update a shared node in place.
  1635. // Add into the current leaf, if possible.
  1636. if (count < MAXLEAF) {
  1637. if (count == items.Length) {
  1638. T[] newItems = new T[MAXLEAF];
  1639. Array.Copy(items, 0, newItems, 0, count);
  1640. items = newItems;
  1641. }
  1642. items[count] = item;
  1643. count += 1;
  1644. return this;
  1645. }
  1646. else {
  1647. return new ConcatNode(this, new LeafNode(item));
  1648. }
  1649. }
  1650. /// <summary>
  1651. /// Append a node after this node. May change this node, or return
  1652. /// a new node.
  1653. /// </summary>
  1654. /// <param name="node">Node to append.</param>
  1655. /// <param name="nodeIsUnused">If true, the given node is not used
  1656. /// in any current list, so it may be change, overwritten, or destroyed
  1657. /// if convenient. If false, the given node is in use. It should be marked
  1658. /// as shared if is is used within the return value.</param>
  1659. /// <returns>A node with the give node appended to this node. May be a new
  1660. /// node or the current node.</returns>
  1661. public override Node AppendInPlace(Node node, bool nodeIsUnused)
  1662. {
  1663. if (shared)
  1664. return Append(node, nodeIsUnused); // Can't update a shared node in place.
  1665. // If we're appending a leaf, try to merge them if possible.
  1666. if (MergeLeafInPlace(node)) {
  1667. return this;
  1668. }
  1669. // If we're appending a tree with a left leaf node, try to merge them if possible.
  1670. ConcatNode otherConcat = (node as ConcatNode);
  1671. if (otherConcat != null && MergeLeafInPlace(otherConcat.left)) {
  1672. if (! nodeIsUnused)
  1673. otherConcat.right.MarkShared();
  1674. return new ConcatNode(this, otherConcat.right);
  1675. }
  1676. // Otherwise, create a Concat node.
  1677. if (! nodeIsUnused)
  1678. node.MarkShared();
  1679. return new ConcatNode(this, node);
  1680. }
  1681. public override Node Append(Node node, bool nodeIsUnused)
  1682. {
  1683. Node result;
  1684. // If we're appending a leaf, try to merge them if possible.
  1685. if ((result = MergeLeaf(node)) != null)
  1686. return result;
  1687. // If we're appending a concat with a left leaf, try to merge them if possible.
  1688. ConcatNode otherConcat = (node as ConcatNode);
  1689. if (otherConcat != null && (result = MergeLeaf(otherConcat.left)) != null) {
  1690. if (! nodeIsUnused)
  1691. otherConcat.right.MarkShared();
  1692. return new ConcatNode(result, otherConcat.right);
  1693. }
  1694. // Otherwise, create a Concat node.
  1695. if (!nodeIsUnused)
  1696. node.MarkShared();
  1697. MarkShared();
  1698. return new ConcatNode(this, node);
  1699. }
  1700. /// <summary>
  1701. /// Inserts an item inside this node. May change this node, or return
  1702. /// a new node with the given appending done. Equivalent to
  1703. /// InsertInPlace(new LeafNode(item), true), but may be more efficient.
  1704. /// </summary>
  1705. /// <param name="index">Index, relative to this node, to insert at. Must
  1706. /// be in bounds.</param>
  1707. /// <param name="item">Item to insert.</param>
  1708. /// <returns>A node with the give item inserted. If done in-place, returns
  1709. /// "this".</returns>
  1710. public override Node InsertInPlace(int index, T item)
  1711. {
  1712. if (shared)
  1713. return Insert(index, new LeafNode(item), true); // Can't update a shared node in place.
  1714. // Insert into the current leaf, if possible.
  1715. if (count < MAXLEAF) {
  1716. if (count == items.Length) {
  1717. T[] newItems = new T[MAXLEAF];
  1718. if (index > 0)
  1719. Array.Copy(items, 0, newItems, 0, index);
  1720. if (count > index)
  1721. Array.Copy(items, index, newItems, index + 1, count - index);
  1722. items = newItems;
  1723. }
  1724. else {
  1725. if (count > index)
  1726. Array.Copy(items, index, items, index + 1, count - index);
  1727. }
  1728. items[index] = item;
  1729. count += 1;
  1730. return this;
  1731. }
  1732. else {
  1733. if (index == count) {
  1734. // Inserting at count is just an appending operation.
  1735. return new ConcatNode(this, new LeafNode(item));
  1736. }
  1737. else if (index == 0) {
  1738. // Inserting at 0 is just a prepending operation.
  1739. return new ConcatNode(new LeafNode(item), this);
  1740. }
  1741. else {
  1742. // Split into two nodes, and put the new item at the end of the first.
  1743. T[] leftItems = new T[MAXLEAF];
  1744. Array.Copy(items, 0, leftItems, 0, index);
  1745. leftItems[index] = item;
  1746. Node leftNode = new LeafNode(index + 1, leftItems);
  1747. T[] rightItems = new T[count - index];
  1748. Array.Copy(items, index, rightItems, 0, count - index);
  1749. Node rightNode = new LeafNode(count - index, rightItems);
  1750. return new ConcatNode(leftNode, rightNode);
  1751. }
  1752. }
  1753. }
  1754. /// <summary>
  1755. /// Inserts a node inside this node. May change this node, or return
  1756. /// a new node with the given appending done.
  1757. /// </summary>
  1758. /// <param name="index">Index, relative to this node, to insert at. Must
  1759. /// be in bounds.</param>
  1760. /// <param name="node">Node to insert.</param>
  1761. /// <param name="nodeIsUnused">If true, the given node is not used
  1762. /// in any current list, so it may be change, overwritten, or destroyed
  1763. /// if convenient. If false, the given node is in use. It should be marked
  1764. /// as shared if is is used within the return value.</param>
  1765. /// <returns>A node with the given item inserted. If done in-place, returns
  1766. /// "this".</returns>
  1767. public override Node InsertInPlace(int index, Node node, bool nodeIsUnused)
  1768. {
  1769. if (shared)
  1770. return Insert(index, node, nodeIsUnused); // Can't update a shared node in place.
  1771. LeafNode otherLeaf = (node as LeafNode);
  1772. int newCount;
  1773. if (otherLeaf != null && (newCount = otherLeaf.Count + this.count) <= MAXLEAF) {
  1774. // Combine the two leaf nodes into one.
  1775. if (newCount > items.Length) {
  1776. T[] newItems = new T[MAXLEAF];
  1777. Array.Copy(items, 0, newItems, 0, index);
  1778. Array.Copy(otherLeaf.items, 0, newItems, index, otherLeaf.Count);
  1779. Array.Copy(items, index, newItems, index + otherLeaf.Count, count - index);
  1780. items = newItems;
  1781. }
  1782. else {
  1783. Array.Copy(items, index, items, index + otherLeaf.Count, count - index);
  1784. Array.Copy(otherLeaf.items, 0, items, index, otherLeaf.count);
  1785. }
  1786. count = newCount;
  1787. return this;
  1788. }
  1789. else if (index == 0) {
  1790. // Inserting at 0 is a prepend.
  1791. return PrependInPlace(node, nodeIsUnused);
  1792. }
  1793. else if (index == count) {
  1794. // Inserting at count is an append.
  1795. return AppendInPlace(node, nodeIsUnused);
  1796. }
  1797. else {
  1798. // Split existing node into two nodes at the insertion point, then concat all three nodes together.
  1799. T[] leftItems = new T[index];
  1800. Array.Copy(items, 0, leftItems, 0, index);
  1801. Node leftNode = new LeafNode(index, leftItems);
  1802. T[] rightItems = new T[count - index];
  1803. Array.Copy(items, index, rightItems, 0, count - index);
  1804. Node rightNode = new LeafNode(count - index, rightItems);
  1805. leftNode = leftNode.AppendInPlace(node, nodeIsUnused);
  1806. leftNode = leftNode.AppendInPlace(rightNode, true);
  1807. return leftNode;
  1808. }
  1809. }
  1810. /// <summary>
  1811. /// Inserts a node inside this node. Never changes this node, but returns
  1812. /// a new node with the given appending done.
  1813. /// </summary>
  1814. /// <param name="index">Index, relative to this node, to insert at. Must
  1815. /// be in bounds.</param>
  1816. /// <param name="node">Node to insert.</param>
  1817. /// <param name="nodeIsUnused">If true, the given node is not used
  1818. /// in any current list, so it may be change, overwritten, or destroyed
  1819. /// if convenient. If false, the given node is in use. It should be marked
  1820. /// as shared if is is used within the return value.</param>
  1821. /// <returns>A new node with the give node inserted.</returns>
  1822. public override Node Insert(int index, Node node, bool nodeIsUnused)
  1823. {
  1824. LeafNode otherLeaf = (node as LeafNode);
  1825. int newCount;
  1826. if (otherLeaf != null && (newCount = otherLeaf.Count + this.count) <= MAXLEAF) {
  1827. // Combine the two leaf nodes into one.
  1828. T[] newItems = new T[MAXLEAF];
  1829. Array.Copy(items, 0, newItems, 0, index);
  1830. Array.Copy(otherLeaf.items, 0, newItems, index, otherLeaf.Count);
  1831. Array.Copy(items, index, newItems, index + otherLeaf.Count, count - index);
  1832. return new LeafNode(newCount, newItems);
  1833. }
  1834. else if (index == 0) {
  1835. // Inserting at 0 is a prepend.
  1836. return Prepend(node, nodeIsUnused);
  1837. }
  1838. else if (index == count) {
  1839. // Inserting at count is an append.
  1840. return Append(node, nodeIsUnused);
  1841. }
  1842. else {
  1843. // Split existing node into two nodes at the insertion point, then concat all three nodes together.
  1844. T[] leftItems = new T[index];
  1845. Array.Copy(items, 0, leftItems, 0, index);
  1846. Node leftNode = new LeafNode(index, leftItems);
  1847. T[] rightItems = new T[count - index];
  1848. Array.Copy(items, index, rightItems, 0, count - index);
  1849. Node rightNode = new LeafNode(count - index, rightItems);
  1850. leftNode = leftNode.AppendInPlace(node, nodeIsUnused);
  1851. leftNode = leftNode.AppendInPlace(rightNode, true);
  1852. return leftNode;
  1853. }
  1854. }
  1855. /// <summary>
  1856. /// Remove a range of items from this node. May change this node, or returns
  1857. /// a new node with the given appending done. The
  1858. /// sub-range may not be empty, but may extend outside the node.
  1859. /// In other words, first might be less than zero or last might be greater
  1860. /// than count. But, last can't be less than zero and first can't be
  1861. /// greater than count. Also, last must be greater than or equal to last.
  1862. /// </summary>
  1863. /// <param name="first">Inclusive index of first item in sub-range, relative
  1864. /// to this node.</param>
  1865. /// <param name="last">Inclusize index of last item in sub-range, relative
  1866. /// to this node.</param>
  1867. /// <returns>A node with the sub-range removed. If done in-place, returns
  1868. /// "this".</returns>
  1869. public override Node RemoveRangeInPlace(int first, int last)
  1870. {
  1871. if (shared)
  1872. return RemoveRange(first, last);
  1873. Debug.Assert(first <= last);
  1874. Debug.Assert(last >= 0);
  1875. if (first <= 0 && last >= count - 1) {
  1876. return null; // removing entire node.
  1877. }
  1878. if (first < 0)
  1879. first = 0;
  1880. if (last >= count)
  1881. last = count - 1;
  1882. int newCount = first + (count - last - 1); // number of items remaining.
  1883. if (count > last + 1)
  1884. Array.Copy(items, last + 1, items, first, count - last - 1);
  1885. for (int i = newCount; i < count; ++i)
  1886. items[i] = default(T);
  1887. count = newCount;
  1888. return this;
  1889. }
  1890. /// <summary>
  1891. /// Remove a range of items from this node. Never changes this node, but returns
  1892. /// a new node with the removing done. The
  1893. /// sub-range may not be empty, but may extend outside the node.
  1894. /// In other words, first might be less than zero or last might be greater
  1895. /// than count. But, last can't be less than zero and first can't be
  1896. /// greater than count. Also, last must be greater than or equal to last.
  1897. /// </summary>
  1898. /// <param name="first">Inclusive index of first item in sub-range, relative
  1899. /// to this node.</param>
  1900. /// <param name="last">Inclusize index of last item in sub-range, relative
  1901. /// to this node.</param>
  1902. /// <returns>A new node with the sub-range removed.</returns>
  1903. public override Node RemoveRange(int first, int last)
  1904. {
  1905. Debug.Assert(first <= last);
  1906. Debug.Assert(last >= 0);
  1907. if (first <= 0 && last >= count - 1) {
  1908. return null; // removing entire node.
  1909. }
  1910. if (first < 0)
  1911. first = 0;
  1912. if (last >= count)
  1913. last = count - 1;
  1914. int newCount = first + (count - last - 1); // number of items remaining.
  1915. T[] newItems = new T[newCount];
  1916. if (first > 0)
  1917. Array.Copy(items, 0, newItems, 0, first);
  1918. if (count > last + 1)
  1919. Array.Copy(items, last + 1, newItems, first, count - last - 1);
  1920. return new LeafNode(newCount, newItems);
  1921. }
  1922. /// <summary>
  1923. /// Returns a node that has a sub-range of items from this node. The
  1924. /// sub-range may not be empty, but may extend outside the node.
  1925. /// In other words, first might be less than zero or last might be greater
  1926. /// than count. But, last can't be less than zero and first can't be
  1927. /// greater than count. Also, last must be greater than or equal to last.
  1928. /// </summary>
  1929. /// <param name="first">Inclusive first element, relative to this node.</param>
  1930. /// <param name="last">Inclusize last element, relative to this node.</param>
  1931. /// <returns>Node with the given sub-range.</returns>
  1932. public override Node Subrange(int first, int last)
  1933. {
  1934. Debug.Assert(first <= last);
  1935. Debug.Assert(last >= 0);
  1936. if (first <= 0 && last >= count - 1) {
  1937. MarkShared();
  1938. return this;
  1939. }
  1940. else {
  1941. if (first < 0)
  1942. first = 0;
  1943. if (last >= count)
  1944. last = count - 1;
  1945. int n = last - first + 1;
  1946. T[] newItems = new T[n];
  1947. Array.Copy(items, first, newItems, 0, n);
  1948. return new LeafNode(n, newItems);
  1949. }
  1950. }
  1951. #if DEBUG
  1952. /// <summary>
  1953. /// Validates the node for consistency, as much as possible. Also validates
  1954. /// child nodes, if any.
  1955. /// </summary>
  1956. public override void Validate()
  1957. {
  1958. // Check count and length of buffer.
  1959. Debug.Assert(count > 0);
  1960. Debug.Assert(items != null);
  1961. Debug.Assert(items.Length > 0);
  1962. Debug.Assert(count <= MAXLEAF);
  1963. Debug.Assert(items.Length <= MAXLEAF);
  1964. Debug.Assert(count <= items.Length);
  1965. }
  1966. /// <summary>
  1967. /// Print out the contents of this node.
  1968. /// </summary>
  1969. /// <param name="prefixNode">Prefix to use in front of this node.</param>
  1970. /// <param name="prefixChildren">Prefixed to use in front of children of this node.</param>
  1971. public override void Print(string prefixNode, string prefixChildren)
  1972. {
  1973. Console.Write("{0}LEAF {1} count={2}/{3} ", prefixNode, shared ? "S" : " ", count, items.Length);
  1974. for (int i = 0; i < count; ++i)
  1975. Console.Write("{0} ", items[i]);
  1976. Console.WriteLine();
  1977. }
  1978. #endif //DEBUG
  1979. }
  1980. /// <summary>
  1981. /// A ConcatNode is an interior (non-leaf) node that represents the concatination of
  1982. /// the left and right child nodes. Both children must always be non-null.
  1983. /// </summary>
  1984. [Serializable]
  1985. private sealed class ConcatNode : Node
  1986. {
  1987. /// <summary>
  1988. /// The left and right child nodes. They are never null.
  1989. /// </summary>
  1990. public Node left, right;
  1991. /// <summary>
  1992. /// The depth of this node -- the maximum length path to
  1993. /// a leaf. If this node has two children that are leaves, the
  1994. /// depth in 1.
  1995. /// </summary>
  1996. private short depth;
  1997. /// <summary>
  1998. /// The depth of this node -- the maximum length path to
  1999. /// a leaf. If this node has two children that are leaves, the
  2000. /// depth in 1.
  2001. /// </summary>
  2002. /// <value>The depth of this node.</value>
  2003. public override int Depth
  2004. {
  2005. get { return depth; }
  2006. }
  2007. /// <summary>
  2008. /// Create a new ConcatNode with the given children.
  2009. /// </summary>
  2010. /// <param name="left">The left child. May not be null.</param>
  2011. /// <param name="right">The right child. May not be null.</param>
  2012. public ConcatNode(Node left, Node right)
  2013. {
  2014. Debug.Assert(left != null && right != null);
  2015. this.left = left;
  2016. this.right = right;
  2017. this.count = left.Count + right.Count;
  2018. if (left.Depth > right.Depth)
  2019. this.depth = (short)(left.Depth + 1);
  2020. else
  2021. this.depth = (short)(right.Depth + 1);
  2022. }
  2023. /// <summary>
  2024. /// Create a new node with the given children. Mark unchanged
  2025. /// children as shared. There are four
  2026. /// possible cases:
  2027. /// 1. If one of the new children is null, the other new child is returned.
  2028. /// 2. If neither child has changed, then this is marked as shared as returned.
  2029. /// 3. If one child has changed, the other child is marked shared an a new node is returned.
  2030. /// 4. If both children have changed, a new node is returned.
  2031. /// </summary>
  2032. /// <param name="newLeft">New left child.</param>
  2033. /// <param name="newRight">New right child.</param>
  2034. /// <returns>New node with the given children. Returns null if and only if both
  2035. /// new children are null.</returns>
  2036. private Node NewNode(Node newLeft, Node newRight)
  2037. {
  2038. if (left == newLeft) {
  2039. if (right == newRight) {
  2040. MarkShared();
  2041. return this; // Nothing changed. In this case we can return the same node.
  2042. }
  2043. else
  2044. left.MarkShared();
  2045. }
  2046. else {
  2047. if (right == newRight)
  2048. right.MarkShared();
  2049. }
  2050. if (newLeft == null)
  2051. return newRight;
  2052. else if (newRight == null)
  2053. return newLeft;
  2054. else
  2055. return new ConcatNode(newLeft, newRight);
  2056. }
  2057. /// <summary>
  2058. /// Updates a node with the given new children. If one of the new children is
  2059. /// null, the other is returned. If both are null, null is returned.
  2060. /// </summary>
  2061. /// <param name="newLeft">New left child.</param>
  2062. /// <param name="newRight">New right child.</param>
  2063. /// <returns>Node with the given children. Usually, but not always, this. Returns
  2064. /// null if and only if both new children are null.</returns>
  2065. private Node NewNodeInPlace(Node newLeft, Node newRight)
  2066. {
  2067. Debug.Assert(!shared);
  2068. if (newLeft == null)
  2069. return newRight;
  2070. else if (newRight == null)
  2071. return newLeft;
  2072. left = newLeft;
  2073. right = newRight;
  2074. count = left.Count + right.Count;
  2075. if (left.Depth > right.Depth)
  2076. depth = (short)(left.Depth + 1);
  2077. else
  2078. depth = (short)(right.Depth + 1);
  2079. return this;
  2080. }
  2081. /// <summary>
  2082. /// Returns the items at the given index in this node.
  2083. /// </summary>
  2084. /// <param name="index">0-based index, relative to this node.</param>
  2085. /// <returns>Item at that index.</returns>
  2086. public override T GetAt(int index)
  2087. {
  2088. int leftCount = left.Count;
  2089. if (index < leftCount)
  2090. return left.GetAt(index);
  2091. else
  2092. return right.GetAt(index - leftCount);
  2093. }
  2094. /// <summary>
  2095. /// Changes the item at the given index. May change this node,
  2096. /// or return a new node with the given item changed.
  2097. /// </summary>
  2098. /// <param name="index">Index, relative to this node, to change.</param>
  2099. /// <param name="item">New item to place at the given index.</param>
  2100. /// <returns>A node with the give item changed. If it can be done in place
  2101. /// then "this" is returned.</returns>
  2102. public override Node SetAtInPlace(int index, T item)
  2103. {
  2104. if (shared)
  2105. return SetAt(index, item); // Can't update a shared node in place.
  2106. int leftCount = left.Count;
  2107. if (index < leftCount) {
  2108. Node newLeft = left.SetAtInPlace(index, item);
  2109. if (newLeft != left)
  2110. return NewNodeInPlace(newLeft, right);
  2111. else
  2112. return this;
  2113. }
  2114. else {
  2115. Node newRight = right.SetAtInPlace(index - leftCount, item);
  2116. if (newRight != right)
  2117. return NewNodeInPlace(left, newRight);
  2118. else
  2119. return this;
  2120. }
  2121. }
  2122. /// <summary>
  2123. /// Changes the item at the given index. Never changes this node,
  2124. /// but always returns a new node with the given item changed.
  2125. /// </summary>
  2126. /// <param name="index">Index, relative to this node, to change.</param>
  2127. /// <param name="item">New item to place at the given index.</param>
  2128. /// <returns>A new node with the given item changed.</returns>
  2129. public override Node SetAt(int index, T item)
  2130. {
  2131. int leftCount = left.Count;
  2132. if (index < leftCount) {
  2133. return NewNode(left.SetAt(index, item), right);
  2134. }
  2135. else {
  2136. return NewNode(left, right.SetAt(index - leftCount, item));
  2137. }
  2138. }
  2139. /// <summary>
  2140. /// Prepend a item before this node. May change this node, or return
  2141. /// a new node. Equivalent to PrependInPlace(new LeafNode(item), true), but
  2142. /// may be more efficient because a new LeafNode might not be allocated.
  2143. /// </summary>
  2144. /// <param name="item">Item to prepend.</param>
  2145. /// <returns>A node with the given item prepended to this node. May be a new
  2146. /// node or the current node.</returns>
  2147. public override Node PrependInPlace(T item)
  2148. {
  2149. if (shared)
  2150. return Prepend(new LeafNode(item), true); // Can't update a shared node in place.
  2151. LeafNode leftLeaf;
  2152. if (left.Count < MAXLEAF && !left.Shared && (leftLeaf = left as LeafNode) != null) {
  2153. // Prepend the item to the left leaf. This keeps repeated prepends from creating
  2154. // single item nodes.
  2155. int c = leftLeaf.Count;
  2156. if (c == leftLeaf.items.Length) {
  2157. T[] newItems = new T[MAXLEAF];
  2158. Array.Copy(leftLeaf.items, 0, newItems, 1, c);
  2159. leftLeaf.items = newItems;
  2160. }
  2161. else {
  2162. Array.Copy(leftLeaf.items, 0, leftLeaf.items, 1, c);
  2163. }
  2164. leftLeaf.items[0] = item;
  2165. leftLeaf.count += 1;
  2166. this.count += 1;
  2167. return this;
  2168. }
  2169. else
  2170. return new ConcatNode(new LeafNode(item), this);
  2171. }
  2172. /// <summary>
  2173. /// Append a item after this node. May change this node, or return
  2174. /// a new node. Equivalent to AppendInPlace(new LeafNode(item), true), but
  2175. /// may be more efficient because a new LeafNode might not be allocated.
  2176. /// </summary>
  2177. /// <param name="item">Item to append.</param>
  2178. /// <returns>A node with the given item appended to this node. May be a new
  2179. /// node or the current node.</returns>
  2180. public override Node AppendInPlace(T item)
  2181. {
  2182. if (shared)
  2183. return Append(new LeafNode(item), true); // Can't update a shared node in place.
  2184. LeafNode rightLeaf;
  2185. if (right.Count < MAXLEAF && !right.Shared && (rightLeaf = right as LeafNode) != null) {
  2186. int c = rightLeaf.Count;
  2187. if (c == rightLeaf.items.Length) {
  2188. T[] newItems = new T[MAXLEAF]; // use MAXLEAF when appending, because we'll probably append again.
  2189. Array.Copy(rightLeaf.items, 0, newItems, 0, c);
  2190. rightLeaf.items = newItems;
  2191. }
  2192. rightLeaf.items[c] = item;
  2193. rightLeaf.count += 1;
  2194. this.count += 1;
  2195. return this;
  2196. }
  2197. else
  2198. return new ConcatNode(this, new LeafNode(item));
  2199. }
  2200. /// <summary>
  2201. /// Append a node after this node. May change this node, or return
  2202. /// a new node.
  2203. /// </summary>
  2204. /// <param name="node">Node to append.</param>
  2205. /// <param name="nodeIsUnused">If true, the given node is not used
  2206. /// in any current list, so it may be change, overwritten, or destroyed
  2207. /// if convenient. If false, the given node is in use. It should be marked
  2208. /// as shared if is is used within the return value.</param>
  2209. /// <returns>A node with the give node appended to this node. May be a new
  2210. /// node or the current node.</returns>
  2211. public override Node AppendInPlace(Node node, bool nodeIsUnused)
  2212. {
  2213. if (shared)
  2214. return Append(node, nodeIsUnused); // Can't update a shared node in place.
  2215. if (right.Count + node.Count <= MAXLEAF && right is LeafNode && node is LeafNode)
  2216. return NewNodeInPlace(left, right.AppendInPlace(node, nodeIsUnused));
  2217. if (!nodeIsUnused)
  2218. node.MarkShared();
  2219. return new ConcatNode(this, node);
  2220. }
  2221. public override Node Append(Node node, bool nodeIsUnused)
  2222. {
  2223. // If possible combine with a child leaf node on the right.
  2224. if (right.Count + node.Count <= MAXLEAF && right is LeafNode && node is LeafNode)
  2225. return NewNode(left, right.Append(node, nodeIsUnused));
  2226. // Concatinate with this node.
  2227. this.MarkShared();
  2228. if (!nodeIsUnused)
  2229. node.MarkShared();
  2230. return new ConcatNode(this, node);
  2231. }
  2232. /// <summary>
  2233. /// Inserts an item inside this node. May change this node, or return
  2234. /// a new node with the given appending done. Equivalent to
  2235. /// InsertInPlace(new LeafNode(item), true), but may be more efficient.
  2236. /// </summary>
  2237. /// <param name="index">Index, relative to this node, to insert at. Must
  2238. /// be in bounds.</param>
  2239. /// <param name="item">Item to insert.</param>
  2240. /// <returns>A node with the give item inserted. If done in-place, returns
  2241. /// "this".</returns>
  2242. public override Node InsertInPlace(int index, T item)
  2243. {
  2244. if (shared)
  2245. return Insert(index, new LeafNode(item), true);
  2246. int leftCount = left.Count;
  2247. if (index <= leftCount)
  2248. return NewNodeInPlace(left.InsertInPlace(index, item), right);
  2249. else
  2250. return NewNodeInPlace(left, right.InsertInPlace(index - leftCount, item));
  2251. }
  2252. /// <summary>
  2253. /// Inserts a node inside this node. May change this node, or return
  2254. /// a new node with the given appending done.
  2255. /// </summary>
  2256. /// <param name="index">Index, relative to this node, to insert at. Must
  2257. /// be in bounds.</param>
  2258. /// <param name="node">Node to insert.</param>
  2259. /// <param name="nodeIsUnused">If true, the given node is not used
  2260. /// in any current list, so it may be change, overwritten, or destroyed
  2261. /// if convenient. If false, the given node is in use. It should be marked
  2262. /// as shared if is is used within the return value.</param>
  2263. /// <returns>A node with the given item inserted. If done in-place, returns
  2264. /// "this".</returns>
  2265. public override Node InsertInPlace(int index, Node node, bool nodeIsUnused)
  2266. {
  2267. if (shared)
  2268. return Insert(index, node, nodeIsUnused);
  2269. int leftCount = left.Count;
  2270. if (index < leftCount)
  2271. return NewNodeInPlace(left.InsertInPlace(index, node, nodeIsUnused), right);
  2272. else
  2273. return NewNodeInPlace(left, right.InsertInPlace(index - leftCount, node, nodeIsUnused));
  2274. }
  2275. /// <summary>
  2276. /// Inserts a node inside this node. Never changes this node, but returns
  2277. /// a new node with the given appending done.
  2278. /// </summary>
  2279. /// <param name="index">Index, relative to this node, to insert at. Must
  2280. /// be in bounds.</param>
  2281. /// <param name="node">Node to insert.</param>
  2282. /// <param name="nodeIsUnused">If true, the given node is not used
  2283. /// in any current list, so it may be change, overwritten, or destroyed
  2284. /// if convenient. If false, the given node is in use. It should be marked
  2285. /// as shared if is is used within the return value.</param>
  2286. /// <returns>A new node with the give node inserted.</returns>
  2287. public override Node Insert(int index, Node node, bool nodeIsUnused)
  2288. {
  2289. int leftCount = left.Count;
  2290. if (index < leftCount)
  2291. return NewNode(left.Insert(index, node, nodeIsUnused), right);
  2292. else
  2293. return NewNode(left, right.Insert(index - leftCount, node, nodeIsUnused));
  2294. }
  2295. /// <summary>
  2296. /// Remove a range of items from this node. May change this node, or returns
  2297. /// a new node with the given appending done. The
  2298. /// sub-range may not be empty, but may extend outside the node.
  2299. /// In other words, first might be less than zero or last might be greater
  2300. /// than count. But, last can't be less than zero and first can't be
  2301. /// greater than count. Also, last must be greater than or equal to last.
  2302. /// </summary>
  2303. /// <param name="first">Inclusive index of first item in sub-range, relative
  2304. /// to this node.</param>
  2305. /// <param name="last">Inclusize index of last item in sub-range, relative
  2306. /// to this node.</param>
  2307. /// <returns>A node with the sub-range removed. If done in-place, returns
  2308. /// "this".</returns>
  2309. public override Node RemoveRangeInPlace(int first, int last)
  2310. {
  2311. if (shared)
  2312. return RemoveRange(first, last);
  2313. Debug.Assert(first < count);
  2314. Debug.Assert(last >= 0);
  2315. if (first <= 0 && last >= count - 1) {
  2316. return null;
  2317. }
  2318. int leftCount = left.Count;
  2319. Node newLeft = left, newRight = right;
  2320. // Is part of the left being removed?
  2321. if (first < leftCount)
  2322. newLeft = left.RemoveRangeInPlace(first, last);
  2323. // Is part of the right being remove?
  2324. if (last >= leftCount)
  2325. newRight = right.RemoveRangeInPlace(first - leftCount, last - leftCount);
  2326. return NewNodeInPlace(newLeft, newRight);
  2327. }
  2328. /// <summary>
  2329. /// Remove a range of items from this node. Never changes this node, but returns
  2330. /// a new node with the removing done. The
  2331. /// sub-range may not be empty, but may extend outside the node.
  2332. /// In other words, first might be less than zero or last might be greater
  2333. /// than count. But, last can't be less than zero and first can't be
  2334. /// greater than count. Also, last must be greater than or equal to last.
  2335. /// </summary>
  2336. /// <param name="first">Inclusive index of first item in sub-range, relative
  2337. /// to this node.</param>
  2338. /// <param name="last">Inclusize index of last item in sub-range, relative
  2339. /// to this node.</param>
  2340. /// <returns>A new node with the sub-range removed.</returns>
  2341. public override Node RemoveRange(int first, int last)
  2342. {
  2343. Debug.Assert(first < count);
  2344. Debug.Assert(last >= 0);
  2345. if (first <= 0 && last >= count - 1) {
  2346. return null;
  2347. }
  2348. int leftCount = left.Count;
  2349. Node newLeft = left, newRight = right;
  2350. // Is part of the left being removed?
  2351. if (first < leftCount)
  2352. newLeft = left.RemoveRange(first, last);
  2353. // Is part of the right being remove?
  2354. if (last >= leftCount)
  2355. newRight = right.RemoveRange(first - leftCount, last - leftCount);
  2356. return NewNode(newLeft, newRight);
  2357. }
  2358. /// <summary>
  2359. /// Returns a node that has a sub-range of items from this node. The
  2360. /// sub-range may not be empty, but may extend outside the node.
  2361. /// In other words, first might be less than zero or last might be greater
  2362. /// than count. But, last can't be less than zero and first can't be
  2363. /// greater than count. Also, last must be greater than or equal to last.
  2364. /// </summary>
  2365. /// <param name="first">Inclusive first element, relative to this node.</param>
  2366. /// <param name="last">Inclusize last element, relative to this node.</param>
  2367. /// <returns>Node with the given sub-range.</returns>
  2368. public override Node Subrange(int first, int last)
  2369. {
  2370. Debug.Assert(first < count);
  2371. Debug.Assert(last >= 0);
  2372. if (first <= 0 && last >= count - 1) {
  2373. // range encapsulate the whole node, so just return it.
  2374. MarkShared();
  2375. return this;
  2376. }
  2377. int leftCount = left.Count;
  2378. Node leftPart = null, rightPart = null;
  2379. // Is part of the left included?
  2380. if (first < leftCount)
  2381. leftPart = left.Subrange(first, last);
  2382. // Is part of the right included?
  2383. if (last >= leftCount)
  2384. rightPart = right.Subrange(first - leftCount, last - leftCount);
  2385. Debug.Assert(leftPart != null || rightPart != null);
  2386. // Combine the left parts and the right parts.
  2387. if (leftPart == null)
  2388. return rightPart;
  2389. else if (rightPart == null)
  2390. return leftPart;
  2391. else
  2392. return new ConcatNode(leftPart, rightPart);
  2393. }
  2394. #if DEBUG
  2395. /// <summary>
  2396. /// Validates the node for consistency, as much as possible. Also validates
  2397. /// child nodes, if any.
  2398. /// </summary>
  2399. public override void Validate()
  2400. {
  2401. Debug.Assert(left != null);
  2402. Debug.Assert(right != null);
  2403. Debug.Assert(Depth > 0);
  2404. Debug.Assert(Count > 0);
  2405. Debug.Assert(Math.Max(left.Depth, right.Depth) + 1 == Depth);
  2406. Debug.Assert(left.Count + right.Count == Count);
  2407. left.Validate();
  2408. right.Validate();
  2409. }
  2410. /// <summary>
  2411. /// Print out the contents of this node.
  2412. /// </summary>
  2413. /// <param name="prefixNode">Prefix to use in front of this node.</param>
  2414. /// <param name="prefixChildren">Prefixed to use in front of children of this node.</param>
  2415. public override void Print(string prefixNode, string prefixChildren)
  2416. {
  2417. Console.WriteLine("{0}CONCAT {1} {2} count={3} depth={4}", prefixNode, shared ? "S" : " ", IsBalanced() ? "B" : (IsAlmostBalanced() ? "A" : " "), count, depth);
  2418. left.Print(prefixChildren + "|-L-", prefixChildren + "| ");
  2419. right.Print(prefixChildren + "|-R-", prefixChildren + " ");
  2420. }
  2421. #endif //DEBUG
  2422. }
  2423. /// <summary>
  2424. /// The class that is used to implement IList&lt;T&gt; to view a sub-range
  2425. /// of a BigList. The object stores a wrapped list, and a start/count indicating
  2426. /// a sub-range of the list. Insertion/deletions through the sub-range view
  2427. /// cause the count to change also; insertions and deletions directly on
  2428. /// the wrapped list do not.
  2429. /// </summary>
  2430. /// <remarks>This is different from Algorithms.Range in a very few respects:
  2431. /// it is specialized to only wrap BigList, and it is a lot more efficient in enumeration.</remarks>
  2432. [Serializable]
  2433. private class BigListRange : ListBase<T>
  2434. {
  2435. private readonly BigList<T> wrappedList;
  2436. private readonly int start;
  2437. private int count;
  2438. /// <summary>
  2439. /// Create a sub-range view object on the indicate part
  2440. /// of the list.
  2441. /// </summary>
  2442. /// <param name="wrappedList">List to wrap.</param>
  2443. /// <param name="start">The start index of the view in the wrapped list.</param>
  2444. /// <param name="count">The number of items in the view.</param>
  2445. public BigListRange(BigList<T> wrappedList, int start, int count)
  2446. {
  2447. this.wrappedList = wrappedList;
  2448. this.start = start;
  2449. this.count = count;
  2450. }
  2451. public override int Count
  2452. {
  2453. get
  2454. {
  2455. return Math.Min(count, wrappedList.Count - start);
  2456. }
  2457. }
  2458. public override void Clear()
  2459. {
  2460. if (wrappedList.Count - start < count)
  2461. count = wrappedList.Count - start;
  2462. while (count > 0) {
  2463. wrappedList.RemoveAt(start + count - 1);
  2464. --count;
  2465. }
  2466. }
  2467. public override void Insert(int index, T item)
  2468. {
  2469. if (index < 0 || index > count)
  2470. throw new ArgumentOutOfRangeException("index");
  2471. wrappedList.Insert(start + index, item);
  2472. ++count;
  2473. }
  2474. public override void RemoveAt(int index)
  2475. {
  2476. if (index < 0 || index >= count)
  2477. throw new ArgumentOutOfRangeException("index");
  2478. wrappedList.RemoveAt(start + index);
  2479. --count;
  2480. }
  2481. public override T this[int index]
  2482. {
  2483. get
  2484. {
  2485. if (index < 0 || index >= count)
  2486. throw new ArgumentOutOfRangeException("index");
  2487. return wrappedList[start + index];
  2488. }
  2489. set
  2490. {
  2491. if (index < 0 || index >= count)
  2492. throw new ArgumentOutOfRangeException("index");
  2493. wrappedList[start + index] = value;
  2494. }
  2495. }
  2496. public override IEnumerator<T> GetEnumerator()
  2497. {
  2498. return wrappedList.GetEnumerator(start, count);
  2499. }
  2500. }
  2501. }
  2502. }