You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
524 lines
14 KiB
524 lines
14 KiB
/*
|
|
* This file is part of a Java port of the program ltl2dstar
|
|
* (http://www.ltl2dstar.de/) for PRISM (http://www.prismmodelchecker.org/)
|
|
* Copyright (C) 2005-2007 Joachim Klein <j.klein@ltl2dstar.de>
|
|
* Copyright (c) 2007 Carlos Bederian
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
package jltl2dstar;
|
|
|
|
import java.util.Iterator;
|
|
import java.io.PrintStream;
|
|
|
|
import jltl2ba.MyBitSet;
|
|
|
|
/** @file
|
|
* Provides class SafraTreeNode.
|
|
*/
|
|
|
|
/**
|
|
* A node in a SafraTree.
|
|
* Each node has: <br>
|
|
* - a name/id (integer)<br>
|
|
* - a final flag (bool)<br>
|
|
* - a parent (NULL for root node)<br>
|
|
* - an older brother (NULL if node is oldest)<br>
|
|
* - a younger brother (NULL if node is youngest)<br>
|
|
* - an oldestChild (NULL if node has no children<br>
|
|
* - a youngestChild (NULL if node has no children<br>
|
|
* - a label (BitSet) for the powerset
|
|
*/
|
|
public class SafraTreeNode implements Iterable<SafraTreeNode> {
|
|
|
|
/** The node name */
|
|
private int _id;
|
|
|
|
/** The label of the node (powerset) */
|
|
private MyBitSet _labeling;
|
|
|
|
/** The final flag */
|
|
private boolean _final_flag;
|
|
|
|
/** The parent node */
|
|
private SafraTreeNode _parent;
|
|
|
|
/** The older brother */
|
|
private SafraTreeNode _olderBrother;
|
|
|
|
/** The younger brother */
|
|
private SafraTreeNode _youngerBrother;
|
|
|
|
/** The oldest child */
|
|
private SafraTreeNode _oldestChild;
|
|
|
|
/** The youngest child */
|
|
private SafraTreeNode _youngestChild;
|
|
|
|
/** The number of children */
|
|
private int _childCount;
|
|
|
|
/**
|
|
* Constructor
|
|
* @param id the name of the node
|
|
*/
|
|
public SafraTreeNode(int id) {
|
|
_id = id;
|
|
_final_flag = false;
|
|
_parent = null;
|
|
_olderBrother = null;
|
|
_youngerBrother = null;
|
|
_oldestChild = null;
|
|
_youngestChild = null;
|
|
_childCount = 0;
|
|
_labeling = new MyBitSet();
|
|
}
|
|
|
|
/** Get the name of the node*/
|
|
public int getID() {return _id;}
|
|
|
|
/** Get the final flag */
|
|
public boolean hasFinalFlag() {return _final_flag;}
|
|
|
|
/** Get the number of children. */
|
|
public int getChildCount() {return _childCount;}
|
|
|
|
/** Get the labeling */
|
|
public MyBitSet getLabeling() {return _labeling;}
|
|
|
|
/**
|
|
* Get the older brother.
|
|
* @return the older brother, or NULL if node is oldest child.
|
|
*/
|
|
public SafraTreeNode getOlderBrother() {return _olderBrother;}
|
|
|
|
/**
|
|
* Get the younger brother.
|
|
* @return the younger brother, or NULL if node is youngest child.
|
|
*/
|
|
public SafraTreeNode getYoungerBrother() {return _youngerBrother;}
|
|
|
|
/**
|
|
* Get the oldest child.
|
|
* @return the oldest child, or NULL if node has no children
|
|
*/
|
|
public SafraTreeNode getOldestChild() {return _oldestChild;}
|
|
|
|
/**
|
|
* Get the youngest child.
|
|
* @return the youngest child, or NULL if node has no children
|
|
*/
|
|
public SafraTreeNode getYoungestChild() {return _youngestChild;}
|
|
|
|
/**
|
|
* Get the parent of the node.
|
|
* @return the parent, or NULL if node has no parent (is root node)
|
|
*/
|
|
public SafraTreeNode getParent() {return _parent;}
|
|
|
|
/**
|
|
* Set the final flag.
|
|
* @param finalFlag the value
|
|
*/
|
|
public void setFinalFlag(boolean finalFlag) {_final_flag=finalFlag;}
|
|
|
|
/**
|
|
* Set the labeling
|
|
* @param labeling the new labeling
|
|
*/
|
|
public void setLabeling(MyBitSet labeling) {_labeling=labeling;}
|
|
|
|
private boolean null_or_eq(SafraTreeNode a, SafraTreeNode b) {
|
|
return (a == null && b == null) || ((a != null && b != null) && (a.getID() == b.getID()));
|
|
}
|
|
|
|
/** Equality operator. Does not do a deep compare */
|
|
public boolean equals(SafraTreeNode other) {
|
|
if (_id != other._id) {return false;}
|
|
if (_final_flag != other._final_flag) {return false;}
|
|
if (_childCount != other._childCount) {return false;}
|
|
if (!_labeling.equals(other._labeling)) {return false;}
|
|
if (!null_or_eq(_parent, other._parent)) {return false;}
|
|
if (!null_or_eq(_olderBrother, other._olderBrother)) {return false;}
|
|
if (!null_or_eq(_youngerBrother, other._youngerBrother)) {return false;}
|
|
if (!null_or_eq(_oldestChild, other._oldestChild)) {return false;}
|
|
if (!null_or_eq(_youngestChild, other._youngestChild)) {return false;}
|
|
|
|
return true;
|
|
}
|
|
|
|
public boolean equals(Object other) {
|
|
if (other instanceof SafraTreeNode) {
|
|
return this.equals((SafraTreeNode) other);
|
|
}
|
|
else return false;
|
|
}
|
|
|
|
/**
|
|
* Equality operator ignoring the name of the nodes, doing a deep compare
|
|
* (checks that all children are also structurally equal.
|
|
*/
|
|
public boolean structuralEquals(SafraTreeNode other) {
|
|
if (!(_final_flag == other._final_flag)) {return false;}
|
|
if (!(_childCount == other._childCount)) {return false;}
|
|
if (!(_labeling.equals(other._labeling))) {return false;}
|
|
|
|
if (_childCount > 0) {
|
|
SafraTreeNode this_child = getOldestChild();
|
|
SafraTreeNode other_child = other.getOldestChild();
|
|
|
|
do {
|
|
if (!this_child.structuralEquals(other_child)) {
|
|
return false;
|
|
}
|
|
|
|
this_child = this_child.getYoungerBrother();
|
|
other_child = other_child.getYoungerBrother();
|
|
} while (this_child != null && other_child != null);
|
|
|
|
assert(this_child == null && other_child == null);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private int null_or_cmpID(SafraTreeNode a, SafraTreeNode b) {
|
|
if (a == null) {
|
|
if (b == null) return 0;
|
|
else return -1;
|
|
}
|
|
else {
|
|
if (b == null) return 1;
|
|
return a.getID() - b.getID();
|
|
}
|
|
}
|
|
/** Less-than operator. Does not do deep compare */
|
|
public boolean lessThan(SafraTreeNode other) {
|
|
|
|
if (_id < other._id) return true;
|
|
if (!_final_flag && other._final_flag) return true;
|
|
if (_childCount < other._childCount) return true;
|
|
if (_labeling.compareTo(other._labeling) < 0) return true;
|
|
|
|
if (null_or_cmpID(_parent, other._parent) != 0)
|
|
return (null_or_cmpID(_parent, other._parent) < 0);
|
|
if (null_or_cmpID(_olderBrother, other._olderBrother) != 0)
|
|
return (null_or_cmpID(_olderBrother, other._olderBrother) < 0);
|
|
if (null_or_cmpID(_youngerBrother, other._youngerBrother) != 0)
|
|
return (null_or_cmpID(_youngerBrother, other._youngerBrother) < 0);
|
|
if (null_or_cmpID(_oldestChild, other._oldestChild) != 0)
|
|
return (null_or_cmpID(_oldestChild, other._oldestChild) < 0);
|
|
if (null_or_cmpID(_youngestChild, other._youngestChild) != 0)
|
|
return (null_or_cmpID(_youngestChild, other._youngestChild) < 0);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Less-than operator ignoring the name of the nodes, doing a deep compare
|
|
* (applies recursively on the children).
|
|
*/
|
|
public boolean structuralLessThan(SafraTreeNode other) {
|
|
return (this.compareTo(other) < 0);
|
|
}
|
|
|
|
/** Do a structural comparison */
|
|
public int compareTo(SafraTreeNode other) {
|
|
int cmp;
|
|
|
|
if (_final_flag ^ other._final_flag)
|
|
return (_final_flag ? 1 : -1);
|
|
|
|
cmp = _childCount - other._childCount;
|
|
if (cmp != 0) {return cmp;}
|
|
|
|
cmp = _labeling.compareTo(other._labeling);
|
|
if (cmp != 0) {return cmp;}
|
|
|
|
// if we are here, this and other have the same number of children
|
|
if (_childCount > 0) {
|
|
SafraTreeNode this_child = getOldestChild();
|
|
SafraTreeNode other_child = other.getOldestChild();
|
|
|
|
do {
|
|
cmp = this_child.compareTo(other_child);
|
|
if (cmp != 0) {return cmp;}
|
|
|
|
this_child = this_child.getYoungerBrother();
|
|
other_child = other_child.getYoungerBrother();
|
|
} while (this_child != null && other_child != null);
|
|
|
|
// assert that there was really the same number of children
|
|
assert(this_child == null && other_child == null);
|
|
}
|
|
|
|
// when we are here, all children were equal
|
|
return 0;
|
|
}
|
|
|
|
/** Add a node as the youngest child */
|
|
public void addAsYoungestChild(SafraTreeNode other) {
|
|
assert(other != null);
|
|
assert(other.getParent() == null);
|
|
assert(other.getOlderBrother() == null);
|
|
assert(other.getYoungerBrother() == null);
|
|
|
|
if (_youngestChild != null) {
|
|
assert(_youngestChild._youngerBrother == null);
|
|
_youngestChild._youngerBrother = other;
|
|
other._olderBrother = _youngestChild;
|
|
}
|
|
|
|
other._parent = this;
|
|
_youngestChild = other;
|
|
if (_oldestChild == null) {
|
|
_oldestChild = other;
|
|
}
|
|
_childCount++;
|
|
}
|
|
|
|
/** Add a node as the oldest child */
|
|
public void addAsOldestChild(SafraTreeNode other) {
|
|
assert(other.getParent() == null);
|
|
assert(other.getOlderBrother() == null);
|
|
assert(other.getYoungerBrother() == null);
|
|
|
|
if (_oldestChild != null) {
|
|
assert(_oldestChild._olderBrother == null);
|
|
_oldestChild._olderBrother = other;
|
|
other._youngerBrother = _oldestChild;
|
|
}
|
|
|
|
other._parent = this;
|
|
_oldestChild = other;
|
|
if (_youngestChild == null) {
|
|
_youngestChild = other;
|
|
}
|
|
_childCount++;
|
|
}
|
|
|
|
/** Remove this node from the tree (relink siblings). The node is not allowed
|
|
* to have children! */
|
|
public void removeFromTree() {
|
|
assert(_childCount == 0);
|
|
|
|
if (_parent == null) {
|
|
// Root-Node or already removed from tree, nothing to do
|
|
return;
|
|
}
|
|
|
|
// Relink siblings
|
|
if (_olderBrother != null) {
|
|
_olderBrother._youngerBrother = _youngerBrother;
|
|
}
|
|
if (_youngerBrother != null) {
|
|
_youngerBrother._olderBrother = _olderBrother;
|
|
}
|
|
|
|
// Relink child-pointers in _parent
|
|
if (_parent._oldestChild == this) {
|
|
// this node is oldest child
|
|
_parent._oldestChild = this._youngerBrother;
|
|
}
|
|
|
|
if (_parent._youngestChild == this) {
|
|
// this node is youngest child
|
|
_parent._youngestChild = this._olderBrother;
|
|
}
|
|
|
|
_parent._childCount--;
|
|
|
|
_youngerBrother = null;
|
|
_olderBrother = null;
|
|
_parent = null;
|
|
}
|
|
|
|
/**
|
|
* Swap the places of two child nodes
|
|
*/
|
|
public void swapChildren(SafraTreeNode a, SafraTreeNode b) {
|
|
assert(a.getParent() == b.getParent() && a.getParent() == this);
|
|
|
|
if (a == b) {return;}
|
|
|
|
if (_oldestChild == a) {
|
|
_oldestChild = b;
|
|
} else if (_oldestChild == b) {
|
|
_oldestChild = a;
|
|
}
|
|
|
|
if (_youngestChild == a) {
|
|
_youngestChild = b;
|
|
} else if (_youngestChild == b) {
|
|
_youngestChild = a;
|
|
}
|
|
|
|
SafraTreeNode a_left = a._olderBrother;
|
|
SafraTreeNode b_left = b._olderBrother;
|
|
SafraTreeNode a_right = a._youngerBrother;
|
|
SafraTreeNode b_right = b._youngerBrother;
|
|
|
|
if (a_left != null) {a_left._youngerBrother = b;}
|
|
if (b_left != null) {b_left._youngerBrother = a;}
|
|
if (a_right != null) {a_right._olderBrother = b;}
|
|
if (b_right != null) {b_right._olderBrother = a;}
|
|
|
|
a._olderBrother = b_left;
|
|
a._youngerBrother = b_right;
|
|
b._olderBrother = a_left;
|
|
b._youngerBrother = a_right;
|
|
|
|
if (a_right == b) {
|
|
// a & b are direct neighbours, a to the left of b
|
|
a._olderBrother = b;
|
|
b._youngerBrother = a;
|
|
} else if (b_right == a) {
|
|
// a & b are direct neighbours, b to the left of a
|
|
a._youngerBrother = b;
|
|
b._olderBrother = a;
|
|
}
|
|
}
|
|
|
|
public Iterator<SafraTreeNode> iterator() {
|
|
return new SafraTreeNodeIterator(this);
|
|
}
|
|
|
|
public static class SafraTreeNodeIterator implements Iterator<SafraTreeNode> {
|
|
|
|
private SafraTreeNode _current;
|
|
|
|
public SafraTreeNodeIterator(SafraTreeNode node) {
|
|
_current = node.getOldestChild();
|
|
}
|
|
|
|
public SafraTreeNode next() {
|
|
SafraTreeNode tmp = _current;
|
|
_current = _current.getYoungerBrother();
|
|
return tmp;
|
|
}
|
|
|
|
public boolean hasNext() {
|
|
return (_current != null);
|
|
}
|
|
|
|
public void remove() {
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
}
|
|
|
|
/** Calculate the height of the subtree rooted at this node. */
|
|
public int treeHeight() {
|
|
int height = 0;
|
|
|
|
if (getChildCount() > 0) {
|
|
for (SafraTreeNode cur_child : this) {
|
|
int child_height = cur_child.treeHeight();
|
|
if (child_height > height) {
|
|
height = child_height;
|
|
}
|
|
}
|
|
}
|
|
return height + 1;
|
|
}
|
|
|
|
/** Calculate the width of the subtree rooted at this node. */
|
|
public int treeWidth() {
|
|
int width = 0;
|
|
|
|
if (getChildCount() > 0) {
|
|
for (SafraTreeNode cur_child : this) {
|
|
width += cur_child.treeWidth();
|
|
}
|
|
}
|
|
else width = 1;
|
|
|
|
return width;
|
|
}
|
|
|
|
/**
|
|
* Calculate a hashvalue using HashFunction for this node.
|
|
* @param hashfunction the HashFunction functor
|
|
* @param only_structure Ignore naming of the nodes?
|
|
*/
|
|
/* public void hashCode(HashFunction hashfunction, boolean only_structure) {
|
|
if (!only_structure) {
|
|
hashfunction.hash(getID());
|
|
}
|
|
|
|
getLabeling().hashCode(hashfunction);
|
|
hashfunction.hash(hasFinalFlag());
|
|
|
|
if (getChildCount () >0) {
|
|
for (SafraTreeNode c : this) {
|
|
c.hashCode(hashfunction, only_structure);
|
|
}
|
|
}
|
|
}*/
|
|
|
|
public int hashCode() {
|
|
int hash = 1;
|
|
hash += (hasFinalFlag() ? 1 : 0);
|
|
hash = hash * 31 + getLabeling().hashCode();
|
|
if (getChildCount () > 0) {
|
|
for (SafraTreeNode c : this) {
|
|
hash = hash * 31 + c.hashCode();
|
|
}
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
/**
|
|
* Print node to output stream
|
|
*/
|
|
public void print(PrintStream out) {
|
|
out.print(_id + " ");
|
|
_labeling.print(out);
|
|
if (_final_flag) {out.print(" !");}
|
|
}
|
|
|
|
/** Print HTML version of this node to output stream */
|
|
public void toHTML(PrintStream out) {
|
|
out.print(this.toHTMLString());
|
|
}
|
|
|
|
public String toHTMLString() {
|
|
StringBuffer buf = new StringBuffer("<TABLE><TR>");
|
|
|
|
if (getChildCount() <= 1) {
|
|
buf.append("<TD>");
|
|
} else {
|
|
buf.append("<TD COLSPAN=\"");
|
|
buf.append(getChildCount());
|
|
buf.append("\">");
|
|
}
|
|
|
|
buf.append(getID());
|
|
buf.append(" ");
|
|
buf.append(_labeling);
|
|
if (_final_flag) {buf.append("!");}
|
|
buf.append("</TD></TR>");
|
|
if (getChildCount() > 0) {
|
|
buf.append("<TR>");
|
|
for (SafraTreeNode child : this) {
|
|
buf.append("<TD>");
|
|
buf.append(child.toHTMLString());
|
|
buf.append("</TD>");
|
|
}
|
|
buf.append("</TR>");
|
|
}
|
|
buf.append("</TABLE>");
|
|
|
|
return buf.toString();
|
|
}
|
|
|
|
}
|