/*
* (C) Copyright 2002 Arnaud Bailly (arnaud.oqube@gmail.com),
* Yves Roos (yroos@lifl.fr) and others.
*
* 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 rationals.transformations;
import rationals.*;
import java.util.*;
/**
* This class implements the mix - ie: synchronization product - operator
* between two automatas.
* <ul>
* <li>C = A mix B</li>
* <li>S(C) = { (a,b) | a in S(A) and b in S(B) }</li>
* <li>S0(C) = (S0(A),SO(B))</li>
* <li>T(C) = { (a,b) | a in T(A) and b in T(B) }</li>
* <li>D(C) = { ((s1a,s1b),a,(s2a,s2b)) | exists (s1a,a,s2a) in D(A) and exists
* (s1b,a,s2b) in D(b) } U { ((s1a,s1b),a,(s1a,s2b)) | a not in S(A) and exists
* (s1b,a,s2b) in D(b) } U { ((s1a,s1b),a,(s2a,s1b)) | a not in S(B) and exists
* (s1a,a,s2a) in D(a) }</li>
* </ul>
*
* @author Arnaud Bailly
* @version 22032002
*/
public class Mix<L, Tr extends Transition<L>, T extends Builder<L, Tr, T>> implements BinaryTransformation<L, Tr, T> {
private Synchronization<L> synchronization;
/**
* Compute mix of two automata using default synchronization scheme which is
* the equality of labels.
*
* @see rationals.DefaultSynchronization
* @see rationals.Synchronization
*/
public Mix() {
this.synchronization = new DefaultSynchronization();
}
/**
* Compute mix of two automata using given synchronization scheme.
*
* @param synch
* a Synchronization object. Must not be null.
*/
public Mix(Synchronization synch) {
this.synchronization = synch;
}
/*
* (non-Javadoc)
*
* @see
* rationals.transformations.BinaryTransformation#transform(rationals.Automaton
* , rationals.Automaton)
*/
public Automaton<L, Tr, T> transform(Automaton<L, Tr, T> a, Automaton<L, Tr, T> b) {
Automaton<L, Tr, T> ret = new Automaton();
// FIXME: Potentially unsafe cast, but adding generics reveals an inconsistency in the APIs
ret.setBuilder((T) new TransitionBuilder<L>());
return transformTo(a, b, ret);
}
/**
* Compute transformation and stores the result in the given automaton.
*
* @param a
* @param b
* @param ret
* @return the automaton {@code ret} containing the result of the
* transformation.
*/
public Automaton<L, Tr, T> transformTo(Automaton<L, Tr, T> a, Automaton<L, Tr, T> b, Automaton<L, Tr, T> ret) {
Set alph = synchronization.synchronizable(a.alphabet(), b.alphabet());
/* check alphabets */
Map<StatesCouple, State> amap = new HashMap();
Map<StatesCouple, State> bmap = new HashMap();
List<StatesCouple> todo = new ArrayList();
Set<StatesCouple> done = new HashSet();
Set<State> as = TransformationsToolBox.epsilonClosure(a.initials(), a);
Set<State> bs = TransformationsToolBox.epsilonClosure(b.initials(), b);
State from = ret.addState(true, TransformationsToolBox
.containsATerminalState(as)
&& TransformationsToolBox.containsATerminalState(bs));
StatesCouple sc = new StatesCouple(as, bs);
amap.put(sc, from);
todo.add(sc);
do {
StatesCouple couple = (StatesCouple) todo.remove(0);
from = (State) amap.get(couple);
if (done.contains(couple))
continue;
done.add(couple);
/* get transition sets */
Map<L, Set<State>> tam = TransformationsToolBox.mapAlphabet(a.delta(couple.sa), a);
Map<L, Set<State>> tbm = TransformationsToolBox.mapAlphabet(b.delta(couple.sb), b);
/* create label map for synchronized trans */
Map<L, StatesCouple> tcm = new HashMap();
/* unsynchronizable transitions in A */
for (Iterator<Map.Entry<L, Set<State>>> i = tam.entrySet().iterator(); i.hasNext();) {
Map.Entry<L, Set<State>> me = i.next();
L l = me.getKey();
as = me.getValue();
if (!alph.contains(l)) {
Set<State> asc = TransformationsToolBox.epsilonClosure(as, a);
tcm.put(l, sc = new StatesCouple(asc, couple.sb));
State to = (State) amap.get(sc);
makeNewState(ret, amap, sc, to);
todo.add(sc);
i.remove();
}
}
/* unsynchronizable transition(s) in B */
for (Iterator<Map.Entry<L, Set<State>>> i = tbm.entrySet().iterator(); i.hasNext();) {
Map.Entry<L, Set<State>> me = i.next();
L l = me.getKey();
bs = me.getValue();
if (!alph.contains(l)) {
Set<State> bsc = TransformationsToolBox.epsilonClosure(bs, b);
tcm.put(l, sc = new StatesCouple(couple.sa, bsc));
State to = amap.get(sc);
makeNewState(ret, amap, sc, to);
todo.add(sc);
i.remove();
}
}
/*
* there remains in tam and tbm only possibly synchronizing
* transitions
*/
for (Iterator<Map.Entry<L, Set<State>>> i = tam.entrySet().iterator(); i.hasNext();) {
Map.Entry<L, Set<State>> me = i.next();
L l = me.getKey();
as = me.getValue();
for (Iterator<Map.Entry<L, Set<State>>> j = tbm.entrySet().iterator(); j.hasNext();) {
Map.Entry<L, Set<State>> mbe = j.next();
L k = mbe.getKey();
bs = mbe.getValue();
L sy = synchronization.synchronize(l, k);
if (sy != null) {
Set<State> asc = TransformationsToolBox.epsilonClosure(as, a);
Set<State> bsc = TransformationsToolBox.epsilonClosure(bs, b);
tcm.put(sy, sc = new StatesCouple(asc, bsc));
State to = amap.get(sc);
makeNewState(ret, amap, sc, to);
todo.add(sc);
}
}
}
/*
*
* create new transitions in return automaton, update maps
*/
for (Iterator<Map.Entry<L, StatesCouple>> i = tcm.entrySet().iterator(); i.hasNext();) {
Map.Entry<L, StatesCouple> me = i.next();
L l = me.getKey();
sc = me.getValue();
State to = amap.get(sc);
makeNewState(ret, amap, sc, to);
try {
ret.build(from, l, to);
} catch (NoSuchStateException e) {
}
// ret.from(from).on(l).go(to);
}
} while (!todo.isEmpty());
return ret;
}
private void makeNewState(Automaton ret, Map amap, StatesCouple sc, State to) {
if (to == null) {
to = ret.addState(false, TransformationsToolBox
.containsATerminalState(sc.sa)
&& TransformationsToolBox.containsATerminalState(sc.sb));
amap.put(sc, to);
}
}
}