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 states; private Set transitions; private String initialState; private Set acceptingStates; private Set 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 getStates() { return this.states; } public void safeAddStates(Set states, Set toCheck, NFAImpl nfa) { for (String state : states) { nfa.states.add(changeIfNecessary(state, toCheck)); } } @Override public Collection getTransitions() { return this.transitions; } @Override public Set getAcceptingStates() { return this.acceptingStates; } @Override public String getInitialState() { return this.initialState; } public Set 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 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 states) throws FinalizedStateException { if (this.isFinalized) { throw new FinalizedStateException(); } for (String state : states) { this.addAcceptingState(state); } } public void safeAddAcceptingStates(Set states, Set 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 otherAcceptingStates = new HashSet<>(other.getAcceptingStates()); unionNFA.safeAddAcceptingStates(this.acceptingStates, otherAcceptingStates, unionNFA); Set 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 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 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 newAcceptingState = new HashSet<>(); for (String state : fakeNFA.getStates()) { if (!fakeNFA.getAcceptingStates().contains(state)) { newAcceptingState.add(state); } } fakeNFA.acceptingStates.clear(); fakeNFA.addAllAcceptingStates(newAcceptingState); Set originalAlphabet = new HashSet<>(fakeNFA.alphabet); fakeNFA.alphabet.clear(); Set 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 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 dfaAcceptingStates = new HashSet<>(); // all states of the DFA List> dfaStates = new ArrayList<>(); // all transitions of the DFA Set dfaTransitions = new HashSet<>(); // all states of the NFA Set 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> queue = new LinkedList<>(); queue.add(startState); // iterate each Letter while (!queue.isEmpty()) { Set currentState = queue.poll(); // for each possible Letter for (char letter : input.getAlphabet()) { // get the new state after the transition Set 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 epsilonClosure(Set states, NFA nfa) { Set closure = new HashSet<>(states); Stack 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 getStateAfterTransition(Set states, char symbol, NFA nfa) { Set 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 states, NFA input) { for (String accState : input.getAcceptingStates()) { if (states.contains(accState)) { return true; } } return false; } private String getIndexOfSet(Set set, List> dfaStates) { return Integer.toString(dfaStates.indexOf(set)); } private String changeIfNecessary(String str, Set toCheck) { while (toCheck.contains(str)) { str = str + "1"; } return str; } }