Quellcode durchsuchen

Renames NodePair to MatchedNodePair and makes in an inner class of Node.
Adds five new tests (one of which fails here and in Maggie's original
implementation)

Caleb Fangmeier vor 7 Jahren
Ursprung
Commit
c4d24657cd
3 geänderte Dateien mit 429 neuen und 368 gelöschten Zeilen
  1. 398 243
      src/com/pact/Node.java
  2. 0 122
      src/com/pact/NodePair.java
  3. 31 3
      src/com/pact/test/NodeTest.java

+ 398 - 243
src/com/pact/Node.java

@@ -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);
+    }
 }

+ 0 - 122
src/com/pact/NodePair.java

@@ -1,122 +0,0 @@
-package com.pact;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-
-public class NodePair {
-    Node node1;
-    Node node2;
-
-    public NodePair(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 + ">";
-    }
-
-    List<Node> combineSubTrees() {
-        System.out.println("combineSubTrees==========================");
-        System.out.println("node1: " + node1.getVenn());
-        System.out.println("node2: " + node2.getVenn());
-        System.out.println("node1Full: " + node1.root().getVenn());
-        System.out.println("node2Full: " + node2.root().getVenn());
-        List<Node> commonSiblings = new ArrayList<>();
-
-        List<Node> node1Siblings = new ArrayList<>(this.node1.getSiblings());
-        Iterator it1 = node1Siblings.iterator();
-        List<Node> node2Siblings = new ArrayList<>(this.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);
-        System.out.println("Result: " + result);
-        return result;
-    }
-
-    List<NodePair> combineNew() {
-        List<NodePair> setResult = new ArrayList<>();
-        Node resultNode1 = new Node(null, this.node2.root().getVenn());
-        String added = "(" + this.node2.getVenn() + this.node1.getVenn() + ")";
-
-        for (Node linkNode : node2.linkNodes) {
-            String result = buildUp(linkNode, added);
-            Node resultNode2 = new Node(null, result);
-            setResult.add(new NodePair(resultNode1, resultNode2));
-        }
-        return setResult;
-    }
-
-    private 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 + ")");
-        }
-    }
-}

+ 31 - 3
src/com/pact/test/NodeTest.java

@@ -3,10 +3,8 @@ package com.pact.test;
 import static org.junit.jupiter.api.Assertions.*;
 
 import com.pact.Node;
-import com.pact.NodePair;
 import org.junit.jupiter.api.Test;
 
-import java.util.HashSet;
 import java.util.List;
 
 class NodeTest {
@@ -43,7 +41,7 @@ class NodeTest {
     void searchTest1() {
         Node node1 = new Node(null, "(A(B(C(DA))))");
         Node node2 = new Node(null, "(A(B(CD)))");
-        List<NodePair> result = Node.deepSearch(node2, node1);
+        List<Node.MatchedNodePair> result = Node.deepSearch(node2, node1);
         System.out.println(result);
     }
 
@@ -182,6 +180,36 @@ class NodeTest {
         reducedCombineTest("((AB)(AB)(AC))", "((AB)(AB)(AB))",
                            "((AB)(AC))");
     }
+
+    @Test
+    void reducedCombineTest20() {
+        reducedCombineTest("(A(B(C(DE))))", "((AF)(BE))",
+                           "((AF)(B(C(DE))))");
+    }
+
+    @Test
+    void reducedCombineTest21() {
+        reducedCombineTest("(A(B(C(DE))))", "((AD)(BE))",
+                           "((AD)(B(C(DE))))");
+    }
+
+    @Test
+    void reducedCombineTest22() {
+        reducedCombineTest("(A(BC))", "(A(DE))",
+                           "(A(BC)(DE))");
+    }
+
+    @Test
+    void reducedCombineTest23() {
+        reducedCombineTest("(A(B(CD)))", "(A(A(B(CD))))",
+                           "(A(A(B(CD))))");
+    }
+
+    @Test
+    void reducedCombineTest24() {
+        reducedCombineTest("(A(B(CD)))", "(E(F(GH)))",
+                           "((E(F(GH)))(A(B(CD))))");
+    }
 //    @Test
 //    void hash() {
 //        String var0 = "Hello World";