diff --git a/CMakeLists.txt b/CMakeLists.txt index db8dc50..703bedc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.10) project("Formalang") # set(CMAKE_CXX_FLAGS "-O3") +# set(CMAKE_CXX_FLAGS "-O0 --coverage -ftest-coverage -fprofile-arcs") find_package(GTest REQUIRED) find_package(Threads REQUIRED) @@ -15,14 +16,33 @@ include_directories( set(CMAKE_CXX_STANDARD 17) -set(SOURCE_FILES) +set(SOURCE_FILES + src/regular/RegularTree.cpp + src/regular/RegularTreeNode.cpp + src/NFA/NFAGraph.cpp + src/NFA/NFAGraphVertex.cpp + src/converters/RegularToNFA.cpp + src/converters/NFAToDFA.cpp + src/DFA/DFAGraph.cpp + src/DFA/DFAGraphVertex.cpp + src/converters/DFAToFDFA.cpp + src/converters/DFAToMinDFA.cpp + src/converters/DFAToRegular.cpp + src/converters/InvertFDFA.cpp + ) -set(TEST_FILES) +set(TEST_FILES + tests/regular/ParseRegular.cpp + tests/NFAToDFA/CheckEquivalence.cpp + tests/regularToDFA/RegularToDFA.cpp + tests/DFAToRegular/DFAToRegular.cpp + tests/invertFDFA/InvertFDFA.cpp + tests/DFAToRegular/DFAToRegular2.cpp + tests/DFAToMinDFA/CountSizesMinDFA.cpp + ) add_executable(Formalang src/main.cpp ${SOURCE_FILES}) -add_executable(Tests tests/test_main.cpp ${TEST_FILES} ${SOURCE_FILES}) +add_executable(Tests tests/MainTest.cpp ${TEST_FILES} ${SOURCE_FILES}) target_link_libraries(Tests ${GTEST_LIBRARIES} Threads::Threads) -target_link_libraries(Formalang Threads::Threads) - diff --git a/README.md b/README.md new file mode 100644 index 0000000..c18d217 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# Formalang +```c++ +#include "regular/RegularTree.hpp" +#include "NFA/NFAGraph.hpp" +#include "DFA/DFAGraph.hpp" +#include "converters/RegularToNFA.hpp" +#include "converters/NFAToDFA.hpp" +#include "converters/DFAToFDFA.hpp" +#include "converters/DFAToMinDFA.hpp" +#include "converters/DFAToRegular.hpp" +#include "converters/InvertFDFA.hpp" + +using namespace regular; +using namespace NFA; +using namespace DFA; +using namespace converters; +``` +так можно подключить все что есть в моей программе. +```c++ +RegularTree r("a*"); // Регулярное выражение +NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); // В НКА +DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); // В ДКА +DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); // Минимизация +DFA_graph.Print(); // Мой вывод +DFA_graph.CreateDotFile("2.dot"); // Вывод в dot файл +std::string reg = DFAGraphToRegular(std::move(DFA_graph)); // В регулярку +``` +Такой вывод в регулярку как показано в прошлом примере сделает много лишних скобок, если мы хотим привести к более нормальному виду, то можно сделать так +```c++ +RegularTree(reg).ToString() // Получится более красивая регулярка +``` +Про сами регулярки + * - это плюс и звезда Клини. Их можно писать после слов, или после скобок. +abacaba* ~ (abacaba)\*. a(b)a\* ~ a(b)(a)\*. Для сложения языков используется |. Символом пустого слова является пробел. + +Примеры регулярок + +a* + +a(b)+a + +(a|b)* diff --git a/include/DFA/DFAGraph.hpp b/include/DFA/DFAGraph.hpp new file mode 100644 index 0000000..f1cb616 --- /dev/null +++ b/include/DFA/DFAGraph.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include +#include + +namespace DFA { +class DFAGraph { + public: + class Vertex { + public: + Vertex(DFAGraph* owner); + + bool IsFinal() const; + bool IsStart() const; + size_t GetNumber() const; + + const std::map& GetTransitions() const; + const std::map& GetBackTransitions() const; + + void AddEdge(char, size_t); + void RemoveEdge(char); + void SetOwner(DFAGraph* owner); + void SetFinal(bool status); + void SetStart(bool status); + private: + DFAGraph* owner_; + std::map transitions_; + std::map back_transitions_; + + size_t number_; + bool is_final_ = false; + bool is_start_ = false; + + friend class DFAGraph; + }; + + DFAGraph() = default; + DFAGraph(const DFAGraph&) = delete; + DFAGraph(DFAGraph&&); + + DFAGraph& operator=(const DFAGraph&) = delete; + DFAGraph& operator=(DFAGraph&&); + + size_t AddNewVertex(); + void AddFinalVertex(size_t number); + void SetStartVertex(size_t number); + + void RemoveVertex(size_t number); + void RemoveFinalVertex(size_t number); + void RemoveStartVertex(size_t number); + + Vertex& GetVertex(size_t number); + bool NotExistVertex(size_t number); + + size_t GetCountVertexes() const; + size_t GetReallyCountVertexes() const; + const std::vector& GetFinalVertexes() const; + size_t GetStartVertex() const; + + void Print() const; + bool Accepted(const std::string&) const; + void CreateDotFile(const std::string& filename) const; + private: + size_t count_vertexes_ = 0; + std::map vertexes_; + std::vector final_vertexes_; + size_t start_vertex_ = -1; +}; +} diff --git a/include/NFA/NFAGraph.hpp b/include/NFA/NFAGraph.hpp new file mode 100644 index 0000000..db389af --- /dev/null +++ b/include/NFA/NFAGraph.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include +#include + +namespace NFA { +class NFAGraph { + public: + class Vertex { + public: + Vertex(NFAGraph* owner); + + bool IsFinal() const; + bool IsStart() const; + size_t GetNumber() const; + + const std::map>& GetTransitions() const; + const std::map>& GetBackTransitions() const; + + void AddEdge(char, size_t); + void RemoveEdge(char, size_t); + void SetOwner(NFAGraph* owner); + void SetFinal(bool status); + void SetStart(bool status); + private: + NFAGraph* owner_; + std::map> transitions_; + std::map> back_transitions_; + + size_t number_; + bool is_final_ = false; + bool is_start_ = false; + + friend class NFAGraph; + }; + + NFAGraph() = default; + NFAGraph(const NFAGraph&) = delete; + NFAGraph(NFAGraph&&); + + NFAGraph& operator=(const NFAGraph&) = delete; + NFAGraph& operator=(NFAGraph&&); + + size_t AddNewVertex(); + void AddFinalVertex(size_t number); + void AddStartVertex(size_t number); + + void RemoveVertex(size_t number); + void RemoveFinalVertex(size_t number); + void RemoveStartVertex(size_t number); + + void Composition(NFAGraph&&, + std::vector start_vertexes, + std::vector final_vertexes); + + Vertex& GetVertex(size_t number); + bool NotExistVertex(size_t number); + + size_t GetCountVertexes() const; + size_t GetReallyCountVertexes() const; + const std::vector& GetFinalVertexes() const; + const std::vector& GetStartVertexes() const; + + void Print() const; + void CreateDotFile(const std::string& filename) const; + private: + size_t count_vertexes_ = 0; + std::map vertexes_; + std::vector final_vertexes_; + std::vector start_vertexes_; +}; +} diff --git a/include/converters/DFAToFDFA.hpp b/include/converters/DFAToFDFA.hpp new file mode 100644 index 0000000..915d51c --- /dev/null +++ b/include/converters/DFAToFDFA.hpp @@ -0,0 +1,7 @@ +#pragma once +#include "DFA/DFAGraph.hpp" + +namespace converters { +using namespace DFA; +DFAGraph DFAGraphToFDFAGraph(DFAGraph&&, const std::vector& alphabet); +} diff --git a/include/converters/DFAToMinDFA.hpp b/include/converters/DFAToMinDFA.hpp new file mode 100644 index 0000000..9791c83 --- /dev/null +++ b/include/converters/DFAToMinDFA.hpp @@ -0,0 +1,7 @@ +#pragma once +#include "DFA/DFAGraph.hpp" + +namespace converters { +using namespace DFA; +DFAGraph DFAGraphToMinDFAGraph(DFAGraph&&); +} diff --git a/include/converters/DFAToRegular.hpp b/include/converters/DFAToRegular.hpp new file mode 100644 index 0000000..4c825da --- /dev/null +++ b/include/converters/DFAToRegular.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "DFA/DFAGraph.hpp" +#include + +namespace converters { +using namespace DFA; +std::string DFAGraphToRegular(DFAGraph&&); +} diff --git a/include/converters/InvertFDFA.hpp b/include/converters/InvertFDFA.hpp new file mode 100644 index 0000000..e5dce0e --- /dev/null +++ b/include/converters/InvertFDFA.hpp @@ -0,0 +1,7 @@ +#pragma once +#include "DFA/DFAGraph.hpp" + +namespace converters { +using namespace DFA; +DFAGraph InvertFDFAGraph(DFAGraph&&); +} diff --git a/include/converters/NFAToDFA.hpp b/include/converters/NFAToDFA.hpp new file mode 100644 index 0000000..66eb908 --- /dev/null +++ b/include/converters/NFAToDFA.hpp @@ -0,0 +1,13 @@ +#pragma once +#include "NFA/NFAGraph.hpp" +#include "DFA/DFAGraph.hpp" + +namespace converters { +using namespace NFA; +using namespace DFA; +NFAGraph AddAllEpsilonTransitions(NFAGraph&&); +NFAGraph AddAllPossibleFinalVertexes(NFAGraph&&); +NFAGraph DeleteEpsilonTransitions(NFAGraph&&); +NFAGraph DeleteTransitionsByOneLetter(NFAGraph&&); +DFAGraph NFAGraphToDFAGraph(NFAGraph&&); +} diff --git a/include/converters/RegularToNFA.hpp b/include/converters/RegularToNFA.hpp new file mode 100644 index 0000000..dce3ef0 --- /dev/null +++ b/include/converters/RegularToNFA.hpp @@ -0,0 +1,10 @@ +#pragma once +#include "regular/RegularTree.hpp" +#include "NFA/NFAGraph.hpp" + +namespace converters { +using namespace NFA; +using namespace regular; + +NFAGraph RegularToNFAGraph(RegularTree&&); +} diff --git a/include/regular/RegularTree.hpp b/include/regular/RegularTree.hpp new file mode 100644 index 0000000..3863b3d --- /dev/null +++ b/include/regular/RegularTree.hpp @@ -0,0 +1,44 @@ +#pragma once +#include +#include +#include +#include + +namespace regular { +class RegularTree { + public: + class Node { + public: + enum class Type { + Addition, Concatenation, Word + }; + enum class Modifier { + Plus, Star, None + }; + + Node(); + Node(Type); + + void Parse(const std::string&); + void Print() const; + std::vector> children; + std::string word; + Type type; + Modifier modifier = Modifier::None; + std::string ToString() const; + private: + void ParseCurrentType(const std::string_view); + void Compression(); + void Compression2(); + void Print(int nesting_level) const; + }; + + RegularTree(const std::string&); + const Node& GetNode() const; + void Print() const; + std::string ToString() const; + + private: + Node node_; +}; +} diff --git a/src/DFA/DFAGraph.cpp b/src/DFA/DFAGraph.cpp new file mode 100644 index 0000000..011df6e --- /dev/null +++ b/src/DFA/DFAGraph.cpp @@ -0,0 +1,149 @@ +#include "DFA/DFAGraph.hpp" +#include +#include +#include + +namespace DFA { +DFAGraph::DFAGraph(DFAGraph&& another) { + std::swap(count_vertexes_, another.count_vertexes_); + std::swap(vertexes_, another.vertexes_); + std::swap(final_vertexes_, another.final_vertexes_); + // TODO + // std::swap(start_vertexes_, another.start_vertexes_); + + for (auto& i: vertexes_) + i.second.owner_ = this; + for (auto& i: another.vertexes_) + i.second.owner_ = &another; +} + +DFAGraph& DFAGraph::operator=(DFAGraph&& another) { + std::swap(count_vertexes_, another.count_vertexes_); + std::swap(vertexes_, another.vertexes_); + std::swap(final_vertexes_, another.final_vertexes_); + // TODO + // std::swap(start_vertexes_, another.start_vertexes_); + + for (auto& i: vertexes_) + i.second.owner_ = this; + for (auto& i: another.vertexes_) + i.second.owner_ = &another; + return *this; +} +size_t DFAGraph::AddNewVertex() { + vertexes_.emplace(count_vertexes_, this); + GetVertex(count_vertexes_).number_ = count_vertexes_; + return count_vertexes_++; +} + +void DFAGraph::AddFinalVertex(size_t number) { + if (!GetVertex(number).is_final_) { + GetVertex(number).is_final_ = true; + final_vertexes_.push_back(number); + } +} + +void DFAGraph::SetStartVertex(size_t number) { + if (!GetVertex(number).is_start_) { + GetVertex(number).is_start_ = true; + start_vertex_ = number; + } +} + +void DFAGraph::RemoveVertex(size_t number) { + RemoveFinalVertex(number); + RemoveStartVertex(number); + vertexes_.erase(number); + if (number == count_vertexes_ - 1) count_vertexes_--; +} + +void DFAGraph::RemoveFinalVertex(size_t number) { + for (size_t i = 0; i < final_vertexes_.size(); ++i) { + if (final_vertexes_[i] == number) { + GetVertex(number).is_final_ = false; + std::swap(final_vertexes_[i], final_vertexes_.back()); + final_vertexes_.pop_back(); + break; + } + } +} + +void DFAGraph::RemoveStartVertex(size_t number) { + if (start_vertex_ == number) { + start_vertex_ = -1; + } +} + +DFAGraph::Vertex& DFAGraph::GetVertex(size_t number) { + if (!NotExistVertex(number)) + return vertexes_.at(number); + throw std::out_of_range("This vertex don't exist"); +} + +bool DFAGraph::NotExistVertex(size_t number) { + return !vertexes_.count(number); +} + +size_t DFAGraph::GetCountVertexes() const { + return count_vertexes_; +} + +size_t DFAGraph::GetReallyCountVertexes() const { + return vertexes_.size(); +} + +const std::vector& DFAGraph::GetFinalVertexes() const { + return final_vertexes_; +} + +size_t DFAGraph::GetStartVertex() const { + return start_vertex_; +} + +void DFAGraph::Print() const { + for (auto i: vertexes_) { + std::cout << i.second.number_ << " " << "f-" << i.second.is_final_ << " s-" << + i.second.is_start_ << std::endl; + } + for (auto& i: vertexes_) { + for (auto& j: i.second.transitions_) { + std::cout << i.second.number_ << "->" << j.second << " <" << j.first << ">" << + std::endl; + } + } + std::cout << std::endl; +} + +bool DFAGraph::Accepted(const std::string& str) const { + size_t current = start_vertex_; + for (auto i: str) { + if (vertexes_.at(current).GetTransitions().count(i)) { + current = vertexes_.at(vertexes_.at(current).GetTransitions().at(i)).GetNumber(); + } else { + return false; + } + } + return vertexes_.at(current).IsFinal(); +} + +void DFAGraph::CreateDotFile(const std::string& filename) const { + std::ofstream out(filename); + out << "digraph G {\n"; + + for (auto& i: vertexes_) { + for (auto& j: i.second.transitions_) { + out << i.first << "->" << j.second << "[label=" << j.first << "]\n"; + } + if (i.second.IsStart() && i.second.IsFinal()) { + out << " " << i.first << " [shape=star];\n"; + } else if (i.second.IsStart()) { + out << " " << i.first << " [shape=rarrow];\n"; + } else if (i.second.IsFinal()) { + out << " " << i.first << " [shape=Msquare];\n"; + } + } + + out << "}\n"; +} +} + diff --git a/src/DFA/DFAGraphVertex.cpp b/src/DFA/DFAGraphVertex.cpp new file mode 100644 index 0000000..ddad254 --- /dev/null +++ b/src/DFA/DFAGraphVertex.cpp @@ -0,0 +1,57 @@ +#include "DFA/DFAGraph.hpp" + +using Vertex = DFA::DFAGraph::Vertex; + +Vertex::Vertex(DFAGraph* owner) : owner_(owner) {} + +bool Vertex::IsFinal() const { + return is_final_; +} + +bool Vertex::IsStart() const { + return is_start_; +} + +size_t Vertex::GetNumber() const { + return number_; +} + +const std::map& Vertex::GetTransitions() const { + return transitions_; +} + +const std::map& Vertex::GetBackTransitions() const { + return back_transitions_; +} + +void Vertex::AddEdge(char symbol, size_t number) { + transitions_[symbol] = number; + owner_->GetVertex(number).back_transitions_[symbol] = number_; +} + +void Vertex::RemoveEdge(char symbol) { + owner_->GetVertex(transitions_[symbol]).back_transitions_.erase(symbol); + transitions_.erase(symbol); +} + +void Vertex::SetOwner(DFAGraph* owner) { + owner_ = owner; +} + +void Vertex::SetFinal(bool status) { + if (status != is_final_) { + if (status) + owner_->AddFinalVertex(number_); + else + owner_->RemoveFinalVertex(number_); + } +} + +void Vertex::SetStart(bool status) { + if (status != is_start_) { + if (status) + owner_->SetStartVertex(number_); + else + owner_->RemoveStartVertex(number_); + } +} diff --git a/src/NFA/NFAGraph.cpp b/src/NFA/NFAGraph.cpp new file mode 100644 index 0000000..2d31831 --- /dev/null +++ b/src/NFA/NFAGraph.cpp @@ -0,0 +1,202 @@ +#include "NFA/NFAGraph.hpp" +#include +#include +#include + +namespace NFA { +NFAGraph::NFAGraph(NFAGraph&& another) { + std::swap(count_vertexes_, another.count_vertexes_); + std::swap(vertexes_, another.vertexes_); + std::swap(final_vertexes_, another.final_vertexes_); + std::swap(start_vertexes_, another.start_vertexes_); + + for (auto& i: vertexes_) + i.second.owner_ = this; + for (auto& i: another.vertexes_) + i.second.owner_ = &another; +} + +NFAGraph& NFAGraph::operator=(NFAGraph&& another) { + std::swap(count_vertexes_, another.count_vertexes_); + std::swap(vertexes_, another.vertexes_); + std::swap(final_vertexes_, another.final_vertexes_); + std::swap(start_vertexes_, another.start_vertexes_); + + for (auto& i: vertexes_) + i.second.owner_ = this; + for (auto& i: another.vertexes_) + i.second.owner_ = &another; + return *this; +} + +size_t NFAGraph::AddNewVertex() { + vertexes_.emplace(count_vertexes_, this); + GetVertex(count_vertexes_).number_ = count_vertexes_; + return count_vertexes_++; +} + +void NFAGraph::AddFinalVertex(size_t number) { + if (!GetVertex(number).is_final_) { + GetVertex(number).is_final_ = true; + final_vertexes_.push_back(number); + } +} + +void NFAGraph::AddStartVertex(size_t number) { + if (!GetVertex(number).is_start_) { + GetVertex(number).is_start_ = true; + start_vertexes_.push_back(number); + } +} + +void NFAGraph::RemoveVertex(size_t number) { + RemoveFinalVertex(number); + RemoveStartVertex(number); + vertexes_.erase(number); +} + +void NFAGraph::RemoveFinalVertex(size_t number) { + for (size_t i = 0; i < final_vertexes_.size(); ++i) { + if (final_vertexes_[i] == number) { + GetVertex(number).is_final_ = false; + std::swap(final_vertexes_[i], final_vertexes_.back()); + final_vertexes_.pop_back(); + break; + } + } +} + +void NFAGraph::RemoveStartVertex(size_t number) { + for (size_t i = 0; i < start_vertexes_.size(); ++i) { + if (start_vertexes_[i] == number) { + GetVertex(number).is_start_ = false; + std::swap(start_vertexes_[i], start_vertexes_.back()); + start_vertexes_.pop_back(); + break; + } + } +} + +void NFAGraph::Composition(NFAGraph&& nfa_graph, + std::vector start_vertexes, + std::vector final_vertexes) { + auto add_final_vertexes = nfa_graph.final_vertexes_; + auto add_start_vertexes = nfa_graph.start_vertexes_; + + size_t new_count_vertexes = count_vertexes_; + + for (auto& i: nfa_graph.vertexes_) { + new_count_vertexes = std::max(new_count_vertexes, i.first + count_vertexes_ + 1); + i.second.number_ += count_vertexes_; + std::map> new_transitions; + for (auto& j: i.second.transitions_) { + for (auto& k: j.second) { + new_transitions[j.first].insert(k + count_vertexes_); + } + } + std::map> new_back_transitions; + for (auto& j: i.second.back_transitions_) { + for (auto& k: j.second) { + new_back_transitions[j.first].insert(k + count_vertexes_); + } + } + i.second.transitions_ = std::move(new_transitions); + i.second.back_transitions_ = std::move(new_back_transitions); + + i.second.SetOwner(this); + vertexes_.emplace(i.second.number_, std::move(i.second)); + } + + for (auto& i: add_start_vertexes) { + i += count_vertexes_; + } + for (auto& i: add_final_vertexes) { + i += count_vertexes_; + } + + count_vertexes_ = new_count_vertexes; + + for (auto& i: start_vertexes) { + for (auto& j: add_start_vertexes) { + GetVertex(i).transitions_[' '].insert(j); + GetVertex(j).back_transitions_[' '].insert(i); + } + } + for (auto& i: add_final_vertexes) { + for (auto& j: final_vertexes) { + GetVertex(i).transitions_[' '].insert(j); + GetVertex(j).back_transitions_[' '].insert(i); + } + } + for (auto& i: add_final_vertexes) { + GetVertex(i).is_final_ = false; + } + for (auto& i: add_start_vertexes) { + GetVertex(i).is_start_ = false; + } +} + +NFAGraph::Vertex& NFAGraph::GetVertex(size_t number) { + if (!NotExistVertex(number)) + return vertexes_.at(number); + throw std::out_of_range("This vertex don't exist"); +} + +bool NFAGraph::NotExistVertex(size_t number) { + return !vertexes_.count(number); +} + +size_t NFAGraph::GetCountVertexes() const { + return count_vertexes_; +} + +size_t NFAGraph::GetReallyCountVertexes() const { + return vertexes_.size(); +} + +const std::vector& NFAGraph::GetFinalVertexes() const { + return final_vertexes_; +} + +const std::vector& NFAGraph::GetStartVertexes() const { + return start_vertexes_; +} + +void NFAGraph::Print() const { + for (auto i: vertexes_) { + std::cout << i.second.number_ << " " << "f-" << i.second.is_final_ << " s-" << + i.second.is_start_ << std::endl; + } + for (auto& i: vertexes_) { + for (auto& j: i.second.transitions_) { + for (auto k: j.second) { + std::cout << i.second.number_ << "." << k << " <" << j.first << ">" << + std::endl; + } + } + } + std::cout << std::endl; +} + +void NFAGraph::CreateDotFile(const std::string& filename) const { + std::ofstream out(filename); + out << "digraph G {\n"; + + for (auto& i: vertexes_) { + for (auto& j: i.second.transitions_) { + for (auto& k: j.second) { + out << i.first << "." << k << "[label=" << j.first << "]\n"; + } + } + if (i.second.IsStart() && i.second.IsFinal()) { + out << " " << i.first << " [shape=Mstar];\n"; + } else if (i.second.IsStart()) { + out << " " << i.first << " [shape=Mdiamond];\n"; + } else if (i.second.IsFinal()) { + out << " " << i.first << " [shape=Msquare];\n"; + } + } + + out << "}\n"; +} +} diff --git a/src/NFA/NFAGraphVertex.cpp b/src/NFA/NFAGraphVertex.cpp new file mode 100644 index 0000000..32610a9 --- /dev/null +++ b/src/NFA/NFAGraphVertex.cpp @@ -0,0 +1,62 @@ +#include "NFA/NFAGraph.hpp" + +using Vertex = NFA::NFAGraph::Vertex; + +Vertex::Vertex(NFAGraph* owner) : owner_(owner) {} + +bool Vertex::IsFinal() const { + return is_final_; +} + +bool Vertex::IsStart() const { + return is_start_; +} + +size_t Vertex::GetNumber() const { + return number_; +} + +const std::map>& Vertex::GetTransitions() const { + return transitions_; +} + +const std::map>& Vertex::GetBackTransitions() const { + return back_transitions_; +} + +void Vertex::AddEdge(char symbol, size_t number) { + transitions_[symbol].insert(number); + owner_->GetVertex(number).back_transitions_[symbol]; + owner_->GetVertex(number).back_transitions_[symbol].insert(number_); +} + +void Vertex::RemoveEdge(char symbol, size_t number) { + transitions_[symbol].erase(number); + if (transitions_[symbol].size() == 0) + transitions_.erase(symbol); + owner_->GetVertex(number).back_transitions_[symbol].erase(number_); + if (owner_->GetVertex(number).back_transitions_[symbol].size() == 0) + owner_->GetVertex(number).back_transitions_.erase(symbol); +} + +void Vertex::SetOwner(NFAGraph* owner) { + owner_ = owner; +} + +void Vertex::SetFinal(bool status) { + if (status != is_final_) { + if (status) + owner_->AddFinalVertex(number_); + else + owner_->RemoveFinalVertex(number_); + } +} + +void Vertex::SetStart(bool status) { + if (status != is_start_) { + if (status) + owner_->AddStartVertex(number_); + else + owner_->RemoveStartVertex(number_); + } +} diff --git a/src/converters/DFAToFDFA.cpp b/src/converters/DFAToFDFA.cpp new file mode 100644 index 0000000..6b7245d --- /dev/null +++ b/src/converters/DFAToFDFA.cpp @@ -0,0 +1,46 @@ +#include "converters/DFAToFDFA.hpp" + +namespace converters { +DFAGraph DFAGraphToFDFAGraph(DFAGraph&& graph, const std::vector& alphabet) { + DFAGraph result; + const int n = graph.GetCountVertexes(); + std::map number_vertex_in_DFA; + for (int i = 0; i < n; ++i) { + if (graph.NotExistVertex(i)) continue; + number_vertex_in_DFA[i] = result.AddNewVertex(); + } + for (int i = 0; i < n; ++i) { + if (graph.NotExistVertex(i)) continue; + if (graph.GetVertex(i).IsFinal()) + result.GetVertex(number_vertex_in_DFA[i]).SetFinal(true); + if (graph.GetVertex(i).IsStart()) + result.GetVertex(number_vertex_in_DFA[i]).SetStart(true); + + const auto& transitions = graph.GetVertex(i).GetTransitions(); + for (const auto& t: transitions) { + result.GetVertex(number_vertex_in_DFA[i]).AddEdge(t.first, t.second); + } + } + + size_t drain = result.AddNewVertex(); + + for (int i = 0; i < n; ++i) { + const auto& transitions = graph.GetVertex(i).GetTransitions(); + for (char j: alphabet) { + if (!transitions.count(j)) { + result.GetVertex(i).AddEdge(j, drain); + } + } + } + + if (result.GetVertex(drain).GetBackTransitions().size() == 0) { + result.RemoveVertex(drain); + } else { + for (char j: alphabet) { + result.GetVertex(drain).AddEdge(j, drain); + } + } + + return result; +} +} diff --git a/src/converters/DFAToMinDFA.cpp b/src/converters/DFAToMinDFA.cpp new file mode 100644 index 0000000..af52be9 --- /dev/null +++ b/src/converters/DFAToMinDFA.cpp @@ -0,0 +1,111 @@ +#include "converters/DFAToMinDFA.hpp" +#include + +namespace converters { +DFAGraph DFAGraphToMinDFAGraph(DFAGraph&& graph) { + const int n = graph.GetCountVertexes(); + DFAGraph result; + + std::vector alphabet; + + { + std::set set_alphabet; + for (int i = 0; i < n; ++i) { + if (graph.NotExistVertex(i)) continue; + for (auto i: graph.GetVertex(i).GetTransitions()) { + set_alphabet.insert(i.first); + } + } + alphabet = std::vector(set_alphabet.begin(), set_alphabet.end()); + } + + std::vector>> table(n, + std::vector>(n, std::vector(alphabet.size() + 1))); + + { + std::vector>& layer(table[0]); + for (int i = 0; i < n; ++i) { + layer[i][0] = graph.GetVertex(i).IsFinal(); + } + for (int i = 0; i < n; ++i) { + for (int j = 0; j < alphabet.size(); ++j) { + const auto& transitions = graph.GetVertex(i).GetTransitions(); + if (transitions.count(alphabet[j])) + layer[i][j + 1] = layer[transitions.at(alphabet[j])][0]; + else + layer[i][j + 1] = -1; + } + } + } + + for (int number_layer = 1; number_layer < n; ++number_layer) { + size_t count_types = 0; + std::vector>& prev_layer(table[number_layer - 1]); + std::vector>& layer(table[number_layer]); + for (int i = 0; i < n; ++i) { + bool is_find = false; + for (int j = 0; j < i; ++j) { + if (prev_layer[j] == prev_layer[i]) { + layer[i][0] = layer[j][0]; + is_find = true; + break; + } + } + if (!is_find) { + layer[i][0] = count_types++; + } + } + for (int i = 0; i < n; ++i) { + for (int j = 0; j < alphabet.size(); ++j) { + const auto& transitions = graph.GetVertex(i).GetTransitions(); + if (transitions.count(alphabet[j])) + layer[i][j + 1] = layer[transitions.at(alphabet[j])][0]; + else + layer[i][j + 1] = -1LL; + } + } + } + + /* + for (int i = 0; i < n; ++i) { + for (int k = 0; k < n; ++k) { + for (int j = 0; j < alphabet.size() + 1; ++j) { + std::cout << table[k][i][j] << " "; + } + std::cout << "| "; + } + std::cout << std::endl; + } + */ + + std::vector>& last_layer(table.back()); + size_t count_vertex = 0; + + for (int i = 0; i < n; ++i) { + count_vertex = std::max(count_vertex, last_layer[i][0]); + } + count_vertex++; + + std::map number_vertex_in_DFA; + for (int i = 0; i < count_vertex; ++i) { + number_vertex_in_DFA[i] = result.AddNewVertex(); + } + + for (int i = 0; i < n; ++i) { + size_t v = number_vertex_in_DFA[last_layer[i][0]]; + + const auto& transitions = graph.GetVertex(i).GetTransitions(); + for (const auto& t: transitions) { + if (last_layer[t.second][0] != -1LL) { + size_t u = number_vertex_in_DFA[last_layer[t.second][0]]; + result.GetVertex(v).AddEdge(t.first, u); + } + } + + if (graph.GetVertex(i).IsStart()) result.GetVertex(v).SetStart(true); + if (graph.GetVertex(i).IsFinal()) result.GetVertex(v).SetFinal(true); + } + + return result; +} +} diff --git a/src/converters/DFAToRegular.cpp b/src/converters/DFAToRegular.cpp new file mode 100644 index 0000000..ffd6f0c --- /dev/null +++ b/src/converters/DFAToRegular.cpp @@ -0,0 +1,124 @@ +#include "converters/DFAToRegular.hpp" + +namespace converters { +std::string DFAGraphToRegular(DFAGraph&& graph) { + const size_t n = graph.GetCountVertexes() + 1; + std::string result = ""; + + std::vector>> transitions(n, + std::vector>(n)); + + std::map rename; + size_t start = -1; + + for (int i = 0; i < n - 1; ++i) { + if (graph.NotExistVertex(i)) continue; + if (graph.GetVertex(i).IsStart()) start = i; + } + + rename[start] = 1; + int cnt = 2; + + for (int i = 0; i < n - 1; ++i) { + if (i == start) continue; + rename[i] = cnt++; + } + + + for (int i = 0; i < n - 1; ++i) { + if (graph.NotExistVertex(i)) continue; + const auto& vertex_transitions = graph.GetVertex(i).GetTransitions(); + + for (auto j: vertex_transitions) { + transitions[rename[i]][rename[j.second]].insert(std::string(1, j.first)); + } + if (graph.GetVertex(i).IsFinal()) { + transitions[rename[i]][0].insert("( )"); + } + } + + for (int i = n - 1; i > 1; --i) { + std::vector>> new_transitions(n, + std::vector>(n)); + std::string loop = ""; + if (transitions[i][i].size() != 0) { + for (const std::string s: transitions[i][i]) { + loop += "("; + loop += s; + loop += ")"; + loop += "|"; + } + if (loop.size()) + loop.pop_back(); + if (transitions[i][i].size() > 1) + loop = "(" + loop + ")*"; + else + loop += "*"; + } + + for (int j = 0; j < i; ++j) { + if (transitions[j][i].size() == 0) continue; + std::string j_to_i = ""; + for (const std::string s: transitions[j][i]) { + j_to_i += "("; + j_to_i += s; + j_to_i += ")"; + j_to_i += "|"; + } + if (j_to_i.size()) + j_to_i.pop_back(); + if (transitions[j][i].size() > 1) + j_to_i = "(" + j_to_i + ")"; + + for (int k = 0; k < i; ++k) { + if (transitions[i][k].size() == 0) continue; + std::string i_to_k = ""; + for (const std::string s: transitions[i][k]) { + i_to_k += "("; + i_to_k += s; + i_to_k += ")"; + i_to_k += "|"; + } + if (i_to_k.size()) + i_to_k.pop_back(); + if (transitions[i][k].size() > 1) + i_to_k = "(" + j_to_i + ")"; + + new_transitions[j][k].insert(j_to_i + loop + i_to_k); + } + } + + for (int j = 0; j < i; ++j) { + for (int k = 0; k < i; ++k) { + transitions[j][k].insert(new_transitions[j][k].begin(), new_transitions[j][k].end()); + } + } + } + + std::string loop; + std::string begin_to_end; + + if (transitions[1][1].size() != 0) { + for (const std::string& s: transitions[1][1]) { + loop += "("; + loop += s; + loop += ")"; + loop += "|"; + } + if (loop.size()) + loop.pop_back(); + loop = "(" + loop + ")*"; + } + for (const std::string& s: transitions[1][0]) { + begin_to_end += "("; + begin_to_end += s; + begin_to_end += ")"; + begin_to_end += "|"; + } + if (begin_to_end.size()) + begin_to_end.pop_back(); + + std::string res(loop + "(" + begin_to_end + ")"); + return res; +} +} diff --git a/src/converters/InvertFDFA.cpp b/src/converters/InvertFDFA.cpp new file mode 100644 index 0000000..429060b --- /dev/null +++ b/src/converters/InvertFDFA.cpp @@ -0,0 +1,12 @@ +#include "converters/InvertFDFA.hpp" + +namespace converters { +DFAGraph InvertFDFAGraph(DFAGraph&& fdfa_graph) { + const size_t n = fdfa_graph.GetCountVertexes(); + for (int i = 0; i < n; ++i) { + if (fdfa_graph.NotExistVertex(i)) continue; + fdfa_graph.GetVertex(i).SetFinal(!fdfa_graph.GetVertex(i).IsFinal()); + } + return std::move(fdfa_graph); +} +} diff --git a/src/converters/NFAToDFA.cpp b/src/converters/NFAToDFA.cpp new file mode 100644 index 0000000..843fc06 --- /dev/null +++ b/src/converters/NFAToDFA.cpp @@ -0,0 +1,183 @@ +#include "converters/NFAToDFA.hpp" +#include +#include +#include + +namespace converters { +NFAGraph AddAllEpsilonTransitions(NFAGraph&& nfa_graph) { + const int n = nfa_graph.GetCountVertexes(); + std::vector used(n, false); + + std::function dfs = [&nfa_graph, &used, &dfs](int u, int v) -> void { + used[v] = true; + const auto& transitions = nfa_graph.GetVertex(v).GetTransitions(); + if (transitions.count(' ')) { + const auto& s = transitions.at(' '); + for (auto i: s) { + if (!used[i]) { + if (u != i) + nfa_graph.GetVertex(u).AddEdge(' ', i); + dfs(u, i); + } + } + } + }; + + for (int i = 0; i < n; ++i) { + if (nfa_graph.NotExistVertex(i)) continue; + + used.assign(n, false); + dfs(i, i); + } + return std::move(nfa_graph); +} + +NFAGraph AddAllPossibleFinalVertexes(NFAGraph&& nfa_graph) { + if (nfa_graph.GetStartVertexes().size() != 1) { + size_t start_vertex = nfa_graph.AddNewVertex(); + for (auto v: nfa_graph.GetStartVertexes()) { + nfa_graph.GetVertex(start_vertex).AddEdge(' ', v); + } + } + for (const auto& v: nfa_graph.GetFinalVertexes()) { + const auto& transitions = nfa_graph.GetVertex(v).GetBackTransitions(); + if (transitions.count(' ')) { + const auto& s = transitions.at(' '); + for (auto i: s) { + nfa_graph.GetVertex(i).SetFinal(true); + } + } + } + return std::move(nfa_graph); +} + +NFAGraph DeleteEpsilonTransitions(NFAGraph&& nfa_graph) { + const int n = nfa_graph.GetCountVertexes(); + + for (int v = 0; v < n; ++v) { + if (nfa_graph.NotExistVertex(v)) continue; + + const auto& transitions = nfa_graph.GetVertex(v).GetTransitions(); + if (transitions.count(' ')) { + auto s = transitions.at(' '); + for (auto u: s) { + for (auto& i: nfa_graph.GetVertex(u).GetTransitions()) { + if (i.first == ' ') continue; + for (auto t: i.second) { + nfa_graph.GetVertex(v).AddEdge(i.first, t); + } + } + } + for (auto u: s) { + nfa_graph.GetVertex(v).RemoveEdge(' ', u); + } + } + } + return std::move(nfa_graph); +} + +NFAGraph DeleteTransitionsByOneLetter(NFAGraph&& nfa_graph) { + const int n = nfa_graph.GetCountVertexes(); + + NFAGraph result_tree; + + std::map, char>, std::set> transitions; + std::map, size_t> number_vertex_in_result_tree; + std::queue> queue; + + std::set alphabet; + for (int i = 0; i < n; ++i) { + if (nfa_graph.NotExistVertex(i)) continue; + for (const auto& j: nfa_graph.GetVertex(i).GetTransitions()) { + if (j.second.size() > 0) + alphabet.insert(j.first); + } + } + + for (auto i: nfa_graph.GetStartVertexes()) { + queue.push({i}); + number_vertex_in_result_tree[{i}] = result_tree.AddNewVertex(); + } + + while (!queue.empty()) { + auto current = queue.front(); + queue.pop(); + + for (auto symbol: alphabet) { + std::set result; + for (auto v: current) { + const auto& transitions = nfa_graph.GetVertex(v).GetTransitions(); + if (transitions.count(symbol)) { + const auto& s = transitions.at(symbol); + result.insert(s.begin(), s.end()); + } + } + + if (result.size() == 0) continue; + + transitions[std::make_pair(current, symbol)] = result; + if (!number_vertex_in_result_tree.count(result)) { + queue.push(result); + number_vertex_in_result_tree[result] = result_tree.AddNewVertex(); + } + } + } + + for (auto i: transitions) { + int v = number_vertex_in_result_tree[i.first.first]; + int u = number_vertex_in_result_tree[i.second]; + char symbol = i.first.second; + result_tree.GetVertex(v).AddEdge(symbol, u); + } + + for (auto i: number_vertex_in_result_tree) { + auto s = i.first; + auto v = i.second; + + for (auto i: s) { + if (nfa_graph.GetVertex(i).IsFinal()) + result_tree.GetVertex(v).SetFinal(true); + if (nfa_graph.GetVertex(i).IsStart()) + result_tree.GetVertex(v).SetStart(true); + } + } + + return result_tree; +} + +DFAGraph NFAGraphToDFAGraph(NFAGraph&& nfa_graph) { + nfa_graph = DeleteTransitionsByOneLetter(DeleteEpsilonTransitions(AddAllPossibleFinalVertexes( + AddAllEpsilonTransitions(std::move(nfa_graph))))); + + const int n = nfa_graph.GetCountVertexes(); + DFAGraph result; + std::map number_vertex_in_DFA; + for (int i = 0; i < n; ++i) { + if (nfa_graph.NotExistVertex(i)) continue; + number_vertex_in_DFA[i] = result.AddNewVertex(); + } + bool exists_start_vertex = false; + for (int i = 0; i < n; ++i) { + if (nfa_graph.NotExistVertex(i)) continue; + if (nfa_graph.GetVertex(i).IsFinal()) + result.GetVertex(number_vertex_in_DFA[i]).SetFinal(true); + if (nfa_graph.GetVertex(i).IsStart()) { + if (exists_start_vertex) { + throw std::runtime_error("I can't delete starts vertex"); + } + result.GetVertex(number_vertex_in_DFA[i]).SetStart(true); + exists_start_vertex = true; + } + + const auto& transitions = nfa_graph.GetVertex(i).GetTransitions(); + for (const auto& t: transitions) { + if (t.second.size() != 1) throw std::logic_error(""); + result.GetVertex(number_vertex_in_DFA[i]).AddEdge(t.first, *t.second.begin()); + } + } + if (!exists_start_vertex) { + throw std::runtime_error("I can't find start vertex"); + } + return result; +} +} diff --git a/src/converters/RegularToNFA.cpp b/src/converters/RegularToNFA.cpp new file mode 100644 index 0000000..acae0a6 --- /dev/null +++ b/src/converters/RegularToNFA.cpp @@ -0,0 +1,87 @@ +#include "converters/RegularToNFA.hpp" +#include "regular/RegularTree.hpp" +#include "NFA/NFAGraph.hpp" + +using namespace NFA; +using namespace regular; +using Node = RegularTree::Node; + +struct do_nothing_deleter{ + template + void operator()(T*) const {} +}; + +template +NFAGraph RegularToNFA(std::unique_ptr node) { + NFAGraph result; + if (node->type == Node::Type::Word) { + auto end = result.AddNewVertex(); + auto start = end; + result.AddStartVertex(end); + for (char i: node->word) { + auto tmp = result.AddNewVertex(); + result.GetVertex(end).AddEdge(i, tmp); + end = tmp; + } + if (node->modifier == Node::Modifier::Plus) { + result.GetVertex(end).AddEdge(' ', start); + } else if (node->modifier == Node::Modifier::Star) { + result.GetVertex(end).AddEdge(' ', start); + result.GetVertex(start).AddEdge(' ', end); + } + result.AddFinalVertex(end); + } else if (node->type == Node::Type::Concatenation) { + result = RegularToNFA(std::move(node->children[0])); + for (auto it = node->children.begin() + 1; it != node->children.end(); ++it) { + auto tmp = RegularToNFA(std::move(*it)); + auto v = result.AddNewVertex(); + result.Composition(std::move(tmp), result.GetFinalVertexes(), {v}); + while (!result.GetFinalVertexes().empty()) { + result.RemoveFinalVertex(result.GetFinalVertexes()[0]); + } + result.AddFinalVertex(v); + } + const auto& start_vertexes = result.GetStartVertexes(); + const auto& end_vertexes = result.GetFinalVertexes(); + + if (node->modifier == Node::Modifier::Plus) { + for (auto start: start_vertexes) { + for (auto end: end_vertexes) { + result.GetVertex(end).AddEdge(' ', start); + } + } + } else if (node->modifier == Node::Modifier::Star) { + for (auto start: start_vertexes) { + for (auto end: end_vertexes) { + result.GetVertex(end).AddEdge(' ', start); + result.GetVertex(start).AddEdge(' ', end); + } + } + } + + } else if (node->type == Node::Type::Addition) { + auto start = result.AddNewVertex(); + result.AddStartVertex(start); + auto end = result.AddNewVertex(); + result.AddFinalVertex(end); + for (auto& i: node->children) { + auto tmp = RegularToNFA(std::move(i)); + result.Composition(std::move(tmp), {start}, {end}); + } + if (node->modifier == Node::Modifier::Plus) { + result.GetVertex(end).AddEdge(' ', start); + } else if (node->modifier == Node::Modifier::Star) { + result.GetVertex(end).AddEdge(' ', start); + result.GetVertex(start).AddEdge(' ', end); + } + } + return result; +} + +namespace converters { +NFAGraph RegularToNFAGraph(RegularTree&& tree) { + const Node& root = tree.GetNode(); + return RegularToNFA(std::unique_ptr(&const_cast(tree.GetNode()))); +} +} diff --git a/src/main.cpp b/src/main.cpp index a03be09..9187628 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,108 @@ #include +#include "regular/RegularTree.hpp" +#include "NFA/NFAGraph.hpp" +#include "DFA/DFAGraph.hpp" +#include "converters/RegularToNFA.hpp" +#include "converters/NFAToDFA.hpp" +#include "converters/DFAToFDFA.hpp" +#include "converters/DFAToMinDFA.hpp" +#include "converters/DFAToRegular.hpp" +#include "converters/InvertFDFA.hpp" + +using namespace regular; +using namespace NFA; +using namespace DFA; +using namespace converters; + +void example1() { + RegularTree r("a*"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); // Regular to FA + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); // to DFA + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); // minimize + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); // DFA to regular + r = RegularTree(reg); // create regular + NFA_tree = RegularToNFAGraph(std::move(r)); // to FA + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); // to DFA + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); // minimize + DFA_graph.Print(); // print +} + +void example2() { + NFAGraph nfa_graph; + const size_t N = 10; + + size_t a[N]; + for (size_t i = 0; i < N; ++i) + a[i] = nfa_graph.AddNewVertex(); + + auto AddEdge = [&nfa_graph, &a](char symbol, int u, int v) { + nfa_graph.GetVertex(a[u]).AddEdge(symbol, a[v]); + }; + + AddEdge('a', 0, 1); + AddEdge('b', 0, 2); + AddEdge('b', 1, 3); + AddEdge('b', 2, 4); + + DFAGraph dfa_graph = NFAGraphToDFAGraph(std::move(nfa_graph)); + dfa_graph = DFAGraphToMinDFAGraph(std::move(dfa_graph)); + dfa_graph.Print(); +} int main() { - std::cout << "hello world"; + { + RegularTree r("a*"); + NFAGraph NFA_graph = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_graph)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + DFA_graph = DFAGraphToFDFAGraph(std::move(DFA_graph), {'a', 'b'}); + DFA_graph.Print(); + DFA_graph.CreateDotFile("1.dot"); + DFA_graph = InvertFDFAGraph(std::move(DFA_graph)); + DFA_graph.CreateDotFile("2.dot"); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + } + + return 0; + { + RegularTree r("((ab|ba)*( |a|ba))"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + // DFA_graph.CreateDotFile("1.dot"); + // DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + DFA_graph = DFAGraphToFDFAGraph(std::move(DFA_graph), {'a', 'b'}); + DFA_graph = InvertFDFAGraph(std::move(DFA_graph)); + // DFA_graph.CreateDotFile("2.dot"); + auto ss = DFAGraphToRegular(std::move(DFA_graph)); + std::cout << ss << std::endl; + r = RegularTree(ss); + // r.Print(); + std::cout << r.ToString() << std::endl; + // DFA_graph.Print(); + return 0; + + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + DFA_graph.Print(); + auto s = DFAGraphToRegular(std::move(DFA_graph)); + std::cout << s << std::endl; + r = RegularTree(s); + // r.Print(); + std::cout << r.ToString() << std::endl; + } + return 0; + RegularTree r("a*"); + auto nfa = RegularToNFAGraph(std::move(r)); + auto dfa = NFAGraphToDFAGraph(std::move(nfa)); + dfa = DFAGraphToMinDFAGraph(std::move(dfa)); + auto fdfa = DFAGraphToFDFAGraph(std::move(dfa), {'a', 'b'}); + fdfa.Print(); + fdfa = InvertFDFAGraph(std::move(fdfa)); + fdfa.Print(); + dfa = DFAGraphToMinDFAGraph(std::move(fdfa)); + auto s = DFAGraphToRegular(std::move(dfa)); + + /* + RegularTree r("(a|b)+bab(a|b)+"); + std::cout << DFAGraphToRegular(DFAGraphToMinDFAGraph(InvertFDFAGraph(DFAGraphToFDFAGraph(DFAGraphToMinDFAGraph(NFAGraphToDFAGraph(RegularToNFAGraph(std::move(r)))), {'a', 'b'})))) << std::endl; + */ } diff --git a/src/regular/RegularTree.cpp b/src/regular/RegularTree.cpp new file mode 100644 index 0000000..70285b7 --- /dev/null +++ b/src/regular/RegularTree.cpp @@ -0,0 +1,19 @@ +#include "regular/RegularTree.hpp" + +namespace regular { +RegularTree::RegularTree(const std::string& regular) { + node_.Parse(regular); +} + +const RegularTree::Node& RegularTree::GetNode() const { + return node_; +} + +void RegularTree::Print() const { + node_.Print(); +} + +std::string RegularTree::ToString() const { + return node_.ToString(); +} +} diff --git a/src/regular/RegularTreeNode.cpp b/src/regular/RegularTreeNode.cpp new file mode 100644 index 0000000..ac297ae --- /dev/null +++ b/src/regular/RegularTreeNode.cpp @@ -0,0 +1,254 @@ +#include "regular/RegularTree.hpp" +#include + +using Node = regular::RegularTree::Node; +Node::Node() {} +Node::Node(Type type) : type(type) {} + +void Node::Parse(const std::string& regular) { + type = Type::Addition; + ParseCurrentType(std::string_view(regular.c_str(), regular.size())); + for (int i = 0; i < 10; ++i) { + Compression(); + Compression2(); + } +} + +void Node::Print() const { + Print(0); +} + +void Node::ParseCurrentType(const std::string_view regular) { + const size_t n = regular.size(); + children.clear(); + word.clear(); + + auto AddChild = [this](const std::string_view regular) { + this->children.push_back(std::make_unique(Type::Addition)); + this->children.back()->ParseCurrentType(regular); + }; + + if (n == 1) { + type = Type::Word; + } + + if (type == Type::Addition) { + int balance = 0; + int begin_child = 0; + bool wrapped_brackets = (regular[0] == '('); + + for (size_t i = 0; i < n; ++i) { + if (regular[i] == '(') { + ++balance; + } else if (regular[i] == ')') { + --balance; + } + + if (regular[i] == '|') { + if (balance == 0) { + AddChild(regular.substr(begin_child + wrapped_brackets, i - + begin_child - 2 * + wrapped_brackets)); + begin_child = i + 1; + + if (i + 1 < n) + wrapped_brackets = (regular[i + 1] == '('); + } + } else { + if (i + 1 == n) { + if (children.size() == 0) { + type = Type::Concatenation; + break; + } else { + AddChild(regular.substr(begin_child + wrapped_brackets, i - + begin_child + 1 - 2 * + wrapped_brackets)); + begin_child = i + 1; + if (i + 1 < n) + wrapped_brackets = (regular[i + 1] == '('); + } + } else if (balance == 0) { + wrapped_brackets = false; + } + } + } + } + + if (type == Type::Concatenation) { + int balance = 0; + int begin_child = 0; + + for (size_t i = 0; i < n; ++i) { + if (regular[i] == '(') { + ++balance; + if (balance == 1) { + if (begin_child < i) { + AddChild(regular.substr(begin_child, i - begin_child)); + } + begin_child = i + 1; + } + } else if (regular[i] == ')') { + --balance; + if (balance == 0) { + AddChild(regular.substr(begin_child, i - begin_child)); + begin_child = i + 1; + } + } else if (i + 1 == n) { + if (balance != 0) { + throw std::logic_error("invalid regular"); + } + + if (children.size() == 0) { + type = Type::Word; + break; + } else { + if (regular[i] == '+') { + if (begin_child < i) { + AddChild(regular.substr(begin_child, i - begin_child)); + } + children.back()->modifier = Modifier::Plus; + begin_child = i + 1; + } else if (regular[i] == '*') { + if (begin_child < i) { + AddChild(regular.substr(begin_child, i - begin_child)); + } + children.back()->modifier = Modifier::Star; + begin_child = i + 1; + } else { + AddChild(regular.substr(begin_child, i - begin_child + 1)); + begin_child = i + 1; + } + } + } else if (balance == 0) { + if (regular[i] == '+') { + if (begin_child < i) { + AddChild(regular.substr(begin_child, i - begin_child)); + } + children.back()->modifier = Modifier::Plus; + begin_child = i + 1; + } else if (regular[i] == '*') { + if (begin_child < i) { + AddChild(regular.substr(begin_child, i - begin_child)); + } + children.back()->modifier = Modifier::Star; + begin_child = i + 1; + } + } + } + } + + if (type == Type::Word) { + bool exist_modifire = regular.back() == '+' || regular.back() == '*'; + if (regular.back() == '+') { + modifier = Modifier::Plus; + } else if (regular.back() == '*') { + modifier = Modifier::Star; + } + for (size_t i = 0; i < n - exist_modifire; ++i) { + if (regular[i] == '|' || regular[i] == '(' || regular[i] == ')') { + throw std::logic_error("invalid regular"); + } + word += regular[i]; + } + } +} + +void Node::Compression() { + for (auto& i: children) { + i->Compression(); + } + if (children.size() == 1 && modifier == Modifier::None) { + auto tmp = std::move(*children[0]); + *this = std::move(tmp); + } +} + +void Node::Compression2() { + for (auto& i: children) { + i->Compression2(); + } + bool f = type == Type::Concatenation; + for (auto& i: children) { + f &= i->modifier == Modifier::None; + f &= i->type == Type::Word; + } + if (f) { + auto tmp = std::move(*children[0]); + tmp.modifier = modifier; + for (int i = 1; i < children.size(); ++i) { + tmp.word += children[i]->word; + } + *this = std::move(tmp); + } +} + +void Node::Print(int nesting_level) const { + auto PrintNesingLevel = [](int nesting_level) { + for (int i = 0; i < nesting_level; ++i) { + std::cout << " "; + } + }; + + PrintNesingLevel(nesting_level); + if (type == Type::Addition) { + std::cout << "Addition"; + if (modifier == Modifier::Plus) { + std::cout << "+"; + } else if (modifier == Modifier::Star) { + std::cout << "*"; + } + std::cout << " " << std::to_string(children.size()) << ":" << std::endl; + } else if (type == Type::Concatenation) { + std::cout << "Concatenation"; + if (modifier == Modifier::Plus) { + std::cout << "+"; + } else if (modifier == Modifier::Star) { + std::cout << "*"; + } + std::cout << " " << children.size() << ":" << std::endl; + } else if (type == Type::Word) { + std::cout << "Word"; + if (modifier == Modifier::Plus) { + std::cout << "+"; + } else if (modifier == Modifier::Star) { + std::cout << "*"; + } + std::cout << ": " << word << std::endl; + } + + for (const auto& i: children) { + i->Print(nesting_level + 1); + } +} + +std::string regular::RegularTree::Node::ToString() const { + std::string res; + if (type == Type::Word) { + res = word; + if (modifier == Modifier::Plus) + res = "(" + res + ")+"; + else if (modifier == Modifier::Star) + res = "(" + res + ")*"; + } else if (type == Type::Concatenation) { + for (auto& i: children) { + res += i->ToString(); + } + if (modifier == Modifier::Plus) + res = "(" + res + ")+"; + else if (modifier == Modifier::Star) + res = "(" + res + ")*"; + } else if (type == Type::Addition) { + for (auto& i: children) { + res += i->ToString(); + res += "|"; + } + if (res.size()) + res.pop_back(); + res = "(" + res + ")"; + if (modifier == Modifier::Plus) + res += "+"; + else if (modifier == Modifier::Star) + res += "*"; + } + return res; +} diff --git a/tests/DFAToMinDFA/CountSizesMinDFA.cpp b/tests/DFAToMinDFA/CountSizesMinDFA.cpp new file mode 100644 index 0000000..66a1cf5 --- /dev/null +++ b/tests/DFAToMinDFA/CountSizesMinDFA.cpp @@ -0,0 +1,40 @@ +#include +#include "regular/RegularTree.hpp" +#include "converters/RegularToNFA.hpp" +#include "converters/NFAToDFA.hpp" +#include "converters/DFAToMinDFA.hpp" +#include "converters/DFAToRegular.hpp" + +using namespace regular; +using namespace NFA; +using namespace DFA; +using namespace converters; + +size_t GetCountVertexes(const std::string& regular) { + RegularTree r(regular); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + return DFA_graph.GetReallyCountVertexes(); +} + +TEST(DFA_to_min_DFA, a_start) { + EXPECT_EQ(GetCountVertexes("a*"), GetCountVertexes("( a | )*| | | aaa a a")); +} + +TEST(DFA_to_min_DFA, a_plus) { + EXPECT_EQ(GetCountVertexes("a+"), GetCountVertexes("( a | a a )+|a | a |a ")); +} + +TEST(DFA_to_min_DFA, a_star_or_b_star) { + EXPECT_EQ(GetCountVertexes("a*|b*"), GetCountVertexes("a|b|a*|a+|b*|b+|bbb b b b| a a aa a aa | a+")); +} + +TEST(DFA_to_min_DFA, epsilon) { + EXPECT_EQ(GetCountVertexes(" "), GetCountVertexes(" | | | ")); +} + +TEST(DFA_to_min_DFA, a_or_b_star) { + EXPECT_EQ(GetCountVertexes("(a|b)*"), GetCountVertexes("a+|b+|a*|b*|a*b*a*|a|b(a|b)*|b| |(a|b)*")); +} diff --git a/tests/DFAToRegular/DFAToRegular.cpp b/tests/DFAToRegular/DFAToRegular.cpp new file mode 100644 index 0000000..e46e99f --- /dev/null +++ b/tests/DFAToRegular/DFAToRegular.cpp @@ -0,0 +1,202 @@ +#include +#include +#include "regular/RegularTree.hpp" +#include "converters/RegularToNFA.hpp" +#include "converters/NFAToDFA.hpp" +#include "converters/DFAToMinDFA.hpp" +#include "converters/DFAToRegular.hpp" + +using namespace regular; +using namespace NFA; +using namespace DFA; +using namespace converters; + +extern std::mt19937 rnd; + +extern std::string GenerateRandomString(std::vector alphabet, size_t len); + +TEST(DFA_to_regular, a_star) { + RegularTree r("a*"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); + + r = RegularTree(reg); + NFA_tree = RegularToNFAGraph(std::move(r)); + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + + std::string s = ""; + for (int i = 0; i < 100; ++i) { + ASSERT_EQ(DFA_graph.Accepted(s), true); + s += "a"; + } +} + +TEST(DFA_to_regular, a_plus) { + std::string regulars[] = {"a+", "(a)+", "(a+)"}; + for (const auto& regular: regulars) { + RegularTree r(regular); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); + + r = RegularTree(reg); + NFA_tree = RegularToNFAGraph(std::move(r)); + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string s = ""; + ASSERT_EQ(DFA_graph.Accepted(s), false); + s += "a"; + for (int i = 0; i < 100; ++i) { + ASSERT_EQ(DFA_graph.Accepted(s), true); + s += "a"; + } + } +} + +TEST(DFA_to_regular, abc) { + std::string regulars[] = {"abc"}; + for (const auto& regular: regulars) { + RegularTree r(regular); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); + + r = RegularTree(reg); + NFA_tree = RegularToNFAGraph(std::move(r)); + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + ASSERT_EQ(DFA_graph.Accepted("abc"), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + if (s == "abc") continue; + ASSERT_EQ(DFA_graph.Accepted(s), false); + } + } +} + +TEST(DFA_to_regular, a_or_b_or_c) { + std::string regulars[] = {}; + for (const auto& regular: regulars) { + RegularTree r("a|b|c"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); + + r = RegularTree(reg); + NFA_tree = RegularToNFAGraph(std::move(r)); + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + ASSERT_EQ(DFA_graph.Accepted("a"), true); + ASSERT_EQ(DFA_graph.Accepted("b"), true); + ASSERT_EQ(DFA_graph.Accepted("c"), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + if (s == "a") continue; + if (s == "b") continue; + if (s == "c") continue; + ASSERT_EQ(DFA_graph.Accepted(s), false); + } + } +} + +TEST(DFA_to_regular, a_star_or_b_star_or_c_star) { + std::string regulars[] = {"a*|b*|c*", "(a*)|(b*|c*)", "(a*|b*|c*)", "((a*)|(b*)|(c*))"}; + for (const auto& regular: regulars) { + RegularTree r(regular); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); + + r = RegularTree(reg); + NFA_tree = RegularToNFAGraph(std::move(r)); + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + auto check = [](const std::string str) { + std::set s(str.begin(), str.end()); + if (s.size() == 0) + return true; + else { + if (s.size() == 1) + return *s.begin() != 'd'; + else + return false; + } + }; + + ASSERT_EQ(DFA_graph.Accepted(""), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + + ASSERT_EQ(DFA_graph.Accepted(s), check(s)); + } + } +} + +TEST(DFA_to_regular, _a_star_or_b_star_or_c_star_plus) { + RegularTree r("(a*|b*|c*)+"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); + + r = RegularTree(reg); + NFA_tree = RegularToNFAGraph(std::move(r)); + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + auto check = [](const std::string str) { + std::set s(str.begin(), str.end()); + return s.find('d') == s.end(); + }; + + ASSERT_EQ(DFA_graph.Accepted(""), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + + ASSERT_EQ(DFA_graph.Accepted(s), check(s)); + } +} + +TEST(DFA_to_regular, _a_or_b_or_c_star_plus) { + RegularTree r("(a|b|c*)+"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); + + r = RegularTree(reg); + NFA_tree = RegularToNFAGraph(std::move(r)); + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + auto check = [](const std::string str) { + std::set s(str.begin(), str.end()); + return s.find('d') == s.end(); + }; + + ASSERT_EQ(DFA_graph.Accepted(""), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + + ASSERT_EQ(DFA_graph.Accepted(s), check(s)); + } +} diff --git a/tests/DFAToRegular/DFAToRegular2.cpp b/tests/DFAToRegular/DFAToRegular2.cpp new file mode 100644 index 0000000..17603ab --- /dev/null +++ b/tests/DFAToRegular/DFAToRegular2.cpp @@ -0,0 +1,209 @@ +#include +#include +#include "regular/RegularTree.hpp" +#include "converters/RegularToNFA.hpp" +#include "converters/NFAToDFA.hpp" +#include "converters/DFAToMinDFA.hpp" +#include "converters/DFAToRegular.hpp" + +using namespace regular; +using namespace NFA; +using namespace DFA; +using namespace converters; + +extern std::mt19937 rnd; + +extern std::string GenerateRandomString(std::vector alphabet, size_t len); + +TEST(DFA_to_regular2, a_star) { + RegularTree r("a*"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); + reg = RegularTree(reg).ToString(); + + r = RegularTree(reg); + NFA_tree = RegularToNFAGraph(std::move(r)); + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + + std::string s = ""; + for (int i = 0; i < 100; ++i) { + ASSERT_EQ(DFA_graph.Accepted(s), true); + s += "a"; + } +} + +TEST(DFA_to_regular2, a_plus) { + std::string regulars[] = {"a+", "(a)+", "(a+)"}; + for (const auto& regular: regulars) { + RegularTree r(regular); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); + reg = RegularTree(reg).ToString(); + + r = RegularTree(reg); + NFA_tree = RegularToNFAGraph(std::move(r)); + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string s = ""; + ASSERT_EQ(DFA_graph.Accepted(s), false); + s += "a"; + for (int i = 0; i < 100; ++i) { + ASSERT_EQ(DFA_graph.Accepted(s), true); + s += "a"; + } + } +} + +TEST(DFA_to_regular2, abc) { + std::string regulars[] = {"abc"}; + for (const auto& regular: regulars) { + RegularTree r(regular); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); + reg = RegularTree(reg).ToString(); + + r = RegularTree(reg); + NFA_tree = RegularToNFAGraph(std::move(r)); + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + ASSERT_EQ(DFA_graph.Accepted("abc"), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + if (s == "abc") continue; + ASSERT_EQ(DFA_graph.Accepted(s), false); + } + } +} + +TEST(DFA_to_regular2, a_or_b_or_c) { + std::string regulars[] = {}; + for (const auto& regular: regulars) { + RegularTree r("a|b|c"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); + reg = RegularTree(reg).ToString(); + + r = RegularTree(reg); + NFA_tree = RegularToNFAGraph(std::move(r)); + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + ASSERT_EQ(DFA_graph.Accepted("a"), true); + ASSERT_EQ(DFA_graph.Accepted("b"), true); + ASSERT_EQ(DFA_graph.Accepted("c"), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + if (s == "a") continue; + if (s == "b") continue; + if (s == "c") continue; + ASSERT_EQ(DFA_graph.Accepted(s), false); + } + } +} + +TEST(DFA_to_regular2, a_star_or_b_star_or_c_star) { + std::string regulars[] = {"a*|b*|c*", "(a*)|(b*|c*)", "(a*|b*|c*)", "((a*)|(b*)|(c*))"}; + for (const auto& regular: regulars) { + RegularTree r(regular); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); + reg = RegularTree(reg).ToString(); + + r = RegularTree(reg); + NFA_tree = RegularToNFAGraph(std::move(r)); + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + auto check = [](const std::string str) { + std::set s(str.begin(), str.end()); + if (s.size() == 0) + return true; + else { + if (s.size() == 1) + return *s.begin() != 'd'; + else + return false; + } + }; + + ASSERT_EQ(DFA_graph.Accepted(""), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + + ASSERT_EQ(DFA_graph.Accepted(s), check(s)); + } + } +} + +TEST(DFA_to_regular2, _a_star_or_b_star_or_c_star_plus) { + RegularTree r("(a*|b*|c*)+"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); + reg = RegularTree(reg).ToString(); + + r = RegularTree(reg); + NFA_tree = RegularToNFAGraph(std::move(r)); + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + auto check = [](const std::string str) { + std::set s(str.begin(), str.end()); + return s.find('d') == s.end(); + }; + + ASSERT_EQ(DFA_graph.Accepted(""), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + + ASSERT_EQ(DFA_graph.Accepted(s), check(s)); + } +} + +TEST(DFA_to_regular2, _a_or_b_or_c_star_plus) { + RegularTree r("(a|b|c*)+"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string reg = DFAGraphToRegular(std::move(DFA_graph)); + reg = RegularTree(reg).ToString(); + + r = RegularTree(reg); + NFA_tree = RegularToNFAGraph(std::move(r)); + DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + auto check = [](const std::string str) { + std::set s(str.begin(), str.end()); + return s.find('d') == s.end(); + }; + + ASSERT_EQ(DFA_graph.Accepted(""), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + + ASSERT_EQ(DFA_graph.Accepted(s), check(s)); + } +} diff --git a/tests/test_main.cpp b/tests/MainTest.cpp similarity index 100% rename from tests/test_main.cpp rename to tests/MainTest.cpp diff --git a/tests/NFAToDFA/CheckEquivalence.cpp b/tests/NFAToDFA/CheckEquivalence.cpp new file mode 100644 index 0000000..2b84cfa --- /dev/null +++ b/tests/NFAToDFA/CheckEquivalence.cpp @@ -0,0 +1,111 @@ +#include +#include +#include "DFA/DFAGraph.hpp" +#include "NFA/NFAGraph.hpp" +#include "converters/NFAToDFA.hpp" + +using namespace NFA; +using namespace DFA; +using namespace converters; + +std::mt19937 rnd(1337); + +std::string GenerateRandomString(std::vector alphabet, size_t len) { + std::string res(len, ' '); + for (auto& i: res) { + i = alphabet[rnd() % alphabet.size()]; + } + return res; +} + +TEST(check_equivalence_NFA_to_DFA, 1) { + NFA::NFAGraph tree; + + const int N = 10; + int a[N]; + for (int i = 0; i < N; ++i) + a[i] = tree.AddNewVertex(); + + tree.GetVertex(a[0]).AddEdge('a', a[1]); + tree.GetVertex(a[1]).AddEdge('a', a[2]); + tree.GetVertex(a[0]).AddEdge('a', a[3]); + + tree.GetVertex(a[0]).SetStart(true); + tree.GetVertex(a[2]).SetFinal(true); + tree.GetVertex(a[3]).SetFinal(true); + + DFA::DFAGraph res = converters::NFAGraphToDFAGraph(std::move(tree)); + + EXPECT_FALSE(res.Accepted("")); + EXPECT_TRUE(res.Accepted("a")); + EXPECT_TRUE(res.Accepted("aa")); + EXPECT_FALSE(res.Accepted("aaa")); + EXPECT_FALSE(res.Accepted("aaaa")); + EXPECT_FALSE(res.Accepted("aaaaa")); + EXPECT_FALSE(res.Accepted("b")); +} + +TEST(check_equivalence_NFA_to_DFA, 2) { + NFA::NFAGraph tree; + + const int N = 10; + int a[N]; + for (int i = 0; i < N; ++i) + a[i] = tree.AddNewVertex(); + + tree.GetVertex(a[0]).AddEdge('a', a[1]); + tree.GetVertex(a[0]).AddEdge('a', a[2]); + tree.GetVertex(a[1]).AddEdge('b', a[1]); + tree.GetVertex(a[2]).AddEdge('c', a[2]); + + tree.GetVertex(a[0]).SetStart(true); + tree.GetVertex(a[1]).SetFinal(true); + tree.GetVertex(a[2]).SetFinal(true); + + DFA::DFAGraph res = converters::NFAGraphToDFAGraph(std::move(tree)); + + auto check = [](const std::string& str) { + if (str.size() > 0) { + if (str[0] == 'a') { + if (str.size() == 1) { + return true; + } + if (str[1] == 'b') { + for (int i = 1; i < str.size(); ++i) { + if (str[i] != 'b') { + return false; + } + } + return true; + } + if (str[1] == 'c') { + for (int i = 1; i < str.size(); ++i) { + if (str[i] != 'c') { + return false; + } + } + return true; + } + } + } + + return false; + }; + + EXPECT_FALSE(res.Accepted("")); + EXPECT_TRUE(res.Accepted("a")); + EXPECT_FALSE(res.Accepted("aa")); + EXPECT_FALSE(res.Accepted("aaa")); + EXPECT_FALSE(res.Accepted("aaaa")); + EXPECT_FALSE(res.Accepted("aaaaa")); + EXPECT_FALSE(res.Accepted("b")); + EXPECT_TRUE(res.Accepted("ab")); + EXPECT_TRUE(res.Accepted("abb")); + EXPECT_TRUE(res.Accepted("ac")); + EXPECT_TRUE(res.Accepted("acc")); + + for (int i = 0; i < 5000; ++i) { + auto str = GenerateRandomString({'a', 'b', 'c', 'd'}, 20); + ASSERT_EQ(res.Accepted(str), check(str)); + } +} diff --git a/tests/invertFDFA/InvertFDFA.cpp b/tests/invertFDFA/InvertFDFA.cpp new file mode 100644 index 0000000..cc1efb2 --- /dev/null +++ b/tests/invertFDFA/InvertFDFA.cpp @@ -0,0 +1,181 @@ +#include +#include +#include "regular/RegularTree.hpp" +#include "converters/RegularToNFA.hpp" +#include "converters/NFAToDFA.hpp" +#include "converters/DFAToMinDFA.hpp" +#include "converters/DFAToFDFA.hpp" +#include "converters/InvertFDFA.hpp" + +using namespace regular; +using namespace NFA; +using namespace DFA; +using namespace converters; + +extern std::mt19937 rnd; + +extern std::string GenerateRandomString(std::vector alphabet, size_t len); + +TEST(invert_DFA, a_star) { + RegularTree r("a*"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + DFA_graph = DFAGraphToFDFAGraph(std::move(DFA_graph), {'a', 'b', 'c', 'd'}); + DFA_graph = InvertFDFAGraph(std::move(DFA_graph)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string s = ""; + for (int i = 0; i < 100; ++i) { + ASSERT_EQ(DFA_graph.Accepted(s), false); + s += "a"; + } +} + +TEST(invert_DFA, a_plus) { + std::string regulars[] = {"a+", "(a)+", "(a+)"}; + for (const auto& regular: regulars) { + RegularTree r(regular); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + DFA_graph = DFAGraphToFDFAGraph(std::move(DFA_graph), {'a', 'b', 'c', 'd'}); + DFA_graph = InvertFDFAGraph(std::move(DFA_graph)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string s = ""; + ASSERT_EQ(DFA_graph.Accepted(s), true); + s += "a"; + for (int i = 0; i < 100; ++i) { + ASSERT_EQ(DFA_graph.Accepted(s), false); + s += "a"; + } + } +} + +TEST(invert_DFA, abc) { + std::string regulars[] = {"abc"}; + for (const auto& regular: regulars) { + RegularTree r(regular); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + DFA_graph = DFAGraphToFDFAGraph(std::move(DFA_graph), {'a', 'b', 'c', 'd'}); + DFA_graph = InvertFDFAGraph(std::move(DFA_graph)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + ASSERT_EQ(DFA_graph.Accepted("abc"), false); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + if (s == "abc") continue; + ASSERT_EQ(DFA_graph.Accepted(s), true); + } + } +} + +TEST(invert_DFA, a_or_b_or_c) { + std::string regulars[] = {}; + for (const auto& regular: regulars) { + RegularTree r("a|b|c"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + DFA_graph = DFAGraphToFDFAGraph(std::move(DFA_graph), {'a', 'b', 'c', 'd'}); + DFA_graph = InvertFDFAGraph(std::move(DFA_graph)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + ASSERT_EQ(DFA_graph.Accepted("a"), false); + ASSERT_EQ(DFA_graph.Accepted("b"), false); + ASSERT_EQ(DFA_graph.Accepted("c"), false); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + if (s == "a") continue; + if (s == "b") continue; + if (s == "c") continue; + ASSERT_EQ(DFA_graph.Accepted(s), true); + } + } +} + +TEST(invert_DFA, a_star_or_b_star_or_c_star) { + std::string regulars[] = {"a*|b*|c*", "(a*)|(b*|c*)", "(a*|b*|c*)", "((a*)|(b*)|(c*))"}; + for (const auto& regular: regulars) { + RegularTree r(regular); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + DFA_graph = DFAGraphToFDFAGraph(std::move(DFA_graph), {'a', 'b', 'c', 'd'}); + DFA_graph = InvertFDFAGraph(std::move(DFA_graph)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + auto check = [](const std::string str) { + std::set s(str.begin(), str.end()); + if (s.size() == 0) + return true; + else { + if (s.size() == 1) + return *s.begin() != 'd'; + else + return false; + } + }; + + ASSERT_EQ(DFA_graph.Accepted(""), false); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + + ASSERT_EQ(DFA_graph.Accepted(s), !check(s)); + } + } +} + +TEST(invert_DFA, _a_star_or_b_star_or_c_star_plus) { + RegularTree r("(a*|b*|c*)+"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + DFA_graph = DFAGraphToFDFAGraph(std::move(DFA_graph), {'a', 'b', 'c', 'd'}); + DFA_graph = InvertFDFAGraph(std::move(DFA_graph)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + auto check = [](const std::string str) { + std::set s(str.begin(), str.end()); + return s.find('d') == s.end(); + }; + + ASSERT_EQ(DFA_graph.Accepted(""), false); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + + ASSERT_EQ(DFA_graph.Accepted(s), !check(s)); + } +} + +TEST(invert_DFA, _a_or_b_or_c_star_plus) { + RegularTree r("(a|b|c*)+"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + DFA_graph = DFAGraphToFDFAGraph(std::move(DFA_graph), {'a', 'b', 'c', 'd'}); + DFA_graph = InvertFDFAGraph(std::move(DFA_graph)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + auto check = [](const std::string str) { + std::set s(str.begin(), str.end()); + return s.find('d') == s.end(); + }; + + ASSERT_EQ(DFA_graph.Accepted(""), false); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + + ASSERT_EQ(DFA_graph.Accepted(s), !check(s)); + } +} diff --git a/tests/regular/ParseRegular.cpp b/tests/regular/ParseRegular.cpp new file mode 100644 index 0000000..25bf34a --- /dev/null +++ b/tests/regular/ParseRegular.cpp @@ -0,0 +1,40 @@ +#include +#include "regular/RegularTree.hpp" + +using namespace regular; + +TEST(parse_regular, only_addition) { + RegularTree("a"); + RegularTree("aa"); + RegularTree("aaa"); + RegularTree("aaaa"); + RegularTree("aaaaa"); + RegularTree("a|aaaa"); + RegularTree("a|a|a|a|a"); + RegularTree("hello|world"); + RegularTree("qe|wr|lkj|alk"); +} + +TEST(parse_regular, only_folding) { + RegularTree("(kajfkasf(aksdjf)jka(((aksjdf)K)))jakd"); + RegularTree("(kajsdfk(aksdjf)kajsdf)kjasdkfja(skdjf(((aksjdkadf)ksjf(kdja))))"); + RegularTree("((((kdjf))))"); + RegularTree("kasjf(akjsfkjasdg)kajsdg"); + RegularTree("akjsdf(akjdf(kdjfak(jkasdf)))"); + RegularTree("123k4j1k351kk21jkj21k6j2k36j1(((((ajkfajsdfkafdalkdjflk)))))"); +} + +TEST(parse_regular, only_modifiers) { + RegularTree("jlakjdf*aksdjflaf8*laksfj*lakjsf*alksjdf"); + RegularTree("jlakjdf*aksdjflaf8+laksfj*lakjsf*alksjdf*"); + RegularTree("jlakjdf*aksdjflaf8*laksfj*lakjsf+alksjdf"); + RegularTree("jlakjd+aksdjflaf8*laksfj*lakjsf*alksjdf+"); +} + +TEST(parse_regular, all_operations) { + RegularTree("(alkjdfaksdf*|lkasdj*|(kasjdf|kajdf*|kjd)*|laksjf*)+"); + RegularTree("(alkjdfaksdf|lkasdj*|(kas+jdf|kajdf*|kjd)*|laksjf*)+"); + RegularTree("a|(a|(a|(a|)*))*"); + RegularTree("kj*|kjadf*|(kajsdf|(kajsd|kjadf|(kasjdf)|kajs)*)*"); +} + diff --git a/tests/regularToDFA/RegularToDFA.cpp b/tests/regularToDFA/RegularToDFA.cpp new file mode 100644 index 0000000..6087923 --- /dev/null +++ b/tests/regularToDFA/RegularToDFA.cpp @@ -0,0 +1,151 @@ +#include +#include +#include "regular/RegularTree.hpp" +#include "converters/RegularToNFA.hpp" +#include "converters/NFAToDFA.hpp" +#include "converters/DFAToMinDFA.hpp" + +using namespace regular; +using namespace NFA; +using namespace DFA; +using namespace converters; + +extern std::mt19937 rnd; + +extern std::string GenerateRandomString(std::vector alphabet, size_t len); + +TEST(regular_to_DFA, a_star) { + RegularTree r("a*"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string s = ""; + for (int i = 0; i < 100; ++i) { + ASSERT_EQ(DFA_graph.Accepted(s), true); + s += "a"; + } +} + +TEST(regular_to_DFA, a_plus) { + std::string regulars[] = {"a+", "(a)+", "(a+)"}; + for (const auto& regular: regulars) { + RegularTree r(regular); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + std::string s = ""; + ASSERT_EQ(DFA_graph.Accepted(s), false); + s += "a"; + for (int i = 0; i < 100; ++i) { + ASSERT_EQ(DFA_graph.Accepted(s), true); + s += "a"; + } + } +} + +TEST(regular_to_DFA, abc) { + std::string regulars[] = {"abc"}; + for (const auto& regular: regulars) { + RegularTree r(regular); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + ASSERT_EQ(DFA_graph.Accepted("abc"), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + if (s == "abc") continue; + ASSERT_EQ(DFA_graph.Accepted(s), false); + } + } +} + +TEST(regular_to_DFA, a_or_b_or_c) { + std::string regulars[] = {}; + for (const auto& regular: regulars) { + RegularTree r("a|b|c"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + ASSERT_EQ(DFA_graph.Accepted("a"), true); + ASSERT_EQ(DFA_graph.Accepted("b"), true); + ASSERT_EQ(DFA_graph.Accepted("c"), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + if (s == "a") continue; + if (s == "b") continue; + if (s == "c") continue; + ASSERT_EQ(DFA_graph.Accepted(s), false); + } + } +} + +TEST(regular_to_DFA, a_star_or_b_star_or_c_star) { + std::string regulars[] = {"a*|b*|c*", "(a*)|(b*|c*)", "(a*|b*|c*)", "((a*)|(b*)|(c*))"}; + for (const auto& regular: regulars) { + RegularTree r(regular); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + auto check = [](const std::string str) { + std::set s(str.begin(), str.end()); + if (s.size() == 0) + return true; + else { + if (s.size() == 1) + return *s.begin() != 'd'; + else + return false; + } + }; + + ASSERT_EQ(DFA_graph.Accepted(""), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + + ASSERT_EQ(DFA_graph.Accepted(s), check(s)); + } + } +} + +TEST(regular_to_DFA, _a_star_or_b_star_or_c_star_plus) { + RegularTree r("(a*|b*|c*)+"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + auto check = [](const std::string str) { + std::set s(str.begin(), str.end()); + return s.find('d') == s.end(); + }; + + ASSERT_EQ(DFA_graph.Accepted(""), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + + ASSERT_EQ(DFA_graph.Accepted(s), check(s)); + } +} + +TEST(regular_to_DFA, _a_or_b_or_c_star_plus) { + RegularTree r("(a|b|c*)+"); + NFAGraph NFA_tree = RegularToNFAGraph(std::move(r)); + DFAGraph DFA_graph = NFAGraphToDFAGraph(std::move(NFA_tree)); + DFA_graph = DFAGraphToMinDFAGraph(std::move(DFA_graph)); + + auto check = [](const std::string str) { + std::set s(str.begin(), str.end()); + return s.find('d') == s.end(); + }; + + ASSERT_EQ(DFA_graph.Accepted(""), true); + for (int i = 0; i < 1000; ++i) { + auto s = GenerateRandomString({'a', 'b', 'c', 'd'}, rnd() % 10); + + ASSERT_EQ(DFA_graph.Accepted(s), check(s)); + } +}