#pragma once #include //todo clean up includes. lots of duplicate cstdint, cassert. #include #include #include #include namespace tc { using Mult = std::uint16_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; /** * @brief Support generating values given a Cosets and transformation callback. * @tparam Gen_ */ template struct Path; 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) {} template Index(It begin, It end) : _gens(begin, end) {} 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 Path<> { using Action = std::tuple; // coset, gen private: std::vector _data; public: // todo be smarter about move semantics explicit Path(Cosets<> const &cosets) : _data() { _data.resize(cosets.order()); std::vector complete(cosets.order(), false); complete[0] = true; for (size_t cos = 0; cos < cosets.order(); ++cos) { for (size_t gen = 0; gen < cosets.rank(); ++gen) { size_t tgt = cosets.get(cos, gen); if (!complete[tgt]) { _data[tgt] = {cos, gen}; complete[tgt] = true; } } } } template void walk(Elem const &start, BinaryOp op, It out) { // todo how to work for std::back_insert_iterator? // todo how to work for non-default-constructible values? out[0] = start; for (int tgt = 1; tgt < _data.size(); ++tgt) { auto [cos, gen] = _data[tgt]; out[tgt] = op(out[cos], gen); } } template void walk(Elem const &start, BinaryOp op, It out, Gens const &gens) { out[0] = start; for (int tgt = 1; tgt < _data.size(); ++tgt) { auto [cos, gen] = _data[tgt]; out[tgt] = op(out[cos], gens[gen]); } } [[nodiscard]] size_t order() const { return _data.size(); } }; template struct Cosets : public Cosets<> { using Gen = Gen_; private: Index _index; friend Path; 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), _index._gens); } }; template struct Path : public Path<> { using Gen = Gen_; private: Index _index; public: // todo be smarter about move semantics explicit Path(Cosets const &cosets) : Path<>(cosets), _index(cosets._index) {} template Path(Cosets<> const &cosets, T const &gens) : Path<>(cosets), _index(gens.begin(), gens.end()) {} template void walk(Elem const &start, BinaryOp op, It out) { Path<>::walk(start, op, out, _index._gens.begin()); } }; }