/*******************************************************************************
* Copyright (c) 2012-present Jakub Kováč, Jozef Brandýs, Katarína Kotrlová,
* Pavol Lukča, Ladislav Pápay, Viktor Tomkovič, Tatiana Tóthová
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package algvis.ds.dictionaries.bst;
import algvis.core.DataStructure;
import algvis.core.Node;
import algvis.core.NodeColor;
import algvis.core.NodePair;
import algvis.core.history.HashtableStoreSupport;
import algvis.ui.Fonts;
import algvis.ui.view.Layout;
import algvis.ui.view.View;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.util.Hashtable;
import java.util.Vector;
public class BSTNode extends Node {
private BSTNode left = null, right = null, parent = null;
public int leftw, rightw;
// variables for the Reingold-Tilford layout
private int offset = 0; // offset from parent node
private int level; // distance to root
private boolean thread = false; // is this node threaded?
// statistics
public int size = 1, height = 1, sumh = 1;
protected BSTNode(DataStructure D, int key, int x, int y) {
super(D, key, x, y);
}
public BSTNode(DataStructure D, int key, int zDepth) {
super(D, key, zDepth);
}
public BSTNode(DataStructure d, int key, int x, int y, int zDepth) {
super(d, key, x, y, zDepth);
}
public BSTNode getLeft() {
if (thread) {
return null;
} else {
return left;
}
}
public void setLeft(BSTNode left) {
if (thread) {
thread = false;
right = null;
}
this.left = left;
}
public BSTNode getRight() {
if (thread) {
return null;
} else {
return right;
}
}
public BSTNode setRight(BSTNode right) {
if (thread) {
thread = false;
left = null;
}
this.right = right;
return right;
}
public BSTNode getParent() {
return parent;
}
public BSTNode setParent(BSTNode parent) {
return this.parent = parent;
}
public void setLevel(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
public boolean isRoot() {
return getParent() == null;
}
public boolean isLeaf() {
return getLeft() == null && getRight() == null;
}
public boolean isLeft() {
return getParent() != null && getParent().getLeft() == this;
}
/**
* removes edge between this and left; removes edge between newLeft and its
* parent; creates new edge between this and newLeft
*/
public void linkLeft(BSTNode newLeft) {
if (getLeft() != newLeft) {
if (getLeft() != null) {
// remove edge between this and left
unlinkLeft();
}
if (newLeft != null) {
if (newLeft.getParent() != null) {
// remove edge between newLeft and its parent
newLeft.unlinkParent();
}
// create new edge between this and newLeft
newLeft.setParent(this);
}
setLeft(newLeft);
}
}
/**
* removes edge between this and left
*/
public void unlinkLeft() {
getLeft().setParent(null);
setLeft(null);
}
/**
* removes edge between this and right; removes edge between newRight and
* its parent; creates new edge between this and newRight
*/
public void linkRight(BSTNode newRight) {
if (getRight() != newRight) {
if (getRight() != null) {
// remove edge between this and right
unlinkRight();
}
if (newRight != null) {
if (newRight.getParent() != null) {
// remove edge between newRight and its parent
newRight.unlinkParent();
}
// create new edge between this and newRight
newRight.setParent(this);
}
setRight(newRight);
}
}
/**
* removes edge between this and right
*/
public void unlinkRight() {
getRight().setParent(null);
setRight(null);
}
private void unlinkParent() {
if (isLeft()) {
getParent().unlinkLeft();
} else {
getParent().unlinkRight();
}
}
public void isolate() {
setLeft(setRight(setParent(null)));
}
private void _preorder(Vector<BSTNode> acc) {
acc.add(this);
if (getLeft() != null) {
getLeft()._preorder(acc);
}
if (getRight() != null) {
getRight()._preorder(acc);
}
}
private void _inorder(Vector<BSTNode> acc) {
if (getLeft() != null) {
getLeft()._inorder(acc);
}
acc.add(this);
if (getRight() != null) {
getRight()._inorder(acc);
}
}
private void _postorder(Vector<BSTNode> acc) {
if (getLeft() != null) {
getLeft()._postorder(acc);
}
if (getRight() != null) {
getRight()._postorder(acc);
}
acc.add(this);
}
public Vector<BSTNode> preorder() {
Vector<BSTNode> acc = new Vector<BSTNode>();
this._preorder(acc);
return acc;
}
public Vector<BSTNode> inorder() {
Vector<BSTNode> acc = new Vector<BSTNode>();
this._inorder(acc);
return acc;
}
public Vector<BSTNode> postorder() {
Vector<BSTNode> acc = new Vector<BSTNode>();
this._postorder(acc);
return acc;
}
/**
* Calculate the height, size, and sum of heights of this node, assuming
* that this was already calculated for its children.
*/
public void calc() {
int ls = 0, rs = 0, lh = 0, rh = 0, lsh = 0, rsh = 0;
if (getLeft() != null) {
ls = getLeft().size;
lh = getLeft().height;
lsh = getLeft().sumh;
}
if (getRight() != null) {
rs = getRight().size;
rh = getRight().height;
rsh = getRight().sumh;
}
size = ls + rs + 1;
height = Math.max(lh, rh) + 1;
sumh = lsh + rsh + size;
}
/**
* Calculate the height, size, and sum of heights for all the nodes in this
* subtree (recursively bottom-up).
*/
public void calcTree() {
for (BSTNode v : postorder()) {
v.calc();
}
}
public void setArc() {
setArc(getParent());
}
// private static int i;
public void drawTree(View v) {
if (D instanceof BST && ((BST) D).order) {
int i = 0;
v.setColor(Color.LIGHT_GRAY);
for (BSTNode w : inorder()) {
++i;
if (i % 10 == 0) {
v.drawLine(w.x, w.y, w.x, -22);
v.drawString("" + i, w.x, -29, Fonts.NORMAL);
} else {
v.drawLine(w.x, w.y, w.x, -20);
v.drawString("" + i % 10, w.x, -27, (i % 10 == 5) ? Fonts.NORMAL : Fonts.SMALL);
}
}
}
for (BSTNode w : postorder()) {
if (w.state != INVISIBLE && !w.isRoot()) {
v.setColor(Color.black);
v.drawLine(w.x, w.y, w.getParent().x, w.getParent().y);
}
w.draw(v);
}
}
public void moveTree() {
for (BSTNode v : postorder()) {
v.move();
}
}
public void shiftTree(int dx, int dy) {
for (BSTNode v : postorder()) {
v.goTo(v.tox + dx, v.toy + dy);
}
}
@Override
public Rectangle2D getBoundingBox() {
Rectangle2D retVal = super.getBoundingBox();
if (left != null) {
retVal = retVal.createUnion(left.getBoundingBox());
}
if (right != null) {
retVal = retVal.createUnion(right.getBoundingBox());
}
return retVal;
}
@Override
public void endAnimation() {
super.endAnimation();
if (left != null) {
left.endAnimation();
}
if (right != null) {
right.endAnimation();
}
}
@Override
public boolean isAnimationDone() {
return super.isAnimationDone()
&& (left == null || left.isAnimationDone())
&& (right == null || right.isAnimationDone());
}
/**
* Create an (imaginary) box around the subtree rooted at this node.
* Calculate the width from the node to the left side (leftw) and the width
* from the node to the right side (rightw). Assumption: this box has
* already been created for both children.
*/
protected void rebox() {
/*
* if there is a left child, leftw = width of the box enclosing the
* whole left subtree, i.e., leftw+rightw; otherwise the width is the
* node RADIUS plus some additional space called xspan
*/
leftw = (getLeft() == null) ? DataStructure.minsepx / 2
: getLeft().leftw + getLeft().rightw;
// rightw is computed analogically
rightw = (getRight() == null) ? DataStructure.minsepx / 2
: getRight().leftw + getRight().rightw;
}
/**
* Rebox the whole subtree calculating the widths in postorder.
*/
public void reboxTree() {
for (BSTNode v : postorder()) {
v.rebox();
}
}
/**
* Calculate the coordinates of each node from the widths of boxes around
* them and direct the nodes to their new positions.
*/
private void repos() {
if (isRoot()) {
goToRoot();
D.x1 = -leftw;
D.x2 = rightw;
D.y2 = this.toy;
}
if (this.toy > D.y2) {
D.y2 = this.toy;
}
if (getLeft() != null) {
getLeft().goTo(this.tox - getLeft().rightw,
this.toy + DataStructure.minsepy);
getLeft().repos();
}
if (getRight() != null) {
getRight().goTo(this.tox + getRight().leftw,
this.toy + DataStructure.minsepy);
getRight().repos();
}
}
public void repos(int x, int y) {
goTo(x, y);
if (getLeft() != null) {
getLeft().repos(this.tox - getLeft().rightw,
this.toy + DataStructure.minsepy);
}
if (getRight() != null) {
getRight().repos(this.tox + getRight().leftw,
this.toy + DataStructure.minsepy);
}
if (isRoot()) {
D.x1 = x - leftw;
D.x2 = x + rightw;
D.y2 = this.toy;
}
if (this.toy > D.y2) {
D.y2 = this.toy;
}
}
public void reposition() {
if (D.getLayout() == Layout.SIMPLE) { // simple layout
reboxTree();
repos();
} else { // Reingold-Tilford layout
RTThreads();
RTPreposition();
RTPetrification(0, 0);
reboxTree();
}
}
private void RTThreads() {
if (thread) {
thread = false;
left = null;
right = null;
}
if (getLeft() != null) {
left.RTThreads();
}
if (getRight() != null) {
right.RTThreads();
}
}
/**
* Find the node at coordinates (x,y). This is used to identify the node
* that has been clicked by user.
*/
public BSTNode find(int x, int y) {
if (inside(x, y)) {
return this;
}
if (getLeft() != null) {
final BSTNode tmp = getLeft().find(x, y);
if (tmp != null) {
return tmp;
}
}
if (getRight() != null) {
return getRight().find(x, y);
}
return null;
}
/**
* Set up the threads with the help of extreme nodes. A node is "extreme"
* when it is the leftmost/rightmost in the lowest level.
* <p/>
* 1. work out left and right subtree 2. get extreme nodes from the left and
* right subtree 3. calculate the offset from parent & set a new thread if
* required
*
* @return the leftmost and the rightmost node on the deepest level of the
* subtree rooted at this node
*/
private NodePair<BSTNode> RTPreposition() {
final NodePair<BSTNode> result = new NodePair<BSTNode>();
NodePair<BSTNode> fromLeftSubtree = null, fromRightSubtree = null;
offset = 0;
// 1. & 2. work out left & right subtree
if (getLeft() != null) {
fromLeftSubtree = getLeft().RTPreposition();
}
if (getRight() != null) {
fromRightSubtree = getRight().RTPreposition();
}
// 3. examine this node
if (isLeaf()) {
if (!isRoot()) {
offset = isLeft() ? -DataStructure.minsepx / 2
: +DataStructure.minsepx / 2;
}
result.left = this;
result.right = this;
} else { // This is not a leaf; at least one subtree is non-empty.
/*
* If one subtree is empty, it is not necessary to make a new
* thread. A proper offset must be set.
*/
if (getLeft() == null) {
getRight().offset = DataStructure.minsepx / 2;
result.left = fromRightSubtree.left;
result.right = fromRightSubtree.right;
return result;
}
if (getRight() == null) {
getLeft().offset = -DataStructure.minsepx / 2;
result.left = fromLeftSubtree.left;
result.right = fromLeftSubtree.right;
return result;
}
// Calculate offsets for the left and the right son.
int loffset = 0; // offset of this node from the right contour of
// the left subtree.
int roffset = 0; // offset of this node from the left contour of the
// right subtree.
BSTNode L = getLeft();
BSTNode R = getRight();
/*
* First, left.offset is 0 and only right.offset accumulates. The
* offsets are corrected afterwards (this way is easier to
* generalize to m-ary trees). Note that offsets can be negative.
*/
getLeft().offset = 0;
getRight().offset = 0;
// traverse the right contour of the left subtree and the left
// counour of the right subtree
while ((L != null) && (R != null)) {
/*
* left.offset + loffset is the horizontal distance from L to
* this node. Similarly, right.offset + roffset is the
* horizontal distance from R to this node.
*/
final int distance = (loffset + DataStructure.minsepx - roffset);
if (distance > 0) {
getRight().offset += distance;
roffset += distance;
}
/*
* When passes through thread there will be for sure incorrect
* offset! So Elevator calculate this new offset. In algorithm
* TR published by Reingold this value is already calculated.
*/
boolean LwasThread = L.thread;
final boolean RwasThread = R.thread;
L = (L.right != null) ? L.right : L.left;
if (L != null) {
loffset += L.offset;
}
R = (R.left != null) ? R.left : R.right;
if (R != null) {
roffset += R.offset;
}
BSTNode Elevator = null;
if (LwasThread) {
LwasThread = false;
loffset = 0;
Elevator = L;
while (Elevator != this) {
loffset += Elevator.offset;
Elevator = Elevator.getParent();
}
}
if (RwasThread) {
roffset = 0;
Elevator = R;
while (Elevator != this) {
roffset += Elevator.offset;
Elevator = Elevator.getParent();
}
}
}
/*
* Now, distances should be 0 for left and some value for right.. So
* lets change it
*/
getRight().offset /= 2;
getLeft().offset = -getRight().offset;
/*
* General switch of making a new thread: we want to make a thread
* iff one pair of extremes is deeper than others. We assume that
* threads from subtrees are set properly.
*/
if ((R != null) && (L == null)) { // the right subtree is deeper
// than the left subtree
fromLeftSubtree.left.thread = true;
fromLeftSubtree.left.right = R;
result.left = fromRightSubtree.left;
result.right = fromRightSubtree.right;
} else if ((L != null) && (R == null)) { // the left subtree is
// deeper than the right
// subtree
fromRightSubtree.right.thread = true;
fromRightSubtree.right.left = L;
result.left = fromLeftSubtree.left;
result.right = fromLeftSubtree.right;
} else if ((L == null) && (R == null)) { // both subtrees have the
// same height
result.left = fromLeftSubtree.left;
result.right = fromRightSubtree.right;
}
}
return result;
}
/**
* Calculate the absolute coordinates from the relative ones and dispose the
* threads.
*
* @param x real x coordinate of parent node
*/
private void RTPetrification(int x, int y) {
goTo(x + offset, y);
if (tox < D.x1) {
D.x1 = tox;
}
if (tox > D.x2) {
D.x2 = tox;
}
if (toy < D.y1) {
// this case should be always false
D.y1 = toy;
}
if (toy > D.y2) {
D.y2 = toy;
}
if (thread) {
// thread = false;
// setLeft(null);
// setRight(null);
}
if (getLeft() != null) {
getLeft().RTPetrification(tox, y + DataStructure.minsepy);
}
if (getRight() != null) {
getRight().RTPetrification(tox, y + DataStructure.minsepy);
}
}
/**
* Set color to this subtree.
*
* @param color
*/
public void subtreeColor(NodeColor color) {
for (BSTNode v : postorder()) {
v.setColor(color);
}
}
@Override
public void storeState(Hashtable<Object, Object> state) {
super.storeState(state);
HashtableStoreSupport.store(state, hash + "left", left);
HashtableStoreSupport.store(state, hash + "right", right);
HashtableStoreSupport.store(state, hash + "parent", parent);
HashtableStoreSupport.store(state, hash + "level", level);
HashtableStoreSupport.store(state, hash + "thread", thread);
HashtableStoreSupport.store(state, hash + "leftw", leftw);
HashtableStoreSupport.store(state, hash + "rightw", rightw);
if (left != null) {
left.storeState(state);
}
if (right != null) {
right.storeState(state);
}
}
@Override
public void restoreState(Hashtable<?, ?> state) {
super.restoreState(state);
final Object left = state.get(hash + "left");
if (left != null) {
this.left = (BSTNode) HashtableStoreSupport.restore(left);
}
final Object right = state.get(hash + "right");
if (right != null) {
this.right = (BSTNode) HashtableStoreSupport.restore(right);
}
final Object parent = state.get(hash + "parent");
if (parent != null) {
this.parent = (BSTNode) HashtableStoreSupport.restore(parent);
}
final Object level = state.get(hash + "level");
if (level != null) {
this.level = (Integer) HashtableStoreSupport.restore(level);
}
final Object thread = state.get(hash + "thread");
if (thread != null) {
this.thread = (Boolean) HashtableStoreSupport.restore(thread);
}
final Object leftw = state.get(hash + "leftw");
if (leftw != null) {
this.leftw = (Integer) HashtableStoreSupport.restore(leftw);
}
final Object rightw = state.get(hash + "rightw");
if (rightw != null) {
this.rightw = (Integer) HashtableStoreSupport.restore(rightw);
}
if (this.left != null) {
this.left.restoreState(state);
}
if (this.right != null) {
this.right.restoreState(state);
}
}
public boolean testStructure() {
for (BSTNode v : postorder()) {
if (v.getLeft() != null && v.getLeft().getParent() != v) {
return false;
}
if (v.getRight() != null && v.getRight().getParent() != v) {
return false;
}
}
return true;
}
public boolean testOrder() {
Vector<BSTNode> order = inorder();
for (int i = 0; i < order.size() - 1; ++i) {
if (order.get(i).getKey() >= order.get(i + 1).getKey()) {
return false;
}
}
return true;
}
}