/******************************************************************************* * Copyright 2012 Analog Devices, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ********************************************************************************/ package com.analog.lyric.util.misc; import java.util.ArrayList; import java.util.HashSet; import java.util.UUID; import org.eclipse.jdt.annotation.Nullable; import com.analog.lyric.dimple.model.core.FactorGraph; import com.analog.lyric.dimple.model.core.FactorGraphIterables; import com.analog.lyric.dimple.model.core.INameable; import com.analog.lyric.dimple.model.core.INode; import com.analog.lyric.dimple.model.core.Port; import com.analog.lyric.dimple.model.factors.Factor; import com.analog.lyric.dimple.model.variables.Discrete; @Internal public class FactorGraphDiffs { private @Nullable INameable _a = null; private @Nullable INameable _b = null; private @Nullable ArrayList<INameable> _childrenInANotB = null; private @Nullable ArrayList<INameable> _childrenInBNotA = null; private @Nullable ArrayList<FactorGraphDiffs> _childDiffs = null; public FactorGraphDiffs() { this(null, null); } public FactorGraphDiffs(@Nullable INameable a, @Nullable INameable b) { _a = a; _b = b; } public @Nullable INameable getA() { return _a; } public @Nullable INameable getB() { return _a; } public @Nullable ArrayList<INameable> inANotB() { return _childrenInANotB; } public @Nullable ArrayList<INameable> inBNotA() { return _childrenInBNotA; } public @Nullable ArrayList<FactorGraphDiffs> childDiffs() { return _childDiffs; } static private String tabString(int numTabs) { String s = ""; for(int i = 0; i < numTabs; ++i) { s += "\t"; } return s; } static private String typeString(@Nullable INameable n) { String s = ""; if(n instanceof FactorGraph) { s = "Graph "; } else if(n instanceof Discrete) { s = "Variable"; } else if(n instanceof Factor) { s = "Factor "; } else if (n != null) { s = n.getClass().toString(); } return s; } public String toString(int numTabs) { String s = ""; int pathDBG = 0; try { //String tabString = tabString(numTabs); if(numTabs == 0) { pathDBG |= 0x00000001; s += "diffs "; } pathDBG |= 0x00000002; String aName = "none"; String bName = "none"; int inANotB = 0; int inBNotA = 0; int childDiffsCount = 0; String typeString = "none"; final INameable a = _a; if(a != null) { aName = a.getQualifiedLabel(); typeString = typeString(a); } final INameable b = _b; if(b != null) { bName = b.getQualifiedLabel(); typeString = typeString(b); } final ArrayList<INameable> childrenInANotB = _childrenInANotB; if(childrenInANotB != null) { inANotB = childrenInANotB.size(); } final ArrayList<INameable> childrenInBNotA = _childrenInBNotA; if(childrenInBNotA != null) { inANotB = childrenInBNotA.size(); } final ArrayList<FactorGraphDiffs> childDiffs = _childDiffs; if(childDiffs != null) { childDiffsCount = childDiffs.size(); } s += String.format("[%s] a:[%s] b:[%s] inANotB:%d inBNotA:%d childDiffs:%d" ,typeString ,aName ,bName ,inANotB ,inBNotA ,childDiffsCount); pathDBG |= 0x00000004; String oneMoreTab = tabString(numTabs + 1); String twoMoreTabs = tabString(numTabs + 2); pathDBG |= 0x00000008; if(childrenInANotB != null) { pathDBG |= 0x00000010; s += String.format("\n%sIn A not B:\n", oneMoreTab); for(INameable n : childrenInANotB) { pathDBG |= 0x00000020; s += twoMoreTabs; s += "["; s += typeString(n); s += "] "; if(n != null) { s += n.getQualifiedLabel(); } else { s += "none"; } s += "\n"; } } pathDBG |= 0x00000040; if(childrenInBNotA != null) { pathDBG |= 0x00000080; s += String.format("\n%sIn B not A:\n", oneMoreTab); for(INameable n : childrenInBNotA) { pathDBG |= 0x00000100; s += twoMoreTabs; s += "["; s += typeString(n); s += "] "; if(n != null) { s += n.getQualifiedLabel(); } else { s += "none"; } s += "\n"; } } pathDBG |= 0x00000200; if(childDiffs != null) { pathDBG |= 0x00000400; s += String.format("\n%sChild diffs:\n", oneMoreTab); for(FactorGraphDiffs d : childDiffs) { pathDBG |= 0x00000800; s += twoMoreTabs; if(d != null) { //s += n.getQualifiedLabel(); s += d.toString(numTabs + 1); } else { s += "none"; } s += "\n"; } pathDBG |= 0x00001000; } pathDBG |= 0x00002000; } catch(Exception e) { s += "\nException - " + e.toString() + " Path debug: " + pathDBG; } return s; } @Override public String toString() { return toString(0); } public void print() { print(""); } public void print(String tag) { System.out.println(tag + " - " + toString()); } public boolean noDiffs() { return _childrenInANotB == null && _childrenInBNotA == null && _childDiffs == null; } private void addANotB(INameable node) { ArrayList<INameable> childrenInANotB = _childrenInANotB; if(childrenInANotB == null) { childrenInANotB = _childrenInANotB = new ArrayList<INameable>(); } childrenInANotB.add(node); //System.out.println("--addANotB [" + node.getLabel() + "] [\n" + toString() + "\n]\n"); } private void addBNotA(INameable node) { ArrayList<INameable> childrenInBNotA = _childrenInBNotA; if(childrenInBNotA == null) { childrenInBNotA = _childrenInBNotA = new ArrayList<INameable>(); } childrenInBNotA.add(node); //System.out.println("--addBNotA [" + node.getLabel() + "] [\n" + toString() + "\n]\n"); } private void addDifference(FactorGraphDiffs difference) { ArrayList<FactorGraphDiffs> childDiffs = _childDiffs; if(childDiffs == null) { childDiffs = _childDiffs = new ArrayList<FactorGraphDiffs>(); } childDiffs.add(difference); } private void addDifference(INameable a, INameable b, @Nullable ArrayList<FactorGraphDiffs> childFactorGraphDiffs) { FactorGraphDiffs oneDiff = new FactorGraphDiffs(); oneDiff._a = a; oneDiff._b = b; oneDiff._childDiffs = childFactorGraphDiffs; addDifference(oneDiff); //System.out.println("--addBNotA [" + a.getLabel() + "] [" + b.getLabel() + "]\n" + toString() + "\n]\n"); } static private boolean CompareTwoNameables(INameable a, INameable b, boolean byName) { String aName = a.getName(); String bName = b.getName(); UUID aUUID = a.getUUID(); UUID bUUID = b.getUUID(); boolean bEquals = //identity same? (byName ? aName.equals(bName) : aUUID.equals(bUUID) ) && //if identity same, class same? a.getClass().equals(b.getClass()); return bEquals; } static private boolean CompareTwoPorts(Port a, Port b, boolean byName) { INode aParent = a.getNode(); INode aConnected = a.getSiblingNode(); INode bParent = b.getNode(); INode bConnected = b.getSiblingNode(); boolean equal = CompareTwoNameables(aParent, bParent, byName) && CompareTwoNameables(aConnected, bConnected, byName); return equal; } static private FactorGraphDiffs comparePorts( INameable a, INameable b, boolean quickExit, boolean byName) { INode ndA = (INode) a; INode ndB = (INode) b; ArrayList<Port> aPorts = new ArrayList<>(ndA.getPorts()); ArrayList<Port> bPorts = new ArrayList<>(ndB.getPorts()); FactorGraphDiffs diffs = new FactorGraphDiffs(a, b); //variables don't have an ordered set of ports like //factors, as they can 'accumulate' connections //whereas factors have all their connections added //exactly in addFactor //So for variables we must 'go looking' HashSet<Integer> bPortIndices = new HashSet<Integer>(); for(int i = 0; i < bPorts.size(); ++i) { bPortIndices.add(i); } //a against b boolean exit = false; for(int i = 0; i < aPorts.size() && !exit; ++i) { boolean same = true; if(i < bPorts.size()) { //For Factors, just go pairwise if(ndA instanceof Factor || ndB instanceof Factor) { same = CompareTwoPorts(aPorts.get(i), bPorts.get(i), byName); if(!same) { diffs.addANotB(aPorts.get(i).getSiblingNode()); diffs.addBNotA(bPorts.get(i).getSiblingNode()); } } else { //For variables, go 'looking' int match = -1; for(Integer bIdx : bPortIndices) { if(CompareTwoPorts(aPorts.get(i), bPorts.get(bIdx), byName)) { match = bIdx; break; } } if(match == -1) { diffs.addANotB(aPorts.get(i).getSiblingNode()); //Not strictly complete; if there's a mismatch, a //and b have same number of ports, there must also //be a port in B but not in A //But we don't bother to go look for that. } else { bPortIndices.remove(match); } } } else { same = false; diffs.addANotB(aPorts.get(i).getSiblingNode()); } exit = quickExit && !same; } if(!exit && bPorts.size() > aPorts.size()) { for(int i = aPorts.size(); i < bPorts.size() && !exit; ++i) { diffs.addBNotA(bPorts.get(i).getSiblingNode()); exit = quickExit; } } return diffs; } static private void compareNameableMaps(FactorGraphDiffs diffs, NameableMap aMap, NameableMap bMap, boolean quickExit, boolean byName) { boolean someInANotB = false; //a against b for(INameable nA : aMap) { @Nullable INameable nB = null; if(byName) { nB = bMap.get(nA.getName()); } else { nB = bMap.get(nA.getUUID()); } if(nB == null || //in a, missing from b //or in both places with same identity, but different class !nB.getClass().equals(nA.getClass())) { someInANotB = true; diffs.addANotB(nA); if(quickExit) { break; }//else keep accumulating } //else in both; are they the same? else { FactorGraphDiffs oneDiff = null; if(nA instanceof FactorGraph) { oneDiff = getFactorGraphDiffsNoRootSearch( (FactorGraph) nA, (FactorGraph) nB, quickExit, byName); } else { oneDiff = comparePorts( nA, nB, quickExit, byName); } if(!oneDiff.noDiffs()) { diffs.addDifference(oneDiff); if(quickExit) { break; }//else keep accumulating }//else same }//end check one nA vs one nB }//end loop comparing a list against b list //look for in b but not a if((someInANotB || aMap.size() != bMap.size()) && !quickExit) { for(INameable nB : bMap) { INameable nA = null; if(byName) { nA = aMap.get(nB.getName()); } else { nA = aMap.get(nB.getUUID()); } if(nA == null) { diffs.addBNotA(nB); if(quickExit) { break; }//else keep accumulating }//else both there - we must hace checked for node equality above }//end loop comparing b list against a list }//else everything happy or quick bomb out } static public FactorGraphDiffs getFactorGraphDiffs( FactorGraph a, FactorGraph b, boolean quickExit, boolean byName) { return getFactorGraphDiffsNoRootSearch( a.getRootGraph(), b.getRootGraph(), quickExit, byName); } static public FactorGraphDiffs getFactorGraphDiffsNoRootSearch( FactorGraph a, FactorGraph b, boolean quickExit, boolean byName) { FactorGraphDiffs diffs = new FactorGraphDiffs(a, b); String aName = a.getName(); String bName = b.getName(); UUID aUUID = a.getUUID(); UUID bUUID = b.getUUID(); //System.out.println("++getFactorGraphDiffs [" + a.getLabel() + "] [" + b.getLabel() + "]\n"); boolean bEquals = byName ? aName.equals(bName) : aUUID.equals(bUUID); if(!bEquals) { diffs.addDifference(a, b, null); } if(!a.hasParentGraph()) { @SuppressWarnings("all") NameableMap aMap = new NameableMap(FactorGraphIterables.boundary(a)); @SuppressWarnings("all") NameableMap bMap = new NameableMap(FactorGraphIterables.boundary(b)); compareNameableMaps(diffs, aMap, bMap, quickExit, byName); } if(diffs.noDiffs() || !quickExit) { @SuppressWarnings("all") NameableMap aMap = new NameableMap(a.getNodesTop() .values()); @SuppressWarnings("all") NameableMap bMap = new NameableMap(b.getNodesTop() .values()); compareNameableMaps(diffs, aMap, bMap, quickExit, byName); } if(diffs.noDiffs() || !quickExit) { @SuppressWarnings("all") NameableMap aMap = new NameableMap(a.getOwnedGraphs()); @SuppressWarnings("all") NameableMap bMap = new NameableMap(b.getOwnedGraphs()); compareNameableMaps(diffs, aMap, bMap, quickExit, byName); } //System.out.println("--getFactorGraphDiffs [\n" + diffs.toString() + "\n]\n"); return diffs; } }