diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e2c4cf..da7e51d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ include(External/glfw.cmake) include(External/gtest.cmake) include(External/imgui.cmake) include(External/json.cmake) +include(External/peglib.cmake) include_directories(include) diff --git a/External/peglib.cmake b/External/peglib.cmake new file mode 100644 index 0000000..c2a6842 --- /dev/null +++ b/External/peglib.cmake @@ -0,0 +1,16 @@ +FetchContent_Declare( + peglib + GIT_REPOSITORY https://github.com/yhirose/cpp-peglib + GIT_TAG v1.8.2 + GIT_PROGRESS TRUE +) +set(PEGLIB_BUILD_TESTS OFF CACHE INTERNAL "") +FetchContent_GetProperties(peglib) +if(NOT ${peglib}_POPULATED) + FetchContent_Populate(peglib) + find_package(Threads) + add_library(peglib INTERFACE ${peglib_SOURCE_DIR}/peglib.h) + target_include_directories(peglib INTERFACE ${peglib_SOURCE_DIR}) + target_link_libraries(peglib INTERFACE Threads::Threads) + add_library(peglib::peglib ALIAS peglib) +endif() diff --git a/tc/CMakeLists.txt b/tc/CMakeLists.txt index fa3e134..63812a6 100644 --- a/tc/CMakeLists.txt +++ b/tc/CMakeLists.txt @@ -1,14 +1,25 @@ add_library(tc - include/tc/pair_map.hpp include/tc/core.hpp include/tc/cosets.hpp include/tc/group.hpp include/tc/groups.hpp + include/tc/lang.hpp + include/tc/pair_map.hpp include/tc/util.hpp src/core.cpp - src/groups.cpp) - + src/groups.cpp + src/lang.cpp + ) +target_link_libraries(tc PUBLIC peglib::peglib fmt::fmt) target_include_directories(tc PUBLIC include) +add_library(tc::tc ALIAS tc) + add_subdirectory(test) add_subdirectory(bench) + +add_executable(langtest src/langtest.cpp) +target_link_libraries(langtest PUBLIC tc::tc) + +add_executable(pegtest src/pegtest.cpp) +target_link_libraries(pegtest PUBLIC peglib::peglib fmt::fmt) diff --git a/tc/bench/benchmark.cpp b/tc/bench/benchmark.cpp index 83c0584..f1a5005 100644 --- a/tc/bench/benchmark.cpp +++ b/tc/bench/benchmark.cpp @@ -8,14 +8,14 @@ #include #include -#define NAMED(x) #x, x - void bench( - std::string group_expr, - const tc::Group &group, + const std::string &group_expr, + const std::string &symbol, const std::vector &gens, const tc::Coset bound = tc::UNBOUNDED ) { + tc::Group group = tc::coxeter(symbol); + std::clock_t s = std::clock(); tc::Cosets cosets = tc::solve(group, gens, bound); std::clock_t e = std::clock(); @@ -43,29 +43,27 @@ tc::Group sch(T ...arg) { int main(int argc, char *argv[]) { std::vector args(argv + 1, argv + argc); - using namespace tc::group; - fmt::print("{:>24},{:>10},{:>6},{:>9},{:>10}\n", "NAME", "ORDER", "COMPL", "TIME", "COS/S"); - bench(NAMED(H(2)), {}); - bench(NAMED(H(3)), {}); - bench(NAMED(H(4)), {}); + bench("H2", "5", {}); + bench("H3", "5 3", {}); + bench("H4", "5 3 3", {}); - bench(NAMED(T(100)), {}); - bench(NAMED(T(500)), {}); - bench(NAMED(T(1000)), {}); + bench("T100", "100 2 100", {}); + bench("T500", "500 2 500", {}); + bench("T1000", "1000 2 1000", {}); - bench(NAMED(E(6)), {}); - bench(NAMED(E(7)), {}); + bench("E6", "3 * [2 2 1]", {}); + bench("E7", "3 * [3 2 1]", {}); +// bench("E8", "3 * [4 2 1]", {}); // too big - bench(NAMED(B(6)), {}); - bench(NAMED(B(7)), {}); - bench(NAMED(B(8)), {}); + bench("B6", "4 3 * 4", {}); + bench("B7", "4 3 * 5", {}); + bench("B8", "4 3 * 6", {}); - auto g = tc::group::A(4); - g.set(tc::Rel{0, 3, 3}); - bench("~A(3)", g, {}, 4385964); - bench("~I(1)", sch(tc::FREE), {}, 4385964); + bench("~A3", "{3 * 4}", {}, 5000000); + bench("~A4", "{3 * 5}", {}, 5000000); + bench("~I1", "{- * 5}", {}, 5000000); return EXIT_SUCCESS; } diff --git a/tc/include/tc/core.hpp b/tc/include/tc/core.hpp index e65d4fe..89ab80b 100644 --- a/tc/include/tc/core.hpp +++ b/tc/include/tc/core.hpp @@ -10,7 +10,5 @@ #include "group.hpp" namespace tc { - constexpr Coset UNBOUNDED = (Coset) (-1); - Cosets solve(const Group &group, const std::vector &sub_gens, const Coset &bound = UNBOUNDED); } diff --git a/tc/include/tc/cosets.hpp b/tc/include/tc/cosets.hpp index 541447d..26897dd 100644 --- a/tc/include/tc/cosets.hpp +++ b/tc/include/tc/cosets.hpp @@ -2,17 +2,9 @@ #include -#include "cosets.hpp" +#include "util.hpp" namespace tc { - using Coset = uint32_t; - using Gen = uint8_t; - using Mult = uint16_t; - constexpr Mult FREE = Mult(-1); - constexpr Coset UNSET = Coset(-1); - - using Rel = std::tuple; - struct Cosets { Gen ngens; std::vector data; diff --git a/tc/include/tc/group.hpp b/tc/include/tc/group.hpp index 49111cf..692759e 100644 --- a/tc/include/tc/group.hpp +++ b/tc/include/tc/group.hpp @@ -1,9 +1,10 @@ #pragma once +#include #include +#include #include -#include namespace tc { struct Group; diff --git a/tc/include/tc/groups.hpp b/tc/include/tc/groups.hpp index 436469b..d931af8 100644 --- a/tc/include/tc/groups.hpp +++ b/tc/include/tc/groups.hpp @@ -1,7 +1,8 @@ #pragma once -#include "core.hpp" +#include +#include namespace tc { /** @@ -10,55 +11,13 @@ namespace tc { */ Group schlafli(const std::vector &mults); - namespace group { - /** - * Simplex - */ - Group A(int dim); + Group coxeter(const std::string &symbol); - /** - * Cube, Orthoplex - */ - Group B(int dim); + Group vcoxeter(const std::string &symbol, std::vector &values); - /** - * Demicube, Orthoplex - */ - Group D(int dim); - - /** - * E groups - */ - Group E(int dim); - - /** - * 24 Cell - */ - Group F4(); - - /** - * Hexagon - */ - Group G2(); - - /** - * Icosahedron - */ - Group H(int dim); - - /** - * Polygonal - */ - Group I2(int n); - - /** - * Toroidal. I2(n) * I2(m) - */ - Group T(int n, int m); - - /** - * Toroidal. T(n, n) - */ - Group T(int n); + template + Group coxeter(const std::string &symbol, const Args &... args) { + std::vector values = {{args...}}; + return vcoxeter(symbol, values); } } diff --git a/tc/include/tc/util.hpp b/tc/include/tc/util.hpp index 51f2df5..f42bf1e 100644 --- a/tc/include/tc/util.hpp +++ b/tc/include/tc/util.hpp @@ -6,6 +6,17 @@ #include namespace tc { + using Gen = uint8_t; + using Mult = uint16_t; + + constexpr Mult FREE = Mult(-1); + + using Coset = uint32_t; + constexpr Coset UNSET = Coset(-1); + constexpr Coset UNBOUNDED = (Coset) (-1); + + using Rel = std::tuple; + struct Action { int from_idx = -1; int gen = -1; diff --git a/tc/src/groups.cpp b/tc/src/groups.cpp index 30b47d2..e063867 100644 --- a/tc/src/groups.cpp +++ b/tc/src/groups.cpp @@ -1,6 +1,7 @@ -#include +#include -#include "tc/groups.hpp" +#include +#include namespace tc { Group schlafli(const std::vector &mults) { @@ -11,74 +12,13 @@ namespace tc { return res; } - namespace group { - Group A(const int dim) { - assert(dim >= 1); - - return schlafli(std::vector(dim - 1, 3)); + Group v_coxeter(const std::string &symbol, std::vector &values) { + fmt::dynamic_format_arg_store ds; + + for (const auto &value: values) { + ds.push_back(value); } - Group B(const int dim) { - assert(dim >= 2); - - std::vector mults(dim - 1, 3); - mults[0] = 4; - - return schlafli(mults); - } - - Group D(const int dim) { - assert(dim >= 4); - - std::vector mults(dim - 1, 3); - mults[dim - 2] = 2; - - Group g = schlafli(mults); - g.set(Rel{1, dim - 1, 3}); - - return g; - } - - Group E(const int dim) { - assert(dim >= 6); - - std::vector mults(dim - 1, 3); - mults[dim - 2] = 2; - - Group g = schlafli(mults); - g.set(Rel{2, dim - 1, 3}); - - return g; - } - - Group F4() { - return schlafli({3, 4, 3}); - } - - Group G2() { - return schlafli({6}); - } - - Group H(const int dim) { - assert(dim >= 2); - assert(dim <= 4); - - std::vector mults(dim - 1, 3); - mults[0] = 5; - - return schlafli(mults); - } - - Group I2(const int n) { - return schlafli({n}); - } - - Group T(const int n, const int m) { - return schlafli({n, 2, m}); - } - - Group T(const int n) { - return T(n, n); - } + return coxeter(fmt::vformat(symbol, ds)); } } diff --git a/tc/src/keep.cpp b/tc/src/keep.cpp deleted file mode 100644 index 4020e10..0000000 --- a/tc/src/keep.cpp +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/tc/src/lang.cpp b/tc/src/lang.cpp new file mode 100644 index 0000000..3ab5404 --- /dev/null +++ b/tc/src/lang.cpp @@ -0,0 +1,289 @@ +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +struct Op { + enum Code { + LINK, + PUSH, + POP, + LOOP, + }; + + Code code: 4; + unsigned int value: 12; + + explicit Op(Code code, unsigned int value = 0) + : code(code), value(value) {} +}; + +template<> +struct fmt::formatter { + template + constexpr auto parse(ParseContext &ctx) { + return ctx.begin(); + } + + template + auto format(const Op &op, FormatContext &ctx) { + switch (op.code) { + case Op::LINK: + return fmt::format_to(ctx.out(), "link({})", + (unsigned int) op.value); + case Op::PUSH: + return fmt::format_to(ctx.out(), "push()"); + case Op::POP: + return fmt::format_to(ctx.out(), "pop()"); + case Op::LOOP: + return fmt::format_to(ctx.out(), "loop()"); + default: + return fmt::format_to(ctx.out(), "[{}]({})", + (unsigned int) op.code, + (unsigned int) op.value); + } + } +}; + +struct Factor { + unsigned int mode; + std::vector orders; +}; + +struct codegen { + std::vector ops; + + void link(unsigned int order) { + ops.emplace_back(Op::LINK, order); + } + + void push() { + ops.emplace_back(Op::PUSH); + } + + void pop() { + ops.emplace_back(Op::POP); + } + + void loop() { + ops.emplace_back(Op::LOOP); + } + + template + void insert(It begin, It end) { + std::copy(begin, end, std::back_inserter(ops)); + } + + void insert(const codegen &o) { + insert(o.ops.begin(), o.ops.end()); + } +}; + +static const std::string GRAMMAR = R"( + root <- term+ + term <- product / op + op <- block / link + block <- '(' root ')' / '{' root '}' / '[' root ']' + link <- int + product <- op '*' factor + factor <- int / '{' int+ '}' / '[' int+ ']' + int <- < [0-9]+ > + + %whitespace <- [ \t\n\r]* +)"; + +peg::parser build_parser() { + peg::parser parser; + parser.set_logger([](size_t line, size_t col, const std::string &msg, const std::string &rule) { + fmt::print(stderr, "{}:{} [{}] {}\n", line, col, rule, msg); + }); + auto ok = parser.load_grammar(GRAMMAR); + assert(ok); + + parser["int"] = [](const peg::SemanticValues &vs) -> std::any { + return vs.token_to_number(); + }; + + parser["link"] = [](const peg::SemanticValues &vs) -> std::any { + codegen cg; + + auto order = std::any_cast(vs[0]); + cg.link(order); + + return cg; + }; + + parser["root"] = [](const peg::SemanticValues &vs) -> std::any { + codegen cg; + + for (const auto &sub: vs) { + cg.insert(std::any_cast(sub)); + } + + return cg; + }; + + parser["block"] = [](const peg::SemanticValues &vs) -> std::any { + if (vs.choice() == 0) return vs[0]; + + codegen cg; + + cg.push(); + cg.insert(std::any_cast(vs[0])); + if (vs.choice() == 1) cg.loop(); + cg.pop(); + + return cg; + }; + + parser["factor"] = [](const peg::SemanticValues &vs) -> std::any { + return Factor{ + (unsigned int) vs.choice(), + vs.transform(), + }; + }; + + parser["product"] = [](const peg::SemanticValues &vs) -> std::any { + auto sub = std::any_cast(vs[0]); + auto fac = std::any_cast(vs[1]); + + codegen cg; + + for (const auto &order: fac.orders) { + if (fac.mode == 0) { + for (unsigned int i = 0; i < order; ++i) { + cg.insert(sub); + } + } else { + cg.push(); + for (unsigned int i = 0; i < order; ++i) { + cg.insert(sub); + } + if (fac.mode == 1) cg.loop(); + cg.pop(); + } + } + + return cg; + }; + + return parser; +} + +#ifndef NDEBUG +peg::parser build_ast_parser() { + peg::parser parser; + parser.set_logger([](size_t line, size_t col, const std::string &msg, const std::string &rule) { + fmt::print(stderr, "{}:{} [{}] {}\n", line, col, rule, msg); + }); + auto ok = parser.load_grammar(GRAMMAR); + assert(ok); + + parser.enable_ast(); + + return parser; +} +#endif + +std::vector compile(const std::string &source) { +#ifndef NDEBUG + static peg::parser ast_parser = build_ast_parser(); + std::shared_ptr ast; + bool ast_ok = ast_parser.parse(source, ast); + assert(ast_ok); + std::cout << peg::ast_to_s(ast) << std::endl; +#endif + + static peg::parser parser = build_parser(); + codegen cg; + bool ok = parser.parse(source, cg); + assert(ok); + return cg.ops; +} + +struct Graph { + size_t rank{}; + std::vector> edges{}; +}; + +Graph eval(const std::vector &ops) { + std::vector> stacks(1); + + Graph g; + stacks.back().emplace(g.rank++); + + for (const auto &op: ops) { + switch (op.code) { + case Op::LINK: { + auto order = op.value; + + auto top = stacks.back().top(); + auto curr = g.rank++; + + stacks.back().emplace(curr); + g.edges.emplace_back(top, curr, order); + + break; + } + case Op::PUSH: { + stacks.emplace_back(); + + auto ptop = stacks[stacks.size() - 2].top(); + stacks.back().emplace(ptop); + + break; + } + case Op::POP: { + stacks.pop_back(); + + break; + } + case Op::LOOP: { + g.rank--; + + auto ptop = stacks[stacks.size() - 2].top(); + auto &[top, _, order] = g.edges.back(); + + stacks.back().pop(); + g.edges.pop_back(); + + g.edges.emplace_back(top, ptop, order); + + break; + } + default: + throw std::runtime_error("Invalid opcode"); + } + } + + return g; +} + +namespace tc { + Group coxeter(const std::string &symbol) { + auto ops = compile(symbol); + +// fmt::print("#ops: {}\n", ops.size()); +// for (const auto &op: ops) { +// fmt::print(" {}\n", op); +// } + + auto diagram = eval(ops); + + Group res((int) diagram.rank); + + for (const auto &[i, j, order]: diagram.edges) { + res.set({i, j, order}); + } + + return res; + } +} diff --git a/tc/src/langtest.cpp b/tc/src/langtest.cpp new file mode 100644 index 0000000..1c3a58e --- /dev/null +++ b/tc/src/langtest.cpp @@ -0,0 +1,39 @@ +#include + +#include +#include + +int main() { + std::vector symbols = { + "5 3 3", + "5 (3 3)", + "[5 3 3]", + "[4 3 [3 5] 3]", + "{3 4 5 6 7 8 9}", + "3 {3 3 [4] 3} 5", + "5 * 3", + "5 * [3]", + "5 * {2 3}", + "5 * [3 2]", + "(5 2) * [3 2]", + "4 [3 * [2 3]] 5", + "{3 * 3} [4] [5]", + }; + + for (const auto &symbol: symbols) { + auto group = tc::coxeter(symbol); + + std::cout << "'" << symbol << "'" << std::endl; + + for (int i = 0; i < group.ngens; ++i) { + for (int j = 0; j < group.ngens; ++j) { + std::cout << group.get(i, j) << " "; + } + std::cout << std::endl; + } + + std::cout << "===========================" << std::endl; + } + + return EXIT_SUCCESS; +} diff --git a/tc/src/pegtest.cpp b/tc/src/pegtest.cpp new file mode 100644 index 0000000..43a2805 --- /dev/null +++ b/tc/src/pegtest.cpp @@ -0,0 +1,280 @@ +#include +#include +#include + +#include +#include + +#include + +struct Op { + enum Code { + LINK, + PUSH, + POP, + LOOP, + }; + + Code code: 4; + unsigned int value: 12; + + explicit Op(Code code, unsigned int value = 0) + : code(code), value(value) {} +}; + +template<> +struct fmt::formatter { + template + constexpr auto parse(ParseContext &ctx) { + return ctx.begin(); + } + + template + auto format(const Op &op, FormatContext &ctx) { + switch (op.code) { + case Op::LINK: + return fmt::format_to(ctx.out(), "link({})", + (unsigned int) op.value); + case Op::PUSH: + return fmt::format_to(ctx.out(), "push()"); + case Op::POP: + return fmt::format_to(ctx.out(), "pop()"); + case Op::LOOP: + return fmt::format_to(ctx.out(), "loop()"); + default: + return fmt::format_to(ctx.out(), "[{}]({})", + (unsigned int) op.code, + (unsigned int) op.value); + } + } +}; + +struct Factor { + unsigned int mode; + std::vector orders; +}; + +struct codegen { + std::vector ops; + + void link(unsigned int order) { + ops.emplace_back(Op::LINK, order); + } + + void push() { + ops.emplace_back(Op::PUSH); + } + + void pop() { + ops.emplace_back(Op::POP); + } + + void loop() { + ops.emplace_back(Op::LOOP); + } + + template + void insert(It begin, It end) { + std::copy(begin, end, std::back_inserter(ops)); + } + + void insert(const codegen &o) { + insert(o.ops.begin(), o.ops.end()); + } +}; + +peg::parser build_parser() { + const auto grammar = R"( + root <- term+ + term <- product / op + op <- block / link + block <- '(' root ')' / '{' root '}' / '[' root ']' + link <- int + product <- op '*' factor + factor <- int / '{' int+ '}' / '[' int+ ']' + int <- < [0-9]+ > + + %whitespace <- [ \t\n\r]* + )"; + + peg::parser parser; + parser.set_logger([](size_t line, size_t col, const std::string &msg, const std::string &rule) { + fmt::print(stderr, "{}:{} [{}] {}\n", line, col, rule, msg); + }); + auto ok = parser.load_grammar(grammar); + assert(ok); + + parser["int"] = [](const peg::SemanticValues &vs) -> std::any { + return vs.token_to_number(); + }; + + parser["link"] = [](const peg::SemanticValues &vs) -> std::any { + codegen cg; + + auto order = std::any_cast(vs[0]); + cg.link(order); + + return cg; + }; + + parser["root"] = [](const peg::SemanticValues &vs) -> std::any { + codegen cg; + + for (const auto &sub: vs) { + cg.insert(std::any_cast(sub)); + } + + return cg; + }; + + parser["block"] = [](const peg::SemanticValues &vs) -> std::any { + if (vs.choice() == 0) return vs[0]; + + codegen cg; + + cg.push(); + cg.insert(std::any_cast(vs[0])); + if (vs.choice() == 1) cg.loop(); + cg.pop(); + + return cg; + }; + + parser["factor"] = [](const peg::SemanticValues &vs) -> std::any { + return Factor{ + (unsigned int) vs.choice(), + vs.transform(), + }; + }; + + parser["product"] = [](const peg::SemanticValues &vs) -> std::any { + auto sub = std::any_cast(vs[0]); + auto fac = std::any_cast(vs[1]); + + codegen cg; + + for (const auto &order: fac.orders) { + if (fac.mode == 0) { + for (unsigned int i = 0; i < order; ++i) { + cg.insert(sub); + } + } else { + cg.push(); + for (unsigned int i = 0; i < order; ++i) { + cg.insert(sub); + } + if (fac.mode == 1) cg.loop(); + cg.pop(); + } + } + + return cg; + }; + + return parser; +} + +std::vector compile(const std::string &source) { + static peg::parser parser = build_parser(); + + std::any val; + codegen cg; + + bool success = parser.parse(source, cg); + assert(success); + + return cg.ops; +} + +struct Graph { + size_t rank{}; + std::vector> edges{}; +}; + +Graph eval(const std::vector &ops) { + std::vector> stacks(1); + + Graph g; + stacks.back().emplace(g.rank++); + + for (const auto &op: ops) { + switch (op.code) { + case Op::LINK: { + auto order = op.value; + + auto top = stacks.back().top(); + auto curr = g.rank++; + + stacks.back().emplace(curr); + g.edges.emplace_back(top, curr, order); + + break; + } + case Op::PUSH: { + stacks.emplace_back(); + + auto ptop = stacks[stacks.size() - 2].top(); + stacks.back().emplace(ptop); + + break; + } + case Op::POP: { + stacks.pop_back(); + + break; + } + case Op::LOOP: { + g.rank--; + + auto ptop = stacks[stacks.size() - 2].top(); + auto &[top, _, order] = g.edges.back(); + + stacks.back().pop(); + g.edges.pop_back(); + + g.edges.emplace_back(top, ptop, order); + + break; + } + default: + throw std::runtime_error("Invalid opcode"); + } + } + + return g; +} + +int main() { + std::vector sources = { + "5 3 3", + "5 (3 3)", + "[5 3 3]", + "[4 3 [3 5] 3]", + "{3 4 3 5}", + "3 {3 3 [4] 3} 5", + "5 * 3", + "5 * [3]", + "5 * {2 3}", + "5 * [3 2]", + "(5 2) * [3 2]", + "4 [3 * [2 3]] 5", + "{3 * 3} [4] [5]", + }; + + for (const auto &source: sources) { + fmt::print("source: '{}'\n", source); + + auto ops = compile(source); + for (const auto &op: ops) { + fmt::print("- {}\n", op); + } + auto g = eval(ops); + fmt::print("rank: {}\n", g.rank); + for (const auto &edge: g.edges) { + fmt::print("- {}\n", edge); + } + fmt::print("=======================================\n"); + } + + return 0; +}