Map<CmpVertex,Integer> equivalenceClasses = new HashMapWithSearch<CmpVertex,Integer>(fsm.getStateNumber()), newEquivClasses = new HashMapWithSearch<CmpVertex,Integer>(fsm.getStateNumber());
// Since this one associates maps to numbers, make it Hash set so that fewer computations have to be performed.
Map<Map<Label,Integer>,Integer> sortedRows = new HashMap<Map<Label,Integer>,Integer>();
int WNext[] = new int[fsm.transitionMatrix.size()*(fsm.transitionMatrix.size()+1)/2];
Label WChar[] = new Label[fsm.transitionMatrix.size()*(fsm.transitionMatrix.size()+1)/2];
final Map<Label,AtomicInteger> distinguishingLabels = new HashMap<Label,AtomicInteger>();
for(CmpVertex stateA:fsm.transitionMatrix.keySet())
{
boolean stateAaccept = stateA.isAccept();
equivalenceClasses.put(stateA, stateAaccept?1:0);
Iterator<Entry<CmpVertex,Integer>> stateB_It = equivalenceClasses.entrySet().iterator();
while(stateB_It.hasNext())
{
Entry<CmpVertex,Integer> stateB = stateB_It.next();if (stateB.getKey().equals(stateA)) break; // we only process a triangular subset.
int index = fsm.wmethod.vertexToInt(stateA,stateB.getKey());
WChar[index] = null;
if (stateAaccept == stateB.getKey().isAccept())
WNext[index]=W_INDIST;// where two states have the same acceptance conditions,
// they cannot be distinguished at this stage.
else
WNext[index]=W_NOPREV;// an empty sequence distinguishes between the two,
// the fact that seq is empty is due to WChar[index] being null.
}
}
int equivalenceClassNumber = 0,oldEquivalenceClassNumber=0;
int statesEquivalentToSink = 0;
do
{
oldEquivalenceClassNumber = equivalenceClassNumber;statesEquivalentToSink = 0;
Map<CmpVertex,TransitionRowEqClass> newMap = new HashMapWithSearch<CmpVertex,TransitionRowEqClass>(fsm.getStateNumber());
equivalenceClassNumber = 0;sortedRows.clear();newEquivClasses.clear();
int sinkEqClass = equivalenceClasses.get(sink);
for(CmpVertex stateA:equivalenceClasses.keySet())
{
// This one is a vector associating names of inputs to equivalence classes of target states
TransitionRowEqClass map = new TransitionRowEqClass(stateA.isAccept());
Map<Label,CmpVertex> labelNSmap = fsm.transitionMatrix.get(stateA);
if (labelNSmap != null)
for(Entry<Label,CmpVertex> labelstate:labelNSmap.entrySet())
{
int targetEqClass = equivalenceClasses.get(labelstate.getValue());
if (targetEqClass != sinkEqClass) // filter out all transitions to sink - this is important because otherwise
// vectors for two reject-states with transitions to a sink state (equivalence class 0)
// { a->0, b->0 } and { a->0 } look superficially different even though they
// both denote states accepting an empty language.
// Note that, any real sink states would belong to the same equivalce class as our sink state.
map.put(labelstate.getKey(), targetEqClass);
}
newMap.put(stateA, map);
if (map.looksLikeSink()) statesEquivalentToSink++;
if (!sortedRows.containsKey(map))
{
sortedRows.put(map,equivalenceClassNumber);newEquivClasses.put(stateA, equivalenceClassNumber);
equivalenceClassNumber++;
}
else
newEquivClasses.put(stateA, sortedRows.get(map));
//System.out.println("state "+stateA+" has a map of "+map+" and a number "+sortedRows.get(map));
}
distinguishingLabels.clear();// clear a map from pairs of states to sets of labels which distinguish between them
for(Entry<CmpVertex,Integer> stateA:equivalenceClasses.entrySet())
{
Iterator<Entry<CmpVertex,Integer>> stateB_It = equivalenceClasses.entrySet().iterator();
while(stateB_It.hasNext())
{
Entry<CmpVertex,Integer> stateB = stateB_It.next();if (stateB.getKey().equals(stateA.getKey())) break; // we only process a triangular subset.
if (stateA.getValue().equals(stateB.getValue()) &&
!newEquivClasses.get(stateA.getKey()).equals(newEquivClasses.get(stateB.getKey())))
{// the two states used to be in the same equivalence class, now they are in different ones, hence we populate the matrix.
// the two states used to be equivalent but not any more, find inputs which
// distinguish between them and update the histogram to count the number
// of inputs which can be used distinguish between states at this stage.
for(Label distLabel:newMap.get(stateA.getKey()).computeDistinguishingLabel(newMap.get(stateB.getKey())))
{
AtomicInteger aint = distinguishingLabels.get(distLabel);
if (aint == null) { aint = new AtomicInteger(1);distinguishingLabels.put(distLabel, aint); }
else aint.addAndGet(1);
}
}
}
}
// distinguishingLabels contains all labels we may use; the choice of an optimal subset is NP, hence we simply pick
// those which look best until we distinguish all states.
ArrayList<Label> labelList = new ArrayList<Label>(distinguishingLabels.size());labelList.addAll(0, distinguishingLabels.keySet());
Collections.sort(labelList, new Comparator<Label>(){
@Override
public int compare(Label o1, Label o2) {
int diffInNumberOfdistStates = -distinguishingLabels.get(o1).get() + distinguishingLabels.get(o2).get();
if (diffInNumberOfdistStates != 0) return diffInNumberOfdistStates;
return o1.compareTo(o2);// otherwise, just compare strings.
}
});
for(Entry<CmpVertex,Integer> stateA:equivalenceClasses.entrySet())
{
Iterator<Entry<CmpVertex,Integer>> stateB_It = equivalenceClasses.entrySet().iterator();
while(stateB_It.hasNext())
{
Entry<CmpVertex,Integer> stateB = stateB_It.next();if (stateB.getKey().equals(stateA.getKey())) break; // we only process a triangular subset.
//System.out.println("looking at "+stateA.getKey()+", "+stateB.getKey()+" eq classes: "+stateA.getValue()+", "+stateB.getValue());
if (stateA.getValue().equals(stateB.getValue()) &&
!newEquivClasses.get(stateA.getKey()).equals(newEquivClasses.get(stateB.getKey())))
{// the two states used to be in the same equivalence class, now they are in different ones, hence we populate the matrix.
Set<Label> distLabels = newMap.get(stateA.getKey()).computeDistinguishingLabel(newMap.get(stateB.getKey()));
Label topLabel = null;
Iterator<Label> topLabelIter = labelList.iterator();
while(topLabel == null)
{
Label lbl = topLabelIter.next();if (distLabels.contains(lbl)) topLabel = lbl;
}
CmpVertex toA = stateA.getKey() == sink? sink:fsm.transitionMatrix.get(stateA.getKey()).get(topLabel);if (toA == null) toA=sink;
CmpVertex toB = stateB.getKey() == sink? sink:fsm.transitionMatrix.get(stateB.getKey()).get(topLabel);if (toB == null) toB=sink;
int index = fsm.wmethod.vertexToInt(stateA.getKey(),stateB.getKey());
assert WChar[index] == null : "In states ("+stateA.getKey()+","+stateB.getKey()+") Wsequence is non-empty and contains "+WChar[index];
WChar[index] = topLabel;
int previous = fsm.wmethod.vertexToInt(toA,toB);
assert toA.isAccept() != toB.isAccept() || WChar[previous] != null : "In states ("+stateA.getKey()+","+stateB.getKey()+") previous pair ("+toA+","+toB+") has a null sequence";
WNext[index] = previous;
}
}
}
equivalenceClasses = newEquivClasses;newEquivClasses = new HashMapWithSearch<CmpVertex,Integer>(fsm.getStateNumber());
}
while(equivalenceClassNumber > oldEquivalenceClassNumber);
Collection<List<Label>> result = new HashSet<List<Label>>();
assert statesEquivalentToSink > 0: "missing equivalence class associated with sink state";
// If all states have been distinguished, statesEquivalentToSink can be either 1 (no existing state is like sink) or 2 (there is an existing sink-like state).
// (to test where sink is partial)
// If statesEquivalentToSink is 1, oldEquivalenceClassNumber == fsm.transitionMatrix.size()
// for 2, oldEquivalenceClassNumber == fsm.transitionMatrix.size()-1
if ((statesEquivalentToSink <= 2 && oldEquivalenceClassNumber == fsm.transitionMatrix.size()+1-statesEquivalentToSink )
|| fsm.config.getEquivalentStatesAllowedForW())
{
// This one means that we only consider our artificial sink state as a real state
// if there is no graph state which accepts an empty language.
boolean sinkAsRealState = !fsm.config.isPrefixClosed() && statesEquivalentToSink == 1;
for(Entry<CmpVertex,Integer> stateA:equivalenceClasses.entrySet())
if (sinkAsRealState || stateA.getKey() != sink)
{
Iterator<Entry<CmpVertex,Integer>> stateB_It = equivalenceClasses.entrySet().iterator();
while(stateB_It.hasNext())
{
Entry<CmpVertex,Integer> stateB = stateB_It.next();if (stateB.getKey().equals(stateA.getKey())) break; // we only process a triangular subset.
if (sinkAsRealState || stateB.getKey() != sink)
{
LinkedList<Label> seq = new LinkedList<Label>();
int index = fsm.wmethod.vertexToInt(stateA.getKey(),stateB.getKey());
assert index >= 0;
if (WNext[index]!=W_INDIST)
{// stateA and stateB have been separated.
Label elementToAdd = WChar[index];
while(elementToAdd != null)
{
seq.add(elementToAdd);
index=WNext[index];
elementToAdd = WChar[index];