com.google.gwt.emul.java.util.TreeMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vaadin-client Show documentation
Show all versions of vaadin-client Show documentation
Vaadin is a web application framework for Rich Internet Applications (RIA).
Vaadin enables easy development and maintenance of fast and
secure rich web
applications with a stunning look and feel and a wide browser support.
It features a server-side architecture with the majority of the logic
running
on the server. Ajax technology is used at the browser-side to ensure a
rich
and interactive user experience.
/*
* Copyright 2008 Google Inc.
*
* 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.
*/
package java.util;
import static com.google.gwt.core.shared.impl.InternalPreconditions.checkNotNull;
import java.io.Serializable;
/**
* Implements a TreeMap using a red-black tree. This guarantees O(log n)
* performance on lookups, inserts, and deletes while maintaining linear
* in-order traversal time. Null keys and values are fully supported if the
* comparator supports them (the default comparator does not).
*
* @param key type
* @param value type
*/
public class TreeMap extends AbstractNavigableMap implements Serializable {
/*
* Implementation derived from public domain C implementation as of 5
* September 2007 at:
* http://eternallyconfuzzled.com/tuts/datastructures/jsw_tut_rbtree.aspx
* written by Julienne Walker.
*
* This version does not require a parent pointer kept in each node.
*/
/**
* Iterator for descendingMap().entrySet()
.
*/
private final class DescendingEntryIterator implements Iterator> {
private final ListIterator> iter;
private Entry last;
/**
* Constructor for DescendingEntryIterator
.
*/
public DescendingEntryIterator() {
this(SubMapType.All, null, false, null, false);
}
/**
* Create an iterator which may return only a restricted range.
*
* @param fromKey the first key to return in the iterator.
* @param toKey the upper bound of keys to return.
*/
public DescendingEntryIterator(SubMapType type,
K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
List> list = new ArrayList>();
inOrderAdd(list, type, TreeMap.this.root,
fromKey, fromInclusive, toKey, toInclusive);
this.iter = list.listIterator(list.size());
}
@Override
public boolean hasNext() {
return iter.hasPrevious();
}
@Override
public Entry next() {
return last = iter.previous();
}
@Override
public void remove() {
iter.remove();
removeEntry(last);
last = null;
}
}
/**
* Iterator for EntrySet
.
*/
private final class EntryIterator implements Iterator> {
private final ListIterator> iter;
private Entry last;
/**
* Constructor for EntrySetIterator
.
*/
public EntryIterator() {
this(SubMapType.All, null, false, null, false);
}
/**
* Create an iterator which may return only a restricted range.
*
* @param fromKey the first key to return in the iterator.
* @param toKey the upper bound of keys to return.
*/
public EntryIterator(SubMapType type,
K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
List> list = new ArrayList>();
inOrderAdd(list, type, TreeMap.this.root,
fromKey, fromInclusive, toKey, toInclusive);
this.iter = list.listIterator();
}
@Override
public boolean hasNext() {
return iter.hasNext();
}
@Override
public Entry next() {
return last = iter.next();
}
@Override
public void remove() {
iter.remove();
removeEntry(last);
last = null;
}
}
private final class EntrySet extends AbstractNavigableMap.EntrySet {
@Override
public void clear() {
TreeMap.this.clear();
}
}
/**
* Tree node.
*
* @param key type
* @param value type
*/
private static class Node extends SimpleEntry {
/*
* The children are kept in an array to minimize the normal duplication of
* code.
*/
@SuppressWarnings("unchecked")
protected final Node[] child = new Node[2];
protected boolean isRed;
/**
* Create a red node.
*
* @param key
* @param value
*/
public Node(K key, V value) {
this(key, value, true);
}
/**
* Create a node of the specified color.
*
* @param key
* @param value
* @param isRed true if this should be a red node, false for black
*/
public Node(K key, V value, boolean isRed) {
super(key, value);
this.isRed = isRed;
}
}
/**
* A state object which is passed down the tree for both insert and remove.
* All uses make use of the done flag to indicate when no further rebalancing
* of the tree is required. Remove methods use the found flag to indicate when
* the desired key has been found. value is used both to return the value of a
* removed node as well as to pass in a value which must match (used for
* entrySet().remove(entry)), and the matchValue flag is used to request this
* behavior.
*
* @param value type
*/
private static class State {
public boolean done;
public boolean found;
public boolean matchValue;
public V value;
@Override
public String toString() {
return "State: mv=" + matchValue + " value=" + value + " done=" + done + " found=" + found;
}
}
private class SubMap extends AbstractNavigableMap {
private final boolean fromInclusive;
// valid only if type is Range or Tail
private final K fromKey;
private final boolean toInclusive;
// valid only if type is Range or Head
private final K toKey;
private final SubMapType type;
SubMap(SubMapType type,
K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive) {
switch (type) {
case Range:
if (cmp.compare(toKey, fromKey) < 0) {
throw new IllegalArgumentException("subMap: " + toKey
+ " less than " + fromKey);
}
break;
case Head:
// check key for compatibility with comparator
cmp.compare(toKey, toKey);
break;
case Tail:
// check key for compatibility with comparator
cmp.compare(fromKey, fromKey);
break;
case All:
// no checks are needed
break;
}
this.type = type;
this.fromKey = fromKey;
this.fromInclusive = fromInclusive;
this.toKey = toKey;
this.toInclusive = toInclusive;
}
@Override
public Comparator super K> comparator() {
return TreeMap.this.comparator();
}
@Override
public Set> entrySet() {
return new SubMap.EntrySet() {
@Override
public boolean isEmpty() {
return SubMap.this.isEmpty();
}
};
}
@Override
public NavigableMap headMap(K toKey, boolean toInclusive) {
if (type.toKeyValid() && cmp.compare(toKey, this.toKey) > 0) {
throw new IllegalArgumentException("subMap: " + toKey +
" greater than " + this.toKey);
}
if (type.fromKeyValid()) {
return TreeMap.this.subMap(fromKey, fromInclusive, toKey, toInclusive);
} else {
return TreeMap.this.headMap(toKey, toInclusive);
}
}
@Override
public boolean isEmpty() {
return getFirstEntry() == null;
}
@Override
public V put(K key, V value) {
if (!inRange(key)) {
throw new IllegalArgumentException(key + " outside the range "
+ fromKey + " to " + toKey);
}
return TreeMap.this.put(key, value);
}
@SuppressWarnings("unchecked")
@Override
public V remove(Object k) {
K key = (K) k;
if (!inRange(key)) {
return null;
}
return TreeMap.this.remove(key);
}
@Override
public int size() {
// TODO(jat): more efficient way to do this?
int count = 0;
for (Iterator> it = entryIterator(); it.hasNext(); it.next()) {
count++;
}
return count;
}
@Override
public NavigableMap subMap(K newFromKey, boolean newFromInclusive,
K newToKey, boolean newToInclusive) {
if (type.fromKeyValid() && cmp.compare(newFromKey, fromKey) < 0) {
throw new IllegalArgumentException("subMap: " + newFromKey +
" less than " + fromKey);
}
if (type.toKeyValid() && cmp.compare(newToKey, toKey) > 0) {
throw new IllegalArgumentException("subMap: " + newToKey +
" greater than " + toKey);
}
return TreeMap.this.subMap(newFromKey, newFromInclusive, newToKey, newToInclusive);
}
@Override
public NavigableMap tailMap(K fromKey, boolean fromInclusive) {
if (type.fromKeyValid() && cmp.compare(fromKey, this.fromKey) < 0) {
throw new IllegalArgumentException("subMap: " + fromKey +
" less than " + this.fromKey);
}
if (type.toKeyValid()) {
return TreeMap.this.subMap(fromKey, fromInclusive, toKey, toInclusive);
} else {
return TreeMap.this.tailMap(fromKey, fromInclusive);
}
}
@Override
Iterator> descendingEntryIterator() {
return new DescendingEntryIterator(type, fromKey, fromInclusive, toKey, toInclusive);
}
@Override
Iterator> entryIterator() {
return new EntryIterator(type, fromKey, fromInclusive, toKey, toInclusive);
}
@Override
Entry getEntry(K key) {
return guardInRange(TreeMap.this.getEntry(key));
}
@Override
Entry getFirstEntry() {
Entry entry;
if (type.fromKeyValid()) {
if (fromInclusive) {
entry = TreeMap.this.getCeilingEntry(fromKey);
} else {
entry = TreeMap.this.getHigherEntry(fromKey);
}
} else {
entry = TreeMap.this.getFirstEntry();
}
// The map is empty if the first key after fromKey is out of range.
return guardInRange(entry);
}
@Override
Entry getLastEntry() {
Entry entry;
if (type.toKeyValid()) {
if (toInclusive) {
entry = TreeMap.this.getFloorEntry(toKey);
} else {
entry = TreeMap.this.getLowerEntry(toKey);
}
} else {
entry = TreeMap.this.getLastEntry();
}
// The map is empty if the last key before toKey is out of range.
return guardInRange(entry);
}
@Override
Entry getCeilingEntry(K key) {
return guardInRange(TreeMap.this.getCeilingEntry(key));
}
@Override
Entry getFloorEntry(K key) {
return guardInRange(TreeMap.this.getFloorEntry(key));
}
@Override
Entry getHigherEntry(K key) {
return guardInRange(TreeMap.this.getHigherEntry(key));
}
@Override
Entry getLowerEntry(K key) {
return guardInRange(TreeMap.this.getLowerEntry(key));
}
@Override
boolean removeEntry(Entry entry) {
return inRange(entry.getKey()) && TreeMap.this.removeEntry(entry);
}
private Entry guardInRange(Entry entry) {
return entry != null && inRange(entry.getKey()) ? entry : null;
}
private boolean inRange(K key) {
return TreeMap.this.inRange(type, key, fromKey, fromInclusive, toKey, toInclusive);
}
}
private enum SubMapType {
All,
Head {
@Override
public boolean toKeyValid() {
return true;
}
},
Range {
@Override
public boolean fromKeyValid() {
return true;
}
@Override
public boolean toKeyValid() {
return true;
}
},
Tail {
@Override
public boolean fromKeyValid() {
return true;
}
};
/**
* Returns true if this submap type uses a from-key.
*/
public boolean fromKeyValid() {
return false;
}
/**
* Returns true if this submap type uses a to-key.
*/
public boolean toKeyValid() {
return false;
}
}
private static final int LEFT = 0;
private static final int RIGHT = 1;
private static int otherChild(int child) {
assert (child == 0 || child == 1);
return 1 - child;
}
// The comparator to use.
private Comparator super K> cmp;
/*
* These two fields are just hints to STOB so that it generates serializers
* for K and V
*/
@SuppressWarnings("unused")
private K exposeKeyType;
@SuppressWarnings("unused")
private V exposeValueType;
// The root of the tree.
private transient Node root;
// The number of nodes in the tree.
private int size = 0;
public TreeMap() {
this((Comparator super K>) null);
}
@SuppressWarnings("unchecked")
public TreeMap(Comparator super K> c) {
root = null;
if (c == null) {
c = Comparators.natural();
}
cmp = c;
}
public TreeMap(Map extends K, ? extends V> map) {
this();
putAll(map);
}
@SuppressWarnings("unchecked")
public TreeMap(SortedMap map) {
this(checkNotNull(map).comparator());
putAll(map); // TODO(jat): more efficient init from sorted map
}
@Override
public void clear() {
root = null;
size = 0;
}
@Override
public Comparator super K> comparator() {
if (cmp == Comparators.natural()) {
return null;
}
return cmp;
}
@Override
public Set> entrySet() {
return new EntrySet();
}
@Override
public NavigableMap headMap(K toKey, boolean inclusive) {
return new SubMap(SubMapType.Head, null, false, toKey, inclusive);
}
@Override
public V put(K key, V value) {
Node node = new Node(key, value);
State state = new State();
root = insert(root, node, state);
if (!state.found) {
++size;
}
root.isRed = false;
return state.value;
}
@Override
@SuppressWarnings("unchecked")
public V remove(Object k) {
K key = (K) k;
State state = new State();
removeWithState(key, state);
return state.value;
}
@Override
public int size() {
return size;
}
@Override
public NavigableMap subMap(K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive) {
return new SubMap(SubMapType.Range, fromKey, fromInclusive, toKey, toInclusive);
}
@Override
public NavigableMap tailMap(K fromKey, boolean inclusive) {
return new SubMap(SubMapType.Tail, fromKey, inclusive, null, false);
}
/**
* Returns the first node which compares greater than the given key.
*
* @param key the key to search for
* @return the next node, or null if there is none
*/
private Node getNodeAfter(K key, boolean inclusive) {
Node foundNode = null;
Node node = root;
while (node != null) {
int c = cmp.compare(key, node.getKey());
if (inclusive && c == 0) {
return node;
}
if (c >= 0) {
node = node.child[RIGHT];
} else {
foundNode = node;
node = node.child[LEFT];
}
}
return foundNode;
}
/**
* Returns the last node which is strictly less than the given key.
*
* @param key the key to search for
* @return the previous node, or null if there is none
*/
private Node getNodeBefore(K key, boolean inclusive) {
Node foundNode = null;
Node node = root;
while (node != null) {
int c = cmp.compare(key, node.getKey());
if (inclusive && c == 0) {
return node;
}
if (c <= 0) {
node = node.child[LEFT];
} else {
foundNode = node;
node = node.child[RIGHT];
}
}
return foundNode;
}
/**
* Used for testing. Validate that the tree meets all red-black correctness
* requirements. These include:
*
*
* - root is black
* - no children of a red node may be red
* - the black height of every path through the three to a leaf is exactly the same
*
*
* @throws RuntimeException if any correctness errors are detected.
*/
void assertCorrectness() {
assertCorrectness(root, true);
}
@Override
Iterator> descendingEntryIterator() {
return new DescendingEntryIterator();
}
@Override
Iterator> entryIterator() {
return new EntryIterator();
}
/**
* Internal helper function for public {@link #assertCorrectness()}.
*
* @param tree the subtree to validate.
* @param isRed true if the parent of this node is red.
* @return the black height of this subtree.
* @throws RuntimeException if this RB-tree is not valid.
*/
private int assertCorrectness(Node tree, boolean isRed) {
if (tree == null) {
return 0;
}
if (isRed && tree.isRed) {
throw new RuntimeException("Two red nodes adjacent");
}
Node leftNode = tree.child[LEFT];
if (leftNode != null
&& cmp.compare(leftNode.getKey(), tree.getKey()) > 0) {
throw new RuntimeException("Left child " + leftNode
+ " larger than " + tree);
}
Node rightNode = tree.child[RIGHT];
if (rightNode != null
&& cmp.compare(rightNode.getKey(), tree.getKey()) < 0) {
throw new RuntimeException("Right child " + rightNode
+ " smaller than " + tree);
}
int leftHeight = assertCorrectness(leftNode, tree.isRed);
int rightHeight = assertCorrectness(rightNode, tree.isRed);
if (leftHeight != 0 && rightHeight != 0 && leftHeight != rightHeight) {
throw new RuntimeException("Black heights don't match");
}
return tree.isRed ? leftHeight : leftHeight + 1;
}
/**
* Finds an entry given a key and returns the node.
*
* @param key the search key
* @return the node matching the key or null
*/
@Override
Entry getEntry(K key) {
Node tree = root;
while (tree != null) {
int c = cmp.compare(key, tree.getKey());
if (c == 0) {
return tree;
}
int childNum = c < 0 ? LEFT : RIGHT;
tree = tree.child[childNum];
}
return null;
}
/**
* Returns the left-most node of the tree, or null if empty.
*/
@Override
Entry getFirstEntry() {
if (root == null) {
return null;
}
Node node = root;
Node nextNode;
while ((nextNode = node.child[LEFT]) != null) {
node = nextNode;
}
return node;
}
/**
* Returns the right-most node of the tree, or null if empty.
*/
@Override
Entry getLastEntry() {
if (root == null) {
return null;
}
Node node = root;
Node nextNode;
while ((nextNode = node.child[RIGHT]) != null) {
node = nextNode;
}
return node;
}
@Override
Entry getCeilingEntry(K key) {
return getNodeAfter(key, true);
}
@Override
Entry getFloorEntry(K key) {
return getNodeBefore(key, true);
}
@Override
Entry getHigherEntry(K key) {
return getNodeAfter(key, false);
}
@Override
Entry getLowerEntry(K key) {
return getNodeBefore(key, false);
}
@Override
boolean removeEntry(Entry entry) {
State state = new State();
state.matchValue = true;
state.value = entry.getValue();
return removeWithState(entry.getKey(), state);
}
private void inOrderAdd(List> list, SubMapType type, Node current,
K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
if (current == null) {
return;
}
// TODO: truncate this recursion if the whole subtree is known to be
// outside of bounds?
Node leftNode = current.child[LEFT];
if (leftNode != null) {
inOrderAdd(list, type, leftNode,
fromKey, fromInclusive, toKey, toInclusive);
}
if (inRange(type, current.getKey(), fromKey, fromInclusive, toKey, toInclusive)) {
list.add(current);
}
Node rightNode = current.child[RIGHT];
if (rightNode != null) {
inOrderAdd(list, type, rightNode, fromKey, fromInclusive, toKey, toInclusive);
}
}
private boolean inRange(SubMapType type, K key,
K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
if (type.fromKeyValid() && smaller(key, fromKey, !fromInclusive)) {
return false;
}
if (type.toKeyValid() && larger(key, toKey, !toInclusive)) {
return false;
}
return true;
}
/**
* Insert a node into a subtree, collecting state about the insertion.
*
* If the same key already exists, the value of the node is overwritten with
* the value from the new node instead.
*
* @param tree subtree to insert into
* @param newNode new node to insert
* @param state result of the insertion: state.found true if the key already
* existed in the tree state.value the old value if the key existed
* @return the new subtree root
*/
private Node insert(Node tree, Node newNode, State state) {
if (tree == null) {
return newNode;
} else {
int c = cmp.compare(newNode.getKey(), tree.getKey());
if (c == 0) {
state.value = tree.setValue(newNode.getValue());
state.found = true;
return tree;
}
int childNum = c < 0 ? LEFT : RIGHT;
tree.child[childNum] = insert(tree.child[childNum], newNode, state);
if (isRed(tree.child[childNum])) {
if (isRed(tree.child[otherChild(childNum)])) {
// both children are red (nulls are black), make both black and me red
tree.isRed = true;
tree.child[LEFT].isRed = false;
tree.child[RIGHT].isRed = false;
} else {
//
if (isRed(tree.child[childNum].child[childNum])) {
tree = rotateSingle(tree, otherChild(childNum));
} else if (isRed(tree.child[childNum].child[otherChild(childNum)])) {
tree = rotateDouble(tree, otherChild(childNum));
}
}
}
}
return tree;
}
/**
* Returns true if node
is red. Note that null pointers are
* considered black.
*/
private boolean isRed(Node node) {
return node != null && node.isRed;
}
/**
* Returns true if a
is greater than or equal to b
.
*/
private boolean larger(K a, K b, boolean orEqual) {
int compare = cmp.compare(a, b);
return compare > 0 || (orEqual && compare == 0);
}
/**
* Returns true if a
is less than or equal to b
.
*/
private boolean smaller(K a, K b, boolean orEqual) {
int compare = cmp.compare(a, b);
return compare < 0 || (orEqual && compare == 0);
}
/**
* Remove a key from the tree, returning whether it was found and its value.
*
* @param key key to remove
* @param state return state, not null
* @return true if the value was found
*/
private boolean removeWithState(K key, State state) {
if (root == null) {
return false;
}
Node found = null;
Node parent = null;
// create a fake tree root to minimize special cases for changing the root
Node head = new Node(null, null);
int dir = RIGHT;
head.child[RIGHT] = root;
Node node = head;
while (node.child[dir] != null) {
int last = dir;
Node grandparent = parent;
parent = node;
node = node.child[dir];
int c = cmp.compare(key, node.getKey());
dir = c < 0 ? LEFT : RIGHT;
if (c == 0 && (!state.matchValue || Objects.equals(node.getValue(), state.value))) {
found = node;
}
if (!isRed(node) && !isRed(node.child[dir])) {
if (isRed(node.child[otherChild(dir)])) {
parent = parent.child[last] = rotateSingle(node, dir);
} else if (!isRed(node.child[otherChild(dir)])) {
Node sibling = parent.child[otherChild(last)];
if (sibling != null) {
if (!isRed(sibling.child[otherChild(last)])
&& !isRed(sibling.child[last])) {
parent.isRed = false;
sibling.isRed = true;
node.isRed = true;
} else {
assert grandparent != null;
int dir2 = grandparent.child[RIGHT] == parent ? RIGHT : LEFT;
if (isRed(sibling.child[last])) {
grandparent.child[dir2] = rotateDouble(parent, last);
} else if (isRed(sibling.child[otherChild(last)])) {
grandparent.child[dir2] = rotateSingle(parent, last);
}
node.isRed = grandparent.child[dir2].isRed = true;
grandparent.child[dir2].child[LEFT].isRed = false;
grandparent.child[dir2].child[RIGHT].isRed = false;
}
}
}
}
}
if (found != null) {
state.found = true;
state.value = found.getValue();
/**
* put the "node" values in "found" (the node with key K) and cut "node"
* out. However, we do not want to corrupt "found" -- issue 3423. So
* create a new node "newNode" to replace the "found" node.
*
* TODO: (jat's suggestion) Consider using rebalance to move the deleted
* node to a leaf to avoid the extra traversal in replaceNode.
*/
if (node != found) {
Node newNode = new Node(node.getKey(), node.getValue());
replaceNode(head, found, newNode);
if (parent == found) {
parent = newNode;
}
}
// cut "node" out
parent.child[parent.child[RIGHT] == node ? RIGHT : LEFT] = node.child[node.child[LEFT] == null
? RIGHT : LEFT];
size--;
}
root = head.child[RIGHT];
if (root != null) {
root.isRed = false;
}
return state.found;
}
/**
* replace 'node' with 'newNode' in the tree rooted at 'head'. Could have
* avoided this traversal if each node maintained a parent pointer.
*/
private void replaceNode(Node head, Node node, Node newNode) {
Node parent = head;
int direction = (parent.getKey() == null || cmp.compare(node.getKey(), parent.getKey()) > 0)
? RIGHT : LEFT; // parent.key == null handles the fake root node
while (parent.child[direction] != node) {
parent = parent.child[direction];
assert parent != null;
direction = cmp.compare(node.getKey(), parent.getKey()) > 0 ? RIGHT : LEFT;
}
// replace node with newNode
parent.child[direction] = newNode;
newNode.isRed = node.isRed;
newNode.child[LEFT] = node.child[LEFT];
newNode.child[RIGHT] = node.child[RIGHT];
node.child[LEFT] = null;
node.child[RIGHT] = null;
}
/**
* Perform a double rotation, first rotating the child which will become the
* root in the opposite direction, then rotating the root in the specified
* direction.
*
*
* A F
* B C becomes (with rotateDirection=0) A C
* D E F G B E G
* D
*
*
* @param tree root of the subtree to rotate
* @param rotateDirection the direction to rotate: 0=left, 1=right
* @return the new root of the rotated subtree
*/
private Node rotateDouble(Node tree, int rotateDirection) {
// free the pointer of the new root
int otherChildDir = otherChild(rotateDirection);
tree.child[otherChildDir] = rotateSingle(tree.child[otherChildDir], otherChildDir);
return rotateSingle(tree, rotateDirection);
}
/**
* Perform a single rotation, pushing the root of the subtree to the specified
* direction.
*
*
* A B
* B C becomes (with rotateDirection=1) D A
* D E E C
*
*
* @param tree the root of the subtree to rotate
* @param rotateDirection the direction to rotate: 0=left rotation, 1=right
* @return the new root of the rotated subtree
*/
private Node rotateSingle(Node tree, int rotateDirection) {
int otherChildDir = otherChild(rotateDirection);
Node save = tree.child[otherChildDir];
tree.child[otherChildDir] = save.child[rotateDirection];
save.child[rotateDirection] = tree;
tree.isRed = true;
save.isRed = false;
return save;
}
}