Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.ExtLibs/HeuristicLab.AvalonEdit/5.0.1/AvalonEdit-5.0.1/Utils/CompressingTreeList.cs @ 12062

Last change on this file since 12062 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 25.7 KB
Line 
1// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19using System;
20using System.Collections.Generic;
21using System.Diagnostics;
22using System.Text;
23
24namespace ICSharpCode.AvalonEdit.Utils
25{
26  /// <summary>
27  /// A IList{T} implementation that has efficient insertion and removal (in O(lg n) time)
28  /// and that saves memory by allocating only one node when a value is repeated in adjacent indices.
29  /// Based on this "compression", it also supports efficient InsertRange/SetRange/RemoveRange operations.
30  /// </summary>
31  /// <remarks>
32  /// Current memory usage: 5*IntPtr.Size + 12 + sizeof(T) per node.
33  /// Use this class only if lots of adjacent values are identical (can share one node).
34  /// </remarks>
35  [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix",
36                                                   Justification = "It's an IList<T> implementation")]
37  public sealed class CompressingTreeList<T> : IList<T>
38  {
39    // Further memory optimization: this tree could work without parent pointers. But that
40    // requires changing most of tree manipulating logic.
41    // Also possible is to remove the count field and calculate it as totalCount-left.totalCount-right.totalCount
42    // - but that would make tree manipulations more difficult to handle.
43   
44    #region Node definition
45    sealed class Node
46    {
47      internal Node left, right, parent;
48      internal bool color;
49      internal int count, totalCount;
50      internal T value;
51     
52      public Node(T value, int count)
53      {
54        this.value = value;
55        this.count = count;
56        this.totalCount = count;
57      }
58     
59      internal Node LeftMost {
60        get {
61          Node node = this;
62          while (node.left != null)
63            node = node.left;
64          return node;
65        }
66      }
67     
68      internal Node RightMost {
69        get {
70          Node node = this;
71          while (node.right != null)
72            node = node.right;
73          return node;
74        }
75      }
76     
77      /// <summary>
78      /// Gets the inorder predecessor of the node.
79      /// </summary>
80      internal Node Predecessor {
81        get {
82          if (left != null) {
83            return left.RightMost;
84          } else {
85            Node node = this;
86            Node oldNode;
87            do {
88              oldNode = node;
89              node = node.parent;
90              // go up until we are coming out of a right subtree
91            } while (node != null && node.left == oldNode);
92            return node;
93          }
94        }
95      }
96     
97      /// <summary>
98      /// Gets the inorder successor of the node.
99      /// </summary>
100      internal Node Successor {
101        get {
102          if (right != null) {
103            return right.LeftMost;
104          } else {
105            Node node = this;
106            Node oldNode;
107            do {
108              oldNode = node;
109              node = node.parent;
110              // go up until we are coming out of a left subtree
111            } while (node != null && node.right == oldNode);
112            return node;
113          }
114        }
115      }
116     
117      public override string ToString()
118      {
119        return "[TotalCount=" + totalCount + " Count=" + count + " Value=" + value + "]";
120      }
121    }
122    #endregion
123   
124    #region Fields and Constructor
125    readonly Func<T, T, bool> comparisonFunc;
126    Node root;
127   
128    /// <summary>
129    /// Creates a new CompressingTreeList instance.
130    /// </summary>
131    /// <param name="equalityComparer">The equality comparer used for comparing consequtive values.
132    /// A single node may be used to store the multiple values that are considered equal.</param>
133    public CompressingTreeList(IEqualityComparer<T> equalityComparer)
134    {
135      if (equalityComparer == null)
136        throw new ArgumentNullException("equalityComparer");
137      this.comparisonFunc = equalityComparer.Equals;
138    }
139   
140    /// <summary>
141    /// Creates a new CompressingTreeList instance.
142    /// </summary>
143    /// <param name="comparisonFunc">A function that checks two values for equality. If this
144    /// function returns true, a single node may be used to store the two values.</param>
145    public CompressingTreeList(Func<T, T, bool> comparisonFunc)
146    {
147      if (comparisonFunc == null)
148        throw new ArgumentNullException("comparisonFunc");
149      this.comparisonFunc = comparisonFunc;
150    }
151    #endregion
152   
153    #region InsertRange
154    /// <summary>
155    /// Inserts <paramref name="item"/> <paramref name="count"/> times at position
156    /// <paramref name="index"/>.
157    /// </summary>
158    public void InsertRange(int index, int count, T item)
159    {
160      if (index < 0 || index > this.Count)
161        throw new ArgumentOutOfRangeException("index", index, "Value must be between 0 and " + this.Count);
162      if (count < 0)
163        throw new ArgumentOutOfRangeException("count", count, "Value must not be negative");
164      if (count == 0)
165        return;
166      unchecked {
167        if (this.Count + count < 0)
168          throw new OverflowException("Cannot insert elements: total number of elements must not exceed int.MaxValue.");
169      }
170     
171      if (root == null) {
172        root = new Node(item, count);
173      } else {
174        Node n = GetNode(ref index);
175        // check if we can put the value into the node n:
176        if (comparisonFunc(n.value, item)) {
177          n.count += count;
178          UpdateAugmentedData(n);
179        } else if (index == n.count) {
180          // this can only happen when appending at the end
181          Debug.Assert(n == root.RightMost);
182          InsertAsRight(n, new Node(item, count));
183        } else if (index == 0) {
184          // insert before:
185          // maybe we can put the value in the previous node?
186          Node p = n.Predecessor;
187          if (p != null && comparisonFunc(p.value, item)) {
188            p.count += count;
189            UpdateAugmentedData(p);
190          } else {
191            InsertBefore(n, new Node(item, count));
192          }
193        } else {
194          Debug.Assert(index > 0 && index < n.count);
195          // insert in the middle:
196          // split n into a new node and n
197          n.count -= index;
198          InsertBefore(n, new Node(n.value, index));
199          // then insert the new item in between
200          InsertBefore(n, new Node(item, count));
201          UpdateAugmentedData(n);
202        }
203      }
204      CheckProperties();
205    }
206   
207    void InsertBefore(Node node, Node newNode)
208    {
209      if (node.left == null) {
210        InsertAsLeft(node, newNode);
211      } else {
212        InsertAsRight(node.left.RightMost, newNode);
213      }
214    }
215    #endregion
216   
217    #region RemoveRange
218    /// <summary>
219    /// Removes <paramref name="count"/> items starting at position
220    /// <paramref name="index"/>.
221    /// </summary>
222    public void RemoveRange(int index, int count)
223    {
224      if (index < 0 || index > this.Count)
225        throw new ArgumentOutOfRangeException("index", index, "Value must be between 0 and " + this.Count);
226      if (count < 0 || index + count > this.Count)
227        throw new ArgumentOutOfRangeException("count", count, "0 <= length, index(" + index + ")+count <= " + this.Count);
228      if (count == 0)
229        return;
230     
231      Node n = GetNode(ref index);
232      if (index + count < n.count) {
233        // just remove inside a single node
234        n.count -= count;
235        UpdateAugmentedData(n);
236      } else {
237        // keep only the part of n from 0 to index
238        Node firstNodeBeforeDeletedRange;
239        if (index > 0) {
240          count -= (n.count - index);
241          n.count = index;
242          UpdateAugmentedData(n);
243          firstNodeBeforeDeletedRange = n;
244          n = n.Successor;
245        } else {
246          Debug.Assert(index == 0);
247          firstNodeBeforeDeletedRange = n.Predecessor;
248        }
249        while (n != null && count >= n.count) {
250          count -= n.count;
251          Node s = n.Successor;
252          RemoveNode(n);
253          n = s;
254        }
255        if (count > 0) {
256          Debug.Assert(n != null && count < n.count);
257          n.count -= count;
258          UpdateAugmentedData(n);
259        }
260        if (n != null) {
261          Debug.Assert(n.Predecessor == firstNodeBeforeDeletedRange);
262          if (firstNodeBeforeDeletedRange != null && comparisonFunc(firstNodeBeforeDeletedRange.value, n.value)) {
263            firstNodeBeforeDeletedRange.count += n.count;
264            RemoveNode(n);
265            UpdateAugmentedData(firstNodeBeforeDeletedRange);
266          }
267        }
268      }
269     
270      CheckProperties();
271    }
272    #endregion
273   
274    #region SetRange
275    /// <summary>
276    /// Sets <paramref name="count"/> indices starting at <paramref name="index"/> to
277    /// <paramref name="item"/>
278    /// </summary>
279    public void SetRange(int index, int count, T item)
280    {
281      RemoveRange(index, count);
282      InsertRange(index, count, item);
283    }
284    #endregion
285   
286    #region GetNode
287    Node GetNode(ref int index)
288    {
289      Node node = root;
290      while (true) {
291        if (node.left != null && index < node.left.totalCount) {
292          node = node.left;
293        } else {
294          if (node.left != null) {
295            index -= node.left.totalCount;
296          }
297          if (index < node.count || node.right == null)
298            return node;
299          index -= node.count;
300          node = node.right;
301        }
302      }
303    }
304    #endregion
305   
306    #region UpdateAugmentedData
307    void UpdateAugmentedData(Node node)
308    {
309      int totalCount = node.count;
310      if (node.left != null) totalCount += node.left.totalCount;
311      if (node.right != null) totalCount += node.right.totalCount;
312      if (node.totalCount != totalCount) {
313        node.totalCount = totalCount;
314        if (node.parent != null)
315          UpdateAugmentedData(node.parent);
316      }
317    }
318    #endregion
319   
320    #region IList<T> implementation
321    /// <summary>
322    /// Gets or sets an item by index.
323    /// </summary>
324    public T this[int index] {
325      get {
326        if (index < 0 || index >= this.Count)
327          throw new ArgumentOutOfRangeException("index", index, "Value must be between 0 and " + (this.Count - 1));
328        return GetNode(ref index).value;
329      }
330      set {
331        RemoveAt(index);
332        Insert(index, value);
333      }
334    }
335   
336    /// <summary>
337    /// Gets the number of items in the list.
338    /// </summary>
339    public int Count {
340      get {
341        if (root != null)
342          return root.totalCount;
343        else
344          return 0;
345      }
346    }
347   
348    bool ICollection<T>.IsReadOnly {
349      get {
350        return false;
351      }
352    }
353   
354    /// <summary>
355    /// Gets the index of the specified <paramref name="item"/>.
356    /// </summary>
357    public int IndexOf(T item)
358    {
359      int index = 0;
360      if (root != null) {
361        Node n = root.LeftMost;
362        while (n != null) {
363          if (comparisonFunc(n.value, item))
364            return index;
365          index += n.count;
366          n = n.Successor;
367        }
368      }
369      Debug.Assert(index == this.Count);
370      return -1;
371    }
372   
373    /// <summary>
374    /// Gets the the first index so that all values from the result index to <paramref name="index"/>
375    /// are equal.
376    /// </summary>
377    public int GetStartOfRun(int index)
378    {
379      if (index < 0 || index >= this.Count)
380        throw new ArgumentOutOfRangeException("index", index, "Value must be between 0 and " + (this.Count - 1));
381      int indexInRun = index;
382      GetNode(ref indexInRun);
383      return index - indexInRun;
384    }
385
386    /// <summary>
387    /// Gets the first index after <paramref name="index"/> so that the value at the result index is not
388    /// equal to the value at <paramref name="index"/>.
389    /// That is, this method returns the exclusive end index of the run of equal values.
390    /// </summary>
391    public int GetEndOfRun(int index)
392    {
393      if (index < 0 || index >= this.Count)
394        throw new ArgumentOutOfRangeException("index", index, "Value must be between 0 and " + (this.Count - 1));
395      int indexInRun = index;
396      int runLength = GetNode(ref indexInRun).count;
397      return index - indexInRun + runLength;
398    }
399
400    /// <summary>
401    /// Gets the number of elements after <paramref name="index"/> that have the same value as each other.
402    /// </summary>
403    [Obsolete("This method may be confusing as it returns only the remaining run length after index. " +
404              "Use GetStartOfRun/GetEndOfRun instead.")]
405    public int GetRunLength(int index)
406    {
407      if (index < 0 || index >= this.Count)
408        throw new ArgumentOutOfRangeException("index", index, "Value must be between 0 and " + (this.Count - 1));
409      return GetNode(ref index).count - index;
410    }
411   
412    /// <summary>
413    /// Applies the conversion function to all elements in this CompressingTreeList.
414    /// </summary>
415    public void Transform(Func<T, T> converter)
416    {
417      if (root == null)
418        return;
419      Node prevNode = null;
420      for (Node n = root.LeftMost; n != null; n = n.Successor) {
421        n.value = converter(n.value);
422        if (prevNode != null && comparisonFunc(prevNode.value, n.value)) {
423          n.count += prevNode.count;
424          UpdateAugmentedData(n);
425          RemoveNode(prevNode);
426        }
427        prevNode = n;
428      }
429      CheckProperties();
430    }
431   
432    /// <summary>
433    /// Applies the conversion function to the elements in the specified range.
434    /// </summary>
435    public void TransformRange(int index, int length, Func<T, T> converter)
436    {
437      if (root == null)
438        return;
439      int endIndex = index + length;
440      int pos = index;
441      while (pos < endIndex) {
442        int endPos = Math.Min(endIndex, GetEndOfRun(pos));
443        T oldValue = this[pos];
444        T newValue = converter(oldValue);
445        SetRange(pos, endPos - pos, newValue);
446        pos = endPos;
447      }
448    }
449   
450    /// <summary>
451    /// Inserts the specified <paramref name="item"/> at <paramref name="index"/>
452    /// </summary>
453    public void Insert(int index, T item)
454    {
455      InsertRange(index, 1, item);
456    }
457   
458    /// <summary>
459    /// Removes one item at <paramref name="index"/>
460    /// </summary>
461    public void RemoveAt(int index)
462    {
463      RemoveRange(index, 1);
464    }
465   
466    /// <summary>
467    /// Adds the specified <paramref name="item"/> to the end of the list.
468    /// </summary>
469    public void Add(T item)
470    {
471      InsertRange(this.Count, 1, item);
472    }
473   
474    /// <summary>
475    /// Removes all items from this list.
476    /// </summary>
477    public void Clear()
478    {
479      root = null;
480    }
481   
482    /// <summary>
483    /// Gets whether this list contains the specified item.
484    /// </summary>
485    public bool Contains(T item)
486    {
487      return IndexOf(item) >= 0;
488    }
489   
490    /// <summary>
491    /// Copies all items in this list to the specified array.
492    /// </summary>
493    public void CopyTo(T[] array, int arrayIndex)
494    {
495      if (array == null)
496        throw new ArgumentNullException("array");
497      if (array.Length < this.Count)
498        throw new ArgumentException("The array is too small", "array");
499      if (arrayIndex < 0 || arrayIndex + this.Count > array.Length)
500        throw new ArgumentOutOfRangeException("arrayIndex", arrayIndex, "Value must be between 0 and " + (array.Length - this.Count));
501      foreach (T v in this) {
502        array[arrayIndex++] = v;
503      }
504    }
505   
506    /// <summary>
507    /// Removes the specified item from this list.
508    /// </summary>
509    public bool Remove(T item)
510    {
511      int index = IndexOf(item);
512      if (index >= 0) {
513        RemoveAt(index);
514        return true;
515      } else {
516        return false;
517      }
518    }
519    #endregion
520   
521    #region IEnumerable<T>
522    /// <summary>
523    /// Gets an enumerator for this list.
524    /// </summary>
525    public IEnumerator<T> GetEnumerator()
526    {
527      if (root != null) {
528        Node n = root.LeftMost;
529        while (n != null) {
530          for (int i = 0; i < n.count; i++) {
531            yield return n.value;
532          }
533          n = n.Successor;
534        }
535      }
536    }
537   
538    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
539    {
540      return GetEnumerator();
541    }
542    #endregion
543   
544    #region Red/Black Tree
545    internal const bool RED = true;
546    internal const bool BLACK = false;
547   
548    void InsertAsLeft(Node parentNode, Node newNode)
549    {
550      Debug.Assert(parentNode.left == null);
551      parentNode.left = newNode;
552      newNode.parent = parentNode;
553      newNode.color = RED;
554      UpdateAugmentedData(parentNode);
555      FixTreeOnInsert(newNode);
556    }
557   
558    void InsertAsRight(Node parentNode, Node newNode)
559    {
560      Debug.Assert(parentNode.right == null);
561      parentNode.right = newNode;
562      newNode.parent = parentNode;
563      newNode.color = RED;
564      UpdateAugmentedData(parentNode);
565      FixTreeOnInsert(newNode);
566    }
567   
568    void FixTreeOnInsert(Node node)
569    {
570      Debug.Assert(node != null);
571      Debug.Assert(node.color == RED);
572      Debug.Assert(node.left == null || node.left.color == BLACK);
573      Debug.Assert(node.right == null || node.right.color == BLACK);
574     
575      Node parentNode = node.parent;
576      if (parentNode == null) {
577        // we inserted in the root -> the node must be black
578        // since this is a root node, making the node black increments the number of black nodes
579        // on all paths by one, so it is still the same for all paths.
580        node.color = BLACK;
581        return;
582      }
583      if (parentNode.color == BLACK) {
584        // if the parent node where we inserted was black, our red node is placed correctly.
585        // since we inserted a red node, the number of black nodes on each path is unchanged
586        // -> the tree is still balanced
587        return;
588      }
589      // parentNode is red, so there is a conflict here!
590     
591      // because the root is black, parentNode is not the root -> there is a grandparent node
592      Node grandparentNode = parentNode.parent;
593      Node uncleNode = Sibling(parentNode);
594      if (uncleNode != null && uncleNode.color == RED) {
595        parentNode.color = BLACK;
596        uncleNode.color = BLACK;
597        grandparentNode.color = RED;
598        FixTreeOnInsert(grandparentNode);
599        return;
600      }
601      // now we know: parent is red but uncle is black
602      // First rotation:
603      if (node == parentNode.right && parentNode == grandparentNode.left) {
604        RotateLeft(parentNode);
605        node = node.left;
606      } else if (node == parentNode.left && parentNode == grandparentNode.right) {
607        RotateRight(parentNode);
608        node = node.right;
609      }
610      // because node might have changed, reassign variables:
611      parentNode = node.parent;
612      grandparentNode = parentNode.parent;
613     
614      // Now recolor a bit:
615      parentNode.color = BLACK;
616      grandparentNode.color = RED;
617      // Second rotation:
618      if (node == parentNode.left && parentNode == grandparentNode.left) {
619        RotateRight(grandparentNode);
620      } else {
621        // because of the first rotation, this is guaranteed:
622        Debug.Assert(node == parentNode.right && parentNode == grandparentNode.right);
623        RotateLeft(grandparentNode);
624      }
625    }
626   
627    void RemoveNode(Node removedNode)
628    {
629      if (removedNode.left != null && removedNode.right != null) {
630        // replace removedNode with it's in-order successor
631       
632        Node leftMost = removedNode.right.LeftMost;
633        RemoveNode(leftMost); // remove leftMost from its current location
634       
635        // and overwrite the removedNode with it
636        ReplaceNode(removedNode, leftMost);
637        leftMost.left = removedNode.left;
638        if (leftMost.left != null) leftMost.left.parent = leftMost;
639        leftMost.right = removedNode.right;
640        if (leftMost.right != null) leftMost.right.parent = leftMost;
641        leftMost.color = removedNode.color;
642       
643        UpdateAugmentedData(leftMost);
644        if (leftMost.parent != null) UpdateAugmentedData(leftMost.parent);
645        return;
646      }
647     
648      // now either removedNode.left or removedNode.right is null
649      // get the remaining child
650      Node parentNode = removedNode.parent;
651      Node childNode = removedNode.left ?? removedNode.right;
652      ReplaceNode(removedNode, childNode);
653      if (parentNode != null) UpdateAugmentedData(parentNode);
654      if (removedNode.color == BLACK) {
655        if (childNode != null && childNode.color == RED) {
656          childNode.color = BLACK;
657        } else {
658          FixTreeOnDelete(childNode, parentNode);
659        }
660      }
661    }
662   
663    void FixTreeOnDelete(Node node, Node parentNode)
664    {
665      Debug.Assert(node == null || node.parent == parentNode);
666      if (parentNode == null)
667        return;
668     
669      // warning: node may be null
670      Node sibling = Sibling(node, parentNode);
671      if (sibling.color == RED) {
672        parentNode.color = RED;
673        sibling.color = BLACK;
674        if (node == parentNode.left) {
675          RotateLeft(parentNode);
676        } else {
677          RotateRight(parentNode);
678        }
679       
680        sibling = Sibling(node, parentNode); // update value of sibling after rotation
681      }
682     
683      if (parentNode.color == BLACK
684          && sibling.color == BLACK
685          && GetColor(sibling.left) == BLACK
686          && GetColor(sibling.right) == BLACK)
687      {
688        sibling.color = RED;
689        FixTreeOnDelete(parentNode, parentNode.parent);
690        return;
691      }
692     
693      if (parentNode.color == RED
694          && sibling.color == BLACK
695          && GetColor(sibling.left) == BLACK
696          && GetColor(sibling.right) == BLACK)
697      {
698        sibling.color = RED;
699        parentNode.color = BLACK;
700        return;
701      }
702     
703      if (node == parentNode.left &&
704          sibling.color == BLACK &&
705          GetColor(sibling.left) == RED &&
706          GetColor(sibling.right) == BLACK)
707      {
708        sibling.color = RED;
709        sibling.left.color = BLACK;
710        RotateRight(sibling);
711      }
712      else if (node == parentNode.right &&
713               sibling.color == BLACK &&
714               GetColor(sibling.right) == RED &&
715               GetColor(sibling.left) == BLACK)
716      {
717        sibling.color = RED;
718        sibling.right.color = BLACK;
719        RotateLeft(sibling);
720      }
721      sibling = Sibling(node, parentNode); // update value of sibling after rotation
722     
723      sibling.color = parentNode.color;
724      parentNode.color = BLACK;
725      if (node == parentNode.left) {
726        if (sibling.right != null) {
727          Debug.Assert(sibling.right.color == RED);
728          sibling.right.color = BLACK;
729        }
730        RotateLeft(parentNode);
731      } else {
732        if (sibling.left != null) {
733          Debug.Assert(sibling.left.color == RED);
734          sibling.left.color = BLACK;
735        }
736        RotateRight(parentNode);
737      }
738    }
739   
740    void ReplaceNode(Node replacedNode, Node newNode)
741    {
742      if (replacedNode.parent == null) {
743        Debug.Assert(replacedNode == root);
744        root = newNode;
745      } else {
746        if (replacedNode.parent.left == replacedNode)
747          replacedNode.parent.left = newNode;
748        else
749          replacedNode.parent.right = newNode;
750      }
751      if (newNode != null) {
752        newNode.parent = replacedNode.parent;
753      }
754      replacedNode.parent = null;
755    }
756   
757    void RotateLeft(Node p)
758    {
759      // let q be p's right child
760      Node q = p.right;
761      Debug.Assert(q != null);
762      Debug.Assert(q.parent == p);
763      // set q to be the new root
764      ReplaceNode(p, q);
765     
766      // set p's right child to be q's left child
767      p.right = q.left;
768      if (p.right != null) p.right.parent = p;
769      // set q's left child to be p
770      q.left = p;
771      p.parent = q;
772      UpdateAugmentedData(p);
773      UpdateAugmentedData(q);
774    }
775   
776    void RotateRight(Node p)
777    {
778      // let q be p's left child
779      Node q = p.left;
780      Debug.Assert(q != null);
781      Debug.Assert(q.parent == p);
782      // set q to be the new root
783      ReplaceNode(p, q);
784     
785      // set p's left child to be q's right child
786      p.left = q.right;
787      if (p.left != null) p.left.parent = p;
788      // set q's right child to be p
789      q.right = p;
790      p.parent = q;
791      UpdateAugmentedData(p);
792      UpdateAugmentedData(q);
793    }
794   
795    static Node Sibling(Node node)
796    {
797      if (node == node.parent.left)
798        return node.parent.right;
799      else
800        return node.parent.left;
801    }
802   
803    static Node Sibling(Node node, Node parentNode)
804    {
805      Debug.Assert(node == null || node.parent == parentNode);
806      if (node == parentNode.left)
807        return parentNode.right;
808      else
809        return parentNode.left;
810    }
811   
812    static bool GetColor(Node node)
813    {
814      return node != null ? node.color : BLACK;
815    }
816    #endregion
817   
818    #region CheckProperties
819    [Conditional("DATACONSISTENCYTEST")]
820    internal void CheckProperties()
821    {
822      #if DEBUG
823      if (root != null) {
824        CheckProperties(root);
825       
826        // check red-black property:
827        int blackCount = -1;
828        CheckNodeProperties(root, null, RED, 0, ref blackCount);
829       
830        // ensure that the tree is compressed:
831        Node p = root.LeftMost;
832        Node n = p.Successor;
833        while (n != null) {
834          Debug.Assert(!comparisonFunc(p.value, n.value));
835          p = n;
836          n = p.Successor;
837        }
838      }
839      #endif
840    }
841   
842    #if DEBUG
843    void CheckProperties(Node node)
844    {
845      Debug.Assert(node.count > 0);
846      int totalCount = node.count;
847      if (node.left != null) {
848        CheckProperties(node.left);
849        totalCount += node.left.totalCount;
850      }
851      if (node.right != null) {
852        CheckProperties(node.right);
853        totalCount += node.right.totalCount;
854      }
855      Debug.Assert(node.totalCount == totalCount);
856    }
857   
858    /*
859    1. A node is either red or black.
860    2. The root is black.
861    3. All leaves are black. (The leaves are the NIL children.)
862    4. Both children of every red node are black. (So every red node must have a black parent.)
863    5. Every simple path from a node to a descendant leaf contains the same number of black nodes. (Not counting the leaf node.)
864     */
865    void CheckNodeProperties(Node node, Node parentNode, bool parentColor, int blackCount, ref int expectedBlackCount)
866    {
867      if (node == null) return;
868     
869      Debug.Assert(node.parent == parentNode);
870     
871      if (parentColor == RED) {
872        Debug.Assert(node.color == BLACK);
873      }
874      if (node.color == BLACK) {
875        blackCount++;
876      }
877      if (node.left == null && node.right == null) {
878        // node is a leaf node:
879        if (expectedBlackCount == -1)
880          expectedBlackCount = blackCount;
881        else
882          Debug.Assert(expectedBlackCount == blackCount);
883      }
884      CheckNodeProperties(node.left, node, node.color, blackCount, ref expectedBlackCount);
885      CheckNodeProperties(node.right, node, node.color, blackCount, ref expectedBlackCount);
886    }
887    #endif
888    #endregion
889   
890    #region GetTreeAsString
891    internal string GetTreeAsString()
892    {
893      #if DEBUG
894      if (root == null)
895        return "<empty tree>";
896      StringBuilder b = new StringBuilder();
897      AppendTreeToString(root, b, 0);
898      return b.ToString();
899      #else
900      return "Not available in release build.";
901      #endif
902    }
903   
904    #if DEBUG
905    static void AppendTreeToString(Node node, StringBuilder b, int indent)
906    {
907      if (node.color == RED)
908        b.Append("RED   ");
909      else
910        b.Append("BLACK ");
911      b.AppendLine(node.ToString());
912      indent += 2;
913      if (node.left != null) {
914        b.Append(' ', indent);
915        b.Append("L: ");
916        AppendTreeToString(node.left, b, indent);
917      }
918      if (node.right != null) {
919        b.Append(' ', indent);
920        b.Append("R: ");
921        AppendTreeToString(node.right, b, indent);
922      }
923    }
924    #endif
925    #endregion
926  }
927}
Note: See TracBrowser for help on using the repository browser.