001/* Copyright (C) 2013 TU Dortmund
002 * This file is part of AutomataLib, http://www.automatalib.net/.
003 * 
004 * AutomataLib is free software; you can redistribute it and/or
005 * modify it under the terms of the GNU Lesser General Public
006 * License version 3.0 as published by the Free Software Foundation.
007 * 
008 * AutomataLib is distributed in the hope that it will be useful,
009 * but WITHOUT ANY WARRANTY; without even the implied warranty of
010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
011 * Lesser General Public License for more details.
012 * 
013 * You should have received a copy of the GNU Lesser General Public
014 * License along with AutomataLib; if not, see
015 * http://www.gnu.de/documents/lgpl.en.html.
016 */
017package net.automatalib.incremental.dfa;
018
019import java.util.ArrayDeque;
020import java.util.Deque;
021
022import net.automatalib.incremental.ConflictException;
023import net.automatalib.words.Alphabet;
024import net.automatalib.words.Word;
025
026public class IncrementalPCDFABuilder<I> extends AbstractIncrementalDFABuilder<I> {
027        
028        public IncrementalPCDFABuilder(Alphabet<I> inputAlphabet) {
029                super(inputAlphabet);
030        }
031        
032        /*
033         * (non-Javadoc)
034         * @see net.automatalib.incremental.dfa.AbstractIncrementalDFABuilder#getState(net.automatalib.words.Word)
035         */
036        @Override
037        protected State getState(Word<I> word) {
038                State s = init;
039                
040                for(I sym : word) {
041                        int idx = inputAlphabet.getSymbolIndex(sym);
042                        s = s.getSuccessor(idx);
043                        if(s == null || s == sink) {
044                                return s;
045                        }
046                }
047                return s;
048        }
049        
050        /*
051         * (non-Javadoc)
052         * @see net.automatalib.incremental.dfa.AbstractIncrementalDFABuilder#lookup(net.automatalib.words.Word)
053         */
054        @Override
055        public Acceptance lookup(Word<I> word) {
056                State s = getState(word);
057                if(s == null)
058                        return Acceptance.DONT_KNOW;
059                return (s != sink) ? s.getAcceptance() : Acceptance.FALSE;
060        }
061        
062        /*
063         * (non-Javadoc)
064         * @see net.automatalib.incremental.dfa.AbstractIncrementalDFABuilder#insert(net.automatalib.words.Word, boolean)
065         */
066        @Override
067        public void insert(Word<I> word, boolean accepting) {
068                int len = word.length();
069                Acceptance acc = Acceptance.fromBoolean(accepting);
070                
071                State curr = init;
072                State conf = null;
073                
074                Deque<PathElem> path = new ArrayDeque<>();
075                
076                for(I sym : word) {
077                        if(curr == sink || curr.getAcceptance() == Acceptance.FALSE) {
078                                if(accepting) {
079                                        throw new IllegalArgumentException("Conflict");
080                                }
081                                if(curr != sink) {
082                                        purge(curr);
083                                }
084                                return;
085                        }
086                        
087                        if(conf == null && curr.isConfluence()) {
088                                conf = curr;
089                        }
090                        
091                        int idx = inputAlphabet.getSymbolIndex(sym);
092                        
093                        State succ = curr.getSuccessor(idx);
094                        if(succ == null) {
095                                break;
096                        }
097                        path.push(new PathElem(curr, idx));
098                        curr = succ;
099                }
100                
101                int prefixLen = path.size();
102                
103
104                State last = curr;
105                
106                if(prefixLen == len) {
107                        Acceptance currAcc = curr.getAcceptance();
108                        if(currAcc == acc) {
109                                return;
110                        }
111                        if(currAcc != Acceptance.DONT_KNOW) {
112                                throw new ConflictException("Incompatible acceptances: " + currAcc + " vs " + acc);
113                        }
114                        if(!accepting) {
115                                purge(last);
116                                last = sink;
117                        }
118                        else {
119                                if(conf != null || last.isConfluence()) {
120                                        last = clone(last, Acceptance.TRUE);
121                                }
122                                else if(last == init) {
123                                        updateInitSignature(Acceptance.TRUE);
124                                        return;
125                                }
126                                else {
127                                        last = updateSignature(last, acc);
128                                }
129                        }
130                }
131                else {
132                        if(conf != null) {
133                                if(conf == last) {
134                                        conf = null;
135                                }
136                                last = hiddenClone(last);
137                        }
138                        else if(last != init) {
139                                hide(last);
140                        }
141                        
142                        Word<I> suffix = word.subWord(prefixLen);
143                        I sym = suffix.firstSymbol();
144                        int suffTransIdx = inputAlphabet.getSymbolIndex(sym);
145                        State suffixState = createSuffix(suffix.subWord(1), accepting);
146                        
147                        if(last != init) {
148                                if(accepting) {
149                                        last = unhide(last, Acceptance.TRUE, suffTransIdx, suffixState);
150                                }
151                                else {
152                                        last = unhide(last, suffTransIdx, suffixState);
153                                }
154                        }
155                        else {
156                                if(accepting) {
157                                        updateInitSignature(Acceptance.TRUE, suffTransIdx, suffixState);
158                                }
159                                else {
160                                        updateInitSignature(suffTransIdx, suffixState);
161                                }
162                        }
163                }
164                
165                if(path.isEmpty()) {
166                        return;
167                }
168                        
169                if(conf != null) {
170                        PathElem next;
171                        do {
172                                next = path.pop();
173                                State state = next.state;
174                                int idx = next.transIdx;
175                                if(accepting) {
176                                        state = clone(state, Acceptance.TRUE, idx, last);
177                                }
178                                else {
179                                        state = clone(state, idx, last);
180                                }
181                                last = state;
182                        } while(next.state != conf);
183                }
184                
185                
186                
187                while(path.size() > 1) {
188                        PathElem next = path.pop();
189                        State state = next.state;
190                        int idx = next.transIdx;
191                        State updated;
192                        Acceptance oldAcc = state.getAcceptance();
193                        if(accepting) {
194                                updated = updateSignature(state, Acceptance.TRUE, idx, last);
195                        }
196                        else {
197                                updated = updateSignature(state, idx, last);
198                        }
199                        if(state == updated && oldAcc == updated.getAcceptance())
200                                return;
201                        last = updated;
202                }
203                
204                int finalIdx = path.pop().transIdx;
205                
206                if(accepting) {
207                        updateInitSignature(Acceptance.TRUE, finalIdx, last);
208                }
209                else {
210                        updateInitSignature(finalIdx, last);
211                }
212        }
213                
214        /**
215         * Removes a state and all of its successors from the register.
216         * @param state the state to purge
217         */
218        private void purge(State state) {
219                StateSignature sig = state.getSignature();
220                if(state.getAcceptance() == Acceptance.TRUE) {
221                        throw new IllegalStateException("Attempting to purge accepting state");
222                }
223                if(register.remove(sig) == null) {
224                        return;
225                }
226                sig.acceptance = Acceptance.FALSE;
227                for(int i = 0; i < alphabetSize; i++) {
228                        State succ = sig.successors[i];
229                        if(succ != null) {
230                                purge(succ);
231                        }
232                }
233        }
234        
235        /**
236         * Creates a suffix state sequence, i.e., a linear sequence of states connected by transitions
237         * labeled by the letters of the given suffix word.
238         * @param suffix the suffix word
239         * @param acc whether or not the final state should be accepting
240         * @return the first state in the sequence
241         */
242        private State createSuffix(Word<I> suffix, boolean accepting) {
243                State last;
244                Acceptance intermediate;
245                if(!accepting) {
246                        if(sink == null)
247                                sink = new State(null);
248                        last = sink;
249                        intermediate = Acceptance.DONT_KNOW;
250                }
251                else {
252                        StateSignature sig = new StateSignature(alphabetSize, Acceptance.TRUE);
253                        last = replaceOrRegister(sig);
254                        intermediate = Acceptance.TRUE;
255                }
256                
257                int len = suffix.length();
258                for(int i = len - 1; i >= 0; i--) {
259                        StateSignature sig = new StateSignature(alphabetSize, intermediate);
260                        I sym = suffix.getSymbol(i);
261                        int idx = inputAlphabet.getSymbolIndex(sym);
262                        sig.successors[idx] = last;
263                        last = replaceOrRegister(sig);
264                }
265                
266                return last;
267        }
268
269}