Bladeren bron

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 7 jaren geleden
bovenliggende
commit
c4d24657cd
3 gewijzigde bestanden met toevoegingen van 429 en 368 verwijderingen
  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";