reglibcpp  2.1.0
A C++ implementation of models for regular languages
fabuilder.cpp
Go to the documentation of this file.
1 // Copyright 2017, 2018 Tom Kranz
2 //
3 // This file is part of reglibcpp.
4 //
5 // reglibcpp is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // reglibcpp is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with reglibcpp. If not, see <https://www.gnu.org/licenses/>.
17 
19 #include "fabuilder.h"
20 
21 #include <forward_list>
22 #include <unordered_map>
23 
24 #include "utils.h"
25 
26 using std::forward_list;
27 using std::string;
28 using std::u32string;
29 using std::unordered_map;
30 using std::unordered_set;
31 using std::valarray;
32 using std::vector;
33 
34 using std::move;
35 
36 namespace reg {
37 
40  string const* initial = nullptr;
48 
54 
56 
57  string const* findStatePointer(string const& state) {
58  return &(transitions.emplace(state, alphabet.size()).first->first);
59  }
60 
62 
63  string const* findStatePointer(string&& state) {
64  return &(transitions.emplace(move(state), alphabet.size()).first->first);
65  }
66 
68  bool isTrashState(string const* q) const {
69  if (acceptingStates.count(q)) {
70  return false;
71  }
72  auto const& from = transitions.at(*q);
73  for (auto const& via : from) {
74  if (via.second.size() > 1 ||
75  (via.second.size() == 1 && !via.second.count(q))) {
76  return false;
77  }
78  }
79  return true;
80  }
81 
83  string const* generateNewState() {
84  size_t q(0);
85  string const* newStateP = nullptr;
86  while (newStateP == nullptr) {
87  auto newStateIns =
88  transitions.emplace("q" + std::to_string(q++), alphabet.size());
89  if (newStateIns.second) {
90  newStateP = &(newStateIns.first->first);
91  }
92  }
93  if (transitions.size() == 1) {
94  initial = newStateP;
95  }
96  return newStateP;
97  }
98 
100  impl() = default;
101 
104  impl(impl const& other)
105  : alphabet(other.alphabet),
106  transitions(other.transitions.size()),
107  acceptingStates(other.acceptingStates.size()) {
108  for (auto const& from : other.transitions) {
109  auto newFrom = transitions.emplace(from.first, from.second.size()).first;
110  auto& newVias = newFrom->second;
111  for (auto const& via : from.second) {
112  auto& newTos =
113  newVias.emplace(via.first, via.second.size()).first->second;
114  for (string const* to : via.second) {
115  newTos.emplace(findStatePointer(*to));
116  }
117  }
118  if (other.acceptingStates.count(&(from.first))) {
119  acceptingStates.emplace(&(newFrom->first));
120  }
121  }
122  initial = findStatePointer(*(other.initial));
123  }
124 
126  impl(nfa const& nfa)
127  : alphabet(nfa.getAlphabet_().begin(), nfa.getAlphabet_().end()),
128  transitions(nfa.getStates().size()),
129  acceptingStates(nfa.getStates().size()) {
130  auto const& alphabetV = nfa.getAlphabet_();
131  auto const& statesV = nfa.getStates();
133  for (size_t qi = 0; qi < statesV.size(); qi++) {
134  auto qIt = transitions.emplace(statesV[qi], alphabetV.size()).first;
135  for (size_t si = 0; si < alphabetV.size(); si++) {
136  auto const& pis = nfa.delta(qi, si);
137  auto sIt = qIt->second.emplace(alphabetV[si], statesV.size()).first;
138  for (size_t pi = 0; pi < pis.size(); pi++) {
139  if (pis[pi]) {
140  sIt->second.emplace(findStatePointer(statesV[pi]));
141  }
142  }
143  }
144  if (nfa.isAccepting(qi)) {
145  acceptingStates.emplace(&(qIt->first));
146  }
147  }
148  }
149 
151  impl(dfa const& dfa)
152  : alphabet(dfa.getAlphabet_().begin(), dfa.getAlphabet_().end()),
153  transitions(dfa.getStates().size()),
154  acceptingStates(dfa.getStates().size()) {
155  auto const& alphabetV = dfa.getAlphabet_();
156  auto const& statesV = dfa.getStates();
158  for (size_t qi = 0; qi < statesV.size(); qi++) {
159  auto qIt = transitions.emplace(statesV[qi], alphabetV.size()).first;
160  for (size_t si = 0; si < alphabetV.size(); si++) {
161  size_t pi = dfa.delta(qi, si);
162  auto sIt = qIt->second.emplace(alphabetV[si], statesV.size()).first;
163  sIt->second.emplace(findStatePointer(statesV[pi]));
164  }
165  if (dfa.isAccepting(qi)) {
166  acceptingStates.emplace(&(qIt->first));
167  }
168  }
169  }
170 };
171 
173 fabuilder::fabuilder() : pim(new impl) {}
174 
177 fabuilder::fabuilder(nfa const& nfa) : pim(new impl(nfa)) {}
178 
181 
185 fabuilder::fabuilder(dfa const& dfa) : pim(new impl(dfa)) {}
186 
189 fabuilder::fabuilder(fabuilder const& b) : pim(new impl(*(b.pim))) {}
190 
193 
194 fabuilder::fabuilder(fabuilder&& b) : pim(new impl) { pim.swap(b.pim); }
195 
196 fabuilder::~fabuilder() = default;
197 
201  if (this != &b) {
202  pim.reset(new impl(*(b.pim)));
203  }
204  return *this;
205 }
206 
209 
211  if (this != &b) {
212  pim.reset(new impl());
213  pim.swap(b.pim);
214  }
215  return *this;
216 }
217 
219 
225 fabuilder& fabuilder::setAccepting(std::string const& state, bool accept) {
226  string const* stateP = pim->findStatePointer(state);
227  if (pim->transitions.size() == 1) {
228  pim->initial = stateP;
229  }
230  if (accept) {
231  pim->acceptingStates.emplace(stateP);
232  } else {
233  pim->acceptingStates.erase(stateP);
234  }
235  return *this;
236 }
237 
239 
244  pim->initial = pim->findStatePointer(state);
245  return *this;
246 }
247 
249 
254  return addSymbol_(converter.from_bytes(symbol)[0]);
255 }
256 
258 
269  std::string const& to,
270  std::string const& symbol) {
271  return addTransition_(from, to, converter.from_bytes(symbol)[0]);
272 }
273 
275 
279 fabuilder& fabuilder::addSymbol_(char32_t u32symbol) {
280  pim->alphabet.emplace(u32symbol);
281  return *this;
282 }
283 
285 
296  std::string const& to,
297  char32_t u32symbol) {
298  auto fromIt = pim->transitions.emplace(from, pim->alphabet.size()).first;
299  if (pim->transitions.size() == 1) {
300  pim->initial = &(fromIt->first);
301  }
302  string const* toP = pim->findStatePointer(to);
303  auto u32symbolIt = fromIt->second.emplace(u32symbol, 1).first;
304  u32symbolIt->second.emplace(toP);
305  addSymbol_(u32symbol);
306  return *this;
307 }
308 
311 
317  nfa nfa = buildNfa();
318  pim->transitions.clear();
319  pim->acceptingStates.clear();
320  pim->alphabet.erase(U'\0');
323  size_t stackSize(1);
324  pim->initial = pim->findStatePointer(nfa.to_string(stack.front()));
325  while (stackSize) {
326  valarray<bool> current(move(stack.front()));
327  stackSize--, stack.pop_front();
328  auto currentIt =
329  pim->transitions.emplace(nfa.to_string(current), pim->alphabet.size())
330  .first;
331  for (char32_t symbol : pim->alphabet) {
332  valarray<bool> next(nfa.deltaHat_(current, u32string(1, symbol)));
333  currentIt->second[symbol].emplace(
334  pim->findStatePointer(nfa.to_string(next)));
335  auto equalToNext = [&](valarray<bool> const& ref) -> bool {
336  return (next == ref).min() == true;
337  };
338  if (!equalToNext(current) &&
339  none_of(stack.begin(), stack.end(), equalToNext) &&
340  none_of(done.begin(), done.end(), equalToNext)) {
341  stack.push_front(next);
342  stackSize++;
343  }
344  }
345  if (nfa.isAccepting(current)) {
346  pim->acceptingStates.emplace(&(currentIt->first));
347  }
348  done.insert(current);
349  }
350  return *this;
351 }
352 
355 
364  bool trashUsed(false);
365  string const* trashState = [&] {
366  for (auto const& entry : pim->transitions) {
367  if (pim->isTrashState(&entry.first)) {
368  trashUsed = true;
369  return &entry.first;
370  }
371  }
372  return pim->generateNewState();
373  }();
374  for (auto& from : pim->transitions) {
375  for (char32_t symbol : pim->alphabet) {
376  auto viaIt = from.second.emplace(symbol, 1).first;
377  if (symbol == U'\0') {
378  if (viaIt->second.empty()) {
379  from.second.erase(viaIt);
380  }
381  } else {
382  if (viaIt->second.empty()) {
383  viaIt->second.emplace(trashState);
384  trashUsed |= (&from.first != trashState);
385  }
386  }
387  }
388  }
389  if (!trashUsed) {
390  pim->transitions.erase(*trashState);
391  }
392  pim->alphabet.erase(U'\0');
393  return *this;
394 }
395 
399 
422  if (!hasNondeterminism()) {
423  complete();
424  } else {
425  purge();
426  if (!hasNondeterminism()) {
427  complete();
428  } else if (force) {
429  powerset();
430  } else {
431  throw nondeterminism_exception();
432  }
433  }
434  vector<vector<size_t>> tTable(pim->transitions.size());
435  valarray<bool> accepting(false, pim->transitions.size());
436  vector<char32_t> sortedAlphabet(pim->alphabet.begin(), pim->alphabet.end());
437  std::sort(sortedAlphabet.begin(), sortedAlphabet.end());
438  vector<string const*> orderedStates;
439  orderedStates.reserve(pim->transitions.size());
440  orderedStates.push_back(pim->initial);
441  for (auto const& entry : pim->transitions) {
442  if (&entry.first != pim->initial) {
443  orderedStates.push_back(&entry.first);
444  }
445  }
446  for (size_t qi(0); qi < orderedStates.size(); qi++) {
447  string const* q(orderedStates[qi]);
448  accepting[qi] = pim->acceptingStates.count(q);
449  for (size_t si(0); si < sortedAlphabet.size(); si++) {
450  tTable[qi].push_back(index_of(
451  orderedStates, *(pim->transitions[*q][sortedAlphabet[si]].begin())));
452  }
453  }
454  auto equivalences = dfa::indistinguishableStates(tTable, accepting);
455  for (size_t q(0); q < orderedStates.size(); q++) {
456  for (size_t first(0); first < equivalences[q].size(); first++) {
457  if (equivalences[q][first] && q > first) {
458  string const* superfluous(orderedStates[q]);
459  string const* remaining(orderedStates[first]);
460  pim->acceptingStates.erase(superfluous);
461  pim->transitions.erase(*superfluous);
462  for (auto& from : pim->transitions) {
463  for (auto& via : from.second) {
464  auto supIt = via.second.find(superfluous);
465  if (supIt != via.second.end()) {
466  via.second.erase(supIt);
467  via.second.insert(remaining);
468  }
469  }
470  }
471  break;
472  }
473  }
474  }
475  return *this;
476 }
477 
480 
490  unordered_set<string const*> reachable(&(pim->initial), &(pim->initial) + 1);
491  unordered_set<string const*> newReachable(reachable);
492  while (newReachable.size() > 0) {
493  unordered_set<string const*> oldReachable(move(newReachable));
494  newReachable.clear();
495  for (string const* reachableState : oldReachable) {
496  for (auto const& via : pim->transitions[*reachableState]) {
497  for (auto reachedState : via.second) {
498  if (reachable.insert(reachedState).second) {
499  newReachable.insert(reachedState);
500  }
501  }
502  }
503  }
504  }
505  for (auto it = pim->transitions.begin(); it != pim->transitions.end();) {
506  if (!reachable.count(&(it->first))) {
507  pim->acceptingStates.erase(&(it->first));
508  it = pim->transitions.erase(it);
509  } else {
510  it++;
511  }
512  }
513  unordered_set<string const*> producing(pim->acceptingStates);
514  unordered_set<string const*> newProducing(producing);
515  while (newProducing.size() > 0) {
516  unordered_set<string const*> oldProducing(move(newProducing));
517  newProducing.clear();
518  for (auto const& from : pim->transitions) {
519  for (auto const& via : from.second) {
520  for (auto target : via.second) {
521  if (producing.count(target)) {
522  if (producing.insert(&(from.first)).second) {
523  newProducing.insert(&(from.first));
524  }
525  break;
526  }
527  }
528  }
529  }
530  }
531  for (auto it = pim->transitions.begin(); it != pim->transitions.end();) {
532  if (!producing.count(&(it->first)) && &(it->first) != pim->initial) {
533  pim->acceptingStates.erase(&(it->first));
534  for (auto& from : pim->transitions) {
535  for (auto& via : from.second) {
536  via.second.erase(&(it->first));
537  }
538  }
539  it = pim->transitions.erase(it);
540  } else {
541  it++;
542  }
543  }
544  return *this;
545 }
546 
549 
559 fabuilder& fabuilder::minimize(bool force) { return merge(force).purge(); }
560 
562 
572  if (!pim->transitions.empty()) {
573  auto const& oAlph = other.getAlphabet_();
574  for (char32_t symbol : oAlph) {
575  addSymbol_(symbol);
576  }
577  size_t trash =
578  other.getStates().size() * !!(pim->alphabet.size() - oAlph.size());
579  complete();
580  vector<string const*> stateNames;
581  stateNames.reserve(pim->transitions.size());
582  stateNames.push_back(pim->initial);
583  for (auto const& fromVia : pim->transitions) {
584  if (&(fromVia.first) != pim->initial) {
585  stateNames.push_back(&(fromVia.first));
586  }
587  }
589  newTr(stateNames.size() * (other.getStates().size() + !!trash));
590  unordered_set<string const*> newAcc(stateNames.size() *
591  (other.getStates().size() + !!trash));
593  stateNames.size());
594  for (size_t q = 0; q < stateNames.size(); q++) {
595  for (size_t qq = 0; qq < other.getStates().size() + !!trash; qq++) {
596  string potentialName = *stateNames[q];
597  if (qq < other.getStates().size()) {
598  potentialName.append(other.getStates()[qq]);
599  }
600  while (newTr.count(potentialName)) {
601  potentialName.append("_");
602  }
603  auto nameIt =
604  newTr.emplace(move(potentialName), pim->alphabet.size()).first;
605  pairToName.emplace(q, 1).first->second.emplace(qq, &(nameIt->first));
606  if (pim->acceptingStates.count(stateNames[q]) ||
607  (qq < other.getStates().size() && other.isAccepting(qq))) {
608  newAcc.insert(&(nameIt->first));
609  }
610  }
611  }
612  valarray<bool> empty(other.getStates().size());
613  for (char32_t symbol : pim->alphabet) {
614  for (size_t q = 0; q < stateNames.size(); q++) {
615  auto const& viaTo = pim->transitions[*stateNames[q]][symbol];
616  for (size_t qq = 0; qq < other.getStates().size() + !!trash; qq++) {
617  for (string const* pLabel : viaTo) {
618  size_t p = index_of(stateNames, pLabel);
619  valarray<bool> const* pps;
620  try {
621  pps = &other.delta_(qq, symbol);
622  } catch (std::logic_error e) {
623  pps = &empty;
624  }
625  if (!pps->max()) {
626  newTr[*(pairToName[q][qq])][symbol].emplace(pairToName[p][trash]);
627  } else {
628  for (size_t pp = 0; pp < other.getStates().size(); pp++) {
629  if ((*pps)[pp]) {
630  newTr[*(pairToName[q][qq])][symbol].emplace(
631  pairToName[p][pp]);
632  }
633  }
634  }
635  }
636  }
637  }
638  }
639  pim->transitions = move(newTr);
640  pim->acceptingStates = move(newAcc);
641  pim->initial = pairToName[0][0];
642  } else {
643  pim->alphabet.insert(other.getAlphabet_().begin(),
644  other.getAlphabet_().end());
645  makeInitial(other.getInitialState());
646  for (size_t q = 0; q < other.getStates().size(); ++q) {
647  for (size_t si = 0; si < other.getAlphabet_().size(); si++) {
648  auto const& reached = other.delta(q, si);
649  for (size_t p = 0; p < other.getStates().size(); p++) {
650  if (reached[q]) {
651  addTransition_(other.getStates()[q], other.getStates()[p],
652  other.getAlphabet_()[si]);
653  }
654  }
655  }
656  setAccepting(other.getStates()[q], other.isAccepting(q));
657  }
658  }
659  return *this;
660 }
661 
663 
673  if (!pim->transitions.empty()) {
674  vector<string const*> stateNames;
675  stateNames.reserve(pim->transitions.size());
676  stateNames.emplace_back(pim->initial);
677  for (auto const& fromTo : pim->transitions) {
678  if (&(fromTo.first) != pim->initial) {
679  stateNames.emplace_back(&(fromTo.first));
680  }
681  }
682  auto const& oAlph = other.getAlphabet_();
683  size_t commonSymbols = 0;
684  for (char32_t symbol : pim->alphabet) {
685  if (index_of(oAlph, symbol) < oAlph.size()) {
686  commonSymbols++;
687  } else {
688  for (auto& fromVia : pim->transitions) {
689  fromVia.second.erase(symbol);
690  }
691  }
692  }
693  pim->alphabet.insert(oAlph.begin(), oAlph.end());
695  newTr(stateNames.size() * other.getStates().size());
696  unordered_set<string const*> newAcc(stateNames.size() *
697  other.getStates().size());
699  stateNames.size() * other.getStates().size());
700  for (size_t q = 0; q < stateNames.size(); q++) {
701  for (size_t qq = 0; qq < other.getStates().size(); qq++) {
702  string potentialName = *stateNames[q] + other.getStates()[qq];
703  while (newTr.count(potentialName)) {
704  potentialName.append("_");
705  }
706  auto nameIt = newTr.emplace(move(potentialName), commonSymbols).first;
707  pairToName.emplace(q, 1).first->second.emplace(qq, &(nameIt->first));
708  if (pim->acceptingStates.count(stateNames[q]) &&
709  other.isAccepting(qq)) {
710  newAcc.emplace(&(nameIt->first));
711  }
712  }
713  pim->transitions[*stateNames[q]][U'\0'].emplace(stateNames[q]);
714  // Needed due to the equivalence of standing still to “taking” an
715  // ε-transition.
716  }
717  for (size_t q = 0; q < stateNames.size(); q++) {
718  auto const& viaTos = pim->transitions[*stateNames[q]];
719  for (auto const& viaTo : viaTos) {
720  for (size_t qq = 0; qq < other.getStates().size(); qq++) {
721  valarray<bool> const& reached = other.delta_(qq, viaTo.first);
722  for (string const* to : viaTo.second) {
723  size_t p = index_of(stateNames, to);
724  for (size_t pp = 0; pp < reached.size(); pp++) {
725  if (reached[pp] || (pp == qq && viaTo.first == U'\0')) {
726  // Needed due to the equivalence of standing still to “taking”
727  // an ε-transition.
728  newTr[*(pairToName[q][qq])][viaTo.first].insert(
729  &(newTr.find(*(pairToName[p][pp]))->first));
730  }
731  }
732  }
733  }
734  }
735  }
736  for (auto& fromVia : newTr) {
737  auto const& from = fromVia.first;
738  auto to = fromVia.second.find(U'\0');
739  to->second.erase(&from);
740  if (to->second.empty()) {
741  fromVia.second.erase(to);
742  }
743  }
744  pim->transitions = move(newTr);
745  pim->acceptingStates = move(newAcc);
746  pim->initial = pim->findStatePointer(*(pairToName[0][0]));
747  }
748  return *this;
749 }
750 
752 
770 fabuilder& fabuilder::subtract(nfa const& other, bool force) {
771  for (char32_t symbol : other.getAlphabet_()) {
772  addSymbol_(symbol);
773  }
774  return this->complement(force).unite(other).complement(force);
775 }
776 
779 
796  if (!hasNondeterminism()) {
797  complete();
798  } else {
799  purge();
800  if (!hasNondeterminism()) {
801  complete();
802  } else if (force) {
803  powerset();
804  } else {
805  throw nondeterminism_exception();
806  }
807  }
808  for (auto const& fromVia : pim->transitions) {
809  if (!pim->acceptingStates.erase(&(fromVia.first))) {
810  pim->acceptingStates.insert(&(fromVia.first));
811  }
812  }
813  return *this;
814 }
815 
818 
823  if (!pim->transitions.empty()) {
824  vector<string const*> stateNames;
825  stateNames.reserve(pim->transitions.size());
826  stateNames.push_back(pim->initial);
827  for (auto const& fromTo : pim->transitions) {
828  if (&(fromTo.first) != pim->initial) {
829  stateNames.push_back(&(fromTo.first));
830  }
831  }
833  newTr(pim->transitions.size());
834  unordered_set<string const*> newAcc(pim->acceptingStates.size());
835  string const* newInitial;
836  for (size_t q = 0; q < stateNames.size(); q++) {
837  auto const& vias = pim->transitions[*stateNames[q]];
838  auto newIt = newTr.emplace(prefix + std::to_string(q), vias.size()).first;
839  string const* newQ = &(newIt->first);
840  auto& newVias = newIt->second;
841  for (auto const& viaTo : vias) {
842  auto& newTos =
843  newVias.emplace(viaTo.first, viaTo.second.size()).first->second;
844  for (size_t p = 0; p < stateNames.size(); p++) {
845  if (viaTo.second.count(stateNames[p])) {
846  newTos.emplace(
847  &(newTr
848  .emplace(
849  prefix + std::to_string(p),
850  pim->transitions[*stateNames[p]][viaTo.first].size())
851  .first->first));
852  }
853  }
854  }
855  if (pim->acceptingStates.count(stateNames[q])) {
856  newAcc.insert(newQ);
857  }
858  if (pim->initial == stateNames[q]) {
859  newInitial = newQ;
860  }
861  }
862  pim->initial = newInitial;
863  pim->transitions = move(newTr);
864  pim->acceptingStates = move(newAcc);
865  }
866  return *this;
867 }
868 
872  for (auto const& from : pim->transitions) {
873  for (char32_t symbol : pim->alphabet) {
874  if (symbol == U'\0') {
875  continue;
876  }
877  auto viaIt = from.second.find(symbol);
878  if (viaIt == from.second.end() || viaIt->second.empty()) {
879  return false;
880  }
881  }
882  }
883  return true;
884 }
885 
888  for (auto const& from : pim->transitions) {
889  for (auto const& via : from.second) {
890  if ((via.first == U'\0' && !via.second.empty()) ||
891  via.second.size() > 1) {
892  return true;
893  }
894  }
895  }
896  return false;
897 }
898 
900 
905  if (pim->initial == nullptr) {
906  pim->initial = &(pim->transitions.emplace("", 0).first->first);
907  }
908  vector<char32_t> alphabetV(pim->alphabet.begin(), pim->alphabet.end());
909  if (!pim->alphabet.count(U'\0')) {
910  alphabetV.emplace_back(U'\0');
911  }
912  std::sort(alphabetV.begin(), alphabetV.end());
913  vector<string> labelV;
914  labelV.reserve(pim->transitions.size());
915  labelV.emplace_back(*pim->initial);
916  valarray<bool> acceptingV(pim->transitions.size());
917  acceptingV[0] = pim->acceptingStates.count(pim->initial);
918  for (auto const& entry : pim->transitions) {
919  if (&(entry.first) == pim->initial) {
920  continue;
921  }
922  acceptingV[labelV.size()] = pim->acceptingStates.count(&(entry.first));
923  labelV.emplace_back(entry.first);
924  }
925  vector<vector<valarray<bool>>> transitionV(
926  labelV.size(),
927  vector<valarray<bool>>(alphabetV.size(), valarray<bool>(labelV.size())));
928  for (size_t qi(0); qi < labelV.size(); qi++) {
929  auto const& fromQ = pim->transitions[labelV[qi]];
930  for (size_t si(0); si < alphabetV.size(); si++) {
931  auto viaIt = fromQ.find(alphabetV[si]);
932  if (viaIt == fromQ.end()) {
933  continue;
934  }
935  unordered_set<string const*> const& viaSymbol = viaIt->second;
936  for (size_t pi(0); pi < labelV.size(); pi++) {
937  transitionV[qi][si][pi] =
938  viaSymbol.count(pim->findStatePointer(labelV[pi]));
939  }
940  }
941  }
942  return {move(alphabetV), move(transitionV), move(labelV), move(acceptingV)};
943 }
944 
947 
966  if (!hasNondeterminism()) {
967  complete();
968  } else {
969  purge();
970  if (!hasNondeterminism()) {
971  complete();
972  } else if (force) {
973  powerset();
974  } else {
975  throw nondeterminism_exception();
976  }
977  }
978  if (pim->initial == nullptr) {
979  pim->initial = &(pim->transitions.emplace("", 0).first->first);
980  }
981  vector<char32_t> alphabetV(pim->alphabet.begin(), pim->alphabet.end());
982  std::sort(alphabetV.begin(), alphabetV.end());
983  vector<string> labelV;
984  labelV.reserve(pim->transitions.size());
985  labelV.emplace_back(*pim->initial);
986  valarray<bool> acceptingV(pim->transitions.size());
987  acceptingV[0] = pim->acceptingStates.count(pim->initial);
988  for (auto const& entry : pim->transitions) {
989  if (&(entry.first) == pim->initial) {
990  continue;
991  }
992  acceptingV[labelV.size()] = pim->acceptingStates.count(&(entry.first));
993  labelV.emplace_back(entry.first);
994  }
995  vector<vector<size_t>> transitionV(labelV.size(),
996  vector<size_t>(alphabetV.size()));
997  for (size_t qi(0); qi < labelV.size(); qi++) {
998  auto const& fromQ = pim->transitions[labelV[qi]];
999  for (size_t si(0); si < alphabetV.size(); si++) {
1000  auto viaIt = fromQ.find(alphabetV[si]);
1001  if (viaIt == fromQ.end() || viaIt->second.empty()) {
1002  continue;
1003  }
1004  unordered_set<string const*> const& viaSymbol = viaIt->second;
1005  transitionV[qi][si] = index_of(labelV, **viaSymbol.begin());
1006  }
1007  }
1008  return {move(alphabetV), move(transitionV), move(labelV), move(acceptingV)};
1009 }
1010 
1011 } // namespace reg
std::string const & getInitialState() const
Names this DFA's initial state.
Definition: dfa.cpp:391
impl(impl const &other)
Copies a private implementation object by copying all non-pointer data verbatim and reconstructing po...
Definition: fabuilder.cpp:104
fabuilder & operator=(fabuilder const &b)
Copy-assigns a builder by copying another one's private implementation object.
Definition: fabuilder.cpp:200
fabuilder & addSymbol(std::string const &symbol)
Adds a symbol to the prospective FA's alphabet.
Definition: fabuilder.cpp:253
bool isAccepting(size_t qi) const
Tests whether a state is an accept state within this DFA.
Definition: dfa.cpp:420
unordered_set< char32_t > alphabet
Set of symbols processable by the prospective FA.
Definition: fabuilder.cpp:42
Represents nondeterministic finite automata with ε-moves.
Definition: nfa.h:38
unordered_map< string, unordered_map< char32_t, unordered_set< string const * > > > transitions
Transition function (state × symbol → set of states) of the prospective NFA.
Definition: fabuilder.cpp:45
impl(nfa const &nfa)
Constructs a private implementation object for fabuilder(nfa const&).
Definition: fabuilder.cpp:126
Represents deterministic finite automata.
Definition: dfa.h:43
fabuilder & subtract(nfa const &other, bool force=false)
Makes the prospective FA reject all words accepted by another FA.
Definition: fabuilder.cpp:770
bool isComplete()
Reports whether the prospective FA has at least one transition defined for every state-symbol combina...
Definition: fabuilder.cpp:871
fabuilder & intersect(nfa const &other)
Makes the prospective FA accept only words accepted also by another FA.
Definition: fabuilder.cpp:672
std::vector< char32_t > const & getAlphabet_() const
Fetches this NFA's set of processable symbols.
Definition: nfa.cpp:598
std::string to_string(std::valarray< bool > const &qs) const
Puts a name to a set of indices.
Definition: nfa.cpp:506
fabuilder & addTransition(std::string const &from, std::string const &to, std::string const &symbol="")
Adds a transition to the prospective FA's state transition function.
Definition: fabuilder.cpp:268
bool hasNondeterminism()
Checks whether any nondeterministic transitions have been defined.
Definition: fabuilder.cpp:887
size_t delta(size_t qi, size_t si) const
Computes this DFA's transition function for a state index and a symbol index.
Definition: dfa.cpp:215
fabuilder & complete()
Totalizes a partial transition function by pointing any undefined transitions towards a trash state...
Definition: fabuilder.cpp:363
Contains utility classes, objects and functions used throughout the library.
impl(dfa const &dfa)
Constructs a private implementation object for fabuilder(dfa const&).
Definition: fabuilder.cpp:151
fabuilder & purge()
Purges the prospective FA of unreachable and non-producing states, possibly completing minimization...
Definition: fabuilder.cpp:489
size_t index_of(C const &container, T const &element)
Basically Java&#39;s List interface&#39;s indexOf, but as a non-member function and returning the container&#39;s...
Definition: utils.h:43
fabuilder & minimize(bool force=false)
Convenience method for chaining purge and merge to achieve proper DFA minimization.
Definition: fabuilder.cpp:559
impl()=default
Constructs empty private implementation object.
dfa buildDfa(bool force=false)
Builds a DFA as defined by previous operations, including completion.
Definition: fabuilder.cpp:965
bool isTrashState(string const *q) const
Tests whether all of a state's outgoing transitions point to itself.
Definition: fabuilder.cpp:68
bool isAccepting(size_t qi) const
Tests whether a state is an accept state within this NFA.
Definition: nfa.cpp:613
string const * initial
Points to the initial state within transitions.
Definition: fabuilder.cpp:40
std::vector< std::string > const & getStates() const
Fetches this DFA's set of states.
Definition: dfa.cpp:398
string const * generateNewState()
Generates a uniquely named new state and adds it to the set of states.
Definition: fabuilder.cpp:83
Private implementation details of FA builders.
Definition: fabuilder.cpp:39
std::wstring_convert< std::codecvt_utf8< char32_t >, char32_t > converter
Converts between UTF-8-encoded and UTF-32-encoded strings.
Definition: utils.h:52
fabuilder & addSymbol_(char32_t u32symbol)
Adds a symbol to the prospective FA's alphabet.
Definition: fabuilder.cpp:279
fabuilder & unite(nfa const &other)
Makes the prospective FA also accept every word of another FA's language.
Definition: fabuilder.cpp:571
fabuilder & normalizeStateNames(std::string const &prefix)
Reduces the prospective FA's state names to consecutive numbers, prefixed with a given string...
Definition: fabuilder.cpp:822
std::valarray< bool > const & delta_(size_t qi, char32_t u32symbol) const
Computes this NFA's transition function for a state index and a UTF-32-encoded symbol.
Definition: nfa.cpp:170
fabuilder & setAccepting(std::string const &state, bool accept)
Sets whether or not a state will be accepting within the prospective FA.
Definition: fabuilder.cpp:225
unordered_set< string const * > acceptingStates
Set of pointers to accept states within transitions.
Definition: fabuilder.cpp:52
Signals that an operation requires full determinism and that no powerset construction was forced...
Definition: fabuilder.h:67
fabuilder()
Constructs a blank builder object.
Definition: fabuilder.cpp:173
Constructs finite automata step by step.
Definition: fabuilder.h:31
fabuilder & powerset()
Converts the builder's prospective NFA into a DFA via powerset construction.
Definition: fabuilder.cpp:316
static std::vector< std::valarray< bool > > indistinguishableStates(std::vector< std::vector< size_t >> const &transitions, std::valarray< bool > const &accepting)
Builds the table of indistinguishable states w.r.t. a transition function.
Definition: dfa.cpp:558
fabuilder & merge(bool force=false)
Merges the prospective DFA's indistinguishable states, allowing for minimization. ...
Definition: fabuilder.cpp:421
Where this library lives.
Definition: dfa.cpp:48
fabuilder & makeInitial(std::string const &state)
Resets the initial state for the prospective FA.
Definition: fabuilder.cpp:243
std::string const & getInitialState() const
Names this NFA's initial state.
Definition: nfa.cpp:567
string const * findStatePointer(string &&state)
Delivers a pointer towards one of transitions' keys.
Definition: fabuilder.cpp:63
std::valarray< bool > const & delta(size_t qi, size_t si) const
Computes this NFA's transition function for a state index and a symbol index.
Definition: nfa.cpp:146
std::valarray< bool > const & epsilonClosure(size_t qi) const
Computes a state's ε-closure.
Definition: nfa.cpp:418
string const * findStatePointer(string const &state)
Delivers a pointer towards one of transitions' keys.
Definition: fabuilder.cpp:57
std::vector< std::string > const & getStates() const
Fetches this NFA's set of states in order of internal representation.
Definition: nfa.cpp:574
std::valarray< bool > deltaHat_(size_t qi, std::u32string const &u32word) const
Computes this NFA's transition function recursively for a string of symbols, starting in a state spec...
Definition: nfa.cpp:250
fabuilder & addTransition_(std::string const &from, std::string const &to, char32_t u32symbol=U'\0')
Adds a transition to the prospective FA's state transition function.
Definition: fabuilder.cpp:295
nfa buildNfa()
Builds an NFA as defined by previous operations.
Definition: fabuilder.cpp:904
std::vector< char32_t > const & getAlphabet_() const
Fetches this DFA's set of processable symbols.
Definition: dfa.cpp:405
Contains the reg::fabuilder class definition.
fabuilder & complement(bool force=false)
Inverts the prospective DFA's language with respect to all possible strings over its alphabet...
Definition: fabuilder.cpp:795