Index: quiver/lib/src/collection/treeset.dart |
diff --git a/quiver/lib/src/collection/treeset.dart b/quiver/lib/src/collection/treeset.dart |
deleted file mode 100644 |
index 200c21fcf1a61df65451a798e40cf3d31b6c0cf6..0000000000000000000000000000000000000000 |
--- a/quiver/lib/src/collection/treeset.dart |
+++ /dev/null |
@@ -1,1050 +0,0 @@ |
-// Copyright 2014 Google Inc. All Rights Reserved. |
-// |
-// Licensed under the Apache License, Version 2.0 (the "License"); |
-// you may not use this file except in compliance with the License. |
-// You may obtain a copy of the License at |
-// |
-// http://www.apache.org/licenses/LICENSE-2.0 |
-// |
-// Unless required by applicable law or agreed to in writing, software |
-// distributed under the License is distributed on an "AS IS" BASIS, |
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-// See the License for the specific language governing permissions and |
-// limitations under the License. |
- |
-part of quiver.collection; |
- |
-/** |
- * A [Set] of items stored in a binary tree according to [comparator]. |
- * Supports bidirectional iteration. |
- */ |
-abstract class TreeSet<V> extends IterableBase<V> implements Set<V> { |
- final Comparator<V> comparator; |
- |
- int get length; |
- |
- /** |
- * Create a new [TreeSet] with an ordering defined by [comparator] or the |
- * default `(a, b) => a.compareTo(b)`. |
- */ |
- factory TreeSet({Comparator<V> comparator}) { |
- if (comparator == null) { |
- comparator = (a, b) => a.compareTo(b); |
- } |
- return new AvlTreeSet(comparator: comparator); |
- } |
- |
- TreeSet._(this.comparator); |
- |
- /** |
- * Returns an [BidirectionalIterator] that iterates over this tree. |
- */ |
- BidirectionalIterator<V> get iterator; |
- |
- /** |
- * Returns an [BidirectionalIterator] that iterates over this tree, in reverse. |
- */ |
- BidirectionalIterator<V> get reverseIterator; |
- |
- /** |
- * Returns an [BidirectionalIterator] that starts at [anchor]. |
- * By default, the iterator includes the anchor with the first movement; |
- * set [inclusive] to false if you want to exclude the anchor. Set [reversed] |
- * to true to change the direction of of moveNext and movePrevious. |
- * |
- * Note: This iterator allows you to walk the entire set. It does not present |
- * a subview. |
- */ |
- BidirectionalIterator<V> fromIterator(V anchor, |
- {bool reversed: false, bool inclusive: true}); |
- |
- /** |
- * Search the tree for the matching [object] or the [nearestOption] |
- * if missing. See [TreeSearch]. |
- */ |
- V nearest(V object, {TreeSearch nearestOption: TreeSearch.NEAREST}); |
- |
- // TODO: toString or not toString, that is the question. |
-} |
- |
-/** |
- * Controls the results for [TreeSet.searchNearest]() |
- */ |
-class TreeSearch { |
- |
- /** |
- * If result not found, always chose the smaler element |
- */ |
- static const LESS_THAN = const TreeSearch._(1); |
- |
- /** |
- * If result not found, chose the nearest based on comparison |
- */ |
- static const NEAREST = const TreeSearch._(2); |
- |
- /** |
- * If result not found, always chose the greater element |
- */ |
- static const GREATER_THAN = const TreeSearch._(3); |
- |
- final int _val; |
- const TreeSearch._(this._val); |
-} |
- |
-/** |
- * A node in the [TreeSet]. |
- */ |
-abstract class _TreeNode<V> { |
- _TreeNode<V> get left; |
- _TreeNode<V> get right; |
- |
- //TODO(codefu): Remove need for [parent]; this is just an implementation note |
- _TreeNode<V> get parent; |
- V object; |
- |
- /** |
- * TreeNodes are always allocated as leafs. |
- */ |
- _TreeNode({this.object}); |
- |
- /** |
- * Return the minimum node for the subtree |
- */ |
- _TreeNode<V> get minimumNode { |
- var node = this; |
- while (node.left != null) { |
- node = node.left; |
- } |
- return node; |
- } |
- |
- /** |
- * Return the maximum node for the subtree |
- */ |
- _TreeNode<V> get maximumNode { |
- var node = this; |
- while (node.right != null) { |
- node = node.right; |
- } |
- return node; |
- } |
- |
- /** |
- * Return the next greatest element (or null) |
- */ |
- _TreeNode<V> get successor { |
- var node = this; |
- if (node.right != null) { |
- return node.right.minimumNode; |
- } |
- while (node.parent != null && node == node.parent.right) { |
- node = node.parent; |
- } |
- return node.parent; |
- } |
- |
- /** |
- * Return the next smaller element (or null) |
- */ |
- _TreeNode<V> get predecessor { |
- var node = this; |
- if (node.left != null) { |
- return node.left.maximumNode; |
- } |
- while (node.parent != null && node.parent._left == node) { |
- node = node.parent; |
- } |
- return node.parent; |
- } |
-} |
- |
-/** |
- * AVL implementation of a self-balancing binary tree. Optimized for lookup |
- * operations. |
- * |
- * Notes: Adapted from "Introduction to Algorithms", second edition, |
- * by Thomas H. Cormen, Charles E. Leiserson, |
- * Ronald L. Rivest, Clifford Stein. |
- * chapter 13.2 |
- */ |
-class AvlTreeSet<V> extends TreeSet<V> { |
- int _length = 0; |
- AvlNode<V> _root; |
- // Modification count to the tree, monotonically increasing |
- int _modCount = 0; |
- |
- int get length => _length; |
- |
- AvlTreeSet({Comparator<V> comparator}) : super._(comparator); |
- |
- /** |
- * Add the element to the tree. |
- */ |
- bool add(V element) { |
- if (_root == null) { |
- AvlNode<V> node = new AvlNode<V>(object: element); |
- _root = node; |
- ++_length; |
- ++_modCount; |
- return true; |
- } |
- |
- AvlNode<V> x = _root; |
- while (true) { |
- int compare = comparator(element, x.object); |
- if (compare == 0) { |
- return false; |
- } else if (compare < 0) { |
- if (x._left == null) { |
- AvlNode<V> node = new AvlNode<V>(object: element).._parent = x; |
- x |
- .._left = node |
- .._balanceFactor -= 1; |
- break; |
- } |
- x = x.left; |
- } else { |
- if (x._right == null) { |
- AvlNode<V> node = new AvlNode<V>(object: element).._parent = x; |
- x |
- .._right = node |
- .._balanceFactor += 1; |
- break; |
- } |
- x = x.right; |
- } |
- } |
- |
- ++_modCount; |
- |
- // AVL balancing act (for height balanced trees) |
- // Now that we've inserted, we've unbalanced some trees, we need |
- // to follow the tree back up to the _root double checking that the tree |
- // is still balanced and _maybe_ perform a single or double rotation. |
- // Note: Left additions == -1, Right additions == +1 |
- // Balanced Node = { -1, 0, 1 }, out of balance = { -2, 2 } |
- // Single rotation when Parent & Child share signed balance, |
- // Double rotation when sign differs! |
- AvlNode<V> node = x; |
- while (node._balanceFactor != 0 && node.parent != null) { |
- // Find out which side of the parent we're on |
- if (node.parent._left == node) { |
- node.parent._balanceFactor -= 1; |
- } else { |
- node.parent._balanceFactor += 1; |
- } |
- |
- node = node.parent; |
- if (node._balanceFactor == 2) { |
- // Heavy on the right side - Test for which rotation to perform |
- if (node.right._balanceFactor == 1) { |
- // Single (left) rotation; this will balance everything to zero |
- _rotateLeft(node); |
- node._balanceFactor = node.parent._balanceFactor = 0; |
- node = node.parent; |
- } else { |
- // Double (Right/Left) rotation |
- // node will now be old node.right.left |
- _rotateRightLeft(node); |
- node = node.parent; // Update to new parent (old grandchild) |
- if (node._balanceFactor == 1) { |
- node.right._balanceFactor = 0; |
- node.left._balanceFactor = -1; |
- } else if (node._balanceFactor == 0) { |
- node.right._balanceFactor = 0; |
- node.left._balanceFactor = 0; |
- } else { |
- node.right._balanceFactor = 1; |
- node.left._balanceFactor = 0; |
- } |
- node._balanceFactor = 0; |
- } |
- break; // out of loop, we're balanced |
- } else if (node._balanceFactor == -2) { |
- // Heavy on the left side - Test for which rotation to perform |
- if (node.left._balanceFactor == -1) { |
- _rotateRight(node); |
- node._balanceFactor = node.parent._balanceFactor = 0; |
- node = node.parent; |
- } else { |
- // Double (Left/Right) rotation |
- // node will now be old node.left.right |
- _rotateLeftRight(node); |
- node = node.parent; |
- if (node._balanceFactor == -1) { |
- node.right._balanceFactor = 1; |
- node.left._balanceFactor = 0; |
- } else if (node._balanceFactor == 0) { |
- node.right._balanceFactor = 0; |
- node.left._balanceFactor = 0; |
- } else { |
- node.right._balanceFactor = 0; |
- node.left._balanceFactor = -1; |
- } |
- node._balanceFactor = 0; |
- } |
- break; // out of loop, we're balanced |
- } |
- } // end of while (balancing) |
- _length++; |
- return true; |
- } |
- |
- /** |
- * Test to see if an element is stored in the tree |
- */ |
- AvlNode<V> _getNode(V element) { |
- if (element == null) return null; |
- AvlNode<V> x = _root; |
- while (x != null) { |
- int compare = comparator(element, x.object); |
- if (compare == 0) { |
- // This only means our node matches; we need to search for the exact |
- // element. We could have been glutons and used a hashmap to back. |
- return x; |
- } else if (compare < 0) { |
- x = x.left; |
- } else { |
- x = x.right; |
- } |
- } |
- return null; |
- } |
- |
- /** |
- * This function will right rotate/pivot N with its left child, placing |
- * it on the right of its left child. |
- * |
- * N Y |
- * / \ / \ |
- * Y A Z N |
- * / \ ==> / \ / \ |
- * Z B D CB A |
- * / \ |
- *D C |
- * |
- * Assertion: must have a left element |
- */ |
- void _rotateRight(AvlNode<V> node) { |
- AvlNode<V> y = node.left; |
- if (y == null) throw new AssertionError(); |
- |
- // turn Y's right subtree(B) into N's left subtree. |
- node._left = y.right; |
- if (node.left != null) { |
- node.left._parent = node; |
- } |
- y._parent = node.parent; |
- if (y._parent == null) { |
- _root = y; |
- } else { |
- if (node.parent._left == node) { |
- node.parent._left = y; |
- } else { |
- node.parent._right = y; |
- } |
- } |
- y._right = node; |
- node._parent = y; |
- } |
- |
- /** |
- * This function will left rotate/pivot N with its right child, placing |
- * it on the left of its right child. |
- * |
- * N Y |
- * / \ / \ |
- * A Y N Z |
- * / \ ==> / \ / \ |
- * B Z A BC D |
- * / \ |
- * C D |
- * |
- * Assertion: must have a right element |
- */ |
- void _rotateLeft(AvlNode<V> node) { |
- AvlNode<V> y = node.right; |
- if (y == null) throw new AssertionError(); |
- |
- // turn Y's left subtree(B) into N's right subtree. |
- node._right = y.left; |
- if (node.right != null) { |
- node.right._parent = node; |
- } |
- y._parent = node.parent; |
- if (y._parent == null) { |
- _root = y; |
- } else { |
- if (node.parent._left == node) { |
- y.parent._left = y; |
- } else { |
- y.parent._right = y; |
- } |
- } |
- y._left = node; |
- node._parent = y; |
- } |
- |
- /** |
- * This function will double rotate node with right/left operations. |
- * node is S. |
- * |
- * S G |
- * / \ / \ |
- * A C S C |
- * / \ ==> / \ / \ |
- * G B A DC B |
- * / \ |
- * D C |
- */ |
- void _rotateRightLeft(AvlNode<V> node) { |
- _rotateRight(node.right); |
- _rotateLeft(node); |
- } |
- |
- /** |
- * This function will double rotate node with left/right operations. |
- * node is S. |
- * |
- * S G |
- * / \ / \ |
- * C A C S |
- * / \ ==> / \ / \ |
- *B G B CD A |
- * / \ |
- * C D |
- */ |
- void _rotateLeftRight(AvlNode<V> node) { |
- _rotateLeft(node.left); |
- _rotateRight(node); |
- } |
- |
- bool addAll(Iterable<V> items) { |
- bool modified = false; |
- for (V ele in items) { |
- modified = add(ele) ? true : modified; |
- } |
- return modified; |
- } |
- |
- void clear() { |
- _length = 0; |
- _root = null; |
- ++_modCount; |
- } |
- |
- bool containsAll(Iterable<Object> items) { |
- for (var ele in items) { |
- if (!contains(ele)) return false; |
- } |
- return true; |
- } |
- |
- bool remove(Object item) { |
- AvlNode<V> x = _getNode(item); |
- if (x != null) { |
- _removeNode(x); |
- return true; |
- } |
- return false; |
- } |
- |
- void _removeNode(AvlNode<V> node) { |
- AvlNode<V> y, w; |
- |
- ++_modCount; |
- --_length; |
- |
- // note: if you read wikipedia, it states remove the node if its a leaf, |
- // otherwise, replace it with its predecessor or successor. We're not. |
- if (node._right == null || node.right._left == null) { |
- // simple solutions |
- if (node.right != null) { |
- y = node.right; |
- y._parent = node.parent; |
- y._balanceFactor = node._balanceFactor - 1; |
- y._left = node.left; |
- if (y.left != null) { |
- y.left._parent = y; |
- } |
- } else if (node.left != null) { |
- y = node.left; |
- y._parent = node.parent; |
- y._balanceFactor = node._balanceFactor + 1; |
- } else { |
- y = null; |
- } |
- if (_root == node) { |
- _root = y; |
- } else if (node.parent._left == node) { |
- node.parent._left = y; |
- if (y == null) { |
- // account for leaf deletions changing the balance |
- node.parent._balanceFactor += 1; |
- y = node.parent; // start searching from here; |
- } |
- } else { |
- node.parent._right = y; |
- if (y == null) { |
- node.parent._balanceFactor -= 1; |
- y = node.parent; |
- } |
- } |
- w = y; |
- } else { |
- // This node is not a leaf; we should find the successor node, swap |
- //it with this* and then update the balance factors. |
- y = node.successor; |
- y._left = node.left; |
- if (y.left != null) { |
- y.left._parent = y; |
- } |
- |
- w = y.parent; |
- w._left = y.right; |
- if (w.left != null) { |
- w.left._parent = w; |
- } |
- // known: we're removing from the left |
- w._balanceFactor += 1; |
- |
- // known due to test for n->r->l above |
- y._right = node.right; |
- y.right._parent = y; |
- y._balanceFactor = node._balanceFactor; |
- |
- y._parent = node.parent; |
- if (_root == node) { |
- _root = y; |
- } else if (node.parent._left == node) { |
- node.parent._left = y; |
- } else { |
- node.parent._right = y; |
- } |
- } |
- |
- // Safe to kill node now; its free to go. |
- node._balanceFactor = 0; |
- node._left = node._right = node._parent = null; |
- node.object = null; |
- |
- // Recalculate max values all the way to the top. |
- node = w; |
- while (node != null) { |
- node = node.parent; |
- } |
- |
- // Re-balance to the top, ending early if OK |
- node = w; |
- while (node != null) { |
- if (node._balanceFactor == -1 || node._balanceFactor == 1) { |
- // The height of node hasn't changed; done! |
- break; |
- } |
- if (node._balanceFactor == 2) { |
- // Heavy on the right side; figure out which rotation to perform |
- if (node.right._balanceFactor == -1) { |
- _rotateRightLeft(node); |
- node = node.parent; // old grand-child! |
- if (node._balanceFactor == 1) { |
- node.right._balanceFactor = 0; |
- node.left._balanceFactor = -1; |
- } else if (node._balanceFactor == 0) { |
- node.right._balanceFactor = 0; |
- node.left._balanceFactor = 0; |
- } else { |
- node.right._balanceFactor = 1; |
- node.left._balanceFactor = 0; |
- } |
- node._balanceFactor = 0; |
- } else { |
- // single left-rotation |
- _rotateLeft(node); |
- if (node.parent._balanceFactor == 0) { |
- node.parent._balanceFactor = -1; |
- node._balanceFactor = 1; |
- break; |
- } else { |
- node.parent._balanceFactor = 0; |
- node._balanceFactor = 0; |
- node = node.parent; |
- continue; |
- } |
- } |
- } else if (node._balanceFactor == -2) { |
- // Heavy on the left |
- if (node.left._balanceFactor == 1) { |
- _rotateLeftRight(node); |
- node = node.parent; // old grand-child! |
- if (node._balanceFactor == -1) { |
- node.right._balanceFactor = 1; |
- node.left._balanceFactor = 0; |
- } else if (node._balanceFactor == 0) { |
- node.right._balanceFactor = 0; |
- node.left._balanceFactor = 0; |
- } else { |
- node.right._balanceFactor = 0; |
- node.left._balanceFactor = -1; |
- } |
- node._balanceFactor = 0; |
- } else { |
- _rotateRight(node); |
- if (node.parent._balanceFactor == 0) { |
- node.parent._balanceFactor = 1; |
- node._balanceFactor = -1; |
- break; |
- } else { |
- node.parent._balanceFactor = 0; |
- node._balanceFactor = 0; |
- node = node.parent; |
- continue; |
- } |
- } |
- } |
- |
- // continue up the tree for testing |
- if (node.parent != null) { |
- // The concept of balance here is reverse from addition; since |
- // we are taking away weight from one side or the other (thus |
- // the balance changes in favor of the other side) |
- if (node.parent.left == node) { |
- node.parent._balanceFactor += 1; |
- } else { |
- node.parent._balanceFactor -= 1; |
- } |
- } |
- node = node.parent; |
- } |
- } |
- |
- /** |
- * See [Set.removeAll] |
- */ |
- void removeAll(Iterable items) { |
- for (var ele in items) { |
- remove(ele); |
- } |
- } |
- |
- /** |
- * See [Set.retainAll] |
- */ |
- void retainAll(Iterable<Object> elements) { |
- List<V> chosen = []; |
- for (var target in elements) { |
- if (contains(target)) { |
- chosen.add(target); |
- } |
- } |
- clear(); |
- addAll(chosen); |
- } |
- |
- /** |
- * See [Set.retainWhere] |
- */ |
- void retainWhere(bool test(V element)) { |
- List<V> chosen = []; |
- for (var target in this) { |
- if (test(target)) { |
- chosen.add(target); |
- } |
- } |
- clear(); |
- addAll(chosen); |
- } |
- |
- /** |
- * See [Set.removeWhere] |
- */ |
- void removeWhere(bool test(V element)) { |
- List<V> damned = []; |
- for (var target in this) { |
- if (test(target)) { |
- damned.add(target); |
- } |
- } |
- removeAll(damned); |
- } |
- |
- /** |
- * See [IterableBase.first] |
- */ |
- V get first { |
- if (_root == null) return null; |
- AvlNode<V> min = _root.minimumNode; |
- return min != null ? min.object : null; |
- } |
- |
- /** |
- * See [IterableBase.last] |
- */ |
- V get last { |
- if (_root == null) return null; |
- AvlNode<V> max = _root.maximumNode; |
- return max != null ? max.object : null; |
- } |
- |
- /** |
- * See [Set.lookup] |
- */ |
- V lookup(Object element) { |
- if (element == null || _root == null) return null; |
- AvlNode<V> x = _root; |
- int compare = 0; |
- while (x != null) { |
- compare = comparator(element, x.object); |
- if (compare == 0) { |
- return x.object; |
- } else if (compare < 0) { |
- x = x.left; |
- } else { |
- x = x.right; |
- } |
- } |
- return null; |
- } |
- |
- V nearest(V object, {TreeSearch nearestOption: TreeSearch.NEAREST}) { |
- AvlNode<V> found = _searchNearest(object, option: nearestOption); |
- return (found != null) ? found.object : null; |
- } |
- |
- /** |
- * Search the tree for the matching element, or the 'nearest' node. |
- * NOTE: [BinaryTree.comparator] needs to have finer granulatity than -1,0,1 |
- * in order for this to return something that's meaningful. |
- */ |
- AvlNode<V> _searchNearest(V element, |
- {TreeSearch option: TreeSearch.NEAREST}) { |
- if (element == null || _root == null) { |
- return null; |
- } |
- AvlNode<V> x = _root; |
- AvlNode<V> previous; |
- int compare = 0; |
- while (x != null) { |
- previous = x; |
- compare = comparator(element, x.object); |
- if (compare == 0) { |
- return x; |
- } else if (compare < 0) { |
- x = x.left; |
- } else { |
- x = x.right; |
- } |
- } |
- |
- if (option == TreeSearch.GREATER_THAN) { |
- return (compare < 0) ? previous : previous.successor; |
- } else if (option == TreeSearch.LESS_THAN) { |
- return (compare < 0) ? previous.predecessor : previous; |
- } |
- // Default: nearest absolute value |
- // Fell off the tree looking for the exact match; now we need |
- // to find the nearest element. |
- x = (compare < 0) ? previous.predecessor : previous.successor; |
- if (x == null) { |
- return previous; |
- } |
- int otherCompare = comparator(element, x.object); |
- if (compare < 0) { |
- return compare.abs() < otherCompare ? previous : x; |
- } |
- return otherCompare.abs() < compare ? x : previous; |
- } |
- |
- // |
- // [IterableBase]<V> Methods |
- // |
- |
- /** |
- * See [IterableBase.iterator] |
- */ |
- BidirectionalIterator<V> get iterator => new _AvlTreeIterator._(this); |
- |
- /** |
- * See [TreeSet.reverseIterator] |
- */ |
- BidirectionalIterator<V> get reverseIterator => |
- new _AvlTreeIterator._(this, reversed: true); |
- |
- /** |
- * See [TreeSet.fromIterator] |
- */ |
- BidirectionalIterator<V> fromIterator(V anchor, |
- {bool reversed: false, bool inclusive: true}) => |
- new _AvlTreeIterator<V>._(this, |
- anchorObject: anchor, reversed: reversed, inclusive: inclusive); |
- |
- /** |
- * See [IterableBase.contains] |
- */ |
- bool contains(Object object) { |
- AvlNode<V> x = _getNode(object as V); |
- return x != null; |
- } |
- |
- // |
- // [Set] methods |
- // |
- |
- /** |
- * See [Set.intersection] |
- */ |
- Set<V> intersection(Set<Object> other) { |
- TreeSet<V> set = new TreeSet(comparator: comparator); |
- |
- // Opitimized for sorted sets |
- if (other is TreeSet) { |
- var i1 = iterator; |
- var i2 = other.iterator; |
- var hasMore1 = i1.moveNext(); |
- var hasMore2 = i2.moveNext(); |
- while (hasMore1 && hasMore2) { |
- var c = comparator(i1.current, i2.current); |
- if (c == 0) { |
- set.add(i1.current); |
- hasMore1 = i1.moveNext(); |
- hasMore2 = i2.moveNext(); |
- } else if (c < 0) { |
- hasMore1 = i1.moveNext(); |
- } else { |
- hasMore2 = i2.moveNext(); |
- } |
- } |
- return set; |
- } |
- |
- // Non-optimized version. |
- for (var target in this) { |
- if (other.contains(target)) { |
- set.add(target); |
- } |
- } |
- return set; |
- } |
- |
- /** |
- * See [Set.union] |
- */ |
- Set<V> union(Set<V> other) { |
- TreeSet<V> set = new TreeSet(comparator: comparator); |
- |
- if (other is TreeSet) { |
- var i1 = iterator; |
- var i2 = other.iterator; |
- var hasMore1 = i1.moveNext(); |
- var hasMore2 = i2.moveNext(); |
- while (hasMore1 && hasMore2) { |
- var c = comparator(i1.current, i2.current); |
- if (c == 0) { |
- set.add(i1.current); |
- hasMore1 = i1.moveNext(); |
- hasMore2 = i2.moveNext(); |
- } else if (c < 0) { |
- set.add(i1.current); |
- hasMore1 = i1.moveNext(); |
- } else { |
- set.add(i2.current); |
- hasMore2 = i2.moveNext(); |
- } |
- } |
- if (hasMore1 || hasMore2) { |
- i1 = hasMore1 ? i1 : i2; |
- do { |
- set.add(i1.current); |
- } while (i1.moveNext()); |
- } |
- return set; |
- } |
- |
- // Non-optimized version. |
- return set |
- ..addAll(this) |
- ..addAll(other); |
- } |
- |
- /** |
- * See [Set.difference] |
- */ |
- Set<V> difference(Set<V> other) { |
- TreeSet<V> set = new TreeSet(comparator: comparator); |
- |
- if (other is TreeSet) { |
- var i1 = iterator; |
- var i2 = other.iterator; |
- var hasMore1 = i1.moveNext(); |
- var hasMore2 = i2.moveNext(); |
- while (hasMore1 && hasMore2) { |
- var c = comparator(i1.current, i2.current); |
- if (c == 0) { |
- hasMore1 = i1.moveNext(); |
- hasMore2 = i2.moveNext(); |
- } else if (c < 0) { |
- set.add(i1.current); |
- hasMore1 = i1.moveNext(); |
- } else { |
- hasMore2 = i2.moveNext(); |
- } |
- } |
- if (hasMore1) { |
- do { |
- set.add(i1.current); |
- } while (i1.moveNext()); |
- } |
- return set; |
- } |
- |
- // Non-optimized version. |
- for (var target in this) { |
- if (!other.contains(target)) { |
- set.add(target); |
- } |
- } |
- return set; |
- } |
- |
- /** |
- * Visible for testing only. |
- */ |
- AvlNode<V> getNode(V object) => _getNode(object); |
-} |
- |
-typedef bool _IteratorMove(); |
- |
-/** |
- * This iterator either starts at the beginning or end (see [TreeSet.iterator] |
- * and [TreeSet.reverseIterator]) or from an anchor point in the set (see |
- * [TreeSet.fromIterator]). When using fromIterator, the inital |
- * anchor point is included in the first movement (either [moveNext] or |
- * [movePrevious]) but can optionally be excluded in the constructor. |
- */ |
-class _AvlTreeIterator<V> implements BidirectionalIterator<V> { |
- static const LEFT = -1; |
- static const WALK = 0; |
- static const RIGHT = 1; |
- |
- final bool reversed; |
- final AvlTreeSet<V> tree; |
- final int _modCountGuard; |
- final Object anchorObject; |
- final bool inclusive; |
- |
- _IteratorMove _moveNext; |
- _IteratorMove _movePrevious; |
- |
- int state; |
- _TreeNode<V> _current; |
- |
- _AvlTreeIterator._(AvlTreeSet<V> tree, |
- {reversed: false, this.inclusive: true, this.anchorObject: null}) |
- : this.tree = tree, |
- this._modCountGuard = tree._modCount, |
- this.reversed = reversed { |
- if (anchorObject == null || tree.length == 0) { |
- // If the anchor is far left or right, we're just a normal iterator. |
- state = reversed ? RIGHT : LEFT; |
- _moveNext = reversed ? _movePreviousNormal : _moveNextNormal; |
- _movePrevious = reversed ? _moveNextNormal : _movePreviousNormal; |
- return; |
- } |
- |
- state = WALK; |
- // Else we've got an anchor we have to worry about initalizing from. |
- // This isn't known till the caller actually performs a previous/next. |
- _moveNext = () { |
- _current = tree._searchNearest(anchorObject, |
- option: reversed ? TreeSearch.LESS_THAN : TreeSearch.GREATER_THAN); |
- _moveNext = reversed ? _movePreviousNormal : _moveNextNormal; |
- _movePrevious = reversed ? _moveNextNormal : _movePreviousNormal; |
- if (_current == null) { |
- state = reversed ? LEFT : RIGHT; |
- } else if (tree.comparator(_current.object, anchorObject) == 0 && |
- !inclusive) { |
- _moveNext(); |
- } |
- return state == WALK; |
- }; |
- |
- _movePrevious = () { |
- _current = tree._searchNearest(anchorObject, |
- option: reversed ? TreeSearch.GREATER_THAN : TreeSearch.LESS_THAN); |
- _moveNext = reversed ? _movePreviousNormal : _moveNextNormal; |
- _movePrevious = reversed ? _moveNextNormal : _movePreviousNormal; |
- if (_current == null) { |
- state = reversed ? RIGHT : LEFT; |
- } else if (tree.comparator(_current.object, anchorObject) == 0 && |
- !inclusive) { |
- _movePrevious(); |
- } |
- return state == WALK; |
- }; |
- } |
- |
- V get current => (state != WALK || _current == null) ? null : _current.object; |
- |
- bool moveNext() => _moveNext(); |
- bool movePrevious() => _movePrevious(); |
- |
- bool _moveNextNormal() { |
- if (_modCountGuard != tree._modCount) { |
- throw new ConcurrentModificationError(tree); |
- } |
- if (state == RIGHT || tree.length == 0) return false; |
- switch (state) { |
- case LEFT: |
- _current = tree._root.minimumNode; |
- state = WALK; |
- return true; |
- case WALK: |
- default: |
- _current = _current.successor; |
- if (_current == null) { |
- state = RIGHT; |
- } |
- return state == WALK; |
- } |
- } |
- |
- bool _movePreviousNormal() { |
- if (_modCountGuard != tree._modCount) { |
- throw new ConcurrentModificationError(tree); |
- } |
- if (state == LEFT || tree.length == 0) return false; |
- switch (state) { |
- case RIGHT: |
- _current = tree._root.maximumNode; |
- state = WALK; |
- return true; |
- case WALK: |
- default: |
- _current = _current.predecessor; |
- if (_current == null) { |
- state = LEFT; |
- } |
- return state == WALK; |
- } |
- } |
-} |
- |
-/** |
- * Private class used to track element insertions in the [TreeSet]. |
- */ |
-class AvlNode<V> extends _TreeNode<V> { |
- AvlNode<V> _left; |
- AvlNode<V> _right; |
- //TODO(codefu): Remove need for [parent]; this is just an implementation note |
- AvlNode<V> _parent; |
- int _balanceFactor = 0; |
- |
- AvlNode<V> get left => _left; |
- AvlNode<V> get right => _right; |
- AvlNode<V> get parent => _parent; |
- int get balance => _balanceFactor; |
- |
- AvlNode({V object}) : super(object: object); |
- |
- String toString() => |
- "(b:$balance o: $object l:${left != null} r:${right != null})"; |
-} |