diff --git a/CMakeLists.txt b/CMakeLists.txt index 32c5cdc..0b06114 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,4 +18,4 @@ include(embed.cmake) enable_testing() add_subdirectory(tc) -add_subdirectory(vis) +#add_subdirectory(vis) diff --git a/tc/CMakeLists.txt b/tc/CMakeLists.txt index aed8e25..5d632eb 100644 --- a/tc/CMakeLists.txt +++ b/tc/CMakeLists.txt @@ -1,13 +1,13 @@ add_library(tc include/tc/core.hpp - include/tc/cosets.hpp - include/tc/group.hpp include/tc/groups.hpp - include/tc/pair_map.hpp include/tc/util.hpp - src/core.cpp + + src/cosets.cpp + src/group.cpp src/groups.cpp src/lang.cpp + src/solve.cpp ) target_link_libraries(tc peglib::peglib fmt::fmt) target_include_directories(tc PUBLIC include) diff --git a/tc/bench/CMakeLists.txt b/tc/bench/CMakeLists.txt index c12a5b7..b263d6f 100644 --- a/tc/bench/CMakeLists.txt +++ b/tc/bench/CMakeLists.txt @@ -1,2 +1,5 @@ add_executable(benchmark benchmark.cpp) target_link_libraries(benchmark PUBLIC tc fmt::fmt) + +add_executable(named named.cpp) +target_link_libraries(named PUBLIC tc fmt::fmt) diff --git a/tc/bench/benchmark.cpp b/tc/bench/benchmark.cpp index 9149138..fab0c19 100644 --- a/tc/bench/benchmark.cpp +++ b/tc/bench/benchmark.cpp @@ -11,20 +11,20 @@ void bench( const std::string &group_expr, const std::string &symbol, - const std::vector &gens, - const tc::Coset bound = tc::UNBOUNDED + const std::vector &gens, + const size_t bound = SIZE_MAX ) { - tc::Group group = tc::coxeter(symbol); + tc::Group<> group = tc::coxeter(symbol); std::clock_t s = std::clock(); - tc::Cosets cosets = tc::solve(group, gens, bound); + tc::Cosets<> cosets = group.solve(gens, bound); std::clock_t e = std::clock(); auto time = (double) (e - s) / CLOCKS_PER_SEC; - tc::Coset order = cosets.size(); + size_t order = cosets.order(); auto cos_s = (size_t) (order / time); - bool complete = cosets.complete; + bool complete = cosets.complete(); std::string name = fmt::format("{}/{}", group_expr, gens); std::string row = fmt::format( @@ -80,59 +80,59 @@ int main(int argc, char *argv[]) { // Affine Groups // ~A_n: {3 * `n+1`} - bench("~A_5", "{3 * 6}", {}, 1000000); - bench("~A_6", "{3 * 7}", {}, 1000000); - bench("~A_7", "{3 * 8}", {}, 1000000); - bench("~A_8", "{3 * 9}", {}, 1000000); + bench("~A_5", "{3 * 6}", {}, 10'000'000); + bench("~A_6", "{3 * 7}", {}, 10'000'000); + bench("~A_7", "{3 * 8}", {}, 10'000'000); + bench("~A_8", "{3 * 9}", {}, 10'000'000); // ~B_n: 4 3 * `n-3` 3 * [1 1] - bench("~B_5", "4 3 * 2 3 * [1 1]", {}, 1000000); - bench("~B_6", "4 3 * 3 3 * [1 1]", {}, 1000000); - bench("~B_7", "4 3 * 4 3 * [1 1]", {}, 1000000); - bench("~B_8", "4 3 * 5 3 * [1 1]", {}, 1000000); + bench("~B_5", "4 3 * 2 3 * [1 1]", {}, 10'000'000); + bench("~B_6", "4 3 * 3 3 * [1 1]", {}, 10'000'000); + bench("~B_7", "4 3 * 4 3 * [1 1]", {}, 10'000'000); + bench("~B_8", "4 3 * 5 3 * [1 1]", {}, 10'000'000); // ~B_n: 4 3 * `n-2` 4 - bench("~C_5", "4 3 * 3 4", {}, 1000000); - bench("~C_6", "4 3 * 4 4", {}, 1000000); - bench("~C_7", "4 3 * 5 4", {}, 1000000); - bench("~C_8", "4 3 * 6 4", {}, 1000000); + bench("~C_5", "4 3 * 3 4", {}, 10'000'000); + bench("~C_6", "4 3 * 4 4", {}, 10'000'000); + bench("~C_7", "4 3 * 5 4", {}, 10'000'000); + bench("~C_8", "4 3 * 6 4", {}, 10'000'000); // ~D_n: 3 * [1 1] 3 * `n-4` 3 * [1 1] - bench("~D_5", "3 * [1 1] 3 * 1 3 * [1 1]", {}, 1000000); - bench("~D_6", "3 * [1 1] 3 * 2 3 * [1 1]", {}, 1000000); - bench("~D_7", "3 * [1 1] 3 * 3 3 * [1 1]", {}, 1000000); - bench("~D_8", "3 * [1 1] 3 * 4 3 * [1 1]", {}, 1000000); + bench("~D_5", "3 * [1 1] 3 * 1 3 * [1 1]", {}, 10'000'000); + bench("~D_6", "3 * [1 1] 3 * 2 3 * [1 1]", {}, 10'000'000); + bench("~D_7", "3 * [1 1] 3 * 3 3 * [1 1]", {}, 10'000'000); + bench("~D_8", "3 * [1 1] 3 * 4 3 * [1 1]", {}, 10'000'000); // grid: `p` `q` ; 2(p+q) = pq // triangle: `p` `q` `r` ; 1/p + 1/q + 1/r = 1 // Special Affine Groups - bench("~I_1", "-", {}, 1000000); - bench("~E_6", "3 * [2 2 2]", {}, 1000000); - bench("~E_7", "3 * [1 3 3]", {}, 1000000); - bench("~E_8", "3 * [1 2 5]", {}, 1000000); -// bench("E_9", "3 * [1 2 5]", {}, 1000000); // ~E_8 == E_9 - bench("~F_4", "3 4 3 3", {}, 1000000); - bench("~G_2", "6 3", {}, 1000000); + bench("~I_1", "-", {}, 10'000'000); + bench("~E_6", "3 * [2 2 2]", {}, 10'000'000); + bench("~E_7", "3 * [1 3 3]", {}, 10'000'000); + bench("~E_8", "3 * [1 2 5]", {}, 10'000'000); +// bench("E_9", "3 * [1 2 5]", {}, 10'000'000); // ~E_8 == E_9 + bench("~F_4", "3 4 3 3", {}, 10'000'000); + bench("~G_2", "6 3", {}, 10'000'000); // Hyperbolic Groups // grid: `p` `q` ; 2(p+q) < pq // triangle: `p` `q` `r` ; 1/p + 1/q + 1/r < 1 // Special Hyperbolic Groups - bench("-BH_3", "4 3 5", {}, 1000000); - bench("-K_3", "5 3 5", {}, 1000000); - bench("-J_3", "3 5 3", {}, 1000000); -// bench("~H_3", "3 5 3", {}, 1000000); // -J_3 == ~H_3 - bench("-DH_3", "5 3 * [1 1]", {}, 1000000); - bench("^AB_3", "{3 3 3 4}", {}, 1000000); - bench("^AH_3", "{3 3 3 5}", {}, 1000000); - bench("^BB_3", "{3 4 3 4}", {}, 1000000); - bench("^BH_3", "{3 4 3 5}", {}, 1000000); - bench("^HH_3", "{3 5 3 5}", {}, 1000000); - bench("-H_4", "5 3 3 3", {}, 1000000); -// bench("~H_4", "5 3 3 3", {}, 1000000); // -H_4 == ~H_4 == H_5 -// bench("H_5", "5 3 3 3", {}, 1000000); - bench("-BH_4", "4 3 3 5", {}, 1000000); - bench("-K_4", "5 3 3 5", {}, 1000000); - bench("-DH_4", "5 3 3 * [1 1]", {}, 1000000); - bench("^AF_4", "{3 3 3 3 4}", {}, 1000000); + bench("-BH_3", "4 3 5", {}, 10'000'000); + bench("-K_3", "5 3 5", {}, 10'000'000); + bench("-J_3", "3 5 3", {}, 10'000'000); +// bench("~H_3", "3 5 3", {}, 10'000'000); // -J_3 == ~H_3 + bench("-DH_3", "5 3 * [1 1]", {}, 10'000'000); + bench("^AB_3", "{3 3 3 4}", {}, 10'000'000); + bench("^AH_3", "{3 3 3 5}", {}, 10'000'000); + bench("^BB_3", "{3 4 3 4}", {}, 10'000'000); + bench("^BH_3", "{3 4 3 5}", {}, 10'000'000); + bench("^HH_3", "{3 5 3 5}", {}, 10'000'000); + bench("-H_4", "5 3 3 3", {}, 10'000'000); +// bench("~H_4", "5 3 3 3", {}, 10'000'000); // -H_4 == ~H_4 == H_5 +// bench("H_5", "5 3 3 3", {}, 10'000'000); + bench("-BH_4", "4 3 3 5", {}, 10'000'000); + bench("-K_4", "5 3 3 5", {}, 10'000'000); + bench("-DH_4", "5 3 3 * [1 1]", {}, 10'000'000); + bench("^AF_4", "{3 3 3 3 4}", {}, 10'000'000); return EXIT_SUCCESS; } diff --git a/tc/bench/named.cpp b/tc/bench/named.cpp new file mode 100644 index 0000000..2e0f225 --- /dev/null +++ b/tc/bench/named.cpp @@ -0,0 +1,55 @@ +#include +#include + +#include +#include + +#include + +template +void show(G const &g) { // todo add formatter for groups, cosets. + fmt::print(" | "); + for (const auto &v: g.gens()) { + fmt::print("{} ", v); + } + fmt::print("\n"); + + for (const auto &u: g.gens()) { + fmt::print("{} | ", u); + for (const auto &v: g.gens()) { + fmt::print("{} ", g.get(u, v)); + } + fmt::print("\n"); + } +} + +int main() { + tc::Group group(4, {'r', 'g', 'b', 'y'}); + + group.set('r', 'g', 5); + group.set('g', 'b', 4); + group.set('b', 'y', 3); + + show(group); + + auto sub = group.sub({'r', 'g', 'y'}); + show(sub); + + auto res = sub.solve({}); + fmt::print("res order: {}\n", res.order()); + + auto cos = sub.solve({'r', 'y'}); + + fmt::print("order: {}\n", cos.order()); + +// tc::Group group(4, {0, 1, 2, 3}); +// +// group.set(0, 1, 5); +// group.set(1, 2, 4); +// group.set(2, 3, 3); +// +// show(group); +// +// auto sub = group.sub({3, 2, 0, 1}); +// show(sub); +} diff --git a/tc/include/tc/core.hpp b/tc/include/tc/core.hpp index 8d2268e..db9fc6a 100644 --- a/tc/include/tc/core.hpp +++ b/tc/include/tc/core.hpp @@ -1,30 +1,220 @@ #pragma once -#include -#include -#include -#include +#include //todo clean up includes. lots of duplicate cstdint, cassert. +#include +#include -#include "util.hpp" -#include "cosets.hpp" -#include "group.hpp" +#include namespace tc { - Cosets solve(const Group &group, const std::vector &sub_gens, const Coset &bound = UNBOUNDED); - - // todo - ///** - // * Solve the cosets generated by sg_gens within the subgroup generated by g_gens of the group context - // */ - // tc::Cosets solve( - // const tc::Group &context, - // const std::vector &g_gens, - // const std::vector &sg_gens - // ) { - // // todo this should also be handled with 'global' generators. - // const auto proper_sg_gens = recontext_gens(context, g_gens, sg_gens); - // - // return tc::solve(context.subgroup(g_gens), proper_sg_gens); - // } + using Mult = u_int16_t; + constexpr Mult FREE = 0; + /** + * @brief Mapping from "global" generator names or objects to indexes used for value lookup. + * @tparam Gen_ + */ + template + struct Index; + + /** + * @brief Complete representation of a quotient group. Describes the action of each generator on each coset. + * @tparam Gen_ + */ + template + struct Cosets; + + /** + * @brief Manage the presentation of a Coxeter group and enforce constraints + * on the multiplicities of its relations. + *
    + *
  • + * m_ij = 1 iff i != j + *
  • + *
  • + * m_ij = m_ji + *
  • + *
  • + * If m_ij == inf (tc::FREE) then no relation is imposed. + *
  • + *
+ * @see + * Coxeter Group (Wikipedia) + */ + template + struct Group; + + template<> + struct Index<> { + size_t operator()(size_t const &idx) const { + return idx; + } + }; + + template + struct Index { + using Gen = Gen_; + + std::vector _gens{}; + + explicit Index(std::vector gens) : _gens(gens) {} + + size_t operator()(Gen const &gen) const { + auto it = std::find(_gens.begin(), _gens.end(), gen); + assert(it != _gens.end()); + return it - _gens.begin(); + } + }; + + template<> + struct Cosets<> { + static constexpr size_t UNSET = std::numeric_limits::max(); + + private: + size_t _rank; + size_t _order; + bool _complete; + std::vector _data; + + public: + Cosets(Cosets const &) = default; + + Cosets(Cosets &&) noexcept = default; + + ~Cosets() = default; + + void set(size_t coset, size_t gen, size_t target); + + [[nodiscard]] size_t get(size_t coset, size_t gen) const; + + [[nodiscard]] bool isset(size_t coset, size_t gen) const; + + [[nodiscard]] size_t rank() const; + + [[nodiscard]] size_t order() const; + + [[nodiscard]] bool complete() const; + + [[nodiscard]] size_t size() const; + + friend Group<>; // only constructible via Group<>::solve + + private: + explicit Cosets(size_t rank); + + void add_row(); + + void set(size_t idx, size_t target); + + [[nodiscard]] size_t get(size_t idx) const; + + [[nodiscard]] bool isset(size_t idx) const; + }; + + template<> + struct Group<> { + using Rel = std::tuple; + + private: + size_t _rank; + std::vector _mults; + + public: + Group(Group const &) = default; + + Group(Group &&) noexcept = default; + + ~Group() = default; + + explicit Group(size_t rank); + + void set(size_t, size_t, Mult); + + [[nodiscard]] Mult get(size_t, size_t) const; + + [[nodiscard]] size_t rank() const; + + [[nodiscard]] Group sub(std::vector const &idxs) const; + + [[nodiscard]] Cosets<> solve(std::vector const &idxs, size_t bound = SIZE_MAX) const; + }; + + template + struct Cosets : public Cosets<> { + using Gen = Gen_; + + private: + Index _index; + + public: + Cosets(Cosets<> g, std::vector gens) + : Cosets<>(g), _index(gens) {} + + void set(size_t coset, Gen const &gen, size_t target) { + Cosets<>::set(coset, _index(gen), target); + } + + [[nodiscard]] size_t get(size_t coset, Gen const &gen) const { + return Cosets<>::get(coset, _index(gen)); + } + + [[nodiscard]] bool isset(size_t coset, Gen const &gen) const { + return Cosets<>::isset(coset, _index(gen)); + } + + [[nodiscard]] std::vector gens() const { + return _index._gens; + } + + private: + Cosets(size_t rank, std::vector gens) + : Cosets<>(rank), _index(gens) {} + }; + + template + struct Group : public Group<> { + using Gen = Gen_; + using Rel = std::tuple; + + private: + Index _index; + + public: + Group(Group const &) = default; + + Group(Group &&) noexcept = default; + + Group(Group<> g, std::vector gens) + : Group<>(g), _index(gens) {} + + Group(size_t rank, std::vector gens) + : Group<>(rank), _index(gens) {} + + ~Group() = default; + + void set(Gen const &u, Gen const &v, Mult m) { + Group<>::set(_index(u), _index(v), m); + } + + [[nodiscard]] Mult get(Gen const &u, Gen const &v) const { + return Group<>::get(_index(u), _index(v)); + } + + [[nodiscard]] std::vector gens() const { + return _index._gens; + } + + [[nodiscard]] Group sub(std::vector const &gens) const { + std::vector idxs(gens.size()); + std::transform(gens.begin(), gens.end(), idxs.begin(), _index); + return Group(Group<>::sub(idxs), gens); + } + + [[nodiscard]] Cosets solve(std::vector const &gens, size_t bound = SIZE_MAX) const { + std::vector idxs(gens.size()); + std::transform(gens.begin(), gens.end(), idxs.begin(), _index); + + return Cosets(Group<>::solve(idxs, bound), gens); + } + }; } diff --git a/tc/include/tc/cosets.hpp b/tc/include/tc/cosets.hpp deleted file mode 100644 index 26897dd..0000000 --- a/tc/include/tc/cosets.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include - -#include "util.hpp" - -namespace tc { - struct Cosets { - Gen ngens; - std::vector data; - Path path; - bool complete = false; - - Cosets(const Cosets &) = default; - - explicit Cosets(Gen ngens) - : ngens(ngens) { - } - - void add_row() { - data.resize(data.size() + ngens, UNSET); - path.add_row(); - } - - void put(Coset coset, Gen gen, Coset target) { - data[coset * ngens + gen] = target; - data[target * ngens + gen] = coset; - - if (path.get(target).from_idx == UNSET) { - path.put(coset, gen, target); - } - } - - void put(size_t idx, Coset target) { - Coset coset = idx / ngens; - Gen gen = idx % ngens; - - data[idx] = target; - data[target * ngens + gen] = coset; - - if (path.get(target).from_idx == UNSET) { - path.put(coset, gen, target); - } - } - - [[nodiscard]] Coset get(Coset coset, Gen gen) const { - return data[coset * ngens + gen]; - } - - [[nodiscard]] Coset get(size_t idx) const { - return data[idx]; - } - - [[nodiscard]] bool isset(Coset coset, Gen gen) const { - return get(coset, gen) != UNSET; - } - - [[nodiscard]] bool isset(size_t idx) const { - return get(idx) != UNSET; - } - - - [[nodiscard]] tc::Coset size() const { - return path.size(); - } - }; -} \ No newline at end of file diff --git a/tc/include/tc/group.hpp b/tc/include/tc/group.hpp deleted file mode 100644 index 81eff7a..0000000 --- a/tc/include/tc/group.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -namespace tc { - struct Group; - struct SubGroup; - - struct Graph { - size_t rank{}; - std::vector edges{}; - }; - - /** - * @brief Manage the presentation of a Coxeter group and enforce constraints - * on the multiplicities of its relations. - *
    - *
  • - * m_ij = 1 iff i != j - *
  • - *
  • - * m_ij = m_ji - *
  • - *
  • - * If m_ij == inf (tc::FREE) then no relation is imposed. - *
  • - *
- * @see - * Coxeter Group (Wikipedia) - */ - struct Group { - int rank; - tc::pair_map _orders; - - Group(const Group &) = default; - - explicit Group(int rank, const std::vector &rels = {}) - : rank(rank), _orders(rank, 2) { - - for (int i = 0; i < rank; ++i) { - set(Rel{i, i, 1}); - } - - for (const auto &rel: rels) { - set(rel); - } - } - - explicit Group(const Graph &graph) - : rank(graph.rank), _orders(graph.rank, 2) { - for (const auto &[i, j, order]: graph.edges) { - set({i, j, order}); - } - } - - void set(const Rel &r) { - auto &[i, j, m] = r; - assert(i != j || m == 1); - _orders(i, j) = m; - } - - [[nodiscard]] int get(int i, int j) const { - return _orders(i, j); - } - - [[nodiscard]] SubGroup subgroup(const std::vector &gens) const; - }; - - struct SubGroup : public Group { - std::vector gen_map; - const Group &parent; - - SubGroup(const Group &parent, std::vector gen_map) - : Group(gen_map.size()), parent(parent), gen_map() { - - std::sort(gen_map.begin(), gen_map.end()); - this->gen_map = gen_map; - - for (size_t i = 0; i < gen_map.size(); ++i) { - for (size_t j = 0; j < gen_map.size(); ++j) { - int mult = parent.get(gen_map[i], gen_map[j]); - set(Rel(i, j, mult)); - } - } - } - }; -} diff --git a/tc/include/tc/groups.hpp b/tc/include/tc/groups.hpp index e8e8137..2a52119 100644 --- a/tc/include/tc/groups.hpp +++ b/tc/include/tc/groups.hpp @@ -1,22 +1,23 @@ #pragma once #include +#include -#include +#include namespace tc { /** * Construct a group from a (simplified) Schlafli Symbol of the form [a, b, ..., c] * @param mults: The sequence of multiplicites between adjacent generators. */ - Group schlafli(const std::vector &mults); + Group<> schlafli(const std::vector &mults); - Group coxeter(const std::string &symbol); + Group<> coxeter(const std::string &symbol); - Group vcoxeter(const std::string &symbol, const std::vector &values); + Group<> vcoxeter(const std::string &symbol, const std::vector &values); template - Group coxeter(const std::string &symbol, const Args &... args) { + Group<> coxeter(const std::string &symbol, const Args &... args) { std::vector values = {{args...}}; return vcoxeter(symbol, values); } diff --git a/tc/include/tc/pair_map.hpp b/tc/include/tc/pair_map.hpp deleted file mode 100644 index d282846..0000000 --- a/tc/include/tc/pair_map.hpp +++ /dev/null @@ -1,453 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace tc { - template - struct pair_map { - struct iterator; - struct const_iterator; - struct view; - struct const_view; - - private: - size_t _size; - std::vector _data{}; - - static size_t idx(size_t i, size_t j); - - public: - explicit pair_map(size_t size); - - explicit pair_map(size_t size, const T &value); - - [[nodiscard]] size_t size() const; - - T &operator()(size_t i, size_t j); - - T operator()(size_t i, size_t j) const; - - view of(size_t f); - - const_view of(size_t f) const; - - const_view cof(size_t f); - - iterator begin(); - - iterator end(); - - const_iterator begin() const; - - const_iterator end() const; - - const_iterator cbegin(); - - const_iterator cend(); - }; - - template - struct pair_map::iterator { - using reference = std::tuple; - - private: - pair_map &_map; - size_t _i, _j; - - public: - iterator(pair_map &map, size_t i, size_t j); - - iterator operator++(); - - iterator operator++(int) &; - - reference operator*(); - - bool operator!=(const iterator &other); - }; - - template - struct pair_map::const_iterator { - using value_type = std::tuple; - - private: - const pair_map &_map; - size_t _i, _j; - - public: - const_iterator(const pair_map &map, size_t i, size_t j); - - const_iterator operator++(); - - const_iterator operator++(int) &; - - value_type operator*(); - - bool operator!=(const const_iterator &other); - }; - - template - struct pair_map::view { - struct iterator; - struct const_iterator; - - private: - pair_map &_map; - size_t _f; - - public: - view(pair_map &map, size_t f); - - iterator begin(); - - iterator end(); - - const_iterator begin() const; - - const_iterator end() const; - - const_iterator cbegin(); - - const_iterator cend(); - }; - - template - struct pair_map::view::iterator { - using reference = std::tuple; - - private: - pair_map &_map; - size_t _f, _v; - - public: - iterator(pair_map &map, size_t f, size_t v); - - iterator operator++(); - - iterator operator++(int); - - reference operator*(); - - bool operator!=(const iterator &other); - }; - - template - struct pair_map::view::const_iterator { - using value_type = std::tuple; - - private: - const pair_map &_map; - size_t _f, _v; - - public: - const_iterator(const pair_map &map, size_t f, size_t v); - - const_iterator operator++(); - - const_iterator operator++(int); - - value_type operator*(); - - bool operator!=(const const_iterator &other); - }; - - template - struct pair_map::const_view { - using const_iterator = typename pair_map::view::const_iterator; - - private: - const pair_map &_map; - size_t _f; - - public: - const_view(const pair_map &map, size_t f); - - const_iterator begin() const; - - const_iterator end() const; - - const_iterator cbegin(); - - const_iterator cend(); - }; - - // region pair_map - - template - size_t pair_map::idx(size_t i, size_t j) { - if (i > j) std::swap(i, j); - return j * (j + 1) / 2 + i; - } - - template - pair_map::pair_map(size_t size) - : _size(size), _data(size * (size + 1) / 2) {} - - template - pair_map::pair_map(size_t size, const T &value) - : _size(size), _data(size * (size + 1) / 2, value) {} - - template - size_t pair_map::size() const { - return _size; - } - - template - T &pair_map::operator()(size_t i, size_t j) { - return _data[idx(i, j)]; - } - - template - T pair_map::operator()(size_t i, size_t j) const { - return _data[idx(i, j)]; - } - - template - typename pair_map::view pair_map::of(size_t f) { - return view(*this, f); - } - - template - typename pair_map::const_view pair_map::of(size_t f) const { - return const_view(*this, f); - } - - template - typename pair_map::const_view pair_map::cof(size_t f) { - return const_view(*this, f); - } - - template - typename pair_map::iterator pair_map::begin() { - return iterator(*this, 0, 0); - } - - template - typename pair_map::iterator pair_map::end() { - return iterator(*this, 0, _size); - } - - template - typename pair_map::const_iterator pair_map::begin() const { - return const_iterator(*this, 0, 0); - } - - template - typename pair_map::const_iterator pair_map::end() const { - return const_iterator(*this, 0, _size); - } - - template - typename pair_map::const_iterator pair_map::cbegin() { - return const_iterator(*this, 0, 0); - } - - template - typename pair_map::const_iterator pair_map::cend() { - return const_iterator(*this, 0, _size); - } - - // endregion - - // region pair_map::iterator - - template - pair_map::iterator::iterator(pair_map &map, size_t i, size_t j) - :_map(map), _i(i), _j(j) {} - - template - typename pair_map::iterator pair_map::iterator::operator++() { - if (++_i > _j) { - _i = 0; - ++_j; - } - return *this; - } - - template - typename pair_map::iterator pair_map::iterator::operator++(int) &{ - iterator it = *this; - ++this; - return it; - } - - template - typename pair_map::iterator::reference pair_map::iterator::operator*() { - return std::tie(_i, _j, _map(_i, _j)); - } - - template - bool pair_map::iterator::operator!=(const pair_map::iterator &other) { - return &_map != &other._map || _i != other._i || _j != other._j; - } - - // endregion - - // region pair_map::const_iterator - - template - pair_map::const_iterator::const_iterator(const pair_map &map, size_t i, size_t j) - :_map(map), _i(i), _j(j) {} - - template - typename pair_map::const_iterator pair_map::const_iterator::operator++() { - if (++_i > _j) { - _i = 0; - ++_j; - } - return *this; - } - - template - typename pair_map::const_iterator pair_map::const_iterator::operator++(int) &{ - const_iterator it = *this; - ++this; - return it; - } - - template - typename pair_map::const_iterator::value_type pair_map::const_iterator::operator*() { - return std::tuple(_i, _j, _map(_i, _j)); - } - - template - bool pair_map::const_iterator::operator!=(const pair_map::const_iterator &other) { - return &_map != &other._map || _i != other._i || _j != other._j; - } - - // endregion - - // region pair_map::view - - template - pair_map::view::view(pair_map &map, size_t f) - : _map(map), _f(f) {} - - template - typename pair_map::view::iterator pair_map::view::begin() { - return iterator(_map, _f, 0); - } - - template - typename pair_map::view::iterator pair_map::view::end() { - return iterator(_map, _f, _map._size); - } - - template - typename pair_map::view::const_iterator pair_map::view::begin() const { - return const_iterator(_map, _f, 0); - } - - template - typename pair_map::view::const_iterator pair_map::view::end() const { - return const_iterator(_map, _f, _map._size); - } - - template - typename pair_map::view::const_iterator pair_map::view::cbegin() { - return const_iterator(_map, _f, 0); - } - - template - typename pair_map::view::const_iterator pair_map::view::cend() { - return const_iterator(_map, _f, _map._size); - } - - // endregion - - // region pair_map::view::iterator - - template - pair_map::view::iterator::iterator(pair_map &map, size_t f, size_t v) - : _map(map), _f(f), _v(v) {} - - template - typename pair_map::view::iterator pair_map::view::iterator::operator++() { - ++_v; - return *this; - } - - template - typename pair_map::view::iterator pair_map::view::iterator::operator++(int) { - iterator it = *this; - ++this; - return it; - } - - template - typename pair_map::view::iterator::reference pair_map::view::iterator::operator*() { - auto [i, j] = std::minmax(_f, _v); - return std::tie(i, j, _map(i, j)); - } - - template - bool pair_map::view::iterator::operator!=(const pair_map::view::iterator &other) { - return &_map != &other._map || _f != other._f || _v != other._v; - } - - // endregion - - // region pair_map::view::const_iterator - - template - pair_map::view::const_iterator::const_iterator(const pair_map &map, size_t f, size_t v) - : _map(map), _f(f), _v(v) {} - - template - typename pair_map::view::const_iterator pair_map::view::const_iterator::operator++() { - ++_v; - return *this; - } - - template - typename pair_map::view::const_iterator pair_map::view::const_iterator::operator++(int) { - const_iterator it = *this; - ++this; - return it; - } - - template - typename pair_map::view::const_iterator::value_type pair_map::view::const_iterator::operator*() { - auto [i, j] = std::minmax(_f, _v); - return std::tuple(i, j, _map(i, j)); - } - - template - bool pair_map::view::const_iterator::operator!=(const pair_map::view::const_iterator &other) { - return &_map != &other._map || _f != other._f || _v != other._v; - } - - // endregion - - // region pair_map::const_view - - template - pair_map::const_view::const_view(const pair_map &map, size_t f) - : _map(map), _f(f) {} - - template - typename pair_map::const_view::const_iterator pair_map::const_view::begin() const { - return const_iterator(_map, _f, 0); - } - - template - typename pair_map::const_view::const_iterator pair_map::const_view::end() const { - return const_iterator(_map, _f, _map._size); - } - - template - typename pair_map::const_view::const_iterator pair_map::const_view::cbegin() { - return const_iterator(_map, _f, 0); - } - - template - typename pair_map::const_view::const_iterator pair_map::const_view::cend() { - return const_iterator(_map, _f, _map._size); - } - - // endregion -} diff --git a/tc/include/tc/util.hpp b/tc/include/tc/util.hpp index f42bf1e..3387754 100644 --- a/tc/include/tc/util.hpp +++ b/tc/include/tc/util.hpp @@ -4,18 +4,11 @@ #include #include #include +#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; +// using Mult = uint16_t; +// constexpr Mult FREE = 0; struct Action { int from_idx = -1; diff --git a/tc/src/cosets.cpp b/tc/src/cosets.cpp new file mode 100644 index 0000000..d91ab55 --- /dev/null +++ b/tc/src/cosets.cpp @@ -0,0 +1,55 @@ +#include + +namespace tc { + Cosets<>::Cosets(size_t rank) + : _rank(rank), _order(0), _complete(false), _data() {} + + void Cosets<>::set(size_t coset, size_t gen, size_t target) { + set(coset * rank() + gen, target); + } + + [[nodiscard]] size_t Cosets<>::get(size_t coset, size_t gen) const { + return get(coset * rank() + gen); + } + + [[nodiscard]] bool Cosets<>::isset(size_t coset, size_t gen) const { + return isset(coset * rank() + gen); + } + + [[nodiscard]] size_t Cosets<>::rank() const { + return _rank; + } + + [[nodiscard]] size_t Cosets<>::order() const { + return _order; + } + + [[nodiscard]] bool Cosets<>::complete() const { + return _complete; + } + + [[nodiscard]] size_t Cosets<>::size() const { + return _data.size(); + } + + void Cosets<>::add_row() { + _data.resize(_data.size() + rank(), UNSET); + _order++; + } + + void Cosets<>::set(size_t idx, size_t target) { + size_t coset = idx / rank(); + size_t gen = idx % rank(); + _data[idx] = target; + _data[target * rank() + gen] = coset; + } + + [[nodiscard]] size_t Cosets<>::get(size_t idx) const { + return _data[idx]; + } + + [[nodiscard]] bool Cosets<>::isset(size_t idx) const { + return get(idx) != UNSET; + } + +} \ No newline at end of file diff --git a/tc/src/group.cpp b/tc/src/group.cpp new file mode 100644 index 0000000..9866c02 --- /dev/null +++ b/tc/src/group.cpp @@ -0,0 +1,42 @@ +#include + +#include + +namespace tc { + Group<>::Group(size_t rank) : _rank(rank), _mults(_rank * _rank, 2) { + for (int idx = 0; idx < rank; ++idx) { + set(idx, idx, 1); + } + } + + void Group<>::set(size_t u, size_t v, Mult m) { + assert(u < rank()); + assert(v < rank()); + + _mults[u * rank() + v] = m; + _mults[v * rank() + u] = m; + } + + [[nodiscard]] Mult Group<>::get(size_t u, size_t v) const { + assert(u < rank()); + assert(v < rank()); + + return _mults[u * rank() + v]; + } + + [[nodiscard]] size_t Group<>::rank() const { + return _rank; + } + + [[nodiscard]] Group<> Group<>::sub(std::vector const &idxs) const { + Group<> res(idxs.size()); + + for (int i = 0; i < idxs.size(); ++i) { + for (int j = i; j < idxs.size(); ++j) { + res.set(i, j, get(idxs[i], idxs[j])); + } + } + + return res; + } +} diff --git a/tc/src/groups.cpp b/tc/src/groups.cpp index 9b98ac5..dab2cb8 100644 --- a/tc/src/groups.cpp +++ b/tc/src/groups.cpp @@ -2,17 +2,18 @@ #include #include +#include namespace tc { - Group schlafli(const std::vector &mults) { - Group res(mults.size() + 1); + Group<> schlafli(const std::vector &mults) { + Group<> res(mults.size() + 1); for (size_t i = 0; i < mults.size(); ++i) { - res.set(Rel{i, i + 1, mults[i]}); + res.set(i, i + 1, mults[i]); } return res; } - Group vcoxeter(const std::string &symbol, const std::vector &values) { + Group<> vcoxeter(const std::string &symbol, const std::vector &values) { fmt::dynamic_format_arg_store ds; for (const auto &value: values) { diff --git a/tc/src/lang.cpp b/tc/src/lang.cpp index 6235909..f6376ba 100644 --- a/tc/src/lang.cpp +++ b/tc/src/lang.cpp @@ -4,13 +4,19 @@ #include #include -#include +#include #include #include #include #include +#include + +struct Graph { + size_t rank{}; + std::vector::Rel> edges{}; +}; struct Op { enum Code { @@ -191,6 +197,7 @@ peg::parser build_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) { @@ -203,6 +210,7 @@ peg::parser build_ast_parser() { return parser; } + #endif std::vector compile(const std::string &source) { @@ -221,10 +229,10 @@ std::vector compile(const std::string &source) { return cg.ops; } -tc::Graph eval(const std::vector &ops) { +Graph eval(const std::vector &ops) { std::vector> stacks(1); - tc::Graph g; + Graph g; stacks.back().emplace(g.rank++); for (const auto &op: ops) { @@ -280,9 +288,13 @@ tc::Graph eval(const std::vector &ops) { } namespace tc { - Group coxeter(const std::string &symbol) { + Group<> coxeter(const std::string &symbol) { auto ops = compile(symbol); auto diagram = eval(ops); - return Group(diagram); + Group<> res(diagram.rank); + for (const auto &[i, j, m]: diagram.edges) { + res.set(i, j, m); + } + return res; } } diff --git a/tc/src/core.cpp b/tc/src/solve.cpp similarity index 74% rename from tc/src/core.cpp rename to tc/src/solve.cpp index 0808e59..779deed 100644 --- a/tc/src/core.cpp +++ b/tc/src/solve.cpp @@ -1,15 +1,11 @@ -#include - #include #include #include #include -namespace tc { - SubGroup Group::subgroup(const std::vector &gens) const { - return {*this, gens}; - } +#include +namespace tc { /** * Each coset is associated a row in each table. * Rows document the "loops" formed by @@ -24,10 +20,10 @@ namespace tc { }; struct Tables { - std::vector rels; + std::vector::Rel> rels; std::vector> rows; - explicit Tables(std::vector rels) + explicit Tables(std::vector::Rel> rels) : rels(std::move(rels)), rows() { } @@ -40,46 +36,44 @@ namespace tc { } }; - Cosets solve( - const Group &group, - const std::vector &sub_gens, - const Coset &bound - ) { - auto rank = group.rank; - + [[nodiscard]] Cosets<> Group<>::solve(std::vector const &idxs, size_t bound) const { // region Initialize Cosets Table - Cosets cosets(rank); + Cosets<> cosets(rank()); cosets.add_row(); - if (rank == 0) { - cosets.complete = true; + if (rank() == 0) { + cosets._complete = true; return cosets; } - for (Coset g: sub_gens) { - if (g < rank) - cosets.put(0, g, 0); + for (size_t g: idxs) { + if (g < rank()) + cosets.set(0, g, 0); } // endregion // region Initialize Relation Tables - std::vector> rels; - for (const auto &[i, j, m]: group._orders) { - // The algorithm only works for Coxeter groups; multiplicities m_ii=1 are assumed. Relation tables - // _may_ be added for them, but they are redundant and hurt performance so are skipped. - if (i == j) continue; + std::vector::Rel> rels; + for (int i = 0; i < rank(); ++i) { + for (int j = i + 1; j < rank(); ++j) { + // The algorithm only works for Coxeter groups; multiplicities m_ii=1 are assumed. Relation tables + // _may_ be added for them, but they are redundant and hurt performance so are skipped. + if (i == j) continue; - // Coxeter groups admit infinite multiplicities, represented by contexpr tc::FREE. Relation tables - // for these should be skipped. - if (m == FREE) { - continue; + // Coxeter groups admit infinite multiplicities, represented by contexpr tc::FREE. Relation tables + // for these should be skipped. + auto m = get(i, j); + + if (m == FREE) { + continue; + } + + rels.emplace_back(i, j, m); } - - rels.emplace_back(i, j, m); } Tables rel_tables(rels); - std::vector> tables_for(rank); + std::vector> tables_for(rank()); int rel_idx = 0; for (const auto &[i, j, m]: rels) { tables_for[i].push_back(rel_idx); @@ -87,7 +81,7 @@ namespace tc { rel_idx++; } - std::vector lst_vals; + std::vector lst_vals; rel_tables.add_row(); for (int table_idx = 0; table_idx < rel_tables.size(); ++table_idx) { const auto &[i, j, m] = rel_tables.rels[table_idx]; @@ -108,26 +102,26 @@ namespace tc { size_t idx = 0; size_t fact_idx; - Coset coset, gen, target, lst; + size_t coset, gen, target, lst; while (true) { // find next unknown product - while (idx < cosets.data.size() and cosets.isset(idx)) + while (idx < cosets.size() and cosets.isset(idx)) idx++; - if (cosets.size() >= bound) { + if (cosets.order() >= bound) { return cosets; } // if there are none, then return - if (idx == cosets.data.size()) { + if (idx == cosets.size()) { // todo unrolled linked list interval // rel_tables.del_rows_to(idx / ngens); break; } // the unknown product must be a new coset, so add it - target = cosets.size(); + target = cosets.order(); cosets.add_row(); rel_tables.add_row(); @@ -136,8 +130,6 @@ namespace tc { facts.push(idx); // new product should be recorded and propagated // todo unrolled linked list interval -// coset = idx / ngens; -// gen = idx % ngens; // rel_tables.del_rows_to(coset); // find all products which also lead to target @@ -148,10 +140,10 @@ namespace tc { // skip if this product was already learned if (cosets.get(fact_idx) != -1) continue; - cosets.put(fact_idx, target); + cosets.set(fact_idx, target); - coset = fact_idx / rank; - gen = fact_idx % rank; + coset = fact_idx / rank(); + gen = fact_idx % rank(); // If the product stays within the coset todo for (size_t table_idx: tables_for[gen]) { @@ -159,7 +151,7 @@ namespace tc { auto &trow = rel_tables.rows[target][table_idx]; auto &crow = rel_tables.rows[coset][table_idx]; - Coset other_gen = (i == gen) ? j : i; + size_t other_gen = (i == gen) ? j : i; // Test if loop is closed if (trow.free) { @@ -174,7 +166,7 @@ namespace tc { if (trow.gnr == m) { // loop is closed, but idempotent, so the target links to itself via the other generator. // todo might be able to move this logic up into the (target == coset) block and avoid those computations. - facts.push(target * rank + other_gen); + facts.push(target * rank() + other_gen); } } else { if (trow.gnr == m - 1) { @@ -184,7 +176,7 @@ namespace tc { // loop is closed. We know the last element in the loop must link with this one. lst = lst_vals[trow.lst_idx]; // delete trow.lst_ptr; - facts.push(lst * rank + other_gen); + facts.push(lst * rank() + other_gen); } } } @@ -213,7 +205,7 @@ namespace tc { } } - cosets.complete = true; + cosets._complete = true; return cosets; } } diff --git a/tc/test/CMakeLists.txt b/tc/test/CMakeLists.txt index 02fde42..b42c21a 100644 --- a/tc/test/CMakeLists.txt +++ b/tc/test/CMakeLists.txt @@ -1,8 +1,5 @@ include(GoogleTest) -add_executable(test_pair_map test_pair_map.cpp) -target_link_libraries(test_pair_map tc GTest::gtest_main) - add_executable(test_solve test_solve.cpp) target_link_libraries(test_solve tc GTest::gtest_main) @@ -17,6 +14,5 @@ target_compile_definitions( MIN_COS_PER_SEC=$,${MIN_DEBUG_CPS},${MIN_RELEASE_CPS}> ) -gtest_discover_tests(test_pair_map) gtest_discover_tests(test_solve) gtest_discover_tests(test_lang) diff --git a/tc/test/test_lang.cpp b/tc/test/test_lang.cpp index e9cfd1b..8ca8091 100644 --- a/tc/test/test_lang.cpp +++ b/tc/test/test_lang.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -20,24 +20,33 @@ TEST(coxeter, simple) { auto g = tc::coxeter("5 3 3"); - ASSERT_EQ(g.rank, 4); + ASSERT_EQ(g.rank(), 4); + EXPECT_EQ(g.get(0, 0), 1); EXPECT_EQ(g.get(0, 1), 5); - EXPECT_EQ(g.get(1, 2), 3); - EXPECT_EQ(g.get(3, 4), 3); - EXPECT_EQ(g.get(0, 2), 2); EXPECT_EQ(g.get(0, 3), 2); - EXPECT_EQ(g.get(0, 4), 2); + + EXPECT_EQ(g.get(1, 0), 5); + EXPECT_EQ(g.get(1, 1), 1); + EXPECT_EQ(g.get(1, 2), 3); EXPECT_EQ(g.get(1, 3), 2); - EXPECT_EQ(g.get(1, 4), 2); - EXPECT_EQ(g.get(2, 4), 2); + + EXPECT_EQ(g.get(2, 0), 2); + EXPECT_EQ(g.get(2, 1), 3); + EXPECT_EQ(g.get(2, 2), 1); + EXPECT_EQ(g.get(2, 3), 3); + + EXPECT_EQ(g.get(3, 0), 2); + EXPECT_EQ(g.get(3, 1), 2); + EXPECT_EQ(g.get(3, 2), 3); + EXPECT_EQ(g.get(3, 3), 1); } TEST(coxeter, looping) { auto g = tc::coxeter("{5 3 4}"); - ASSERT_EQ(g.rank, 3); + ASSERT_EQ(g.rank(), 3); EXPECT_EQ(g.get(0, 1), 5); EXPECT_EQ(g.get(1, 2), 3); diff --git a/tc/test/test_pair_map.cpp b/tc/test/test_pair_map.cpp deleted file mode 100644 index 1a79054..0000000 --- a/tc/test/test_pair_map.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include -#include -#include - -#include - -#include - -/// helper for comparing against two options -template -testing::AssertionResult AssertEqEither( - const char *val_expr, - const char *o1_expr, - const char *o2_expr, - T val, U o1, V o2 -) { - if ((val == o1) || (val == o2)) { - return testing::AssertionSuccess(); - } - - return testing::AssertionFailure() - << val_expr << " (" << val << ") " << "does not equal " << o1_expr - << " (" << o1 << ") " << "or " << o2_expr << " (" << o2 << ")"; -} - -#define EXPECT_EQ_EITHER(val, o1, o2) EXPECT_PRED_FORMAT3(AssertEqEither, val, o1, o2) - - -/// Naive symmetric pair hash -size_t key(size_t i, size_t j) { - return ((i + j) << 12) ^ i ^ j; -} - -/// factory to build a simple pair_map -tc::pair_map populate(size_t size) { - tc::pair_map pm(6); - - for (int i = 0; i < pm.size(); ++i) { - for (int j = i; j < pm.size(); ++j) { - pm(i, j) = key(i, j); - } - } - - return pm; -} - -TEST(pair_map, fill) { - tc::pair_map pm(6, 42); - - for (int i = 0; i < pm.size(); ++i) { - for (int j = i; j < pm.size(); ++j) { - EXPECT_EQ(pm(i, j), 42); - } - } -} - -TEST(pair_map, symmetry) { - auto pm = populate(6); - - for (int i = 0; i < pm.size(); ++i) { - for (int j = i; j < pm.size(); ++j) { - EXPECT_EQ(pm(i, j), key(i, j)); - EXPECT_EQ(pm(j, i), pm(i, j)); - } - } -} - -TEST(pair_map, copy) { - auto pm = populate(6); - auto pm_ = pm; - - ASSERT_EQ(pm_.size(), 6); - - for (int i = 0; i < pm_.size(); ++i) { - for (int j = i; j < pm_.size(); ++j) { - EXPECT_EQ(pm_(i, j), pm(i, j)); - EXPECT_EQ(pm_(i, j), key(i, j)); - } - } -} - -TEST(pair_map, move) { - auto pm = populate(6); - auto pm_ = std::move(pm); - - ASSERT_EQ(pm_.size(), 6); - - for (int i = 0; i < pm_.size(); ++i) { - for (int j = i; j < pm_.size(); ++j) { - EXPECT_EQ(pm_(i, j), key(i, j)); - } - } -} - -TEST(pair_map, iterate) { - auto pm = populate(6); - - size_t count = 0; - for (const auto &[i, j, m]: pm) { - EXPECT_EQ(m, key(i, j)); - count++; - } - EXPECT_EQ(count, 21); -} - -TEST(pair_map, iterate_ref) { - auto pm = populate(6); - - for (const auto &[i, j, m]: pm) { - m = 42; - } - - for (const auto &[i, j, m]: pm) { - EXPECT_EQ(m, 42); - } -} - -TEST(pair_map, view) { - auto pm = populate(6); - - size_t count = 0; - for (const auto &[i, j, m]: pm.of(4)) { - EXPECT_EQ_EITHER(4, i, j); - count++; - } - EXPECT_EQ(count, pm.size()); -} diff --git a/tc/test/test_solve.cpp b/tc/test/test_solve.cpp index a814771..2efcc74 100644 --- a/tc/test/test_solve.cpp +++ b/tc/test/test_solve.cpp @@ -11,15 +11,15 @@ testing::AssertionResult AssertSolveOrder( const char *group_expr, const char *sub_gens_expr, const char *expected_order_expr, - const tc::Group &group, - const std::vector &sub_gens, - tc::Coset expected_order + const tc::Group<> &group, + const std::vector &sub_gens, + size_t expected_order ) { auto start = std::clock(); - auto cosets = tc::solve(group, sub_gens); + auto cosets = group.solve(sub_gens); auto end = std::clock(); - tc::Coset actual_order = cosets.size(); + size_t actual_order = cosets.order(); auto total_sec = (double) (end - start) / CLOCKS_PER_SEC; auto cosets_per_sec = (double) actual_order / total_sec; @@ -48,45 +48,45 @@ testing::AssertionResult AssertSolveOrder( #define EXPECT_SOLVE_ORDER(group, sub_gens, expected_order) EXPECT_PRED_FORMAT3(AssertSolveOrder, group, sub_gens, expected_order); -using v = std::vector; +using v = std::vector; -tc::Group A(unsigned int n) { +tc::Group<> A(unsigned int n) { return tc::vcoxeter("3 * {}", {n - 1}); } -tc::Group B(unsigned int n) { +tc::Group<> B(unsigned int n) { return tc::vcoxeter("4 3 * {}", {n - 2}); } -tc::Group D(unsigned int n) { +tc::Group<> D(unsigned int n) { return tc::vcoxeter("3 * [1 1 {}]", {n - 3}); } -tc::Group E(unsigned int n) { +tc::Group<> E(unsigned int n) { return tc::vcoxeter("3 * [1 2 {}]", {n - 4}); } -tc::Group F4() { +tc::Group<> F4() { return tc::coxeter("3 4 3"); } -tc::Group G2() { +tc::Group<> G2() { return tc::coxeter("6"); } -tc::Group H(unsigned int n) { +tc::Group<> H(unsigned int n) { return tc::vcoxeter("5 3 * {}", {n - 2}); } -tc::Group I2(unsigned int n) { +tc::Group<> I2(unsigned int n) { return tc::vcoxeter("{}", {n}); } -tc::Group T(unsigned int m, unsigned int n) { +tc::Group<> T(unsigned int m, unsigned int n) { return tc::vcoxeter("{} 2 {}", {m, n}); } -tc::Group T(unsigned int n) { +tc::Group<> T(unsigned int n) { return T(n, n); }