21 #include <forward_list> 22 #include <unordered_map> 70 for (
auto const& via : from.second) {
71 if ((via.first == U
'\0' && !via.second.empty()) ||
72 via.second.size() > 1) {
86 for (
auto const& via : from) {
87 if (via.second.size() > 1 ||
88 (via.second.size() == 1 && !via.second.count(q))) {
98 string const* newStateP =
nullptr;
99 while (newStateP ==
nullptr) {
102 if (newStateIns.second) {
103 newStateP = &(newStateIns.first->first);
122 auto newFrom =
transitions.emplace(from.first, from.second.size()).first;
123 auto& newVias = newFrom->second;
124 for (
auto const& via : from.second) {
126 newVias.emplace(via.first, via.second.size()).first->second;
127 for (
string const* to : via.second) {
146 for (
size_t qi = 0; qi < statesV.size(); qi++) {
147 auto qIt =
transitions.emplace(statesV[qi], alphabetV.size()).first;
148 for (
size_t si = 0; si < alphabetV.size(); si++) {
149 auto const& pis =
nfa.
delta(qi, si);
150 auto sIt = qIt->second.emplace(alphabetV[si], statesV.size()).first;
151 for (
size_t pi = 0; pi < pis.size(); pi++) {
171 for (
size_t qi = 0; qi < statesV.size(); qi++) {
172 auto qIt =
transitions.emplace(statesV[qi], alphabetV.size()).first;
173 for (
size_t si = 0; si < alphabetV.size(); si++) {
175 auto sIt = qIt->second.emplace(alphabetV[si], statesV.size()).first;
209 fabuilder::~fabuilder() =
default;
215 pim.reset(
new impl(*(b.pim)));
225 pim.reset(
new impl());
239 string const* stateP = pim->findStatePointer(state);
240 if (pim->transitions.size() == 1) {
241 pim->initial = stateP;
244 pim->acceptingStates.emplace(stateP);
246 pim->acceptingStates.erase(stateP);
257 pim->initial = pim->findStatePointer(state);
293 pim->alphabet.emplace(u32symbol);
310 char32_t u32symbol) {
311 auto fromIt = pim->transitions.emplace(from, pim->alphabet.size()).first;
312 if (pim->transitions.size() == 1) {
313 pim->initial = &(fromIt->first);
315 string const* toP = pim->findStatePointer(to);
316 auto u32symbolIt = fromIt->second.emplace(u32symbol, 1).first;
317 u32symbolIt->second.emplace(toP);
331 pim->transitions.clear();
332 pim->acceptingStates.clear();
333 pim->alphabet.erase(U
'\0');
337 pim->initial = pim->findStatePointer(
nfa.
to_string(stack.front()));
340 stackSize--, stack.pop_front();
342 pim->transitions.emplace(
nfa.
to_string(current), pim->alphabet.size())
344 for (char32_t symbol : pim->alphabet) {
346 currentIt->second[symbol].emplace(
349 return (next == ref).min() ==
true;
351 if (!equalToNext(current) &&
352 none_of(stack.begin(), stack.end(), equalToNext) &&
353 none_of(done.begin(), done.end(), equalToNext)) {
354 stack.push_front(next);
359 pim->acceptingStates.emplace(&(currentIt->first));
361 done.insert(current);
377 bool trashUsed(
false);
378 string const* trashState = [&] {
379 for (
auto const& entry : pim->transitions) {
380 if (pim->isTrashState(&entry.first)) {
385 return pim->generateNewState();
387 for (
auto& from : pim->transitions) {
388 for (char32_t symbol : pim->alphabet) {
389 if (symbol == U
'\0') {
390 from.second.erase(symbol);
392 auto& via = from.second[symbol];
394 via.insert(trashState);
395 trashUsed |= (&from.first != trashState);
401 pim->transitions.erase(*trashState);
403 pim->alphabet.erase(U
'\0');
433 if (pim->isDeterministic()) {
437 if (pim->isDeterministic()) {
447 vector<char32_t> sortedAlphabet(pim->alphabet.begin(), pim->alphabet.end());
448 std::sort(sortedAlphabet.begin(), sortedAlphabet.end());
450 orderedStates.reserve(pim->transitions.size());
451 orderedStates.push_back(pim->initial);
452 for (
auto const& entry : pim->transitions) {
453 if (&entry.first != pim->initial) {
454 orderedStates.push_back(&entry.first);
457 for (
size_t qi(0); qi < orderedStates.size(); qi++) {
458 string const* q(orderedStates[qi]);
459 accepting[qi] = pim->acceptingStates.count(q);
460 for (
size_t si(0); si < sortedAlphabet.size(); si++) {
462 orderedStates, *(pim->transitions[*q][sortedAlphabet[si]].begin())));
466 for (
size_t q(0); q < orderedStates.size(); q++) {
467 for (
size_t first(0); first < equivalences[q].size(); first++) {
468 if (equivalences[q][first] && q > first) {
469 string const* superfluous(orderedStates[q]);
470 string const* remaining(orderedStates[first]);
471 pim->acceptingStates.erase(superfluous);
472 pim->transitions.erase(*superfluous);
473 for (
auto& from : pim->transitions) {
474 for (
auto& via : from.second) {
475 auto supIt = via.second.find(superfluous);
476 if (supIt != via.second.end()) {
477 via.second.erase(supIt);
478 via.second.insert(remaining);
503 while (newReachable.size() > 0) {
505 newReachable.clear();
506 for (
string const* reachableState : oldReachable) {
507 for (
auto const& via : pim->transitions[*reachableState]) {
508 for (
auto reachedState : via.second) {
509 if (reachable.insert(reachedState).second) {
510 newReachable.insert(reachedState);
516 for (
auto it = pim->transitions.begin(); it != pim->transitions.end();) {
517 if (!reachable.count(&(it->first))) {
518 pim->acceptingStates.erase(&(it->first));
519 it = pim->transitions.erase(it);
526 while (newProducing.size() > 0) {
528 newProducing.clear();
529 for (
auto const& from : pim->transitions) {
530 for (
auto const& via : from.second) {
531 for (
auto target : via.second) {
532 if (producing.count(target)) {
533 if (producing.insert(&(from.first)).second) {
534 newProducing.insert(&(from.first));
542 for (
auto it = pim->transitions.begin(); it != pim->transitions.end();) {
543 if (!producing.count(&(it->first)) && &(it->first) != pim->initial) {
544 pim->acceptingStates.erase(&(it->first));
545 for (
auto& from : pim->transitions) {
546 for (
auto& via : from.second) {
547 via.second.erase(&(it->first));
550 it = pim->transitions.erase(it);
583 if (!pim->transitions.empty()) {
585 for (char32_t symbol : oAlph) {
589 other.
getStates().size() * !!(pim->alphabet.size() - oAlph.size());
592 stateNames.reserve(pim->transitions.size());
593 stateNames.push_back(pim->initial);
594 for (
auto const& fromVia : pim->transitions) {
595 if (&(fromVia.first) != pim->initial) {
596 stateNames.push_back(&(fromVia.first));
600 newTr(stateNames.size() * (other.
getStates().size() + !!trash));
605 for (
size_t q = 0; q < stateNames.size(); q++) {
606 for (
size_t qq = 0; qq < other.
getStates().size() + !!trash; qq++) {
607 string potentialName = *stateNames[q];
609 potentialName.append(other.
getStates()[qq]);
611 while (newTr.count(potentialName)) {
612 potentialName.append(
"_");
615 newTr.emplace(move(potentialName), pim->alphabet.size()).first;
616 pairToName.emplace(q, 1).first->second.emplace(qq, &(nameIt->first));
617 if (pim->acceptingStates.count(stateNames[q]) ||
619 newAcc.insert(&(nameIt->first));
624 for (char32_t symbol : pim->alphabet) {
625 for (
size_t q = 0; q < stateNames.size(); q++) {
626 auto const& viaTo = pim->transitions[*stateNames[q]][symbol];
627 for (
size_t qq = 0; qq < other.
getStates().size() + !!trash; qq++) {
628 for (
string const* pLabel : viaTo) {
629 size_t p =
index_of(stateNames, pLabel);
632 pps = &other.
delta_(qq, symbol);
637 newTr[*(pairToName[q][qq])][symbol].emplace(pairToName[p][trash]);
639 for (
size_t pp = 0; pp < other.
getStates().size(); pp++) {
641 newTr[*(pairToName[q][qq])][symbol].emplace(
650 pim->transitions = move(newTr);
651 pim->acceptingStates = move(newAcc);
652 pim->initial = pairToName[0][0];
657 for (
size_t q = 0; q < other.
getStates().size(); ++q) {
658 for (
size_t si = 0; si < other.
getAlphabet_().size(); si++) {
659 auto const& reached = other.
delta(q, si);
660 for (
size_t p = 0; p < other.
getStates().size(); p++) {
684 if (!pim->transitions.empty()) {
686 stateNames.reserve(pim->transitions.size());
687 stateNames.emplace_back(pim->initial);
688 for (
auto const& fromTo : pim->transitions) {
689 if (&(fromTo.first) != pim->initial) {
690 stateNames.emplace_back(&(fromTo.first));
694 size_t commonSymbols = 0;
695 for (char32_t symbol : pim->alphabet) {
696 if (
index_of(oAlph, symbol) < oAlph.size()) {
699 for (
auto& fromVia : pim->transitions) {
700 fromVia.second.erase(symbol);
704 pim->alphabet.insert(oAlph.begin(), oAlph.end());
706 newTr(stateNames.size() * other.
getStates().size());
710 stateNames.size() * other.
getStates().size());
711 for (
size_t q = 0; q < stateNames.size(); q++) {
712 for (
size_t qq = 0; qq < other.
getStates().size(); qq++) {
713 string potentialName = *stateNames[q] + other.
getStates()[qq];
714 while (newTr.count(potentialName)) {
715 potentialName.append(
"_");
717 auto nameIt = newTr.emplace(move(potentialName), commonSymbols).first;
718 pairToName.emplace(q, 1).first->second.emplace(qq, &(nameIt->first));
719 if (pim->acceptingStates.count(stateNames[q]) &&
721 newAcc.emplace(&(nameIt->first));
724 pim->transitions[*stateNames[q]][U
'\0'].emplace(stateNames[q]);
728 for (
size_t q = 0; q < stateNames.size(); q++) {
729 auto const& viaTos = pim->transitions[*stateNames[q]];
730 for (
auto const& viaTo : viaTos) {
731 for (
size_t qq = 0; qq < other.
getStates().size(); qq++) {
733 for (
string const* to : viaTo.second) {
734 size_t p =
index_of(stateNames, to);
735 for (
size_t pp = 0; pp < reached.size(); pp++) {
736 if (reached[pp] || (pp == qq && viaTo.first == U
'\0')) {
739 newTr[*(pairToName[q][qq])][viaTo.first].insert(
740 &(newTr.find(*(pairToName[p][pp]))->first));
747 for (
auto& fromVia : newTr) {
748 auto const& from = fromVia.first;
749 auto to = fromVia.second.find(U
'\0');
750 to->second.erase(&from);
751 if (to->second.empty()) {
752 fromVia.second.erase(to);
755 pim->transitions = move(newTr);
756 pim->acceptingStates = move(newAcc);
757 pim->initial = pim->findStatePointer(*(pairToName[0][0]));
781 if (pim->isDeterministic()) {
785 if (pim->isDeterministic()) {
793 for (
auto const& fromVia : pim->transitions) {
794 if (!pim->acceptingStates.erase(&(fromVia.first))) {
795 pim->acceptingStates.insert(&(fromVia.first));
808 if (!pim->transitions.empty()) {
810 stateNames.reserve(pim->transitions.size());
811 stateNames.push_back(pim->initial);
812 for (
auto const& fromTo : pim->transitions) {
813 if (&(fromTo.first) != pim->initial) {
814 stateNames.push_back(&(fromTo.first));
818 newTr(pim->transitions.size());
820 string const* newInitial;
821 for (
size_t q = 0; q < stateNames.size(); q++) {
822 auto const& vias = pim->transitions[*stateNames[q]];
823 auto newIt = newTr.emplace(prefix + std::to_string(q), vias.size()).first;
824 string const* newQ = &(newIt->first);
825 auto& newVias = newIt->second;
826 for (
auto const& viaTo : vias) {
828 newVias.emplace(viaTo.first, viaTo.second.size()).first->second;
829 for (
size_t p = 0; p < stateNames.size(); p++) {
830 if (viaTo.second.count(stateNames[p])) {
834 prefix + std::to_string(p),
835 pim->transitions[*stateNames[p]][viaTo.first].size())
840 if (pim->acceptingStates.count(stateNames[q])) {
843 if (pim->initial == stateNames[q]) {
847 pim->initial = newInitial;
848 pim->transitions = move(newTr);
849 pim->acceptingStates = move(newAcc);
860 if (pim->initial ==
nullptr) {
861 pim->initial = &(pim->transitions.emplace(
"", 0).first->first);
864 if (!pim->alphabet.count(U
'\0')) {
865 alphabetV.emplace_back(U
'\0');
867 std::sort(alphabetV.begin(), alphabetV.end());
869 labelV.reserve(pim->transitions.size());
870 labelV.emplace_back(*pim->initial);
872 acceptingV[0] = pim->acceptingStates.count(pim->initial);
873 for (
auto const& entry : pim->transitions) {
874 if (&(entry.first) == pim->initial) {
877 acceptingV[labelV.size()] = pim->acceptingStates.count(&(entry.first));
878 labelV.emplace_back(entry.first);
883 for (
size_t qi(0); qi < labelV.size(); qi++) {
884 auto const& fromQ = pim->transitions[labelV[qi]];
885 for (
size_t si(0); si < alphabetV.size(); si++) {
886 auto viaIt = fromQ.find(alphabetV[si]);
887 if (viaIt == fromQ.end()) {
891 for (
size_t pi(0); pi < labelV.size(); pi++) {
892 transitionV[qi][si][pi] =
893 viaSymbol.count(pim->findStatePointer(labelV[pi]));
897 return {move(alphabetV), move(transitionV), move(labelV), move(acceptingV)};
921 if (pim->isDeterministic()) {
925 if (pim->isDeterministic()) {
933 if (pim->initial ==
nullptr) {
934 pim->initial = &(pim->transitions.emplace(
"", 0).first->first);
937 std::sort(alphabetV.begin(), alphabetV.end());
939 labelV.reserve(pim->transitions.size());
940 labelV.emplace_back(*pim->initial);
942 acceptingV[0] = pim->acceptingStates.count(pim->initial);
943 for (
auto const& entry : pim->transitions) {
944 if (&(entry.first) == pim->initial) {
947 acceptingV[labelV.size()] = pim->acceptingStates.count(&(entry.first));
948 labelV.emplace_back(entry.first);
952 for (
size_t qi(0); qi < labelV.size(); qi++) {
953 auto const& fromQ = pim->transitions[labelV[qi]];
954 for (
size_t si(0); si < alphabetV.size(); si++) {
955 auto viaIt = fromQ.find(alphabetV[si]);
956 if (viaIt == fromQ.end() || viaIt->second.empty()) {
960 transitionV[qi][si] =
index_of(labelV, **viaSymbol.begin());
963 return {move(alphabetV), move(transitionV), move(labelV), move(acceptingV)};
std::string const & getInitialState() const
Names this DFA's initial state.
impl(impl const &other)
Copies a private implementation object by copying all non-pointer data verbatim and reconstructing po...
fabuilder & operator=(fabuilder const &b)
Copy-assigns a builder by copying another one's private implementation object.
fabuilder & addSymbol(std::string const &symbol)
Adds a symbol to the prospective FA's alphabet.
bool isAccepting(size_t qi) const
Tests whether a state is an accept state within this DFA.
unordered_set< char32_t > alphabet
Set of symbols processable by the prospective FA.
Represents nondeterministic finite automata with ε-moves.
unordered_map< string, unordered_map< char32_t, unordered_set< string const * > > > transitions
Transition function (state × symbol → set of states) of the prospective NFA.
impl(nfa const &nfa)
Constructs a private implementation object for fabuilder(nfa const&).
Represents deterministic finite automata.
fabuilder & intersect(nfa const &other)
Makes the prospective FA accept only words accepted also by another FA.
bool isDeterministic()
Checks for nondeterministic transitions.
std::vector< char32_t > const & getAlphabet_() const
Fetches this NFA's set of processable symbols.
std::string to_string(std::valarray< bool > const &qs) const
Puts a name to a set of indices.
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.
size_t delta(size_t qi, size_t si) const
Computes this DFA's transition function for a state index and a symbol index.
fabuilder & complete()
Totalizes a partial transition function by pointing any undefined transitions towards a trash state...
Contains utility classes, objects and functions used throughout the library.
impl(dfa const &dfa)
Constructs a private implementation object for fabuilder(dfa const&).
fabuilder & purge()
Purges the prospective FA of unreachable and non-producing states, possibly completing minimization...
size_t index_of(C const &container, T const &element)
Basically Java's List interface's indexOf, but as a non-member function and returning the container's...
fabuilder & minimize(bool force=false)
Convenience method for chaining purge and merge to achieve proper DFA minimization.
impl()=default
Constructs empty private implementation object.
dfa buildDfa(bool force=false)
Builds a DFA as defined by previous operations, including completion.
bool isTrashState(string const *q) const
Tests whether all of a state's outgoing transitions point to itself.
bool isAccepting(size_t qi) const
Tests whether a state is an accept state within this NFA.
string const * initial
Points to the initial state within transitions.
std::vector< std::string > const & getStates() const
Fetches this DFA's set of states.
string const * generateNewState()
Generates a uniquely named new state and adds it to the set of states.
Private implementation details of FA builders.
std::wstring_convert< std::codecvt_utf8< char32_t >, char32_t > converter
Converts between UTF-8-encoded and UTF-32-encoded strings.
fabuilder & addSymbol_(char32_t u32symbol)
Adds a symbol to the prospective FA's alphabet.
fabuilder & unite(nfa const &other)
Makes the prospective FA also accept every word of another FA's language.
fabuilder & normalizeStateNames(std::string const &prefix)
Reduces the prospective FA's state names to consecutive numbers, prefixed with a given string...
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.
fabuilder & setAccepting(std::string const &state, bool accept)
Sets whether or not a state will be accepting within the prospective FA.
unordered_set< string const * > acceptingStates
Set of pointers to accept states within transitions.
Signals that an operation requires full determinism and that no powerset construction was forced...
fabuilder()
Constructs a blank builder object.
Constructs NFAs step by step.
fabuilder & powerset()
Converts the builder's prospective NFA into a DFA via powerset construction.
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.
fabuilder & merge(bool force=false)
Merges the prospective DFA's indistinguishable states, allowing for minimization. ...
Where this library lives.
fabuilder & makeInitial(std::string const &state)
Resets the initial state for the prospective FA.
std::string const & getInitialState() const
Names this NFA's initial state.
string const * findStatePointer(string &&state)
Delivers a pointer towards one of transitions' keys.
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.
std::valarray< bool > const & epsilonClosure(size_t qi) const
Computes a state's ε-closure.
string const * findStatePointer(string const &state)
Delivers a pointer towards one of transitions' keys.
std::vector< std::string > const & getStates() const
Fetches this NFA's set of states in order of internal representation.
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...
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.
nfa buildNfa()
Builds an NFA as defined by previous operations.
std::vector< char32_t > const & getAlphabet_() const
Fetches this DFA's set of processable symbols.
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...