/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org
This file is part of Gephi.
Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Gephi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Gephi. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.utils.collection.avl;
import java.lang.reflect.Array;
import java.util.Iterator;
/**
* Special type of AVL tree which possess a {@link AVLItemAccessor}. It allows to configure the indexes returned
* by the tree nodes
*
* @author Mathieu Bastian
* @param <Item> The type of Object in the tree
*/
public class ParamAVLTree<Item> implements Iterable<Item> {
protected ParamAVLNode<Item> root;
protected int count;
private AVLItemAccessor<Item> accessor;
public ParamAVLTree(AVLItemAccessor<Item> accessor) {
this.accessor = accessor;
}
public ParamAVLTree() {
}
public boolean add(Item item) {
ParamAVLNode<Item> p = this.root;
if (p == null) {
this.root = new ParamAVLNode(item);
} else {
while (true) {
int c = accessor.getNumber(item) - accessor.getNumber(p.item);
if (c < 0) {
if (p.left != null) {
p = p.left;
} else {
p.left = new ParamAVLNode(item, p);
p.balance--;
break;
}
} else if (c > 0) {
if (p.right != null) {
p = p.right;
} else {
p.right = new ParamAVLNode(item, p);
p.balance++;
break;
}
} else {
return false;
}
}
while ((p.balance != 0) && (p.parent != null)) {
if (p.parent.left == p) {
p.parent.balance--;
} else {
p.parent.balance++;
}
p = p.parent;
if (p.balance == -2) {
ParamAVLNode x = p.left;
if (x.balance == -1) {
x.parent = p.parent;
if (p.parent == null) {
this.root = x;
} else {
if (p.parent.left == p) {
p.parent.left = x;
} else {
p.parent.right = x;
}
}
p.left = x.right;
if (p.left != null) {
p.left.parent = p;
}
x.right = p;
p.parent = x;
x.balance = 0;
p.balance = 0;
} else {
ParamAVLNode w = x.right;
w.parent = p.parent;
if (p.parent == null) {
this.root = w;
} else {
if (p.parent.left == p) {
p.parent.left = w;
} else {
p.parent.right = w;
}
}
x.right = w.left;
if (x.right != null) {
x.right.parent = x;
}
p.left = w.right;
if (p.left != null) {
p.left.parent = p;
}
w.left = x;
w.right = p;
x.parent = w;
p.parent = w;
if (w.balance == -1) {
x.balance = 0;
p.balance = 1;
} else if (w.balance == 0) {
x.balance = 0;
p.balance = 0;
} else // w.balance == 1
{
x.balance = -1;
p.balance = 0;
}
w.balance = 0;
}
break;
} else if (p.balance == 2) {
ParamAVLNode x = p.right;
if (x.balance == 1) {
x.parent = p.parent;
if (p.parent == null) {
this.root = x;
} else {
if (p.parent.left == p) {
p.parent.left = x;
} else {
p.parent.right = x;
}
}
p.right = x.left;
if (p.right != null) {
p.right.parent = p;
}
x.left = p;
p.parent = x;
x.balance = 0;
p.balance = 0;
} else {
ParamAVLNode w = x.left;
w.parent = p.parent;
if (p.parent == null) {
this.root = w;
} else {
if (p.parent.left == p) {
p.parent.left = w;
} else {
p.parent.right = w;
}
}
x.left = w.right;
if (x.left != null) {
x.left.parent = x;
}
p.right = w.left;
if (p.right != null) {
p.right.parent = p;
}
w.right = x;
w.left = p;
x.parent = w;
p.parent = w;
if (w.balance == 1) {
x.balance = 0;
p.balance = -1;
} else if (w.balance == 0) {
x.balance = 0;
p.balance = 0;
} else // w.balance == -1
{
x.balance = 1;
p.balance = 0;
}
w.balance = 0;
}
break;
}
}
}
this.count++;
return true;
}
public boolean remove(Item item) {
ParamAVLNode<Item> p = this.root;
while (p != null) {
int c = accessor.getNumber(item) - accessor.getNumber(p.item);
if (c < 0) {
p = p.left;
} else if (c > 0) {
p = p.right;
} else {
ParamAVLNode y; // node from which rebalancing begins
int choice = 0; //0:Done 1:Left 2:Right
if (p.right == null) // Case 1: p has no right child
{
if (p.left != null) {
p.left.parent = p.parent;
}
if (p.parent == null) {
this.root = p.left;
count--;
return true;
}
if (p == p.parent.left) {
p.parent.left = p.left;
y = p.parent;
choice = 1;
// goto LeftDelete;
} else {
p.parent.right = p.left;
y = p.parent;
choice = 2;
//goto RightDelete;
}
} else if (p.right.left == null) // Case 2: p's right child has no left child
{
if (p.left != null) {
p.left.parent = p.right;
p.right.left = p.left;
}
p.right.balance = p.balance;
p.right.parent = p.parent;
if (p.parent == null) {
this.root = p.right;
} else {
if (p == p.parent.left) {
p.parent.left = p.right;
} else {
p.parent.right = p.right;
}
}
y = p.right;
choice = 2;
//goto RightDelete;
} else // Case 3: p's right child has a left child
{
ParamAVLNode s = p.right.left;
while (s.left != null) {
s = s.left;
}
if (p.left != null) {
p.left.parent = s;
s.left = p.left;
}
s.parent.left = s.right;
if (s.right != null) {
s.right.parent = s.parent;
}
p.right.parent = s;
s.right = p.right;
y = s.parent; // for rebalacing, must be set before we change s.parent
s.balance = p.balance;
s.parent = p.parent;
if (p.parent == null) {
this.root = s;
} else {
if (p == p.parent.left) {
p.parent.left = s;
} else {
p.parent.right = s;
}
}
choice = 1;
// goto LeftDelete;
}
// rebalancing begins
while (choice != 0) {
if (choice == 1) {
//LeftDelete:
y.balance++;
if (y.balance == 1) {
//goto Done;
choice = 0;
} else if (y.balance == 2) {
ParamAVLNode x = y.right;
if (x.balance == -1) {
ParamAVLNode w = x.left;
w.parent = y.parent;
if (y.parent == null) {
this.root = w;
} else {
if (y.parent.left == y) {
y.parent.left = w;
} else {
y.parent.right = w;
}
}
x.left = w.right;
if (x.left != null) {
x.left.parent = x;
}
y.right = w.left;
if (y.right != null) {
y.right.parent = y;
}
w.right = x;
w.left = y;
x.parent = w;
y.parent = w;
if (w.balance == 1) {
x.balance = 0;
y.balance = -1;
} else if (w.balance == 0) {
x.balance = 0;
y.balance = 0;
} else // w.balance == -1
{
x.balance = 1;
y.balance = 0;
}
w.balance = 0;
y = w; // for next iteration
} else {
x.parent = y.parent;
if (y.parent != null) {
if (y.parent.left == y) {
y.parent.left = x;
} else {
y.parent.right = x;
}
} else {
this.root = x;
}
y.right = x.left;
if (y.right != null) {
y.right.parent = y;
}
x.left = y;
y.parent = x;
if (x.balance == 0) {
x.balance = -1;
y.balance = 1;
//goto Done
choice = 0;
} else {
x.balance = 0;
y.balance = 0;
y = x; // for next iteration
}
}
}
} else if (choice == 2) {
//goto LoopTest;
//RightDelete:
y.balance--;
if (y.balance == -1) {
choice = 0;
//goto Done;
} else if (y.balance == -2) {
ParamAVLNode x = y.left;
if (x.balance == 1) {
ParamAVLNode w = x.right;
w.parent = y.parent;
if (y.parent == null) {
this.root = w;
} else {
if (y.parent.left == y) {
y.parent.left = w;
} else {
y.parent.right = w;
}
}
x.right = w.left;
if (x.right != null) {
x.right.parent = x;
}
y.left = w.right;
if (y.left != null) {
y.left.parent = y;
}
w.left = x;
w.right = y;
x.parent = w;
y.parent = w;
if (w.balance == -1) {
x.balance = 0;
y.balance = 1;
} else if (w.balance == 0) {
x.balance = 0;
y.balance = 0;
} else // w.balance == 1
{
x.balance = -1;
y.balance = 0;
}
w.balance = 0;
y = w; // for next iteration
} else {
x.parent = y.parent;
if (y.parent != null) {
if (y.parent.left == y) {
y.parent.left = x;
} else {
y.parent.right = x;
}
} else {
this.root = x;
}
y.left = x.right;
if (y.left != null) {
y.left.parent = y;
}
x.right = y;
y.parent = x;
if (x.balance == 0) {
x.balance = 1;
y.balance = -1;
choice = 0;
//goto Done;
} else {
x.balance = 0;
y.balance = 0;
y = x; // for next iteration
}
}
}
}
if (choice == 0) {
this.count--;
return true;
}
//LoopTest: {
if (y.parent != null) {
if (y == y.parent.left) {
y = y.parent;
choice = 1;
// goto LeftDelete;
} else {
y = y.parent;
choice = 2;
//goto RightDelete;
}
} else {
//Done
this.count--;
return true;
}
}
}
}
return false;
}
public boolean contains(Item item) {
ParamAVLNode<Item> p = this.root;
while (p != null) {
int c = accessor.getNumber(item) - accessor.getNumber(p.item);
if (c < 0) {
p = p.left;
} else if (c > 0) {
p = p.right;
} else {
return true;
}
}
return false;
}
public Item getItem(int number) {
ParamAVLNode<Item> p = this.root;
while (p != null) {
int c = number - accessor.getNumber(p.item);
if (c < 0) {
p = p.left;
} else if (c > 0) {
p = p.right;
} else {
return p.item;
}
}
return null;
}
public void clear() {
this.root = null;
this.count = 0;
}
public Iterator<Item> iterator() {
return new ParamAVLIterator(this);
}
public int getCount() {
return count;
}
public boolean isEmpty() {
return count==0;
}
public AVLItemAccessor<Item> getAccessor() {
return accessor;
}
public void setAccessor(AVLItemAccessor<Item> accessor) {
this.accessor = accessor;
}
public Item[] toArray(Item[] array) {
Item[] result = (Item[]) java.lang.reflect.Array.newInstance(array.getClass().getComponentType(), count);
if(count==0)
return result;
ParamAVLIterator<Item> itr = new ParamAVLIterator<Item>(root);
for (int i = 0; itr.hasNext(); i++) {
Item item = itr.next();
result[i] = item;
}
return result;
}
}