#pragma once #include "Utils.hpp" #include "List.hpp" #include "Map.hpp" #include "Tree.hpp" namespace tp { // Non-Deterministic Finite-State Automata template class FiniteStateAutomation { public: struct State; public: class Transition { friend FiniteStateAutomation; public: enum Type { ANY, EPSILON, SYMBOL }; public: Transition(Type type, State* state, tAlphabetType symbol = tAlphabetType()) { mState = state; mType = type; mSymbol = symbol; } [[nodiscard]] bool isTransition(const tAlphabetType& symbol) const { return (mType == ANY || mType == EPSILON) || (mSymbol == symbol); } [[nodiscard]] bool doesConsumes(const tAlphabetType& symbol) const { return (mType == ANY || (mType == SYMBOL && mSymbol == symbol)); } [[nodiscard]] bool isEpsilon() const { return mType == EPSILON; } const State* getState() const { return mState; } const tAlphabetType& getSymbol() const { return mSymbol; } private: State* mState = nullptr; Type mType; tAlphabetType mSymbol; }; class State { friend FiniteStateAutomation; public: State() = default; public: void setValue(const tStateType& stateValue) { mStateVal = stateValue; } void setAcceptance(bool isAccepting) { mIsAccepting = isAccepting; } [[nodiscard]] bool isAccepting() const { return mIsAccepting; } const tStateType& getStateVal() const { return mStateVal; } [[nodiscard]] const Buffer* getTransitions() const { return &mTransitions; } private: Buffer mTransitions{}; tStateType mStateVal = tStateType(); bool mIsAccepting = false; }; private: List mStates; State* mStartState = nullptr; Range mAlphabetRange = { ENV_UALNI_MAX, ENV_UALNI_MIN }; public: FiniteStateAutomation() = default; State* addState(const tStateType& state, bool accepting) { auto node = mStates.newNode(); node->data.mIsAccepting = accepting; node->data.mStateVal = state; mStates.pushBack(node); return &node->data; } void addTransition(State* from, State* to, const tAlphabetType& symbol) { from->mTransitions.append(Transition(Transition::SYMBOL, to, symbol)); if (mAlphabetRange.mBegin < ualni(symbol)) mAlphabetRange.mBegin = ualni(symbol); if (mAlphabetRange.mEnd > ualni(symbol)) mAlphabetRange.mEnd = ualni(symbol); } void addEpsilonTransition(State* from, State* to) { from->mTransitions.append(Transition(Transition::SYMBOL, to)); } void addAnyTransition(State* from, State* to) { from->mTransitions.append(Transition(Transition::ANY, to)); } void setStartState(State* start) { mStartState = start; } [[nodiscard]] State* getStartState() const { return mStartState; } [[nodiscard]] bool isValid() const { if (!mStartState) { return false; } return true; } [[nodiscard]] ualni numStates() const { return mStates.length(); } [[nodiscard]] const List* getStates() const { return &mStates; } [[nodiscard]] Range getAlphabetRange() const { return mAlphabetRange; } private: typedef AvlTree, bool> StatesSet; // Expands initial set with states that are reachable from initial set with no input consumption (E-transitions) static void expandSet(StatesSet& set) { List workingSet; set.forEach([&](AvlNumericKey& key, bool) { workingSet.pushBack(key.val); }); while (workingSet.length()) { auto first = workingSet.first()->data; set.insert(first, {}); for (auto transition : first->mTransitions) { if (!transition->isEpsilon()) continue; if (set.find(transition->mState)) continue; workingSet.pushBack(transition->mState); } workingSet.popFront(); } } // States that are reachable from initial set with symbol transition static void findMoveSet(StatesSet& from, StatesSet& moveSet, tAlphabetType symbol) { from.forEach([&](AvlNumericKey& key, bool) { for (auto transition : key.val->mTransitions) { if (transition->isEpsilon()) continue; if (!transition->isTransition(symbol)) continue; if (moveSet.find(transition->mState)) continue; moveSet.insert(transition->mState, {}); } }); } public: bool makeDeterministic() { if (!isValid()) return false; struct GroupKey { const StatesSet* group; static ualni hash(GroupKey key) { return 0; } bool operator==(const GroupKey& key) const { return false; } }; struct GroupInfo { StatesSet* group = nullptr; AvlTree, tAlphabetType> transitions; State* newState = nullptr; bool accepting = false; tStateType stateVal = tStateType(); }; Buffer groups = { {} }; Map groupInfos; groups.first().insert(getStartState(), false); expandSet(groups.first()); groupInfos.put({ &groups.first() }, { &groups.first() }); // 1) find new states List workingSet; workingSet.pushBack(&groups.first()); while (workingSet.length()) { StatesSet* group = workingSet.first()->data; GroupInfo* info = &groupInfos.get({ group }); for (auto symbol : getAlphabetRange()) { // calculate new possible state StatesSet potentialGroup; findMoveSet(*group, potentialGroup, tAlphabetType(symbol)); expandSet(potentialGroup); if (!potentialGroup.size()) continue; // find existing or create group StatesSet* targetGroup = nullptr; auto iter = groupInfos.presents({ &potentialGroup }); if (iter) { targetGroup = groupInfos.getSlotVal(iter).group; } else { targetGroup = &groups.append(potentialGroup); groupInfos.put({ targetGroup }, { targetGroup }); workingSet.pushBack(targetGroup); } // assert transition is added info->transitions.insert(targetGroup, tAlphabetType(symbol)); } workingSet.popFront(); } // 2) find new states termination values for (auto group : groupInfos) { GroupInfo* info = &group->val; ualni accepting = 0; info->group->forEach([&](AvlNumericKey& key, bool) { if (key.val->mIsAccepting) { accepting++; info->accepting = true; info->stateVal = key.val->mStateVal; } }); if (!accepting) { info->accepting = false; info->stateVal = info->group->head()->key.val->mStateVal; } } // 3) transfer mStates.removeAll(); // create states for (auto group : groupInfos) { group->val.newState = addState(group->val.stateVal, group->val.accepting); } // create transitions for (auto group : groupInfos) { auto functor = [&](AvlNumericKey targetGroupKey, tAlphabetType symbol) { GroupInfo* targetGroup = &groupInfos.get({ (StatesSet*) targetGroupKey.val }); addTransition(group->val.newState, targetGroup->newState, symbol); }; group->val.transitions.forEach(functor); } return true; } }; }