499 lines
16 KiB
Java
499 lines
16 KiB
Java
package ab1.impl.GRUPPE;
|
|
|
|
import ab1.FinalizedStateException;
|
|
import ab1.NFA;
|
|
import ab1.Transition;
|
|
import lombok.Getter;
|
|
|
|
import java.util.*;
|
|
|
|
public class NFAImpl implements NFA {
|
|
private Set<String> states;
|
|
private Set<Transition> transitions;
|
|
|
|
private String initialState;
|
|
private Set<String> acceptingStates;
|
|
|
|
private Set<Character> alphabet;
|
|
|
|
private boolean isFinalized;
|
|
|
|
public NFAImpl(String startState) {
|
|
this.initialState = startState;
|
|
this.states = new HashSet<>();
|
|
this.states.add(startState);
|
|
this.transitions = new HashSet<>();
|
|
this.acceptingStates = new HashSet<>();
|
|
this.alphabet = new HashSet<>();
|
|
this.isFinalized = false;
|
|
|
|
}
|
|
|
|
@Override
|
|
public Set<String> getStates() {
|
|
return this.states;
|
|
}
|
|
|
|
public void safeAddStates(Set<String> states, Set<String> toCheck, NFAImpl nfa) {
|
|
for (String state : states) {
|
|
nfa.states.add(changeIfNecessary(state, toCheck));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Collection<Transition> getTransitions() {
|
|
return this.transitions;
|
|
}
|
|
|
|
@Override
|
|
public Set<String> getAcceptingStates() {
|
|
return this.acceptingStates;
|
|
}
|
|
|
|
@Override
|
|
public String getInitialState() {
|
|
return this.initialState;
|
|
}
|
|
|
|
public Set<Character> getAlphabet() {
|
|
return this.alphabet;
|
|
}
|
|
|
|
@Override
|
|
public void addTransition(Transition transition) throws FinalizedStateException {
|
|
if (this.isFinalized) {
|
|
throw new FinalizedStateException();
|
|
}
|
|
if (!states.contains(transition.fromState())) {
|
|
this.states.add(transition.fromState());
|
|
}
|
|
if (!states.contains(transition.toState())) {
|
|
this.states.add(transition.toState());
|
|
}
|
|
// add symbol to alphabet. If it is already in the alphabet, nothing happens
|
|
if (transition.readSymbol() != null) {
|
|
this.alphabet.add(transition.readSymbol());
|
|
}
|
|
this.transitions.add(transition);
|
|
}
|
|
|
|
public void addAllTransitions(Set<Transition> transitions) throws FinalizedStateException {
|
|
if (this.isFinalized) {
|
|
throw new FinalizedStateException();
|
|
}
|
|
for (Transition transition : transitions) {
|
|
this.addTransition(transition);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void addAcceptingState(String state) throws FinalizedStateException {
|
|
if (this.isFinalized) {
|
|
throw new FinalizedStateException();
|
|
}
|
|
this.states.add(state);
|
|
this.acceptingStates.add(state);
|
|
}
|
|
|
|
public void addAllAcceptingStates(Set<String> states) throws FinalizedStateException {
|
|
if (this.isFinalized) {
|
|
throw new FinalizedStateException();
|
|
}
|
|
for (String state : states) {
|
|
this.addAcceptingState(state);
|
|
}
|
|
}
|
|
|
|
public void safeAddAcceptingStates(Set<String> states, Set<String> toCheck, NFAImpl nfa) {
|
|
for (String state : states) {
|
|
nfa.addAcceptingState(changeIfNecessary(state, toCheck));
|
|
}
|
|
}
|
|
|
|
// #TODO
|
|
// write more testcases
|
|
@Override
|
|
public NFA union(NFA other) throws FinalizedStateException {
|
|
if (!this.isFinalized || !other.isFinalized()) {
|
|
throw new FinalizedStateException();
|
|
}
|
|
// new initialState with epsilon to initialState of this and other.
|
|
// Problem: what if states are called the same in this and other + what do we use as initialState?
|
|
NFAImpl unionNFA = new NFAImpl(this.getInitialState());
|
|
|
|
unionNFA.states.addAll(this.states);
|
|
|
|
unionNFA.addAllAcceptingStates(this.acceptingStates);
|
|
|
|
Set<String> otherAcceptingStates = new HashSet<>(other.getAcceptingStates());
|
|
unionNFA.safeAddAcceptingStates(this.acceptingStates, otherAcceptingStates, unionNFA);
|
|
|
|
|
|
Set<Transition> newTransitions = new HashSet<>();
|
|
for (Transition transition : other.getTransitions()) {
|
|
Transition newTransition =
|
|
new Transition(transition.fromState(), transition.readSymbol(), transition.toState());
|
|
newTransitions.add(newTransition);
|
|
}
|
|
String flag = "";
|
|
newTransitions.add(new Transition(flag, null, other.getInitialState()));
|
|
|
|
for (String state : other.getStates()) {
|
|
if (!unionNFA.states.contains(state)) {
|
|
unionNFA.states.add(state);
|
|
if (other.getAcceptingStates().contains(state)) {
|
|
unionNFA.acceptingStates.add(state);
|
|
}
|
|
} else {
|
|
// a state of other has the same name as one in this.
|
|
// change name of state and every Transition it is a part of in other
|
|
String newState = changeIfNecessary(state, unionNFA.states);
|
|
|
|
// wieder a neues set
|
|
Set<Transition> tempTransitions = new HashSet<>();
|
|
|
|
// check each transition of other if start or to state was state
|
|
for (Transition transition : newTransitions) {
|
|
if (transition.fromState().equals(state)) {
|
|
transition = new Transition(newState, transition.readSymbol(), transition.toState());
|
|
}
|
|
if (transition.toState().equals(state)) {
|
|
transition = new Transition(transition.fromState(), transition.readSymbol(), newState);
|
|
}
|
|
tempTransitions.add(transition);
|
|
}
|
|
for (String accState : other.getAcceptingStates()) {
|
|
if (accState.equals(state)) {
|
|
unionNFA.acceptingStates.add(newState);
|
|
}
|
|
}
|
|
newTransitions = tempTransitions; // Replace the original set with the updated set
|
|
}
|
|
}
|
|
// add, alphabet
|
|
|
|
Iterator<Transition> iterator = newTransitions.iterator();
|
|
while (iterator.hasNext()){
|
|
Transition transition = iterator.next();
|
|
if(transition.fromState().isEmpty()){
|
|
iterator.remove();
|
|
|
|
newTransitions.add(new Transition(unionNFA.initialState, transition.readSymbol(), transition.toState()));
|
|
break;
|
|
}
|
|
}
|
|
|
|
unionNFA.addAllTransitions(newTransitions);
|
|
|
|
unionNFA.addAllTransitions(this.transitions);
|
|
|
|
unionNFA.finalizeAutomaton();
|
|
|
|
|
|
return unionNFA;
|
|
}
|
|
|
|
// #TODO
|
|
// needs to work with a DFA not NFA
|
|
@Override
|
|
public NFA intersection(NFA other) throws FinalizedStateException {
|
|
if (!this.isFinalized || !other.isFinalized()) {
|
|
throw new FinalizedStateException();
|
|
}
|
|
NFAImpl intersectionNFA = new NFAImpl(this.initialState);
|
|
|
|
intersectionNFA.states.addAll(this.states);
|
|
intersectionNFA.states.retainAll(other.getStates());
|
|
|
|
intersectionNFA.transitions.addAll(this.transitions);
|
|
intersectionNFA.transitions.retainAll(other.getTransitions());
|
|
|
|
return intersectionNFA;
|
|
}
|
|
|
|
// #TODO
|
|
@Override
|
|
public NFA concatenation(NFA other) throws FinalizedStateException {
|
|
if (!this.isFinalized || !other.isFinalized()) {
|
|
throw new FinalizedStateException();
|
|
}
|
|
NFAImpl concatenationNFA = new NFAImpl(this.initialState);
|
|
|
|
concatenationNFA.states.addAll(this.states);
|
|
concatenationNFA.states.addAll(other.getStates());
|
|
|
|
concatenationNFA.transitions.addAll(this.transitions);
|
|
concatenationNFA.transitions.addAll(other.getTransitions());
|
|
|
|
for (String accceptingState : this.acceptingStates) {
|
|
Transition epsilon = new Transition(accceptingState, null, other.getInitialState());
|
|
concatenationNFA.transitions.add(epsilon);
|
|
}
|
|
|
|
return concatenationNFA;
|
|
}
|
|
|
|
@Override
|
|
public NFA kleeneStar() throws FinalizedStateException {
|
|
if (!this.isFinalized) {
|
|
throw new FinalizedStateException();
|
|
}
|
|
|
|
NFAImpl nfa = new NFAImpl(this.initialState);
|
|
|
|
// copy, but without accepting states
|
|
nfa.states.addAll(this.states);
|
|
nfa.addAllTransitions(this.transitions);
|
|
|
|
// adding the initial state as accepting state because we have to accept the empty string
|
|
nfa.acceptingStates.add(this.initialState);
|
|
|
|
// for each accepting state
|
|
for (String acceptingState : this.getAcceptingStates()) {
|
|
Transition loopBackTransition =
|
|
// creating an epsilon transition (null) for each accepting state
|
|
new Transition(acceptingState, null, this.initialState);
|
|
nfa.transitions.add(loopBackTransition);
|
|
}
|
|
|
|
|
|
return nfa;
|
|
}
|
|
|
|
@Override
|
|
public NFA plusOperator() throws FinalizedStateException {
|
|
if (!this.isFinalized) {
|
|
throw new FinalizedStateException();
|
|
}
|
|
|
|
String newInitialState = "newSTART";
|
|
NFAImpl nfa = new NFAImpl(newInitialState);
|
|
|
|
// simple copy
|
|
nfa.states.addAll(this.states);
|
|
nfa.transitions.addAll(this.transitions);
|
|
nfa.acceptingStates.addAll(this.acceptingStates);
|
|
|
|
nfa.transitions.add(new Transition(newInitialState, null, this.initialState));
|
|
|
|
|
|
for (String acceptingState : this.acceptingStates) {
|
|
nfa.transitions.add(new Transition(acceptingState, null, newInitialState));
|
|
}
|
|
|
|
/*
|
|
// for each accepting state
|
|
for (String acceptingState : this.acceptingStates) {
|
|
Transition loopBackTransition =
|
|
// creating an epsilon transition (null) for each accepting state
|
|
new Transition(acceptingState, null, this.initialState);
|
|
if (!nfa.transitions.contains(loopBackTransition)) {
|
|
nfa.transitions.add(loopBackTransition);
|
|
}
|
|
}
|
|
*/
|
|
|
|
return nfa;
|
|
}
|
|
|
|
// #TODO
|
|
@Override
|
|
public NFA complement() throws FinalizedStateException {
|
|
if (!this.isFinalized) {
|
|
throw new FinalizedStateException();
|
|
}
|
|
|
|
// create a copy of this NFA
|
|
NFAImpl nfa = new NFAImpl(this.initialState);
|
|
nfa.states.addAll(this.states);
|
|
nfa.addAllTransitions(this.transitions);
|
|
nfa.addAllAcceptingStates(this.acceptingStates);
|
|
|
|
NFAImpl fakeNFA = convertNFAtoDFA(nfa);
|
|
|
|
// state -> accepting
|
|
// accepting -> state
|
|
|
|
Set<String> newAcceptingState = new HashSet<>();
|
|
|
|
for (String state : fakeNFA.getStates()) {
|
|
if (!fakeNFA.getAcceptingStates().contains(state)) {
|
|
newAcceptingState.add(state);
|
|
}
|
|
}
|
|
fakeNFA.acceptingStates.clear();
|
|
fakeNFA.addAllAcceptingStates(newAcceptingState);
|
|
|
|
Set<Character> originalAlphabet = new HashSet<>(fakeNFA.alphabet);
|
|
|
|
fakeNFA.alphabet.clear();
|
|
|
|
Set<Character> newAlphabet = new HashSet<>();
|
|
|
|
for (char ch = 'a'; ch <= 'z'; ch++) {
|
|
if (!originalAlphabet.contains(ch)) {
|
|
newAlphabet.add(ch);
|
|
}
|
|
}
|
|
|
|
for (Transition transition : fakeNFA.transitions) {
|
|
for (Character letter : newAlphabet) {
|
|
fakeNFA.addTransition(new Transition(transition.fromState(), letter, transition.toState()));
|
|
}
|
|
}
|
|
|
|
fakeNFA.alphabet = newAlphabet;
|
|
|
|
fakeNFA.finalizeAutomaton();
|
|
|
|
return fakeNFA;
|
|
}
|
|
|
|
@Override
|
|
public boolean isFinalized() {
|
|
return isFinalized;
|
|
}
|
|
|
|
@Override
|
|
public void finalizeAutomaton() {
|
|
this.isFinalized = true;
|
|
}
|
|
|
|
// #TODO
|
|
@Override
|
|
public boolean isFinite() {
|
|
//transitions={fromState==toState}
|
|
//states={A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z}
|
|
//obergrenze = anzahl der states (26)
|
|
return false;
|
|
}
|
|
|
|
// #TODO
|
|
@Override
|
|
public boolean acceptsWord(String word) throws FinalizedStateException {
|
|
if (!this.isFinalized) {
|
|
throw new FinalizedStateException();
|
|
}
|
|
|
|
char[] wordArray = word.toCharArray();
|
|
|
|
Set<String> currentStates = new HashSet<>();
|
|
currentStates.add(this.initialState);
|
|
currentStates = (epsilonClosure(currentStates, this));
|
|
|
|
for (char letter : wordArray) {
|
|
// schritt a
|
|
currentStates = getStateAfterTransition(currentStates, letter, this);
|
|
// schritt b
|
|
currentStates = epsilonClosure(currentStates, this);
|
|
}
|
|
|
|
return isAcceptingState(currentStates, this);
|
|
}
|
|
|
|
public NFAImpl convertNFAtoDFA(NFAImpl input) {
|
|
|
|
// all accepting states of the DFA
|
|
Set<String> dfaAcceptingStates = new HashSet<>();
|
|
// all states of the DFA
|
|
List<Set<String>> dfaStates = new ArrayList<>();
|
|
// all transitions of the DFA
|
|
Set<Transition> dfaTransitions = new HashSet<>();
|
|
|
|
// all states of the NFA
|
|
Set<String> startState = new HashSet<>();
|
|
startState.add(input.getInitialState());
|
|
|
|
// getting the epsilon closure of the start state
|
|
startState = epsilonClosure(startState, input);
|
|
dfaStates.add(startState);
|
|
if (isAcceptingState(startState, input)) {
|
|
dfaAcceptingStates.add(getIndexOfSet(startState, dfaStates));
|
|
}
|
|
|
|
// do the above with each DFA state
|
|
Queue<Set<String>> queue = new LinkedList<>();
|
|
queue.add(startState);
|
|
// iterate each Letter
|
|
while (!queue.isEmpty()) {
|
|
Set<String> currentState = queue.poll();
|
|
// for each possible Letter
|
|
for (char letter : input.getAlphabet()) {
|
|
// get the new state after the transition
|
|
Set<String> newState = epsilonClosure(getStateAfterTransition(currentState, letter, input), input);
|
|
if (!dfaStates.contains(newState)) {
|
|
queue.add(newState);
|
|
dfaStates.add(newState);
|
|
if (isAcceptingState(newState, input)) {
|
|
dfaAcceptingStates.add(getIndexOfSet(newState, dfaStates));
|
|
}
|
|
|
|
}
|
|
|
|
// build new Transition
|
|
dfaTransitions.add(new Transition(getIndexOfSet(currentState, dfaStates), letter, getIndexOfSet(newState, dfaStates)));
|
|
}
|
|
}
|
|
|
|
NFAImpl newDFA = new NFAImpl("0");
|
|
|
|
for (int i = 0; i < dfaStates.size(); i++) {
|
|
newDFA.states.add("" + i);
|
|
}
|
|
|
|
newDFA.addAllTransitions(dfaTransitions);
|
|
newDFA.addAllAcceptingStates(dfaAcceptingStates);
|
|
|
|
return newDFA;
|
|
}
|
|
|
|
private Set<String> epsilonClosure(Set<String> states, NFA nfa) {
|
|
Set<String> closure = new HashSet<>(states);
|
|
Stack<String> stack = new Stack<>();
|
|
stack.addAll(states);
|
|
while (!stack.isEmpty()) {
|
|
String state = stack.pop();
|
|
for (Transition transition : nfa.getTransitions()) {
|
|
if (transition.fromState().equals(state) && transition.readSymbol() == null) {
|
|
if (closure.add(transition.toState())) {
|
|
stack.push(transition.toState());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return closure;
|
|
}
|
|
|
|
private Set<String> getStateAfterTransition(Set<String> states, char symbol, NFA nfa) {
|
|
Set<String> result = new HashSet<>();
|
|
for (String state : states) {
|
|
for (Transition transition : nfa.getTransitions()) {
|
|
if (transition.fromState().equals(state) && transition.readSymbol() != null && transition.readSymbol() == symbol) {
|
|
result.add(transition.toState());
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private boolean isAcceptingState(Set<String> states, NFA input) {
|
|
for (String accState : input.getAcceptingStates()) {
|
|
if (states.contains(accState)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private String getIndexOfSet(Set<String> set, List<Set<String>> dfaStates) {
|
|
return Integer.toString(dfaStates.indexOf(set));
|
|
}
|
|
|
|
private String changeIfNecessary(String str, Set<String> toCheck) {
|
|
while (toCheck.contains(str)) {
|
|
str = str + "1";
|
|
}
|
|
return str;
|
|
}
|
|
|
|
}
|