|
@@ -3,28 +3,68 @@ package com.pact;
|
|
|
import java.util.*;
|
|
|
|
|
|
public class Node {
|
|
|
- static int ID = 0;
|
|
|
|
|
|
- Node parent;
|
|
|
+ public static class MatchedNodePair {
|
|
|
+ Node node1;
|
|
|
+ Node node2;
|
|
|
+
|
|
|
+ MatchedNodePair(Node n1, Node n2) {
|
|
|
+ this.node1 = n1;
|
|
|
+ this.node2 = n2;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Gets size of shared node (just length of venn)
|
|
|
+ * @return size of node
|
|
|
+ */
|
|
|
+ int nodesSize()
|
|
|
+ {
|
|
|
+ return this.node1.getVenn().length();
|
|
|
+ }
|
|
|
+
|
|
|
+ public String toString() {
|
|
|
+ return "<" + this.node1 + ", " + this.node2 + ">";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private Node parent;
|
|
|
private String venn;
|
|
|
private List<Node> children;
|
|
|
- List<Node> linkNodes;
|
|
|
- private Node linkNode;
|
|
|
-
|
|
|
- private final int id = ++ID; // Unique Identifier for all created nodes
|
|
|
+ private Node linkNode = null;
|
|
|
+ private List<Node> linkNodes = new ArrayList<>();
|
|
|
|
|
|
- private int hash;
|
|
|
+ private int hash = 0;
|
|
|
|
|
|
- public Node(Node p, String s) {
|
|
|
- this.linkNode = null;
|
|
|
- this.linkNodes = new ArrayList<>();
|
|
|
- this.venn = s;
|
|
|
+ public Node(Node p, String venn) {
|
|
|
+ this.venn = venn;
|
|
|
this.parent = p;
|
|
|
- this.hash = 0;
|
|
|
|
|
|
this.children = new ArrayList<>();
|
|
|
- if (s.length() > 1)
|
|
|
- setChildren(s);
|
|
|
+ if (venn.length() > 1)
|
|
|
+ setChildren(venn);
|
|
|
+ updateHash();
|
|
|
+ }
|
|
|
+
|
|
|
+ private Node(Node p, Collection<Node> children) {
|
|
|
+ this.parent = p;
|
|
|
+ this.children = new ArrayList<>();
|
|
|
+ this.children.addAll(children);
|
|
|
+ for(Node child : children) {
|
|
|
+ Node child_ = child.copy();
|
|
|
+ child_.parent = this;
|
|
|
+ this.children.add(child_);
|
|
|
+ }
|
|
|
+
|
|
|
+ updateVenn();
|
|
|
+ updateHash();
|
|
|
+ }
|
|
|
+
|
|
|
+ private Node(Node p, char s) {
|
|
|
+ this(p, ""+s);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void updateHash(){
|
|
|
+ hash = 0;
|
|
|
if (children.isEmpty()) {
|
|
|
hash = (venn.hashCode()*1073676287) ^ (venn.hashCode()*97);
|
|
|
} else {
|
|
@@ -35,20 +75,63 @@ public class Node {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public Node(Node p, char s) {
|
|
|
- this(p, ""+s);
|
|
|
+ private void updateVenn() {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ for(Node child : children){
|
|
|
+ sb.append(child.venn);
|
|
|
+ }
|
|
|
+ venn = "(" + sb.toString() + ")";
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- public boolean isRoot() {
|
|
|
+ private boolean isRoot() {
|
|
|
return parent == null;
|
|
|
}
|
|
|
|
|
|
- public boolean isLeaf() {
|
|
|
+ private boolean isLeaf() {
|
|
|
return children.isEmpty();
|
|
|
}
|
|
|
|
|
|
- public int nSiblings() {
|
|
|
+ /**
|
|
|
+ * Traverses the tree with root at given node and returns all leaf nodes
|
|
|
+ * @return Leaf nodes under this root
|
|
|
+ */
|
|
|
+ private List<Node> getLeaves() {
|
|
|
+ List<Node> result = new ArrayList<>();
|
|
|
+ // Use stack to avoid recursion here
|
|
|
+ Stack<Node> searchNodes = new Stack<>();
|
|
|
+ searchNodes.push(this);
|
|
|
+ while (!searchNodes.isEmpty()) {
|
|
|
+ Node curr = searchNodes.pop();
|
|
|
+ if (curr.isLeaf()) {
|
|
|
+ result.add(curr);
|
|
|
+ } else {
|
|
|
+ searchNodes.addAll(curr.children);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Traverses the tree with root at given node and returns the number of leaves found.
|
|
|
+ * @return Number of leaf nodes under this root
|
|
|
+ */
|
|
|
+ private int getNLeaves() {
|
|
|
+ int result = 0;
|
|
|
+ // Use stack to avoid recursion here
|
|
|
+ Stack<Node> searchNodes = new Stack<>();
|
|
|
+ searchNodes.push(this);
|
|
|
+ while (!searchNodes.isEmpty()) {
|
|
|
+ Node curr = searchNodes.pop();
|
|
|
+ if (curr.isLeaf()) {
|
|
|
+ result++;
|
|
|
+ } else {
|
|
|
+ searchNodes.addAll(curr.children);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private int nSiblings() {
|
|
|
if (isRoot()) return 0;
|
|
|
else return parent.children.size() - 1;
|
|
|
}
|
|
@@ -103,7 +186,7 @@ public class Node {
|
|
|
return this.venn;
|
|
|
}
|
|
|
|
|
|
- List<Node> getSiblings() {
|
|
|
+ private List<Node> getSiblings() {
|
|
|
if (this.parent != null) {
|
|
|
List<Node> sibs = new ArrayList<>(parent.children);
|
|
|
sibs.remove(this);
|
|
@@ -112,6 +195,7 @@ public class Node {
|
|
|
return new ArrayList<>();
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
public boolean equals(Object obj) {
|
|
|
if (this == obj) return true; // Literally the same object
|
|
|
if (!(obj instanceof Node)) return false; // Wrong type
|
|
@@ -119,19 +203,19 @@ public class Node {
|
|
|
Node node = (Node) obj;
|
|
|
if (hash != node.hash) return false; // equal object have equal hash codes
|
|
|
|
|
|
- // If above checks fall through, we need to actually look at the structure
|
|
|
- // Of the Node.
|
|
|
-
|
|
|
- String nodeVenn = node.getVenn();
|
|
|
// Case for leaf nodes
|
|
|
- if ((nodeVenn.length() == 1) && (this.venn.length() == 1)) {
|
|
|
- return nodeVenn.equals(this.venn);
|
|
|
+ if (this.isLeaf() && node.isLeaf()) {
|
|
|
+ return this.venn.equals(node.venn);
|
|
|
}
|
|
|
|
|
|
if (children.size() != node.children.size()){
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ // If above checks fall through, we need to actually look at the structure
|
|
|
+ // Of the Node.
|
|
|
+
|
|
|
+
|
|
|
// Check to make sure all of my children exist in node's children
|
|
|
for (Node theirChild : node.children) {
|
|
|
boolean foundMatch = false;
|
|
@@ -146,59 +230,26 @@ public class Node {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-// @Override
|
|
|
-// public int hashCode(){
|
|
|
-// return hash;
|
|
|
-// }
|
|
|
-
|
|
|
- public static List<NodePair> deepSearch(Node nodeA, Node nodeB) {
|
|
|
- List<NodePair> result = new ArrayList<>();
|
|
|
-
|
|
|
- Stack<Node> searchNodes = new Stack<>();
|
|
|
- searchNodes.push(nodeB);
|
|
|
- while (!searchNodes.isEmpty()) {
|
|
|
- Node curr = searchNodes.pop();
|
|
|
-
|
|
|
- // res1: Occurrences of node in this tree
|
|
|
- List<NodePair> res1 = nodeA.search(curr);
|
|
|
-
|
|
|
- // res2: Occurrences of this tree in node
|
|
|
- List<NodePair> res2 = curr.search(nodeA);
|
|
|
-
|
|
|
- assert(res1.size() == 0 || res2.size() == 0 || res1.equals(res2));
|
|
|
-
|
|
|
- if(!res1.isEmpty()){
|
|
|
- result.addAll(res1);
|
|
|
- } else if(!res2.isEmpty()){
|
|
|
- result.addAll(res2);
|
|
|
- }
|
|
|
-
|
|
|
- searchNodes.addAll(curr.children);
|
|
|
- }
|
|
|
- return result;
|
|
|
+ @Override
|
|
|
+ public int hashCode(){
|
|
|
+ return hash;
|
|
|
}
|
|
|
|
|
|
- Node root() {
|
|
|
- if (isRoot()) return this;
|
|
|
- else return this.parent.root();
|
|
|
+ private Node copy(){
|
|
|
+ return new Node(parent, children);
|
|
|
}
|
|
|
|
|
|
- private int getNumAreas() {
|
|
|
- int count = 0;
|
|
|
- for (int i = 0; i < this.venn.length(); i++) {
|
|
|
- if ((this.venn.charAt(i) != ')') && (this.venn.charAt(i) != '(')) {
|
|
|
- count++;
|
|
|
- }
|
|
|
- }
|
|
|
- return count;
|
|
|
+ private Node getRoot() {
|
|
|
+ if (isRoot()) return this;
|
|
|
+ else return this.parent.getRoot();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Search this tree for the subtree with given root
|
|
|
* @param targetNode the root of the subtree to search for
|
|
|
*/
|
|
|
- private List<NodePair> search(Node targetNode) {
|
|
|
- List<NodePair> result = new ArrayList<>();
|
|
|
+ private List<MatchedNodePair> search(Node targetNode) {
|
|
|
+ List<MatchedNodePair> result = new ArrayList<>();
|
|
|
// Use stack to avoid recursion here
|
|
|
Stack<Node> searchNodes = new Stack<>();
|
|
|
searchNodes.push(this);
|
|
@@ -207,7 +258,7 @@ public class Node {
|
|
|
if (curr.equals(targetNode)) {
|
|
|
curr.linkNodes.add(targetNode);
|
|
|
targetNode.linkNodes.add(curr);
|
|
|
- result.add(new NodePair(curr, targetNode));
|
|
|
+ result.add(new MatchedNodePair(curr, targetNode));
|
|
|
} else {
|
|
|
searchNodes.addAll(curr.children);
|
|
|
}
|
|
@@ -217,8 +268,8 @@ public class Node {
|
|
|
|
|
|
/**
|
|
|
* Searches in this tree for the presence of the Node node.
|
|
|
- * @param node
|
|
|
- * @return
|
|
|
+ * @param node root of subtree to search for
|
|
|
+ * @return whether node was found in this tree
|
|
|
*/
|
|
|
private boolean isIn(Node node) {
|
|
|
if (this == node) {
|
|
@@ -235,9 +286,9 @@ public class Node {
|
|
|
/**
|
|
|
* Looks through this Node's siblings and returns the first one that has linkNodes. If no sibling
|
|
|
* has linkNodes, returns null.
|
|
|
- * @return
|
|
|
+ * @return the link, null if it doesn't exist
|
|
|
*/
|
|
|
- private Node hasLink() {
|
|
|
+ private Node getLink() {
|
|
|
for (Node curr : getSiblings()) {
|
|
|
if (!curr.linkNodes.isEmpty()) {
|
|
|
return curr;
|
|
@@ -246,6 +297,10 @@ public class Node {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ private boolean hasLink() {
|
|
|
+ return getLink() != null;
|
|
|
+ }
|
|
|
+
|
|
|
private boolean unshared() {
|
|
|
if (this.linkNodes.isEmpty()) {
|
|
|
if(this.children.isEmpty()) {
|
|
@@ -274,109 +329,138 @@ public class Node {
|
|
|
|
|
|
/**
|
|
|
* Searches in h for all NodePairs that contain this object as either node1 or node2
|
|
|
- * @param h
|
|
|
- * @return
|
|
|
+ * @param matchedNodePairs collection to search for duplicates in
|
|
|
+ * @return a copy of the list with only duplicates remaining
|
|
|
*/
|
|
|
- private List<Node> getDuplicates(List<NodePair> h) {
|
|
|
+ private List<Node> getDuplicates(List<MatchedNodePair> matchedNodePairs) {
|
|
|
List<Node> result = new ArrayList<>();
|
|
|
- for (NodePair curr : h) {
|
|
|
- if (curr.node1 == this)
|
|
|
- result.add(curr.node2);
|
|
|
- else if (curr.node2 == this) {
|
|
|
- result.add(curr.node1);
|
|
|
+ for (MatchedNodePair nodePair : matchedNodePairs) {
|
|
|
+ if (nodePair.node1 == this)
|
|
|
+ result.add(nodePair.node2);
|
|
|
+ else if (nodePair.node2 == this) {
|
|
|
+ result.add(nodePair.node1);
|
|
|
}
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Traverses tree and collects all unshared nodes.
|
|
|
+ * @return unshared nodes
|
|
|
+ */
|
|
|
+ private List<Node> collectUnshared() {
|
|
|
+ Stack<Node> stack = new Stack<>();
|
|
|
+ List<Node> unshared = new ArrayList<>();
|
|
|
+ stack.push(this);
|
|
|
+ while (!stack.isEmpty()) {
|
|
|
+ Node n = stack.pop();
|
|
|
+ if (n.unshared()) {
|
|
|
+ unshared.add(n);
|
|
|
+ }
|
|
|
+ stack.addAll(n.children);
|
|
|
+ }
|
|
|
+ return unshared;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Produces a reduced version of unreduced where any duplicate children have been removed. Note that this doesn't
|
|
|
* look for duplicate sub-trees at lower levels. (ie duplicate grandchildren)
|
|
|
- * @param unreduced
|
|
|
- * @return
|
|
|
+ * @param unreduced The unreduced tree
|
|
|
+ * @return Node with duplicate children removed
|
|
|
*/
|
|
|
private static Node reduce(Node unreduced) {
|
|
|
- List<Node> uniqueChildren = new ArrayList<>();
|
|
|
+ if(unreduced.isLeaf()) {
|
|
|
+ // Cannot reduce a leaf, so return unmodified
|
|
|
+ return unreduced;
|
|
|
+ }
|
|
|
+
|
|
|
+ HashSet<Node> uniqueChildren = new HashSet<>();
|
|
|
for (Node child : unreduced.children) {
|
|
|
- if (!child.inSet(uniqueChildren)) {
|
|
|
+ if (!uniqueChildren.contains(child)) {
|
|
|
uniqueChildren.add(child);
|
|
|
}
|
|
|
}
|
|
|
- if (uniqueChildren.size() == unreduced.children.size() || (unreduced.venn.length() == 1)) {
|
|
|
+ if (uniqueChildren.size() == unreduced.children.size()) {
|
|
|
+ // There are no duplicate children so return unmodified
|
|
|
return unreduced;
|
|
|
}
|
|
|
Iterator<Node> uIt = uniqueChildren.iterator();
|
|
|
- String result = uIt.next().getVenn();
|
|
|
+
|
|
|
+ StringBuilder result = new StringBuilder();
|
|
|
+ result.append(uIt.next().getVenn());
|
|
|
if (uIt.hasNext()) {
|
|
|
while (uIt.hasNext()) {
|
|
|
- result += uIt.next().getVenn();
|
|
|
+ result.append(uIt.next().getVenn());
|
|
|
}
|
|
|
- result = "(" + result + ")";
|
|
|
+ result.insert(0, "(");
|
|
|
+ result.append(")");
|
|
|
}
|
|
|
- Node reduced = new Node(null, result);
|
|
|
- return reduced;
|
|
|
+ return new Node(null, result.toString());
|
|
|
}
|
|
|
|
|
|
- private boolean inSet(List<Node> h) {
|
|
|
- return h.contains(this);
|
|
|
- }
|
|
|
+ public static List<MatchedNodePair> deepSearch(Node nodeA, Node nodeB) {
|
|
|
+ nodeA.resetSearch();
|
|
|
+ nodeB.resetSearch();
|
|
|
+ List<MatchedNodePair> result = new ArrayList<>();
|
|
|
|
|
|
- /**
|
|
|
- * Traverses tree and collects all unshared nodes.
|
|
|
- * @return
|
|
|
- */
|
|
|
- private List<Node> collectUnshared() {
|
|
|
- Stack<Node> stack = new Stack<>();
|
|
|
- List<Node> unshared = new ArrayList();
|
|
|
- stack.push(this);
|
|
|
- while (!stack.isEmpty()){
|
|
|
- Node n = stack.pop();
|
|
|
- if (n.unshared()) {
|
|
|
- unshared.add(n);
|
|
|
+ Stack<Node> searchNodes = new Stack<>();
|
|
|
+ searchNodes.push(nodeB);
|
|
|
+ while (!searchNodes.isEmpty()) {
|
|
|
+ Node curr = searchNodes.pop();
|
|
|
+
|
|
|
+ // res1: Occurrences of node in this tree
|
|
|
+ List<MatchedNodePair> res1 = nodeA.search(curr);
|
|
|
+
|
|
|
+ // res2: Occurrences of this tree in node
|
|
|
+ List<MatchedNodePair> res2 = curr.search(nodeA);
|
|
|
+
|
|
|
+ assert(res1.size() == 0 || res2.size() == 0 || res1.equals(res2));
|
|
|
+
|
|
|
+ if(!res1.isEmpty()){
|
|
|
+ result.addAll(res1);
|
|
|
+ } else if(!res2.isEmpty()){
|
|
|
+ result.addAll(res2);
|
|
|
}
|
|
|
- stack.addAll(n.children);
|
|
|
+ searchNodes.addAll(curr.children);
|
|
|
}
|
|
|
- return unshared;
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
- static List<Node> combine(Node node1, Node node2) {
|
|
|
- System.out.println("Combining: " + node1 + ", " + node2);
|
|
|
+ private static List<Node> combine(Node node1, Node node2) {
|
|
|
+ log("Combining: " + node1 + ", " + node2);
|
|
|
List<Node> result = _combine(node1, node2);
|
|
|
- System.out.println("Combining Result: " + result);
|
|
|
+ result = removeDuplicates(result);
|
|
|
+ log("Combining Result: " + result);
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- static List<Node> _combine(Node node1, Node node2) {
|
|
|
+ private static List<Node> _combine(Node node1, Node node2) {
|
|
|
+ List<Node> result = new ArrayList<>();
|
|
|
+
|
|
|
+ // Step1. Remove any duplicate children of node1 and node2.
|
|
|
node1 = reduce(node1);
|
|
|
node2 = reduce(node2);
|
|
|
|
|
|
- node2.resetSearch();
|
|
|
- node1.resetSearch();
|
|
|
- List<Node> result = new ArrayList<>();
|
|
|
-
|
|
|
+ // Case of the two reduced nodes being identical, no extra work required, just return one of them.
|
|
|
if (node1.equals(node2)) {
|
|
|
result.add(node2);
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- List<NodePair> sResult = deepSearch(node2, node1);
|
|
|
|
|
|
- if (sResult.isEmpty()) {
|
|
|
- result.add(combineAtRoot(node2, node1));
|
|
|
- return result;
|
|
|
- }
|
|
|
|
|
|
+ List<MatchedNodePair> sResult = deepSearch(node2, node1);
|
|
|
|
|
|
List<Node> unsharedNodes = new ArrayList<>();
|
|
|
unsharedNodes.addAll(node2.collectUnshared());
|
|
|
unsharedNodes.addAll(node1.collectUnshared());
|
|
|
|
|
|
- if ((unsharedNodes.isEmpty()) && (node1.getNumAreas() == node2.getNumAreas())) {
|
|
|
+ if ((unsharedNodes.isEmpty()) && (node1.getNLeaves() == node2.getNLeaves())) {
|
|
|
if (node1.children.size() < node2.children.size()) {
|
|
|
Node toAdd = new Node(null, node1.getVenn());
|
|
|
result.add(toAdd);
|
|
|
return result;
|
|
|
- } if (node2.children.size() < node1.children.size()) {
|
|
|
+ } else if (node2.children.size() < node1.children.size()) {
|
|
|
Node toAdd = new Node(null, node2.getVenn());
|
|
|
result.add(toAdd);
|
|
|
return result;
|
|
@@ -384,11 +468,11 @@ public class Node {
|
|
|
|
|
|
}
|
|
|
|
|
|
- List<NodePair> largest = getLargestNodePairs(sResult);
|
|
|
+ List<MatchedNodePair> largest = getLargestNodePairs(sResult);
|
|
|
if (!largest.isEmpty()) {
|
|
|
Iterator itLargest = largest.iterator();
|
|
|
|
|
|
- NodePair flargest = (NodePair)itLargest.next();
|
|
|
+ MatchedNodePair flargest = (MatchedNodePair)itLargest.next();
|
|
|
|
|
|
List<Node> lg1sibs = flargest.node1.getSiblings();
|
|
|
List<Node> lg2sibs = flargest.node2.getSiblings();
|
|
@@ -407,24 +491,22 @@ public class Node {
|
|
|
|
|
|
}
|
|
|
|
|
|
- List<NodePair> newLinkedNodePair = getLinkedNodePair(unsharedNodes);
|
|
|
+ List<MatchedNodePair> newLinkedNodePair = getLinkedNodePair(unsharedNodes);
|
|
|
|
|
|
- List<List<NodePair>> polys = reviewPolys(newLinkedNodePair);
|
|
|
+ List<List<MatchedNodePair>> polys = reviewPolytomies(newLinkedNodePair);
|
|
|
if (!polys.isEmpty()) {
|
|
|
- Iterator<List<NodePair>> polyIt = polys.iterator();
|
|
|
+ Iterator<List<MatchedNodePair>> polyIt = polys.iterator();
|
|
|
if (polyIt.hasNext()) {
|
|
|
- List<NodePair> polyresult = addPolys(polyIt.next());
|
|
|
+ List<MatchedNodePair> polyresult = addPolys(polyIt.next());
|
|
|
Iterator prIt = polyresult.iterator();
|
|
|
if (prIt.hasNext()) {
|
|
|
- NodePair curr = (NodePair)prIt.next();
|
|
|
+ MatchedNodePair curr = (MatchedNodePair)prIt.next();
|
|
|
if ((curr.node1.parent != null) && (curr.node1.linkNode.parent != null)) {
|
|
|
String polyString1 = buildUp(curr.node1.parent, curr.node2.getVenn());
|
|
|
String polyString2 = buildUp(curr.node1.linkNode.parent, curr.node2.getVenn());
|
|
|
Node polyOne = new Node(null, polyString1);
|
|
|
Node polyTwo = new Node(null, polyString2);
|
|
|
- List<Node> polyresults = combine(polyOne, polyTwo);
|
|
|
- for (Node polyResult : polyresults)
|
|
|
- result.add(polyResult);
|
|
|
+ result.addAll(combine(polyOne, polyTwo));
|
|
|
} else if (!curr.node1.isRoot()) {
|
|
|
String polyString1 = buildUp(curr.node1.parent, curr.node2.getVenn());
|
|
|
Node polyOne = new Node(null, polyString1);
|
|
@@ -434,7 +516,7 @@ public class Node {
|
|
|
Node polyTwo = new Node(null, polyString2);
|
|
|
result.add(polyTwo);
|
|
|
} else {
|
|
|
- System.out.println("What's up Houston?");
|
|
|
+ throw new RuntimeException("");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -442,8 +524,8 @@ public class Node {
|
|
|
}
|
|
|
|
|
|
if (!newLinkedNodePair.isEmpty()) {
|
|
|
- List<NodePair> combined = newLinkedNodePair.get(0).combineNew();
|
|
|
- for (NodePair curr : combined) {
|
|
|
+ List<MatchedNodePair> combined = combineNew(newLinkedNodePair.get(0));
|
|
|
+ for (MatchedNodePair curr : combined) {
|
|
|
result.addAll(combine(curr.node1, curr.node2));
|
|
|
}
|
|
|
return result;
|
|
@@ -455,13 +537,8 @@ public class Node {
|
|
|
for (Node curr : unique) {
|
|
|
List<Node> dupres = curr.getDuplicates(sResult);
|
|
|
if (dupres.size() > 1) {
|
|
|
- List<NodePair> newDupNodePair = getLinkedNodePair(dupres);
|
|
|
- Iterator<NodePair> ndIt = newDupNodePair.iterator();
|
|
|
- while (ndIt.hasNext()) {
|
|
|
- List<NodePair> combined = ndIt.next().combineNew();
|
|
|
- Iterator cIt = combined.iterator();
|
|
|
- while (cIt.hasNext()) {
|
|
|
- NodePair curr2 = (NodePair)cIt.next();
|
|
|
+ for (MatchedNodePair aNewDupNodePair : getLinkedNodePair(dupres)) {
|
|
|
+ for (MatchedNodePair curr2 : combineNew(aNewDupNodePair)) {
|
|
|
List<Node> newResults = combine(curr2.node1, curr2.node2);
|
|
|
result.addAll(newResults);
|
|
|
}
|
|
@@ -474,7 +551,7 @@ public class Node {
|
|
|
return result;
|
|
|
}
|
|
|
else { // shares sub-trees
|
|
|
- for (NodePair largest_ : largest) {
|
|
|
+ for (MatchedNodePair largest_ : largest) {
|
|
|
result.addAll(subTrees(largest_));
|
|
|
}
|
|
|
return result;
|
|
@@ -482,33 +559,40 @@ public class Node {
|
|
|
}
|
|
|
|
|
|
public static List<Node> reducedCombine(Node node1, Node node2) {
|
|
|
+ log("Attempting to combine " + node1 + " and " + node2);
|
|
|
List<Node> result = new ArrayList<>();
|
|
|
|
|
|
- if (oneLeafShared(node1, node2)) {
|
|
|
+ int nSharedLeaves = sharedLeaves(node1, node2).size();
|
|
|
+ if (nSharedLeaves <= 1) {
|
|
|
+ log(String.format("Found %d shared leaves, combining at root", nSharedLeaves));
|
|
|
result.add(combineAtRoot(node1, node2));
|
|
|
- return result;
|
|
|
- }
|
|
|
- List<Node> compCombine = combine(node1, node2);
|
|
|
-
|
|
|
- int smallest = Integer.MAX_VALUE;
|
|
|
- for (Node curr : compCombine) {
|
|
|
- int currAreas = curr.getNumAreas();
|
|
|
- if (currAreas < smallest) {
|
|
|
- smallest = currAreas;
|
|
|
- result.clear();
|
|
|
- result.add(curr);
|
|
|
- } else if (currAreas == smallest) {
|
|
|
- result.add(curr);
|
|
|
+ } else {
|
|
|
+ log(String.format("Found %d shared leaves, running full combine", nSharedLeaves));
|
|
|
+ List<Node> compCombine = combine(node1, node2);
|
|
|
+ log(String.format("combine yielded %d unique trees, reducing to smallest.", compCombine.size()));
|
|
|
+
|
|
|
+ int smallest = Integer.MAX_VALUE;
|
|
|
+ for (Node curr : compCombine) {
|
|
|
+ int currAreas = curr.getLeaves().size();
|
|
|
+ if (currAreas < smallest) {
|
|
|
+ smallest = currAreas;
|
|
|
+ result.clear();
|
|
|
+ result.add(curr);
|
|
|
+ } else if (currAreas == smallest) {
|
|
|
+ result.add(curr);
|
|
|
+ }
|
|
|
}
|
|
|
+ log(String.format("After reduction, there are %d unique trees with size %d.", result.size(), result.get(0).getNLeaves()));
|
|
|
}
|
|
|
- return removeDuplicates(result);
|
|
|
+ log("Result of Combination is " + result);
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Removes any duplicate nodes. Guarantees that
|
|
|
* forall A, B in result, A.equals(B) == false
|
|
|
- * @param all
|
|
|
- * @return
|
|
|
+ * @param all List of nodes from which to remove duplicates
|
|
|
+ * @return all with duplicates removed
|
|
|
*/
|
|
|
private static List<Node> removeDuplicates(List<Node> all) {
|
|
|
// TODO: Speed up w/ sets as soon as hash problems are sorted
|
|
@@ -529,18 +613,15 @@ public class Node {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Returns true if the trees with roots node1 and node1 share exactly one leaf node
|
|
|
- * @param node1
|
|
|
- * @param node2
|
|
|
- * @return
|
|
|
+ * Returns all leaf nodes that are shared by the two input trees.
|
|
|
+ * @param node1 Tree to search for shared leaves
|
|
|
+ * @param node2 Tree to search for shared leaves
|
|
|
+ * @return The list of shared nodes (Actual nodes belong to the tree node1)
|
|
|
*/
|
|
|
- private static boolean oneLeafShared(Node node1, Node node2) {
|
|
|
- List<NodePair> result = deepSearch(node2, node1);
|
|
|
- if (result.isEmpty()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- List<NodePair> largest = getLargestNodePairs(result);
|
|
|
- return largest.size() == 1 && largest.get(0).nodesSize() == 1;
|
|
|
+ private static List<Node> sharedLeaves(Node node1, Node node2) {
|
|
|
+ Set<Node> shared = new HashSet<>(node1.getLeaves());
|
|
|
+ shared.retainAll(node2.getLeaves());
|
|
|
+ return new ArrayList<>(shared);
|
|
|
}
|
|
|
|
|
|
private static void sortLargestSibs(List<Node> lgsibs, Node lg, List<Node> newSibs, List<Node> os) {
|
|
@@ -565,14 +646,17 @@ public class Node {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private static List<Node> subTrees(NodePair largest) {
|
|
|
+ private static List<Node> subTrees(MatchedNodePair largest) {
|
|
|
if (largest.node1.isRoot() && largest.node2.isRoot())
|
|
|
throw new IllegalArgumentException("Cannot calculate subtrees with two roots.");
|
|
|
|
|
|
- List<Node> result = new ArrayList();
|
|
|
- List<Node> combined = largest.combineSubTrees();
|
|
|
+ List<Node> result = new ArrayList<>();
|
|
|
+ List<Node> combined = combineSubTrees(largest);
|
|
|
|
|
|
- for (Node node : combined) {
|
|
|
+ if(combined.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ } else {
|
|
|
+ Node node = combined.get(0);
|
|
|
String curr = node.getVenn();
|
|
|
|
|
|
if ((!node.isLeaf()) && ((largest.node1.nSiblings() != 1) || (largest.node2.nSiblings() != 1))) {
|
|
@@ -596,19 +680,18 @@ public class Node {
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
- return result;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
*
|
|
|
- * @param h HashSet
|
|
|
- * @return The largest NodePair in h, based on the length of n1's Venn diagram
|
|
|
+ * @param h List
|
|
|
+ * @return The largest MatchedNodePair in h, based on the length of n1's Venn diagram
|
|
|
*/
|
|
|
- private static List<NodePair> getLargestNodePairs(List<NodePair> h) {
|
|
|
- List<NodePair> result = new ArrayList<>();
|
|
|
+ private static List<MatchedNodePair> getLargestNodePairs(List<MatchedNodePair> h) {
|
|
|
+ List<MatchedNodePair> result = new ArrayList<>();
|
|
|
int size = -1;
|
|
|
|
|
|
- for(NodePair curr : h){
|
|
|
+ for(MatchedNodePair curr : h){
|
|
|
if (curr.nodesSize() > size) {
|
|
|
size = curr.nodesSize();
|
|
|
result.clear();
|
|
@@ -623,112 +706,184 @@ public class Node {
|
|
|
private static Node combineAtRoot(Node node1, Node node2) { // CONVERTED
|
|
|
List<Node> uniqueNodes = new ArrayList<>();
|
|
|
for(Node curr : node1.children) {
|
|
|
- if (!curr.inSet(uniqueNodes))
|
|
|
+ if (!uniqueNodes.contains(curr))
|
|
|
uniqueNodes.add(curr);
|
|
|
}
|
|
|
for(Node curr : node2.children) {
|
|
|
- if (!curr.inSet(uniqueNodes))
|
|
|
+ if (!uniqueNodes.contains(curr))
|
|
|
uniqueNodes.add(curr);
|
|
|
}
|
|
|
if (uniqueNodes.size() < (node1.children.size() + node2.children.size())) {
|
|
|
- String result_venn = "(";
|
|
|
+ StringBuilder result_venn = new StringBuilder();
|
|
|
+ result_venn.append("(");
|
|
|
for (Node unique : uniqueNodes) {
|
|
|
- result_venn = result_venn + unique;
|
|
|
+ result_venn.append(unique);
|
|
|
}
|
|
|
- result_venn = result_venn + ")";
|
|
|
- return new Node(null, result_venn);
|
|
|
+ result_venn.append(")");
|
|
|
+ return new Node(null, result_venn.toString());
|
|
|
} else {
|
|
|
return new Node(null, "(" + node1.venn + node2.getVenn() + ")");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private static List<NodePair> getLinkedNodePair(List<Node> h) {
|
|
|
- List<NodePair> result = new ArrayList<>();
|
|
|
+ private static List<MatchedNodePair> getLinkedNodePair(List<Node> h) {
|
|
|
+ List<MatchedNodePair> result = new ArrayList<>();
|
|
|
for (Node curr : h) {
|
|
|
- if (curr.hasLink() != null)
|
|
|
- result.add(new NodePair(curr, curr.hasLink()));
|
|
|
+ if (curr.hasLink())
|
|
|
+ result.add(new MatchedNodePair(curr, curr.getLink()));
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- private static String buildUp(Node n, String s) { // CONVERTED
|
|
|
- System.out.println("buildUp: " + s);
|
|
|
- if (n.parent == null) {
|
|
|
- return s;
|
|
|
- }
|
|
|
- String result = "(" + s;
|
|
|
- List<Node> sibs = n.getSiblings();
|
|
|
- for (Node sib : sibs) {
|
|
|
- result += sib.getVenn();
|
|
|
- }
|
|
|
- result += ")";
|
|
|
- return buildUp(n.parent, result);
|
|
|
- }
|
|
|
-
|
|
|
- private static List<List<NodePair>> reviewPolys(List<NodePair> h) {
|
|
|
- List<List<NodePair>> result = new ArrayList<>();
|
|
|
- for (NodePair curr : h) {
|
|
|
- List<NodePair> curresult = new ArrayList<>();
|
|
|
- for (NodePair inCurr : h) {
|
|
|
+ private static List<List<MatchedNodePair>> reviewPolytomies(List<MatchedNodePair> nodePairs) {
|
|
|
+ List<List<MatchedNodePair>> result = new ArrayList<>();
|
|
|
+ for (MatchedNodePair curr : nodePairs) {
|
|
|
+ List<MatchedNodePair> currResult = new ArrayList<>();
|
|
|
+ for (MatchedNodePair inCurr : nodePairs) {
|
|
|
if (inCurr == curr) {
|
|
|
continue;
|
|
|
}
|
|
|
- List<Node> linkedNodePair = inCurr.node2.linkNodes;
|
|
|
+ List<Node> linkedNodes = inCurr.node2.linkNodes;
|
|
|
boolean found = false;
|
|
|
- for (Node currln : linkedNodePair) {
|
|
|
- if (currln == curr.node2) {
|
|
|
+ for (Node currNP : linkedNodes) {
|
|
|
+ if (currNP == curr.node2) {
|
|
|
found = true;
|
|
|
- inCurr.node2.linkNode = currln;
|
|
|
+ inCurr.node2.linkNode = currNP;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
if (found) {
|
|
|
- curresult.add(inCurr);
|
|
|
+ currResult.add(inCurr);
|
|
|
}
|
|
|
}
|
|
|
- if (!curresult.isEmpty()) {
|
|
|
- curresult.add(curr);
|
|
|
- result.add(curresult);
|
|
|
+ if (!currResult.isEmpty()) {
|
|
|
+ currResult.add(curr);
|
|
|
+ result.add(currResult);
|
|
|
}
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- private static List<NodePair> addPolys(List<NodePair> h) {
|
|
|
- List<NodePair> setResult = new ArrayList<>();
|
|
|
- NodePair first = null;
|
|
|
- String result = "";
|
|
|
- for (NodePair nodes : h) {
|
|
|
+ private static List<MatchedNodePair> addPolys(List<MatchedNodePair> h) {
|
|
|
+ List<MatchedNodePair> setResult = new ArrayList<>();
|
|
|
+ MatchedNodePair first = null;
|
|
|
+ StringBuilder result = new StringBuilder();
|
|
|
+ for (MatchedNodePair nodes : h) {
|
|
|
if (first == null) {
|
|
|
first = nodes;
|
|
|
- result = first.node2.getVenn();
|
|
|
+ result.append(first.node2.getVenn());
|
|
|
}
|
|
|
- result += nodes.node1.getVenn();
|
|
|
+ result.append(nodes.node1.getVenn());
|
|
|
}
|
|
|
- for (NodePair nodes : h) {
|
|
|
- NodePair toAdd = new NodePair((nodes).node2, new Node(null, "(" + result + ")"));
|
|
|
+ for (MatchedNodePair nodes : h) {
|
|
|
+ MatchedNodePair toAdd = new MatchedNodePair((nodes).node2, new Node(null, "(" + result + ")"));
|
|
|
setResult.add(toAdd);
|
|
|
}
|
|
|
return setResult;
|
|
|
}
|
|
|
|
|
|
- private static List<Node> getUniqueNodes(List<NodePair> nodePairs) { // CONVERTED
|
|
|
- HashSet<Node> result = new HashSet<>();
|
|
|
- for (NodePair curr : nodePairs) {
|
|
|
- if (!result.contains(curr.node1)) {
|
|
|
- result.add(curr.node1);
|
|
|
- }
|
|
|
- if (!result.contains(curr.node2)) {
|
|
|
- result.add(curr.node2);
|
|
|
- }
|
|
|
- }
|
|
|
- System.out.println("result: " + result);
|
|
|
+ private static List<Node> getUniqueNodes(List<MatchedNodePair> nodePairs) {
|
|
|
+ /* TODO: Figure out what this is actually supposed to do.
|
|
|
+ * By the name it should return the set of nodes in the node pairs that
|
|
|
+ * are unique, but if it actually does that, some tests fail. For now, just
|
|
|
+ * adding all the nodes into the result seems to work.
|
|
|
+ */
|
|
|
List<Node> result_list = new ArrayList<>();
|
|
|
- for (NodePair curr : nodePairs) {
|
|
|
+ for (MatchedNodePair curr : nodePairs) {
|
|
|
result_list.add(curr.node1);
|
|
|
result_list.add(curr.node2);
|
|
|
}
|
|
|
- System.out.println("result_list: " + result_list);
|
|
|
return result_list;
|
|
|
}
|
|
|
+
|
|
|
+ private static List<Node> combineSubTrees(MatchedNodePair np) {
|
|
|
+ List<Node> commonSiblings = new ArrayList<>();
|
|
|
+
|
|
|
+ List<Node> node1Siblings = new ArrayList<>(np.node1.getSiblings());
|
|
|
+ Iterator it1 = node1Siblings.iterator();
|
|
|
+ List<Node> node2Siblings = new ArrayList<>(np.node2.getSiblings());
|
|
|
+ Iterator it2 = node2Siblings.iterator();
|
|
|
+
|
|
|
+ StringBuilder siblingsVenn = new StringBuilder();
|
|
|
+
|
|
|
+ if (!node1Siblings.isEmpty()) {
|
|
|
+ siblingsVenn.append(((Node) it2.next()).getVenn());
|
|
|
+ } else {
|
|
|
+ if (it1.hasNext()) {
|
|
|
+ siblingsVenn.append(((Node) it1.next()).getVenn());
|
|
|
+ if (it1.hasNext()) {
|
|
|
+ String temp = siblingsVenn.toString();
|
|
|
+ siblingsVenn = new StringBuilder("(" + temp);
|
|
|
+ while (it1.hasNext()) {
|
|
|
+ siblingsVenn.append(((Node) it1.next()).getVenn());
|
|
|
+ }
|
|
|
+ siblingsVenn.append(")");
|
|
|
+ }
|
|
|
+ commonSiblings.add(new Node(null, siblingsVenn.toString()));
|
|
|
+ return commonSiblings;
|
|
|
+ }
|
|
|
+ return commonSiblings;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (it2.hasNext()) {
|
|
|
+ String temp = siblingsVenn.toString();
|
|
|
+ siblingsVenn = new StringBuilder("(" + temp);
|
|
|
+ while (it2.hasNext()) {
|
|
|
+ siblingsVenn.append(((Node) it2.next()).getVenn());
|
|
|
+ }
|
|
|
+ siblingsVenn.append(")");
|
|
|
+ }
|
|
|
+
|
|
|
+ Node nodesibs1 = new Node(null, siblingsVenn.toString());
|
|
|
+ if (!it1.hasNext()) {
|
|
|
+ commonSiblings.add(nodesibs1);
|
|
|
+ return commonSiblings;
|
|
|
+ }
|
|
|
+ StringBuilder siblings2 = new StringBuilder();
|
|
|
+ if (it1.hasNext()) {
|
|
|
+ siblings2.append(((Node) it1.next()).getVenn());
|
|
|
+ if (it1.hasNext()) {
|
|
|
+ String temp = siblings2.toString();
|
|
|
+ siblings2 = new StringBuilder("(" + temp);
|
|
|
+ while (it1.hasNext()) {
|
|
|
+ siblings2.append(((Node) it1.next()).getVenn());
|
|
|
+ }
|
|
|
+ siblings2.append(")");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Node nodeSibs2 = new Node(null, siblings2.toString());
|
|
|
+ List<Node> result = Node.combine(nodesibs1, nodeSibs2);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static List<MatchedNodePair> combineNew(MatchedNodePair np) {
|
|
|
+ List<MatchedNodePair> setResult = new ArrayList<>();
|
|
|
+ Node resultNode1 = new Node(null, np.node2.getRoot().getVenn());
|
|
|
+ String added = "(" + np.node2.getVenn() + np.node1.getVenn() + ")";
|
|
|
+
|
|
|
+ for (Node linkNode : np.node2.linkNodes) {
|
|
|
+ String result = buildUp(linkNode, added);
|
|
|
+ Node resultNode2 = new Node(null, result);
|
|
|
+ setResult.add(new MatchedNodePair(resultNode1, resultNode2));
|
|
|
+ }
|
|
|
+ return setResult;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String buildUp(Node n, String s) {
|
|
|
+ if (n.parent == null) {
|
|
|
+ return s;
|
|
|
+ } else {
|
|
|
+ StringBuilder result = new StringBuilder();
|
|
|
+ result.append(s);
|
|
|
+ for(Node sibling : n.getSiblings()){
|
|
|
+ result.append(sibling.getVenn());
|
|
|
+ }
|
|
|
+ return buildUp(n.parent, "(" + result + ")");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void log(String s){
|
|
|
+ System.out.println(s);
|
|
|
+ }
|
|
|
}
|