diff --git a/Makefile.am b/Makefile.am index 9daaa10d7..7509c1771 100644 --- a/Makefile.am +++ b/Makefile.am @@ -106,7 +106,7 @@ pkginclude_HEADERS += include/libsemigroups/ukkonen.hpp pkginclude_HEADERS += include/libsemigroups/ukkonen.tpp pkginclude_HEADERS += include/libsemigroups/word-graph.hpp pkginclude_HEADERS += include/libsemigroups/word-graph.tpp -pkginclude_HEADERS += include/libsemigroups/words.hpp +pkginclude_HEADERS += include/libsemigroups/word-range.hpp detailincludedir = $(includedir)/libsemigroups/detail detailinclude_HEADERS = include/libsemigroups/detail/bruidhinn-traits.hpp @@ -656,7 +656,7 @@ libsemigroups_la_SOURCES += src/to-knuth-bendix.cpp libsemigroups_la_SOURCES += src/to-todd-coxeter.cpp libsemigroups_la_SOURCES += src/todd-coxeter.cpp libsemigroups_la_SOURCES += src/ukkonen.cpp -libsemigroups_la_SOURCES += src/words.cpp +libsemigroups_la_SOURCES += src/word-range.cpp libsemigroups_la_SOURCES += src/detail/felsch-tree.cpp libsemigroups_la_SOURCES += src/detail/ke.cpp diff --git a/benchmarks/bench-kambites.cpp b/benchmarks/bench-kambites.cpp index 20467b27e..dd59d9770 100644 --- a/benchmarks/bench-kambites.cpp +++ b/benchmarks/bench-kambites.cpp @@ -29,7 +29,7 @@ #include "libsemigroups/kambites.hpp" // for Kambites #include "libsemigroups/knuth-bendix.hpp" // for KnuthBendix #include "libsemigroups/ranges.hpp" // for operator|, to_vector -#include "libsemigroups/words.hpp" // for StringRange +#include "libsemigroups/word-range.hpp" // for StringRange namespace libsemigroups { using detail::MultiStringView; diff --git a/benchmarks/bench-siso.cpp b/benchmarks/bench-siso.cpp index a02cc5116..3ef3aa63d 100644 --- a/benchmarks/bench-siso.cpp +++ b/benchmarks/bench-siso.cpp @@ -19,8 +19,8 @@ #include "bench-main.hpp" // for LIBSEMIGROUPS_BENCHMARK #include "catch.hpp" // for REQUIRE, REQUIRE_NOTHROW, REQUIRE_THROWS_AS -#include "libsemigroups/order.hpp" // for LexicographicalCompare -#include "libsemigroups/words.hpp" // for number_of_words +#include "libsemigroups/order.hpp" // for LexicographicalCompare +#include "libsemigroups/word-range.hpp" // for number_of_words namespace libsemigroups { diff --git a/benchmarks/bench-todd-coxeter.cpp b/benchmarks/bench-todd-coxeter.cpp index f77b5b37c..2f73104b7 100644 --- a/benchmarks/bench-todd-coxeter.cpp +++ b/benchmarks/bench-todd-coxeter.cpp @@ -31,7 +31,7 @@ #include "libsemigroups/presentation.hpp" #include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter, ToddCoxeter::... #include "libsemigroups/types.hpp" // for word_type, letter_type -#include "libsemigroups/words.hpp" // for operator""_p +#include "libsemigroups/word-range.hpp" // for operator""_p #include "libsemigroups/detail/report.hpp" // for ReportGuard // diff --git a/benchmarks/bench-wilo.cpp b/benchmarks/bench-wilo.cpp index 04434030f..7b67455c2 100644 --- a/benchmarks/bench-wilo.cpp +++ b/benchmarks/bench-wilo.cpp @@ -19,8 +19,8 @@ #include "bench-main.hpp" // for LIBSEMIGROUPS_BENCHMARK #include "catch.hpp" // for REQUIRE, REQUIRE_NOTHROW, REQUIRE_THROWS_AS -#include "libsemigroups/order.hpp" // for lexicographical_compare -#include "libsemigroups/words.hpp" // for number_of_words +#include "libsemigroups/order.hpp" // for lexicographical_compare +#include "libsemigroups/word-range.hpp" // for number_of_words namespace libsemigroups { namespace { diff --git a/benchmarks/bench-wislo.cpp b/benchmarks/bench-wislo.cpp index 2c10d7016..c2bed78c6 100644 --- a/benchmarks/bench-wislo.cpp +++ b/benchmarks/bench-wislo.cpp @@ -19,8 +19,8 @@ #include "bench-main.hpp" // for LIBSEMIGROUPS_BENCHMARK #include "catch.hpp" // for REQUIRE, REQUIRE_NOTHROW, REQUIRE_THROWS_AS -#include "libsemigroups/order.hpp" // for shortlex_compare -#include "libsemigroups/words.hpp" // for number_of_words +#include "libsemigroups/order.hpp" // for shortlex_compare +#include "libsemigroups/word-range.hpp" // for number_of_words namespace libsemigroups { namespace { diff --git a/include/libsemigroups/detail/ke.hpp b/include/libsemigroups/detail/ke.hpp index 35185c387..dfb15653c 100644 --- a/include/libsemigroups/detail/ke.hpp +++ b/include/libsemigroups/detail/ke.hpp @@ -37,7 +37,7 @@ #include "libsemigroups/order.hpp" // for shortlex_compare #include "libsemigroups/presentation.hpp" // for to_word #include "libsemigroups/types.hpp" // for word_type, tril -#include "libsemigroups/words.hpp" // for ToWord, ToString +#include "libsemigroups/word-range.hpp" // for ToWord, ToString namespace libsemigroups { namespace detail { diff --git a/include/libsemigroups/detail/string.hpp b/include/libsemigroups/detail/string.hpp index 8dddfd666..9b40e6513 100644 --- a/include/libsemigroups/detail/string.hpp +++ b/include/libsemigroups/detail/string.hpp @@ -234,7 +234,7 @@ namespace libsemigroups { // Returns the string s to the power N, not optimized, complexity is O(N * // |s|) - // TODO move to words.hpp and/or remove + // TODO move to word-range.hpp and/or remove std::string power_string(std::string const& s, size_t N); std::string group_digits(int64_t num); diff --git a/include/libsemigroups/knuth-bendix.hpp b/include/libsemigroups/knuth-bendix.hpp index 74e4ee48f..de2d32b2b 100644 --- a/include/libsemigroups/knuth-bendix.hpp +++ b/include/libsemigroups/knuth-bendix.hpp @@ -57,7 +57,7 @@ #include "to-presentation.hpp" // for to_presentation #include "types.hpp" // for word_type #include "word-graph.hpp" // for WordGraph -#include "words.hpp" // for to_strings +#include "word-range.hpp" // for to_strings #include "detail/multi-string-view.hpp" // for MultiStringView #include "detail/report.hpp" // for Reporter, REPORT_DEFAULT, REP... diff --git a/include/libsemigroups/libsemigroups.hpp b/include/libsemigroups/libsemigroups.hpp index eefe665d5..4796df7cf 100644 --- a/include/libsemigroups/libsemigroups.hpp +++ b/include/libsemigroups/libsemigroups.hpp @@ -74,6 +74,6 @@ #include "types.hpp" #include "ukkonen.hpp" #include "word-graph.hpp" -#include "words.hpp" +#include "word-range.hpp" #endif // LIBSEMIGROUPS_LIBSEMIGROUPS_HPP_ diff --git a/include/libsemigroups/paths.hpp b/include/libsemigroups/paths.hpp index 7a8e78b7a..101fc1810 100644 --- a/include/libsemigroups/paths.hpp +++ b/include/libsemigroups/paths.hpp @@ -42,7 +42,7 @@ #include "ranges.hpp" // for is_input_range #include "types.hpp" // for word_type #include "word-graph.hpp" // for WordGraph -#include "words.hpp" // for number_of_words +#include "word-range.hpp" // for number_of_words #include "detail/containers.hpp" // for DynamicArray2 #include "detail/path-iterators.hpp" // for default_postfix_increment diff --git a/include/libsemigroups/presentation.hpp b/include/libsemigroups/presentation.hpp index 114dfb7eb..26c4dcc39 100644 --- a/include/libsemigroups/presentation.hpp +++ b/include/libsemigroups/presentation.hpp @@ -42,14 +42,14 @@ #include // for move, pair #include // for vector, operator!= -#include "adapters.hpp" // for Hash, EqualTo -#include "constants.hpp" // for Max, UNDEFINED, operator== -#include "debug.hpp" // for LIBSEMIGROUPS_ASSERT -#include "order.hpp" // for ShortLexCompare -#include "ranges.hpp" // for seq, operator|, rx, take, chain, is_sorted -#include "types.hpp" // for word_type -#include "ukkonen.hpp" // for GreedyReduceHelper, Ukkonen -#include "words.hpp" // for operator+ +#include "adapters.hpp" // for Hash, EqualTo +#include "constants.hpp" // for Max, UNDEFINED, operator== +#include "debug.hpp" // for LIBSEMIGROUPS_ASSERT +#include "order.hpp" // for ShortLexCompare +#include "ranges.hpp" // for seq, operator|, rx, take, chain, is_sorted +#include "types.hpp" // for word_type +#include "ukkonen.hpp" // for GreedyReduceHelper, Ukkonen +#include "word-range.hpp" // for operator+ #include "detail/fmt.hpp" // for format #include "detail/string.hpp" // for maximum_common_prefix diff --git a/include/libsemigroups/words.hpp b/include/libsemigroups/words.hpp deleted file mode 100644 index 27aba6b67..000000000 --- a/include/libsemigroups/words.hpp +++ /dev/null @@ -1,2402 +0,0 @@ -// -// libsemigroups - C++ library for semigroups and monoids -// Copyright (C) 2020-2023 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 . -// - -// This file contains declarations for function related to words (counting, and -// converting) in libsemigroups. - -// TODO: -// * iwyu -// * nodiscard -// * tests for code coverage - -#ifndef LIBSEMIGROUPS_WORDS_HPP_ -#define LIBSEMIGROUPS_WORDS_HPP_ - -#ifndef PARSED_BY_DOXYGEN -#define NOT_PARSED_BY_DOXYGEN -#endif - -#include // for size_t -#include // for uint64_t, uint8_t -#include // for initializer_list -#include // for numeric_limits -#include // for basic_string -#include // for string_view -#include // for enable_if_t -#include // for vector, operator== -#include // for forward -#include // for visit, operator== -#include // for vector, operator== - -#include "debug.hpp" // for LIBSEMIGROUPS_ASSERT -#include "exception.hpp" // for LibsemigroupsException -#include "types.hpp" // for word_type - -#include "detail/word-iterators.hpp" // for const_wilo_iterator - -#include "ranges.hpp" // for begin, end - -namespace libsemigroups { - - enum class Order : uint8_t; // forward decl - - namespace detail { - std::string const& chars_in_human_readable_order(); - } - // TODO(0) doc - template - Word& reverse(Word&& w) { - std::reverse(w.begin(), w.end()); - return w; - } - - //////////////////////////////////////////////////////////////////////// - // Words - //////////////////////////////////////////////////////////////////////// - - //! \defgroup words_group Words - //! This page contains details of the functionality in \c libsemigroups - //! related to generating words in a given range and in a given order. - //! - //! This file contains documentation for functionality for: - //! - //! * generating words and strings in a given range and in a certain order: - //! - \ref WordRange - //! - \ref StringRange - //! - \ref random_word - //! - \ref random_string - //! - \ref random_strings - //! - //! * counting words: - //! - //! - \ref number_of_words - //! - //! * converting to and from strings and words: - //! - //! - \ref ToWord - //! - \ref ToString - //! - //! * parsing algebraic expressions in a string: - //! - //! - \ref literal_operator_p - - //! \ingroup words_group - //! \brief Returns the number of words over an alphabet with a given number of - //! letters with length in a specified range. - //! - //! Returns the number of words over an alphabet with a given number of - //! letters with length in a specified range. - //! - //! \param n the number of letters in the alphabet. - //! \param min the minimum length of a word. - //! \param max one greater than the maximum length of a word. - //! - //! \returns - //! A value of type \c uint64_t. - //! - //! \exceptions - //! \no_libsemigroups_except - //! - //! \warning If the number of words exceeds 2 ^ 64 - 1, then the return value - //! of this function will not be correct. - [[nodiscard]] uint64_t number_of_words(size_t n, size_t min, size_t max); - - //! \ingroup words_group - //! \brief Returns a random word. - //! - //! Returns a random word on \f$\{0, \ldots, n - 1\}\f$ of length \p length - //! where \f$n\f$ is \p nr_letters. - //! - //! \param length the length of the word. - //! \param nr_letters the size of the alphabet. - //! - //! \returns A random word, value of `word_type`. - //! - //! \throws LibsemigroupsException if \p nr_letters is \c 0. - //! - //! \sa \ref random_string - [[nodiscard]] word_type random_word(size_t length, size_t nr_letters); - - //! \ingroup words_group - //! \brief Returns a forward iterator pointing to the 3rd parameter \p first. - //! - //! Returns a forward iterator used to iterate over words in lexicographic - //! order (wilo). If incremented, the iterator will point to the next least - //! lexicographic word after \p first over an \p n letter alphabet with length - //! less than \p upper_bound. Iterators of the type returned by this function - //! are equal whenever they are obtained by advancing the return value of any - //! call to \c cbegin_wilo by the same amount, or they are both obtained by - //! any call to \c cend_wilo. - //! - //! \param n the number of letters in the alphabet. - //! \param upper_bound only words of length less than this value are - //! considered. - //! \param first the starting point for the iteration. - //! \param last the value one past the end of the last value in the - //! iteration. - //! - //! \returns An iterator pointing to \p first. - //! - //! \exceptions - //! \no_libsemigroups_except - //! - //! \note - //! The parameter \p upper_bound is required because lexicographical - //! ordering is not a well-ordering, and there might be infinitely many words - //! between a given pair of words. - //! - //! \warning - //! Copying iterators of this type is expensive. As a consequence, prefix - //! incrementing \c ++it the iterator \c it returned by \c cbegin_wilo is - //! significantly cheaper than postfix incrementing \c it++. - //! - //! \warning - //! Iterators constructed using different parameters may not be equal, so - //! best not to loop over them. - //! - //! \sa cend_wilo - //! - //! \par Example - //! \code - //! std::vector(cbegin_wilo(2, 3, {0}, {1, 1, 1}), - //! cend_wilo(2, 3, {0}, {1, 1, 1})); - //! // {{0}, {0, 0}, {0, 1}, {1}, {1, 0}, {1, 1}}; - //! \endcode - [[nodiscard]] detail::const_wilo_iterator cbegin_wilo(size_t n, - size_t upper_bound, - word_type&& first, - word_type&& last); - - //! \ingroup words_group - //! \brief Returns a forward iterator pointing to the 3rd parameter \p first. - //! \copydoc cbegin_wilo(size_t, size_t, word_type&&, word_type&&) - [[nodiscard]] detail::const_wilo_iterator cbegin_wilo(size_t n, - size_t upper_bound, - word_type const& first, - word_type const& last); - - //! \ingroup words_group - //! \brief Returns a forward iterator pointing to one after the end of the - //! range from \p first to \p last. - //! - //! The iterator returned by this function is still dereferenceable and - //! incrementable, but does not point to a word in the correct range. - //! - //! \sa cbegin_wilo - [[nodiscard]] detail::const_wilo_iterator - cend_wilo(size_t n, size_t upper_bound, word_type&& first, word_type&& last); - - //! \ingroup words_group - //! \brief Returns a forward iterator pointing to one after the end of the - //! range from \p first to \p last. - //! \copydoc cend_wilo(size_t, size_t, word_type&&, word_type&&) - [[nodiscard]] detail::const_wilo_iterator cend_wilo(size_t n, - size_t upper_bound, - word_type const& first, - word_type const& last); - - //! \ingroup words_group - //! \brief Returns a forward iterator pointing to the 2nd parameter \p first. - //! - //! Returns a forward iterator used to iterate over words in - //! short-lexicographic order (wislo). If incremented, the iterator will point - //! to the next least short-lex word after \p w over an \p n letter alphabet. - //! Iterators of the type returned by this function are equal whenever they - //! are obtained by advancing the return value of any call to \c cbegin_wislo - //! by the same amount, or they are both obtained by any call to \c - //! cend_wislo. - //! - //! \param n the number of letters in the alphabet. - //! \param first the starting point for the iteration. - //! \param last the ending point for the iteration. - //! - //! \returns An iterator pointing to \p first. - //! - //! \exceptions - //! \no_libsemigroups_except - //! - //! \warning - //! Copying iterators of this type is expensive. As a consequence, prefix - //! incrementing \c ++it the iterator \c it returned by \c cbegin_wislo is - //! significantly cheaper than postfix incrementing \c it++. - //! - //! \warning - //! Iterators constructed using different parameters may not be equal, so - //! best not to loop over them. - //! - //! \sa cend_wislo - //! - //! \par Example - //! \code - //! std::vector(cbegin_wislo(2, {0}, {0, 0, 0}), - //! cend_wislo(2, {0}, {0, 0, 0})); - //! // {{0}, {1}, {0, 0}, {0, 1}, {1, 0}, {1, 1}}; - //! \endcode - [[nodiscard]] detail::const_wislo_iterator cbegin_wislo(size_t n, - word_type&& first, - word_type&& last); - - //! \ingroup words_group - //! \brief Returns a forward iterator pointing to the 2nd parameter \p first. - //! \copydoc cbegin_wislo(size_t const, word_type&&, word_type&&) - [[nodiscard]] detail::const_wislo_iterator - cbegin_wislo(size_t n, word_type const& first, word_type const& last); - - //! \ingroup words_group - //! \brief Returns a forward iterator pointing to one after the end of the - //! range from \p first to \p last. - //! - //! The iterator returned by this is still dereferenceable and incrementable, - //! but does not point to a word in the correct range. - //! - //! \sa cbegin_wislo - [[nodiscard]] detail::const_wislo_iterator cend_wislo(size_t n, - word_type&& first, - word_type&& last); - - //! \ingroup words_group - //! \brief Returns a forward iterator pointing to one after the end of the - //! range from \p first to \p last. - //! \copydoc cend_wislo(size_t const, word_type&&, word_type&&) - [[nodiscard]] detail::const_wislo_iterator cend_wislo(size_t n, - word_type const& first, - word_type const& last); - - //! \ingroup words_group - //! \brief Class for generating words in a given range and in a particular - //! order. - //! - //! Defined in `words.hpp`. - //! - //! This class implements a range object for the lower level - //! functions \ref cbegin_wislo and \ref cbegin_wilo. The purpose of this - //! class is to provide a more user-friendly interface with \ref cbegin_wislo - //! and \ref cbegin_wilo. - //! - //! \note - //! There is a small overhead to using a WordRange object rather than using - //! \ref cbegin_wislo or \ref cbegin_wilo directly. - //! - //! The order and range of the words in a WordRange instance can be set using - //! the member functions: - //! * \ref order - //! * \ref alphabet_size - //! * \ref min - //! * \ref max - //! * \ref first - //! * \ref last - //! - //! \par Example - //! \code - //! WordRange words; - //! words.order(Order::shortlex) // words in shortlex order - //! .alphabet_size(2) // on 2 letters - //! .min(1) // of length in the range from 1 - //! .max(5); // to 5 - //! \endcode - class WordRange { - public: - //! Alias for the size type. - using size_type = typename std::vector::size_type; - - //! The type of the output of a WordRange object. - using output_type = word_type const&; - - private: - using const_iterator = std::variant; - - size_type _alphabet_size; - mutable const_iterator _current; - mutable const_iterator _end; - mutable bool _current_valid; - word_type _first; - word_type _last; - Order _order; - size_type _upper_bound; - mutable size_type _visited; - - void set_iterator() const; - - public: - //! \brief Get the current value. - //! - //! Returns the current word in a WordRange object. - //! - //! \returns A value of type \ref output_type. - //! - //! \exceptions - //! \noexcept - //! - //! \warning If at_end() returns \c true, then the return value of this - //! function could be anything. - [[nodiscard]] output_type get() const noexcept { - set_iterator(); - return std::visit( - [](auto& it) -> auto const& { return *it; }, _current); - } - - //! \brief Advance to the next value. - //! - //! Advances a WordRange object to the next value (if any). - //! - //! \exceptions - //! \noexcept - //! - //! \sa \ref at_end - void next() noexcept { - set_iterator(); - if (!at_end()) { - ++_visited; - } - std::visit([](auto& it) { ++it; }, _current); - } - - //! \brief Check if the range object is exhausted. - //! - //! Returns \c true if a WordRange object is exhausted, and \c false if not. - //! \returns A value of type \c bool. - //! - //! \exceptions - //! \noexcept - [[nodiscard]] bool at_end() const noexcept { - set_iterator(); - return _current == _end; - } - - //! \brief The possible size of the range. - //! - //! Returns the number of words in a WordRange object if order() is - //! Order::shortlex. If order() is not Order::shortlex, then the return - //! value of this function is meaningless. - //! - //! \returns A value of type \c size_t. - //! - //! \exceptions - //! \noexcept - [[nodiscard]] size_t size_hint() const noexcept { - return number_of_words(_alphabet_size, _first.size(), _last.size()) - - _visited; - // This is only the actual size if _order is shortlex - } - - //! \brief The actual size of the range. - //! - //! Returns the number of words in a WordRange object. If order() is - //! Order::shortlex, then size_hint() is used. If order() is not - //! Order::shortlex, then a copy of the range may have to be looped over in - //! order to find the return value of this function. - //! - //! \returns A value of type \c size_t. - //! - //! \exceptions - //! \noexcept - [[nodiscard]] size_t count() const noexcept; - - // For some reason, there needs to be two doxygen comment lines here for - // this to render. - //! Value indicating that the range is finite. - //! - static constexpr bool is_finite = true; // This may not always be true - - //! Value indicating that if get() is called twice on a WordRange object - //! that is not changed between the two calls, then the return value of - //! get() is the same both times. - static constexpr bool is_idempotent = true; - - //! \brief Default constructor. - //! - //! Constructs an empty range with: - //! * min() equal to \c 0; - //! * max() equal to \c 0; - //! * order() equal to Order::shortlex; - //! * first() equal to the empty word; - //! * last() equal to the empty word; - //! * upper_bound() equal to \c 0; - //! * alphabet_size() equal to \c 0. - WordRange() { - init(); - } - - //! \brief Initialize an existing WordRange object. - //! - //! This function puts a WordRange object back into the same state as if it - //! had been newly default constructed. - //! - //! \returns A reference to \c *this. - //! - //! \exceptions - //! \no_libsemigroups_except - WordRange& init(); - - //! \brief Default copy constructor. - //! - //! Default copy constructor. - WordRange(WordRange const&); - - //! \brief Default move constructor. - //! - //! Default move constructor. - WordRange(WordRange&&); - - //! \brief Default copy assignment operator. - //! - //! Default copy assignment operator. - WordRange& operator=(WordRange const&); - - //! \brief Default move assignment operator. - //! - //! Default move assignment operator. - WordRange& operator=(WordRange&&); - - //! \brief Default destructor. - //! - //! Default destructor. - ~WordRange(); - - //! \brief Set the number of letters in the alphabet. - //! - //! Sets the number of letters in a WordRange object to \p n. - //! - //! \param n the number of letters. - //! - //! \returns A reference to \c *this. - //! - //! \exceptions - //! \noexcept - WordRange& alphabet_size(size_type n) noexcept { - _current_valid &= (n == _alphabet_size); - _alphabet_size = n; - return *this; - } - - //! \brief The current number of letters in the alphabet. - //! - //! Returns the current number of letters in a WordRange object. - //! - //! \returns A value of type \ref size_type. - //! - //! \exceptions - //! \noexcept - [[nodiscard]] size_type alphabet_size() const noexcept { - return _alphabet_size; - } - - //! \brief Set the first word in the range. - //! - //! Sets the first word in a WordRange object to be \p frst. This function - //! performs no checks on its arguments. If \p frst contains letters greater - //! than alphabet_size(), then the WordRange object will be empty. - //! Similarly, if first() is greater than last() with respect to order(), - //! then the object will be empty. - //! - //! \param frst the first word. - //! - //! \returns A reference to \c *this. - //! - //! \sa \ref min - WordRange& first(word_type const& frst) { - _current_valid &= (frst == _first); - _first = frst; - return *this; - } - - //! \brief The current first word in the range. - //! - //! Returns the first word in a WordRange object. - //! - //! \returns A const reference to a \ref word_type. - //! - //! \exceptions - //! \noexcept - //! - //! \sa \ref min - [[nodiscard]] word_type const& first() const noexcept { - return _first; - } - - //! \brief Set one past the last word in the range. - //! - //! Sets one past the last word in a WordRange object to be \p lst. This - //! function performs no checks on its arguments. If \p lst contains - //! letters greater than alphabet_size(), then the WordRange object will be - //! empty. - //! - //! \param lst one past the last word. - //! - //! \returns A reference to \c *this. - //! - //! \sa \ref max - WordRange& last(word_type const& lst) { - _current_valid &= (lst == _last); - _last = lst; - return *this; - } - - //! \brief The current one past the last word in the range. - //! - //! Returns one past the last word in a WordRange object. - //! - //! \returns A const reference to a \ref word_type. - //! - //! \exceptions - //! \noexcept - //! - //! \sa \ref max - [[nodiscard]] word_type const& last() const noexcept { - return _last; - } - - //! \brief Set the order of the words in the range. - //! - //! Sets the order of the words in a WordRange object to \p val. - //! - //! \param val the order. - //! - //! \returns A reference to \c *this. - //! - //! \throws LibsemigroupsException if val is not Order::shortlex or - //! Order::lex. - WordRange& order(Order val); - - //! \brief The current order of the words in the range. - //! - //! Returns the current order of the words in a WordRange object. - //! - //! \returns A value of type \ref Order. - //! - //! \exceptions - //! \noexcept - [[nodiscard]] Order order() const noexcept { - return _order; - } - - //! \brief Set an upper bound for the length of a word in the range. - //! - //! Sets an upper bound for the length of a word in a WordRange object. - //! This setting is only used if order() is Order::lex. - //! - //! \param n the upper bound. - //! - //! \returns A reference to \c *this. - //! - //! \exceptions - //! \no_libsemigroups_except - WordRange& upper_bound(size_type n) { - _current_valid &= (n == _upper_bound); - _upper_bound = n; - return *this; - } - - //! \brief The current upper bound on the length of a word in the range. - //! - //! Returns the current upper bound on the length of a word in a WordRange - //! object. This setting is only used if order() is Order::lex. - //! - //! \returns A value of type \ref size_type. - //! - //! \exceptions - //! \noexcept - [[nodiscard]] size_type upper_bound() const noexcept { - return _upper_bound; - } - - //! \brief Set the first word in the range by length. - //! - //! Sets the first word in a WordRange object to be `pow(0_w, val)` (the - //! word consisting of \p val letters equal to \c 0). - //! - //! \param val the exponent. - //! - //! \returns A reference to \c *this. - //! - //! \exceptions - //! \no_libsemigroups_except - WordRange& min(size_type val) { - first(word_type(val, 0)); - return *this; - } - - // No corresponding getter for min, because what would it mean? Could be the - // length of first(), but that doesn't correspond well to what happens with - // the setter. - - //! \brief Set one past the last word in the range by length. - //! - //! Sets one past the last word in a WordRange object to be `pow(0_w, val)` - //! (the word consisting of \p val letters equal to \c 0). - //! - //! \param val the exponent. - //! - //! \returns A reference to \c *this. - //! - //! \exceptions - //! \no_libsemigroups_except - WordRange& max(size_type val) { - last(word_type(val, 0)); - return *this; - } - - //! \brief Returns an input iterator pointing to the first word in the - //! range. - //! - //! This function returns an input iterator pointing to the first word in - //! a WordRange object. - //! - //! \returns An input iterator. - //! - //! \exceptions - //! \noexcept - //! - //! \note The return type of \ref end might be different from the return - //! type of \ref begin. - //! - //! \sa \ref end. - // REQUIRED so that we can use StringRange in range based loops - auto begin() const noexcept { - return rx::begin(*this); - } - - //! \brief Returns an input iterator pointing one beyond the last word in - //! the range. - //! - //! This function returns an input iterator pointing one beyond the last - //! word in a WordRange object. - //! - //! \returns An input iterator. - //! - //! \exceptions - //! \noexcept - //! - //! \note The return type of \ref end might be different from the return - //! type of \ref begin. - //! - //! \sa \ref begin. - // REQUIRED so that we can use StringRange in range based loops - auto end() const noexcept { - return rx::end(*this); - } - - // TODO(now) this doc doesn't feel nice, but JDE can't think of a good way - // to write it. - //! \brief Returns whether or not the settings have been changed since the - //! last time either \ref next or \ref get has been called. - //! - //! Other than by calling \ref next, the value returned by \ref get may be - //! altered by a call to one of the following: - //! * \ref order(Order) - //! * \ref alphabet_size(size_type) - //! * \ref min(size_type) - //! * \ref max(size_type) - //! * \ref first(word_type const&) - //! * \ref last(word_type const&) - //! * \ref upper_bound(size_type) - //! - //! This function returns \c true if none of the above settings have been - //! changed since the last time \ref next or \ref get is called, and \c - //! false otherwise. - //! - //! \returns A value of type `bool`. - // Required so StringRange can accurately set _current_valid - bool valid() const noexcept { - return _current_valid; - } - }; - - //! \ingroup words_group - //! - //! \brief Return a human readable representation of a WordRange object. - //! - //! Return a human readable representation of a WordRange object. - //! - //! \param wr the WordRange object. - //! - //! \exceptions - //! \no_libsemigroups_except - [[nodiscard]] std::string to_human_readable_repr(WordRange const& wr, - size_t max_width = 72); - - //////////////////////////////////////////////////////////////////////// - // Strings -> Words - //////////////////////////////////////////////////////////////////////// - - //! \ingroup words_group - //! \brief Class for converting strings to \ref word_type with specified - //! alphabet. - //! - //! Defined in `words.hpp`. - //! - //! An instance of this class is used to convert from std::string to \ref - //! word_type. The characters in the string are converted to integers - //! according to their position in alphabet used to construct a ToWord - //! instance if one is provided, or using \ref words::human_readable_index - //! otherwise. - //! - //! \par Example - //! \code - //! ToWord toword("bac"); - //! toword("bac"); // returns {0, 1, 2} - //! toword("bababbbcbc"); // returns { 0, 1, 0, 1, 0, 0, 0, 2, 0, 2} - //! - //! toword.init(); - //! toword("bac"); // returns {1, 0, 2} - //! \endcode - // TODO (later) a version that takes a word_type, so that we can permute the - // letters in a word - class ToWord { - public: - //! \brief Default constructor. - //! - //! Constructs an empty object with no alphabet set. - // Not noexcept because std::array::fill isn't - ToWord() : _alphabet_map() { - init(); - } - - // TODO (later) noexcept? - //! \brief Default copy constructor. - //! - //! Default copy constructor. - ToWord(ToWord const&); - - //! \brief Default move constructor. - //! - //! Default move constructor. - ToWord(ToWord&&); - - //! \brief Default copy assignment. - //! - //! Default copy assignment. - ToWord& operator=(ToWord const&); - - //! \brief Default move assignment. - //! - //! Default move assignment. - ToWord& operator=(ToWord&&); - - //! \brief Default destructor. - //! - //! Default destructor. - ~ToWord(); - - //! \brief Initialize an existing ToWord object. - //! - //! This function puts a ToWord object back into the same state as if it had - //! been newly default constructed. - //! - //! \returns A reference to \c *this. - //! - //! \exceptions - //! \no_libsemigroups_except - //! - //! \sa ToWord() - ToWord& init() { - _alphabet_map.clear(); - return *this; - } - - //! \brief Construct with given alphabet. - //! - //! Construct a ToWord object with the given alphabet. - //! - //! \param alphabet the alphabet. - //! - //! \throws LibsemigroupsException if there are repeated letters in - //! \p alphabet. - explicit ToWord(std::string const& alphabet) : _alphabet_map() { - init(alphabet); - } - - //! \brief Initialize an existing ToWord object. - //! - //! This function puts a ToWord object back into the same state as if it had - //! been newly constructed from \p alphabet. - //! - //! \param alphabet the alphabet. - //! - //! \returns A reference to \c *this. - //! - //! \throws LibsemigroupsException if there are repeated letters in - //! \p alphabet. - //! - //! \sa ToWord(std::string const& alphabet) - ToWord& init(std::string const& alphabet); - - //! \brief Check if the alphabet is defined. - //! - //! This function returns \c true if no alphabet has been defined, and \c - //! false otherwise. - //! - //! \returns A value of type \c bool. - //! - //! \exceptions - //! \noexcept - [[nodiscard]] bool empty() const noexcept { - return _alphabet_map.empty(); - } - - //! \brief Return the alphabet used for conversion. - //! - //! This function returns a std::string corresponding to the ordered-set - //! alphabet \f$\{a_0, a_1, \dots, a_{n-1}\}\f$ that the initialised ToWord - //! object will use to convert from std::string to \ref word_type. - //! Specifically, \f$a_i \mapsto i\f$ where \f$a_i\f$ will correspond to a - //! letter in a std::string, and \f$i\f$ is a \ref letter_type. - //! - //! If this function returns the empty string, then conversion will be - //! performed using \ref words::human_readable_index. - //! - //! \returns A value of type std::string. - //! - //! \exceptions - //! \no_libsemigroups_except. - [[nodiscard]] std::string alphabet() const; - - //! Check if the current ToWord instance can convert a specified letter. - //! - //! This function returns \c true if \p c can can be converted to a \ref - //! letter_type using this ToWord instance, and \c false otherwise. - //! - //! \param c the char to check the convertibility of. - //! - //! \returns A value of type bool. - //! - //! \exceptions - //! \no_libsemigroups_except - [[nodiscard]] bool can_convert_letter(char const& c) const { - return _alphabet_map.count(c) == 1; - } - - //! \brief Convert a string to a word_type. - //! - //! This function converts its second argument \p input into a word_type and - //! stores the result in the first argument \p output. The characters of \p - //! input are converted using the alphabet used to construct the object or - //! set via init(), or with \ref words::human_readable_index if \ref empty - //! returns `true`. - //! - //! The contents of the first argument \p output, if any, is removed. - //! - //! \param output word to hold the result. - //! \param input the string to convert. - //! - //! \warning This functions performs no checks on its arguments. In - //! particular, if the alphabet used to define an instance of ToWord is not - //! empty, and \p input contains letters that do not correspond to letters - //! of the alphabet, then bad things will happen. - //! - //! \sa - //! * \ref literals - void call_no_checks(word_type& output, std::string const& input) const; - - //! \brief Convert a string to a word_type. - //! - //! This function converts its argument \p input into a word_type. The - //! characters of \p input are converted using the alphabet used to - //! construct the object or set via init(), or with \ref - //! words::human_readable_index if \ref empty returns `true`. - //! - //! \param input the string to convert. - //! - //! \warning This functions performs no checks on its arguments. In - //! particular, if the alphabet used to define an instance of ToWord is not - //! empty, and \p input contains letters that do not correspond to letters - //! of the alphabet, then bad things will happen. - //! - //! \sa - //! * \ref literals - [[nodiscard]] word_type call_no_checks(std::string const& input) const { - word_type output; - call_no_checks(output, input); - return output; - } - - //! \brief Convert a string to a word_type. - //! - //! This function converts its second argument \p input into a word_type and - //! stores the result in the first argument \p output. The characters of \p - //! input are converted using the alphabet used to construct the object or - //! set via init(), or with \ref words::human_readable_index if \ref empty - //! returns `true`. - //! - //! The contents of the first argument \p output, if any, is removed. - //! - //! \param output word to hold the result. - //! \param input the string to convert. - //! - //! \throw LibsemigroupsException if the alphabet used to define an instance - //! of ToWord is not empty and \p input contains letters that do not - //! correspond to letters of the alphabet. - //! - //! \sa - //! * \ref literals - void operator()(word_type& output, std::string const& input) const; - - //! \brief Convert a string to a word_type. - //! - //! This function converts its argument \p input into a word_type The - //! characters of \p input are converted using the alphabet used to - //! construct the object or set via init(), or with \ref - //! words::human_readable_index if \ref empty returns `true`. - //! - //! \param input the string to convert. - //! - //! \throw LibsemigroupsException if the alphabet used to define an instance - //! of ToWord is not empty and \p input contains letters that do not - //! correspond to letters of the alphabet. - //! - //! \sa - //! * \ref literals - [[nodiscard]] word_type operator()(std::string const& input) const { - word_type output; - operator()(output, input); - return output; - } - - [[nodiscard]] letter_type operator()(char input) const { - // TODO improve this - // FIXME it also doesn't work - word_type output; - // operator()(output, std::string_view(&input, 1)); - operator()(output, std::string(input, 1)); - return output[0]; - } - - [[nodiscard]] letter_type call_no_checks(char input) const { - return _alphabet_map.find(input)->second; - } - - template - struct Range; - - //! \brief Call operator for combining with other range objects. - //! - //! A custom combinator for rx::ranges to convert the output of a - //! StringRange object into \ref word_type, that can be combined with other - //! combinators using `operator|`. - //! - //! \par Example - //! \code - //! StringRange strings; - //! strings.alphabet("ab").first("a").last("bbbb"); - //! auto words = (strings | ToWord("ba")); - //! // contains the words - //! // {1_w, 0_w, 11_w, 10_w, 01_w, 00_w, 111_w, - //! // 110_w, 101_w, 100_w, 011_w, 010_w, 001_w, 000_w, - //! // 1111_w, 1110_w, 1101_w, 1100_w, 1011_w, 1010_w, 1001_w, - //! // 1000_w, 0111_w, 0110_w, 0101_w, 0100_w, 0011_w, 0010_w, - //! // 0001_w})); - //! \endcode - template >> - [[nodiscard]] constexpr auto operator()(InputRange&& input) const { - using Inner = rx::get_range_type_t; - return Range(std::forward(input), *this); - } - - private: - std::unordered_map _alphabet_map; - }; - - template - struct ToWord::Range { - using output_type = word_type; - - static constexpr bool is_finite = rx::is_finite_v; - static constexpr bool is_idempotent = rx::is_idempotent_v; - - InputRange _input; - ToWord _to_word; - - explicit Range(InputRange const& input, ToWord const& t_wrd) - : _input(input), _to_word(t_wrd) {} - - explicit Range(InputRange&& input, ToWord const& t_wrd) - : _input(std::move(input)), _to_word(t_wrd) {} - - explicit Range(InputRange const& input, ToWord&& t_wrd) - : _input(input), _to_word(std::move(t_wrd)) {} - - explicit Range(InputRange&& input, ToWord&& t_wrd) - : _input(std::move(input)), _to_word(std::move(t_wrd)) {} - - // Not noexcept because ToWord()() isn't - [[nodiscard]] output_type get() const { - return _to_word.operator()(_input.get()); - } - - constexpr void next() noexcept { - _input.next(); - } - - [[nodiscard]] constexpr bool at_end() const noexcept { - return _input.at_end(); - } - - [[nodiscard]] constexpr size_t size_hint() const noexcept { - return _input.size_hint(); - } - }; - - //! \ingroup words_group - //! - //! \brief Return a human readable representation of a ToWord object. - //! - //! Return a human readable representation of a ToWord object. - //! - //! \param twrd the ToWord object. - //! - //! \exceptions - //! \no_libsemigroups_except - [[nodiscard]] inline std::string to_human_readable_repr(ToWord const& twrd) { - return fmt::format("", twrd.alphabet()); - } - - //////////////////////////////////////////////////////////////////////// - // Words -> Strings - //////////////////////////////////////////////////////////////////////// - - //! \ingroup words_group - //! \brief Class for converting \ref word_type into std::string with specified - //! alphabet. - //! - //! Defined in `words.hpp`. - //! - //! An instance of this class is used to convert from \ref word_type to - //! std::string. The letters in the word are converted to characters - //! according to their position in alphabet used to construct a ToString - //! instance if one is provided, or using \ref words::human_readable_letter - //! otherwise. - //! - //! \par Example - //! \code - //! ToString tostring("bac"); - //! tostring(word_type({1, 0, 2})); // returns "abc" - //! tostring(word_type({0, 1, 1, 0, 1, 1, 0, 2})); // returns "baabaabc" - //! - //! tostring.init(); - //! tostring(word_type({1, 0, 2})); // returns "bac" - //! \endcode - class ToString { - public: - //! \brief Default constructor. - //! - //! Constructs an empty object with no alphabet set. - ToString() : _alphabet_map() { - init(); - } - - // TODO (later) noexcept? - //! \brief Default copy constructor. - //! - //! Default copy constructor. - ToString(ToString const&); - - //! \brief Default move constructor. - //! - //! Default move constructor. - ToString(ToString&&); - - //! \brief Default copy assignment. - //! - //! Default copy assignment. - ToString& operator=(ToString const&); - - //! \brief Default move assignment. - //! - //! Default move assignment. - ToString& operator=(ToString&&); - - //! \brief Default destructor. - //! - //! Default destructor. - ~ToString(); - - //! \brief Initialize an existing ToString object. - //! - //! This function puts a ToString object back into the same state as if it - //! had been newly default constructed. - //! - //! \returns A reference to \c *this. - //! - //! \exceptions - //! \noexcept - //! - //! \sa ToString() - ToString& init() noexcept { - _alphabet_map.clear(); - return *this; - } - - //! \brief Construct with given alphabet. - //! - //! Construct a ToString object with the given alphabet. - //! - //! \param alphabet the alphabet. - //! - //! \throws LibsemigroupsException if there are repeated letters in - //! \p alphabet. - explicit ToString(std::string const& alphabet) : _alphabet_map() { - init(alphabet); - } - - //! \brief Initialize an existing Tostring object. - //! - //! This function puts a ToString object back into the same state as if it - //! had been newly constructed from \p alphabet. - //! - //! \param alphabet the alphabet. - //! - //! \returns A reference to \c *this. - //! - //! \throws LibsemigroupsException if there are repeated letters in - //! \p alphabet. - //! - //! \sa ToString(std::string const& alphabet) - ToString& init(std::string const& alphabet); - - //! \brief Check if the alphabet is defined. - //! - //! This function return \c true if no alphabet has been defined, and \c - //! false otherwise. - //! - //! \returns A value of type \c bool. - //! - //! \exceptions - //! \noexcept - [[nodiscard]] bool empty() const noexcept { - return _alphabet_map.empty(); - } - - //! \brief Return the alphabet used for conversion. - //! - //! This function returns a std::string corresponding to the ordered-set - //! alphabet \f$\{a_0, a_1, \dots a_{n-1}\}\f$ that the initialised ToString - //! object will use to convert from a \ref word_type to a std::string. - //! Specifically, \f$i\mapsto a_i\f$ where \f$i\f$ will correspond to a - //! letter in a word_type, and \f$a_i\f$ is a \c char. - //! - //! If this function returns the empty string, then conversion will be - //! performed using \ref words::human_readable_index. - //! - //! \returns A value of type std::string. - //! - //! \exceptions - //! \no_libsemigroups_except. - [[nodiscard]] std::string alphabet() const; - - //! Check if the current ToString instance can convert a specified letter. - //! - //! This function returns \c true if \p l can can be converted to a ``char`` - //! using this ToString instance, and \c false otherwise. - //! - //! \param l the letter to check the convertibility of. - //! - //! \returns A value of type bool. - //! - //! \exceptions - //! \no_libsemigroups_except - [[nodiscard]] bool can_convert_letter(letter_type const& l) const { - return _alphabet_map.count(l) == 1; - } - - //! \brief Convert a \ref word_type to a std::string. - //! - //! This function converts its second argument \p input into a std::string - //! and stores the result in the first argument \p output. The characters of - //! \p input are converted using the alphabet used to construct the object - //! or set via init(), or with \ref words::human_readable_letter if \ref - //! empty returns `true`. - //! - //! The contents of the first argument \p output, if any, is removed. - //! - //! \param output std::string to hold the result. - //! \param input the \ref word_type to convert. - //! - //! \warning This functions performs no checks on its arguments. In - //! particular, if the alphabet used to define an instance of ToString is - //! not empty, and \p input contains letters that do not correspond to - //! letters of the alphabet, then bad things will happen. - //! - //! \sa - //! * \ref literals - void call_no_checks(std::string& output, word_type const& input) const; - - //! \brief Convert a \ref word_type to a std::string. - //! - //! This function converts its argument \p input into a std::string. The - //! characters of \p input are converted using the alphabet used to - //! construct the object or set via init(), or with \ref - //! words::human_readable_letter if \ref empty returns `true`. - //! - //! \param input the \ref word_type to convert. - //! - //! \warning This functions performs no checks on its arguments. In - //! particular, if the alphabet used to define an instance of ToString is - //! not empty, and \p input contains letters that do not correspond to - //! letters of the alphabet, then bad things will happen. - //! - //! \sa - //! * \ref literals - [[nodiscard]] std::string call_no_checks(word_type const& input) const { - std::string output; - call_no_checks(output, input); - return output; - } - - //! \brief Convert a \ref word_type to a std::string. - //! - //! This function converts its second argument \p input into a std::string - //! and stores the result in the first argument \p output. The characters of - //! \p input are converted using the alphabet used to construct the object - //! or set via init(), or with \ref words::human_readable_letter if \ref - //! empty returns `true`. - //! - //! The contents of the first argument \p output, if any, is removed. - //! - //! \param output word to hold the result. - //! \param input the string to convert. - //! - //! \throw LibsemigroupsException if the alphabet used to define an instance - //! of ToString is not empty and \p input contains letters that do not - //! correspond to letters of the alphabet. - //! - //! \sa - //! * \ref literals - void operator()(std::string& output, word_type const& input) const; - - //! \brief Convert a \ref word_type to a std::string. - //! - //! This function converts its argument \p input into a std::string. The - //! characters of \p input are converted using the alphabet used to - //! construct the object or set via init(), or with \ref - //! words::human_readable_letter if \ref empty returns `true`. - //! - //! \param input the string to convert. - //! - //! \throw LibsemigroupsException if the alphabet used to define an instance - //! of ToString is not empty and \p input contains letters that do not - //! correspond to letters of the alphabet. - //! - //! \sa - //! * \ref literals - [[nodiscard]] std::string operator()(word_type const& input) const { - std::string output; - operator()(output, input); - return output; - } - - template - [[nodiscard]] std::string - operator()(std::initializer_list input) const { - // TODO(0) use iterators instead - word_type copy(input.begin(), input.end()); - return operator()(copy); - } - - template - struct Range; - - //! \brief Call operator for combining with other range objects. - //! - //! A custom combinator for rx::ranges to convert the output of a WordRange - //! object into std::string, that can be combined with other combinators - //! using `operator|`. - //! - //! \par Example - //! \code - //! WordRange words; - //! words.alphabet_size(1).min(0).max(10); - //! - //! auto strings = (words | ToString("a")); - //! // Contains the strings - //! // {"", "a", "aa", "aaa", "aaaa", "aaaaa", "aaaaaa", "aaaaaaa", - //! // "aaaaaaaa", "aaaaaaaaa"}; - //! \endcode - template >> - [[nodiscard]] constexpr auto operator()(InputRange&& input) const { - using Inner = rx::get_range_type_t; - return Range(std::forward(input), *this); - } - - private: - // We could use std::vector (or similar) here, but an - // unordered_ordered hap has been used instead to allow for potential future - // conversions between different types. - std::unordered_map _alphabet_map; - }; - - template - struct ToString::Range { - using output_type = std::string; - - static constexpr bool is_finite = rx::is_finite_v; - static constexpr bool is_idempotent = rx::is_idempotent_v; - - InputRange _input; - ToString _to_string; - - Range(InputRange const& input, ToString const& t_str) - : _input(input), _to_string(t_str) {} - - Range(InputRange&& input, ToString const& t_str) - : _input(std::move(input)), _to_string(t_str) {} - - Range(InputRange const& input, ToString&& t_str) - : _input(input), _to_string(std::move(t_str)) {} - - Range(InputRange&& input, ToString&& t_str) - : _input(std::move(input)), _to_string(std::move(t_str)) {} - - // Not noexcept because ToString()() isn't - [[nodiscard]] output_type get() const { - return _to_string(_input.get()); - } - - constexpr void next() noexcept { - _input.next(); - } - - [[nodiscard]] constexpr bool at_end() const noexcept { - return _input.at_end(); - } - - [[nodiscard]] constexpr size_t size_hint() const noexcept { - return _input.size_hint(); - } - }; - - //! \ingroup words_group - //! - //! \brief Return a human readable representation of a ToString object. - //! - //! Return a human readable representation of a ToString object. - //! - //! \param tstr the ToString object. - //! - //! \exceptions - //! \no_libsemigroups_except - [[nodiscard]] inline std::string - to_human_readable_repr(ToString const& tstr) { - return fmt::format("", - tstr.alphabet()); - } - - //////////////////////////////////////////////////////////////////////// - // StringRange - //////////////////////////////////////////////////////////////////////// - - namespace detail { - void throw_if_random_string_should_throw(std::string const& alphabet, - size_t min, - size_t max); - } // namespace detail - - //! \ingroup words_group - //! \brief Returns a random string. - //! - //! Returns a random string with length \p length over alphabet \p alphabet. - //! - //! \param alphabet the alphabet over which the string is constructed. - //! \param length the length of the string. - //! - //! \returns A random string, value of `std::string`. - //! - //! \exceptions - //! \no_libsemigroups_except - //! - //! \sa \ref random_word - std::string random_string(std::string const& alphabet, size_t length); - - //! \ingroup words_group - //! \brief Returns a random string. - //! - //! Returns a random string with random length in the range `[min, max)` over - //! alphabet \p alphabet. - //! - //! \param alphabet the alphabet over which the string is constructed. - //! \param min the minimum length of the returned string. - //! \param max one above the maximum length of the returned string. - //! - //! \returns A random string, value of `std::string`. - //! - //! \throws LibsemigroupsException if either: - //! * `min > max`; or - //! * `alphabet.size() == 0` and `min != 0`. - //! - //! \sa \ref random_word - std::string random_string(std::string const& alphabet, - size_t min, - size_t max); - - //! \ingroup words_group - //! \brief Returns a range object of random strings. - //! - //! Returns a range object of random strings, each of which with random length - //! in the range `[min, max)` over alphabet \p alphabet. - //! - //! \param alphabet the alphabet over which the string is constructed. - //! \param number the number of random strings to construct. - //! \param min the minimum length of the returned string. - //! \param max one above the maximum length of the returned string. - //! - //! \returns A range of random strings. - //! - //! \throws LibsemigroupsException if either: - //! * `min > max`; or - //! * `alphabet.size() == 0` and `min != 0`. - //! - //! \sa \ref random_word - auto inline random_strings(std::string const& alphabet, - size_t number, - size_t min, - size_t max) { - detail::throw_if_random_string_should_throw(alphabet, min, max); - - // Lambda must capture by copy, as the lambda will exist outside the scope - // of this function once the range is returned. - return rx::generate([alphabet, min, max] { - return random_string(alphabet, min, max); - }) - | rx::take(number); - } - - //! \ingroup words_group - //! \brief Class for generating strings in a given range and in a particular - //! order. - //! - //! Defined in `words.hpp`. - //! - //! This class implements a range object for strings and produces the same - //! output as `WordRange() | ToString("ab")`, but is more convenient in some - //! cases. - //! - //! \note There is a small overhead to using a StringRange object rather than - //! using \ref cbegin_wislo or \ref cbegin_wilo directly. - //! - //! The order and range of the words in a StringRange instance can be set - //! using the member functions: - //! * \ref order - //! * \ref alphabet - //! * \ref min - //! * \ref max - //! * \ref first - //! * \ref last - //! - //! \par Example - //! \code - //! StringRange strings; - //! strings.order(Order::shortlex) // strings in shortlex order - //! .alphabet("ab") // on 2 letters - //! .min(1) // of length in the range from 1 - //! .max(5); // to 5 - //! \endcode - //! - //! \sa WordRange - // This can in many places be replaced by "WordRange | ToString" but this - // makes some things more awkward and so we retain this class for its - // convenience. - class StringRange { - public: - //! Alias for the size type. - using size_type = typename std::vector::size_type; - - //! The type of the output of the range object. - using output_type = std::string const&; - - private: - mutable std::string _current; - mutable bool _current_valid; - std::string _letters; - ToWord _to_word; - ToString _to_string; - WordRange _word_range; - - void init_current() const { - if (!_current_valid) { - _current = _to_string(_word_range.get()); - _current_valid = true; - } - } - - public: - //! \brief Get the current value. - //! - //! Returns the current string in a StringRange object. - //! - //! \returns A value of type \ref output_type. - //! - //! \exceptions - //! \noexcept - //! - //! \warning If at_end() returns \c true, then the return value of this - //! function could be anything. - output_type get() const { - init_current(); - return _current; - } - - //! \brief Advance to the next value. - //! - //! Advances a StringRange object to the next value (if any). - //! - //! \exceptions - //! \noexcept - //! - //! \sa \ref at_end - void next() noexcept { - _word_range.next(); - _current_valid = false; - } - - //! \brief Check if the range object is exhausted. - //! - //! Returns \c true if a StringRange object is exhausted, and \c false if - //! not. - //! - //! \returns A value of type \c bool. - //! - //! \exceptions - //! \noexcept - bool at_end() const noexcept { - return _word_range.at_end(); - } - - //! \brief The possible size of the range. - //! - //! Returns the number of words in a StringRange object if order() is - //! Order::shortlex. If order() is not Order::shortlex, then the return - //! value of this function is meaningless. - //! - //! \returns A value of type \c size_t. - //! - //! \exceptions - //! \noexcept - size_t size_hint() const noexcept { - return _word_range.size_hint(); - } - - //! \brief The actual size of the range. - //! - //! Returns the number of words in the range object. - //! If order() is Order::shortlex, then size_hint() is used. If order() is - //! not Order::shortlex, then a copy of the range may have to be looped over - //! in order to find the return value of this function. - //! - //! \returns A value of type \c size_t. - //! - //! \exceptions - //! \noexcept - size_t count() const noexcept { - return _word_range.count(); - } - - //! Value indicating that the range is finite. - static constexpr bool is_finite = true; // This may not always be true - - //! Value indicating that if get() is called twice on a StringRange object - //! that is not changed between the two calls, then the return value of - //! get() is the same both times. - static constexpr bool is_idempotent = true; - - //! \brief Default constructor. - //! - //! Constructs an empty range with: - //! * min() equal to \c 0; - //! * max() equal to \c 0; - //! * order() equal to Order::shortlex; - //! * first() equal to the empty string; - //! * last() equal to the empty string; - //! * upper_bound() equal to \c 0; - //! * alphabet() equal to ``""``. - StringRange() { - init(); - } - - //! \brief Initialize an existing StringRange object. - //! - //! This function puts a StringRange object back into the same state as if - //! it had been newly default constructed. - //! - //! \returns A reference to \c *this. - //! - //! \exceptions - //! \no_libsemigroups_except - StringRange& init(); - - //! \brief Default copy constructor. - StringRange(StringRange const&); - - //! \brief Default move constructor. - StringRange(StringRange&&); - - //! \brief Default copy assignment operator. - StringRange& operator=(StringRange const&); - - //! \brief Default move assignment operator. - StringRange& operator=(StringRange&&); - - //! \brief Default destructor. - ~StringRange(); - - //! \brief Set the alphabet. - //! - //! Sets the alphabet in a StringRange object. - //! - //! \param x the alphabet. - //! - //! \returns A reference to \c *this. - //! - //! \throws LibsemigroupsException if \p x contains repeated letters. - StringRange& alphabet(std::string const& x); - - //! \brief The current alphabet. - //! - //! Returns the current alphabet in a StringRange object. - //! - //! \returns A value of type \ref std::string. - //! - //! \exceptions - //! \noexcept - [[nodiscard]] std::string const& alphabet() const noexcept { - return _letters; - } - - //! \brief Set the first string in the range. - //! - //! Sets the first string in a StringRange object to be \p frst. - //! - //! \param frst the first string. - //! - //! \returns A reference to \c *this. - //! - //! \note Unlike WordRange::first, this function will throw if \p frst - //! contains letters not belonging to alphabet(). - //! - //! \sa \ref min - StringRange& first(std::string const& frst) { - _word_range.first(_to_word(frst)); - _current_valid &= _word_range.valid(); - return *this; - } - - //! \brief The current first string in the range. - //! - //! Returns the first string in a StringRange object. - //! - //! \returns A \ref std::string by value. - //! - //! \exceptions - //! \noexcept - //! - //! \sa \ref min - [[nodiscard]] std::string first() const noexcept { - return _to_string(_word_range.first()); - } - - //! \brief Set one past the last string in the range. - //! - //! Sets one past the last string in a StringRange object to be \p lst. - //! - //! \param lst one past the last string. - //! - //! \returns A reference to \c *this. - //! - //! \throws LibsemigroupsException if \p lst contains letters not belonging - //! to alphabet(). - //! - //! \note The behaviour of this function is not exactly the same as - //! ``WordRange::last(word_type const&)``. That function will not throw if a - //! word contains letters not in the alphabet. - //! - //! \sa \ref max - StringRange& last(std::string const& lst) { - _word_range.last(_to_word(lst)); - _current_valid &= _word_range.valid(); - return *this; - } - - //! \brief The current one past the last string in the range. - //! - //! Returns one past the last string in a StringRange object. - //! - //! \returns A \ref std::string by value. - //! - //! \exceptions - //! \noexcept - //! - //! \sa \ref max - [[nodiscard]] std::string last() const noexcept { - return _to_string(_word_range.last()); - } - - //! \brief Set the order of the strings in the range. - //! - //! Sets the order of the strings in a StringRange object to \p val. - //! - //! \param val the order. - //! - //! \returns A reference to \c *this. - //! - //! \throws LibsemigroupsException if val is not Order::shortlex or - //! Order::lex. - StringRange& order(Order val) { - _word_range.order(val); - _current_valid &= _word_range.valid(); - return *this; - } - - //! \brief The current order of the strings in the range. - //! - //! Returns the current order of the strings in a StringRange object. - //! - //! \returns A value of type \ref Order. - //! - //! \exceptions - //! \noexcept - [[nodiscard]] Order order() const noexcept { - return _word_range.order(); - } - - //! \brief Set an upper bound for the length of a string in the range. - //! - //! Sets an upper bound for the length of a string in a StringRange object. - //! This setting is only used if order() is Order::lex. - //! - //! \param n the upper bound. - //! - //! \returns A reference to \c *this. - //! - //! \exceptions - //! \no_libsemigroups_except - StringRange& upper_bound(size_type n) { - _word_range.upper_bound(n); - _current_valid &= _word_range.valid(); - return *this; - } - - //! \brief The current upper bound on the length of a string in the range. - //! - //! Returns the current upper bound on the length of a string in a - //! StringRange object. This setting is only used if order() is Order::lex. - //! - //! \returns A value of type \ref size_type. - //! - //! \exceptions - //! \noexcept - [[nodiscard]] size_type upper_bound() const noexcept { - return _word_range.upper_bound(); - } - - //! \brief Set the first string in the range by length. - //! - //! Sets the first string in a StringRange object to be `pow("a", val)` (the - //! string consisting of \p val letters equal to \c "a"). - //! - //! \param val the exponent. - //! - //! \returns A reference to \c *this. - //! - //! \exceptions - //! \no_libsemigroups_except - StringRange& min(size_type val) { - _word_range.min(val); - _current_valid &= _word_range.valid(); - return *this; - } - - // No corresponding getter for min, because what would it mean? Could be the - // length of first(), but that doesn't correspond well to what happens with - // the setter. - - //! \brief Set one past the last string in the range by length. - //! - //! Sets one past the last string in a StringRange object to be - //! \f$\alpha^n\f$ where \f$\alpha\f$ is the first letter of - //! ``alphabet()`` (or ``"a"`` if the alphabet is empty) and - //! \f$n\f$ corresponds to \p val. - //! - //! \param val the exponent. - //! - //! \returns A reference to \c *this. - //! - //! \exceptions - //! \no_libsemigroups_except - StringRange& max(size_type val) { - _word_range.max(val); - _current_valid &= _word_range.valid(); - return *this; - } - - //! \brief Returns an input iterator pointing to the first string in the - //! range. - //! - //! This function returns an input iterator pointing to the first string in - //! a StringRange object. - //! - //! \returns An input iterator. - //! - //! \exceptions - //! \noexcept - //! - //! \note The return type of \ref end might be different from the return - //! type of \ref begin. - //! - //! \sa \ref end. - // REQUIRED so that we can use StringRange in range based loops - auto begin() const noexcept { - return rx::begin(*this); - } - - //! \brief Returns an input iterator pointing one beyond the last string in - //! the range. - //! - //! This function returns an input iterator pointing one beyond the last - //! string in a StringRange object. - //! - //! \returns An input iterator. - //! - //! \exceptions - //! \noexcept - //! - //! \note The return type of \ref end might be different from the return - //! type of \ref begin. - //! - //! \sa \ref begin. - // REQUIRED so that we can use StringRange in range based loops - auto end() const noexcept { - return rx::end(*this); - } - }; - - //! \ingroup words_group - //! - //! \brief Return a human readable representation of a StringRange object. - //! - //! Return a human readable representation of a StringRange object. - //! - //! \param sr the StringRange object. - //! - //! \exceptions - //! \no_libsemigroups_except - [[nodiscard]] std::string to_human_readable_repr(StringRange const& sr, - size_t max_width = 72); - - //////////////////////////////////////////////////////////////////////// - // Literals - //////////////////////////////////////////////////////////////////////// - - //! \ingroup words_group - //! - //! \brief Namespace containing some custom literals for creating words. - //! - //! Defined in `words.hpp`. - //! - //! This namespace contains some functions for creating \ref word_type objects - //! in a compact form. - //! \par Example - //! \code - //! 012_w // same as word_type({0, 1, 2}) - //! "abc"_w // also same as word_type({0, 1, 2}) - //! "(ab)^3"_p // same as "ababab" - //! \endcode - namespace literals { - //! \anchor literal_operator_w - //! \brief Literal for defining \ref word_type over integers less than 10. - //! - //! This operator provides a convenient brief means of constructing a \ref - //! word_type from an sequence of literal integer digits or a string. For - //! example, \c 0123_w produces the same output as `word_type({0, 1, 2, - //! 3})` and so too does \c "abcd"_w. - //! - //! There are some gotchas and this operator should be used with some care: - //! - //! * the parameter \p w must consist of the integers \f$\{0, \ldots, - //! 9\}\f$ or the characters in `a-zA-Z` but not both. - //! * if \p w starts with \c 0 and is follows by a value greater than \c 7, - //! then it is necessary to enclose \p w in quotes. For example, \c 08_w - //! will not compile because it is interpreted as an invalid octal. However - //! `"08"_w` behaves as expected. - //! * if \p w consists of characters in `a-zA-Z`, then the output is - //! the same as that of `ToWord::operator()(w)`, see \ref - //! ToWord::operator()() - //! - //! \param w the letters of the word. - //! \param n the length of \p w (defaults to the length of \p w). - //! - //! \returns A value of type \ref word_type. - //! - //! \throws LibsemigroupsException if the input contains a mixture of - //! integers and non-integers. - word_type operator"" _w(const char* w, size_t n); - - //! \brief Literal for defining \ref word_type over integers less than 10. - //! - //! See \ref literal_operator_w "operator\"\"_w" for details. - word_type operator"" _w(const char* w); - - //! \anchor literal_operator_p - //! \brief Literal for defining std::string by parsing an algebraic - //! expression. - //! - //! This operator provides a convenient concise means of constructing a - //! std::string from an algebraic expression. - //! For example, \c "((ab)^3cc)^2"_p equals - //! \c "abababccabababcc" and \c "a^0"_p equals the empty string \c "". - //! - //! This function has the following behaviour: - //! * arbitrarily nested brackets; - //! * spaces are ignored; - //! * redundant matched brackets are ignored; - //! * only the characters in \c "()^ " and in \c a-zA-Z0-9 are allowed. - //! - //! \param w the letters of the word. - //! \param n the length of \p w (defaults to the length of \p w). - //! - //! \returns A value of type \ref std::string. - //! - //! \throws LibsemigroupsException if the string cannot be parsed. - std::string operator"" _p(const char* w, size_t n); - - //! \brief Literal for defining \ref word_type by parsing an algebraic - //! expression. - //! - //! See \ref literal_operator_p "operator\"\"_p" for details. - std::string operator"" _p(const char* w); - } // namespace literals - - //////////////////////////////////////////////////////////////////////// - - //! \ingroup words_group - //! - //! \brief Namespace containing some operators for creating words. - //! - //! Defined in `words.hpp`. - //! - //! This namespace contains some functions for creating \ref word_type objects - //! in a compact form. - //! \par Example - //! \code - //! using namespace words; - //! pow("a", 5) // same as "aaaaa" - //! 01_w + 2 // same as 012_w - //! 01_w + 01_w // same as 0101_w - //! prod(0123_w, 0, 16, 3) // same as 032103_w - //! \endcode - namespace words { - - //! \brief Returns the index of a character in human readable order. - //! - //! Defined in `words.hpp`. - //! - //! This function is the inverse of \ref words::human_readable_letter, see - //! the documentation of that function for more details. - //! - //! \param c character whose index is sought. - //! - //! \returns A value of type \ref letter_type. - //! - //! \exceptions - //! \no_libsemigroups_except - //! - //! \sa human_readable_letter - [[nodiscard]] letter_type human_readable_index(char c); - - //! \brief Returns a character by index in human readable order. - //! - //! This function exists to map the numbers \c 0 to \c 255 to the possible - //! values of a \c char, in such a way that the first characters are \c - //! a-zA-Z0-9. The ascii ranges for these characters are: \f$[97, 123)\f$, - //! \f$[65, 91)\f$, \f$[48, 58)\f$ so the remaining range of chars that are - //! appended to the end after these chars are \f$[0,48)\f$, \f$[58, 65)\f$, - //! \f$[91, 97)\f$, \f$[123, 255]\f$. - //! - //! This function is the inverse of \ref words::human_readable_index. - //! - //! \param i the index of the character. - //! - //! \returns A value of type \c char. - //! - //! \throws LibsemigroupsException if \p i exceeds the number of characters. - template - typename Word::value_type human_readable_letter(size_t i) { - // This check ensures that i is not too large to be converted to a - // Word::value_type. This is check is only needed if the number of - // distinct Word::value objects is less than the number of distinct size_t - // objects. - if constexpr (sizeof(typename Word::value_type) < sizeof(size_t)) { - if (i > std::numeric_limits::max() - - std::numeric_limits::min()) { - LIBSEMIGROUPS_EXCEPTION( - "expected the argument to be in the range [0, {}), found {}", - 1 + std::numeric_limits::max() - - std::numeric_limits::min(), - i); - } - } - if constexpr (!std::is_same_v) { - return static_cast(i); - } else { - // Choose visible characters a-zA-Z0-9 first before anything else - // The ascii ranges for these characters are: [97, 123), [65, 91), - // [48, 58) so the remaining range of chars that are appended to the end - // after these chars are [0,48), [58, 65), [91, 97), [123, 255] - return detail::chars_in_human_readable_order()[i]; - } - } - - //////////////////////////////////////////////////////////////////////// - // operator+ - //////////////////////////////////////////////////////////////////////// - - //! \anchor operator_plus - //! \brief Concatenate two words. - //! - //! Returns the concatenation of \c u and \c w. - //! - //! \param u a word. - //! \param w a word. - //! - //! \returns A \ref word_type. - //! - //! \exceptions - //! \no_libsemigroups_except - static inline word_type operator+(word_type const& u, word_type const& w) { - word_type result(u); - result.insert(result.end(), w.cbegin(), w.cend()); - return result; - } - - //! \brief Concatenate a word and a letter. - //! - //! See \ref operator_plus "operator+". - static inline word_type operator+(word_type const& u, letter_type w) { - word_type result(u); - result.push_back(w); - return result; - } - - //! \brief Concatenate a letter and a word. - //! - //! See \ref operator_plus "operator+". - static inline word_type operator+(letter_type w, word_type const& u) { - return word_type({w}) + u; - } - - //////////////////////////////////////////////////////////////////////// - // operator+= - //////////////////////////////////////////////////////////////////////// - - //! \anchor operator_plus_equals - //! \brief Concatenate a word with another word in-place. - //! - //! Changes \c u to `u + v` in-place. See \ref operator_plus "operator+". - //! - //! \param u a word. - //! \param v a word. - //! - //! \exceptions - //! \no_libsemigroups_except - //! - //! \sa \ref operator_plus "operator+" - static inline void operator+=(word_type& u, word_type const& v) { - u.insert(u.end(), v.cbegin(), v.cend()); - } - - //! \brief Concatenate a word and a letter in-place. - //! - //! See \ref operator_plus_equals "operator+=". - inline void operator+=(word_type& u, letter_type a) { - u.push_back(a); - } - - //! \brief Concatenate a letter and a word in-place. - //! - //! See \ref operator_plus_equals "operator+=". - inline void operator+=(letter_type a, word_type& u) { - u.insert(u.begin(), a); - } - - //////////////////////////////////////////////////////////////////////// - // pow - //////////////////////////////////////////////////////////////////////// - - //! \brief Power a word in-place. - //! - //! Modify the Word \c w to contains its `n`th power. - //! - //! \tparam Word the type of the first parameter. - //! - //! \param w the word. - //! \param n the power. - //! - //! \exceptions - //! \no_libsemigroups_except - template - void pow_inplace(Word& w, size_t n); - // No pow_inplace for string_view or initializer_list because there's no - // where to store the result. - - //! \brief Returns the power of a word. - //! - //! Returns the Word \c w to the power \p n. - //! - //! \tparam Word the type of the first parameter. - //! - //! \param x the word to power. - //! \param n the power. - //! - //! \returns A Word. - //! - //! \exceptions - //! \no_libsemigroups_except - template - Word pow(Word const& x, size_t n) { - Word y(x); - pow_inplace(y, n); - return y; - } - - //! \brief Returns the power of a word. - //! - //! See pow(Word const&, size_t) for details. - static inline word_type pow(std::initializer_list ilist, - size_t n) { - return pow(word_type(ilist), n); - } - - //! \brief Returns the power of a string. - //! - //! See pow(Word const&, size_t) for details. - static inline std::string pow(std::string_view w, size_t n) { - return pow(std::string(w), n); - } - - //////////////////////////////////////////////////////////////////////// - // prod - //////////////////////////////////////////////////////////////////////// - - //! \anchor prod - //! \brief Returns a product of letters or words. - //! - //! Let \p elts correspond to the ordered set \f$a_0, a_1, \ldots, a_{n - - //! 1}\f$, \p first to \f$f\f$, \p last to \f$l\f$, and \p step to - //! \f$s\f$. If \f$f \leq l\f$, let \f$k\f$ be the greatest positive - //! integer such that \f$f + ks < l\f$. Then the function \c prod returns - //! the word corresponding to \f$a_f a_{f + s} a_{f + 2s} \cdots a_{f + - //! ks}\f$. All subscripts are taken modulo \f$n\f$. - //! - //! If there is no such \f$k\f$ (i.e. \f$s < 0\f$, or \f$f = l\f$), then - //! the empty word is returned. Where \f$f > l\f$, the function works - //! analogously, where \f$k\f$ is the greatest positive integer such that - //! \f$f + k s > l\f$. - //! - //! \tparam Container the type of the 1st argument \c elts. - //! \tparam Word the return type (defaults to Container). - //! - //! \param elts the ordered set. - //! \param first the first index. - //! \param last the last index. - //! \param step the step. - //! - //! \return A Word. - //! - //! \throws LibsemigroupsException if `step = 0` - //! \throws LibsemigroupsException if \p elts is empty, but the specified - //! range is not - //! - //! \par Examples - //! \code - //! using namespace words; - //! word_type w = 012345_w - //! prod(w, 0, 5, 2) // {0, 2, 4} - //! prod(w, 1, 9, 2) // {1, 3, 5, 1} - //! prod("abcde", 4, 1, -1) // "edc" - //! prod({"aba", "xyz"}, 0, 4, 1) // "abaxyzabaxyz" - //! \endcode - template - Word prod(Container const& elts, int first, int last, int step = 1); - - //! \brief Returns a product of letters. - //! - //! See \ref prod(Container const&, int, int, int). - static inline word_type prod(std::initializer_list ilist, - int first, - int last, - int step = 1) { - return prod(word_type(ilist), first, last, step); - } - - //! \brief Returns a product of letters. - //! - //! See \ref prod(Container const&, int, int, int). - static inline std::string - prod(std::string_view sv, int first, int last, int step = 1) { - return prod(std::string(sv), first, last, step); - } - - //! \brief Returns a product of words. - //! - //! See \ref prod(Container const&, int, int, int). - static inline word_type prod(std::initializer_list const& elts, - int first, - int last, - int step = 1) { - return prod, word_type>( - std::vector(elts), first, last, step); - } - - //! \brief Returns a product of strings. - //! - //! See \ref prod(Container const&, int, int, int). - static inline std::string - prod(std::initializer_list const& sv, - int first, - int last, - int step = 1) { - return prod, std::string>( - sv, first, last, step); - } - - //! \brief Returns a product of letters or words. - //! - //! Returns the same as `prod(elts, 0, last, 1)`. - //! - //! See \ref prod(Container const&, int, int, int). - template - Word prod(Container const& elts, size_t last) { - return prod(elts, 0, static_cast(last), 1); - } - - //! \brief Returns a product of letters. - //! - //! Returns the same as `prod(elts, 0, last, 1)`. - //! - //! See \ref prod(Container const&, int, int, int). - static inline word_type prod(std::initializer_list const& elts, - size_t last) { - return prod(elts, 0, static_cast(last), 1); - } - - //! \brief Returns a product of letters. - //! - //! Returns the same as `prod(elts, 0, last, 1)`. - //! - //! See \ref prod(Container const&, int, int, int). - static inline std::string prod(std::string_view elts, size_t last) { - return prod( - elts, 0, static_cast(last), 1); - } - - //! \brief Returns a product of words. - //! - //! Returns the same as `prod(elts, 0, last, 1)`. - //! - //! See \ref prod(Container const&, int, int, int). - static inline word_type prod(std::initializer_list const& elts, - size_t last) { - return prod(elts, 0, static_cast(last), 1); - } - - //! \brief Returns a product of words. - //! - //! Returns the same as `prod(elts, 0, last, 1)`. - //! - //! See \ref prod(Container const&, int, int, int). - static inline std::string - prod(std::initializer_list const& elts, size_t last) { - return prod, std::string>( - std::vector(elts), 0, static_cast(last), 1); - } - - //////////////////////////////////////////////////////////////////////// - // pow - implementation - //////////////////////////////////////////////////////////////////////// - - template - void pow_inplace(Word& x, size_t n) { - Word y(x); - x.reserve(x.size() * n); - if (n % 2 == 0) { - x = Word({}); - } - - while (n > 1) { - y += y; - n /= 2; - if (n % 2 == 1) { - x += y; - } - } - } - - //////////////////////////////////////////////////////////////////////// - // prod - implementation - //////////////////////////////////////////////////////////////////////// - - // Note: we could do a version of the below using insert on words, where - // the step is +/- 1. - template - Word prod(Container const& elts, int first, int last, int step) { - if (step == 0) { - LIBSEMIGROUPS_EXCEPTION("the 4th argument must not be 0"); - } else if (((first < last && step > 0) || (first > last && step < 0)) - && elts.size() == 0) { - LIBSEMIGROUPS_EXCEPTION("the 1st argument must not be empty if the " - "given range is not empty"); - // TODO Is int signed? Should this also contain - // std::numeric_limits::min? - } else if (elts.size() > std::numeric_limits::max()) { - LIBSEMIGROUPS_EXCEPTION( - "the 1st argument must have size less than or equal to {}", - std::numeric_limits::max()); - } - Word result; - int const s = elts.size(); - - if (first < last) { - if (step < 0) { - return result; - } - result.reserve((last - first) / step); - - for (int i = first; i < last; i += step) { - size_t const a = (i % s + s) % s; - LIBSEMIGROUPS_ASSERT(static_cast(a) < s); - result += elts[a]; - } - } else { - if (step > 0) { - return result; - } - size_t steppos = static_cast(-step); - result.reserve((first - last) / steppos); - for (int i = first; i > last; i += step) { - size_t const a = (i % s + s) % s; - LIBSEMIGROUPS_ASSERT(static_cast(a) < s); - result += elts[a]; - } - } - return result; - } - } // namespace words - // -} // namespace libsemigroups - -#endif // LIBSEMIGROUPS_WORDS_HPP_ diff --git a/src/fpsemi-examples.cpp b/src/fpsemi-examples.cpp index 0482b44e7..12760191b 100644 --- a/src/fpsemi-examples.cpp +++ b/src/fpsemi-examples.cpp @@ -38,7 +38,7 @@ #include "libsemigroups/presentation.hpp" // for add_rule_no_checks, Presen... #include "libsemigroups/ranges.hpp" // for operator|, to_vector, enum... #include "libsemigroups/types.hpp" // for word_type, letter_type -#include "libsemigroups/words.hpp" // for operator""_w, operator+, pow +#include "libsemigroups/word-range.hpp" // for operator""_w, operator+, pow namespace libsemigroups { using literals::operator""_w; diff --git a/src/obvinf.cpp b/src/obvinf.cpp index 5ecf68a7e..3ff331b17 100644 --- a/src/obvinf.cpp +++ b/src/obvinf.cpp @@ -33,7 +33,7 @@ #include "libsemigroups/constants.hpp" // for UNDEFINED #include "libsemigroups/debug.hpp" // for LIBSEMIGROUPS_ASSERT #include "libsemigroups/todd-coxeter.hpp" -#include "libsemigroups/words.hpp" // for ToWord +#include "libsemigroups/word-range.hpp" // for ToWord #include "libsemigroups/detail/eigen.hpp" diff --git a/src/presentation.cpp b/src/presentation.cpp index a344f6b67..3cc2d96e5 100644 --- a/src/presentation.cpp +++ b/src/presentation.cpp @@ -30,7 +30,7 @@ #include "libsemigroups/presentation.hpp" // for Presentation, to_string, to_word #include "libsemigroups/to-presentation.hpp" // for to_presentation #include "libsemigroups/types.hpp" // for word_type -#include "libsemigroups/words.hpp" // for human_readable_letter +#include "libsemigroups/word-range.hpp" // for human_readable_letter #include "libsemigroups/detail/fmt.hpp" // for format diff --git a/src/words.cpp b/src/words.cpp deleted file mode 100644 index 80847c427..000000000 --- a/src/words.cpp +++ /dev/null @@ -1,771 +0,0 @@ -// -// libsemigroups - C++ library for semigroups and monoids -// Copyright (C) 2019-2023 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 . -// - -// This file contains some functionality missing in some implementations of the -// stl, or to augment the stl implementations. - -#include "libsemigroups/words.hpp" - -#include // for lexicographical_... -#include // for isalpha, isdigit -#include // for pow -#include // for strlen -#include // for iota -#include // for iota -#include // for std::mt19937 -#include // for iota -#include // for allocator -#include // for is_destructible -#include // for move - -#include "libsemigroups/config.hpp" // for LIBSEMIGROUPS_DEBUG -#include "libsemigroups/constants.hpp" // for UNDEFINED -#include "libsemigroups/debug.hpp" // for LIBSEMIGROUPS_ASSERT -#include "libsemigroups/exception.hpp" // for word_type -#include "libsemigroups/order.hpp" // for order -#include "libsemigroups/presentation.hpp" // for index -#include "libsemigroups/ranges.hpp" // for count, operator| -#include "libsemigroups/types.hpp" // for word_type - -#include "libsemigroups/detail/formatters.hpp" // for magic_enum formatting -#include "libsemigroups/detail/word-iterators.hpp" // for const_wilo_iterator - -namespace libsemigroups { - - namespace detail { - std::string const& chars_in_human_readable_order() { - // Choose visible characters a-zA-Z0-9 first before anything else - // The ascii ranges for these characters are: [97, 123), [65, 91), - // [48, 58) so the remaining range of chars that are appended to the end - // after these chars are [0,48), [58, 65), [91, 97), [123, 255] - static std::string letters - = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - static bool first_call = true; - if (first_call) { - letters.resize(256); - std::iota( - letters.begin() + 62, letters.begin() + 110, static_cast(0)); - std::iota(letters.begin() + 110, - letters.begin() + 117, - static_cast(58)); - std::iota(letters.begin() + 117, - letters.begin() + 123, - static_cast(91)); - std::iota(letters.begin() + 123, letters.end(), static_cast(123)); - first_call = false; - LIBSEMIGROUPS_ASSERT(letters.size() - == 1 + std::numeric_limits::max() - - std::numeric_limits::min()); - LIBSEMIGROUPS_ASSERT(letters.end() == letters.begin() + 256); - } - return letters; - } - } // namespace detail - - namespace words { - size_t human_readable_index(char c) { - static bool first_call = true; - // It might be preferable to use an array here but char is sometimes - // signed and so chars[i] can be negative in the loop below. - static std::unordered_map::letter_type, - Presentation::letter_type> - map; - if (first_call) { - first_call = false; - auto const& chars = detail::chars_in_human_readable_order(); - for (letter_type i = 0; i < chars.size(); ++i) { - map.emplace(chars[i], i); - } - } - LIBSEMIGROUPS_ASSERT(map.size() == 256); - - auto it = map.find(c); - // There are only 256 chars and so it shouldn't be possible that is - // not in the map. - LIBSEMIGROUPS_ASSERT(it != map.cend()); - return it->second; - } - } // namespace words - - //////////////////////////////////////////////////////////////////////// - // 1. WordRange - //////////////////////////////////////////////////////////////////////// - - namespace { - uint64_t geometric_progression(size_t n, size_t a, size_t r) { - LIBSEMIGROUPS_ASSERT(r != 1); // to avoid division by 0 - return a * ((1 - std::pow(r, n)) / (1 - static_cast(r))); - } - - bool word_in_language(size_t n, word_type const& w) { - return std::all_of( - w.cbegin(), w.cend(), [&](letter_type x) { return x < n; }); - } - } // namespace - - uint64_t number_of_words(size_t n, size_t min, size_t max) { - if (max <= min) { - return 0; - } else if (n == 1) { - return max - min; - } - return geometric_progression(max, 1, n) - geometric_progression(min, 1, n); - } - - word_type random_word(size_t length, size_t nr_letters) { - static std::random_device rd; - std::mt19937 mt(rd()); - - if (nr_letters == 0) { - LIBSEMIGROUPS_EXCEPTION( - "the 2nd argument (number of letters) must be non-zero, found 0"); - } - - std::uniform_int_distribution dist(0, nr_letters - 1); - word_type out(length); - std::generate(out.begin(), out.end(), [&dist, &mt]() { return dist(mt); }); - return out; - } - - detail::const_wilo_iterator cbegin_wilo(size_t n, - size_t upper_bound, - word_type&& first, - word_type&& last) { - if (!word_in_language(n, first) - || !lexicographical_compare( - first.cbegin(), first.cend(), last.cbegin(), last.cend())) { - return cend_wilo(n, upper_bound, std::move(first), std::move(last)); - } - if (first.size() >= upper_bound) { - return ++detail::const_wilo_iterator( - n, upper_bound, std::move(first), std::move(last)); - } - return detail::const_wilo_iterator( - n, upper_bound, std::move(first), std::move(last)); - } - - detail::const_wilo_iterator cbegin_wilo(size_t n, - size_t upper_bound, - word_type const& first, - word_type const& last) { - return cbegin_wilo(n, upper_bound, word_type(first), word_type(last)); - } - - detail::const_wilo_iterator - cend_wilo(size_t n, size_t upper_bound, word_type&&, word_type&& last) { - return detail::const_wilo_iterator( - n, upper_bound, word_type(last), std::move(last)); - } - - detail::const_wilo_iterator cend_wilo(size_t n, - size_t upper_bound, - word_type const&, - word_type const& last) { - return cend_wilo(n, upper_bound, word_type(), word_type(last)); - } - - detail::const_wislo_iterator cbegin_wislo(size_t n, - word_type&& first, - word_type&& last) { - if (!word_in_language(n, first) - || !shortlex_compare( - first.cbegin(), first.cend(), last.cbegin(), last.cend())) { - return cend_wislo(n, std::move(first), std::move(last)); - } - return detail::const_wislo_iterator(n, std::move(first), std::move(last)); - } - - detail::const_wislo_iterator cbegin_wislo(size_t n, - word_type const& first, - word_type const& last) { - return cbegin_wislo(n, word_type(first), word_type(last)); - } - - detail::const_wislo_iterator cend_wislo(size_t n, - word_type&&, - word_type&& last) { - return detail::const_wislo_iterator(n, word_type(last), std::move(last)); - } - - detail::const_wislo_iterator cend_wislo(size_t n, - word_type const&, - word_type const& last) { - return cend_wislo(n, word_type(), word_type(last)); - } - - void WordRange::set_iterator() const { - if (!_current_valid) { - _current_valid = true; - _visited = 0; - if (_order == Order::shortlex) { - _current = cbegin_wislo(_alphabet_size, _first, _last); - _end = cend_wislo(_alphabet_size, _first, _last); - } else if (_order == Order::lex) { - _current = cbegin_wilo(_alphabet_size, _upper_bound, _first, _last); - _end = cend_wilo(_alphabet_size, _upper_bound, _first, _last); - } - } - } - - size_t WordRange::count() const noexcept { - if (_order == Order::shortlex) { - return size_hint(); - } else { - return (*this | rx::count()); - } - } - - WordRange& WordRange::init() { - _alphabet_size = 0; - _current_valid = false; - _first = {}; - _last = {}; - _order = Order::shortlex; - _upper_bound = 0; // does nothing if _order is shortlex - _visited = 0; - return *this; - } - - WordRange::WordRange(WordRange const&) = default; - WordRange::WordRange(WordRange&&) = default; - WordRange& WordRange::operator=(WordRange const&) = default; - WordRange& WordRange::operator=(WordRange&&) = default; - WordRange::~WordRange() = default; - - WordRange& WordRange::order(Order val) { - if (val != Order::shortlex && val != Order::lex) { - LIBSEMIGROUPS_EXCEPTION( - "the argument must be Order::shortlex or Order::lex, found {}", val); - } - _current_valid &= (val == _order); - _order = val; - return *this; - } - - [[nodiscard]] std::string to_human_readable_repr(WordRange const& wr, - size_t max_width) { - word_type first = wr.first(); - word_type last = wr.last(); - std::string order = (wr.order() == Order::lex) ? "lex" : "shortlex"; - size_t count = wr.count(); - std::string out; - - bool print_short = false; - - if (first.size() > max_width || last.size() > max_width) { - print_short = true; - } - - if (!print_short) { - out = fmt::format("", - count, - first, - last, - wr.alphabet_size(), - order); - } - - if (out.size() > max_width) { - print_short = true; - } - - if (print_short) { - out = fmt::format( - "", - count, - wr.alphabet_size(), - order); - } - - return out; - } - - //////////////////////////////////////////////////////////////////////// - // 2. Strings -> Words - //////////////////////////////////////////////////////////////////////// - - ToWord::ToWord(ToWord const&) = default; - ToWord::ToWord(ToWord&&) = default; - ToWord& ToWord::operator=(ToWord const&) = default; - ToWord& ToWord::operator=(ToWord&&) = default; - ToWord::~ToWord() = default; - - ToWord& ToWord::init(std::string const& alphabet) { - if (alphabet.size() > 256) { - LIBSEMIGROUPS_EXCEPTION( - "The argument (alphabet) is too big, expected at most 256, found {}", - alphabet.size()); - } - auto _old_alphabet_map = _alphabet_map; - init(); - LIBSEMIGROUPS_ASSERT(_alphabet_map.empty()); - for (letter_type l = 0; l < alphabet.size(); ++l) { - auto it = _alphabet_map.emplace(alphabet[l], l); - if (!it.second) { - // Strong exception guarantee - std::swap(_old_alphabet_map, _alphabet_map); - LIBSEMIGROUPS_EXCEPTION("invalid alphabet {}, duplicate letter {}!", - detail::to_printable(alphabet), - detail::to_printable(alphabet[l])); - } - } - return *this; - } - - [[nodiscard]] std::string ToWord::alphabet() const { - if (empty()) { - return ""; - } - std::string output(_alphabet_map.size(), '\0'); - for (auto it : _alphabet_map) { - output[it.second] = it.first; - } - return output; - } - - void ToWord::call_no_checks(word_type& output, - std::string const& input) const { - // Empty alphabet implies conversion should use human_readable_index - if (empty()) { - output.resize(input.size(), 0); - std::transform(input.cbegin(), input.cend(), output.begin(), [](char c) { - return words::human_readable_index(c); - }); - } else { // Non-empty alphabet implies conversion should use the alphabet. - output.clear(); - output.reserve(input.size()); - for (auto const& c : input) { - output.push_back(_alphabet_map.at(c)); - } - } - } - - void ToWord::operator()(word_type& output, std::string const& input) const { - if (!empty()) { - for (auto const& c : input) { - if (_alphabet_map.find(c) == _alphabet_map.cend()) { - // TODO improve this like in presentation - LIBSEMIGROUPS_EXCEPTION( - "invalid letter \'{}\' in the 2nd argument (input word), " - "expected letters in the alphabet {}!", - c, - detail::to_printable(alphabet())); - } - } - } - call_no_checks(output, input); - } - - //////////////////////////////////////////////////////////////////////// - // 3. Words -> Strings - //////////////////////////////////////////////////////////////////////// - - ToString::ToString(ToString const&) = default; - ToString::ToString(ToString&&) = default; - ToString& ToString::operator=(ToString const&) = default; - ToString& ToString::operator=(ToString&&) = default; - ToString::~ToString() = default; - - ToString& ToString::init(std::string const& alphabet) { - if (alphabet.size() > 256) { - LIBSEMIGROUPS_EXCEPTION( - "The argument (alphabet) is too big, expected at most 256, found {}", - alphabet.size()); - } - auto _old_alphabet_map = _alphabet_map; - init(); - LIBSEMIGROUPS_ASSERT(_alphabet_map.empty()); - for (letter_type i = 0; i < alphabet.size(); ++i) { - auto it = _alphabet_map.emplace(i, alphabet[i]); - if (!it.second) { - std::swap(_old_alphabet_map, _alphabet_map); - LIBSEMIGROUPS_EXCEPTION("invalid alphabet {}, duplicate letter {}!", - detail::to_printable(alphabet), - detail::to_printable(alphabet[i])); - } - } - return *this; - } - - [[nodiscard]] std::string ToString::alphabet() const { - if (empty()) { - return ""; - } - - std::string output(_alphabet_map.size(), '\0'); - for (auto it : _alphabet_map) { - output[it.first] = it.second; - } - return output; - } - - void ToString::call_no_checks(std::string& output, - word_type const& input) const { - // Empty alphabet implies conversion should use human_readable_index - if (empty()) { - output.resize(input.size(), 0); - std::transform( - input.cbegin(), input.cend(), output.begin(), [](letter_type c) { - return words::human_readable_letter<>(c); - }); - } else { // Non-empty alphabet implies conversion should use the alphabet. - output.clear(); - output.reserve(input.size()); - for (letter_type const& l : input) { - output.push_back(_alphabet_map.at(l)); - } - } - } - - void ToString::operator()(std::string& output, word_type const& input) const { - if (!empty()) { - for (letter_type const& l : input) { - if (_alphabet_map.find(l) == _alphabet_map.cend()) { - LIBSEMIGROUPS_EXCEPTION( - "invalid letter \'{}\' in the 2nd argument (input word), " - "expected letters in the range [0, {})!", - l, - _alphabet_map.size()); - } - } - } - call_no_checks(output, input); - } - - //////////////////////////////////////////////////////////////////////// - // 4. StringRange - //////////////////////////////////////////////////////////////////////// - - namespace detail { - // This is not in an unnamed namespace because it is used by random_strings. - // The random_strings return type is particularly nasty, and random_strings - // should therefore probably remain an `auto inline` function defined in the - // hpp file. - void throw_if_random_string_should_throw(std::string const& alphabet, - size_t min, - size_t max) { - if (min >= max) { - LIBSEMIGROUPS_EXCEPTION( - "the 2nd argument (min) must be less than the 3rd argument (max)"); - } else if (alphabet.empty() && min != 0) { - LIBSEMIGROUPS_EXCEPTION("expected non-empty 1st argument (alphabet)"); - } - } - } // namespace detail - - std::string random_string(std::string const& alphabet, size_t length) { - static std::random_device rd; - static std::mt19937 generator(rd()); - std::uniform_int_distribution<> distribution(0, alphabet.size() - 1); - - std::string result; - - for (size_t i = 0; i < length; ++i) { - result += alphabet[distribution(generator)]; - } - - return result; - } - - // Random string with random length in the range [min, max) over - std::string random_string(std::string const& alphabet, - size_t min, - size_t max) { - detail::throw_if_random_string_should_throw(alphabet, min, max); - if (max == min + 1) { - return random_string(alphabet, min); - } - static std::random_device rd; - static std::mt19937 generator(rd()); - std::uniform_int_distribution<> distribution(min, max - 1); - return random_string(alphabet, distribution(generator)); - } - - StringRange& StringRange::init() { - _current.clear(); - _current_valid = false; - _letters.clear(); - _to_word.init(); - _to_string.init(); - _word_range.init(); - return *this; - } - - StringRange::StringRange(StringRange const&) = default; - StringRange::StringRange(StringRange&&) = default; - StringRange& StringRange::operator=(StringRange const&) = default; - StringRange& StringRange::operator=(StringRange&&) = default; - StringRange::~StringRange() = default; - - StringRange& StringRange::alphabet(std::string const& x) { - // Need to do this _to_word.init(x) first, because if this throws then the - // rest should remain unchanged. - _to_word.init(x); - _to_string.init(x); - _word_range.alphabet_size(x.size()); - _current_valid = _word_range.valid(); - _letters = x; - return *this; - } - - [[nodiscard]] std::string to_human_readable_repr(StringRange const& sr, - size_t max_width) { - std::string first = sr.first(); - std::string last = sr.last(); - std::string alphabet = sr.alphabet(); - std::string order = (sr.order() == Order::lex) ? "lex" : "shortlex"; - size_t count = sr.count(); - std::string out; - - bool print_short = false; - - if (first.size() > max_width || last.size() > max_width - || alphabet.size() > max_width) { - print_short = true; - } - - if (!print_short) { - out = fmt::format( - "", - count, - first, - last, - alphabet, - order); - } - - if (out.size() > max_width) { - print_short = true; - } - - if (print_short) { - out = fmt::format("", count, order); - } - - return out; - } - - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// - - namespace literals { - word_type operator"" _w(char const* w, size_t n) { - word_type result; -#ifdef LIBSEMIGROUPS_DEBUG - static const std::string valid_chars = "0123456789"; -#endif - // 0 is unset, 1 is reading integers, 2 is parsing a string - int mode = 0; - for (size_t i = 0; i < n; ++i) { - if (48 <= w[i] && w[i] < 58) { - if (mode == 0) { - mode = 1; - } else if (mode == 2) { - LIBSEMIGROUPS_EXCEPTION("cannot mix numbers and letters, expected " - "digits in 0123456789, found {}", - w[i]); - } - LIBSEMIGROUPS_ASSERT(valid_chars.find(w[i]) != std::string::npos); - result.push_back(static_cast(w[i] - 48)); - } else if (97 <= w[i] && w[i] < 123) { - if (mode == 0) { - mode = 2; - } else if (mode == 1) { - LIBSEMIGROUPS_EXCEPTION("cannot mix numbers and letters, expected " - "digits in 0123456789, found {}", - w[i]); - } - result.push_back(words::human_readable_index(w[i])); - } else { - LIBSEMIGROUPS_EXCEPTION( - "the argument contains the character \'{}\', expected only " - "digits in 0123456789 or characters in \"a-zA-Z\"", - detail::to_visible(w[i])); - } - } - return result; - } - - word_type operator"" _w(const char* w) { - return operator"" _w(w, std::strlen(w)); - } - - namespace { - // The next function implements the Shunting Yard Algorithm to convert - // the expression in input to reverse Polish notation, as described - // here: https://en.wikipedia.org/wiki/Shunting_yard_algorithm - std::string shunting_yard(char const* input, size_t len) { - std::string input_copy; - if (len == 0) { - return input_copy; - } - - for (size_t i = 0; i < len - 1; ++i) { - if (input[i] == '*') { - LIBSEMIGROUPS_EXCEPTION( - "Illegal character \'*\' in position {} of \"{}\"", i, input); - } - input_copy += input[i]; - if ((std::isalpha(input[i]) - && (std::isalpha(input[i + 1]) || input[i + 1] == '(')) - || (std::isdigit(input[i]) && !std::isdigit(input[i + 1]) - && input[i + 1] != ')') - || (input[i] == ')' && std::isalpha(input[i + 1]))) { - input_copy += "*"; - } - } - input_copy += input[len - 1]; - - std::string output; - std::stack ops; - - for (size_t i = 0; i < input_copy.size(); ++i) { - if (std::isalpha(input_copy[i])) { - output += input_copy[i]; - } else if (std::isdigit(input_copy[i])) { - output += input_copy[i]; - } else if (input_copy[i] == '(' || input_copy[i] == '^') { - ops.push(input_copy[i]); - } else if (input_copy[i] == '*') { - while (!ops.empty() && ops.top() != '(') { - output += ops.top(); - ops.pop(); - } - ops.push(input_copy[i]); - } else if (input_copy[i] == ')') { - if (ops.empty()) { - LIBSEMIGROUPS_EXCEPTION( - "Unmatched closing \')\' in position {} of \"{}\"", - i - std::count(input_copy.begin(), input_copy.end(), '*'), - input); - } - while (!ops.empty() && ops.top() != '(') { - output += ops.top(); - ops.pop(); - } - if (ops.empty()) { - LIBSEMIGROUPS_EXCEPTION( - "Unmatched closing \')\' in position {} of \"{}\"", - i - std::count(input_copy.begin(), input_copy.end(), '*'), - input); - } - ops.pop(); // pop the '(' from the stack and discard - } else if (input_copy[i] != ' ') { - LIBSEMIGROUPS_EXCEPTION( - "Illegal character \'{}\' in position {} of \"{}\"", - input_copy[i], - i - std::count(input_copy.begin(), input_copy.end(), '*'), - input); - } - } - while (!ops.empty()) { - if (ops.top() == '(' || ops.top() == ')') { - LIBSEMIGROUPS_EXCEPTION("Unmatched opening \'(\' in {}", input); - } - output += ops.top(); - ops.pop(); - } - return output; - } - - bool try_pop_two(std::stack& stck, - std::pair& pr) { - if (stck.size() < 2) { - return false; - } - pr.first = std::move(stck.top()); - stck.pop(); - pr.second = std::move(stck.top()); - stck.pop(); - return true; - } - - std::string inline evaluate_rpn(std::string const& rpn, - std::string const& orig) { - using namespace words; // NOLINT(build/namespaces) - std::stack stck; - bool in_digits = false; - std::pair pr; - - for (auto const& term : rpn) { - if (term == '^') { - in_digits = false; - if (try_pop_two(stck, pr)) { - auto it = std::find_if_not( - pr.first.begin(), pr.first.end(), [](auto const& c) { - return std::isdigit(c); - }); - if (it != pr.first.end()) { - LIBSEMIGROUPS_EXCEPTION( - "Incorrect arguments for operator \'^\', expected only " - "digits, found \"^{}\" in \"{}\"", - *it, - orig); - } - stck.push(pow(pr.second, std::stol(pr.first))); - } else { - LIBSEMIGROUPS_EXCEPTION( - "Missing argument(s) for operator \'^\', " - "expected 2 arguments found {} in \"{}\"", - stck.empty() ? "0" : fmt::format("\"{}\"", stck.top()), - orig); - } - } else if (term == '*') { - in_digits = false; - if (try_pop_two(stck, pr)) { - stck.push(pr.second + pr.first); - } else { - LIBSEMIGROUPS_EXCEPTION( - "Missing argument(s) for operator \'*\', " - "expected 2 arguments found {} in \"{}\"", - stck.empty() ? "0" : fmt::format("\"{}\"", stck.top()), - orig); - } - } else if (std::isdigit(term)) { - if (in_digits) { - LIBSEMIGROUPS_ASSERT(!stck.empty()); - stck.top() += term; - } else { - in_digits = true; - stck.emplace(&term, 1); - } - } else { - in_digits = false; - stck.emplace(&term, 1); - } - } - std::string result; - while (!stck.empty()) { - result = stck.top() + result; - stck.pop(); - } - return result; - } - } // namespace - - std::string operator""_p(char const* w, size_t n) { - return evaluate_rpn(shunting_yard(w, n), w); - } - - std::string operator""_p(char const* w) { - return operator""_p(w, std::strlen(w)); - } - } // namespace literals -} // namespace libsemigroups diff --git a/tests/test-cong.cpp b/tests/test-cong.cpp index bb8379371..fe9d37dc6 100644 --- a/tests/test-cong.cpp +++ b/tests/test-cong.cpp @@ -29,9 +29,9 @@ #include "libsemigroups/obvinf.hpp" // for is_obviously_infinite #include "libsemigroups/pbr.hpp" // for PBR #include "libsemigroups/to-froidure-pin.hpp" -#include "libsemigroups/transf.hpp" // for Transf<> -#include "libsemigroups/types.hpp" // for word_type -#include "libsemigroups/words.hpp" // for literals +#include "libsemigroups/transf.hpp" // for Transf<> +#include "libsemigroups/types.hpp" // for word_type +#include "libsemigroups/word-range.hpp" // for literals #include "libsemigroups/detail/report.hpp" // for ReportGuard diff --git a/tests/test-fpsemi-examples-1.cpp b/tests/test-fpsemi-examples-1.cpp index 24d35d451..4d4737dcb 100644 --- a/tests/test-fpsemi-examples-1.cpp +++ b/tests/test-fpsemi-examples-1.cpp @@ -40,7 +40,7 @@ #include "libsemigroups/to-froidure-pin.hpp" // for to_froidure_pin #include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter #include "libsemigroups/types.hpp" // for congruence_kind, word_type -#include "libsemigroups/words.hpp" // for operator""_w +#include "libsemigroups/word-range.hpp" // for operator""_w #include "libsemigroups/detail/fmt.hpp" // for format, print // #include "libsemigroups/detail/report.hpp" // for ReportGuard diff --git a/tests/test-fpsemi-examples-2.cpp b/tests/test-fpsemi-examples-2.cpp index e0b2538f5..ae5b7c7f9 100644 --- a/tests/test-fpsemi-examples-2.cpp +++ b/tests/test-fpsemi-examples-2.cpp @@ -43,7 +43,7 @@ #include "libsemigroups/to-presentation.hpp" // for to_presentation #include "libsemigroups/types.hpp" // for congruence_kind, word_type #include "libsemigroups/word-graph.hpp" // for is_complete -#include "libsemigroups/words.hpp" // for operator""_w, to_string +#include "libsemigroups/word-range.hpp" // for operator""_w, to_string #include "libsemigroups/detail/eigen.hpp" // // for DenseBase::row, DenseBa... #include "libsemigroups/detail/fmt.hpp" // for format, print diff --git a/tests/test-fpsemi-intf.cpp b/tests/test-fpsemi-intf.cpp index 50a46069d..5332ba640 100644 --- a/tests/test-fpsemi-intf.cpp +++ b/tests/test-fpsemi-intf.cpp @@ -33,7 +33,7 @@ // #include "libsemigroups/order.hpp" // for shortlex_words // #include "libsemigroups/todd-coxeter.hpp" // for fpsemigroup::ToddCoxeter // #include "libsemigroups/transf.hpp" // for fpsemigroup::ToddCoxeter -// #include "libsemigroups/words.hpp" // for number_of_words +// #include "libsemigroups/word-range.hpp" // for number_of_words // // #include "libsemigroups/detail/string.hpp" // for to_string of rule_type for // debugging diff --git a/tests/test-kambites.cpp b/tests/test-kambites.cpp index 5d9ea3564..0153dbcfe 100644 --- a/tests/test-kambites.cpp +++ b/tests/test-kambites.cpp @@ -30,7 +30,7 @@ #include "libsemigroups/to-froidure-pin.hpp" // for to_froidure_pin #include "libsemigroups/transf.hpp" // for LeastTransf #include "libsemigroups/types.hpp" // for tril etc -#include "libsemigroups/words.hpp" // for number_of_words +#include "libsemigroups/word-range.hpp" // for number_of_words #include "libsemigroups/detail/report.hpp" // for ReportGuard #include "libsemigroups/detail/string.hpp" // for random_string etc diff --git a/tests/test-kbe.cpp b/tests/test-kbe.cpp index 9a1018228..0c724cca5 100644 --- a/tests/test-kbe.cpp +++ b/tests/test-kbe.cpp @@ -25,7 +25,7 @@ #include "libsemigroups/knuth-bendix.hpp" // for KnuthBendix #include "libsemigroups/to-froidure-pin.hpp" // for to_froidure_pin #include "libsemigroups/transf.hpp" // for Transf<> -#include "libsemigroups/words.hpp" // for literals +#include "libsemigroups/word-range.hpp" // for literals #include "libsemigroups/detail/kbe.hpp" // for KBE diff --git a/tests/test-knuth-bendix-3.cpp b/tests/test-knuth-bendix-3.cpp index 9a75e2885..c870fb476 100644 --- a/tests/test-knuth-bendix-3.cpp +++ b/tests/test-knuth-bendix-3.cpp @@ -51,7 +51,7 @@ #include "libsemigroups/presentation.hpp" // for add_rule, Presentation #include "libsemigroups/ranges.hpp" // for equal #include "libsemigroups/word-graph.hpp" // for WordGraph -#include "libsemigroups/words.hpp" // for Inner, ToString, Str... +#include "libsemigroups/word-range.hpp" // for Inner, ToString, Str... #include "libsemigroups/detail/report.hpp" // for ReportGuard diff --git a/tests/test-knuth-bendix-4.cpp b/tests/test-knuth-bendix-4.cpp index ca465f2bf..a286f4a55 100644 --- a/tests/test-knuth-bendix-4.cpp +++ b/tests/test-knuth-bendix-4.cpp @@ -55,7 +55,7 @@ #include "libsemigroups/paths.hpp" // for Paths #include "libsemigroups/presentation.hpp" // for add_rule, Presentation #include "libsemigroups/word-graph.hpp" // for WordGraph -#include "libsemigroups/words.hpp" // for Inner, StringRange, to_str... +#include "libsemigroups/word-range.hpp" // for Inner, StringRange, to_str... #include "libsemigroups/detail/report.hpp" // for ReportGuard #include "libsemigroups/detail/stl.hpp" // for apply_permutation diff --git a/tests/test-knuth-bendix-5.cpp b/tests/test-knuth-bendix-5.cpp index 372f5cbff..72bec3ca8 100644 --- a/tests/test-knuth-bendix-5.cpp +++ b/tests/test-knuth-bendix-5.cpp @@ -53,7 +53,7 @@ #include "libsemigroups/to-presentation.hpp" // for to_presentation #include "libsemigroups/transf.hpp" // for Transf #include "libsemigroups/types.hpp" // for word_type, letter_type -#include "libsemigroups/words.hpp" // for operator""_w +#include "libsemigroups/word-range.hpp" // for operator""_w #include "libsemigroups/detail/kbe.hpp" // for KBE #include "libsemigroups/detail/report.hpp" // for ReportGuard diff --git a/tests/test-knuth-bendix-6.cpp b/tests/test-knuth-bendix-6.cpp index 5dd26002e..1b0d24a1a 100644 --- a/tests/test-knuth-bendix-6.cpp +++ b/tests/test-knuth-bendix-6.cpp @@ -46,7 +46,7 @@ #include "libsemigroups/presentation.hpp" // for add_rule #include "libsemigroups/to-froidure-pin.hpp" // for to_froidure_pin #include "libsemigroups/types.hpp" // for word_type -#include "libsemigroups/words.hpp" // for operator""_w +#include "libsemigroups/word-range.hpp" // for operator""_w #include "libsemigroups/detail/report.hpp" // for ReportGuard diff --git a/tests/test-obvinf.cpp b/tests/test-obvinf.cpp index b4be0f1e4..b13c70800 100644 --- a/tests/test-obvinf.cpp +++ b/tests/test-obvinf.cpp @@ -22,8 +22,8 @@ #include "libsemigroups/exception.hpp" #include "test-main.hpp" -#include "libsemigroups/obvinf.hpp" // for IsObviouslyInfinite -#include "libsemigroups/words.hpp" // for operator""_w +#include "libsemigroups/obvinf.hpp" // for IsObviouslyInfinite +#include "libsemigroups/word-range.hpp" // for operator""_w namespace libsemigroups { namespace { diff --git a/tests/test-paths.cpp b/tests/test-paths.cpp index 6564e246a..648713da6 100644 --- a/tests/test-paths.cpp +++ b/tests/test-paths.cpp @@ -45,7 +45,7 @@ #include "libsemigroups/to-froidure-pin.hpp" #include "libsemigroups/types.hpp" // for word_type, relation_type #include "libsemigroups/word-graph.hpp" // for WordGraph, make, pow -#include "libsemigroups/words.hpp" // for operator""_w, WordRange +#include "libsemigroups/word-range.hpp" // for operator""_w, WordRange #include "libsemigroups/detail/report.hpp" // for ReportGuard #include "libsemigroups/detail/stl.hpp" // for hash diff --git a/tests/test-presentation.cpp b/tests/test-presentation.cpp index 500f16714..15cc97d69 100644 --- a/tests/test-presentation.cpp +++ b/tests/test-presentation.cpp @@ -51,7 +51,7 @@ #include "libsemigroups/ranges.hpp" // for chain, shortlex_compare #include "libsemigroups/to-presentation.hpp" // for to_presentation #include "libsemigroups/types.hpp" // for word_type, letter_type -#include "libsemigroups/words.hpp" // for operator+=, operator""_w +#include "libsemigroups/word-range.hpp" // for operator+=, operator""_w #include "libsemigroups/detail/containers.hpp" // for StaticVector1, operat... #include "libsemigroups/detail/int-range.hpp" // for IntRange diff --git a/tests/test-stephen.cpp b/tests/test-stephen.cpp index 60014abae..85201f5c2 100644 --- a/tests/test-stephen.cpp +++ b/tests/test-stephen.cpp @@ -46,7 +46,7 @@ #include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter #include "libsemigroups/types.hpp" // for word_type #include "libsemigroups/word-graph.hpp" // for WordGraph, ope... -#include "libsemigroups/words.hpp" // for StringToWord, word... +#include "libsemigroups/word-range.hpp" // for StringToWord, word... #include "libsemigroups/detail/report.hpp" // for ReportGuard diff --git a/tests/test-todd-coxeter.cpp b/tests/test-todd-coxeter.cpp index ff28985bf..5270fbaa6 100644 --- a/tests/test-todd-coxeter.cpp +++ b/tests/test-todd-coxeter.cpp @@ -37,7 +37,7 @@ #include "libsemigroups/to-todd-coxeter.hpp" // for ?? #include "libsemigroups/todd-coxeter.hpp" // for ToddCoxeter #include "libsemigroups/transf.hpp" // for Transf -#include "libsemigroups/words.hpp" // for operator"" _w +#include "libsemigroups/word-range.hpp" // for operator"" _w #include "libsemigroups/detail/report.hpp" // for ReportGuard #include "libsemigroups/detail/tce.hpp" // for TCE diff --git a/tests/test-ukkonen.cpp b/tests/test-ukkonen.cpp index 237a0ec88..134819f5c 100644 --- a/tests/test-ukkonen.cpp +++ b/tests/test-ukkonen.cpp @@ -24,11 +24,11 @@ #include "catch_amalgamated.hpp" // for REQUIRE, REQUIRE_THROWS_AS #include "test-main.hpp" // for LIBSEMIGROUPS_TEST_CASE -#include "libsemigroups/constants.hpp" // for operator==, POSITIVE_INFINITY -#include "libsemigroups/exception.hpp" // for LIBSEMIGROUPS_EXCEPTION -#include "libsemigroups/types.hpp" // for word_type -#include "libsemigroups/ukkonen.hpp" // for Ukkonen, Ukkonen::State -#include "libsemigroups/words.hpp" // for literals +#include "libsemigroups/constants.hpp" // for operator==, POSITIVE_INFINITY +#include "libsemigroups/exception.hpp" // for LIBSEMIGROUPS_EXCEPTION +#include "libsemigroups/types.hpp" // for word_type +#include "libsemigroups/ukkonen.hpp" // for Ukkonen, Ukkonen::State +#include "libsemigroups/word-range.hpp" // for literals namespace libsemigroups { diff --git a/tests/test-word-graph.cpp b/tests/test-word-graph.cpp index b7b88bf8b..994cd2ffe 100644 --- a/tests/test-word-graph.cpp +++ b/tests/test-word-graph.cpp @@ -27,7 +27,7 @@ #include "libsemigroups/forest.hpp" // for Forest #include "libsemigroups/word-graph.hpp" // for WordGraph -#include "libsemigroups/words.hpp" // for literals, WordRange +#include "libsemigroups/word-range.hpp" // for literals, WordRange #include "libsemigroups/detail/string.hpp" // for detail::to_string