diff --git a/diylc/diylc-core/src/main/java/org/diylc/plugins/compare/service/CompareService.java b/diylc/diylc-core/src/main/java/org/diylc/plugins/compare/service/CompareService.java index 58aa2db0..6ac437a1 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/plugins/compare/service/CompareService.java +++ b/diylc/diylc-core/src/main/java/org/diylc/plugins/compare/service/CompareService.java @@ -35,8 +35,10 @@ import org.diylc.presenter.Presenter; import java.io.File; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -179,6 +181,134 @@ public class CompareService { } } + // Try to resolve differences by flipping non-polarized components + if (!connectionDiffs.isEmpty()) { + // Find all non-polarized components that have nodes in different groups + Map> componentGroups = new HashMap<>(); + for (Group group : groups1) { + for (Node node : group.getNodes()) { + if (!node.getComponent().isPolarized()) { + componentGroups.computeIfAbsent(node.getComponent().getName(), k -> new ArrayList<>()) + .add(group); + } + } + } + + // Try swapping nodes for each non-polarized component that appears in multiple groups + for (Map.Entry> entry : componentGroups.entrySet()) { + if (entry.getValue().size() >= 2) { + String componentName = entry.getKey(); + List groupsWithComponent = entry.getValue(); + + // Get the nodes from the first two groups + Group group1 = groupsWithComponent.get(0); + Group group2 = groupsWithComponent.get(1); + + Node node1 = group1.getNodes().stream() + .filter(n -> n.getComponent().getName().equals(componentName)) + .findFirst() + .get(); + Node node2 = group2.getNodes().stream() + .filter(n -> n.getComponent().getName().equals(componentName)) + .findFirst() + .get(); + + // Create new groups with swapped nodes + Group newGroup1 = new Group(); + Group newGroup2 = new Group(); + + // Copy all nodes except the ones we're swapping + for (Node n : group1.getNodes()) { + if (!n.getComponent().getName().equals(componentName)) { + newGroup1.getNodes().add(n); + } + } + for (Node n : group2.getNodes()) { + if (!n.getComponent().getName().equals(componentName)) { + newGroup2.getNodes().add(n); + } + } + + // Add the swapped nodes + newGroup1.getNodes().add(node2); + newGroup2.getNodes().add(node1); + + // Create a new set of groups with the swapped nodes + Set newGroups1 = new HashSet<>(groups1); + newGroups1.remove(group1); + newGroups1.remove(group2); + newGroups1.add(newGroup1); + newGroups1.add(newGroup2); + + // Check if this resolves ALL differences + List newDiffs = new ArrayList<>(); + Set newComponentDiffs = new HashSet<>(); + + // Check groups from netlist1 against netlist2 + for (Group g1 : newGroups1) { + boolean groupFound = false; + for (Group g2 : groups2) { + if (areGroupsEqual(g1, g2)) { + groupFound = true; + break; + } + } + if (!groupFound) { + // Add connection differences for this group + List nodes = new ArrayList<>(g1.getNodes()); + for (int i = 0; i < nodes.size(); i++) { + Node node = nodes.get(i); + for (int j = i + 1; j < nodes.size(); j++) { + Node otherNode = nodes.get(j); + newDiffs.add(new ConnectionDiff( + node.getComponent().getName(), + node.getComponent().getControlPointNodeName(node.getPointIndex()), + otherNode.getComponent().getName(), + otherNode.getComponent().getControlPointNodeName(otherNode.getPointIndex()), + true)); + } + } + } + } + + // Check groups from netlist2 against netlist1 + for (Group g2 : groups2) { + boolean groupFound = false; + for (Group g1 : newGroups1) { + if (areGroupsEqual(g1, g2)) { + groupFound = true; + break; + } + } + if (!groupFound) { + // Add connection differences for this group + List nodes = new ArrayList<>(g2.getNodes()); + for (int i = 0; i < nodes.size(); i++) { + Node node = nodes.get(i); + for (int j = i + 1; j < nodes.size(); j++) { + Node otherNode = nodes.get(j); + newDiffs.add(new ConnectionDiff( + node.getComponent().getName(), + node.getComponent().getControlPointNodeName(node.getPointIndex()), + otherNode.getComponent().getName(), + otherNode.getComponent().getControlPointNodeName(otherNode.getPointIndex()), + false)); + } + } + } + } + + // If this swap resolved all differences, use the new groups + if (newDiffs.isEmpty() && newComponentDiffs.isEmpty()) { + groups1 = newGroups1; + connectionDiffs = new ArrayList<>(); + componentDiffs = new HashSet<>(); + break; + } + } + } + } + if (connectionDiffs.isEmpty() && componentDiffs.isEmpty()) { return new CompareResults(true, List.of(), List.of()); } @@ -194,13 +324,25 @@ public class CompareService { java.util.function.Function nodeKey = node -> node.getComponent().getName() + ":" + node.getComponent().getControlPointNodeName(node.getPointIndex()); - Set group1Keys = group1.getNodes().stream() + // For polarized components, we need to check exact node order + List group1Keys = group1.getNodes().stream() .map(nodeKey) - .collect(Collectors.toSet()); - Set group2Keys = group2.getNodes().stream() + .collect(Collectors.toList()); + List group2Keys = group2.getNodes().stream() .map(nodeKey) - .collect(Collectors.toSet()); + .collect(Collectors.toList()); - return group1Keys.equals(group2Keys); + // If any component in the group is polarized, we need exact order match + boolean hasPolarizedComponent = group1.getNodes().stream() + .anyMatch(node -> node.getComponent().isPolarized()) || + group2.getNodes().stream() + .anyMatch(node -> node.getComponent().isPolarized()); + + if (hasPolarizedComponent) { + return group1Keys.equals(group2Keys); + } + + // For non-polarized components, we can use unordered comparison + return new HashSet<>(group1Keys).equals(new HashSet<>(group2Keys)); } } diff --git a/diylc/diylc-core/src/test/java/org/diylc/plugins/compare/service/CompareServiceTest.java b/diylc/diylc-core/src/test/java/org/diylc/plugins/compare/service/CompareServiceTest.java index b098b236..375a3219 100644 --- a/diylc/diylc-core/src/test/java/org/diylc/plugins/compare/service/CompareServiceTest.java +++ b/diylc/diylc-core/src/test/java/org/diylc/plugins/compare/service/CompareServiceTest.java @@ -187,6 +187,291 @@ public class CompareServiceTest { assertEquals(4, results.connectionDiffs().size()); } + @Test + public void testCompare_NonPolarizedComponent_SwappedNodes() { + Map> componentMap = new HashMap<>(); + + // Create first netlist with non-polarized component in two groups + Netlist netlist1 = new Netlist(new ArrayList<>()); + netlist1.add(createGroup(componentMap, "Resistor1", "Node1", "Component2", "Node2")); + netlist1.add(createGroup(componentMap, "Resistor1", "Node2", "Component3", "Node3")); + + // Create second netlist with same connections but different node order + Netlist netlist2 = new Netlist(new ArrayList<>()); + netlist2.add(createGroup(componentMap, "Resistor1", "Node2", "Component2", "Node2")); + netlist2.add(createGroup(componentMap, "Resistor1", "Node1", "Component3", "Node3")); + + // Make Resistor1 non-polarized + IDIYComponent resistor = componentMap.get("Resistor1"); + when(resistor.isPolarized()).thenReturn(false); + + CompareResults results = compareService.compare(netlist1, netlist2); + + assertTrue(results.matches()); + assertTrue(results.connectionDiffs().isEmpty()); + assertTrue(results.componentDiffs().isEmpty()); + } + + @Test + public void testCompare_PolarizedComponent_SwappedNodes() { + // Create first netlist with polarized component in two groups + Netlist netlist1 = new Netlist(new ArrayList<>()); + + // Create diode for netlist1 + IDIYComponent diode1 = mock(IDIYComponent.class); + when(diode1.getName()).thenReturn("Diode1"); + when(diode1.getControlPointCount()).thenReturn(2); + when(diode1.getControlPoint(0)).thenReturn(new Point2D.Double(0, 0)); + when(diode1.getControlPoint(1)).thenReturn(new Point2D.Double(0, 0)); + when(diode1.isControlPointSticky(0)).thenReturn(true); + when(diode1.isControlPointSticky(1)).thenReturn(true); + when(diode1.isPolarized()).thenReturn(true); + when(diode1.toString()).thenReturn("Diode1"); + when(diode1.getControlPointNodeName(0)).thenReturn("Anode"); + when(diode1.getControlPointNodeName(1)).thenReturn("Cathode"); + + // Create Component2 for netlist1 + IDIYComponent component2_1 = mock(IDIYComponent.class); + when(component2_1.getName()).thenReturn("Component2"); + when(component2_1.getControlPointNodeName(0)).thenReturn("Node2"); + when(component2_1.getControlPointCount()).thenReturn(1); + when(component2_1.getControlPoint(0)).thenReturn(new Point2D.Double(0, 0)); + when(component2_1.isControlPointSticky(0)).thenReturn(true); + when(component2_1.toString()).thenReturn("Component2"); + when(component2_1.isPolarized()).thenReturn(false); + + // Create Component3 for netlist1 + IDIYComponent component3_1 = mock(IDIYComponent.class); + when(component3_1.getName()).thenReturn("Component3"); + when(component3_1.getControlPointNodeName(0)).thenReturn("Node3"); + when(component3_1.getControlPointCount()).thenReturn(1); + when(component3_1.getControlPoint(0)).thenReturn(new Point2D.Double(0, 0)); + when(component3_1.isControlPointSticky(0)).thenReturn(true); + when(component3_1.toString()).thenReturn("Component3"); + when(component3_1.isPolarized()).thenReturn(false); + + // Create groups for netlist1 + Group group1 = new Group(); + Node node1 = new Node(diode1, 0); // Anode + Node node2 = new Node(component2_1, 0); + group1.getNodes().add(node1); + group1.getNodes().add(node2); + netlist1.add(group1); + + Group group2 = new Group(); + Node node3 = new Node(diode1, 1); // Cathode + Node node4 = new Node(component3_1, 0); + group2.getNodes().add(node3); + group2.getNodes().add(node4); + netlist1.add(group2); + + // Create second netlist with same connections but different node order + Netlist netlist2 = new Netlist(new ArrayList<>()); + + // Create diode for netlist2 + IDIYComponent diode2 = mock(IDIYComponent.class); + when(diode2.getName()).thenReturn("Diode1"); + when(diode2.getControlPointCount()).thenReturn(2); + when(diode2.getControlPoint(0)).thenReturn(new Point2D.Double(0, 0)); + when(diode2.getControlPoint(1)).thenReturn(new Point2D.Double(0, 0)); + when(diode2.isControlPointSticky(0)).thenReturn(true); + when(diode2.isControlPointSticky(1)).thenReturn(true); + when(diode2.isPolarized()).thenReturn(true); + when(diode2.toString()).thenReturn("Diode1"); + when(diode2.getControlPointNodeName(0)).thenReturn("Anode"); + when(diode2.getControlPointNodeName(1)).thenReturn("Cathode"); + + // Create Component2 for netlist2 + IDIYComponent component2_2 = mock(IDIYComponent.class); + when(component2_2.getName()).thenReturn("Component2"); + when(component2_2.getControlPointNodeName(0)).thenReturn("Node2"); + when(component2_2.getControlPointCount()).thenReturn(1); + when(component2_2.getControlPoint(0)).thenReturn(new Point2D.Double(0, 0)); + when(component2_2.isControlPointSticky(0)).thenReturn(true); + when(component2_2.toString()).thenReturn("Component2"); + when(component2_2.isPolarized()).thenReturn(false); + + // Create Component3 for netlist2 + IDIYComponent component3_2 = mock(IDIYComponent.class); + when(component3_2.getName()).thenReturn("Component3"); + when(component3_2.getControlPointNodeName(0)).thenReturn("Node3"); + when(component3_2.getControlPointCount()).thenReturn(1); + when(component3_2.getControlPoint(0)).thenReturn(new Point2D.Double(0, 0)); + when(component3_2.isControlPointSticky(0)).thenReturn(true); + when(component3_2.toString()).thenReturn("Component3"); + when(component3_2.isPolarized()).thenReturn(false); + + // Create groups for netlist2 with swapped nodes + Group group3 = new Group(); + Node node5 = new Node(diode2, 1); // Cathode + Node node6 = new Node(component2_2, 0); + group3.getNodes().add(node5); + group3.getNodes().add(node6); + netlist2.add(group3); + + Group group4 = new Group(); + Node node7 = new Node(diode2, 0); // Anode + Node node8 = new Node(component3_2, 0); + group4.getNodes().add(node7); + group4.getNodes().add(node8); + netlist2.add(group4); + + // Debug: Print the groups and verify diode polarity + System.out.println("Netlist1 groups:"); + for (Group g : netlist1.getGroups()) { + System.out.println("Group: " + g); + for (Node n : g.getNodes()) { + System.out.println(" Node: " + n.getComponent().getName() + + " " + n.getComponent().getControlPointNodeName(n.getPointIndex()) + + " (polarized: " + n.getComponent().isPolarized() + ")"); + } + } + System.out.println("\nNetlist2 groups:"); + for (Group g : netlist2.getGroups()) { + System.out.println("Group: " + g); + for (Node n : g.getNodes()) { + System.out.println(" Node: " + n.getComponent().getName() + + " " + n.getComponent().getControlPointNodeName(n.getPointIndex()) + + " (polarized: " + n.getComponent().isPolarized() + ")"); + } + } + + CompareResults results = compareService.compare(netlist1, netlist2); + + // Debug: Print comparison results + System.out.println("\nComparison results:"); + System.out.println("Matches: " + results.matches()); + System.out.println("Component diffs: " + results.componentDiffs().size()); + System.out.println("Connection diffs: " + results.connectionDiffs().size()); + for (ConnectionDiff diff : results.connectionDiffs()) { + System.out.println(" " + diff.fromComponent() + " " + diff.fromNodeName() + + " -> " + diff.toComponent() + " " + diff.toNodeName() + + " (present in current: " + diff.presentInCurrent() + ")"); + } + + assertTrue(!results.matches()); + assertEquals(0, results.componentDiffs().size()); + + // Since connections are reported in both directions, we expect 4 differences + assertEquals(4, results.connectionDiffs().size()); + + // Verify the specific differences + List diffs = results.connectionDiffs(); + boolean foundCathodeToComponent3 = false; + boolean foundComponent3ToCathode = false; + boolean foundComponent2ToAnode = false; + boolean foundAnodeToComponent2 = false; + boolean foundCathodeToComponent2 = false; + boolean foundComponent2ToCathode = false; + boolean foundAnodeToComponent3 = false; + boolean foundComponent3ToAnode = false; + + for (ConnectionDiff diff : diffs) { + // Check Diode1 Cathode <-> Component3 Node3 + if (diff.fromComponent().equals("Diode1") && + diff.fromNodeName().equals("Cathode") && + diff.toComponent().equals("Component3") && + diff.toNodeName().equals("Node3") && + diff.presentInCurrent()) { + foundCathodeToComponent3 = true; + } + if (diff.fromComponent().equals("Component3") && + diff.fromNodeName().equals("Node3") && + diff.toComponent().equals("Diode1") && + diff.toNodeName().equals("Cathode") && + diff.presentInCurrent()) { + foundComponent3ToCathode = true; + } + + // Check Component2 Node2 <-> Diode1 Anode + if (diff.fromComponent().equals("Component2") && + diff.fromNodeName().equals("Node2") && + diff.toComponent().equals("Diode1") && + diff.toNodeName().equals("Anode") && + diff.presentInCurrent()) { + foundComponent2ToAnode = true; + } + if (diff.fromComponent().equals("Diode1") && + diff.fromNodeName().equals("Anode") && + diff.toComponent().equals("Component2") && + diff.toNodeName().equals("Node2") && + diff.presentInCurrent()) { + foundAnodeToComponent2 = true; + } + + // Check Diode1 Cathode <-> Component2 Node2 (not present) + if (diff.fromComponent().equals("Diode1") && + diff.fromNodeName().equals("Cathode") && + diff.toComponent().equals("Component2") && + diff.toNodeName().equals("Node2") && + !diff.presentInCurrent()) { + foundCathodeToComponent2 = true; + } + if (diff.fromComponent().equals("Component2") && + diff.fromNodeName().equals("Node2") && + diff.toComponent().equals("Diode1") && + diff.toNodeName().equals("Cathode") && + !diff.presentInCurrent()) { + foundComponent2ToCathode = true; + } + + // Check Diode1 Anode <-> Component3 Node3 (not present) + if (diff.fromComponent().equals("Diode1") && + diff.fromNodeName().equals("Anode") && + diff.toComponent().equals("Component3") && + diff.toNodeName().equals("Node3") && + !diff.presentInCurrent()) { + foundAnodeToComponent3 = true; + } + if (diff.fromComponent().equals("Component3") && + diff.fromNodeName().equals("Node3") && + diff.toComponent().equals("Diode1") && + diff.toNodeName().equals("Anode") && + !diff.presentInCurrent()) { + foundComponent3ToAnode = true; + } + } + + // Check that we found at least one direction of each connection + assertTrue("Should detect difference in Diode1 Cathode <-> Component3 Node3 (present in current)", + foundCathodeToComponent3 || foundComponent3ToCathode); + assertTrue("Should detect difference in Component2 Node2 <-> Diode1 Anode (present in current)", + foundComponent2ToAnode || foundAnodeToComponent2); + assertTrue("Should detect difference in Diode1 Cathode <-> Component2 Node2 (not present in current)", + foundCathodeToComponent2 || foundComponent2ToCathode); + assertTrue("Should detect difference in Diode1 Anode <-> Component3 Node3 (not present in current)", + foundAnodeToComponent3 || foundComponent3ToAnode); + } + + @Test + public void testCompare_MixedPolarizedAndNonPolarized() { + Map> componentMap = new HashMap<>(); + + // Create first netlist with both polarized and non-polarized components + Netlist netlist1 = new Netlist(new ArrayList<>()); + netlist1.add(createGroup(componentMap, "Resistor1", "Node1", "Diode1", "Anode")); + netlist1.add(createGroup(componentMap, "Resistor1", "Node2", "Component3", "Node3")); + netlist1.add(createGroup(componentMap, "Diode1", "Cathode", "Component4", "Node4")); + + // Create second netlist with same connections but different node order for non-polarized + Netlist netlist2 = new Netlist(new ArrayList<>()); + netlist2.add(createGroup(componentMap, "Resistor1", "Node2", "Diode1", "Anode")); + netlist2.add(createGroup(componentMap, "Resistor1", "Node1", "Component3", "Node3")); + netlist2.add(createGroup(componentMap, "Diode1", "Cathode", "Component4", "Node4")); + + // Make Resistor1 non-polarized and Diode1 polarized + IDIYComponent resistor = componentMap.get("Resistor1"); + IDIYComponent diode = componentMap.get("Diode1"); + when(resistor.isPolarized()).thenReturn(false); + when(diode.isPolarized()).thenReturn(true); + + CompareResults results = compareService.compare(netlist1, netlist2); + + assertTrue(results.matches()); + assertTrue(results.connectionDiffs().isEmpty()); + assertTrue(results.componentDiffs().isEmpty()); + } + private Netlist createNetlist(Map> componentMap, String... componentNodePairs) { Netlist netlist = new Netlist(new ArrayList<>()); netlist.add(createGroup(componentMap, componentNodePairs)); @@ -208,6 +493,8 @@ public class CompareServiceTest { when(c.getControlPoint(0)).thenReturn(new Point2D.Double(0, 0)); when(c.isControlPointSticky(0)).thenReturn(true); when(c.toString()).thenReturn(name); + // Default to non-polarized + when(c.isPolarized()).thenReturn(false); return c; }); // For the same component name, update the node name for this instance