PageRenderTime 39ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/Mono.C5/C5/linkedlists/LinkedList.cs

https://bitbucket.org/danipen/mono
C# | 3919 lines | 2791 code | 357 blank | 771 comment | 695 complexity | 6d21a466f36b04d570234993d218f167 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. /*
  2. Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
  3. Permission is hereby granted, free of charge, to any person obtaining a copy
  4. of this software and associated documentation files (the "Software"), to deal
  5. in the Software without restriction, including without limitation the rights
  6. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. copies of the Software, and to permit persons to whom the Software is
  8. furnished to do so, subject to the following conditions:
  9. The above copyright notice and this permission notice shall be included in
  10. all copies or substantial portions of the Software.
  11. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  17. SOFTWARE.
  18. */
  19. #define HASHINDEXnot
  20. using System;
  21. using System.Diagnostics;
  22. using SCG = System.Collections.Generic;
  23. namespace C5
  24. {
  25. /// <summary>
  26. /// A list collection class based on a doubly linked list data structure.
  27. /// </summary>
  28. [Serializable]
  29. public class LinkedList<T> : SequencedBase<T>, IList<T>, SCG.IList<T>
  30. #if HASHINDEX
  31. #else
  32. , IStack<T>, IQueue<T>
  33. #endif
  34. {
  35. #region Fields
  36. /// <summary>
  37. /// IExtensible.Add(T) always does AddLast(T), fIFO determines
  38. /// if T Remove() does RemoveFirst() or RemoveLast()
  39. /// </summary>
  40. bool fIFO = true;
  41. #region Events
  42. /// <summary>
  43. ///
  44. /// </summary>
  45. /// <value></value>
  46. public override EventTypeEnum ListenableEvents { get { return underlying == null ? EventTypeEnum.All : EventTypeEnum.None; } }
  47. #endregion
  48. //Invariant: startsentinel != null && endsentinel != null
  49. //If size==0: startsentinel.next == endsentinel && endsentinel.prev == startsentinel
  50. //Else: startsentinel.next == First && endsentinel.prev == Last)
  51. /// <summary>
  52. /// Node to the left of first node
  53. /// </summary>
  54. Node startsentinel;
  55. /// <summary>
  56. /// Node to the right of last node
  57. /// </summary>
  58. Node endsentinel;
  59. /// <summary>
  60. /// Offset of this view in underlying list
  61. /// </summary>
  62. #if HASHINDEX
  63. int? offset;
  64. #else
  65. int offset;
  66. #endif
  67. /// <summary>
  68. /// underlying list of this view (or null for the underlying list)
  69. /// </summary>
  70. LinkedList<T> underlying;
  71. //Note: all views will have the same views list since all view objects are created by MemberwiseClone()
  72. WeakViewList<LinkedList<T>> views;
  73. WeakViewList<LinkedList<T>>.Node myWeakReference;
  74. /// <summary>
  75. /// Has this list or view not been invalidated by some operation (by someone calling Dispose())
  76. /// </summary>
  77. bool isValid = true;
  78. #if HASHINDEX
  79. HashDictionary<T, Node> dict;
  80. /// <summary>
  81. /// Number of taggroups
  82. /// </summary>
  83. int taggroups;
  84. /// <summary>
  85. ///
  86. /// </summary>
  87. /// <value></value>
  88. int Taggroups
  89. {
  90. get { return underlying == null ? taggroups : underlying.taggroups; }
  91. set { if (underlying == null) taggroups = value; else underlying.taggroups = value; }
  92. }
  93. #endif
  94. #endregion
  95. #region Util
  96. bool equals(T i1, T i2) { return itemequalityComparer.Equals(i1, i2); }
  97. #region Check utilities
  98. /// <summary>
  99. /// Check if it is valid to perform updates and increment stamp of
  100. /// underlying if this is a view.
  101. /// <para>This method should be called in every public modifying
  102. /// methods before any modifications are performed.
  103. /// </para>
  104. /// </summary>
  105. /// <exception cref="InvalidOperationException"> if check fails.</exception>
  106. protected override void updatecheck()
  107. {
  108. validitycheck();
  109. base.updatecheck();
  110. if (underlying != null)
  111. underlying.stamp++;
  112. }
  113. /// <summary>
  114. /// Check if we are a view that the underlyinglist has only been updated through us.
  115. /// <br/>
  116. /// This method should be called from enumerators etc to guard against
  117. /// modification of the base collection.
  118. /// </summary>
  119. /// <exception cref="InvalidOperationException"> if check fails.</exception>
  120. void validitycheck()
  121. {
  122. if (!isValid)
  123. throw new ViewDisposedException();
  124. }
  125. /// <summary>
  126. /// Check that the list has not been updated since a particular time.
  127. /// </summary>
  128. /// <param name="stamp">The stamp indicating the time.</param>
  129. /// <exception cref="CollectionModifiedException"> if check fails.</exception>
  130. protected override void modifycheck(int stamp)
  131. {
  132. validitycheck();
  133. if ((underlying != null ? underlying.stamp : this.stamp) != stamp)
  134. throw new CollectionModifiedException();
  135. }
  136. #endregion
  137. #region Searching
  138. bool contains(T item, out Node node)
  139. {
  140. #if HASHINDEX
  141. if (dict.Find(item, out node))
  142. return insideview(node);
  143. #else
  144. //TODO: search from both ends? Or search from the end selected by FIFO?
  145. node = startsentinel.next;
  146. while (node != endsentinel)
  147. {
  148. if (equals(item, node.item))
  149. return true;
  150. node = node.next;
  151. }
  152. #endif
  153. return false;
  154. }
  155. /// <summary>
  156. /// Search forwards from a node for a node with a particular item.
  157. /// </summary>
  158. /// <param name="item">The item to look for</param>
  159. /// <param name="node">On input, the node to start at. If item was found, the node found on output.</param>
  160. /// <param name="index">If node was found, the value will be the number of links followed higher than
  161. /// the value on input. If item was not found, the value on output is undefined.</param>
  162. /// <returns>True if node was found.</returns>
  163. bool find(T item, ref Node node, ref int index)
  164. {
  165. while (node != endsentinel)
  166. {
  167. //if (item.Equals(node.item))
  168. if (itemequalityComparer.Equals(item, node.item))
  169. return true;
  170. index++;
  171. node = node.next;
  172. }
  173. return false;
  174. }
  175. bool dnif(T item, ref Node node, ref int index)
  176. {
  177. while (node != startsentinel)
  178. {
  179. //if (item.Equals(node.item))
  180. if (itemequalityComparer.Equals(item, node.item))
  181. return true;
  182. index--;
  183. node = node.prev;
  184. }
  185. return false;
  186. }
  187. #if HASHINDEX
  188. bool insideview(Node node)
  189. {
  190. if (underlying == null)
  191. return true;
  192. return (startsentinel.precedes(node) && node.precedes(endsentinel));
  193. }
  194. #endif
  195. #endregion
  196. #region Indexing
  197. /// <summary>
  198. /// Return the node at position pos
  199. /// </summary>
  200. /// <param name="pos"></param>
  201. /// <returns></returns>
  202. Node get(int pos)
  203. {
  204. if (pos < 0 || pos >= size)
  205. throw new IndexOutOfRangeException();
  206. else if (pos < size / 2)
  207. { // Closer to front
  208. Node node = startsentinel;
  209. for (int i = 0; i <= pos; i++)
  210. node = node.next;
  211. return node;
  212. }
  213. else
  214. { // Closer to end
  215. Node node = endsentinel;
  216. for (int i = size; i > pos; i--)
  217. node = node.prev;
  218. return node;
  219. }
  220. }
  221. /// <summary>
  222. /// Find the distance from pos to the set given by positions. Return the
  223. /// signed distance as return value and as an out parameter, the
  224. /// array index of the nearest position. This is used for up to length 5 of
  225. /// positions, and we do not assume it is sorted.
  226. /// </summary>
  227. /// <param name="pos"></param>
  228. /// <param name="positions"></param>
  229. /// <param name="nearest"></param>
  230. /// <returns></returns>
  231. int dist(int pos, out int nearest, int[] positions)
  232. {
  233. nearest = -1;
  234. int bestdist = int.MaxValue;
  235. int signeddist = bestdist;
  236. for (int i = 0; i < positions.Length; i++)
  237. {
  238. int thisdist = positions[i] - pos;
  239. if (thisdist >= 0 && thisdist < bestdist) { nearest = i; bestdist = thisdist; signeddist = thisdist; }
  240. if (thisdist < 0 && -thisdist < bestdist) { nearest = i; bestdist = -thisdist; signeddist = thisdist; }
  241. }
  242. return signeddist;
  243. }
  244. /// <summary>
  245. /// Find the node at position pos, given known positions of several nodes.
  246. /// </summary>
  247. /// <param name="pos"></param>
  248. /// <param name="positions"></param>
  249. /// <param name="nodes"></param>
  250. /// <returns></returns>
  251. Node get(int pos, int[] positions, Node[] nodes)
  252. {
  253. int nearest;
  254. int delta = dist(pos, out nearest, positions);
  255. Node node = nodes[nearest];
  256. if (delta > 0)
  257. for (int i = 0; i < delta; i++)
  258. node = node.prev;
  259. else
  260. for (int i = 0; i > delta; i--)
  261. node = node.next;
  262. return node;
  263. }
  264. /// <summary>
  265. /// Get nodes at positions p1 and p2, given nodes at several positions.
  266. /// </summary>
  267. /// <param name="p1"></param>
  268. /// <param name="p2"></param>
  269. /// <param name="n1"></param>
  270. /// <param name="n2"></param>
  271. /// <param name="positions"></param>
  272. /// <param name="nodes"></param>
  273. void getPair(int p1, int p2, out Node n1, out Node n2, int[] positions, Node[] nodes)
  274. {
  275. int nearest1, nearest2;
  276. int delta1 = dist(p1, out nearest1, positions), d1 = delta1 < 0 ? -delta1 : delta1;
  277. int delta2 = dist(p2, out nearest2, positions), d2 = delta2 < 0 ? -delta2 : delta2;
  278. if (d1 < d2)
  279. {
  280. n1 = get(p1, positions, nodes);
  281. n2 = get(p2, new int[] { positions[nearest2], p1 }, new Node[] { nodes[nearest2], n1 });
  282. }
  283. else
  284. {
  285. n2 = get(p2, positions, nodes);
  286. n1 = get(p1, new int[] { positions[nearest1], p2 }, new Node[] { nodes[nearest1], n2 });
  287. }
  288. }
  289. #endregion
  290. #region Insertion
  291. #if HASHINDEX
  292. void insert(int index, Node succ, T item)
  293. {
  294. Node newnode = new Node(item);
  295. if (dict.FindOrAdd(item, ref newnode))
  296. throw new DuplicateNotAllowedException("Item already in indexed list");
  297. insertNode(true, succ, newnode);
  298. }
  299. /// <summary>
  300. /// Insert a Node before another one. Unchecked version.
  301. /// </summary>
  302. /// <param name="succ">The successor to be</param>
  303. /// <param name="newnode">Node to insert</param>
  304. /// <param name="updateViews">update overlapping view in this call</param>
  305. void insertNode(bool updateViews, Node succ, Node newnode)
  306. {
  307. newnode.next = succ;
  308. Node pred = newnode.prev = succ.prev;
  309. succ.prev.next = newnode;
  310. succ.prev = newnode;
  311. size++;
  312. if (underlying != null)
  313. underlying.size++;
  314. settag(newnode);
  315. if (updateViews)
  316. fixViewsAfterInsert(succ, pred, 1, 0);
  317. }
  318. #else
  319. /// <summary>
  320. ///
  321. /// </summary>
  322. /// <param name="index">The index in this view</param>
  323. /// <param name="succ"></param>
  324. /// <param name="item"></param>
  325. /// <returns></returns>
  326. Node insert(int index, Node succ, T item)
  327. {
  328. Node newnode = new Node(item, succ.prev, succ);
  329. succ.prev.next = newnode;
  330. succ.prev = newnode;
  331. size++;
  332. if (underlying != null)
  333. underlying.size++;
  334. fixViewsAfterInsert(succ, newnode.prev, 1, Offset + index);
  335. return newnode;
  336. }
  337. #endif
  338. #endregion
  339. #region Removal
  340. T remove(Node node, int index)
  341. {
  342. fixViewsBeforeSingleRemove(node, Offset + index);
  343. node.prev.next = node.next;
  344. node.next.prev = node.prev;
  345. size--;
  346. if (underlying != null)
  347. underlying.size--;
  348. #if HASHINDEX
  349. removefromtaggroup(node);
  350. #endif
  351. return node.item;
  352. }
  353. #if HASHINDEX
  354. private bool dictremove(T item, out Node node)
  355. {
  356. if (underlying == null)
  357. {
  358. if (!dict.Remove(item, out node))
  359. return false;
  360. }
  361. else
  362. {
  363. //We cannot avoid calling dict twice - have to intersperse the listorder test!
  364. if (!contains(item, out node))
  365. return false;
  366. dict.Remove(item);
  367. }
  368. return true;
  369. }
  370. #endif
  371. #endregion
  372. #region fixView utilities
  373. /// <summary>
  374. ///
  375. /// </summary>
  376. /// <param name="added">The actual number of inserted nodes</param>
  377. /// <param name="pred">The predecessor of the inserted nodes</param>
  378. /// <param name="succ">The successor of the added nodes</param>
  379. /// <param name="realInsertionIndex"></param>
  380. void fixViewsAfterInsert(Node succ, Node pred, int added, int realInsertionIndex)
  381. {
  382. if (views != null)
  383. foreach (LinkedList<T> view in views)
  384. {
  385. if (view != this)
  386. {
  387. #if HASHINDEX
  388. if (pred.precedes(view.startsentinel) || (view.startsentinel == pred && view.size > 0))
  389. view.offset += added;
  390. if (view.startsentinel.precedes(pred) && succ.precedes(view.endsentinel))
  391. view.size += added;
  392. if (view.startsentinel == pred && view.size > 0)
  393. view.startsentinel = succ.prev;
  394. if (view.endsentinel == succ)
  395. view.endsentinel = pred.next;
  396. #else
  397. if (view.Offset == realInsertionIndex && view.size > 0)
  398. view.startsentinel = succ.prev;
  399. if (view.Offset + view.size == realInsertionIndex)
  400. view.endsentinel = pred.next;
  401. if (view.Offset < realInsertionIndex && view.Offset + view.size > realInsertionIndex)
  402. view.size += added;
  403. if (view.Offset > realInsertionIndex || (view.Offset == realInsertionIndex && view.size > 0))
  404. view.offset += added;
  405. #endif
  406. }
  407. }
  408. }
  409. void fixViewsBeforeSingleRemove(Node node, int realRemovalIndex)
  410. {
  411. if (views != null)
  412. foreach (LinkedList<T> view in views)
  413. {
  414. if (view != this)
  415. {
  416. #if HASHINDEX
  417. if (view.startsentinel.precedes(node) && node.precedes(view.endsentinel))
  418. view.size--;
  419. if (!view.startsentinel.precedes(node))
  420. view.offset--;
  421. if (view.startsentinel == node)
  422. view.startsentinel = node.prev;
  423. if (view.endsentinel == node)
  424. view.endsentinel = node.next;
  425. #else
  426. if (view.offset - 1 == realRemovalIndex)
  427. view.startsentinel = node.prev;
  428. if (view.offset + view.size == realRemovalIndex)
  429. view.endsentinel = node.next;
  430. if (view.offset <= realRemovalIndex && view.offset + view.size > realRemovalIndex)
  431. view.size--;
  432. if (view.offset > realRemovalIndex)
  433. view.offset--;
  434. #endif
  435. }
  436. }
  437. }
  438. #if HASHINDEX
  439. #else
  440. void fixViewsBeforeRemove(int start, int count, Node first, Node last)
  441. {
  442. int clearend = start + count - 1;
  443. if (views != null)
  444. foreach (LinkedList<T> view in views)
  445. {
  446. if (view == this)
  447. continue;
  448. int viewoffset = view.Offset, viewend = viewoffset + view.size - 1;
  449. //sentinels
  450. if (start < viewoffset && viewoffset - 1 <= clearend)
  451. view.startsentinel = first.prev;
  452. if (start <= viewend + 1 && viewend < clearend)
  453. view.endsentinel = last.next;
  454. //offsets and sizes
  455. if (start < viewoffset)
  456. {
  457. if (clearend < viewoffset)
  458. view.offset = viewoffset - count;
  459. else
  460. {
  461. view.offset = start;
  462. view.size = clearend < viewend ? viewend - clearend : 0;
  463. }
  464. }
  465. else if (start <= viewend)
  466. view.size = clearend <= viewend ? view.size - count : start - viewoffset;
  467. }
  468. }
  469. #endif
  470. /// <summary>
  471. ///
  472. /// </summary>
  473. /// <param name="otherView"></param>
  474. /// <returns>The position of View(otherOffset, otherSize) wrt. this view</returns>
  475. MutualViewPosition viewPosition(LinkedList<T> otherView)
  476. {
  477. #if HASHINDEX
  478. Node otherstartsentinel = otherView.startsentinel, otherendsentinel = otherView.endsentinel,
  479. first = startsentinel.next, last = endsentinel.prev,
  480. otherfirst = otherstartsentinel.next, otherlast = otherendsentinel.prev;
  481. if (last.precedes(otherfirst) || otherlast.precedes(first))
  482. return MutualViewPosition.NonOverlapping;
  483. if (size == 0 || (otherstartsentinel.precedes(first) && last.precedes(otherendsentinel)))
  484. return MutualViewPosition.Contains;
  485. if (otherView.size == 0 || (startsentinel.precedes(otherfirst) && otherlast.precedes(endsentinel)))
  486. return MutualViewPosition.ContainedIn;
  487. return MutualViewPosition.Overlapping;
  488. #else
  489. int end = offset + size, otherOffset = otherView.offset, otherSize = otherView.size, otherEnd = otherOffset + otherSize;
  490. if (otherOffset >= end || otherEnd <= offset)
  491. return MutualViewPosition.NonOverlapping;
  492. if (size == 0 || (otherOffset <= offset && end <= otherEnd))
  493. return MutualViewPosition.Contains;
  494. if (otherSize == 0 || (offset <= otherOffset && otherEnd <= end))
  495. return MutualViewPosition.ContainedIn;
  496. return MutualViewPosition.Overlapping;
  497. #endif
  498. }
  499. void disposeOverlappingViews(bool reverse)
  500. {
  501. if (views != null)
  502. {
  503. foreach (LinkedList<T> view in views)
  504. {
  505. if (view != this)
  506. {
  507. switch (viewPosition(view))
  508. {
  509. case MutualViewPosition.ContainedIn:
  510. if (reverse)
  511. { }
  512. else
  513. view.Dispose();
  514. break;
  515. case MutualViewPosition.Overlapping:
  516. view.Dispose();
  517. break;
  518. case MutualViewPosition.Contains:
  519. case MutualViewPosition.NonOverlapping:
  520. break;
  521. }
  522. }
  523. }
  524. }
  525. }
  526. #endregion
  527. #endregion
  528. #region Constructors
  529. /// <summary>
  530. /// Create a linked list with en external item equalityComparer
  531. /// </summary>
  532. /// <param name="itemequalityComparer">The external equalityComparer</param>
  533. public LinkedList(SCG.IEqualityComparer<T> itemequalityComparer)
  534. : base(itemequalityComparer)
  535. {
  536. offset = 0;
  537. size = stamp = 0;
  538. startsentinel = new Node(default(T));
  539. endsentinel = new Node(default(T));
  540. startsentinel.next = endsentinel;
  541. endsentinel.prev = startsentinel;
  542. #if HASHINDEX
  543. //It is important that the sentinels are different:
  544. startsentinel.taggroup = new TagGroup();
  545. startsentinel.taggroup.tag = int.MinValue;
  546. startsentinel.taggroup.count = 0;
  547. endsentinel.taggroup = new TagGroup();
  548. endsentinel.taggroup.tag = int.MaxValue;
  549. endsentinel.taggroup.count = 0;
  550. dict = new HashDictionary<T, Node>(itemequalityComparer);
  551. #endif
  552. }
  553. /// <summary>
  554. /// Create a linked list with the natural item equalityComparer
  555. /// </summary>
  556. public LinkedList() : this(EqualityComparer<T>.Default) { }
  557. #endregion
  558. #region Node nested class
  559. /// <summary>
  560. /// An individual cell in the linked list
  561. /// </summary>
  562. [Serializable]
  563. class Node
  564. {
  565. public Node prev;
  566. public Node next;
  567. public T item;
  568. #region Tag support
  569. #if HASHINDEX
  570. internal int tag;
  571. internal TagGroup taggroup;
  572. internal bool precedes(Node that)
  573. {
  574. //Debug.Assert(taggroup != null, "taggroup field null");
  575. //Debug.Assert(that.taggroup != null, "that.taggroup field null");
  576. int t1 = taggroup.tag;
  577. int t2 = that.taggroup.tag;
  578. return t1 < t2 ? true : t1 > t2 ? false : tag < that.tag;
  579. }
  580. #endif
  581. #endregion
  582. [Tested]
  583. internal Node(T item) { this.item = item; }
  584. [Tested]
  585. internal Node(T item, Node prev, Node next)
  586. {
  587. this.item = item; this.prev = prev; this.next = next;
  588. }
  589. public override string ToString()
  590. {
  591. #if HASHINDEX
  592. return String.Format("Node: (item={0}, tag={1})", item, tag);
  593. #else
  594. return String.Format("Node(item={0})", item);
  595. #endif
  596. }
  597. }
  598. #endregion
  599. #region Taggroup nested class and tag maintenance utilities
  600. #if HASHINDEX
  601. /// <summary>
  602. /// A group of nodes with the same high tag. Purpose is to be
  603. /// able to tell the sequence order of two nodes without having to scan through
  604. /// the list.
  605. /// </summary>
  606. [Serializable]
  607. class TagGroup
  608. {
  609. internal int tag, count;
  610. internal Node first, last;
  611. /// <summary>
  612. /// Pretty print a tag group
  613. /// </summary>
  614. /// <returns>Formatted tag group</returns>
  615. public override string ToString()
  616. { return String.Format("TagGroup(tag={0}, cnt={1}, fst={2}, lst={3})", tag, count, first, last); }
  617. }
  618. //Constants for tag maintenance
  619. const int wordsize = 32;
  620. const int lobits = 3;
  621. const int hibits = lobits + 1;
  622. const int losize = 1 << lobits;
  623. const int hisize = 1 << hibits;
  624. const int logwordsize = 5;
  625. TagGroup gettaggroup(Node pred, Node succ, out int lowbound, out int highbound)
  626. {
  627. TagGroup predgroup = pred.taggroup, succgroup = succ.taggroup;
  628. if (predgroup == succgroup)
  629. {
  630. lowbound = pred.tag + 1;
  631. highbound = succ.tag - 1;
  632. return predgroup;
  633. }
  634. else if (predgroup.first != null)
  635. {
  636. lowbound = pred.tag + 1;
  637. highbound = int.MaxValue;
  638. return predgroup;
  639. }
  640. else if (succgroup.first != null)
  641. {
  642. lowbound = int.MinValue;
  643. highbound = succ.tag - 1;
  644. return succgroup;
  645. }
  646. else
  647. {
  648. lowbound = int.MinValue;
  649. highbound = int.MaxValue;
  650. return new TagGroup();
  651. }
  652. }
  653. /// <summary>
  654. /// Put a tag on a node (already inserted in the list). Split taggroups and renumber as
  655. /// necessary.
  656. /// </summary>
  657. /// <param name="node">The node to tag</param>
  658. void settag(Node node)
  659. {
  660. Node pred = node.prev, succ = node.next;
  661. TagGroup predgroup = pred.taggroup, succgroup = succ.taggroup;
  662. if (predgroup == succgroup)
  663. {
  664. node.taggroup = predgroup;
  665. predgroup.count++;
  666. if (pred.tag + 1 == succ.tag)
  667. splittaggroup(predgroup);
  668. else
  669. node.tag = (pred.tag + 1) / 2 + (succ.tag - 1) / 2;
  670. }
  671. else if (predgroup.first != null)
  672. {
  673. node.taggroup = predgroup;
  674. predgroup.last = node;
  675. predgroup.count++;
  676. if (pred.tag == int.MaxValue)
  677. splittaggroup(predgroup);
  678. else
  679. node.tag = pred.tag / 2 + int.MaxValue / 2 + 1;
  680. }
  681. else if (succgroup.first != null)
  682. {
  683. node.taggroup = succgroup;
  684. succgroup.first = node;
  685. succgroup.count++;
  686. if (succ.tag == int.MinValue)
  687. splittaggroup(node.taggroup);
  688. else
  689. node.tag = int.MinValue / 2 + (succ.tag - 1) / 2;
  690. }
  691. else
  692. {
  693. Debug.Assert(Taggroups == 0);
  694. TagGroup newgroup = new TagGroup();
  695. Taggroups = 1;
  696. node.taggroup = newgroup;
  697. newgroup.first = newgroup.last = node;
  698. newgroup.count = 1;
  699. return;
  700. }
  701. }
  702. /// <summary>
  703. /// Remove a node from its taggroup.
  704. /// <br/> When this is called, node must already have been removed from the underlying list
  705. /// </summary>
  706. /// <param name="node">The node to remove</param>
  707. void removefromtaggroup(Node node)
  708. {
  709. TagGroup taggroup = node.taggroup;
  710. if (--taggroup.count == 0)
  711. {
  712. Taggroups--;
  713. return;
  714. }
  715. if (node == taggroup.first)
  716. taggroup.first = node.next;
  717. if (node == taggroup.last)
  718. taggroup.last = node.prev;
  719. //node.taggroup = null;
  720. if (taggroup.count != losize || Taggroups == 1)
  721. return;
  722. TagGroup otg;
  723. // bug20070911:
  724. Node neighbor;
  725. if ((neighbor = taggroup.first.prev) != startsentinel
  726. && (otg = neighbor.taggroup).count <= losize)
  727. taggroup.first = otg.first;
  728. else if ((neighbor = taggroup.last.next) != endsentinel
  729. && (otg = neighbor.taggroup).count <= losize)
  730. taggroup.last = otg.last;
  731. else
  732. return;
  733. Node n = otg.first;
  734. for (int i = 0, length = otg.count; i < length; i++)
  735. {
  736. n.taggroup = taggroup;
  737. n = n.next;
  738. }
  739. taggroup.count += otg.count;
  740. Taggroups--;
  741. n = taggroup.first;
  742. const int ofs = wordsize - hibits;
  743. for (int i = 0, count = taggroup.count; i < count; i++)
  744. {
  745. n.tag = (i - losize) << ofs; //(i-8)<<28
  746. n = n.next;
  747. }
  748. }
  749. /// <summary>
  750. /// Split a tag group to make rom for more tags.
  751. /// </summary>
  752. /// <param name="taggroup">The tag group</param>
  753. void splittaggroup(TagGroup taggroup)
  754. {
  755. Node n = taggroup.first;
  756. int ptgt = taggroup.first.prev.taggroup.tag;
  757. int ntgt = taggroup.last.next.taggroup.tag;
  758. Debug.Assert(ptgt + 1 <= ntgt - 1);
  759. int ofs = wordsize - hibits;
  760. int newtgs = (taggroup.count - 1) / hisize;
  761. int tgtdelta = (int)((ntgt + 0.0 - ptgt) / (newtgs + 2)), tgtag = ptgt;
  762. tgtdelta = tgtdelta == 0 ? 1 : tgtdelta;
  763. for (int j = 0; j < newtgs; j++)
  764. {
  765. TagGroup newtaggroup = new TagGroup();
  766. newtaggroup.tag = (tgtag = tgtag >= ntgt - tgtdelta ? ntgt : tgtag + tgtdelta);
  767. newtaggroup.first = n;
  768. newtaggroup.count = hisize;
  769. for (int i = 0; i < hisize; i++)
  770. {
  771. n.taggroup = newtaggroup;
  772. n.tag = (i - losize) << ofs; //(i-8)<<28
  773. n = n.next;
  774. }
  775. newtaggroup.last = n.prev;
  776. }
  777. int rest = taggroup.count - hisize * newtgs;
  778. taggroup.first = n;
  779. taggroup.count = rest;
  780. taggroup.tag = (tgtag = tgtag >= ntgt - tgtdelta ? ntgt : tgtag + tgtdelta); ofs--;
  781. for (int i = 0; i < rest; i++)
  782. {
  783. n.tag = (i - hisize) << ofs; //(i-16)<<27
  784. n = n.next;
  785. }
  786. taggroup.last = n.prev;
  787. Taggroups += newtgs;
  788. if (tgtag == ntgt)
  789. redistributetaggroups(taggroup);
  790. }
  791. private void redistributetaggroups(TagGroup taggroup)
  792. {
  793. TagGroup pred = taggroup, succ = taggroup, tmp;
  794. double limit = 1, bigt = Math.Pow(Taggroups, 1.0 / 30);//?????
  795. int bits = 1, count = 1, lowmask = 0, himask = 0, target = 0;
  796. do
  797. {
  798. bits++;
  799. lowmask = (1 << bits) - 1;
  800. himask = ~lowmask;
  801. target = taggroup.tag & himask;
  802. while ((tmp = pred.first.prev.taggroup).first != null && (tmp.tag & himask) == target)
  803. { count++; pred = tmp; }
  804. while ((tmp = succ.last.next.taggroup).last != null && (tmp.tag & himask) == target)
  805. { count++; succ = tmp; }
  806. limit *= bigt;
  807. } while (count > limit);
  808. //redistibute tags
  809. int lob = pred.first.prev.taggroup.tag, upb = succ.last.next.taggroup.tag;
  810. int delta = upb / (count + 1) - lob / (count + 1);
  811. Debug.Assert(delta > 0);
  812. for (int i = 0; i < count; i++)
  813. {
  814. pred.tag = lob + (i + 1) * delta;
  815. pred = pred.last.next.taggroup;
  816. }
  817. }
  818. #endif
  819. #endregion
  820. #region Position, PositionComparer and ViewHandler nested types
  821. class PositionComparer : SCG.IComparer<Position>
  822. {
  823. static PositionComparer _default;
  824. PositionComparer() { }
  825. public static PositionComparer Default { get { return _default ?? (_default = new PositionComparer()); } }
  826. public int Compare(Position a, Position b)
  827. {
  828. #if HASHINDEX
  829. return a.Endpoint == b.Endpoint ? 0 : a.Endpoint.precedes(b.Endpoint) ? -1 : 1;
  830. #else
  831. return a.Index.CompareTo(b.Index);
  832. #endif
  833. }
  834. }
  835. /// <summary>
  836. /// During RemoveAll, we need to cache the original endpoint indices of views
  837. /// </summary>
  838. struct Position
  839. {
  840. public readonly LinkedList<T> View;
  841. public bool Left;
  842. #if HASHINDEX
  843. public readonly Node Endpoint;
  844. #else
  845. public readonly int Index;
  846. #endif
  847. public Position(LinkedList<T> view, bool left)
  848. {
  849. View = view;
  850. Left = left;
  851. #if HASHINDEX
  852. Endpoint = left ? view.startsentinel.next : view.endsentinel.prev;
  853. #else
  854. Index = left ? view.Offset : view.Offset + view.size - 1;
  855. #endif
  856. }
  857. #if HASHINDEX
  858. public Position(Node node, int foo) { this.Endpoint = node; View = null; Left = false; }
  859. #else
  860. public Position(int index) { this.Index = index; View = null; Left = false; }
  861. #endif
  862. }
  863. //TODO: merge the two implementations using Position values as arguments
  864. /// <summary>
  865. /// Handle the update of (other) views during a multi-remove operation.
  866. /// </summary>
  867. struct ViewHandler
  868. {
  869. ArrayList<Position> leftEnds;
  870. ArrayList<Position> rightEnds;
  871. int leftEndIndex, rightEndIndex, leftEndIndex2, rightEndIndex2;
  872. internal readonly int viewCount;
  873. internal ViewHandler(LinkedList<T> list)
  874. {
  875. leftEndIndex = rightEndIndex = leftEndIndex2 = rightEndIndex2 = viewCount = 0;
  876. leftEnds = rightEnds = null;
  877. if (list.views != null)
  878. foreach (LinkedList<T> v in list.views)
  879. if (v != list)
  880. {
  881. if (leftEnds == null)
  882. {
  883. leftEnds = new ArrayList<Position>();
  884. rightEnds = new ArrayList<Position>();
  885. }
  886. leftEnds.Add(new Position(v, true));
  887. rightEnds.Add(new Position(v, false));
  888. }
  889. if (leftEnds == null)
  890. return;
  891. viewCount = leftEnds.Count;
  892. leftEnds.Sort(PositionComparer.Default);
  893. rightEnds.Sort(PositionComparer.Default);
  894. }
  895. #if HASHINDEX
  896. internal void skipEndpoints(int removed, Node n)
  897. {
  898. if (viewCount > 0)
  899. {
  900. Position endpoint;
  901. while (leftEndIndex < viewCount && ((endpoint = leftEnds[leftEndIndex]).Endpoint.prev.precedes(n)))
  902. {
  903. LinkedList<T> view = endpoint.View;
  904. view.offset = view.offset - removed;//TODO: extract offset.Value?
  905. view.size += removed;
  906. leftEndIndex++;
  907. }
  908. while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Endpoint.precedes(n))
  909. {
  910. LinkedList<T> view = endpoint.View;
  911. view.size -= removed;
  912. rightEndIndex++;
  913. }
  914. }
  915. if (viewCount > 0)
  916. {
  917. Position endpoint;
  918. while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Endpoint.prev.precedes(n))
  919. leftEndIndex2++;
  920. while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Endpoint.next.precedes(n))
  921. rightEndIndex2++;
  922. }
  923. }
  924. /// <summary>
  925. /// To be called with n pointing to the right of each node to be removed in a stretch.
  926. /// And at the endsentinel.
  927. ///
  928. /// Update offset of a view whose left endpoint (has not already been handled and) is n or precedes n.
  929. /// I.e. startsentinel precedes n.
  930. /// Also update the size as a prelude to handling the right endpoint.
  931. ///
  932. /// Update size of a view not already handled and whose right endpoint precedes n.
  933. /// </summary>
  934. /// <param name="removed">The number of nodes left of n to be removed</param>
  935. /// <param name="n"></param>
  936. internal void updateViewSizesAndCounts(int removed, Node n)
  937. {
  938. if (viewCount > 0)
  939. {
  940. Position endpoint;
  941. while (leftEndIndex < viewCount && ((endpoint = leftEnds[leftEndIndex]).Endpoint.prev.precedes(n)))
  942. {
  943. LinkedList<T> view = endpoint.View;
  944. view.offset = view.offset - removed; //TODO: fix use of offset
  945. view.size += removed;
  946. leftEndIndex++;
  947. }
  948. while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Endpoint.precedes(n))
  949. {
  950. LinkedList<T> view = endpoint.View;
  951. view.size -= removed;
  952. rightEndIndex++;
  953. }
  954. }
  955. }
  956. /// <summary>
  957. /// To be called with n being the first not-to-be-removed node after a (stretch of) node(s) to be removed.
  958. ///
  959. /// It will update the startsentinel of views (that have not been handled before and)
  960. /// whose startsentinel precedes n, i.e. is to be deleted.
  961. ///
  962. /// It will update the endsentinel of views (...) whose endsentinel precedes n, i.e. is to be deleted.
  963. ///
  964. /// PROBLEM: DOESNT WORK AS ORIGINALLY ADVERTISED. WE MUST DO THIS BEFORE WE ACTUALLY REMOVE THE NODES. WHEN THE
  965. /// NODES HAVE BEEN REMOVED, THE precedes METHOD WILL NOT WORK!
  966. /// </summary>
  967. /// <param name="n"></param>
  968. /// <param name="newstart"></param>
  969. /// <param name="newend"></param>
  970. internal void updateSentinels(Node n, Node newstart, Node newend)
  971. {
  972. if (viewCount > 0)
  973. {
  974. Position endpoint;
  975. while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Endpoint.prev.precedes(n))
  976. {
  977. LinkedList<T> view = endpoint.View;
  978. view.startsentinel = newstart;
  979. leftEndIndex2++;
  980. }
  981. while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Endpoint.next.precedes(n))
  982. {
  983. LinkedList<T> view = endpoint.View;
  984. view.endsentinel = newend;
  985. rightEndIndex2++;
  986. }
  987. }
  988. }
  989. #else
  990. /// <summary>
  991. /// This is to be called with realindex pointing to the first node to be removed after a (stretch of) node that was not removed
  992. /// </summary>
  993. /// <param name="removed"></param>
  994. /// <param name="realindex"></param>
  995. internal void skipEndpoints(int removed, int realindex)
  996. {
  997. if (viewCount > 0)
  998. {
  999. Position endpoint;
  1000. while (leftEndIndex < viewCount && (endpoint = leftEnds[leftEndIndex]).Index <= realindex)
  1001. {
  1002. LinkedList<T> view = endpoint.View;
  1003. view.offset = view.offset - removed;
  1004. view.size += removed;
  1005. leftEndIndex++;
  1006. }
  1007. while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Index < realindex)
  1008. {
  1009. LinkedList<T> view = endpoint.View;
  1010. view.size -= removed;
  1011. rightEndIndex++;
  1012. }
  1013. }
  1014. if (viewCount > 0)
  1015. {
  1016. Position endpoint;
  1017. while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Index <= realindex)
  1018. leftEndIndex2++;
  1019. while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Index < realindex - 1)
  1020. rightEndIndex2++;
  1021. }
  1022. }
  1023. internal void updateViewSizesAndCounts(int removed, int realindex)
  1024. {
  1025. if (viewCount > 0)
  1026. {
  1027. Position endpoint;
  1028. while (leftEndIndex < viewCount && (endpoint = leftEnds[leftEndIndex]).Index <= realindex)
  1029. {
  1030. LinkedList<T> view = endpoint.View;
  1031. view.offset = view.Offset - removed;
  1032. view.size += removed;
  1033. leftEndIndex++;
  1034. }
  1035. while (rightEndIndex < viewCount && (endpoint = rightEnds[rightEndIndex]).Index < realindex)
  1036. {
  1037. LinkedList<T> view = endpoint.View;
  1038. view.size -= removed;
  1039. rightEndIndex++;
  1040. }
  1041. }
  1042. }
  1043. internal void updateSentinels(int realindex, Node newstart, Node newend)
  1044. {
  1045. if (viewCount > 0)
  1046. {
  1047. Position endpoint;
  1048. while (leftEndIndex2 < viewCount && (endpoint = leftEnds[leftEndIndex2]).Index <= realindex)
  1049. {
  1050. LinkedList<T> view = endpoint.View;
  1051. view.startsentinel = newstart;
  1052. leftEndIndex2++;
  1053. }
  1054. while (rightEndIndex2 < viewCount && (endpoint = rightEnds[rightEndIndex2]).Index < realindex - 1)
  1055. {
  1056. LinkedList<T> view = endpoint.View;
  1057. view.endsentinel = newend;
  1058. rightEndIndex2++;
  1059. }
  1060. }
  1061. }
  1062. #endif
  1063. }
  1064. #endregion
  1065. #region Range nested class
  1066. class Range : DirectedCollectionValueBase<T>, IDirectedCollectionValue<T>
  1067. {
  1068. int start, count, rangestamp;
  1069. Node startnode, endnode;
  1070. LinkedList<T> list;
  1071. bool forwards;
  1072. internal Range(LinkedList<T> list, int start, int count, bool forwards)
  1073. {
  1074. this.list = list; this.rangestamp = list.underlying != null ? list.underlying.stamp : list.stamp;
  1075. this.start = start; this.count = count; this.forwards = forwards;
  1076. if (count > 0)
  1077. {
  1078. startnode = list.get(start);
  1079. endnode = list.get(start + count - 1);
  1080. }
  1081. }
  1082. public override bool IsEmpty { get { list.modifycheck(rangestamp); return count == 0; } }
  1083. [Tested]
  1084. public override int Count { [Tested]get { list.modifycheck(rangestamp); return count; } }
  1085. public override Speed CountSpeed { get { list.modifycheck(rangestamp); return Speed.Constant; } }
  1086. public override T Choose()
  1087. {
  1088. list.modifycheck(rangestamp);
  1089. if (count > 0) return startnode.item;
  1090. throw new NoSuchItemException();
  1091. }
  1092. [Tested]
  1093. public override SCG.IEnumerator<T> GetEnumerator()
  1094. {
  1095. int togo = count;
  1096. list.modifycheck(rangestamp);
  1097. if (togo == 0)
  1098. yield break;
  1099. Node cursor = forwards ? startnode : endnode;
  1100. yield return cursor.item;
  1101. while (--togo > 0)
  1102. {
  1103. cursor = forwards ? cursor.next : cursor.prev;
  1104. list.modifycheck(rangestamp);
  1105. yield return cursor.item;
  1106. }
  1107. }
  1108. [Tested]
  1109. public override IDirectedCollectionValue<T> Backwards()
  1110. {
  1111. list.modifycheck(rangestamp);
  1112. Range b = (Range)MemberwiseClone();
  1113. b.forwards = !forwards;
  1114. return b;
  1115. }
  1116. [Tested]
  1117. IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); }
  1118. [Tested]
  1119. public override EnumerationDirection Direction
  1120. {
  1121. [Tested]
  1122. get
  1123. { return forwards ? EnumerationDirection.Forwards : EnumerationDirection.Backwards; }
  1124. }
  1125. }
  1126. #endregion
  1127. #region IDisposable Members
  1128. /// <summary>
  1129. /// Invalidate this list. If a view, just invalidate the view.
  1130. /// If not a view, invalidate the list and all views on it.
  1131. /// </summary>
  1132. public virtual void Dispose()
  1133. {
  1134. Dispose(false);
  1135. }
  1136. void Dispose(bool disposingUnderlying)
  1137. {
  1138. if (isValid)
  1139. {
  1140. if (underlying != null)
  1141. {
  1142. isValid = false;
  1143. if (!disposingUnderlying && views != null)
  1144. views.Remove(myWeakReference);
  1145. endsentinel = null;
  1146. startsentinel = null;
  1147. underlying = null;
  1148. views = null;
  1149. myWeakReference = null;
  1150. }
  1151. else
  1152. {
  1153. //isValid = false;
  1154. //endsentinel = null;
  1155. //startsentinel = null;
  1156. if (views != null)
  1157. foreach (LinkedList<T> view in views)
  1158. view.Dispose(true);
  1159. //views = null;
  1160. Clear();
  1161. }
  1162. }
  1163. }
  1164. #endregion IDisposable stuff
  1165. #region IList<T> Members
  1166. /// <summary>
  1167. /// </summary>
  1168. /// <exception cref="NoSuchItemException"> if this list is empty.</exception>
  1169. /// <value>The first item in this list.</value>
  1170. [Tested]
  1171. public virtual T First
  1172. {
  1173. [Tested]
  1174. get
  1175. {
  1176. validitycheck();
  1177. if (size == 0)
  1178. throw new NoSuchItemException();
  1179. return startsentinel.next.item;
  1180. }
  1181. }
  1182. /// <summary>
  1183. /// </summary>
  1184. /// <exception cref="NoSuchItemException"> if this list is empty.</exception>
  1185. /// <value>The last item in this list.</value>
  1186. [Tested]
  1187. public virtual T Last
  1188. {
  1189. [Tested]
  1190. get
  1191. {
  1192. validitycheck();
  1193. if (size == 0)
  1194. throw new NoSuchItemException();
  1195. return endsentinel.prev.item;
  1196. }
  1197. }
  1198. /// <summary>
  1199. /// Since <code>Add(T item)</code> always add at the end of the list,
  1200. /// this describes if list has FIFO or LIFO semantics.
  1201. /// </summary>
  1202. /// <value>True if the <code>Remove()</code> operation removes from the
  1203. /// start of the list, false if it removes from the end. THe default for a new linked list is true.</value>
  1204. [Tested]
  1205. public virtual bool FIFO
  1206. {
  1207. [Tested]
  1208. get { validitycheck(); return fIFO; }
  1209. [Tested]
  1210. set { updatecheck(); fIFO = value; }
  1211. }
  1212. /// <summary>
  1213. ///
  1214. /// </summary>
  1215. public virtual bool IsFixedSize
  1216. {
  1217. get { validitycheck(); return false; }
  1218. }
  1219. /// <summary>
  1220. /// On this list, this indexer is read/write.
  1221. /// <exception cref="IndexOutOfRangeException"/> if i is negative or
  1222. /// &gt;= the size of the collection.
  1223. /// </summary>
  1224. /// <value>The i'th item of this list.</value>
  1225. /// <param name="index">The index of the item to fetch or store.</param>
  1226. [Tested]
  1227. public virtual T this[int index]
  1228. {
  1229. [Tested]
  1230. get { validitycheck(); return get(index).item; }
  1231. [Tested]
  1232. set
  1233. {
  1234. updatecheck();
  1235. Node n = get(index);
  1236. //
  1237. T item = n.item;
  1238. #if HASHINDEX
  1239. if (itemequalityComparer.Equals(value, item))
  1240. {
  1241. n.item = value;
  1242. dict.Update(value, n);
  1243. }
  1244. else if (!dict.FindOrAdd(value, ref n))
  1245. {
  1246. dict.Remove(item);
  1247. n.item = value;
  1248. }
  1249. else
  1250. throw new ArgumentException("Item already in indexed list");
  1251. #else
  1252. n.item = value;
  1253. #endif
  1254. (underlying ?? this).raiseForSetThis(index, value, item);
  1255. }
  1256. }
  1257. /// <summary>
  1258. ///
  1259. /// </summary>
  1260. /// <value></value>
  1261. public virtual Speed IndexingSpeed { get { return Speed.Linear; } }
  1262. /// <summary>
  1263. /// Insert an item at a specific index location in this list.
  1264. /// <exception cref="IndexOutOfRangeException"/> if i is negative or
  1265. /// &gt; the size of the collection.</summary>
  1266. /// <param name="i">The index at which to insert.</param>
  1267. /// <param name="item">The item to insert.</param>
  1268. [Tested]
  1269. public virtual void Insert(int i, T item)
  1270. {
  1271. updatecheck();
  1272. insert(i, i == size ? endsentinel : get(i), item);
  1273. if (ActiveEvents != EventTypeEnum.None)
  1274. (underlying ?? this).raiseForInsert(i + Offset, item);
  1275. }
  1276. /// <summary>
  1277. /// Insert an item at the end of a compatible view, used as a pointer.
  1278. /// <para>The <code>pointer</code> must be a view on the same list as
  1279. /// <code>this</code> and the endpoitn of <code>pointer</code> must be
  1280. /// a valid insertion point of <code>this</code></para>
  1281. /// </summary>
  1282. /// <exception cref="IncompatibleViewException">If <code>pointer</code>
  1283. /// is not a view on the same list as <code>this</code></exception>
  1284. /// <exception cref="IndexOutOfRangeException"><b>??????</b> if the endpoint of
  1285. /// <code>pointer</code> is not inside <code>this</code></exception>
  1286. /// <exception cref="DuplicateNotAllowedException"> if the list has
  1287. /// <code>AllowsDuplicates==false</code> and the item is
  1288. /// already in the list.</exception>
  1289. /// <param name="pointer"></param>
  1290. /// <param name="item"></param>
  1291. public void Insert(IList<T> pointer, T item)
  1292. {
  1293. updatecheck();
  1294. if ((pointer == null) || ((pointer.Underlying ?? pointer) != (underlying ?? this)))
  1295. throw new IncompatibleViewException();
  1296. #warning INEFFICIENT
  1297. //TODO: make this efficient (the whole point of the method:
  1298. //Do NOT use Insert, but insert the node at pointer.endsentinel, checking
  1299. //via the ordering that this is a valid insertion point
  1300. Insert(pointer.Offset + pointer.Count - Offset, item);
  1301. }
  1302. /// <summary>
  1303. /// Insert into this list all items from an enumerable collection starting
  1304. /// at a particular index.
  1305. /// <exception cref="IndexOutOfRangeException"/> if i is negative or
  1306. /// &gt; the size of the collection.
  1307. /// </summary>
  1308. /// <param name="i">Index to start inserting at</param>
  1309. /// <param name="items">Items to insert</param>
  1310. /// <typeparam name="U"></typeparam>
  1311. [Tested]
  1312. public virtual void InsertAll<U>(int i, SCG.IEnumerable<U> items) where U : T
  1313. {
  1314. insertAll(i, items, true);
  1315. }
  1316. void insertAll<U>(int i, SCG.IEnumerable<U> items, bool insertion) where U : T
  1317. {
  1318. updatecheck();
  1319. Node succ, node, pred;
  1320. int count = 0;
  1321. succ = i == size ? endsentinel : get(i);
  1322. pred = node = succ.prev;
  1323. #if HASHINDEX
  1324. TagGroup taggroup = null;
  1325. int taglimit = 0, thetag = 0;
  1326. taggroup = gettaggroup(node, succ, out thetag, out taglimit);
  1327. try
  1328. {
  1329. foreach (T item in items)
  1330. {
  1331. Node tmp = new Node(item, node, null);
  1332. if (!dict.FindOrAdd(item, ref tmp))
  1333. {
  1334. tmp.tag = thetag < taglimit ? ++thetag : thetag;
  1335. tmp.taggroup = taggroup;
  1336. node.next = tmp;
  1337. count++;
  1338. node = tmp;
  1339. }
  1340. else
  1341. throw new DuplicateNotAllowedException("Item already in indexed list");
  1342. }
  1343. }
  1344. finally
  1345. {
  1346. if (count != 0)
  1347. {
  1348. taggroup.count += count;
  1349. if (taggroup != pred.taggroup)
  1350. taggroup.first = pred.next;
  1351. if (taggroup != succ.taggroup)
  1352. taggroup.last = node;
  1353. succ.prev = node;
  1354. node.next = succ;
  1355. if (node.tag == node.prev.tag)
  1356. splittaggroup(taggroup);
  1357. size += count;
  1358. if (underlying != null)
  1359. underlying.size += count;
  1360. fixViewsAfterInsert(succ, pred, count, 0);
  1361. raiseForInsertAll(pred, i, count, insertion);
  1362. }
  1363. }
  1364. #else
  1365. foreach (T item in items)
  1366. {
  1367. Node tmp = new Node(item, node, null);
  1368. node.next = tmp;
  1369. count++;
  1370. node = tmp;
  1371. }
  1372. if (count == 0)
  1373. return;
  1374. succ.prev = node;
  1375. node.next = succ;
  1376. size += count;
  1377. if (underlying != null)
  1378. underlying.size += count;
  1379. if (count > 0)
  1380. {
  1381. fixViewsAfterInsert(succ, pred, count, offset + i);
  1382. raiseForInsertAll(pred, i, count, insertion);
  1383. }
  1384. #endif
  1385. }
  1386. private void raiseForInsertAll(Node node, int i, int added, bool insertion)
  1387. {
  1388. if (ActiveEvents != 0)
  1389. {
  1390. int index = Offset + i;
  1391. if ((ActiveEvents & (EventTypeEnum.Added | EventTypeEnum.Inserted)) != 0)
  1392. for (int j = index; j < index + added; j++)
  1393. {
  1394. #warning must we check stamps here?
  1395. node = node.next;
  1396. T item = node.item;
  1397. if (insertion) raiseItemInserted(item, j);
  1398. raiseItemsAdded(item, 1);
  1399. }
  1400. raiseCollectionChanged();
  1401. }
  1402. }
  1403. /// <summary>
  1404. /// Insert an item at the front of this list.
  1405. /// </summary>
  1406. /// <param name="item">The item to insert.</param>
  1407. [Tested]
  1408. public virtual void InsertFirst(T item)
  1409. {
  1410. updatecheck();
  1411. insert(0, startsentinel.next, item);
  1412. if (ActiveEvents != EventTypeEnum.None)
  1413. (underlying ?? this).raiseForInsert(0 + Offset, item);
  1414. }
  1415. /// <summary>
  1416. /// Insert an item at the back of this list.
  1417. /// </summary>
  1418. /// <param name="item">The item to insert.</param>
  1419. [Tested]
  1420. public virtual void InsertLast(T item)
  1421. {
  1422. updatecheck();
  1423. insert(size, endsentinel, item);
  1424. if (ActiveEvents != EventTypeEnum.None)
  1425. (underlying ?? this).raiseForInsert(size - 1 + Offset, item);
  1426. }
  1427. /// <summary>
  1428. /// Create a new list consisting of the results of mapping all items of this
  1429. /// list.
  1430. /// </summary>
  1431. /// <param name="mapper">The delegate defining the map.</param>
  1432. /// <returns>The new list.</returns>
  1433. [Tested]
  1434. public IList<V> Map<V>(Fun<T, V> mapper)
  1435. {
  1436. validitycheck();
  1437. LinkedList<V> retval = new LinkedList<V>();
  1438. return map<V>(mapper, retval);
  1439. }
  1440. /// <summary>
  1441. /// Create a new list consisting of the results of mapping all items of this
  1442. /// list. The new list will use a specified equalityComparer for the item type.
  1443. /// </summary>
  1444. /// <typeparam name="V">The type of items of the new list</typeparam>
  1445. /// <param name="mapper">The delegate defining the map.</param>
  1446. /// <param name="equalityComparer">The equalityComparer to use for the new list</param>
  1447. /// <returns>The new list.</returns>
  1448. public IList<V> Map<V>(Fun<T, V> mapper, SCG.IEqualityComparer<V> equalityComparer)
  1449. {
  1450. validitycheck();
  1451. LinkedList<V> retval = new LinkedList<V>(equalityComparer);
  1452. return map<V>(mapper, retval);
  1453. }
  1454. private IList<V> map<V>(Fun<T, V> mapper, LinkedList<V> retval)
  1455. {
  1456. if (size == 0)
  1457. return retval;
  1458. int stamp = this.stamp;
  1459. Node cursor = startsentinel.next;
  1460. LinkedList<V>.Node mcursor = retval.startsentinel;
  1461. #if HASHINDEX
  1462. double tagdelta = int.MaxValue / (size + 1.0);
  1463. int count = 1;
  1464. LinkedList<V>.TagGroup taggroup = null;
  1465. taggroup = new LinkedList<V>.TagGroup();
  1466. retval.taggroups = 1;
  1467. taggroup.count = size;
  1468. #endif
  1469. while (cursor != endsentinel)
  1470. {
  1471. V v = mapper(cursor.item);
  1472. modifycheck(stamp);
  1473. mcursor.next = new LinkedList<V>.Node(v, mcursor, null);
  1474. cursor = cursor.next;
  1475. mcursor = mcursor.next;
  1476. #if HASHINDEX
  1477. retval.dict.Add(v, mcursor);
  1478. mcursor.taggroup = taggroup;
  1479. mcursor.tag = (int)(tagdelta * count++);
  1480. #endif
  1481. }
  1482. #if HASHINDEX
  1483. taggroup.first = retval.startsentinel.next;
  1484. taggroup.last = mcursor;
  1485. #endif
  1486. retval.endsentinel.prev = mcursor;
  1487. mcursor.next = retval.endsentinel;
  1488. retval.size = size;
  1489. return retval;
  1490. }
  1491. /// <summary>
  1492. /// Remove one item from the list: from the front if <code>FIFO</code>
  1493. /// is true, else from the back.
  1494. /// <exception cref="NoSuchItemException"/> if this list is empty.
  1495. /// </summary>
  1496. /// <returns>The removed item.</returns>
  1497. [Tested]
  1498. public virtual T Remove()
  1499. {
  1500. updatecheck();
  1501. if (size == 0)
  1502. throw new NoSuchItemException("List is empty");
  1503. T item = fIFO ? remove(startsentinel.next, 0) : remove(endsentinel.prev, size - 1);
  1504. #if HASHINDEX
  1505. dict.Remove(item);
  1506. #endif
  1507. (underlying ?? this).raiseForRemove(item);
  1508. return item;
  1509. }
  1510. /// <summary>
  1511. /// Remove one item from the front of the list.
  1512. /// <exception cref="NoSuchItemException"/> if this list is empty.
  1513. /// </summary>
  1514. /// <returns>The removed item.</returns>
  1515. [Tested]
  1516. public virtual T RemoveFirst()
  1517. {
  1518. updatecheck();
  1519. if (size == 0)
  1520. throw new NoSuchItemException("List is empty");
  1521. T item = remove(startsentinel.next, 0);
  1522. #if HASHINDEX
  1523. dict.Remove(item);
  1524. #endif
  1525. if (ActiveEvents != EventTypeEnum.None)
  1526. (underlying ?? this).raiseForRemoveAt(Offset, item);
  1527. return item;
  1528. }
  1529. /// <summary>
  1530. /// Remove one item from the back of the list.
  1531. /// <exception cref="NoSuchItemException"/> if this list is empty.
  1532. /// </summary>
  1533. /// <returns>The removed item.</returns>
  1534. [Tested]
  1535. public virtual T RemoveLast()
  1536. {
  1537. updatecheck();
  1538. if (size == 0)
  1539. throw new NoSuchItemException("List is empty");
  1540. T item = remove(endsentinel.prev, size - 1);
  1541. #if HASHINDEX
  1542. dict.Remove(item);
  1543. #endif
  1544. if (ActiveEvents != EventTypeEnum.None)
  1545. (underlying ?? this).raiseForRemoveAt(size + Offset, item);
  1546. return item;
  1547. }
  1548. /// <summary>
  1549. /// Create a list view on this list.
  1550. /// </summary>
  1551. /// <exception cref="ArgumentOutOfRangeException"> if the start or count is negative</exception>
  1552. /// <exception cref="ArgumentException"> if the range does not fit within list.</exception>
  1553. /// <param name="start">The index in this list of the start of the view.</param>
  1554. /// <param name="count">The size of the view.</param>
  1555. /// <returns>The new list view.</returns>
  1556. [Tested]
  1557. public virtual IList<T> View(int start, int count)
  1558. {
  1559. checkRange(start, count);
  1560. validitycheck();
  1561. if (views == null)
  1562. views = new WeakViewList<LinkedList<T>>();
  1563. LinkedList<T> retval = (LinkedList<T>)MemberwiseClone();
  1564. retval.underlying = underlying != null ? underlying : this;
  1565. retval.offset = offset + start;
  1566. retval.size = count;
  1567. getPair(start - 1, start + count, out retval.startsentinel, out retval.endsentinel,
  1568. new int[] { -1, size }, new Node[] { startsentinel, endsentinel });
  1569. //retval.startsentinel = start == 0 ? startsentinel : get(start - 1);
  1570. //retval.endsentinel = start + count == size ? endsentinel : get(start + count);
  1571. //TODO: for the purpose of Dispose, we need to retain a ref to the node
  1572. retval.myWeakReference = views.Add(retval);
  1573. return retval;
  1574. }
  1575. /// <summary>
  1576. /// Create a list view on this list containing the (first) occurrence of a particular item.
  1577. /// </summary>
  1578. /// <exception cref="ArgumentException"> if the item is not in this list.</exception>
  1579. /// <param name="item">The item to find.</param>
  1580. /// <returns>The new list view.</returns>
  1581. public virtual IList<T> ViewOf(T item)
  1582. {
  1583. #if HASHINDEX
  1584. Node n;
  1585. validitycheck();
  1586. if (!contains(item, out n))
  1587. return null;
  1588. LinkedList<T> retval = (LinkedList<T>)MemberwiseClone();
  1589. retval.underlying = underlying != null ? underlying : this;
  1590. retval.offset = null;
  1591. retval.startsentinel = n.prev;
  1592. retval.endsentinel = n.next;
  1593. retval.size = 1;
  1594. return retval;
  1595. #else
  1596. int index = 0;
  1597. Node n = startsentinel.next;
  1598. if (!find(item, ref n, ref index))
  1599. return null;
  1600. //TODO: optimize with getpair!
  1601. return View(index, 1);
  1602. #endif
  1603. }
  1604. /// <summary>
  1605. /// Create a list view on this list containing the last occurrence of a particular item.
  1606. /// <exception cref="ArgumentException"/> if the item is not in this list.
  1607. /// </summary>
  1608. /// <param name="item">The item to find.</param>
  1609. /// <returns>The new list view.</returns>
  1610. public virtual IList<T> LastViewOf(T item)
  1611. {
  1612. #if HASHINDEX
  1613. return ViewOf(item);
  1614. #else
  1615. int index = size - 1;
  1616. Node n = endsentinel.prev;
  1617. if (!dnif(item, ref n, ref index))
  1618. return null;
  1619. return View(index, 1);
  1620. #endif
  1621. }
  1622. /// <summary>
  1623. /// Null if this list is not a view.
  1624. /// </summary>
  1625. /// <value>Underlying list for view.</value>
  1626. [Tested]
  1627. public virtual IList<T> Underlying { [Tested]get { validitycheck(); return underlying; } }
  1628. /// <summary>
  1629. ///
  1630. /// </summary>
  1631. /// <value></value>
  1632. public virtual bool IsValid { get { return isValid; } }
  1633. /// <summary>
  1634. /// </summary>
  1635. /// <value>Offset for this list view or 0 for a underlying list.</value>
  1636. [Tested]
  1637. public virtual int Offset
  1638. {
  1639. [Tested]
  1640. get
  1641. {
  1642. validitycheck();
  1643. #if HASHINDEX
  1644. if (offset == null && underlying != null)
  1645. {
  1646. //TODO: search from both ends simultaneously!
  1647. Node n = underlying.startsentinel;
  1648. int i = 0;
  1649. while (n != startsentinel) { n = n.next; i++; }
  1650. offset = i;
  1651. }
  1652. #endif
  1653. return (int)offset;
  1654. }
  1655. }
  1656. /// <summary>
  1657. /// Slide this list view along the underlying list.
  1658. /// </summary>
  1659. /// <exception cref="NotAViewException"> if this list is not a view.</exception>
  1660. /// <exception cref="ArgumentOutOfRangeException"> if the operation
  1661. /// would bring either end of the view outside the underlying list.</exception>
  1662. /// <param name="offset">The signed amount to slide: positive to slide
  1663. /// towards the end.</param>
  1664. [Tested]
  1665. public IList<T> Slide(int offset)
  1666. {
  1667. if (!TrySlide(offset, size))
  1668. throw new ArgumentOutOfRangeException();
  1669. return this;
  1670. }
  1671. //TODO: more test cases
  1672. /// <summary>
  1673. /// Slide this list view along the underlying list, perhaps changing its size.
  1674. /// </summary>
  1675. /// <exception cref="NotAViewException"> if this list is not a view.</exception>
  1676. /// <exception cref="ArgumentOutOfRangeException"> if the operation
  1677. /// would bring either end of the view outside the underlying list.</exception>
  1678. /// <param name="offset">The signed amount to slide: positive to slide
  1679. /// towards the end.</param>
  1680. /// <param name="size">The new size of the view.</param>
  1681. public IList<T> Slide(int offset, int size)
  1682. {
  1683. if (!TrySlide(offset, size))
  1684. throw new ArgumentOutOfRangeException();
  1685. return this;
  1686. }
  1687. /// <summary>
  1688. ///
  1689. /// </summary>
  1690. /// <param name="offset"></param>
  1691. /// <returns></returns>
  1692. public virtual bool TrySlide(int offset) { return TrySlide(offset, size); }
  1693. /// <summary>
  1694. ///
  1695. /// </summary>
  1696. /// <param name="offset"></param>
  1697. /// <param name="size"></param>
  1698. /// <returns></returns>
  1699. public virtual bool TrySlide(int offset, int size)
  1700. {
  1701. updatecheck();
  1702. if (underlying == null)
  1703. throw new NotAViewException("List not a view");
  1704. #pragma warning disable 472
  1705. if (this.offset == null) //Note: only possible with HASHINDEX
  1706. #pragma warning restore 472
  1707. {
  1708. #pragma warning disable 162
  1709. try
  1710. {
  1711. getPair(offset - 1, offset + size, out startsentinel, out endsentinel,
  1712. new int[] { -1, this.size }, new Node[] { startsentinel, endsentinel });
  1713. //TODO: maybe-update offset field
  1714. }
  1715. catch (NullReferenceException)
  1716. {
  1717. return false;
  1718. }
  1719. #pragma warning restore 162
  1720. }
  1721. else
  1722. {
  1723. if (offset + this.offset < 0 || offset + this.offset + size > underlying.size)
  1724. return false;
  1725. int oldoffset = (int)(this.offset);
  1726. getPair(offset - 1, offset + size, out startsentinel, out endsentinel,
  1727. new int[] { -oldoffset - 1, -1, this.size, underlying.size - oldoffset },
  1728. new Node[] { underlying.startsentinel, startsentinel, endsentinel, underlying.endsentinel });
  1729. }
  1730. this.size = size;
  1731. this.offset += offset;
  1732. return true;
  1733. }
  1734. //TODO: improve the complexity of the implementation
  1735. /// <summary>
  1736. ///
  1737. /// <para>Returns null if <code>otherView</code> is strictly to the left of this view</para>
  1738. /// </summary>
  1739. /// <param name="otherView"></param>
  1740. /// <exception cref="IncompatibleViewException">If otherView does not have the same underlying list as this</exception>
  1741. /// <returns></returns>
  1742. public virtual IList<T> Span(IList<T> otherView)
  1743. {
  1744. if ((otherView == null) || ((otherView.Underlying ?? otherView) != (underlying ?? this)))
  1745. throw new IncompatibleViewException();
  1746. if (otherView.Offset + otherView.Count - Offset < 0)
  1747. return null;
  1748. return (underlying ?? this).View(Offset, otherView.Offset + otherView.Count - Offset);
  1749. }
  1750. //Question: should we swap items or move nodes around?
  1751. //The first seems much more efficient unless the items are value types
  1752. //with a large memory footprint.
  1753. //(Swapping will do count*3/2 T assignments, linking around will do
  1754. // 4*count ref assignments; note that ref assignments are more expensive
  1755. //than copying non-ref bits)
  1756. /// <summary>
  1757. /// Reverse the list so the items are in the opposite sequence order.
  1758. /// </summary>
  1759. [Tested]
  1760. public virtual void Reverse()
  1761. {
  1762. updatecheck();
  1763. if (size == 0)
  1764. return;
  1765. Position[] positions = null;
  1766. int poslow = 0, poshigh = 0;
  1767. if (views != null)
  1768. {
  1769. CircularQueue<Position> _positions = null;
  1770. foreach (LinkedList<T> view in views)
  1771. {
  1772. if (view != this)
  1773. {
  1774. switch (viewPosition(view))
  1775. {
  1776. case MutualViewPosition.ContainedIn:
  1777. (_positions ?? (_positions = new CircularQueue<Position>())).Enqueue(new Position(view, true));
  1778. _positions.Enqueue(new Position(view, false));
  1779. break;
  1780. case MutualViewPosition.Overlapping:
  1781. view.Dispose();
  1782. break;
  1783. case MutualViewPosition.Contains:
  1784. case MutualViewPosition.NonOverlapping:
  1785. break;
  1786. }
  1787. }
  1788. }
  1789. if (_positions != null)
  1790. {
  1791. positions = _positions.ToArray();
  1792. Sorting.IntroSort<Position>(positions, 0, positions.Length, PositionComparer.Default);
  1793. poshigh = positions.Length - 1;
  1794. }
  1795. }
  1796. Node a = get(0), b = get(size - 1);
  1797. for (int i = 0; i < size / 2; i++)
  1798. {
  1799. T swap;
  1800. swap = a.item; a.item = b.item; b.item = swap;
  1801. #if HASHINDEX
  1802. dict[a.item] = a; dict[b.item] = b;
  1803. #endif
  1804. if (positions != null)
  1805. mirrorViewSentinelsForReverse(positions, ref poslow, ref poshigh, a, b, i);
  1806. a = a.next; b = b.prev;
  1807. }
  1808. if (positions != null && size % 2 != 0)
  1809. mirrorViewSentinelsForReverse(positions, ref poslow, ref poshigh, a, b, size / 2);
  1810. (underlying ?? this).raiseCollectionChanged();
  1811. }
  1812. private void mirrorViewSentinelsForReverse(Position[] positions, ref int poslow, ref int poshigh, Node a, Node b, int i)
  1813. {
  1814. #if HASHINDEX
  1815. int? aindex = offset + i, bindex = offset + size - 1 - i;
  1816. #else
  1817. int aindex = offset + i, bindex = offset + size - 1 - i;
  1818. #endif
  1819. Position pos;
  1820. #if HASHINDEX
  1821. while (poslow <= poshigh && (pos = positions[poslow]).Endpoint == a)
  1822. #else
  1823. while (poslow <= poshigh && (pos = positions[poslow]).Index == aindex)
  1824. #endif
  1825. {
  1826. //TODO: Note: in the case og hashed linked list, if this.offset == null, but pos.View.offset!=null
  1827. //we may at this point compute this.offset and non-null values of aindex and bindex
  1828. if (pos.Left)
  1829. pos.View.endsentinel = b.next;
  1830. else
  1831. {
  1832. pos.View.startsentinel = b.prev;
  1833. pos.View.offset = bindex;
  1834. }
  1835. poslow++;
  1836. }
  1837. #if HASHINDEX
  1838. while (poslow < poshigh && (pos = positions[poshigh]).Endpoint == b)
  1839. #else
  1840. while (poslow < poshigh && (pos = positions[poshigh]).Index == bindex)
  1841. #endif
  1842. {
  1843. if (pos.Left)
  1844. pos.View.endsentinel = a.next;
  1845. else
  1846. {
  1847. pos.View.startsentinel = a.prev;
  1848. pos.View.offset = aindex;
  1849. }
  1850. poshigh--;
  1851. }
  1852. }
  1853. /// <summary>
  1854. /// Check if this list is sorted according to the default sorting order
  1855. /// for the item type T, as defined by the <see cref="T:C5.Comparer`1"/> class
  1856. /// </summary>
  1857. /// <exception cref="NotComparableException">if T is not comparable</exception>
  1858. /// <returns>True if the list is sorted, else false.</returns>
  1859. public bool IsSorted() { return IsSorted(Comparer<T>.Default); }
  1860. /// <summary>
  1861. /// Check if this list is sorted according to a specific sorting order.
  1862. /// </summary>
  1863. /// <param name="c">The comparer defining the sorting order.</param>
  1864. /// <returns>True if the list is sorted, else false.</returns>
  1865. [Tested]
  1866. public virtual bool IsSorted(SCG.IComparer<T> c)
  1867. {
  1868. validitycheck();
  1869. if (size <= 1)
  1870. return true;
  1871. Node node = startsentinel.next;
  1872. T prevItem = node.item;
  1873. node = node.next;
  1874. while (node != endsentinel)
  1875. {
  1876. if (c.Compare(prevItem, node.item) > 0)
  1877. return false;
  1878. else
  1879. {
  1880. prevItem = node.item;
  1881. node = node.next;
  1882. }
  1883. }
  1884. return true;
  1885. }
  1886. /// <summary>
  1887. /// Sort the items of the list according to the default sorting order
  1888. /// for the item type T, as defined by the Comparer[T] class.
  1889. /// (<see cref="T:C5.Comparer`1"/>).
  1890. /// The sorting is stable.
  1891. /// </summary>
  1892. /// <exception cref="InvalidOperationException">if T is not comparable</exception>
  1893. public virtual void Sort() { Sort(Comparer<T>.Default); }
  1894. // Sort the linked list using mergesort
  1895. /// <summary>
  1896. /// Sort the items of the list according to a specific sorting order.
  1897. /// The sorting is stable.
  1898. /// </summary>
  1899. /// <param name="c">The comparer defining the sorting order.</param>
  1900. [Tested]
  1901. public virtual void Sort(SCG.IComparer<T> c)
  1902. {
  1903. updatecheck();
  1904. if (size == 0)
  1905. return;
  1906. disposeOverlappingViews(false);
  1907. #if HASHINDEX
  1908. if (underlying != null)
  1909. {
  1910. Node cursor = startsentinel.next;
  1911. while (cursor != endsentinel)
  1912. {
  1913. cursor.taggroup.count--;
  1914. cursor = cursor.next;
  1915. }
  1916. }
  1917. #endif
  1918. // Build a linked list of non-empty runs.
  1919. // The prev field in first node of a run points to next run's first node
  1920. Node runTail = startsentinel.next;
  1921. Node prevNode = startsentinel.next;
  1922. endsentinel.prev.next = null;
  1923. while (prevNode != null)
  1924. {
  1925. Node node = prevNode.next;
  1926. while (node != null && c.Compare(prevNode.item, node.item) <= 0)
  1927. {
  1928. prevNode = node;
  1929. node = prevNode.next;
  1930. }
  1931. // Completed a run; prevNode is the last node of that run
  1932. prevNode.next = null; // Finish the run
  1933. runTail.prev = node; // Link it into the chain of runs
  1934. runTail = node;
  1935. if (c.Compare(endsentinel.prev.item, prevNode.item) <= 0)
  1936. endsentinel.prev = prevNode; // Update last pointer to point to largest
  1937. prevNode = node; // Start a new run
  1938. }
  1939. // Repeatedly merge runs two and two, until only one run remains
  1940. while (startsentinel.next.prev != null)
  1941. {
  1942. Node run = startsentinel.next;
  1943. Node newRunTail = null;
  1944. while (run != null && run.prev != null)
  1945. { // At least two runs, merge
  1946. Node nextRun = run.prev.prev;
  1947. Node newrun = mergeRuns(run, run.prev, c);
  1948. if (newRunTail != null)
  1949. newRunTail.prev = newrun;
  1950. else
  1951. startsentinel.next = newrun;
  1952. newRunTail = newrun;
  1953. run = nextRun;
  1954. }
  1955. if (run != null) // Add the last run, if any
  1956. newRunTail.prev = run;
  1957. }
  1958. endsentinel.prev.next = endsentinel;
  1959. startsentinel.next.prev = startsentinel;
  1960. //assert invariant();
  1961. //assert isSorted();
  1962. #if HASHINDEX
  1963. {
  1964. Node cursor = startsentinel.next, end = endsentinel;
  1965. int tag, taglimit;
  1966. TagGroup t = gettaggroup(startsentinel, endsentinel, out tag, out taglimit);
  1967. int tagdelta = taglimit / (size + 1) - tag / (size + 1);
  1968. tagdelta = tagdelta == 0 ? 1 : tagdelta;
  1969. if (underlying == null)
  1970. taggroups = 1;
  1971. while (cursor != end)
  1972. {
  1973. tag = tag + tagdelta > taglimit ? taglimit : tag + tagdelta;
  1974. cursor.tag = tag;
  1975. t.count++;
  1976. cursor.taggroup = t;
  1977. cursor = cursor.next;
  1978. }
  1979. if (t != startsentinel.taggroup)
  1980. t.first = startsentinel.next;
  1981. if (t != endsentinel.taggroup)
  1982. t.last = endsentinel.prev;
  1983. if (tag == taglimit)
  1984. splittaggroup(t);
  1985. }
  1986. #endif
  1987. (underlying ?? this).raiseCollectionChanged();
  1988. }
  1989. private static Node mergeRuns(Node run1, Node run2, SCG.IComparer<T> c)
  1990. {
  1991. //assert run1 != null && run2 != null;
  1992. Node prev;
  1993. bool prev1; // is prev from run1?
  1994. if (c.Compare(run1.item, run2.item) <= 0)
  1995. {
  1996. prev = run1;
  1997. prev1 = true;
  1998. run1 = run1.next;
  1999. }
  2000. else
  2001. {
  2002. prev = run2;
  2003. prev1 = false;
  2004. run2 = run2.next;
  2005. }
  2006. Node start = prev;
  2007. //assert start != null;
  2008. start.prev = null;
  2009. while (run1 != null && run2 != null)
  2010. {
  2011. if (prev1)
  2012. {
  2013. //assert prev.next == run1;
  2014. //Comparable run2item = (Comparable)run2.item;
  2015. while (run1 != null && c.Compare(run2.item, run1.item) >= 0)
  2016. {
  2017. prev = run1;
  2018. run1 = prev.next;
  2019. }
  2020. if (run1 != null)
  2021. { // prev.item <= run2.item < run1.item; insert run2
  2022. prev.next = run2;
  2023. run2.prev = prev;
  2024. prev = run2;
  2025. run2 = prev.next;
  2026. prev1 = false;
  2027. }
  2028. }
  2029. else
  2030. {
  2031. //assert prev.next == run2;
  2032. //Comparable run1item = (Comparable)run1.item;
  2033. while (run2 != null && c.Compare(run1.item, run2.item) > 0)
  2034. {
  2035. prev = run2;
  2036. run2 = prev.next;
  2037. }
  2038. if (run2 != null)
  2039. { // prev.item < run1.item <= run2.item; insert run1
  2040. prev.next = run1;
  2041. run1.prev = prev;
  2042. prev = run1;
  2043. run1 = prev.next;
  2044. prev1 = true;
  2045. }
  2046. }
  2047. }
  2048. //assert !(run1 != null && prev1) && !(run2 != null && !prev1);
  2049. if (run1 != null)
  2050. { // last run2 < all of run1; attach run1 at end
  2051. prev.next = run1;
  2052. run1.prev = prev;
  2053. }
  2054. else if (run2 != null)
  2055. { // last run1
  2056. prev.next = run2;
  2057. run2.prev = prev;
  2058. }
  2059. return start;
  2060. }
  2061. /// <summary>
  2062. /// Randomly shuffle the items of this list.
  2063. /// <para>Will invalidate overlapping views???</para>
  2064. /// </summary>
  2065. public virtual void Shuffle() { Shuffle(new C5Random()); }
  2066. /// <summary>
  2067. /// Shuffle the items of this list according to a specific random source.
  2068. /// <para>Will invalidate overlapping views???</para>
  2069. /// </summary>
  2070. /// <param name="rnd">The random source.</param>
  2071. public virtual void Shuffle(Random rnd)
  2072. {
  2073. updatecheck();
  2074. if (size == 0)
  2075. return;
  2076. disposeOverlappingViews(false);
  2077. ArrayList<T> a = new ArrayList<T>();
  2078. a.AddAll(this);
  2079. a.Shuffle(rnd);
  2080. Node cursor = startsentinel.next;
  2081. int j = 0;
  2082. while (cursor != endsentinel)
  2083. {
  2084. cursor.item = a[j++];
  2085. #if HASHINDEX
  2086. dict[cursor.item] = cursor;
  2087. #endif
  2088. cursor = cursor.next;
  2089. }
  2090. (underlying ?? this).raiseCollectionChanged();
  2091. }
  2092. #endregion
  2093. #region IIndexed<T> Members
  2094. /// <summary>
  2095. /// <exception cref="IndexOutOfRangeException"/>.
  2096. /// </summary>
  2097. /// <value>The directed collection of items in a specific index interval.</value>
  2098. /// <param name="start">The low index of the interval (inclusive).</param>
  2099. /// <param name="count">The size of the range.</param>
  2100. [Tested]
  2101. public IDirectedCollectionValue<T> this[int start, int count]
  2102. {
  2103. [Tested]
  2104. get
  2105. {
  2106. validitycheck();
  2107. checkRange(start, count);
  2108. return new Range(this, start, count, true);
  2109. }
  2110. }
  2111. /// <summary>
  2112. /// Searches for an item in the list going forwrds from the start.
  2113. /// </summary>
  2114. /// <param name="item">Item to search for.</param>
  2115. /// <returns>Index of item from start.</returns>
  2116. [Tested]
  2117. public virtual int IndexOf(T item)
  2118. {
  2119. validitycheck();
  2120. Node node;
  2121. #if HASHINDEX
  2122. if (!dict.Find(item, out node) || !insideview(node))
  2123. return ~size;
  2124. #endif
  2125. node = startsentinel.next;
  2126. int index = 0;
  2127. if (find(item, ref node, ref index))
  2128. return index;
  2129. else
  2130. return ~size;
  2131. }
  2132. /// <summary>
  2133. /// Searches for an item in the list going backwords from the end.
  2134. /// </summary>
  2135. /// <param name="item">Item to search for.</param>
  2136. /// <returns>Index of of item from the end.</returns>
  2137. [Tested]
  2138. public virtual int LastIndexOf(T item)
  2139. {
  2140. #if HASHINDEX
  2141. return IndexOf(item);
  2142. #else
  2143. validitycheck();
  2144. Node node = endsentinel.prev;
  2145. int index = size - 1;
  2146. if (dnif(item, ref node, ref index))
  2147. return index;
  2148. else
  2149. return ~size;
  2150. #endif
  2151. }
  2152. /// <summary>
  2153. /// Remove the item at a specific position of the list.
  2154. /// <exception cref="IndexOutOfRangeException"/> if i is negative or
  2155. /// &gt;= the size of the collection.
  2156. /// </summary>
  2157. /// <param name="i">The index of the item to remove.</param>
  2158. /// <returns>The removed item.</returns>
  2159. [Tested]
  2160. public virtual T RemoveAt(int i)
  2161. {
  2162. updatecheck();
  2163. T retval = remove(get(i), i);
  2164. #if HASHINDEX
  2165. dict.Remove(retval);
  2166. #endif
  2167. if (ActiveEvents != EventTypeEnum.None)
  2168. (underlying ?? this).raiseForRemoveAt(Offset + i, retval);
  2169. return retval;
  2170. }
  2171. /// <summary>
  2172. /// Remove all items in an index interval.
  2173. /// <exception cref="IndexOutOfRangeException"/>???.
  2174. /// </summary>
  2175. /// <param name="start">The index of the first item to remove.</param>
  2176. /// <param name="count">The number of items to remove.</param>
  2177. [Tested]
  2178. public virtual void RemoveInterval(int start, int count)
  2179. {
  2180. #if HASHINDEX
  2181. updatecheck();
  2182. checkRange(start, count);
  2183. if (count == 0)
  2184. return;
  2185. View(start, count).Clear();
  2186. #else
  2187. //Note: this is really almost equaivalent to Clear on a view
  2188. updatecheck();
  2189. checkRange(start, count);
  2190. if (count == 0)
  2191. return;
  2192. //for small count: optimize
  2193. //use an optimal get(int i, int j, ref Node ni, ref Node nj)?
  2194. Node a = get(start), b = get(start + count - 1);
  2195. fixViewsBeforeRemove(start, count, a, b);
  2196. a.prev.next = b.next;
  2197. b.next.prev = a.prev;
  2198. if (underlying != null)
  2199. underlying.size -= count;
  2200. size -= count;
  2201. if (ActiveEvents != EventTypeEnum.None)
  2202. (underlying ?? this).raiseForRemoveInterval(start + Offset, count);
  2203. #endif
  2204. }
  2205. void raiseForRemoveInterval(int start, int count)
  2206. {
  2207. if (ActiveEvents != 0)
  2208. {
  2209. raiseCollectionCleared(size == 0, count, start);
  2210. raiseCollectionChanged();
  2211. }
  2212. }
  2213. #endregion
  2214. #region ISequenced<T> Members
  2215. /// <summary>
  2216. ///
  2217. /// </summary>
  2218. /// <returns></returns>
  2219. [Tested]
  2220. public override int GetSequencedHashCode() { validitycheck(); return base.GetSequencedHashCode(); }
  2221. /// <summary>
  2222. ///
  2223. /// </summary>
  2224. /// <param name="that"></param>
  2225. /// <returns></returns>
  2226. [Tested]
  2227. public override bool SequencedEquals(ISequenced<T> that) { validitycheck(); return base.SequencedEquals(that); }
  2228. #endregion
  2229. #region IDirectedCollection<T> Members
  2230. /// <summary>
  2231. /// Create a collection containing the same items as this collection, but
  2232. /// whose enumerator will enumerate the items backwards. The new collection
  2233. /// will become invalid if the original is modified. Method typicaly used as in
  2234. /// <code>foreach (T x in coll.Backwards()) {...}</code>
  2235. /// </summary>
  2236. /// <returns>The backwards collection.</returns>
  2237. [Tested]
  2238. public override IDirectedCollectionValue<T> Backwards()
  2239. { return this[0, size].Backwards(); }
  2240. #endregion
  2241. #region IDirectedEnumerable<T> Members
  2242. [Tested]
  2243. IDirectedEnumerable<T> IDirectedEnumerable<T>.Backwards() { return Backwards(); }
  2244. #endregion
  2245. #region IEditableCollection<T> Members
  2246. /// <summary>
  2247. /// The value is symbolic indicating the type of asymptotic complexity
  2248. /// in terms of the size of this collection (worst-case or amortized as
  2249. /// relevant).
  2250. /// </summary>
  2251. /// <value>Speed.Linear</value>
  2252. [Tested]
  2253. public virtual Speed ContainsSpeed
  2254. {
  2255. [Tested]
  2256. get
  2257. {
  2258. #if HASHINDEX
  2259. return Speed.Constant;
  2260. #else
  2261. return Speed.Linear;
  2262. #endif
  2263. }
  2264. }
  2265. /// <summary>
  2266. /// Performs a check for view validity before calling base.GetUnsequencedHashCode()
  2267. /// </summary>
  2268. /// <returns></returns>
  2269. [Tested]
  2270. public override int GetUnsequencedHashCode()
  2271. { validitycheck(); return base.GetUnsequencedHashCode(); }
  2272. /// <summary>
  2273. ///
  2274. /// </summary>
  2275. /// <param name="that"></param>
  2276. /// <returns></returns>
  2277. [Tested]
  2278. public override bool UnsequencedEquals(ICollection<T> that)
  2279. { validitycheck(); return base.UnsequencedEquals(that); }
  2280. /// <summary>
  2281. /// Check if this collection contains (an item equivalent to according to the
  2282. /// itemequalityComparer) a particular value.
  2283. /// </summary>
  2284. /// <param name="item">The value to check for.</param>
  2285. /// <returns>True if the items is in this collection.</returns>
  2286. [Tested]
  2287. public virtual bool Contains(T item)
  2288. {
  2289. validitycheck();
  2290. Node node;
  2291. return contains(item, out node);
  2292. }
  2293. /// <summary>
  2294. /// Check if this collection contains an item equivalent according to the
  2295. /// itemequalityComparer to a particular value. If so, return in the ref argument (a
  2296. /// binary copy of) the actual value found.
  2297. /// </summary>
  2298. /// <param name="item">The value to look for.</param>
  2299. /// <returns>True if the items is in this collection.</returns>
  2300. [Tested]
  2301. public virtual bool Find(ref T item)
  2302. {
  2303. validitycheck();
  2304. Node node;
  2305. if (contains(item, out node)) { item = node.item; return true; }
  2306. return false;
  2307. }
  2308. /// <summary>
  2309. /// Check if this collection contains an item equivalent according to the
  2310. /// itemequalityComparer to a particular value. If so, update the item in the collection
  2311. /// to with a binary copy of the supplied value. Will update a single item.
  2312. /// </summary>
  2313. /// <param name="item">Value to update.</param>
  2314. /// <returns>True if the item was found and hence updated.</returns>
  2315. [Tested]
  2316. public virtual bool Update(T item) { T olditem; return Update(item, out olditem); }
  2317. /// <summary>
  2318. ///
  2319. /// </summary>
  2320. /// <param name="item"></param>
  2321. /// <param name="olditem"></param>
  2322. /// <returns></returns>
  2323. public virtual bool Update(T item, out T olditem)
  2324. {
  2325. updatecheck();
  2326. Node node;
  2327. if (contains(item, out node))
  2328. {
  2329. olditem = node.item;
  2330. node.item = item;
  2331. #if HASHINDEX
  2332. //Avoid clinging onto a reference to olditem via dict!
  2333. dict.Update(item, node);
  2334. #endif
  2335. (underlying ?? this).raiseForUpdate(item, olditem);
  2336. return true;
  2337. }
  2338. olditem = default(T);
  2339. return false;
  2340. }
  2341. /// <summary>
  2342. /// Check if this collection contains an item equivalent according to the
  2343. /// itemequalityComparer to a particular value. If so, return in the ref argument (a
  2344. /// binary copy of) the actual value found. Else, add the item to the collection.
  2345. /// </summary>
  2346. /// <param name="item">The value to look for.</param>
  2347. /// <returns>True if the item was found (hence not added).</returns>
  2348. [Tested]
  2349. public virtual bool FindOrAdd(ref T item)
  2350. {
  2351. updatecheck();
  2352. #if HASHINDEX
  2353. //This is an extended myinsert:
  2354. Node node = new Node(item);
  2355. if (!dict.FindOrAdd(item, ref node))
  2356. {
  2357. insertNode(true, endsentinel, node);
  2358. (underlying ?? this).raiseForAdd(item);
  2359. return false;
  2360. }
  2361. if (!insideview(node))
  2362. throw new ArgumentException("Item alredy in indexed list but outside view");
  2363. item = node.item;
  2364. return true;
  2365. #else
  2366. if (Find(ref item))
  2367. return true;
  2368. Add(item);
  2369. return false;
  2370. #endif
  2371. }
  2372. /// <summary>
  2373. /// Check if this collection contains an item equivalent according to the
  2374. /// itemequalityComparer to a particular value. If so, update the item in the collection
  2375. /// to with a binary copy of the supplied value; else add the value to the collection.
  2376. /// </summary>
  2377. /// <param name="item">Value to add or update.</param>
  2378. /// <returns>True if the item was found and updated (hence not added).</returns>
  2379. [Tested]
  2380. public virtual bool UpdateOrAdd(T item) { T olditem; return UpdateOrAdd(item, out olditem); }
  2381. /// <summary>
  2382. ///
  2383. /// </summary>
  2384. /// <param name="item"></param>
  2385. /// <param name="olditem"></param>
  2386. /// <returns></returns>
  2387. public virtual bool UpdateOrAdd(T item, out T olditem)
  2388. {
  2389. updatecheck();
  2390. #if HASHINDEX
  2391. Node node = new Node(item);
  2392. //NOTE: it is hard to do this without double access to the dictionary
  2393. //in the update case
  2394. if (dict.FindOrAdd(item, ref node))
  2395. {
  2396. if (!insideview(node))
  2397. throw new ArgumentException("Item in indexed list but outside view");
  2398. olditem = node.item;
  2399. //Avoid clinging onto a reference to olditem via dict!
  2400. dict.Update(item, node);
  2401. node.item = item;
  2402. (underlying ?? this).raiseForUpdate(item, olditem);
  2403. return true;
  2404. }
  2405. insertNode(true, endsentinel, node);
  2406. (underlying ?? this).raiseForAdd(item);
  2407. #else
  2408. if (Update(item, out olditem))
  2409. return true;
  2410. Add(item);
  2411. #endif
  2412. olditem = default(T);
  2413. return false;
  2414. }
  2415. /// <summary>
  2416. /// Remove a particular item from this collection. Since the collection has bag
  2417. /// semantics only one copy equivalent to the supplied item is removed.
  2418. /// </summary>
  2419. /// <param name="item">The value to remove.</param>
  2420. /// <returns>True if the item was found (and removed).</returns>
  2421. [Tested]
  2422. public virtual bool Remove(T item)
  2423. {
  2424. updatecheck();
  2425. int i = 0;
  2426. Node node;
  2427. #if HASHINDEX
  2428. if (!dictremove(item, out node))
  2429. #else
  2430. node = fIFO ? startsentinel.next : endsentinel.prev;
  2431. if (!(fIFO ? find(item, ref node, ref i) : dnif(item, ref node, ref i)))
  2432. #endif
  2433. return false;
  2434. T removeditem = remove(node, i);
  2435. (underlying ?? this).raiseForRemove(removeditem);
  2436. return true;
  2437. }
  2438. /// <summary>
  2439. /// Remove a particular item from this collection if found (only one copy).
  2440. /// If an item was removed, report a binary copy of the actual item removed in
  2441. /// the argument.
  2442. /// </summary>
  2443. /// <param name="item">The value to remove on input.</param>
  2444. /// <param name="removeditem">The value removed.</param>
  2445. /// <returns>True if the item was found (and removed).</returns>
  2446. [Tested]
  2447. public virtual bool Remove(T item, out T removeditem)
  2448. {
  2449. updatecheck();
  2450. int i = 0;
  2451. Node node;
  2452. #if HASHINDEX
  2453. if (!dictremove(item, out node))
  2454. #else
  2455. node = fIFO ? startsentinel.next : endsentinel.prev;
  2456. if (!(fIFO ? find(item, ref node, ref i) : dnif(item, ref node, ref i)))
  2457. #endif
  2458. {
  2459. removeditem = default(T);
  2460. return false;
  2461. }
  2462. removeditem = node.item;
  2463. remove(node, i);
  2464. (underlying ?? this).raiseForRemove(removeditem);
  2465. return true;
  2466. }
  2467. /// <summary>
  2468. /// Remove all items in another collection from this one, taking multiplicities into account.
  2469. /// <para>Always removes from the front of the list.
  2470. /// </para>
  2471. /// <para>The asymptotic running time complexity of this method is <code>O(n+m+v*log(v))</code>,
  2472. /// where <code>n</code> is the size of this list, <code>m</code> is the size of the
  2473. /// <code>items</code> collection and <code>v</code> is the number of views.
  2474. /// The method will temporarily allocate memory of size <code>O(m+v)</code>.
  2475. /// </para>
  2476. /// </summary>
  2477. /// <typeparam name="U"></typeparam>
  2478. /// <param name="items">The items to remove.</param>
  2479. [Tested]
  2480. public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T
  2481. {
  2482. updatecheck();
  2483. if (size == 0)
  2484. return;
  2485. RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
  2486. bool mustFire = raiseHandler.MustFire;
  2487. #if HASHINDEX
  2488. Node node;
  2489. foreach (T item in items)
  2490. if (dictremove(item, out node))
  2491. {
  2492. if (mustFire)
  2493. raiseHandler.Remove(node.item);
  2494. remove(node, 118);
  2495. }
  2496. #else
  2497. HashBag<T> toremove = new HashBag<T>(itemequalityComparer);
  2498. toremove.AddAll(items);
  2499. ViewHandler viewHandler = new ViewHandler(this);
  2500. int index = 0, removed = 0, myoffset = Offset;
  2501. Node node = startsentinel.next;
  2502. while (node != endsentinel)
  2503. {
  2504. //pass by a stretch of nodes
  2505. while (node != endsentinel && !toremove.Contains(node.item))
  2506. {
  2507. node = node.next;
  2508. index++;
  2509. }
  2510. viewHandler.skipEndpoints(removed, myoffset + index);
  2511. //Remove a stretch of nodes
  2512. Node localend = node.prev; //Latest node not to be removed
  2513. while (node != endsentinel && toremove.Remove(node.item))
  2514. {
  2515. if (mustFire)
  2516. raiseHandler.Remove(node.item);
  2517. removed++;
  2518. node = node.next;
  2519. index++;
  2520. viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
  2521. }
  2522. viewHandler.updateSentinels(myoffset + index, localend, node);
  2523. localend.next = node;
  2524. node.prev = localend;
  2525. }
  2526. index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
  2527. viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
  2528. size -= removed;
  2529. if (underlying != null)
  2530. underlying.size -= removed;
  2531. #endif
  2532. raiseHandler.Raise();
  2533. }
  2534. /// <summary>
  2535. ///
  2536. /// </summary>
  2537. /// <param name="predicate"></param>
  2538. void RemoveAll(Fun<T, bool> predicate)
  2539. {
  2540. updatecheck();
  2541. if (size == 0)
  2542. return;
  2543. RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
  2544. bool mustFire = raiseHandler.MustFire;
  2545. #if HASHINDEX
  2546. {
  2547. Node n = startsentinel.next;
  2548. while (n != endsentinel)
  2549. {
  2550. bool removeIt = predicate(n.item);
  2551. updatecheck();
  2552. if (removeIt)
  2553. {
  2554. dict.Remove(n.item);
  2555. remove(n, 119);
  2556. if (mustFire)
  2557. raiseHandler.Remove(n.item);
  2558. }
  2559. n = n.next;
  2560. }
  2561. }
  2562. #else
  2563. ViewHandler viewHandler = new ViewHandler(this);
  2564. int index = 0, removed = 0, myoffset = Offset;
  2565. Node node = startsentinel.next;
  2566. while (node != endsentinel)
  2567. {
  2568. //pass by a stretch of nodes
  2569. while (node != endsentinel && !predicate(node.item))
  2570. {
  2571. updatecheck();
  2572. node = node.next;
  2573. index++;
  2574. }
  2575. updatecheck();
  2576. viewHandler.skipEndpoints(removed, myoffset + index);
  2577. //Remove a stretch of nodes
  2578. Node localend = node.prev; //Latest node not to be removed
  2579. while (node != endsentinel && predicate(node.item))
  2580. {
  2581. updatecheck();
  2582. if (mustFire)
  2583. raiseHandler.Remove(node.item);
  2584. removed++;
  2585. node = node.next;
  2586. index++;
  2587. viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
  2588. }
  2589. updatecheck();
  2590. viewHandler.updateSentinels(myoffset + index, localend, node);
  2591. localend.next = node;
  2592. node.prev = localend;
  2593. }
  2594. index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
  2595. viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
  2596. size -= removed;
  2597. if (underlying != null)
  2598. underlying.size -= removed;
  2599. #endif
  2600. raiseHandler.Raise();
  2601. }
  2602. /// <summary>
  2603. /// Remove all items from this collection.
  2604. /// </summary>
  2605. [Tested]
  2606. public virtual void Clear()
  2607. {
  2608. updatecheck();
  2609. if (size == 0)
  2610. return;
  2611. int oldsize = size;
  2612. #if HASHINDEX
  2613. if (underlying == null)
  2614. dict.Clear();
  2615. else
  2616. foreach (T item in this)
  2617. dict.Remove(item);
  2618. #endif
  2619. clear();
  2620. (underlying ?? this).raiseForRemoveInterval(Offset, oldsize);
  2621. }
  2622. void clear()
  2623. {
  2624. if (size == 0)
  2625. return;
  2626. #if HASHINDEX
  2627. //TODO: mix with tag maintenance to only run through list once?
  2628. ViewHandler viewHandler = new ViewHandler(this);
  2629. if (viewHandler.viewCount > 0)
  2630. {
  2631. int removed = 0;
  2632. Node n = startsentinel.next;
  2633. viewHandler.skipEndpoints(0, n);
  2634. while (n != endsentinel)
  2635. {
  2636. removed++;
  2637. n = n.next;
  2638. viewHandler.updateViewSizesAndCounts(removed, n);
  2639. }
  2640. viewHandler.updateSentinels(endsentinel, startsentinel, endsentinel);
  2641. if (underlying != null)
  2642. viewHandler.updateViewSizesAndCounts(removed, underlying.endsentinel);
  2643. }
  2644. #else
  2645. fixViewsBeforeRemove(Offset, size, startsentinel.next, endsentinel.prev);
  2646. #endif
  2647. #if HASHINDEX
  2648. if (underlying != null)
  2649. {
  2650. Node n = startsentinel.next;
  2651. while (n != endsentinel)
  2652. {
  2653. n.next.prev = startsentinel;
  2654. startsentinel.next = n.next;
  2655. removefromtaggroup(n);
  2656. n = n.next;
  2657. }
  2658. }
  2659. else
  2660. taggroups = 0;
  2661. #endif
  2662. endsentinel.prev = startsentinel;
  2663. startsentinel.next = endsentinel;
  2664. if (underlying != null)
  2665. underlying.size -= size;
  2666. size = 0;
  2667. }
  2668. /// <summary>
  2669. /// Remove all items not in some other collection from this one, taking multiplicities into account.
  2670. /// <para>The asymptotic running time complexity of this method is <code>O(n+m+v*log(v))</code>,
  2671. /// where <code>n</code> is the size of this collection, <code>m</code> is the size of the
  2672. /// <code>items</code> collection and <code>v</code> is the number of views.
  2673. /// The method will temporarily allocate memory of size <code>O(m+v)</code>. The stated complexitiy
  2674. /// holds under the assumption that the itemequalityComparer of this list is well-behaved.
  2675. /// </para>
  2676. /// </summary>
  2677. /// <typeparam name="U"></typeparam>
  2678. /// <param name="items">The items to retain.</param>
  2679. [Tested]
  2680. public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T
  2681. {
  2682. updatecheck();
  2683. if (size == 0)
  2684. return;
  2685. RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
  2686. bool mustFire = raiseHandler.MustFire;
  2687. #if HASHINDEX
  2688. /*if (underlying == null)
  2689. {
  2690. HashDictionary<T, Node> newdict = new HashDictionary<T, Node>(itemequalityComparer);
  2691. foreach (T item in items)
  2692. {
  2693. Node node;
  2694. if (dict.Remove(item, out node))
  2695. newdict.Add(item, node);
  2696. }
  2697. foreach (KeyValuePair<T, Node> pair in dict)
  2698. {
  2699. Node n = pair.Value;
  2700. fixViewsBeforeSingleRemove(n, 117);
  2701. Node p = n.prev, s = n.next; s.prev = p; p.next = s;
  2702. removefromtaggroup(n);
  2703. }
  2704. dict = newdict;
  2705. size = dict.Count;
  2706. //For a small number of items to retain it might be faster to
  2707. //iterate through the list and splice out the chunks not needed
  2708. }
  2709. else*/
  2710. {
  2711. HashSet<T> toremove = new HashSet<T>(itemequalityComparer);
  2712. foreach (T item in this)
  2713. toremove.Add(item);
  2714. foreach (T item in items)
  2715. toremove.Remove(item);
  2716. Node n = startsentinel.next;
  2717. while (n != endsentinel && toremove.Count > 0)
  2718. {
  2719. if (toremove.Contains(n.item))
  2720. {
  2721. dict.Remove(n.item);
  2722. remove(n, 119);
  2723. if (mustFire)
  2724. raiseHandler.Remove(n.item);
  2725. }
  2726. n = n.next;
  2727. }
  2728. }
  2729. #else
  2730. HashBag<T> toretain = new HashBag<T>(itemequalityComparer);
  2731. toretain.AddAll(items);
  2732. ViewHandler viewHandler = new ViewHandler(this);
  2733. int index = 0, removed = 0, myoffset = Offset;
  2734. Node node = startsentinel.next;
  2735. while (node != endsentinel)
  2736. {
  2737. //Skip a stretch of nodes
  2738. while (node != endsentinel && toretain.Remove(node.item))
  2739. {
  2740. node = node.next;
  2741. index++;
  2742. }
  2743. viewHandler.skipEndpoints(removed, myoffset + index);
  2744. //Remove a stretch of nodes
  2745. Node localend = node.prev; //Latest node not to be removed
  2746. while (node != endsentinel && !toretain.Contains(node.item))
  2747. {
  2748. if (mustFire)
  2749. raiseHandler.Remove(node.item);
  2750. removed++;
  2751. node = node.next;
  2752. index++;
  2753. viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
  2754. }
  2755. viewHandler.updateSentinels(myoffset + index, localend, node);
  2756. localend.next = node;
  2757. node.prev = localend;
  2758. }
  2759. index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
  2760. viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
  2761. size -= removed;
  2762. if (underlying != null)
  2763. underlying.size -= removed;
  2764. #endif
  2765. raiseHandler.Raise();
  2766. }
  2767. /// <summary>
  2768. ///
  2769. /// </summary>
  2770. /// <param name="predicate"></param>
  2771. void RetainAll(Fun<T, bool> predicate)
  2772. {
  2773. updatecheck();
  2774. if (size == 0)
  2775. return;
  2776. RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
  2777. bool mustFire = raiseHandler.MustFire;
  2778. #if HASHINDEX
  2779. {
  2780. Node n = startsentinel.next;
  2781. while (n != endsentinel)
  2782. {
  2783. bool removeIt = !predicate(n.item);
  2784. updatecheck();
  2785. if (removeIt)
  2786. {
  2787. dict.Remove(n.item);
  2788. remove(n, 119);
  2789. if (mustFire)
  2790. raiseHandler.Remove(n.item);
  2791. }
  2792. n = n.next;
  2793. }
  2794. }
  2795. #else
  2796. ViewHandler viewHandler = new ViewHandler(this);
  2797. int index = 0, removed = 0, myoffset = Offset;
  2798. Node node = startsentinel.next;
  2799. while (node != endsentinel)
  2800. {
  2801. //Skip a stretch of nodes
  2802. while (node != endsentinel && predicate(node.item))
  2803. {
  2804. updatecheck();
  2805. node = node.next;
  2806. index++;
  2807. }
  2808. updatecheck();
  2809. viewHandler.skipEndpoints(removed, myoffset + index);
  2810. //Remove a stretch of nodes
  2811. Node localend = node.prev; //Latest node not to be removed
  2812. while (node != endsentinel && !predicate(node.item))
  2813. {
  2814. updatecheck();
  2815. if (mustFire)
  2816. raiseHandler.Remove(node.item);
  2817. removed++;
  2818. node = node.next;
  2819. index++;
  2820. viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
  2821. }
  2822. updatecheck();
  2823. viewHandler.updateSentinels(myoffset + index, localend, node);
  2824. localend.next = node;
  2825. node.prev = localend;
  2826. }
  2827. index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
  2828. viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
  2829. size -= removed;
  2830. if (underlying != null)
  2831. underlying.size -= removed;
  2832. #endif
  2833. raiseHandler.Raise();
  2834. }
  2835. /// <summary>
  2836. /// Check if this collection contains all the values in another collection
  2837. /// with respect to multiplicities.
  2838. /// </summary>
  2839. /// <param name="items">The </param>
  2840. /// <typeparam name="U"></typeparam>
  2841. /// <returns>True if all values in <code>items</code>is in this collection.</returns>
  2842. [Tested]
  2843. public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T
  2844. {
  2845. validitycheck();
  2846. #if HASHINDEX
  2847. Node node;
  2848. foreach (T item in items)
  2849. if (!contains(item, out node))
  2850. return false;
  2851. return true;
  2852. #else
  2853. HashBag<T> tocheck = new HashBag<T>(itemequalityComparer);
  2854. tocheck.AddAll(items);
  2855. if (tocheck.Count > size)
  2856. return false;
  2857. Node node = startsentinel.next;
  2858. while (node != endsentinel)
  2859. {
  2860. tocheck.Remove(node.item);
  2861. node = node.next;
  2862. }
  2863. return tocheck.IsEmpty;
  2864. #endif
  2865. }
  2866. /// <summary>
  2867. /// Create a new list consisting of the items of this list satisfying a
  2868. /// certain predicate.
  2869. /// </summary>
  2870. /// <param name="filter">The filter delegate defining the predicate.</param>
  2871. /// <returns>The new list.</returns>
  2872. [Tested]
  2873. public IList<T> FindAll(Fun<T, bool> filter)
  2874. {
  2875. validitycheck();
  2876. int stamp = this.stamp;
  2877. LinkedList<T> retval = new LinkedList<T>();
  2878. Node cursor = startsentinel.next;
  2879. Node mcursor = retval.startsentinel;
  2880. #if HASHINDEX
  2881. double tagdelta = int.MaxValue / (size + 1.0);
  2882. int count = 1;
  2883. TagGroup taggroup = new TagGroup();
  2884. retval.taggroups = 1;
  2885. #endif
  2886. while (cursor != endsentinel)
  2887. {
  2888. bool found = filter(cursor.item);
  2889. modifycheck(stamp);
  2890. if (found)
  2891. {
  2892. mcursor.next = new Node(cursor.item, mcursor, null);
  2893. mcursor = mcursor.next;
  2894. retval.size++;
  2895. #if HASHINDEX
  2896. retval.dict.Add(cursor.item, mcursor);
  2897. mcursor.taggroup = taggroup;
  2898. mcursor.tag = (int)(tagdelta * count++);
  2899. #endif
  2900. }
  2901. cursor = cursor.next;
  2902. }
  2903. #if HASHINDEX
  2904. if (retval.size > 0)
  2905. {
  2906. taggroup.count = retval.size;
  2907. taggroup.first = retval.startsentinel.next;
  2908. taggroup.last = mcursor;
  2909. }
  2910. #endif
  2911. retval.endsentinel.prev = mcursor;
  2912. mcursor.next = retval.endsentinel;
  2913. return retval;
  2914. }
  2915. /// <summary>
  2916. /// Count the number of items of the collection equal to a particular value.
  2917. /// Returns 0 if and only if the value is not in the collection.
  2918. /// </summary>
  2919. /// <param name="item">The value to count.</param>
  2920. /// <returns>The number of copies found.</returns>
  2921. [Tested]
  2922. public virtual int ContainsCount(T item)
  2923. {
  2924. #if HASHINDEX
  2925. return Contains(item) ? 1 : 0;
  2926. #else
  2927. validitycheck();
  2928. int retval = 0;
  2929. Node node = startsentinel.next;
  2930. while (node != endsentinel)
  2931. {
  2932. if (itemequalityComparer.Equals(node.item, item))
  2933. retval++;
  2934. node = node.next;
  2935. }
  2936. return retval;
  2937. #endif
  2938. }
  2939. /// <summary>
  2940. ///
  2941. /// </summary>
  2942. /// <returns></returns>
  2943. public virtual ICollectionValue<T> UniqueItems()
  2944. {
  2945. #if HASHINDEX
  2946. return this;
  2947. #else
  2948. HashBag<T> hashbag = new HashBag<T>(itemequalityComparer);
  2949. hashbag.AddAll(this);
  2950. return hashbag.UniqueItems();
  2951. #endif
  2952. }
  2953. /// <summary>
  2954. ///
  2955. /// </summary>
  2956. /// <returns></returns>
  2957. public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities()
  2958. {
  2959. #if HASHINDEX
  2960. return new MultiplicityOne<T>(this);
  2961. #else
  2962. HashBag<T> hashbag = new HashBag<T>(itemequalityComparer);
  2963. hashbag.AddAll(this);
  2964. return hashbag.ItemMultiplicities();
  2965. #endif
  2966. }
  2967. /// <summary>
  2968. /// Remove all items equivalent to a given value.
  2969. /// <para>The asymptotic complexity of this method is <code>O(n+v*log(v))</code>,
  2970. /// where <code>n</code> is the size of the collection and <code>v</code>
  2971. /// is the number of views.
  2972. /// </para>
  2973. /// </summary>
  2974. /// <param name="item">The value to remove.</param>
  2975. [Tested]
  2976. public virtual void RemoveAllCopies(T item)
  2977. {
  2978. #if HASHINDEX
  2979. Remove(item);
  2980. #else
  2981. updatecheck();
  2982. if (size == 0)
  2983. return;
  2984. RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(underlying ?? this);
  2985. bool mustFire = raiseHandler.MustFire;
  2986. ViewHandler viewHandler = new ViewHandler(this);
  2987. int index = 0, removed = 0, myoffset = Offset;
  2988. //
  2989. Node node = startsentinel.next;
  2990. while (node != endsentinel)
  2991. {
  2992. //pass by a stretch of nodes
  2993. while (node != endsentinel && !itemequalityComparer.Equals(node.item, item))
  2994. {
  2995. node = node.next;
  2996. index++;
  2997. }
  2998. viewHandler.skipEndpoints(removed, myoffset + index);
  2999. //Remove a stretch of nodes
  3000. Node localend = node.prev; //Latest node not to be removed
  3001. while (node != endsentinel && itemequalityComparer.Equals(node.item, item))
  3002. {
  3003. if (mustFire)
  3004. raiseHandler.Remove(node.item);
  3005. removed++;
  3006. node = node.next;
  3007. index++;
  3008. viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
  3009. }
  3010. viewHandler.updateSentinels(myoffset + index, localend, node);
  3011. localend.next = node;
  3012. node.prev = localend;
  3013. }
  3014. index = underlying != null ? underlying.size + 1 - myoffset : size + 1 - myoffset;
  3015. viewHandler.updateViewSizesAndCounts(removed, myoffset + index);
  3016. size -= removed;
  3017. if (underlying != null)
  3018. underlying.size -= removed;
  3019. raiseHandler.Raise();
  3020. #endif
  3021. }
  3022. #endregion
  3023. #region ICollectionValue<T> Members
  3024. /// <summary>
  3025. ///
  3026. /// </summary>
  3027. /// <value>The number of items in this collection</value>
  3028. [Tested]
  3029. public override int Count { [Tested]get { validitycheck(); return size; } }
  3030. /// <summary>
  3031. /// Choose some item of this collection.
  3032. /// </summary>
  3033. /// <exception cref="NoSuchItemException">if collection is empty.</exception>
  3034. /// <returns></returns>
  3035. [Tested]
  3036. public override T Choose() { return First; }
  3037. /// <summary>
  3038. /// Create an enumerable, enumerating the items of this collection that satisfies
  3039. /// a certain condition.
  3040. /// </summary>
  3041. /// <param name="filter">The T->bool filter delegate defining the condition</param>
  3042. /// <returns>The filtered enumerable</returns>
  3043. public override SCG.IEnumerable<T> Filter(Fun<T, bool> filter) { validitycheck(); return base.Filter(filter); }
  3044. #endregion
  3045. #region IEnumerable<T> Members
  3046. /// <summary>
  3047. /// Create an enumerator for the collection
  3048. /// </summary>
  3049. /// <returns>The enumerator</returns>
  3050. [Tested]
  3051. public override SCG.IEnumerator<T> GetEnumerator()
  3052. {
  3053. validitycheck();
  3054. Node cursor = startsentinel.next;
  3055. int enumeratorstamp = underlying != null ? underlying.stamp : this.stamp;
  3056. while (cursor != endsentinel)
  3057. {
  3058. modifycheck(enumeratorstamp);
  3059. yield return cursor.item;
  3060. cursor = cursor.next;
  3061. }
  3062. }
  3063. #endregion
  3064. #region IExtensible<T> Members
  3065. /// <summary>
  3066. /// Add an item to this collection if possible.
  3067. /// </summary>
  3068. /// <param name="item">The item to add.</param>
  3069. /// <returns>True.</returns>
  3070. [Tested]
  3071. public virtual bool Add(T item)
  3072. {
  3073. updatecheck();
  3074. #if HASHINDEX
  3075. Node node = new Node(item);
  3076. if (!dict.FindOrAdd(item, ref node))
  3077. {
  3078. insertNode(true, endsentinel, node);
  3079. (underlying ?? this).raiseForAdd(item);
  3080. return true;
  3081. }
  3082. return false;
  3083. #else
  3084. insert(size, endsentinel, item);
  3085. (underlying ?? this).raiseForAdd(item);
  3086. return true;
  3087. #endif
  3088. }
  3089. /// <summary>
  3090. ///
  3091. /// </summary>
  3092. /// <value>True since this collection has bag semantics.</value>
  3093. [Tested]
  3094. public virtual bool AllowsDuplicates
  3095. {
  3096. [Tested]
  3097. get
  3098. {
  3099. #if HASHINDEX
  3100. return false;
  3101. #else
  3102. return true;
  3103. #endif
  3104. }
  3105. }
  3106. /// <summary>
  3107. /// By convention this is true for any collection with set semantics.
  3108. /// </summary>
  3109. /// <value>True if only one representative of a group of equal items
  3110. /// is kept in the collection together with the total count.</value>
  3111. public virtual bool DuplicatesByCounting
  3112. {
  3113. get
  3114. {
  3115. #if HASHINDEX
  3116. return true;
  3117. #else
  3118. return false;
  3119. #endif
  3120. }
  3121. }
  3122. /// <summary>
  3123. /// Add the elements from another collection with a more specialized item type
  3124. /// to this collection.
  3125. /// </summary>
  3126. /// <typeparam name="U">The type of items to add</typeparam>
  3127. /// <param name="items">The items to add</param>
  3128. [Tested]
  3129. public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T
  3130. {
  3131. #if HASHINDEX
  3132. updatecheck();
  3133. int added = 0;
  3134. Node pred = endsentinel.prev;
  3135. foreach (U item in items)
  3136. {
  3137. Node node = new Node(item);
  3138. if (!dict.FindOrAdd(item, ref node))
  3139. {
  3140. insertNode(false, endsentinel, node);
  3141. added++;
  3142. }
  3143. }
  3144. if (added > 0)
  3145. {
  3146. fixViewsAfterInsert(endsentinel, pred, added, 0);
  3147. raiseForInsertAll(pred, size - added, added, false);
  3148. }
  3149. #else
  3150. insertAll(size, items, false);
  3151. #endif
  3152. }
  3153. #endregion
  3154. #if HASHINDEX
  3155. #else
  3156. #region IStack<T> Members
  3157. /// <summary>
  3158. /// Push an item to the top of the stack.
  3159. /// </summary>
  3160. /// <param name="item">The item</param>
  3161. [Tested]
  3162. public void Push(T item)
  3163. {
  3164. InsertLast(item);
  3165. }
  3166. /// <summary>
  3167. /// Pop the item at the top of the stack from the stack.
  3168. /// </summary>
  3169. /// <returns>The popped item.</returns>
  3170. [Tested]
  3171. public T Pop()
  3172. {
  3173. return RemoveLast();
  3174. }
  3175. #endregion
  3176. #region IQueue<T> Members
  3177. /// <summary>
  3178. /// Enqueue an item at the back of the queue.
  3179. /// </summary>
  3180. /// <param name="item">The item</param>
  3181. [Tested]
  3182. public virtual void Enqueue(T item)
  3183. {
  3184. InsertLast(item);
  3185. }
  3186. /// <summary>
  3187. /// Dequeue an item from the front of the queue.
  3188. /// </summary>
  3189. /// <returns>The item</returns>
  3190. [Tested]
  3191. public virtual T Dequeue()
  3192. {
  3193. return RemoveFirst();
  3194. }
  3195. #endregion
  3196. #endif
  3197. #region Diagnostic
  3198. private bool checkViews()
  3199. {
  3200. if (underlying != null)
  3201. throw new InternalException(System.Reflection.MethodInfo.GetCurrentMethod() + " called on a view");
  3202. if (views == null)
  3203. return true;
  3204. bool retval = true;
  3205. Node[] nodes = new Node[size + 2];
  3206. int i = 0;
  3207. Node n = startsentinel;
  3208. while (n != null)
  3209. {
  3210. nodes[i++] = n;
  3211. n = n.next;
  3212. }
  3213. //Console.WriteLine("###");
  3214. foreach (LinkedList<T> view in views)
  3215. {
  3216. if (!view.isValid)
  3217. {
  3218. Console.WriteLine("Invalid view(hash {0}, offset {1}, size {2})",
  3219. view.GetHashCode(), view.offset, view.size);
  3220. retval = false;
  3221. continue;
  3222. }
  3223. if (view.Offset > size || view.Offset < 0)
  3224. {
  3225. Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), Offset > underlying.size ({2})",
  3226. view.GetHashCode(), view.offset, view.size, size);
  3227. retval = false;
  3228. }
  3229. else if (view.startsentinel != nodes[view.Offset])
  3230. {
  3231. Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), startsentinel {3} should be {4}",
  3232. view.GetHashCode(), view.offset, view.size,
  3233. view.startsentinel + " " + view.startsentinel.GetHashCode(),
  3234. nodes[view.Offset] + " " + nodes[view.Offset].GetHashCode());
  3235. retval = false;
  3236. }
  3237. if (view.Offset + view.size > size || view.Offset + view.size < 0)
  3238. {
  3239. Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), end index > underlying.size ({3})",
  3240. view.GetHashCode(), view.offset, view.size, size);
  3241. retval = false;
  3242. }
  3243. else if (view.endsentinel != nodes[view.Offset + view.size + 1])
  3244. {
  3245. Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), endsentinel {3} should be {4}",
  3246. view.GetHashCode(), view.offset, view.size,
  3247. view.endsentinel + " " + view.endsentinel.GetHashCode(),
  3248. nodes[view.Offset + view.size + 1] + " " + nodes[view.Offset + view.size + 1].GetHashCode());
  3249. retval = false;
  3250. }
  3251. if (view.views != views)
  3252. {
  3253. Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong views list {3} <> {4}",
  3254. view.GetHashCode(), view.offset, view.size, view.views.GetHashCode(), views.GetHashCode());
  3255. retval = false;
  3256. }
  3257. if (view.underlying != this)
  3258. {
  3259. Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong underlying {3} <> this {4}",
  3260. view.GetHashCode(), view.offset, view.size, view.underlying.GetHashCode(), GetHashCode());
  3261. retval = false;
  3262. }
  3263. if (view.stamp != stamp)
  3264. {
  3265. //Console.WriteLine("Bad view(hash {0}, offset {1}, size {2}), wrong stamp view:{2} underlying: {3}", view.GetHashCode(),view.offset, view.size, view.stamp, stamp);
  3266. //retval = false;
  3267. }
  3268. }
  3269. return retval;
  3270. }
  3271. string zeitem(Node node)
  3272. {
  3273. return node == null ? "(null node)" : node.item.ToString();
  3274. }
  3275. /// <summary>
  3276. /// Check the sanity of this list
  3277. /// </summary>
  3278. /// <returns>true if sane</returns>
  3279. [Tested]
  3280. public virtual bool Check()
  3281. {
  3282. bool retval = true;
  3283. /*if (underlying != null && underlying.stamp != stamp)
  3284. {
  3285. Console.WriteLine("underlying != null && underlying.stamp({0}) != stamp({1})", underlying.stamp, stamp);
  3286. retval = false;
  3287. }*/
  3288. if (underlying != null)
  3289. {
  3290. //TODO: check that this view is included in viewsEndpoints tree
  3291. return underlying.Check();
  3292. }
  3293. if (startsentinel == null)
  3294. {
  3295. Console.WriteLine("startsentinel == null");
  3296. retval = false;
  3297. }
  3298. if (endsentinel == null)
  3299. {
  3300. Console.WriteLine("endsentinel == null");
  3301. retval = false;
  3302. }
  3303. if (size == 0)
  3304. {
  3305. if (startsentinel != null && startsentinel.next != endsentinel)
  3306. {
  3307. Console.WriteLine("size == 0 but startsentinel.next != endsentinel");
  3308. retval = false;
  3309. }
  3310. if (endsentinel != null && endsentinel.prev != startsentinel)
  3311. {
  3312. Console.WriteLine("size == 0 but endsentinel.prev != startsentinel");
  3313. retval = false;
  3314. }
  3315. }
  3316. if (startsentinel == null)
  3317. {
  3318. Console.WriteLine("NULL startsentinel");
  3319. return retval;
  3320. }
  3321. int count = 0;
  3322. Node node = startsentinel.next, prev = startsentinel;
  3323. #if HASHINDEX
  3324. int taggroupsize = 0, oldtaggroupsize = losize + 1, seentaggroups = 0;
  3325. TagGroup oldtg = null;
  3326. if (underlying == null)
  3327. {
  3328. TagGroup tg = startsentinel.taggroup;
  3329. if (tg.count != 0 || tg.first != null || tg.last != null || tg.tag != int.MinValue)
  3330. {
  3331. Console.WriteLine("Bad startsentinel tag group: {0}", tg);
  3332. retval = false;
  3333. }
  3334. tg = endsentinel.taggroup;
  3335. if (tg.count != 0 || tg.first != null || tg.last != null || tg.tag != int.MaxValue)
  3336. {
  3337. Console.WriteLine("Bad endsentinel tag group: {0}", tg);
  3338. retval = false;
  3339. }
  3340. }
  3341. #endif
  3342. while (node != endsentinel)
  3343. {
  3344. count++;
  3345. if (node.prev != prev)
  3346. {
  3347. Console.WriteLine("Bad backpointer at node {0}", count);
  3348. retval = false;
  3349. }
  3350. #if HASHINDEX
  3351. if (underlying == null)
  3352. {
  3353. if (!node.prev.precedes(node))
  3354. {
  3355. Console.WriteLine("node.prev.tag ({0}, {1}) >= node.tag ({2}, {3}) at index={4} item={5} ", node.prev.taggroup.tag, node.prev.tag, node.taggroup.tag, node.tag, count, node.item);
  3356. retval = false;
  3357. }
  3358. if (node.taggroup != oldtg)
  3359. {
  3360. if (node.taggroup.first != node)
  3361. {
  3362. string ntfi = zeitem(node.taggroup.first);
  3363. Console.WriteLine("Bad first pointer in taggroup: node.taggroup.first.item ({0}), node.item ({1}) at index={2} item={3}", ntfi, node.item, count, node.item);
  3364. retval = false;
  3365. }
  3366. if (oldtg != null)
  3367. {
  3368. if (oldtg.count != taggroupsize)
  3369. {
  3370. Console.WriteLine("Bad taggroupsize: oldtg.count ({0}) != taggroupsize ({1}) at index={2} item={3}", oldtg.count, taggroupsize, count, node.item);
  3371. retval = false;
  3372. }
  3373. if (oldtaggroupsize <= losize && taggroupsize <= losize)
  3374. {
  3375. Console.WriteLine("Two small taggroups in a row: oldtaggroupsize ({0}), taggroupsize ({1}) at index={2} item={3}", oldtaggroupsize, taggroupsize, count, node.item);
  3376. retval = false;
  3377. }
  3378. if (node.taggroup.tag <= oldtg.tag)
  3379. {
  3380. Console.WriteLine("Taggroup tags not strictly increasing: oldtaggrouptag ({0}), taggrouptag ({1}) at index={2} item={3}", oldtg.tag, node.taggroup.tag, count, node.item);
  3381. retval = false;
  3382. }
  3383. if (oldtg.last != node.prev)
  3384. {
  3385. Console.WriteLine("Bad last pointer in taggroup: oldtg.last.item ({0}), node.prev.item ({1}) at index={2} item={3}", oldtg.last.item, node.prev.item, count, node.item);
  3386. retval = false;
  3387. }
  3388. oldtaggroupsize = taggroupsize;
  3389. }
  3390. seentaggroups++;
  3391. oldtg = node.taggroup;
  3392. taggroupsize = 1;
  3393. }
  3394. else
  3395. {
  3396. taggroupsize++;
  3397. }
  3398. }
  3399. #endif
  3400. prev = node;
  3401. node = node.next;
  3402. if (node == null)
  3403. {
  3404. Console.WriteLine("Null next pointer at node {0}", count);
  3405. return false;
  3406. }
  3407. }
  3408. #if HASHINDEX
  3409. if (underlying == null && size == 0 && taggroups != 0)
  3410. {
  3411. Console.WriteLine("Bad taggroups for empty list: size={0} taggroups={1}", size, taggroups);
  3412. retval = false;
  3413. }
  3414. if (underlying == null && size > 0)
  3415. {
  3416. oldtg = node.prev.taggroup;
  3417. if (oldtg != null)
  3418. {
  3419. if (oldtg.count != taggroupsize)
  3420. {
  3421. Console.WriteLine("Bad taggroupsize: oldtg.count ({0}) != taggroupsize ({1}) at index={2} item={3}", oldtg.count, taggroupsize, count, node.item);
  3422. retval = false;
  3423. }
  3424. if (oldtaggroupsize <= losize && taggroupsize <= losize)
  3425. {
  3426. Console.WriteLine("Two small taggroups in a row: oldtaggroupsize ({0}), taggroupsize ({1}) at index={2} item={3}", oldtaggroupsize, taggroupsize, count, node.item);
  3427. retval = false;
  3428. }
  3429. if (node.taggroup.tag <= oldtg.tag)
  3430. {
  3431. Console.WriteLine("Taggroup tags not strictly increasing: oldtaggrouptag ({0}), taggrouptag ({1}) at index={2} item={3}", oldtg.tag, node.taggroup.tag, count, node.item);
  3432. retval = false;
  3433. }
  3434. if (oldtg.last != node.prev)
  3435. {
  3436. Console.WriteLine("Bad last pointer in taggroup: oldtg.last.item ({0}), node.prev.item ({1}) at index={2} item={3}", zeitem(oldtg.last), zeitem(node.prev), count, node.item);
  3437. retval = false;
  3438. }
  3439. }
  3440. if (seentaggroups != taggroups)
  3441. {
  3442. Console.WriteLine("seentaggroups ({0}) != taggroups ({1}) (at size {2})", seentaggroups, taggroups, size);
  3443. retval = false;
  3444. }
  3445. }
  3446. #endif
  3447. if (count != size)
  3448. {
  3449. Console.WriteLine("size={0} but enumeration gives {1} nodes ", size, count);
  3450. retval = false;
  3451. }
  3452. retval = checkViews() && retval;
  3453. #if HASHINDEX
  3454. if (!retval)
  3455. return false;
  3456. if (underlying == null)
  3457. {
  3458. if (size != dict.Count)
  3459. {
  3460. Console.WriteLine("list.size ({0}) != dict.Count ({1})", size, dict.Count);
  3461. retval = false;
  3462. }
  3463. Node n = startsentinel.next, n2;
  3464. while (n != endsentinel)
  3465. {
  3466. if (!dict.Find(n.item, out n2))
  3467. {
  3468. Console.WriteLine("Item in list but not dict: {0}", n.item);
  3469. retval = false;
  3470. }
  3471. else if (n != n2)
  3472. {
  3473. Console.WriteLine("Wrong node in dict for item: {0}", n.item);
  3474. retval = false;
  3475. }
  3476. n = n.next;
  3477. }
  3478. }
  3479. #endif
  3480. return retval;
  3481. }
  3482. #endregion
  3483. #region ICloneable Members
  3484. /// <summary>
  3485. /// Make a shallow copy of this LinkedList.
  3486. /// </summary>
  3487. /// <returns></returns>
  3488. public virtual object Clone()
  3489. {
  3490. LinkedList<T> clone = new LinkedList<T>(itemequalityComparer);
  3491. clone.AddAll(this);
  3492. return clone;
  3493. }
  3494. #endregion
  3495. #region System.Collections.Generic.IList<T> Members
  3496. void System.Collections.Generic.IList<T>.RemoveAt(int index)
  3497. {
  3498. RemoveAt(index);
  3499. }
  3500. void System.Collections.Generic.ICollection<T>.Add(T item)
  3501. {
  3502. Add(item);
  3503. }
  3504. #endregion
  3505. #region System.Collections.ICollection Members
  3506. bool System.Collections.ICollection.IsSynchronized
  3507. {
  3508. get { return false; }
  3509. }
  3510. [Obsolete]
  3511. Object System.Collections.ICollection.SyncRoot
  3512. {
  3513. // Presumably safe to use the startsentinel (of type Node, always != null) as SyncRoot
  3514. // since the class Node is private.
  3515. get { return underlying != null ? ((System.Collections.ICollection)underlying).SyncRoot : startsentinel; }
  3516. }
  3517. void System.Collections.ICollection.CopyTo(Array arr, int index)
  3518. {
  3519. if (index < 0 || index + Count > arr.Length)
  3520. throw new ArgumentOutOfRangeException();
  3521. foreach (T item in this)
  3522. arr.SetValue(item, index++);
  3523. }
  3524. #endregion
  3525. #region System.Collections.IList Members
  3526. Object System.Collections.IList.this[int index]
  3527. {
  3528. get { return this[index]; }
  3529. set { this[index] = (T)value; }
  3530. }
  3531. int System.Collections.IList.Add(Object o)
  3532. {
  3533. bool added = Add((T)o);
  3534. // What position to report if item not added? SC.IList.Add doesn't say
  3535. return added ? Count-1 : -1;
  3536. }
  3537. bool System.Collections.IList.Contains(Object o)
  3538. {
  3539. return Contains((T)o);
  3540. }
  3541. int System.Collections.IList.IndexOf(Object o)
  3542. {
  3543. return Math.Max(-1, IndexOf((T)o));
  3544. }
  3545. void System.Collections.IList.Insert(int index, Object o)
  3546. {
  3547. Insert(index, (T)o);
  3548. }
  3549. void System.Collections.IList.Remove(Object o)
  3550. {
  3551. Remove((T)o);
  3552. }
  3553. void System.Collections.IList.RemoveAt(int index)
  3554. {
  3555. RemoveAt(index);
  3556. }
  3557. #endregion
  3558. }
  3559. }