/*
* Copyright (C) 2023 Philipp Müller (pm251@venus.uni-freiburg.de)
* Copyright (C) 2023 University of Freiburg
*
* This file is part of the ULTIMATE Automata Library.
*
* The ULTIMATE Automata Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ULTIMATE Automata Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the ULTIMATE Automata Library. If not, see .
*
* Additional permission under GNU GPL version 3 section 7:
* If you modify the ULTIMATE Automata Library, or any covered work, by linking
* or combining it with Eclipse RCP (or a modified version of Eclipse RCP),
* containing parts covered by the terms of the Eclipse Public License, the
* licensors of the ULTIMATE Automata Library grant you additional permission
* to convey the resulting work.
*/
package de.uni_freiburg.informatik.ultimate.automata.rabin;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import de.uni_freiburg.informatik.ultimate.automata.nestedword.transitions.OutgoingInternalTransition;
import de.uni_freiburg.informatik.ultimate.automata.statefactory.IBlackWhiteStateFactory;
import de.uni_freiburg.informatik.ultimate.util.datastructures.relation.Pair;
import de.uni_freiburg.informatik.ultimate.util.datastructures.relation.TransformIterator;
/**
* class to lazyly construct the union of two Rabin automata
*
* @author Philipp Müller (pm251@venus.uni-freiburg.de)
*
* @param
* type of letter
* @param
* type of state
*/
public class RabinUnion implements IRabinAutomaton {
private final IRabinAutomaton mFirstAutomaton;
private final IRabinAutomaton mSecondAutomaton;
private final IBlackWhiteStateFactory mFactory;
private Set mInitialStates;
// 1 ~ firstAutomaton ~ Black, 0 ~ secondAutomaton ~ White
HashMap> mAutomatonMap = new HashMap<>();
/**
* implementation that lazyly constructs the union of two Rabin automata
*
* @param firstAutomaton
* first Rabin automaton to unite
* @param secondAutomaton
* second Rabin automaton to unite
* @param factory
* some BlackWhiteStateFactory for STATE
*/
public RabinUnion(final IRabinAutomaton firstAutomaton,
final IRabinAutomaton secondAutomaton, final IBlackWhiteStateFactory factory) {
mFirstAutomaton = firstAutomaton;
mSecondAutomaton = secondAutomaton;
mFactory = factory;
}
@Override
public Set getAlphabet() {
assert mFirstAutomaton.getAlphabet().equals(mSecondAutomaton.getAlphabet());
return mFirstAutomaton.getAlphabet();
}
@Override
public int size() {
return mFirstAutomaton.size() + mSecondAutomaton.size();
}
@Override
public String sizeInformation() {
return "Number of states: " + size() + "\n" + "of these there are: " + mFirstAutomaton.size()
+ " from firstAutomaton and: " + mSecondAutomaton.size() + " from secondAutomaton";
}
private Set constructInitialStates() {
final Set result = new HashSet<>();
for (final STATE state : mFirstAutomaton.getInitialStates()) {
result.add(getUnionState(state, true));
}
for (final STATE state : mSecondAutomaton.getInitialStates()) {
result.add(getUnionState(state, false));
}
return result;
}
@Override
public Set getInitialStates() {
if (mInitialStates == null) {
mInitialStates = constructInitialStates();
}
return mInitialStates;
}
@Override
public boolean isAccepting(final STATE state) {
final Pair originalStateInformation = mAutomatonMap.get(state);
return getSubautomaton(originalStateInformation.getFirst()).isAccepting(originalStateInformation.getSecond());
}
@Override
public boolean isFinite(final STATE state) {
final Pair originalStateInformation = mAutomatonMap.get(state);
return getSubautomaton(originalStateInformation.getFirst()).isFinite(originalStateInformation.getSecond());
}
@Override
public Iterable> getSuccessors(final STATE state) {
final Pair originalStateInformation = mAutomatonMap.get(state);
final boolean isFirst = originalStateInformation.getFirst();
return constructSuccessors(getSubautomaton(isFirst).getSuccessors(originalStateInformation.getSecond()),
isFirst);
}
@Override
public Iterable> getSuccessors(final STATE state, final LETTER letter) {
final Pair originalStateInformation = mAutomatonMap.get(state);
final boolean isFirst = originalStateInformation.getFirst();
return constructSuccessors(getSubautomaton(isFirst).getSuccessors(originalStateInformation.getSecond(), letter),
isFirst);
}
/**
* a helper function for getSucessors
*/
private Iterable> constructSuccessors(
final Iterable> subautomatonTransitions, final boolean isFirst) {
return () -> new TransformIterator<>(subautomatonTransitions.iterator(),
x -> new OutgoingInternalTransition<>(x.getLetter(), getUnionState(x.getSucc(), isFirst)));
}
/**
* this method creates different states, even if states in mFirstAutomaton and mSecondAutomaton have the same name,
* if a UnionState was already created this method returns its value without further computation
*
* @param originalState
* a state from either of mFirstAutomaton or mSecondAutomaton
* @param isFirst
* a boolean that declares explicitly if this state is from mFirstAutomaton or mSecondAutomaton
* @return
*/
private STATE getUnionState(final STATE originalState, final boolean isFirst) {
final STATE newState;
if (isFirst) {
newState = mFactory.getBlackContent(originalState);
} else {
newState = mFactory.getWhiteContent(originalState);
}
if (mAutomatonMap.containsKey(newState)) {
return newState;
}
mAutomatonMap.put(newState, new Pair<>(isFirst, originalState));
return newState;
}
/**
* a mapping function from a boolean to both subautomata
*
* @param isFirst
* a key, true maps to the mFirstAutomaton, second to mSecondAutomaton
* @return the corresponding subautomaton
*/
private IRabinAutomaton getSubautomaton(final boolean isFirst) {
if (isFirst) {
return mFirstAutomaton;
}
return mSecondAutomaton;
}
}