//
// libsemigroups - C++ library for semigroups and monoids
// Copyright (C) 2019 James D. Mitchell
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// This file contains a generic implementation of a class Action which
// represents the action of a semigroup on a set.
#ifndef LIBSEMIGROUPS_ACTION_HPP_
#define LIBSEMIGROUPS_ACTION_HPP_
#include <cstddef>
// for size_t
#include <type_traits>
// for is_trivially_default_construc...
#include <unordered_map>
// for unordered_map
#include <vector>
// for vector
#include "adapters.hpp" // for One
#include "bruidhinn-traits.hpp" // for detail::BruidhinnTraits
#include "debug.hpp" // for LIBSEMIGROUPS_ASSERT
#include "digraph.hpp" // for ActionDigraph
#include "exception.hpp" // for LIBSEMIGROUPS_EXCEPTION
#include "report.hpp" // for REPORT_DEFAULT
#include "runner.hpp" // for Runner
namespace libsemigroups {
//! The values in this enum can be used as a template parameter for the Action
//! class to specify whether the action of the Action is a left or right
//! action.
enum class side {
//! This value indicates that the action in an Action instance should be a
//! left action.
left,
//! This value indicates that the action in an Action instance should be a
//! right action.
right
};
//! Defined in ``action.hpp``.
//!
//! This page contains details of the Action class in ``libsemigroups`` for
//! finding actions of semigroups, or groups, on sets. The notion of an
//! "action" in the context of ``libsemigroups`` is analogous to the notion
//! of an orbit of a group.
//!
//! You are unlikely to want to use this class directly directly, but rather
//! via the more convenient aliases \ref RightAction and
//! \ref LeftAction.
//!
//! The function \c run finds points
//! that can be obtained by acting on the seeds of \c this by the generators
//! of \c this until no further points can be found, or \c stopped
//! returns \c true. This is achieved by performing a breadth first search.
//!
//! \tparam TElementType the type of the elements of the semigroup.
//!
//! \tparam TPointType the type of the points acted on.
//!
//! \tparam TActionType the type of the action of elements of type
//! `TElementType` on points of type `TPointType`. This type should be a
//! stateless trivially default constructible class with a call operator of
//! signature `void(TPointType&, TPointType const&, TElementType const&)`
//! which computes the action of the 3rd argument on the 2nd argument, and
//! stores it in the 1st argument. See, for example,
//! ImageLeftAction and ImageRightAction.
//!
//! \tparam TTraits the type of a traits class with the requirements of
//! \ref ActionTraits.
//!
//! \tparam TLeftOrRight the libsemigroups::side of the action (i.e. if it is
//! is a left or a right action).
//!
//! \sa libsemigroups::side, ActionTraits, LeftAction, and RightAction.
//!
//! \par Example
//! \code
//! using namespace libsemigroups;
//! auto rg = ReportGuard(REPORT);
//! RightAction<PPerm<16>, PPerm<16>, ImageRightAction<PPerm<16>, PPerm<16>>>
//! o; o.add_seed(PPerm<16>::identity(16)); o.add_generator(
//! PPerm<16>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
//! {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0},
//! 16));
//! o.add_generator(
//! PPerm<16>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
//! {1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
//! 16));
//! o.add_generator(
//! PPerm<16>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
//! {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14},
//! 16));
//! o.add_generator(
//! PPerm<16>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14},
//! {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
//! 16));
//! o.reserve(70000);
//! o.size(); // 65536
//! o.digraph().number_of_scc(); // 17
//! \endcode
//!
//! \complexity
//! The time complexity is \f$O(mn)\f$ where \f$m\f$ is the total
//! number of points in the orbit and \f$n\f$ is the number of generators.
template <
typename TElementType,
typename TPointType,
typename TActionType,
typename TTraits,
side TLeftOrRight>
class Action :
public Runner,
private detail::BruidhinnTraits<TPointType> {
////////////////////////////////////////////////////////////////////////
// Action - typedefs - private
////////////////////////////////////////////////////////////////////////
using internal_point_type =
typename detail::BruidhinnTraits<TPointType>::internal_value_type;
using internal_const_point_type =
typename detail::BruidhinnTraits<TPointType>::internal_const_value_type;
using internal_const_reference =
typename detail::BruidhinnTraits<TPointType>::internal_const_reference;
struct InternalEqualTo :
private detail::BruidhinnTraits<TPointType> {
using EqualTo =
typename TTraits::EqualTo;
bool operator()(internal_const_point_type x,
internal_const_point_type y)
const {
return EqualTo()(this->to_external_const(x),
this->to_external_const(y));
}
};
struct InternalHash :
private detail::BruidhinnTraits<TPointType> {
using Hash =
typename TTraits::Hash;
size_t
operator()(internal_const_point_type x)
const {
return Hash()(this->to_external_const(x));
}
};
static_assert(
std::is_const<internal_const_point_type>::value
|| std::is_const<
typename std::remove_pointer<
internal_const_point_type>::type>::value,
"internal_const_element_type must be const or pointer to const");
public:
////////////////////////////////////////////////////////////////////////
// Action - typedefs - public
////////////////////////////////////////////////////////////////////////
//! The template parameter \p TElementType.
//!
//! Also the type of the elements of the semigroup.
using element_type = TElementType;
//! The template parameter \p TPointType.
//!
//! Also the type of the points acted on by this class.
using point_type = TPointType;
//! The type of a const reference to a \ref point_type.
using const_reference_point_type =
typename detail::BruidhinnTraits<TPointType>::const_reference;
//! The type of a const pointer to a \ref point_type.
using const_pointer_point_type =
typename detail::BruidhinnTraits<TPointType>::const_pointer;
//! The type of the index of a point.
using index_type = size_t;
//! The type of the index of a strongly connected component.
//!
//! \sa ActionDigraph::scc_index_type
using scc_index_type = ActionDigraph<size_t>::scc_index_type;
//! Type of the action of \ref element_type on \ref point_type.
//!
//! \sa ImageRightAction, ImageLeftAction
using action_type = TActionType;
////////////////////////////////////////////////////////////////////////
// Action - iterators - public
////////////////////////////////////////////////////////////////////////
//! The type of a const iterator pointing to a single strongly
//! connected component (scc).
using const_iterator_scc = ActionDigraph<size_t>::const_iterator_scc;
//! The type of a const iterator pointing to a strongly
//! connected component (scc).
using const_iterator_sccs = ActionDigraph<size_t>::const_iterator_sccs;
//! The type of a const iterator pointing to a representative of a
//! strongly connected component (scc).
using const_iterator_scc_roots
= ActionDigraph<size_t>::const_iterator_scc_roots;
//! The type of a const iterator pointing to a \ref point_type.
using const_iterator
= detail::BruidhinnConstIterator<point_type,
std::vector<internal_point_type>>;
private:
////////////////////////////////////////////////////////////////////////
// Action - internal types with call operators - private
////////////////////////////////////////////////////////////////////////
using ActionOp = TActionType;
using One =
typename TTraits::One;
using Product =
typename TTraits::Product;
using Swap =
typename TTraits::Swap;
static_assert(std::is_trivially_default_constructible<ActionOp>::value,
"the third template parameter TActionType is not trivially "
"default constructible");
// TODO(later) more static assertions
////////////////////////////////////////////////////////////////////////
// Action - product functions for left/right multiplication - private
////////////////////////////////////////////////////////////////////////
template <
typename TSfinae =
void>
auto internal_product(element_type& xy,
element_type
const& x,
element_type
const& y)
-> std::enable_if_t<side::right == TLeftOrRight, TSfinae> {
Product()(xy, x, y);
}
template <
typename TSfinae =
void>
auto internal_product(element_type& xy,
element_type
const& x,
element_type
const& y)
-> std::enable_if_t<side::left == TLeftOrRight, TSfinae> {
Product()(xy, y, x);
}
public:
////////////////////////////////////////////////////////////////////////
// Action - constructor + destructor - public
////////////////////////////////////////////////////////////////////////
//! Default constructor.
//!
//! A constructor that creates an Action instance representing a
//! left or right action.
//!
//! \exceptions
//! \no_libsemigroups_except
//!
//! \complexity
//! Constant.
//!
//! \par Parameters
//! (None)
Action()
: _gens(),
_graph(),
_map(),
_options(),
_orb(),
_pos(0),
_tmp_point(),
_tmp_point_init(
false) {}
//! Default copy constructor.
Action(Action
const&) =
default;
//! Default move constructor.
Action(Action&&) =
default;
//! Default copy assignment operator.
Action&
operator=(Action
const&) =
default;
//! Default move assignment operator.
Action&
operator=(Action&&) =
default;
~Action() {
if (_tmp_point_init) {
this->internal_free(_tmp_point);
}
for (
auto pt : _orb) {
this->internal_free(pt);
}
}
////////////////////////////////////////////////////////////////////////
// Action - modifiers - public
////////////////////////////////////////////////////////////////////////
//! Increase the capacity to a value that is greater or equal to \p val.
//!
//! \param val new capacity of an action instance.
//!
//! \returns (None)
//!
//! \throws std::length_error if \p val is too large.
//!
//! \throws std::bad_alloc or any exception thrown by the allocators of
//! private data members.
//!
//! \complexity
//! At most linear in the size() of the Action.
void reserve(size_t val) {
_graph.reserve(val, _gens.size());
_map.reserve(val);
_orb.reserve(val);
}
//! Add a seed to the action.
//!
//! A *seed* is just a starting point for the action, it will belong to the
//! action, as will every point that can be obtained from the seed by
//! acting with the generators of the action.
//!
//! \param seed the seed to add.
//!
//! \returns (None)
//!
//! \exceptions
//! \no_libsemigroups_except
//!
//! \complexity
//! At most linear in the size() of the action.
void add_seed(const_reference_point_type seed) {
auto internal_seed = this->internal_copy(this->to_internal_const(seed));
if (!_tmp_point_init) {
_tmp_point_init =
true;
_tmp_point = this->internal_copy(internal_seed);
}
_map.emplace(internal_seed, _orb.size());
_orb.push_back(internal_seed);
_graph.add_nodes(1);
}
//! Add a generator to the action. An Action instance represents the
//! action of the semigroup generated by the elements added via this
//! member function.
//!
//! \param gen the generator to add.
//!
//! \returns (None)
//!
//! \exceptions
//! \no_libsemigroups_except
//!
//! \complexity
//! At most linear in the size() of the action.
void add_generator(element_type gen) {
_gens.push_back(gen);
}
////////////////////////////////////////////////////////////////////////
// Action - member functions: position, empty, size, etc - public
////////////////////////////////////////////////////////////////////////
//! Returns the position of a point in the so far discovered
//! points.
//!
//! \param pt the point whose position is sought.
//!
//! \returns The index of \p pt in \c this or \ref UNDEFINED.
//!
//! \exceptions
//! \no_libsemigroups_except
//!
//! \complexity
//! Constant.
index_type position(const_reference_point_type pt)
const {
auto it = _map.find(this->to_internal_const(pt));
if (it != _map.end()) {
return (*it).second;
}
else {
return UNDEFINED;
}
}
//! Checks if the Action contains any points.
//!
//! \returns \c true if the action contains no points (including seeds),
//! and \c false if not.
//!
//! \exceptions
//! \noexcept
//!
//! \complexity
//! Constant.
//!
//! \par Parameters
//! (None)
bool empty()
const noexcept {
return _orb.empty();
}
//! Returns a const reference to the point in a given position.
//!
//! \param pos the index of an element.
//!
//! \returns
//! A \ref const_reference_point_type to the point in position
//! \p pos of the currently enumerated points.
//!
//! \exceptions
//! \noexcept
//!
//! \complexity
//! Constant.
inline const_reference_point_type
operator[](size_t pos)
const noexcept {
LIBSEMIGROUPS_ASSERT(pos < _orb.size());
return this->to_external_const(_orb[pos]);
}
//! Returns a const reference to the point in a given position.
//!
//! \param pos the index of an element.
//!
//! \returns
//! A \ref const_reference_point_type to the point in position \p
//! pos of the currently enumerated points.
//!
//! \throws
//! std::out_of_range if `!(pos < current_size())`.
//!
//! \complexity
//! Constant.
inline const_reference_point_type at(size_t pos)
const {
return this->to_external_const(_orb.at(pos));
}
//! Returns the size of the fully enumerated action.
//!
//! \returns The size of the action, a value of type \c size_t.
//!
//! \complexity
//! The time complexity is \f$O(mn)\f$ where \f$m\f$ is the total number of
//! points in the orbit and \f$n\f$ is the number of generators.
//!
//! \exceptions
//! \no_libsemigroups_except
//!
//! \par Parameters
//! (None)
size_t size() {
run();
return _orb.size();
}
//! Returns the number of points found so far.
//!
//! \returns
//! A value of \c size_t.
//!
//! \complexity
//! Constant.
//!
//! \exceptions
//! \noexcept
//!
//! \par Parameters
//! (None)
size_t current_size()
const noexcept {
return _orb.size();
}
//! Returns a \ref const_iterator (random access
//! iterator) pointing at the first point.
//!
//! \returns
//! A const iterator to the first point.
//!
//! \complexity
//! Constant.
//!
//! \exceptions
//! \noexcept
//!
//! \par Parameters
//! (None)
const_iterator cbegin()
const noexcept {
return const_iterator(_orb.cbegin());
}
//! Returns a \ref const_iterator (random access
//! iterator) pointing one past the last point.
//!
//! \returns
//! A const iterator to one past the end.
//!
//! \complexity
//! Constant.
//!
//! \exceptions
//! \noexcept
//!
//! \par Parameters
//! (None)
const_iterator cend()
const noexcept {
return const_iterator(_orb.cend());
}
////////////////////////////////////////////////////////////////////////
// Action - member functions: strongly connected components - public
////////////////////////////////////////////////////////////////////////
//! Returns whether or not we are caching scc multipliers.
//!
//! If the returned value of this function is \c true, then the values
//! returned by multiplier_from_scc_root() and multiplier_to_scc_root() are
//! cached, and not recomputed every time one of these functions is called.
//!
//! \returns
//! A value of type \c bool.
//!
//! \complexity
//! Constant.
//!
//! \exceptions
//! \noexcept
//!
//! \par Parameters
//! (None)
bool cache_scc_multipliers()
const noexcept {
return _options._cache_scc_multipliers;
}
//! Set whether or not to cache scc multipliers.
//!
//! If the parameter \p val is \c true, then the values returned by
//! multiplier_from_scc_root() and multiplier_to_scc_root() are cached, and
//! not recomputed every time one of these functions is called.
//!
//! \param val the value.
//!
//! \returns
//! A reference to \c this.
//!
//! \complexity
//! Constant.
//!
//! \exceptions
//! \noexcept
Action& cache_scc_multipliers(
bool val) noexcept {
_options._cache_scc_multipliers = val;
return *
this;
}
//! Returns a multiplier from a scc root to a given index.
//!
//! Returns an element \c x of the semigroup generated by the generators
//! in the action such that if \c r is the root of the strongly connected
//! component containing \c at(pos), then after `TActionType()(res, r,
//! x)` the point \c res equals \c at(pos).
//!
//! \param pos a position in the action.
//!
//! \returns An element of type \ref element_type.
//!
//! \complexity
//! At most \f$O(mn)\f$ where \f$m\f$ is the complexity of multiplying
//! elements of type \ref element_type and \f$n\f$ is the size of the fully
//! enumerated orbit.
//!
//! \throws LibsemigroupsException if there are no generators yet added
//! or the index \p pos is out of range.
element_type multiplier_from_scc_root(index_type pos) {
validate_gens();
validate_index(pos);
if (cache_scc_multipliers()) {
if (_multipliers_from_scc_root.
defined(pos)) {
return _multipliers_from_scc_root[pos];
}
_multipliers_from_scc_root.init(_graph.number_of_nodes(), _gens[0]);
index_type i = pos;
std::stack<index_type> visited;
while (!_multipliers_from_scc_root.
defined(i)
&& _graph.spanning_forest().parent(i) != UNDEFINED) {
visited.push(i);
_multipliers_from_scc_root[i]
= _gens[_graph.spanning_forest().label(i)];
i = _graph.spanning_forest().parent(i);
}
if (visited.empty()) {
// if pos is the scc root, then this can happen
_multipliers_from_scc_root.set_defined(pos);
}
else {
element_type tmp = One()(_gens[0]);
while (i != pos) {
index_type j = visited.top();
visited.pop();
Swap()(tmp, _multipliers_from_scc_root[j]);
internal_product(_multipliers_from_scc_root[j],
_multipliers_from_scc_root[i],
tmp);
_multipliers_from_scc_root.set_defined(j);
i = j;
}
}
return _multipliers_from_scc_root[pos];
}
else {
element_type out = One()(_gens[0]);
element_type tmp = One()(_gens[0]);
while (_graph.spanning_forest().parent(pos) != UNDEFINED) {
Swap()(tmp, out);
internal_product(
out, _gens[_graph.spanning_forest().label(pos)], tmp);
pos = _graph.spanning_forest().parent(pos);
}
return out;
}
}
//! Returns a multiplier from a given index to a scc root.
//!
//! Returns an element \c x of the semigroup generated by the generators
//! in the action such that after `TActionType()(res, at(pos), x)`
//! the point \c res is the root of the strongly connected component
//! containing \c at(pos).
//!
//! \param pos a position in the action.
//!
//! \returns An element of type \ref element_type.
//!
//! \complexity
//! At most \f$O(mn)\f$ where \f$m\f$ is the complexity of multiplying
//! elements of type \ref element_type and \f$n\f$ is the size of the fully
//! enumerated orbit.
//!
//! \throws LibsemigroupsException if there are no generators yet added
//! or the index \p pos is out of range.
element_type multiplier_to_scc_root(index_type pos) {
validate_gens();
validate_index(pos);
if (cache_scc_multipliers()) {
if (_multipliers_to_scc_root.
defined(pos)) {
return _multipliers_to_scc_root[pos];
}
_multipliers_to_scc_root.init(_graph.number_of_nodes(), _gens[0]);
index_type i = pos;
std::stack<index_type> visited;
while (!_multipliers_to_scc_root.
defined(i)
&& _graph.reverse_spanning_forest().parent(i) != UNDEFINED) {
visited.push(i);
_multipliers_to_scc_root[i]
= _gens[_graph.reverse_spanning_forest().label(i)];
i = _graph.reverse_spanning_forest().parent(i);
}
if (visited.empty()) {
// if pos is the scc root, then this can happen
_multipliers_to_scc_root.set_defined(pos);
}
else {
element_type tmp = One()(_gens[0]);
while (i != pos) {
index_type j = visited.top();
visited.pop();
Swap()(tmp, _multipliers_to_scc_root[j]);
internal_product(
_multipliers_to_scc_root[j], tmp, _multipliers_to_scc_root[i]);
_multipliers_to_scc_root.set_defined(j);
i = j;
}
}
return _multipliers_to_scc_root[pos];
}
else {
element_type out = One()(_gens[0]);
element_type tmp = One()(_gens[0]);
while (_graph.reverse_spanning_forest().parent(pos) != UNDEFINED) {
Swap()(tmp, out);
internal_product(
out, tmp, _gens[_graph.reverse_spanning_forest().label(pos)]);
pos = _graph.reverse_spanning_forest().parent(pos);
}
return out;
}
}
//! Returns a const reference to the root point of a strongly connected
//! component.
//!
//! \param x the point whose root we want to find.
//!
//! \returns A value of type \ref const_reference_point_type.
//!
//! \complexity
//! At most \f$O(mn)\f$ where \f$m\f$ is the complexity of multiplying
//! elements of type \ref element_type and \f$n\f$ is the size of the fully
//! enumerated orbit.
//!
//! \throws LibsemigroupsException if the point \p x does not belong to
//! the action.
const_reference_point_type root_of_scc(const_reference_point_type x) {
return this->to_external_const(_orb[_graph.root_of_scc(position(x))]);
}
//! Returns a const reference to the root point of a strongly connected
//! component.
//!
//! \param pos the index of the point in the action whose root we want to
//! find.
//!
//! \returns A value of type \ref const_reference_point_type.
//!
//! \complexity
//! At most \f$O(mn)\f$ where \f$m\f$ is the complexity of multiplying
//! elements of type \ref element_type and \f$n\f$ is the size of the fully
//! enumerated orbit.
//!
//! \throws LibsemigroupsException if the index \p pos is out of range.
const_reference_point_type root_of_scc(index_type pos) {
return this->to_external_const(_orb[_graph.root_of_scc(pos)]);
}
//! Returns the digraph of the completely enumerated action.
//!
//! \returns A const reference to an ActionDigraph<size_t>.
//!
//! \complexity
//! At most \f$O(mn)\f$ where \f$m\f$ is the complexity of multiplying
//! elements of type \ref element_type and \f$n\f$ is the size of the fully
//! enumerated orbit.
//!
//! \exceptions
//! \no_libsemigroups_except
//!
//! \par Parameters
//! (None)
ActionDigraph<size_t>
const& digraph() {
run();
return _graph;
}
private:
////////////////////////////////////////////////////////////////////////
// Runner - pure virtual member functions - private
////////////////////////////////////////////////////////////////////////
bool finished_impl()
const override {
return (_pos == _orb.size()) && (_graph.out_degree() == _gens.size());
}
void run_impl() override {
size_t old_nr_gens = _graph.out_degree();
_graph.add_to_out_degree(_gens.size() - _graph.out_degree());
if (started() && old_nr_gens < _gens.size()) {
// Generators were added after the last call to run
for (size_t i = 0; i < _pos; i++) {
for (size_t j = old_nr_gens; j < _gens.size(); ++j) {
ActionOp()(this->to_external(_tmp_point),
this->to_external_const(_orb[i]),
_gens[j]);
auto it = _map.find(_tmp_point);
if (it == _map.end()) {
_graph.add_nodes(1);
_graph.add_edge(i, _orb.size(), j);
_orb.push_back(this->internal_copy(_tmp_point));
_map.emplace(_orb.back(), _orb.size() - 1);
}
else {
_graph.add_edge(i, (*it).second, j);
}
}
}
}
for (; _pos < _orb.size() && !stopped(); ++_pos) {
for (size_t j = 0; j < _gens.size(); ++j) {
ActionOp()(this->to_external(_tmp_point),
this->to_external_const(_orb[_pos]),
_gens[j]);
auto it = _map.find(_tmp_point);
if (it == _map.end()) {
_graph.add_nodes(1);
_graph.add_edge(_pos, _orb.size(), j);
_orb.push_back(this->internal_copy(_tmp_point));
_map.emplace(_orb.back(), _orb.size() - 1);
}
else {
_graph.add_edge(_pos, (*it).second, j);
}
}
if (report()) {
REPORT_DEFAULT(
"found %d points, so far\n", _orb.size());
}
}
report_why_we_stopped();
}
////////////////////////////////////////////////////////////////////////
// Action - member functions - private
////////////////////////////////////////////////////////////////////////
void validate_index(index_type i)
const {
if (i > _orb.size()) {
LIBSEMIGROUPS_EXCEPTION(
"index out of range, expected value in [0, %d) but found %d",
current_size(),
i);
}
}
void validate_gens()
const {
if (_gens.empty()) {
LIBSEMIGROUPS_EXCEPTION(
"no generators defined, this methods cannot be "
"used until at least one generator is added")
}
}
class MultiplierCache {
public:
element_type&
operator[](index_type i) {
return _multipliers[i].second;
}
bool defined(index_type i)
const {
return (i < _multipliers.size() ? _multipliers[i].first :
false);
}
bool set_defined(index_type i) {
LIBSEMIGROUPS_ASSERT(i < _multipliers.size());
return _multipliers[i].first =
true;
}
void init(index_type N, element_type
const& sample) {
if (N > _multipliers.size()) {
_multipliers.resize(N, {
false, One()(sample)});
}
}
private:
std::vector<std::pair<
bool, element_type>> _multipliers;
};
////////////////////////////////////////////////////////////////////////
// Action - data members - private
////////////////////////////////////////////////////////////////////////
std::vector<element_type> _gens;
ActionDigraph<size_t> _graph;
std::unordered_map<internal_const_point_type,
size_t,
InternalHash,
InternalEqualTo>
_map;
struct Options {
Options() : _cache_scc_multipliers(
false) {}
Options(Options
const&) =
default;
Options(Options&&) =
default;
Options&
operator=(Options
const&) =
default;
Options&
operator=(Options&&) =
default;
bool _cache_scc_multipliers;
} _options;
std::vector<internal_point_type> _orb;
MultiplierCache _multipliers_from_scc_root;
MultiplierCache _multipliers_to_scc_root;
size_t _pos;
internal_point_type _tmp_point;
bool _tmp_point_init;
};
//! This is a traits class for use with Action, \ref LeftAction,
//! and \ref RightAction.
//!
//! \tparam TElementType the type of the elements.
//! \tparam TPointType the type of the points acted on.
//!
//! \sa Action.
template <
typename TElementType,
typename TPointType>
struct ActionTraits {
//! \copydoc libsemigroups::Hash
using Hash = ::libsemigroups::Hash<TPointType>;
//! \copydoc libsemigroups::EqualTo
using EqualTo = ::libsemigroups::EqualTo<TPointType>;
//! \copydoc libsemigroups::Swap
using Swap = ::libsemigroups::Swap<TElementType>;
//! \copydoc libsemigroups::One
using One = ::libsemigroups::One<TElementType>;
//! \copydoc libsemigroups::Product
using Product = ::libsemigroups::Product<TElementType>;
};
//! This class represents the right action of a semigroup on a set.
//!
//! \sa Action for further details.
template <
typename TElementType,
typename TPointType,
typename TActionType,
typename TTraits = ActionTraits<TElementType, TPointType>>
using RightAction
= Action<TElementType, TPointType, TActionType, TTraits, side::right>;
//! This class represents the left action of a semigroup on a set.
//!
//! \sa Action for further details.
template <
typename TElementType,
typename TPointType,
typename TActionType,
typename TTraits = ActionTraits<TElementType, TPointType>>
using LeftAction
= Action<TElementType, TPointType, TActionType, TTraits, side::left>;
}
// namespace libsemigroups
#endif // LIBSEMIGROUPS_ACTION_HPP_