diff --git a/docs/source/data-structures/order/functions.rst b/docs/source/data-structures/order/functions.rst new file mode 100644 index 00000000..032175a6 --- /dev/null +++ b/docs/source/data-structures/order/functions.rst @@ -0,0 +1,75 @@ +.. + Copyright (c) 2026 Joseph Edwards, J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +Functions for orders +==================== + +This page contains the documentation for functions that compare lists of +integers ``list[int]`` or strings ``str`` with respect to certain reduction +orderings. + +.. important:: + + The following functions are deprecated in v1.5.0 and will be removed v2.0.0: + + * ``lexicographical_compare``, please use :any:`lex_cmp` instead. + * ``recursive_path_compare`` please use :any:`rev_rpo_cmp` instead. + * ``shortlex_compare`` please use :any:`lenlex_cmp` instead. + +Contents +-------- + +.. autosummary:: + :signatures: short + + len_wt_lex_cmp + lenlex_cmp + lex_cmp + rev_len_wt_lex_cmp + rev_lenlex_cmp + rev_lex_cmp + rev_rpo_cmp + rev_wr_cmp + rev_wt_lenlex_cmp + rev_wt_lex_cmp + rpo_cmp + wr_cmp + wt_lenlex_cmp + wt_lex_cmp + +Full API +-------- + +.. autofunction:: len_wt_lex_cmp + +.. autofunction:: lenlex_cmp + +.. autofunction:: lex_cmp + +.. autofunction:: rev_len_wt_lex_cmp + +.. autofunction:: rev_lenlex_cmp + +.. autofunction:: rev_lex_cmp + +.. autofunction:: rev_rpo_cmp + +.. autofunction:: rev_wr_cmp + +.. autofunction:: rev_wt_lenlex_cmp + +.. autofunction:: rev_wt_lex_cmp + +.. autofunction:: rpo_cmp + +.. autofunction:: wr_cmp + +.. autofunction:: wt_lenlex_cmp + +.. autofunction:: wt_lex_cmp diff --git a/docs/source/data-structures/order/index.rst b/docs/source/data-structures/order/index.rst index 30a0a68e..8a8449d0 100644 --- a/docs/source/data-structures/order/index.rst +++ b/docs/source/data-structures/order/index.rst @@ -10,70 +10,33 @@ Orders ====== -This page contains the documentation for several functions for comparing words -or strings with respect to certain reduction orderings. +This page contains the documentation for classes and several functions for +comparing words (i.e. lists of integers ``list[int]`` or strings ``str``) with +respect to certain reduction orderings. .. seealso:: - :any:`Order` - :any:`Alphabet` -.. important:: - - The following functions are deprecated in v1.5.0 and will be removed v2.0.0: - - * ``lexicographical_compare``, please use :any:`lex_cmp` instead. - * ``recursive_path_compare`` please use :any:`rev_rpo_cmp` instead. - * ``shortlex_compare`` please use :any:`lenlex_cmp` instead. - -Contents --------- - -.. autosummary:: - :signatures: short - - len_wt_lex_cmp - lenlex_cmp - lex_cmp - rev_len_wt_lex_cmp - rev_lenlex_cmp - rev_lex_cmp - rev_rpo_cmp - rev_wr_cmp - rev_wt_lenlex_cmp - rev_wt_lex_cmp - rpo_cmp - wr_cmp - wt_lenlex_cmp - wt_lex_cmp - -Full API --------- - -.. autofunction:: len_wt_lex_cmp - -.. autofunction:: lenlex_cmp - -.. autofunction:: lex_cmp - -.. autofunction:: rev_len_wt_lex_cmp - -.. autofunction:: rev_lenlex_cmp - -.. autofunction:: rev_lex_cmp - -.. autofunction:: rev_rpo_cmp - -.. autofunction:: rev_wr_cmp - -.. autofunction:: rev_wt_lenlex_cmp - -.. autofunction:: rev_wt_lex_cmp - -.. autofunction:: rpo_cmp - -.. autofunction:: wr_cmp - -.. autofunction:: wt_lenlex_cmp - -.. autofunction:: wt_lex_cmp +The classes and functions in ``libsemigroups_pybind11`` for comparing words +are described on the following pages: + +.. toctree:: + :maxdepth: 1 + + lex-cmp + rev-lex-cmp + lenlex-cmp + rev-lenlex-cmp + rpo-cmp + rev-rpo-cmp + wr-cmp + rev-wr-cmp + wt-lenlex-cmp + rev-wt-lenlex-cmp + wt-lex-cmp + rev-wt-lex-cmp + len-wt-lex-cmp + rev-len-wt-lex-cmp + functions diff --git a/docs/source/data-structures/order/len-wt-lex-cmp.rst b/docs/source/data-structures/order/len-wt-lex-cmp.rst new file mode 100644 index 00000000..5e158899 --- /dev/null +++ b/docs/source/data-structures/order/len-wt-lex-cmp.rst @@ -0,0 +1,35 @@ +.. + Copyright (c) 2026 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +The LenWtLexCmp class +===================== + +.. autoclass:: LenWtLexCmp + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~LenWtLexCmp + LenWtLexCmp.__call__ + LenWtLexCmp.alphabet + LenWtLexCmp.copy + LenWtLexCmp.init + LenWtLexCmp.weights + +Full API +-------- + +.. autoclass:: LenWtLexCmp + :class-doc-from: init + :special-members: __call__ + :members: diff --git a/docs/source/data-structures/order/lenlex-cmp.rst b/docs/source/data-structures/order/lenlex-cmp.rst new file mode 100644 index 00000000..f2b9c172 --- /dev/null +++ b/docs/source/data-structures/order/lenlex-cmp.rst @@ -0,0 +1,34 @@ +.. + Copyright (c) 2026 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +The LenLexCmp class +=================== + +.. autoclass:: LenLexCmp + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~LenLexCmp + LenLexCmp.__call__ + LenLexCmp.alphabet + LenLexCmp.copy + LenLexCmp.init + +Full API +-------- + +.. autoclass:: LenLexCmp + :class-doc-from: init + :special-members: __call__ + :members: diff --git a/docs/source/data-structures/order/lex-cmp.rst b/docs/source/data-structures/order/lex-cmp.rst new file mode 100644 index 00000000..2a07dbaa --- /dev/null +++ b/docs/source/data-structures/order/lex-cmp.rst @@ -0,0 +1,34 @@ +.. + Copyright (c) 2026 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +The LexCmp class +================ + +.. autoclass:: LexCmp + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~LexCmp + LexCmp.__call__ + LexCmp.alphabet + LexCmp.copy + LexCmp.init + +Full API +-------- + +.. autoclass:: LexCmp + :class-doc-from: init + :special-members: __call__ + :members: diff --git a/docs/source/data-structures/order/rev-len-wt-lex-cmp.rst b/docs/source/data-structures/order/rev-len-wt-lex-cmp.rst new file mode 100644 index 00000000..d372c0f2 --- /dev/null +++ b/docs/source/data-structures/order/rev-len-wt-lex-cmp.rst @@ -0,0 +1,35 @@ +.. + Copyright (c) 2026 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +The RevLenWtLexCmp class +======================== + +.. autoclass:: RevLenWtLexCmp + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~RevLenWtLexCmp + RevLenWtLexCmp.__call__ + RevLenWtLexCmp.alphabet + RevLenWtLexCmp.copy + RevLenWtLexCmp.init + RevLenWtLexCmp.weights + +Full API +-------- + +.. autoclass:: RevLenWtLexCmp + :class-doc-from: init + :special-members: __call__ + :members: diff --git a/docs/source/data-structures/order/rev-lenlex-cmp.rst b/docs/source/data-structures/order/rev-lenlex-cmp.rst new file mode 100644 index 00000000..69deed3f --- /dev/null +++ b/docs/source/data-structures/order/rev-lenlex-cmp.rst @@ -0,0 +1,34 @@ +.. + Copyright (c) 2026 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +The RevLenLexCmp class +====================== + +.. autoclass:: RevLenLexCmp + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~RevLenLexCmp + RevLenLexCmp.__call__ + RevLenLexCmp.alphabet + RevLenLexCmp.copy + RevLenLexCmp.init + +Full API +-------- + +.. autoclass:: RevLenLexCmp + :class-doc-from: init + :special-members: __call__ + :members: diff --git a/docs/source/data-structures/order/rev-lex-cmp.rst b/docs/source/data-structures/order/rev-lex-cmp.rst new file mode 100644 index 00000000..f89f5362 --- /dev/null +++ b/docs/source/data-structures/order/rev-lex-cmp.rst @@ -0,0 +1,34 @@ +.. + Copyright (c) 2026 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +The RevLexCmp class +=================== + +.. autoclass:: RevLexCmp + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~RevLexCmp + RevLexCmp.__call__ + RevLexCmp.alphabet + RevLexCmp.copy + RevLexCmp.init + +Full API +-------- + +.. autoclass:: RevLexCmp + :class-doc-from: init + :special-members: __call__ + :members: diff --git a/docs/source/data-structures/order/rev-rpo-cmp.rst b/docs/source/data-structures/order/rev-rpo-cmp.rst new file mode 100644 index 00000000..c5408590 --- /dev/null +++ b/docs/source/data-structures/order/rev-rpo-cmp.rst @@ -0,0 +1,34 @@ +.. + Copyright (c) 2026 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +The RevRPOCmp class +=================== + +.. autoclass:: RevRPOCmp + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~RevRPOCmp + RevRPOCmp.__call__ + RevRPOCmp.alphabet + RevRPOCmp.copy + RevRPOCmp.init + +Full API +-------- + +.. autoclass:: RevRPOCmp + :class-doc-from: init + :special-members: __call__ + :members: diff --git a/docs/source/data-structures/order/rev-wr-cmp.rst b/docs/source/data-structures/order/rev-wr-cmp.rst new file mode 100644 index 00000000..1883a68a --- /dev/null +++ b/docs/source/data-structures/order/rev-wr-cmp.rst @@ -0,0 +1,35 @@ +.. + Copyright (c) 2026 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +The RevWrCmp class +================== + +.. autoclass:: RevWrCmp + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~RevWrCmp + RevWrCmp.__call__ + RevWrCmp.alphabet + RevWrCmp.copy + RevWrCmp.init + RevWrCmp.levels + +Full API +-------- + +.. autoclass:: RevWrCmp + :class-doc-from: init + :special-members: __call__ + :members: diff --git a/docs/source/data-structures/order/rev-wt-lenlex-cmp.rst b/docs/source/data-structures/order/rev-wt-lenlex-cmp.rst new file mode 100644 index 00000000..3f08065c --- /dev/null +++ b/docs/source/data-structures/order/rev-wt-lenlex-cmp.rst @@ -0,0 +1,35 @@ +.. + Copyright (c) 2026 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +The RevWtLenLexCmp class +======================== + +.. autoclass:: RevWtLenLexCmp + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~RevWtLenLexCmp + RevWtLenLexCmp.__call__ + RevWtLenLexCmp.alphabet + RevWtLenLexCmp.copy + RevWtLenLexCmp.init + RevWtLenLexCmp.weights + +Full API +-------- + +.. autoclass:: RevWtLenLexCmp + :class-doc-from: init + :special-members: __call__ + :members: diff --git a/docs/source/data-structures/order/rev-wt-lex-cmp.rst b/docs/source/data-structures/order/rev-wt-lex-cmp.rst new file mode 100644 index 00000000..bf1be420 --- /dev/null +++ b/docs/source/data-structures/order/rev-wt-lex-cmp.rst @@ -0,0 +1,35 @@ +.. + Copyright (c) 2026 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +The RevWtLexCmp class +===================== + +.. autoclass:: RevWtLexCmp + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~RevWtLexCmp + RevWtLexCmp.__call__ + RevWtLexCmp.alphabet + RevWtLexCmp.copy + RevWtLexCmp.init + RevWtLexCmp.weights + +Full API +-------- + +.. autoclass:: RevWtLexCmp + :class-doc-from: init + :special-members: __call__ + :members: diff --git a/docs/source/data-structures/order/rpo-cmp.rst b/docs/source/data-structures/order/rpo-cmp.rst new file mode 100644 index 00000000..adfd3a9b --- /dev/null +++ b/docs/source/data-structures/order/rpo-cmp.rst @@ -0,0 +1,34 @@ +.. + Copyright (c) 2026 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +The RPOCmp class +================ + +.. autoclass:: RPOCmp + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~RPOCmp + RPOCmp.__call__ + RPOCmp.alphabet + RPOCmp.copy + RPOCmp.init + +Full API +-------- + +.. autoclass:: RPOCmp + :class-doc-from: init + :special-members: __call__ + :members: diff --git a/docs/source/data-structures/order/wr-cmp.rst b/docs/source/data-structures/order/wr-cmp.rst new file mode 100644 index 00000000..082dbbdd --- /dev/null +++ b/docs/source/data-structures/order/wr-cmp.rst @@ -0,0 +1,35 @@ +.. + Copyright (c) 2026 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +The WrCmp class +=============== + +.. autoclass:: WrCmp + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~WrCmp + WrCmp.__call__ + WrCmp.alphabet + WrCmp.copy + WrCmp.init + WrCmp.levels + +Full API +-------- + +.. autoclass:: WrCmp + :class-doc-from: init + :special-members: __call__ + :members: diff --git a/docs/source/data-structures/order/wt-lenlex-cmp.rst b/docs/source/data-structures/order/wt-lenlex-cmp.rst new file mode 100644 index 00000000..e747c9d1 --- /dev/null +++ b/docs/source/data-structures/order/wt-lenlex-cmp.rst @@ -0,0 +1,35 @@ +.. + Copyright (c) 2026 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +The WtLenLexCmp class +===================== + +.. autoclass:: WtLenLexCmp + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~WtLenLexCmp + WtLenLexCmp.__call__ + WtLenLexCmp.alphabet + WtLenLexCmp.copy + WtLenLexCmp.init + WtLenLexCmp.weights + +Full API +-------- + +.. autoclass:: WtLenLexCmp + :class-doc-from: init + :special-members: __call__ + :members: diff --git a/docs/source/data-structures/order/wt-lex-cmp.rst b/docs/source/data-structures/order/wt-lex-cmp.rst new file mode 100644 index 00000000..50358009 --- /dev/null +++ b/docs/source/data-structures/order/wt-lex-cmp.rst @@ -0,0 +1,35 @@ +.. + Copyright (c) 2026 J. D. Mitchell + + Distributed under the terms of the GPL license version 3. + + The full license is in the file LICENSE, distributed with this software. + +.. currentmodule:: libsemigroups_pybind11 + +The WtLexCmp class +================== + +.. autoclass:: WtLexCmp + :doc-only: + +Contents +-------- + +.. autosummary:: + :signatures: short + + ~WtLexCmp + WtLexCmp.__call__ + WtLexCmp.alphabet + WtLexCmp.copy + WtLexCmp.init + WtLexCmp.weights + +Full API +-------- + +.. autoclass:: WtLexCmp + :class-doc-from: init + :special-members: __call__ + :members: diff --git a/src/libsemigroups_pybind11/__init__.py b/src/libsemigroups_pybind11/__init__.py index 212f2498..5aaccce4 100644 --- a/src/libsemigroups_pybind11/__init__.py +++ b/src/libsemigroups_pybind11/__init__.py @@ -53,7 +53,22 @@ from .knuth_bendix import KnuthBendix from .konieczny import Konieczny from .matrix import Matrix, MatrixKind -from .order import wr_cmp +from .order import ( + LenLexCmp, + LenWtLexCmp, + LexCmp, + RevLenLexCmp, + RevLenWtLexCmp, + RevLexCmp, + RevRPOCmp, + RevWrCmp, + RevWtLenLexCmp, + RevWtLexCmp, + RPOCmp, + WrCmp, + WtLenLexCmp, + WtLexCmp, +) from .presentation import InversePresentation, Presentation from .schreier_sims import SchreierSims from .sims import MinimalRepOrc, RepOrc, Sims1, Sims2, SimsRefinerFaithful, SimsRefinerIdeals @@ -127,6 +142,7 @@ shortlex_compare, side, tril, + wr_cmp as _wr_cmp, wt_lenlex_cmp as _wt_lenlex_cmp, wt_lex_cmp as _wt_lex_cmp, ) @@ -148,6 +164,7 @@ rev_wt_lenlex_cmp = _wrap_cxx_free_fn(_rev_wt_lenlex_cmp) rev_wt_lex_cmp = _wrap_cxx_free_fn(_rev_wt_lex_cmp) rpo_cmp = _wrap_cxx_free_fn(_rpo_cmp) +wr_cmp = _wrap_cxx_free_fn(_wr_cmp) wt_lenlex_cmp = _wrap_cxx_free_fn(_wt_lenlex_cmp) wt_lex_cmp = _wrap_cxx_free_fn(_wt_lex_cmp) @@ -168,6 +185,20 @@ "Joiner", "LibsemigroupsError", "LimitMax", + "LenLexCmp", + "LenWtLexCmp", + "LexCmp", + "RPOCmp", + "RevLenLexCmp", + "RevLenWtLexCmp", + "RevLexCmp", + "RevRPOCmp", + "RevWrCmp", + "RevWtLenLexCmp", + "RevWtLexCmp", + "WrCmp", + "WtLenLexCmp", + "WtLexCmp", "Meeter", "NegativeInfinity", "Order", diff --git a/src/libsemigroups_pybind11/du_narendran_rusinowitch.py b/src/libsemigroups_pybind11/du_narendran_rusinowitch.py index 7fd7e3f5..1916de01 100644 --- a/src/libsemigroups_pybind11/du_narendran_rusinowitch.py +++ b/src/libsemigroups_pybind11/du_narendran_rusinowitch.py @@ -6,6 +6,7 @@ """Subpackage for importing and wrapping :any:`du_narendran_rusinowitch`.""" +# pylint: disable=no-name-in-module from _libsemigroups_pybind11 import du_narendran_rusinowitch as _du_narendran_rusinowitch from .detail.cxx_wrapper import wrap_cxx_free_fn as _wrap_cxx_free_fn diff --git a/src/libsemigroups_pybind11/order.py b/src/libsemigroups_pybind11/order.py index 3fc4074f..dc3fccf4 100644 --- a/src/libsemigroups_pybind11/order.py +++ b/src/libsemigroups_pybind11/order.py @@ -4,12 +4,784 @@ # # The full license is in the file LICENSE, distributed with this software. -"""Word-ordering comparison functions.""" +"""Python wrappers for word-ordering objects and comparison functions.""" -from _libsemigroups_pybind11 import wr_cmp as _wr_cmp +from typing_extensions import Self as _Self -from .detail.cxx_wrapper import wrap_cxx_free_fn as _wrap_cxx_free_fn +from _libsemigroups_pybind11 import ( + LenLexCmpDefault as _LenLexCmpDefault, + LenLexCmpString as _LenLexCmpString, + LenLexCmpWord as _LenLexCmpWord, + LenWtLexCmpDefault as _LenWtLexCmpDefault, + LenWtLexCmpString as _LenWtLexCmpString, + LenWtLexCmpWord as _LenWtLexCmpWord, + LexCmpDefault as _LexCmpDefault, + LexCmpString as _LexCmpString, + LexCmpWord as _LexCmpWord, + RevLenLexCmpDefault as _RevLenLexCmpDefault, + RevLenLexCmpString as _RevLenLexCmpString, + RevLenLexCmpWord as _RevLenLexCmpWord, + RevLenWtLexCmpDefault as _RevLenWtLexCmpDefault, + RevLenWtLexCmpString as _RevLenWtLexCmpString, + RevLenWtLexCmpWord as _RevLenWtLexCmpWord, + RevLexCmpDefault as _RevLexCmpDefault, + RevLexCmpString as _RevLexCmpString, + RevLexCmpWord as _RevLexCmpWord, + RevRPOCmpDefault as _RevRPOCmpDefault, + RevRPOCmpString as _RevRPOCmpString, + RevRPOCmpWord as _RevRPOCmpWord, + RevWrCmpDefault as _RevWrCmpDefault, + RevWrCmpString as _RevWrCmpString, + RevWrCmpWord as _RevWrCmpWord, + RevWtLenLexCmpDefault as _RevWtLenLexCmpDefault, + RevWtLenLexCmpString as _RevWtLenLexCmpString, + RevWtLenLexCmpWord as _RevWtLenLexCmpWord, + RevWtLexCmpDefault as _RevWtLexCmpDefault, + RevWtLexCmpString as _RevWtLexCmpString, + RevWtLexCmpWord as _RevWtLexCmpWord, + RPOCmpDefault as _RPOCmpDefault, + RPOCmpString as _RPOCmpString, + RPOCmpWord as _RPOCmpWord, + WrCmpDefault as _WrCmpDefault, + WrCmpString as _WrCmpString, + WrCmpWord as _WrCmpWord, + WtLenLexCmpDefault as _WtLenLexCmpDefault, + WtLenLexCmpString as _WtLenLexCmpString, + WtLenLexCmpWord as _WtLenLexCmpWord, + WtLexCmpDefault as _WtLexCmpDefault, + WtLexCmpString as _WtLexCmpString, + WtLexCmpWord as _WtLexCmpWord, +) -wr_cmp = _wrap_cxx_free_fn(_wr_cmp) +from .alphabet import Alphabet as _Alphabet +from .detail.cxx_wrapper import ( + CxxWrapper as _CxxWrapper, + copy_cxx_mem_fns as _copy_cxx_mem_fns, + register_cxx_wrapped_type as _register_cxx_wrapped_type, + to_cxx as _to_cxx, +) +from .detail.decorators import copydoc as _copydoc -__all__ = ["wr_cmp"] + +class _ConfiguredCmp(_CxxWrapper): + _configuration_name: str + + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, optional_kwargs=("alphabet", self._configuration_name), **kwargs) + if _to_cxx(self) is not None: + return + + if len(args) > 2: + raise TypeError(f"expected at most 2 positional arguments, found {len(args)}") + if args and kwargs: + raise TypeError("expected positional or keyword arguments, but found both") + + if kwargs: + alphabet = kwargs.get("alphabet") + configuration = kwargs.get(self._configuration_name) + has_alphabet = "alphabet" in kwargs + has_configuration = self._configuration_name in kwargs + else: + alphabet = args[0] if len(args) == 2 else None + configuration = args[-1] if args else None + has_alphabet = len(args) == 2 + has_configuration = bool(args) + + if not has_alphabet and not has_configuration: + self.py_template_params = () + self.init_cxx_obj() + elif not has_alphabet and isinstance(configuration, list): + self.py_template_params = () + self.init_cxx_obj(configuration) + elif ( + has_alphabet + and has_configuration + and isinstance(alphabet, _Alphabet) + and isinstance(configuration, list) + ): + self.py_template_params = alphabet.py_template_params + self.init_cxx_obj(alphabet, configuration) + else: + raise TypeError( + f"expected either {self._configuration_name}: list[int], or alphabet: " + f"Alphabet and {self._configuration_name}: list[int]" + ) + + +class LexCmp(_CxxWrapper): + __doc__ = _LexCmpString.__doc__ + + _py_template_params_to_cxx_type = { + (): _LexCmpDefault, + (str,): _LexCmpString, + (list[int],): _LexCmpWord, + } + + _cxx_type_to_py_template_params = dict( + zip( + _py_template_params_to_cxx_type.values(), + _py_template_params_to_cxx_type.keys(), + strict=True, + ) + ) + + _all_wrapped_cxx_types = {*_py_template_params_to_cxx_type.values()} + + @_copydoc(_LexCmpString.__init__, _LexCmpDefault.__init__) + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, optional_kwargs=("alphabet",), **kwargs) + if _to_cxx(self) is not None: + return + + if len(args) > 1: + raise TypeError(f"expected at most 1 positional argument, found {len(args)}") + + if len(args) == 1 and kwargs: + raise TypeError( + 'expected either 1 positional argument or the keyword argument "alphabet", ' + "but found both" + ) + + if len(args) == 1: + alphabet = args[0] + else: + alphabet = kwargs.get("alphabet") + + if len(args) == 0 and "alphabet" not in kwargs: + self.py_template_params = () + self.init_cxx_obj() + elif isinstance(alphabet, _Alphabet): + self.py_template_params = alphabet.py_template_params + self.init_cxx_obj(alphabet) + else: + raise TypeError(f"expected the argument to be an Alphabet, but found {type(alphabet)}") + + @_copydoc(_LexCmpString.__call__) + def __call__(self: _Self, x: str | list[int], y: str | list[int]) -> bool: + return super().__call__(x, y) + + +class RevLexCmp(_CxxWrapper): + __doc__ = _RevLexCmpString.__doc__ + + _py_template_params_to_cxx_type = { + (): _RevLexCmpDefault, + (str,): _RevLexCmpString, + (list[int],): _RevLexCmpWord, + } + + _cxx_type_to_py_template_params = dict( + zip( + _py_template_params_to_cxx_type.values(), + _py_template_params_to_cxx_type.keys(), + strict=True, + ) + ) + + _all_wrapped_cxx_types = {*_py_template_params_to_cxx_type.values()} + + @_copydoc(_RevLexCmpString.__init__, _RevLexCmpDefault.__init__) + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, optional_kwargs=("alphabet",), **kwargs) + if _to_cxx(self) is not None: + return + + if len(args) > 1: + raise TypeError(f"expected at most 1 positional argument, found {len(args)}") + + if len(args) == 1 and kwargs: + raise TypeError( + 'expected either 1 positional argument or the keyword argument "alphabet", ' + "but found both" + ) + + if len(args) == 1: + alphabet = args[0] + else: + alphabet = kwargs.get("alphabet") + + if len(args) == 0 and "alphabet" not in kwargs: + self.py_template_params = () + self.init_cxx_obj() + elif isinstance(alphabet, _Alphabet): + self.py_template_params = alphabet.py_template_params + self.init_cxx_obj(alphabet) + else: + raise TypeError(f"expected the argument to be an Alphabet, but found {type(alphabet)}") + + @_copydoc(_RevLexCmpString.__call__) + def __call__(self: _Self, x: str | list[int], y: str | list[int]) -> bool: + return super().__call__(x, y) + + +class LenLexCmp(_CxxWrapper): + __doc__ = _LenLexCmpString.__doc__ + + _py_template_params_to_cxx_type = { + (): _LenLexCmpDefault, + (str,): _LenLexCmpString, + (list[int],): _LenLexCmpWord, + } + + _cxx_type_to_py_template_params = dict( + zip( + _py_template_params_to_cxx_type.values(), + _py_template_params_to_cxx_type.keys(), + strict=True, + ) + ) + + _all_wrapped_cxx_types = {*_py_template_params_to_cxx_type.values()} + + @_copydoc(_LenLexCmpString.__init__, _LenLexCmpDefault.__init__) + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, optional_kwargs=("alphabet",), **kwargs) + if _to_cxx(self) is not None: + return + + if len(args) > 1: + raise TypeError(f"expected at most 1 positional argument, found {len(args)}") + + if len(args) == 1 and kwargs: + raise TypeError( + 'expected either 1 positional argument or the keyword argument "alphabet", ' + "but found both" + ) + + if len(args) == 1: + alphabet = args[0] + else: + alphabet = kwargs.get("alphabet") + + if len(args) == 0 and "alphabet" not in kwargs: + self.py_template_params = () + self.init_cxx_obj() + elif isinstance(alphabet, _Alphabet): + self.py_template_params = alphabet.py_template_params + self.init_cxx_obj(alphabet) + else: + raise TypeError(f"expected the argument to be an Alphabet, but found {type(alphabet)}") + + @_copydoc(_LenLexCmpString.__call__) + def __call__(self: _Self, x: str | list[int], y: str | list[int]) -> bool: + return super().__call__(x, y) + + +class RevLenLexCmp(_CxxWrapper): + __doc__ = _RevLenLexCmpString.__doc__ + + _py_template_params_to_cxx_type = { + (): _RevLenLexCmpDefault, + (str,): _RevLenLexCmpString, + (list[int],): _RevLenLexCmpWord, + } + + _cxx_type_to_py_template_params = dict( + zip( + _py_template_params_to_cxx_type.values(), + _py_template_params_to_cxx_type.keys(), + strict=True, + ) + ) + + _all_wrapped_cxx_types = {*_py_template_params_to_cxx_type.values()} + + @_copydoc(_RevLenLexCmpString.__init__, _RevLenLexCmpDefault.__init__) + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, optional_kwargs=("alphabet",), **kwargs) + if _to_cxx(self) is not None: + return + + if len(args) > 1: + raise TypeError(f"expected at most 1 positional argument, found {len(args)}") + + if len(args) == 1 and kwargs: + raise TypeError( + 'expected either 1 positional argument or the keyword argument "alphabet", ' + "but found both" + ) + + if len(args) == 1: + alphabet = args[0] + else: + alphabet = kwargs.get("alphabet") + + if len(args) == 0 and "alphabet" not in kwargs: + self.py_template_params = () + self.init_cxx_obj() + elif isinstance(alphabet, _Alphabet): + self.py_template_params = alphabet.py_template_params + self.init_cxx_obj(alphabet) + else: + raise TypeError(f"expected the argument to be an Alphabet, but found {type(alphabet)}") + + @_copydoc(_RevLenLexCmpString.__call__) + def __call__(self: _Self, x: str | list[int], y: str | list[int]) -> bool: + return super().__call__(x, y) + + +class RPOCmp(_CxxWrapper): + __doc__ = _RPOCmpString.__doc__ + + _py_template_params_to_cxx_type = { + (): _RPOCmpDefault, + (str,): _RPOCmpString, + (list[int],): _RPOCmpWord, + } + + _cxx_type_to_py_template_params = dict( + zip( + _py_template_params_to_cxx_type.values(), + _py_template_params_to_cxx_type.keys(), + strict=True, + ) + ) + + _all_wrapped_cxx_types = {*_py_template_params_to_cxx_type.values()} + + @_copydoc(_RPOCmpString.__init__, _RPOCmpDefault.__init__) + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, optional_kwargs=("alphabet",), **kwargs) + if _to_cxx(self) is not None: + return + + if len(args) > 1: + raise TypeError(f"expected at most 1 positional argument, found {len(args)}") + + if len(args) == 1 and kwargs: + raise TypeError( + 'expected either 1 positional argument or the keyword argument "alphabet", ' + "but found both" + ) + + if len(args) == 1: + alphabet = args[0] + else: + alphabet = kwargs.get("alphabet") + + if len(args) == 0 and "alphabet" not in kwargs: + self.py_template_params = () + self.init_cxx_obj() + elif isinstance(alphabet, _Alphabet): + self.py_template_params = alphabet.py_template_params + self.init_cxx_obj(alphabet) + else: + raise TypeError(f"expected the argument to be an Alphabet, but found {type(alphabet)}") + + @_copydoc(_RPOCmpString.__call__) + def __call__(self: _Self, x: str | list[int], y: str | list[int]) -> bool: + return super().__call__(x, y) + + +class RevRPOCmp(_CxxWrapper): + __doc__ = _RevRPOCmpString.__doc__ + + _py_template_params_to_cxx_type = { + (): _RevRPOCmpDefault, + (str,): _RevRPOCmpString, + (list[int],): _RevRPOCmpWord, + } + + _cxx_type_to_py_template_params = dict( + zip( + _py_template_params_to_cxx_type.values(), + _py_template_params_to_cxx_type.keys(), + strict=True, + ) + ) + + _all_wrapped_cxx_types = {*_py_template_params_to_cxx_type.values()} + + @_copydoc(_RevRPOCmpString.__init__, _RevRPOCmpDefault.__init__) + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, optional_kwargs=("alphabet",), **kwargs) + if _to_cxx(self) is not None: + return + + if len(args) > 1: + raise TypeError(f"expected at most 1 positional argument, found {len(args)}") + + if len(args) == 1 and kwargs: + raise TypeError( + 'expected either 1 positional argument or the keyword argument "alphabet", ' + "but found both" + ) + + if len(args) == 1: + alphabet = args[0] + else: + alphabet = kwargs.get("alphabet") + + if len(args) == 0 and "alphabet" not in kwargs: + self.py_template_params = () + self.init_cxx_obj() + elif isinstance(alphabet, _Alphabet): + self.py_template_params = alphabet.py_template_params + self.init_cxx_obj(alphabet) + else: + raise TypeError(f"expected the argument to be an Alphabet, but found {type(alphabet)}") + + @_copydoc(_RevRPOCmpString.__call__) + def __call__(self: _Self, x: str | list[int], y: str | list[int]) -> bool: + return super().__call__(x, y) + + +class WrCmp(_ConfiguredCmp): + __doc__ = _WrCmpString.__doc__ + _configuration_name = "levels" + + _py_template_params_to_cxx_type = { + (): _WrCmpDefault, + (str,): _WrCmpString, + (list[int],): _WrCmpWord, + } + _cxx_type_to_py_template_params = dict( + zip( + _py_template_params_to_cxx_type.values(), + _py_template_params_to_cxx_type.keys(), + strict=True, + ) + ) + _all_wrapped_cxx_types = {*_py_template_params_to_cxx_type.values()} + + @_copydoc(_WrCmpString.__init__, _WrCmpDefault.__init__) + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + @_copydoc(_WrCmpString.__call__) + def __call__(self: _Self, x: str | list[int], y: str | list[int]) -> bool: + return super().__call__(x, y) + + @_copydoc(_WrCmpDefault.init, _WrCmpString.init) + def init(self: _Self, *args) -> _Self: + # pylint: disable=missing-function-docstring + cxx_self = _to_cxx(self) + result = cxx_self.init(*(_to_cxx(arg) for arg in args)) + return self if result is cxx_self else result + + +class RevWrCmp(_ConfiguredCmp): + __doc__ = _RevWrCmpString.__doc__ + _configuration_name = "levels" + + _py_template_params_to_cxx_type = { + (): _RevWrCmpDefault, + (str,): _RevWrCmpString, + (list[int],): _RevWrCmpWord, + } + _cxx_type_to_py_template_params = dict( + zip( + _py_template_params_to_cxx_type.values(), + _py_template_params_to_cxx_type.keys(), + strict=True, + ) + ) + _all_wrapped_cxx_types = {*_py_template_params_to_cxx_type.values()} + + @_copydoc(_RevWrCmpString.__init__, _RevWrCmpDefault.__init__) + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + @_copydoc(_RevWrCmpString.__call__) + def __call__(self: _Self, x: str | list[int], y: str | list[int]) -> bool: + return super().__call__(x, y) + + @_copydoc(_RevWrCmpDefault.init, _RevWrCmpString.init) + def init(self: _Self, *args) -> _Self: + # pylint: disable=missing-function-docstring + cxx_self = _to_cxx(self) + result = cxx_self.init(*(_to_cxx(arg) for arg in args)) + return self if result is cxx_self else result + + +class WtLenLexCmp(_ConfiguredCmp): + __doc__ = _WtLenLexCmpString.__doc__ + _configuration_name = "weights" + + _py_template_params_to_cxx_type = { + (): _WtLenLexCmpDefault, + (str,): _WtLenLexCmpString, + (list[int],): _WtLenLexCmpWord, + } + _cxx_type_to_py_template_params = dict( + zip( + _py_template_params_to_cxx_type.values(), + _py_template_params_to_cxx_type.keys(), + strict=True, + ) + ) + _all_wrapped_cxx_types = {*_py_template_params_to_cxx_type.values()} + + @_copydoc(_WtLenLexCmpString.__init__, _WtLenLexCmpDefault.__init__) + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + @_copydoc(_WtLenLexCmpString.__call__) + def __call__(self: _Self, x: str | list[int], y: str | list[int]) -> bool: + return super().__call__(x, y) + + @_copydoc(_WtLenLexCmpDefault.init, _WtLenLexCmpString.init) + def init(self: _Self, *args) -> _Self: + # pylint: disable=missing-function-docstring + cxx_self = _to_cxx(self) + result = cxx_self.init(*(_to_cxx(arg) for arg in args)) + return self if result is cxx_self else result + + +class RevWtLenLexCmp(_ConfiguredCmp): + __doc__ = _RevWtLenLexCmpString.__doc__ + _configuration_name = "weights" + + _py_template_params_to_cxx_type = { + (): _RevWtLenLexCmpDefault, + (str,): _RevWtLenLexCmpString, + (list[int],): _RevWtLenLexCmpWord, + } + _cxx_type_to_py_template_params = dict( + zip( + _py_template_params_to_cxx_type.values(), + _py_template_params_to_cxx_type.keys(), + strict=True, + ) + ) + _all_wrapped_cxx_types = {*_py_template_params_to_cxx_type.values()} + + @_copydoc(_RevWtLenLexCmpString.__init__, _RevWtLenLexCmpDefault.__init__) + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + @_copydoc(_RevWtLenLexCmpString.__call__) + def __call__(self: _Self, x: str | list[int], y: str | list[int]) -> bool: + return super().__call__(x, y) + + @_copydoc(_RevWtLenLexCmpDefault.init, _RevWtLenLexCmpString.init) + def init(self: _Self, *args) -> _Self: + # pylint: disable=missing-function-docstring + cxx_self = _to_cxx(self) + result = cxx_self.init(*(_to_cxx(arg) for arg in args)) + return self if result is cxx_self else result + + +class WtLexCmp(_ConfiguredCmp): + __doc__ = _WtLexCmpString.__doc__ + _configuration_name = "weights" + + _py_template_params_to_cxx_type = { + (): _WtLexCmpDefault, + (str,): _WtLexCmpString, + (list[int],): _WtLexCmpWord, + } + _cxx_type_to_py_template_params = dict( + zip( + _py_template_params_to_cxx_type.values(), + _py_template_params_to_cxx_type.keys(), + strict=True, + ) + ) + _all_wrapped_cxx_types = {*_py_template_params_to_cxx_type.values()} + + @_copydoc(_WtLexCmpString.__init__, _WtLexCmpDefault.__init__) + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + @_copydoc(_WtLexCmpString.__call__) + def __call__(self: _Self, x: str | list[int], y: str | list[int]) -> bool: + return super().__call__(x, y) + + @_copydoc(_WtLexCmpDefault.init, _WtLexCmpString.init) + def init(self: _Self, *args) -> _Self: + # pylint: disable=missing-function-docstring + cxx_self = _to_cxx(self) + result = cxx_self.init(*(_to_cxx(arg) for arg in args)) + return self if result is cxx_self else result + + +class RevWtLexCmp(_ConfiguredCmp): + __doc__ = _RevWtLexCmpString.__doc__ + _configuration_name = "weights" + + _py_template_params_to_cxx_type = { + (): _RevWtLexCmpDefault, + (str,): _RevWtLexCmpString, + (list[int],): _RevWtLexCmpWord, + } + _cxx_type_to_py_template_params = dict( + zip( + _py_template_params_to_cxx_type.values(), + _py_template_params_to_cxx_type.keys(), + strict=True, + ) + ) + _all_wrapped_cxx_types = {*_py_template_params_to_cxx_type.values()} + + @_copydoc(_RevWtLexCmpString.__init__, _RevWtLexCmpDefault.__init__) + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + @_copydoc(_RevWtLexCmpString.__call__) + def __call__(self: _Self, x: str | list[int], y: str | list[int]) -> bool: + return super().__call__(x, y) + + @_copydoc(_RevWtLexCmpDefault.init, _RevWtLexCmpString.init) + def init(self: _Self, *args) -> _Self: + # pylint: disable=missing-function-docstring + cxx_self = _to_cxx(self) + result = cxx_self.init(*(_to_cxx(arg) for arg in args)) + return self if result is cxx_self else result + + +class LenWtLexCmp(_ConfiguredCmp): + __doc__ = _LenWtLexCmpString.__doc__ + _configuration_name = "weights" + + _py_template_params_to_cxx_type = { + (): _LenWtLexCmpDefault, + (str,): _LenWtLexCmpString, + (list[int],): _LenWtLexCmpWord, + } + _cxx_type_to_py_template_params = dict( + zip( + _py_template_params_to_cxx_type.values(), + _py_template_params_to_cxx_type.keys(), + strict=True, + ) + ) + _all_wrapped_cxx_types = {*_py_template_params_to_cxx_type.values()} + + @_copydoc(_LenWtLexCmpString.__init__, _LenWtLexCmpDefault.__init__) + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + @_copydoc(_LenWtLexCmpString.__call__) + def __call__(self: _Self, x: str | list[int], y: str | list[int]) -> bool: + return super().__call__(x, y) + + @_copydoc(_LenWtLexCmpDefault.init, _LenWtLexCmpString.init) + def init(self: _Self, *args) -> _Self: + # pylint: disable=missing-function-docstring + cxx_self = _to_cxx(self) + result = cxx_self.init(*(_to_cxx(arg) for arg in args)) + return self if result is cxx_self else result + + +class RevLenWtLexCmp(_ConfiguredCmp): + __doc__ = _RevLenWtLexCmpString.__doc__ + _configuration_name = "weights" + + _py_template_params_to_cxx_type = { + (): _RevLenWtLexCmpDefault, + (str,): _RevLenWtLexCmpString, + (list[int],): _RevLenWtLexCmpWord, + } + _cxx_type_to_py_template_params = dict( + zip( + _py_template_params_to_cxx_type.values(), + _py_template_params_to_cxx_type.keys(), + strict=True, + ) + ) + _all_wrapped_cxx_types = {*_py_template_params_to_cxx_type.values()} + + @_copydoc(_RevLenWtLexCmpString.__init__, _RevLenWtLexCmpDefault.__init__) + def __init__(self: _Self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + @_copydoc(_RevLenWtLexCmpString.__call__) + def __call__(self: _Self, x: str | list[int], y: str | list[int]) -> bool: + return super().__call__(x, y) + + @_copydoc(_RevLenWtLexCmpDefault.init, _RevLenWtLexCmpString.init) + def init(self: _Self, *args) -> _Self: + # pylint: disable=missing-function-docstring + cxx_self = _to_cxx(self) + result = cxx_self.init(*(_to_cxx(arg) for arg in args)) + return self if result is cxx_self else result + + +_copy_cxx_mem_fns(_LenLexCmpString, LenLexCmp) +_register_cxx_wrapped_type(_LenLexCmpDefault, LenLexCmp) +_register_cxx_wrapped_type(_LenLexCmpString, LenLexCmp) +_register_cxx_wrapped_type(_LenLexCmpWord, LenLexCmp) + +_copy_cxx_mem_fns(_RevLenLexCmpString, RevLenLexCmp) +_register_cxx_wrapped_type(_RevLenLexCmpDefault, RevLenLexCmp) +_register_cxx_wrapped_type(_RevLenLexCmpString, RevLenLexCmp) +_register_cxx_wrapped_type(_RevLenLexCmpWord, RevLenLexCmp) + +_copy_cxx_mem_fns(_LexCmpString, LexCmp) +_register_cxx_wrapped_type(_LexCmpDefault, LexCmp) +_register_cxx_wrapped_type(_LexCmpString, LexCmp) +_register_cxx_wrapped_type(_LexCmpWord, LexCmp) + +_copy_cxx_mem_fns(_RevLexCmpString, RevLexCmp) +_register_cxx_wrapped_type(_RevLexCmpDefault, RevLexCmp) +_register_cxx_wrapped_type(_RevLexCmpString, RevLexCmp) +_register_cxx_wrapped_type(_RevLexCmpWord, RevLexCmp) + +_copy_cxx_mem_fns(_RPOCmpString, RPOCmp) +_register_cxx_wrapped_type(_RPOCmpDefault, RPOCmp) +_register_cxx_wrapped_type(_RPOCmpString, RPOCmp) +_register_cxx_wrapped_type(_RPOCmpWord, RPOCmp) + +_copy_cxx_mem_fns(_RevRPOCmpString, RevRPOCmp) +_register_cxx_wrapped_type(_RevRPOCmpDefault, RevRPOCmp) +_register_cxx_wrapped_type(_RevRPOCmpString, RevRPOCmp) +_register_cxx_wrapped_type(_RevRPOCmpWord, RevRPOCmp) + +_copy_cxx_mem_fns(_WrCmpString, WrCmp) +_register_cxx_wrapped_type(_WrCmpDefault, WrCmp) +_register_cxx_wrapped_type(_WrCmpString, WrCmp) +_register_cxx_wrapped_type(_WrCmpWord, WrCmp) + +_copy_cxx_mem_fns(_RevWrCmpString, RevWrCmp) +_register_cxx_wrapped_type(_RevWrCmpDefault, RevWrCmp) +_register_cxx_wrapped_type(_RevWrCmpString, RevWrCmp) +_register_cxx_wrapped_type(_RevWrCmpWord, RevWrCmp) + +_copy_cxx_mem_fns(_WtLenLexCmpString, WtLenLexCmp) +_register_cxx_wrapped_type(_WtLenLexCmpDefault, WtLenLexCmp) +_register_cxx_wrapped_type(_WtLenLexCmpString, WtLenLexCmp) +_register_cxx_wrapped_type(_WtLenLexCmpWord, WtLenLexCmp) + +_copy_cxx_mem_fns(_RevWtLenLexCmpString, RevWtLenLexCmp) +_register_cxx_wrapped_type(_RevWtLenLexCmpDefault, RevWtLenLexCmp) +_register_cxx_wrapped_type(_RevWtLenLexCmpString, RevWtLenLexCmp) +_register_cxx_wrapped_type(_RevWtLenLexCmpWord, RevWtLenLexCmp) + +_copy_cxx_mem_fns(_WtLexCmpString, WtLexCmp) +_register_cxx_wrapped_type(_WtLexCmpDefault, WtLexCmp) +_register_cxx_wrapped_type(_WtLexCmpString, WtLexCmp) +_register_cxx_wrapped_type(_WtLexCmpWord, WtLexCmp) + +_copy_cxx_mem_fns(_RevWtLexCmpString, RevWtLexCmp) +_register_cxx_wrapped_type(_RevWtLexCmpDefault, RevWtLexCmp) +_register_cxx_wrapped_type(_RevWtLexCmpString, RevWtLexCmp) +_register_cxx_wrapped_type(_RevWtLexCmpWord, RevWtLexCmp) + +_copy_cxx_mem_fns(_LenWtLexCmpString, LenWtLexCmp) +_register_cxx_wrapped_type(_LenWtLexCmpDefault, LenWtLexCmp) +_register_cxx_wrapped_type(_LenWtLexCmpString, LenWtLexCmp) +_register_cxx_wrapped_type(_LenWtLexCmpWord, LenWtLexCmp) + +_copy_cxx_mem_fns(_RevLenWtLexCmpString, RevLenWtLexCmp) +_register_cxx_wrapped_type(_RevLenWtLexCmpDefault, RevLenWtLexCmp) +_register_cxx_wrapped_type(_RevLenWtLexCmpString, RevLenWtLexCmp) +_register_cxx_wrapped_type(_RevLenWtLexCmpWord, RevLenWtLexCmp) + + +__all__ = [ + "LenLexCmp", + "LenWtLexCmp", + "LexCmp", + "RPOCmp", + "RevLenLexCmp", + "RevLenWtLexCmp", + "RevLexCmp", + "RevRPOCmp", + "RevWrCmp", + "RevWtLenLexCmp", + "RevWtLexCmp", + "WrCmp", + "WtLenLexCmp", + "WtLexCmp", +] diff --git a/src/order.cpp b/src/order.cpp index 54c6b99c..e928e1a4 100644 --- a/src/order.cpp +++ b/src/order.cpp @@ -142,6 +142,8 @@ Words are first ordered by length and then lexicographically. :returns: Whether *x* is less than *y*. :rtype: bool +.. seealso:: :any:`LenLexCmp` for a reusable lenlex comparison object. + .. doctest:: python >>> from libsemigroups_pybind11 import lenlex_cmp @@ -180,6 +182,7 @@ compared by their positions in *alphabet*. belong to *alphabet*. .. doctest:: python + >>> from libsemigroups_pybind11 import Alphabet, lenlex_cmp >>> alphabet = Alphabet("ba") >>> lenlex_cmp(alphabet, "b", "a") @@ -863,6 +866,8 @@ Compare two words using recursive-path ordering. This function has significantly worse performance than :any:`lenlex_cmp` and :any:`lex_cmp`. +.. seealso:: :any:`RPOCmp` for a reusable recursive-path comparison object. + .. doctest:: python >>> from libsemigroups_pybind11 import rpo_cmp @@ -934,6 +939,9 @@ left. This function has significantly worse performance than :any:`lenlex_cmp` and :any:`lex_cmp`. +.. seealso:: + :any:`RevRPOCmp` for a reusable reversed recursive-path comparison object. + .. doctest:: python >>> from libsemigroups_pybind11 import rev_rpo_cmp @@ -974,6 +982,9 @@ left, with letters compared by their positions in *alphabet*. This function has significantly worse performance than :any:`lenlex_cmp` and :any:`lex_cmp`. +.. seealso:: + :any:`RevRPOCmp` for a reusable reversed recursive-path comparison object. + .. doctest:: python >>> from libsemigroups_pybind11 import Alphabet, rev_rpo_cmp @@ -1230,87 +1241,1841 @@ differences within one level are compared using lenlex ordering. True )pbdoc"); } - } // namespace - void init_order(py::module& m) { - py::options options; - options.disable_enum_members_docstring(); + //////////////////////////////////////////////////////////////////////// + // Functions for binding the various Cmp structs such as WtLenLexCmp, + // RPOCmp, and so on + //////////////////////////////////////////////////////////////////////// - py::enum_(m, "Order", R"pbdoc( -An enum class for the possible orderings of words and strings. + // The remaining documentation is included in the corresponding + // alphabet-aware binding function below. + template + void bind_cmp_default(py::module& m, std::string name) { + std::string arg_type(name); + name += "CmpDefault"; + arg_type += "Cmp"; + py::class_ thing(m, name.c_str()); -The values in this enum can be used as the arguments for functions such as -:any:`ToddCoxeter.standardize` or :any:`WordRange.order` to specify which -ordering should be used. + thing.def(py::init<>(), + fmt::format(R"pbdoc( +:sig=(self: {0}) -> None: +Construct a comparison object. -The values :any:`Order.shortlex` and :any:`Order.recursive` are retained for -backwards compatibility; use :any:`Order.lenlex` and :any:`Order.rev_rpo`, -respectively, in new code. +Constructs an object whose call operator compares either ``str`` or +``list[int]`` words using the natural order of their letters. -.. py:attribute:: Order.none - :value: +.. doctest:: python - No ordering + >>> from libsemigroups_pybind11 import {0} + >>> {0}()("a", "b") + True +)pbdoc", + arg_type) + .c_str()); -.. py:attribute:: Order.lenlex - :value: + thing.def("__repr__", + [](Cmp const& self) { return to_human_readable_repr(self); }); - The lenlex ordering. Words are first ordered by length, and then - lexicographically. + thing.def("__copy__", [](Cmp const& self) { return Cmp(self); }); -.. py:attribute:: Order.shortlex - :value: + thing.def("copy", [](Cmp const& self) { return Cmp(self); }); - The short-lex ordering. Words are first ordered by length, and then - lexicographically. + thing.def( + "__call__", + [](Cmp const& self, std::string const& x, std::string const& y) { + return self(x, y); + }, + py::arg("x"), + py::arg("y")); - This is deprecated; use :any:`Order.lenlex` instead. + thing.def( + "__call__", + [](Cmp const& self, word_type const& x, word_type const& y) { + return self(x, y); + }, + py::arg("x"), + py::arg("y")); + } -.. py:attribute:: Order.lex - :value: + template + void bind_configured_cmp_default( + py::module& m, + std::string const& name, + std::string const& configuration, + std::vector const& (Cmp::*get_configuration)() const) { + std::string const binding_name = name + "Default"; + py::class_ thing(m, binding_name.c_str()); - The lexicographic ordering. Note that this is not a well-order, so there - may not be a lexicographically least word in a given congruence class of - words. + thing.def(py::init<>(), + fmt::format(R"pbdoc( +:sig=(self: {0}) -> None: +Construct a comparison object with an empty {1} vector. -.. py:attribute:: Order.rpo - :value: +.. doctest:: python - The recursive-path ordering, as described in :cite:`Jantzen2012aa` - (Definition 1.2.14, page 24). + >>> from libsemigroups_pybind11 import {0} + >>> {0}().{1}() + [] +)pbdoc", + name, + configuration) + .c_str()); + thing.def(py::init const&>(), + py::arg(configuration.c_str()), + fmt::format(R"pbdoc( +:sig=(self: {0}, {1}: list[int]) -> None: +Construct a comparison object for ``str`` or ``list[int]`` words. + +:param {1}: the {1} of the generators. +:type {1}: list[int] -.. py:attribute:: Order.rev_rpo - :value: +.. doctest:: python - The reversed recursive-path ordering, based on the description in - :cite:`Jantzen2012aa` (Definition 1.2.14, page 24), where words are read - right-to-left before ordering. + >>> from libsemigroups_pybind11 import {0} + >>> {0}([1, 2]).{1}() + [1, 2] +)pbdoc", + name, + configuration) + .c_str()); + thing.def("__repr__", + [](Cmp const& self) { return to_human_readable_repr(self); }); + thing.def("__copy__", [](Cmp const& self) { return Cmp(self); }); + thing.def("copy", [](Cmp const& self) { return Cmp(self); }); + thing.def( + "init", + [](Cmp& self, std::vector const& values) -> Cmp& { + return self.init(values); + }, + py::arg(configuration.c_str()), + fmt::format(R"pbdoc( +:sig=(self: {0}, {1}: list[int]) -> {0}: +Reinitialize a {0} object from a {1} list. -.. py:attribute:: Order.recursive - :value: +:param {1}: the {1} of the generators. +:type {1}: list[int] - The recursive-path ordering, as described in :cite:`Jantzen2012aa` - (Definition 1.2.14, page 24). +:returns: The first argument *self*. +:rtype: {0} - This is deprecated; use :any:`Order.rpo` instead. +.. warning:: + This overload only works if *self* was constructed without an alphabet, as + ``{0}()`` or ``{0}({1})``. An object constructed as + ``{0}(alphabet, {1})`` must be reinitialized with + ``init(alphabet, {1})``. .. doctest:: python - >>> from libsemigroups_pybind11 import Order - >>> Order.lenlex - - >>> Order.shortlex == Order.lenlex + >>> from libsemigroups_pybind11 import {0} + >>> compare = {0}() + >>> compare.init([2, 1]) is compare True -)pbdoc") - .value("none", Order::none) - .value("lenlex", Order::lenlex) - .value("shortlex", Order::lenlex) - .value("lex", Order::lex) - .value("rpo", Order::rpo) - .value("rev_rpo", Order::rev_rpo) - .value("recursive", Order::rev_rpo); + >>> compare.{1}() + [2, 1] +)pbdoc", + name, + configuration) + .c_str()); + thing.def(configuration.c_str(), [get_configuration](Cmp const& self) { + return (self.*get_configuration)(); + }); + thing.def( + "__call__", + [](Cmp const& self, std::string const& x, std::string const& y) { + return self(x, y); + }, + py::arg("x"), + py::arg("y")); + thing.def( + "__call__", + [](Cmp const& self, word_type const& x, word_type const& y) { + return self(x, y); + }, + py::arg("x"), + py::arg("y")); + } - bind_order_comparisons(m); - bind_order_comparisons(m); + template + void bind_configured_cmp_with_alphabet( + py::module& m, + char const* binding_name, + std::string const& name, + std::string const& configuration, + std::string const& ordering, + std::vector const& (Cmp::*get_configuration)() const) { + std::string const non_alphabet_string_doctest = fmt::format(R"pbdoc( + >>> {0}([1, 2])("\x00", "\x01") + True +)pbdoc", + name); + py::class_ thing(m, + binding_name, + fmt::format(R"pbdoc( +Compare words using {2}. + +Use ``{0}({1})`` to compare either ``str`` or ``list[int]`` words. In this +form, every letter is interpreted as an index into *{1}*. Use +``{0}(alphabet, {1})`` to compare words whose letters belong to *alphabet*. +The latter form copies both arguments and only accepts words with the same +type as *alphabet*. + +.. note:: + The constructor fixes whether this object is alphabet-aware and fixes the + word type of an alphabet-aware object. Reinitialization cannot change either. + +.. seealso:: + + :any:`Alphabet` + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, {0} + >>> {0}([1, 2])([0], [1]) + True + >>> {0}(Alphabet("ab"), [1, 2])("a", "b") + True + >>> {0}(Alphabet([0, 1]), [1, 2])([0], [1]) + True +{3} +)pbdoc", + name, + configuration, + ordering, + non_alphabet_string_doctest) + .c_str()); + + thing.def(py::init const&, std::vector const&>(), + py::arg("alphabet"), + py::arg(configuration.c_str()), + fmt::format(R"pbdoc( +:sig=(self: {0}, alphabet: Alphabet, {1}: list[int]) -> None: +Construct a comparison object from an alphabet and {1}. + +The two arguments must have the same size. + +:param alphabet: the alphabet defining the letters and their order. +:type alphabet: Alphabet +:param {1}: the {1} of the letters in *alphabet*. +:type {1}: list[int] + +:raises LibsemigroupsError: + if *alphabet* and *{1}* have different sizes. +:raises TypeError: + if the arguments do not have the required types. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, {0} + >>> {0}(Alphabet("ab"), [1, 2])("a", "b") + True +)pbdoc", + name, + configuration) + .c_str()); + thing.def("__repr__", + [](Cmp const& self) { return to_human_readable_repr(self); }); + thing.def("__copy__", [](Cmp const& self) { return Cmp(self); }); + thing.def( + "copy", + [](Cmp const& self) { return Cmp(self); }, + fmt::format(R"pbdoc( +:sig=(self: {0}) -> {0}: +Copy a comparison object. + +:returns: An independent copy of *self*. +:rtype: {0} + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, {0} + >>> {0}(Alphabet("ab"), [1, 2]).copy()("a", "b") + True +)pbdoc", + name) + .c_str()); + thing.def( + "init", + [](Cmp& self, + Alphabet const& alphabet, + std::vector const& values) -> Cmp& { + return self.init(alphabet, values); + }, + py::arg("alphabet"), + py::arg(configuration.c_str()), + fmt::format(R"pbdoc( +:sig=(self: {0}, alphabet: Alphabet, {1}: list[int]) -> {0}: +Reinitialize an alphabet-aware comparison object. + +The alphabet must have the original word type (i.e. ``list[int]`` or ``str``), +and the alphabet and {1} list must have the same size. + +:param alphabet: the alphabet defining the letters and their order. +:type alphabet: Alphabet +:param {1}: the {1} of the letters in *alphabet*. +:type {1}: list[int] + +:returns: The first argument *self*. +:rtype: {0} + +:raises LibsemigroupsError: + if *alphabet* and *{1}* have different sizes. +:raises TypeError: + if the arguments do not have the required types. + +.. warning:: + This overload only works if *self* was constructed as + ``{0}(alphabet, {1})``. An object constructed as ``{0}()`` or + ``{0}({1})`` must be reinitialized with ``init({1})``. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, {0} + >>> compare = {0}(Alphabet("ab"), [1, 2]) + >>> compare.init(Alphabet("ba"), [1, 2]) is compare + True +)pbdoc", + name, + configuration) + .c_str()); + + thing.def( + "__call__", + [](Cmp const& self, Word const& x, Word const& y) { + return self(x, y); + }, + py::arg("x"), + py::arg("y"), + fmt::format(R"pbdoc( +:sig=(self: {0}, x: str | list[int], y: str | list[int]) -> bool: +Compare two words using {2}. + +Non-alphabet-aware objects accept either ``str`` or ``list[int]`` words, and +every letter is interpreted as an index into the stored *{1}*. Alphabet-aware +objects accept words of the same type as their alphabet. + +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] + +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises TypeError: + if *x* and *y* do not both have the supported word type, or if their type + does not match the alphabet used to construct an alphabet-aware object. +:raises LibsemigroupsError: + if a non-alphabet-aware word contains a letter that is not a valid index into + the stored *{1}*, or an alphabet-aware word contains a letter outside its + alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, {0} + >>> {0}([1, 2])([0], [1]) + True + >>> {0}(Alphabet("ab"), [1, 2])("a", "b") + True +{3} +)pbdoc", + name, + configuration, + ordering, + non_alphabet_string_doctest) + .c_str()); + thing.def("alphabet", + &Cmp::alphabet, + fmt::format(R"pbdoc( +:sig=(self: {0}) -> Alphabet: +Return the stored alphabet. + +:returns: The stored alphabet. +:rtype: Alphabet + +:raises AttributeError: + if *self* was constructed without an alphabet. + +.. warning:: + This method only works if *self* was constructed as + ``{0}(alphabet, {1})``. An object constructed as ``{0}()`` or + ``{0}({1})`` does not have a stored alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, {0} + >>> alphabet = Alphabet("ab") + >>> {0}(alphabet, [1, 2]).alphabet() == alphabet + True +)pbdoc", + name, + configuration) + .c_str(), + py::return_value_policy::reference_internal); + thing.def( + configuration.c_str(), + [get_configuration](Cmp const& self) { + return (self.*get_configuration)(); + }, + fmt::format(R"pbdoc( +:sig=(self: {0}) -> list[int]: +Return the stored {1}. + +:returns: The stored {1}. +:rtype: list[int] + +.. doctest:: python + + >>> from libsemigroups_pybind11 import {0} + >>> {0}([1, 2]).{1}() + [1, 2] +)pbdoc", + name, + configuration) + .c_str()); + } + + template + void bind_lex_cmp_with_alphabet(py::module& m, char const* name) { + using LexCmp_ = LexCmp; + + // The Python wrapper copies all documentation from this + // specialization, except those given above. + py::class_ thing(m, name, R"pbdoc( +Compare words lexicographically. + +Use ``LexCmp()`` to compare either ``str`` or ``list[int]`` words using the +natural order of their letters. Use ``LexCmp(alphabet)`` to compare words by +the positions of their letters in *alphabet*. The latter form copies +*alphabet* and only accepts words with the same type as *alphabet*. + +.. note:: + The constructor fixes whether this object is alphabet-aware (i.e. constructed + from an :any:`Alphabet` object). It also fixes the word type of an + alphabet-aware object. In particular, ``LexCmp()`` cannot be changed into an + alphabet-aware object by calling ``init(alphabet)``. Similarly, + ``LexCmp(alphabet).init(new_alphabet)`` requires *new_alphabet* to have the + same word type as *alphabet*. + +.. seealso:: + + :any:`Alphabet` + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, LexCmp + >>> LexCmp()("a", "b") + True + >>> LexCmp()([0], [1]) + True + >>> LexCmp(Alphabet("ba"))("b", "a") + True +)pbdoc"); + + thing.def(py::init const&>(), + py::arg("alphabet"), + R"pbdoc( +:sig=(self: LexCmp, alphabet: Alphabet) -> None: +Construct a lexicographic comparison object from an alphabet. + +Constructs an object whose call operator compares words by their positions in +*alphabet*. The type of letters in *alphabet* also fixes the accepted word type +for the call operator. + +:param alphabet: the optional alphabet defining the order of letters. +:type alphabet: Alphabet + +:raises TypeError: + if more than one argument is given or the argument is not an :any:`Alphabet`. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, LexCmp + >>> LexCmp(Alphabet("ba"))("b", "a") + True +)pbdoc"); + + thing.def("__repr__", [](LexCmp_ const& self) { + return to_human_readable_repr(self); + }); + + thing.def("__copy__", [](LexCmp_ const& self) { return LexCmp_(self); }); + + thing.def( + "copy", + [](LexCmp_ const& self) { return LexCmp_(self); }, + R"pbdoc( +:sig=(self: LexCmp) -> LexCmp: +Copy a comparison object. + +The copy has the same word type as *self*; and for an alphabet-aware +object, the stored alphabet is also copied, so subsequently reinitializing one +comparison object does not affect the other. + +:returns: A copy of *self*. +:rtype: LexCmp + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, LexCmp + >>> LexCmp(Alphabet("ba")).copy()("b", "a") + True +)pbdoc"); + + thing.def( + "init", + [](LexCmp_& self, Alphabet const& alphabet) -> LexCmp_& { + return self.init(alphabet); + }, + py::arg("alphabet"), + R"pbdoc( +:sig=(self: LexCmp, alphabet: Alphabet) -> LexCmp: +Reinitialize the comparison object. + +If *self* was constructed using an :any:`Alphabet`, then ``init(new_alphabet)`` +puts *self* back into the same state it would have been had it been newly +constructed from *alphabet*. + +:param alphabet: the replacement alphabet. +:type alphabet: Alphabet + +:returns: The first argument *self*. +:rtype: LexCmp + +:raises TypeError: + if the type of the words in the new alphabet is not the same as the existing alphabet. +:raises AttributeError: + if *self* was constructed without an alphabet. + +.. warning:: + This method only works if *self* was constructed as + ``LexCmp(alphabet)``. An object constructed as ``LexCmp()`` does not have a + stored alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, LexCmp + >>> compare = LexCmp(Alphabet("ab")) + >>> compare.init(Alphabet("ba")) is compare + True + >>> compare("b", "a") + True +)pbdoc"); + + thing.def( + "__call__", + [](LexCmp_ const& self, Word const& x, Word const& y) { + return self(x, y); + }, + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(self: LexCmp, x: str | list[int], y: str | list[int]) -> bool: +Compare two words lexicographically. + +If *self* was constructed as ``LexCmp()``, then *x* and *y* must either both +be strings or both be lists of integers, and letters are compared using their +natural order. If *self* was constructed using an :any:`Alphabet`, then *x* and +*y* must have the same type of words as *alphabet*, and letters are compared by +their positions in the alphabet. + +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises TypeError: + if *x* and *y* are not both strings or both lists of integers, or if their + type does not match the alphabet used to construct *self*. +:raises LibsemigroupsError: + if *self* is alphabet-aware and either word contains a letter that does not + belong to its alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import LexCmp + >>> LexCmp()("ab", "ba") + True + >>> LexCmp()([0, 1], [1, 0]) + True +)pbdoc"); + + thing.def("alphabet", + &LexCmp_::alphabet, + R"pbdoc( +:sig=(self: LexCmp) -> Alphabet: +Return the alphabet used to compare letters. + +:returns: The stored alphabet. +:rtype: Alphabet + +:raises AttributeError: + if *self* was constructed without an alphabet. + +.. warning:: + This method only works if *self* was constructed as + ``LexCmp(alphabet)``. An object constructed as ``LexCmp()`` does not have a + stored alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, LexCmp + >>> alphabet = Alphabet("ba") + >>> LexCmp(alphabet).alphabet() == alphabet + True +)pbdoc", + py::return_value_policy::reference_internal); + } // bind_lex_cmp_with_alphabet + + template + void bind_lenlex_cmp_with_alphabet(py::module& m, char const* name) { + using LenLexCmp_ = LenLexCmp; + + // The Python wrapper copies all documentation from this + // specialization, except those given above. + py::class_ thing(m, name, R"pbdoc( +Compare words using lenlex ordering. + +Words are first compared by length and then lexicographically. Use +``LenLexCmp()`` to compare either ``str`` or ``list[int]`` words using the +natural order of their letters. Use ``LenLexCmp(alphabet)`` to compare words +of equal length by the positions of their letters in *alphabet*. The latter +form copies *alphabet* and only accepts words with the same type as *alphabet*. + +.. note:: + The constructor fixes whether this object is alphabet-aware (i.e. constructed + from an :any:`Alphabet` object). It also fixes the word type of an + alphabet-aware object. In particular, ``LenLexCmp()`` cannot be changed into + an alphabet-aware object by calling ``init(alphabet)``. Similarly, + ``LenLexCmp(alphabet).init(new_alphabet)`` requires *new_alphabet* to have + the same word type as *alphabet*. + +.. seealso:: + + :any:`Alphabet` + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, LenLexCmp + >>> LenLexCmp()("a", "b") + True + >>> LenLexCmp()([0], [1]) + True + >>> LenLexCmp(Alphabet("ba"))("b", "a") + True +)pbdoc"); + + thing.def(py::init const&>(), + py::arg("alphabet"), + R"pbdoc( +:sig=(self: LenLexCmp, alphabet: Alphabet) -> None: +Construct a lenlex comparison object from an alphabet. + +Constructs an object whose call operator first compares words by length and +then compares words of equal length by the positions of their letters in +*alphabet*. The type of letters in *alphabet* also fixes the accepted word +type for the call operator. + +:param alphabet: the optional alphabet defining the order of letters. +:type alphabet: Alphabet + +:raises TypeError: + if more than one argument is given or the argument is not an :any:`Alphabet`. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, LenLexCmp + >>> LenLexCmp(Alphabet("ba"))("b", "a") + True +)pbdoc"); + + thing.def("__repr__", [](LenLexCmp_ const& self) { + return to_human_readable_repr(self); + }); + + thing.def("__copy__", + [](LenLexCmp_ const& self) { return LenLexCmp_(self); }); + + thing.def( + "copy", + [](LenLexCmp_ const& self) { return LenLexCmp_(self); }, + R"pbdoc( +:sig=(self: LenLexCmp) -> LenLexCmp: +Copy a comparison object. + +The copy has the same word type as *self*; and for an alphabet-aware +object, the stored alphabet is also copied, so subsequently reinitializing one +comparison object does not affect the other. + +:returns: A copy of *self*. +:rtype: LenLexCmp + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, LenLexCmp + >>> LenLexCmp(Alphabet("ba")).copy()("b", "a") + True +)pbdoc"); + + thing.def( + "init", + [](LenLexCmp_& self, Alphabet const& alphabet) -> LenLexCmp_& { + return self.init(alphabet); + }, + py::arg("alphabet"), + R"pbdoc( +:sig=(self: LenLexCmp, alphabet: Alphabet) -> LenLexCmp: +Reinitialize the comparison object. + +If *self* was constructed using an :any:`Alphabet`, then +``init(new_alphabet)`` puts *self* back into the same state it would have been +had it been newly constructed from *alphabet*. + +:param alphabet: the replacement alphabet. +:type alphabet: Alphabet + +:returns: The first argument *self*. +:rtype: LenLexCmp + +:raises TypeError: + if the type of the words in the new alphabet is not the same as the existing + alphabet. +:raises AttributeError: + if *self* was constructed without an alphabet. + +.. warning:: + This method only works if *self* was constructed as + ``LenLexCmp(alphabet)``. An object constructed as ``LenLexCmp()`` does not have a + stored alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, LenLexCmp + >>> compare = LenLexCmp(Alphabet("ab")) + >>> compare.init(Alphabet("ba")) is compare + True + >>> compare("b", "a") + True +)pbdoc"); + + thing.def( + "__call__", + [](LenLexCmp_ const& self, Word const& x, Word const& y) { + return self(x, y); + }, + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(self: LenLexCmp, x: str | list[int], y: str | list[int]) -> bool: +Compare two words using lenlex ordering. + +Words are first compared by length and then lexicographically. If *self* was +constructed as ``LenLexCmp()``, then *x* and *y* must either both be strings or +both be lists of integers, and letters are compared using their natural order. +If *self* was constructed using an :any:`Alphabet`, then *x* and *y* must have +the same type of words as *alphabet*, and letters are compared by their +positions in the alphabet. + +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises TypeError: + if *x* and *y* are not both strings or both lists of integers, or if their + type does not match the alphabet used to construct *self*. +:raises LibsemigroupsError: + if *self* is alphabet-aware and either word contains a letter that does not + belong to its alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import LenLexCmp + >>> LenLexCmp()("z", "aa") + True + >>> LenLexCmp()([9], [0, 0]) + True +)pbdoc"); + + thing.def("alphabet", + &LenLexCmp_::alphabet, + R"pbdoc( +:sig=(self: LenLexCmp) -> Alphabet: +Return the alphabet used to compare letters. + +:returns: The stored alphabet. +:rtype: Alphabet + +:raises AttributeError: + if *self* was constructed without an alphabet. + +.. warning:: + This method only works if *self* was constructed as + ``LenLexCmp(alphabet)``. An object constructed as ``LenLexCmp()`` does not + have a stored alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, LenLexCmp + >>> alphabet = Alphabet("ba") + >>> LenLexCmp(alphabet).alphabet() == alphabet + True +)pbdoc", + py::return_value_policy::reference_internal); + } // bind_lenlex_cmp_with_alphabet + + template + void bind_rpo_cmp_with_alphabet(py::module& m, char const* name) { + using RPOCmp_ = RPOCmp; + + // The Python wrapper copies all documentation from this specialization, + // except those given above. + py::class_ thing(m, name, R"pbdoc( +Compare words using recursive-path ordering. + +Use ``RPOCmp()`` to compare either ``str`` or ``list[int]`` words using the +natural order of their letters. Use ``RPOCmp(alphabet)`` to compare words by +the positions of their letters in *alphabet*. The latter form copies +*alphabet* and only accepts words with the same type as *alphabet*. + +.. note:: + The constructor fixes whether this object is alphabet-aware (i.e. constructed + from an :any:`Alphabet` object). It also fixes the word type of an + alphabet-aware object. In particular, ``RPOCmp()`` cannot be changed into an + alphabet-aware object by calling ``init(alphabet)``. Similarly, + ``RPOCmp(alphabet).init(new_alphabet)`` requires *new_alphabet* to have the + same word type as *alphabet*. + +.. warning:: + This comparison has significantly worse performance than :any:`LenLexCmp` + and :any:`LexCmp`. + +.. seealso:: + + :any:`Alphabet` + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RPOCmp + >>> RPOCmp()("a", "b") + True + >>> RPOCmp()([0], [1]) + True + >>> RPOCmp(Alphabet("ba"))("b", "a") + True +)pbdoc"); + + thing.def(py::init const&>(), + py::arg("alphabet"), + R"pbdoc( +:sig=(self: RPOCmp, alphabet: Alphabet) -> None: +Construct a recursive-path comparison object from an alphabet. + +Constructs an object whose call operator compares words using recursive-path +ordering and orders letters by their positions in *alphabet*. The type of +letters in *alphabet* also fixes the accepted word type for the call operator. + +:param alphabet: the optional alphabet defining the order of letters. +:type alphabet: Alphabet + +:raises TypeError: + if more than one argument is given or the argument is not an :any:`Alphabet`. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RPOCmp + >>> RPOCmp(Alphabet("ba"))("b", "a") + True +)pbdoc"); + + thing.def("__repr__", [](RPOCmp_ const& self) { + return to_human_readable_repr(self); + }); + + thing.def("__copy__", [](RPOCmp_ const& self) { return RPOCmp_(self); }); + + thing.def( + "copy", + [](RPOCmp_ const& self) { return RPOCmp_(self); }, + R"pbdoc( +:sig=(self: RPOCmp) -> RPOCmp: +Copy a comparison object. + +The copy has the same word type as *self*; and for an alphabet-aware +object, the stored alphabet is also copied, so subsequently reinitializing one +comparison object does not affect the other. + +:returns: A copy of *self*. +:rtype: RPOCmp + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RPOCmp + >>> RPOCmp(Alphabet("ba")).copy()("b", "a") + True +)pbdoc"); + + thing.def( + "init", + [](RPOCmp_& self, Alphabet const& alphabet) -> RPOCmp_& { + return self.init(alphabet); + }, + py::arg("alphabet"), + R"pbdoc( +:sig=(self: RPOCmp, alphabet: Alphabet) -> RPOCmp: +Reinitialize the comparison object. + +If *self* was constructed using an :any:`Alphabet`, then +``init(new_alphabet)`` puts *self* back into the same state it would have been +had it been newly constructed from *alphabet*. + +:param alphabet: the replacement alphabet. +:type alphabet: Alphabet + +:returns: The first argument *self*. +:rtype: RPOCmp + +:raises TypeError: + if the type of the words in the new alphabet is not the same as the existing + alphabet. +:raises AttributeError: + if *self* was constructed without an alphabet. + +.. warning:: + This method only works if *self* was constructed as + ``RPOCmp(alphabet)``. An object constructed as ``RPOCmp()`` does not have a + stored alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RPOCmp + >>> compare = RPOCmp(Alphabet("ab")) + >>> compare.init(Alphabet("ba")) is compare + True + >>> compare("b", "a") + True +)pbdoc"); + + thing.def( + "__call__", + [](RPOCmp_ const& self, Word const& x, Word const& y) { + return self(x, y); + }, + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(self: RPOCmp, x: str | list[int], y: str | list[int]) -> bool: +Compare two words using recursive-path ordering. + +If *self* was constructed as ``RPOCmp()``, then *x* and *y* must either both +be strings or both be lists of integers, and letters are compared using their +natural order. If *self* was constructed using an :any:`Alphabet`, then *x* and +*y* must have the same type of words as *alphabet*, and letters are compared by +their positions in the alphabet. + +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises TypeError: + if *x* and *y* are not both strings or both lists of integers, or if their + type does not match the alphabet used to construct *self*. +:raises LibsemigroupsError: + if *self* is alphabet-aware and either word contains a letter that does not + belong to its alphabet. + +.. warning:: + This comparison has significantly worse performance than :any:`LenLexCmp` + and :any:`LexCmp`. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import RPOCmp + >>> RPOCmp()("a", "b") + True + >>> RPOCmp()([0], [1]) + True +)pbdoc"); + + thing.def("alphabet", + &RPOCmp_::alphabet, + R"pbdoc( +:sig=(self: RPOCmp) -> Alphabet: +Return the alphabet used to compare letters. + +:returns: The stored alphabet. +:rtype: Alphabet + +:raises AttributeError: + if *self* was constructed without an alphabet. + +.. warning:: + This method only works if *self* was constructed as + ``RPOCmp(alphabet)``. An object constructed as ``RPOCmp()`` does not have a + stored alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RPOCmp + >>> alphabet = Alphabet("ba") + >>> RPOCmp(alphabet).alphabet() == alphabet + True +)pbdoc", + py::return_value_policy::reference_internal); + } // bind_rpo_cmp_with_alphabet + + template + void bind_rev_rpo_cmp_with_alphabet(py::module& m, char const* name) { + using RevRPOCmp_ = RevRPOCmp; + + // The Python wrapper copies all documentation from this specialization, + // except those given above. + py::class_ thing(m, name, R"pbdoc( +Compare words using reversed recursive-path ordering. + +This is recursive-path ordering applied after reading both words from right to +left. Use ``RevRPOCmp()`` to compare either ``str`` or ``list[int]`` words +using the natural order of their letters. Use ``RevRPOCmp(alphabet)`` to +compare words by the positions of their letters in *alphabet*. The latter form +copies *alphabet* and only accepts words with the same type as *alphabet*. + +.. note:: + The constructor fixes whether this object is alphabet-aware (i.e. constructed + from an :any:`Alphabet` object). It also fixes the word type of an + alphabet-aware object. In particular, ``RevRPOCmp()`` cannot be changed into + an alphabet-aware object by calling ``init(alphabet)``. Similarly, + ``RevRPOCmp(alphabet).init(new_alphabet)`` requires *new_alphabet* to have + the same word type as *alphabet*. + +.. warning:: + This comparison has significantly worse performance than :any:`LenLexCmp` + and :any:`LexCmp`. + +.. seealso:: + + :any:`Alphabet` + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevRPOCmp + >>> RevRPOCmp()("ab", "ba") + True + >>> RevRPOCmp()([0, 1], [1, 0]) + True + >>> RevRPOCmp(Alphabet("ba"))("b", "a") + True +)pbdoc"); + + thing.def(py::init const&>(), + py::arg("alphabet"), + R"pbdoc( +:sig=(self: RevRPOCmp, alphabet: Alphabet) -> None: +Construct a reversed recursive-path comparison object from an alphabet. + +Constructs an object whose call operator compares words using reversed +recursive-path ordering and orders letters by their positions in *alphabet*. +The type of letters in *alphabet* also fixes the accepted word type for the +call operator. + +:param alphabet: the optional alphabet defining the order of letters. +:type alphabet: Alphabet + +:raises TypeError: + if more than one argument is given or the argument is not an :any:`Alphabet`. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevRPOCmp + >>> RevRPOCmp(Alphabet("ba"))("b", "a") + True +)pbdoc"); + + thing.def("__repr__", [](RevRPOCmp_ const& self) { + return to_human_readable_repr(self); + }); + + thing.def("__copy__", + [](RevRPOCmp_ const& self) { return RevRPOCmp_(self); }); + + thing.def( + "copy", + [](RevRPOCmp_ const& self) { return RevRPOCmp_(self); }, + R"pbdoc( +:sig=(self: RevRPOCmp) -> RevRPOCmp: +Copy a comparison object. + +The copy has the same word type as *self*; and for an alphabet-aware +object, the stored alphabet is also copied, so subsequently reinitializing one +comparison object does not affect the other. + +:returns: A copy of *self*. +:rtype: RevRPOCmp + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevRPOCmp + >>> RevRPOCmp(Alphabet("ba")).copy()("b", "a") + True +)pbdoc"); + + thing.def( + "init", + [](RevRPOCmp_& self, Alphabet const& alphabet) -> RevRPOCmp_& { + return self.init(alphabet); + }, + py::arg("alphabet"), + R"pbdoc( +:sig=(self: RevRPOCmp, alphabet: Alphabet) -> RevRPOCmp: +Reinitialize the comparison object. + +If *self* was constructed using an :any:`Alphabet`, then +``init(new_alphabet)`` puts *self* back into the same state it would have been +had it been newly constructed from *alphabet*. + +:param alphabet: the replacement alphabet. +:type alphabet: Alphabet + +:returns: The first argument *self*. +:rtype: RevRPOCmp + +:raises TypeError: + if the type of the words in the new alphabet is not the same as the existing + alphabet. +:raises AttributeError: + if *self* was constructed without an alphabet. + +.. warning:: + This method only works if *self* was constructed as + ``RevRPOCmp(alphabet)``. An object constructed as ``RevRPOCmp()`` does not + have a stored alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevRPOCmp + >>> compare = RevRPOCmp(Alphabet("ab")) + >>> compare.init(Alphabet("ba")) is compare + True + >>> compare("b", "a") + True +)pbdoc"); + + thing.def( + "__call__", + [](RevRPOCmp_ const& self, Word const& x, Word const& y) { + return self(x, y); + }, + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(self: RevRPOCmp, x: str | list[int], y: str | list[int]) -> bool: +Compare two words using reversed recursive-path ordering. + +This is recursive-path ordering applied after reading both words from right to +left. If *self* was constructed as ``RevRPOCmp()``, then *x* and *y* must +either both be strings or both be lists of integers, and letters are compared +using their natural order. If *self* was constructed using an :any:`Alphabet`, +then *x* and *y* must have the same type of words as *alphabet*, and letters are +compared by their positions in the alphabet. + +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises TypeError: + if *x* and *y* are not both strings or both lists of integers, or if their + type does not match the alphabet used to construct *self*. +:raises LibsemigroupsError: + if *self* is alphabet-aware and either word contains a letter that does not + belong to its alphabet. + +.. warning:: + This comparison has significantly worse performance than :any:`LenLexCmp` + and :any:`LexCmp`. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import RevRPOCmp + >>> RevRPOCmp()("ab", "ba") + True + >>> RevRPOCmp()([0, 1], [1, 0]) + True +)pbdoc"); + + thing.def("alphabet", + &RevRPOCmp_::alphabet, + R"pbdoc( +:sig=(self: RevRPOCmp) -> Alphabet: +Return the alphabet used to compare letters. + +:returns: The stored alphabet. +:rtype: Alphabet + +:raises AttributeError: + if *self* was constructed without an alphabet. + +.. warning:: + This method only works if *self* was constructed as + ``RevRPOCmp(alphabet)``. An object constructed as ``RevRPOCmp()`` does not + have a stored alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevRPOCmp + >>> alphabet = Alphabet("ba") + >>> RevRPOCmp(alphabet).alphabet() == alphabet + True +)pbdoc", + py::return_value_policy::reference_internal); + } // bind_rev_rpo_cmp_with_alphabet + + template + void bind_rev_lex_cmp_with_alphabet(py::module& m, char const* name) { + using RevLexCmp_ = RevLexCmp; + + // The Python wrapper copies all documentation from this specialization, + // except those given above. + py::class_ thing(m, name, R"pbdoc( +Compare words using reversed lexicographic ordering. + +This is lexicographic ordering applied after reading both words from right to +left. + +Use ``RevLexCmp()`` to compare either ``str`` or ``list[int]`` words using the +natural order of their letters. Use ``RevLexCmp(alphabet)`` to compare words by +the positions of their letters in *alphabet*. The latter form copies +*alphabet* and only accepts words with the same type as *alphabet*. + +.. note:: + The constructor fixes whether this object is alphabet-aware (i.e. constructed + from an :any:`Alphabet` object). It also fixes the word type of an + alphabet-aware object. In particular, ``RevLexCmp()`` cannot be changed into + an alphabet-aware object by calling ``init(alphabet)``. Similarly, + ``RevLexCmp(alphabet).init(new_alphabet)`` requires *new_alphabet* to have the + same word type as *alphabet*. + +.. seealso:: + + :any:`Alphabet` + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevLexCmp + >>> RevLexCmp()("a", "b") + True + >>> RevLexCmp()([0], [1]) + True + >>> RevLexCmp(Alphabet("ba"))("b", "a") + True +)pbdoc"); + + thing.def(py::init const&>(), + py::arg("alphabet"), + R"pbdoc( +:sig=(self: RevLexCmp, alphabet: Alphabet) -> None: +Construct a reversed lexicographic comparison object from an alphabet. + +Constructs an object whose call operator compares words by their positions in +*alphabet*. The type of letters in *alphabet* also fixes the accepted word type +for the call operator. + +:param alphabet: the optional alphabet defining the order of letters. +:type alphabet: Alphabet + +:raises TypeError: + if more than one argument is given or the argument is not an :any:`Alphabet`. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevLexCmp + >>> RevLexCmp(Alphabet("ba"))("b", "a") + True +)pbdoc"); + + thing.def("__repr__", [](RevLexCmp_ const& self) { + return to_human_readable_repr(self); + }); + + thing.def("__copy__", + [](RevLexCmp_ const& self) { return RevLexCmp_(self); }); + + thing.def( + "copy", + [](RevLexCmp_ const& self) { return RevLexCmp_(self); }, + R"pbdoc( +:sig=(self: RevLexCmp) -> RevLexCmp: +Copy a comparison object. + +The copy has the same word type as *self*; and for an alphabet-aware +object, the stored alphabet is also copied, so subsequently reinitializing one +comparison object does not affect the other. + +:returns: A copy of *self*. +:rtype: RevLexCmp + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevLexCmp + >>> RevLexCmp(Alphabet("ba")).copy()("b", "a") + True +)pbdoc"); + + thing.def( + "init", + [](RevLexCmp_& self, Alphabet const& alphabet) -> RevLexCmp_& { + return self.init(alphabet); + }, + py::arg("alphabet"), + R"pbdoc( +:sig=(self: RevLexCmp, alphabet: Alphabet) -> RevLexCmp: +Reinitialize the comparison object. + +If *self* was constructed using an :any:`Alphabet`, then ``init(new_alphabet)`` +puts *self* back into the same state it would have been had it been newly +constructed from *alphabet*. + +:param alphabet: the replacement alphabet. +:type alphabet: Alphabet + +:returns: The first argument *self*. +:rtype: RevLexCmp + +:raises TypeError: + if the type of the words in the new alphabet is not the same as the existing alphabet. +:raises AttributeError: + if *self* was constructed without an alphabet. + +.. warning:: + This method only works if *self* was constructed as + ``RevLexCmp(alphabet)``. An object constructed as ``RevLexCmp()`` does not have a + stored alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevLexCmp + >>> compare = RevLexCmp(Alphabet("ab")) + >>> compare.init(Alphabet("ba")) is compare + True + >>> compare("b", "a") + True +)pbdoc"); + + thing.def( + "__call__", + [](RevLexCmp_ const& self, Word const& x, Word const& y) { + return self(x, y); + }, + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(self: RevLexCmp, x: str | list[int], y: str | list[int]) -> bool: +Compare two words using reversed lexicographic ordering. + +This is lexicographic ordering applied after reading both words from right to +left. + +If *self* was constructed as ``RevLexCmp()``, then *x* and *y* must either both +be strings or both be lists of integers, and letters are compared using their +natural order. If *self* was constructed using an :any:`Alphabet`, then *x* and +*y* must have the same type of words as *alphabet*, and letters are compared by +their positions in the alphabet. + +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises TypeError: + if *x* and *y* are not both strings or both lists of integers, or if their + type does not match the alphabet used to construct *self*. +:raises LibsemigroupsError: + if *self* is alphabet-aware and either word contains a letter that does not + belong to its alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import RevLexCmp + >>> RevLexCmp()("ba", "ab") + True + >>> RevLexCmp()([1, 0], [0, 1]) + True +)pbdoc"); + + thing.def("alphabet", + &RevLexCmp_::alphabet, + R"pbdoc( +:sig=(self: RevLexCmp) -> Alphabet: +Return the alphabet used to compare letters. + +:returns: The stored alphabet. +:rtype: Alphabet + +:raises AttributeError: + if *self* was constructed without an alphabet. + +.. warning:: + This method only works if *self* was constructed as + ``RevLexCmp(alphabet)``. An object constructed as ``RevLexCmp()`` does not have a + stored alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevLexCmp + >>> alphabet = Alphabet("ba") + >>> RevLexCmp(alphabet).alphabet() == alphabet + True +)pbdoc", + py::return_value_policy::reference_internal); + } // bind_rev_lex_cmp_with_alphabet + + template + void bind_rev_lenlex_cmp_with_alphabet(py::module& m, char const* name) { + using RevLenLexCmp_ = RevLenLexCmp; + + // The Python wrapper copies all documentation from this specialization, + // except those given above. + py::class_ thing(m, name, R"pbdoc( +Compare words using reversed lenlex ordering. + +Words are first compared by length and then lexicographically after being read +from right to left. Use ``RevLenLexCmp()`` to compare either ``str`` or +``list[int]`` words using the natural order of their letters. Use +``RevLenLexCmp(alphabet)`` to compare words of equal length by the positions of +their letters in *alphabet*. The latter form copies *alphabet* and only accepts +words with the same type as *alphabet*. + +.. note:: + The constructor fixes whether this object is alphabet-aware (i.e. constructed + from an :any:`Alphabet` object). It also fixes the word type of an + alphabet-aware object. In particular, ``RevLenLexCmp()`` cannot be changed + into an alphabet-aware object by calling ``init(alphabet)``. Similarly, + ``RevLenLexCmp(alphabet).init(new_alphabet)`` requires *new_alphabet* to have + the same word type as *alphabet*. + +.. seealso:: + + :any:`Alphabet` + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevLenLexCmp + >>> RevLenLexCmp()("a", "b") + True + >>> RevLenLexCmp()([0], [1]) + True + >>> RevLenLexCmp(Alphabet("ba"))("b", "a") + True +)pbdoc"); + + thing.def(py::init const&>(), + py::arg("alphabet"), + R"pbdoc( +:sig=(self: RevLenLexCmp, alphabet: Alphabet) -> None: +Construct a reversed lenlex comparison object from an alphabet. + +Constructs an object whose call operator first compares words by length and +then compares words of equal length from right to left by the positions of +their letters in *alphabet*. The type of letters in *alphabet* also fixes the +accepted word type for the call operator. + +:param alphabet: the optional alphabet defining the order of letters. +:type alphabet: Alphabet + +:raises TypeError: + if more than one argument is given or the argument is not an :any:`Alphabet`. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevLenLexCmp + >>> RevLenLexCmp(Alphabet("ba"))("b", "a") + True +)pbdoc"); + + thing.def("__repr__", [](RevLenLexCmp_ const& self) { + return to_human_readable_repr(self); + }); + + thing.def("__copy__", + [](RevLenLexCmp_ const& self) { return RevLenLexCmp_(self); }); + + thing.def( + "copy", + [](RevLenLexCmp_ const& self) { return RevLenLexCmp_(self); }, + R"pbdoc( +:sig=(self: RevLenLexCmp) -> RevLenLexCmp: +Copy a comparison object. + +The copy has the same word type as *self*; and for an alphabet-aware +object, the stored alphabet is also copied, so subsequently reinitializing one +comparison object does not affect the other. + +:returns: A copy of *self*. +:rtype: RevLenLexCmp + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevLenLexCmp + >>> RevLenLexCmp(Alphabet("ba")).copy()("b", "a") + True +)pbdoc"); + + thing.def( + "init", + [](RevLenLexCmp_& self, Alphabet const& alphabet) + -> RevLenLexCmp_& { return self.init(alphabet); }, + py::arg("alphabet"), + R"pbdoc( +:sig=(self: RevLenLexCmp, alphabet: Alphabet) -> RevLenLexCmp: +Reinitialize the comparison object. + +If *self* was constructed using an :any:`Alphabet`, then +``init(new_alphabet)`` puts *self* back into the same state it would have been +had it been newly constructed from *alphabet*. + +:param alphabet: the replacement alphabet. +:type alphabet: Alphabet + +:returns: The first argument *self*. +:rtype: RevLenLexCmp + +:raises TypeError: + if the type of the words in the new alphabet is not the same as the existing + alphabet. +:raises AttributeError: + if *self* was constructed without an alphabet. + +.. warning:: + This method only works if *self* was constructed as + ``RevLenLexCmp(alphabet)``. An object constructed as ``RevLenLexCmp()`` does not have a + stored alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevLenLexCmp + >>> compare = RevLenLexCmp(Alphabet("ab")) + >>> compare.init(Alphabet("ba")) is compare + True + >>> compare("b", "a") + True +)pbdoc"); + + thing.def( + "__call__", + [](RevLenLexCmp_ const& self, Word const& x, Word const& y) { + return self(x, y); + }, + py::arg("x"), + py::arg("y"), + R"pbdoc( +:sig=(self: RevLenLexCmp, x: str | list[int], y: str | list[int]) -> bool: +Compare two words using reversed lenlex ordering. + +Words are first compared by length and then lexicographically after being read +from right to left. If *self* was constructed as ``RevLenLexCmp()``, then *x* +and *y* must either both be strings or both be lists of integers, and letters +are compared using their natural order. If *self* was constructed using an +:any:`Alphabet`, then *x* and *y* must have the same type of words as +*alphabet*, and letters are compared by their positions in the alphabet. + +:param x: the first word. +:type x: str | list[int] +:param y: the second word. +:type y: str | list[int] +:returns: Whether *x* is less than *y*. +:rtype: bool + +:raises TypeError: + if *x* and *y* are not both strings or both lists of integers, or if their + type does not match the alphabet used to construct *self*. +:raises LibsemigroupsError: + if *self* is alphabet-aware and either word contains a letter that does not + belong to its alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import RevLenLexCmp + >>> RevLenLexCmp()("ba", "ab") + True + >>> RevLenLexCmp()([1, 0], [0, 1]) + True +)pbdoc"); + + thing.def("alphabet", + &RevLenLexCmp_::alphabet, + R"pbdoc( +:sig=(self: RevLenLexCmp) -> Alphabet: +Return the alphabet used to compare letters. + +:returns: The stored alphabet. +:rtype: Alphabet + +:raises AttributeError: + if *self* was constructed without an alphabet. + +.. warning:: + This method only works if *self* was constructed as + ``RevLenLexCmp(alphabet)``. An object constructed as ``RevLenLexCmp()`` does not + have a stored alphabet. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Alphabet, RevLenLexCmp + >>> alphabet = Alphabet("ba") + >>> RevLenLexCmp(alphabet).alphabet() == alphabet + True +)pbdoc", + py::return_value_policy::reference_internal); + } // bind_rev_lenlex_cmp_with_alphabet + } // namespace + + void init_order(py::module& m) { + py::options options; + options.disable_enum_members_docstring(); + + py::enum_(m, "Order", R"pbdoc( +An enum class for the possible orderings of lists of integers and strings. + +The values in this enum can be used as the arguments for functions such as +:any:`ToddCoxeter.standardize` or :any:`WordRange.order` to specify which +ordering should be used. + +The values :any:`Order.shortlex` and :any:`Order.recursive` are retained for +backwards compatibility; use :any:`Order.lenlex` and :any:`Order.rev_rpo`, +respectively, in new code. + +.. py:attribute:: Order.none + :value: + + No ordering + +.. py:attribute:: Order.lenlex + :value: + + The lenlex ordering. Words are first ordered by length, and then + lexicographically. + +.. py:attribute:: Order.shortlex + :value: + + The short-lex ordering. Words are first ordered by length, and then + lexicographically. + + This is deprecated; use :any:`Order.lenlex` instead. + +.. py:attribute:: Order.lex + :value: + + The lexicographic ordering. Note that this is not a well-order, so there + may not be a lexicographically least word in a given congruence class of + words. + +.. py:attribute:: Order.rpo + :value: + + The recursive-path ordering, as described in :cite:`Jantzen2012aa` + (Definition 1.2.14, page 24). + +.. py:attribute:: Order.rev_rpo + :value: + + The reversed recursive-path ordering, based on the description in + :cite:`Jantzen2012aa` (Definition 1.2.14, page 24), where words are read + right-to-left before ordering. + +.. py:attribute:: Order.recursive + :value: + + The recursive-path ordering, as described in :cite:`Jantzen2012aa` + (Definition 1.2.14, page 24). + + This is deprecated; use :any:`Order.rpo` instead. + +.. doctest:: python + + >>> from libsemigroups_pybind11 import Order + >>> Order.lenlex + +)pbdoc") + .value("none", Order::none) + .value("lenlex", Order::lenlex) + .value("shortlex", Order::lenlex) + .value("lex", Order::lex) + .value("rpo", Order::rpo) + .value("rev_rpo", Order::rev_rpo) + .value("recursive", Order::rev_rpo); + + bind_order_comparisons(m); + bind_order_comparisons(m); + + bind_cmp_default>(m, "Lex"); + bind_cmp_default>(m, "RevLex"); + bind_cmp_default>(m, "LenLex"); + bind_cmp_default>(m, "RevLenLex"); + bind_cmp_default>(m, "RPO"); + bind_cmp_default>(m, "RevRPO"); + + bind_lex_cmp_with_alphabet(m, "LexCmpString"); + bind_lex_cmp_with_alphabet(m, "LexCmpWord"); + + bind_rev_lex_cmp_with_alphabet(m, "RevLexCmpString"); + bind_rev_lex_cmp_with_alphabet(m, "RevLexCmpWord"); + + bind_lenlex_cmp_with_alphabet(m, "LenLexCmpString"); + bind_lenlex_cmp_with_alphabet(m, "LenLexCmpWord"); + + bind_rev_lenlex_cmp_with_alphabet(m, "RevLenLexCmpString"); + bind_rev_lenlex_cmp_with_alphabet(m, "RevLenLexCmpWord"); + + bind_rpo_cmp_with_alphabet(m, "RPOCmpString"); + bind_rpo_cmp_with_alphabet(m, "RPOCmpWord"); + + bind_rev_rpo_cmp_with_alphabet(m, "RevRPOCmpString"); + bind_rev_rpo_cmp_with_alphabet(m, "RevRPOCmpWord"); + + bind_configured_cmp_default>( + m, "WrCmp", "levels", &WrCmp<>::levels); + bind_configured_cmp_with_alphabet, std::string>( + m, + "WrCmpString", + "WrCmp", + "levels", + "wreath-product ordering", + &WrCmp::levels); + bind_configured_cmp_with_alphabet, word_type>( + m, + "WrCmpWord", + "WrCmp", + "levels", + "wreath-product ordering", + &WrCmp::levels); + + bind_configured_cmp_default>( + m, "RevWrCmp", "levels", &RevWrCmp<>::levels); + bind_configured_cmp_with_alphabet, std::string>( + m, + "RevWrCmpString", + "RevWrCmp", + "levels", + "reversed wreath-product ordering", + &RevWrCmp::levels); + bind_configured_cmp_with_alphabet, word_type>( + m, + "RevWrCmpWord", + "RevWrCmp", + "levels", + "reversed wreath-product ordering", + &RevWrCmp::levels); + + bind_configured_cmp_default>( + m, "WtLenLexCmp", "weights", &WtLenLexCmp<>::weights); + bind_configured_cmp_with_alphabet, std::string>( + m, + "WtLenLexCmpString", + "WtLenLexCmp", + "weights", + "weighted lenlex ordering", + &WtLenLexCmp::weights); + bind_configured_cmp_with_alphabet, word_type>( + m, + "WtLenLexCmpWord", + "WtLenLexCmp", + "weights", + "weighted lenlex ordering", + &WtLenLexCmp::weights); + + bind_configured_cmp_default>( + m, "RevWtLenLexCmp", "weights", &RevWtLenLexCmp<>::weights); + bind_configured_cmp_with_alphabet, std::string>( + m, + "RevWtLenLexCmpString", + "RevWtLenLexCmp", + "weights", + "reversed weighted lenlex ordering", + &RevWtLenLexCmp::weights); + bind_configured_cmp_with_alphabet, word_type>( + m, + "RevWtLenLexCmpWord", + "RevWtLenLexCmp", + "weights", + "reversed weighted lenlex ordering", + &RevWtLenLexCmp::weights); + + bind_configured_cmp_default>( + m, "WtLexCmp", "weights", &WtLexCmp<>::weights); + bind_configured_cmp_with_alphabet, std::string>( + m, + "WtLexCmpString", + "WtLexCmp", + "weights", + "weighted lexicographic ordering", + &WtLexCmp::weights); + bind_configured_cmp_with_alphabet, word_type>( + m, + "WtLexCmpWord", + "WtLexCmp", + "weights", + "weighted lexicographic ordering", + &WtLexCmp::weights); + + bind_configured_cmp_default>( + m, "RevWtLexCmp", "weights", &RevWtLexCmp<>::weights); + bind_configured_cmp_with_alphabet, std::string>( + m, + "RevWtLexCmpString", + "RevWtLexCmp", + "weights", + "reversed weighted lexicographic ordering", + &RevWtLexCmp::weights); + bind_configured_cmp_with_alphabet, word_type>( + m, + "RevWtLexCmpWord", + "RevWtLexCmp", + "weights", + "reversed weighted lexicographic ordering", + &RevWtLexCmp::weights); + + bind_configured_cmp_default>( + m, "LenWtLexCmp", "weights", &LenWtLexCmp<>::weights); + bind_configured_cmp_with_alphabet, std::string>( + m, + "LenWtLexCmpString", + "LenWtLexCmp", + "weights", + "length then weighted lexicographic ordering", + &LenWtLexCmp::weights); + bind_configured_cmp_with_alphabet, word_type>( + m, + "LenWtLexCmpWord", + "LenWtLexCmp", + "weights", + "length then weighted lexicographic ordering", + &LenWtLexCmp::weights); + + bind_configured_cmp_default>( + m, "RevLenWtLexCmp", "weights", &RevLenWtLexCmp<>::weights); + bind_configured_cmp_with_alphabet, std::string>( + m, + "RevLenWtLexCmpString", + "RevLenWtLexCmp", + "weights", + "length then reversed weighted lexicographic ordering", + &RevLenWtLexCmp::weights); + bind_configured_cmp_with_alphabet, word_type>( + m, + "RevLenWtLexCmpWord", + "RevLenWtLexCmp", + "weights", + "length then reversed weighted lexicographic ordering", + &RevLenWtLexCmp::weights); } } // namespace libsemigroups diff --git a/tests/test_order.py b/tests/test_order.py index 1b2f1950..80756759 100644 --- a/tests/test_order.py +++ b/tests/test_order.py @@ -4,14 +4,32 @@ # # The full license is in the file LICENSE, distributed with this software. +# pylint: disable=too-many-lines, invalid-name + """Tests for order.cpp.""" +from copy import copy + import pytest from libsemigroups_pybind11 import ( Alphabet, + LenLexCmp, + LenWtLexCmp, + LexCmp, LibsemigroupsError, Order, + RevLenLexCmp, + RevLenWtLexCmp, + RevLexCmp, + RevRPOCmp, + RevWrCmp, + RevWtLenLexCmp, + RevWtLexCmp, + RPOCmp, + WrCmp, + WtLenLexCmp, + WtLexCmp, len_wt_lex_cmp, lenlex_cmp, lex_cmp, @@ -32,182 +50,1089 @@ ) -@pytest.mark.parametrize( - ("compare", "x", "y"), - [ - (lex_cmp, "ab", "ba"), - (lex_cmp, [0, 1], [1, 0]), - (lenlex_cmp, "ba", "aaa"), - (lenlex_cmp, [1, 0], [0, 0, 0]), - (rev_lex_cmp, "ab", "ba"), - (rev_lex_cmp, [0, 1], [1, 0]), - (rev_lenlex_cmp, "ba", "aaa"), - (rev_lenlex_cmp, [1, 0], [0, 0, 0]), - (rpo_cmp, "ab", "ba"), - (rpo_cmp, [0, 1], [1, 0]), - (rev_rpo_cmp, "ab", "ba"), - (rev_rpo_cmp, [0, 1], [1, 0]), - ], -) -def test_compare_without_alphabet(compare, x, y): - """Check that each comparison function works without an alphabet.""" - assert isinstance(compare(x, y), bool) +def _check_simple_copy_and_repr(Cmp, name): + """Check copies of alphabet-free and alphabet-aware objects.""" + for original, alphabet, replacement, x, y in ( + (Cmp(), None, None, "a", "b"), + (Cmp(Alphabet("ba")), Alphabet("ba"), Alphabet("ab"), "b", "a"), + (Cmp(Alphabet([1, 0])), Alphabet([1, 0]), Alphabet([0, 1]), [1], [0]), + ): + copies = ( + original.copy(), + original.__copy__(), # pylint: disable=unnecessary-dunder-call + copy(original), + ) + assert all(isinstance(copied, Cmp) for copied in copies) + assert all(copied is not original for copied in copies) + assert all(copied.py_template_params == original.py_template_params for copied in copies) + assert all(copied(x, y) for copied in copies) + if alphabet is not None: + assert all(copied.alphabet() == alphabet for copied in copies) + if replacement is not None: + original.init(replacement) + assert not original(x, y) + assert all(copied(x, y) for copied in copies) -@pytest.mark.parametrize( - "compare", [lex_cmp, lenlex_cmp, rev_lex_cmp, rev_lenlex_cmp, rpo_cmp, rev_rpo_cmp] -) -@pytest.mark.parametrize( - ("alphabet", "x", "y", "missing"), - [(Alphabet("ba"), "ba", "ab", "c"), (Alphabet([1, 0]), [1, 0], [0, 1], [2])], -) -def test_compare_with_alphabet(compare, alphabet, x, y, missing): - """Check alphabet-aware comparisons and reject unknown letters.""" - assert isinstance(compare(alphabet, x, y), bool) + assert repr(Cmp()) == f"<{name} object>" + assert repr(Cmp(Alphabet("ba"))) == (f'<{name} object over >') + assert repr(Cmp(Alphabet([1, 0]))) == (f"<{name} object over >") + + +def _check_configured_copy_and_repr(Cmp, name, configuration_name, accessor_name): + """Check copies of configured alphabet-free and alphabet-aware objects.""" + for original, alphabet, replacement_args in ( + (Cmp([1, 2]), None, ([2, 1],)), + (Cmp(Alphabet("ab"), [1, 2]), Alphabet("ab"), (Alphabet("ab"), [2, 1])), + (Cmp(Alphabet([0, 1]), [1, 2]), Alphabet([0, 1]), (Alphabet([0, 1]), [2, 1])), + ): + copies = ( + original.copy(), + original.__copy__(), # pylint: disable=unnecessary-dunder-call + copy(original), + ) + original.init(*replacement_args) + assert all(isinstance(copied, Cmp) for copied in copies) + assert all(copied is not original for copied in copies) + assert all(getattr(copied, accessor_name)() == [1, 2] for copied in copies) + if alphabet is not None: + assert all(copied.alphabet() == alphabet for copied in copies) + assert getattr(original, accessor_name)() == [2, 1] + + assert repr(Cmp([1, 2])) == (f"<{name} object with {configuration_name} [1, 2]>") + assert repr(Cmp(Alphabet("ab"), [1, 2])) == ( + f'<{name} object over with {configuration_name} [1, 2]>' + ) + assert repr(Cmp(Alphabet([0, 1]), [1, 2])) == ( + f"<{name} object over with {configuration_name} [1, 2]>" + ) + + +def _check_simple_bad_constructor_arguments(Cmp): + """Check constructor and zero-argument init validation.""" + with pytest.raises(TypeError): + Cmp("abc") + with pytest.raises(TypeError): + Cmp(Alphabet("abc"), None) + with pytest.raises((AttributeError, TypeError)): + Cmp().init() + +def _check_configured_bad_constructor_arguments(Cmp): + """Check constructor and zero-argument init validation.""" + with pytest.raises(TypeError): + Cmp(Alphabet("ab")) with pytest.raises(LibsemigroupsError): - compare(alphabet, x, x + missing) + Cmp(Alphabet("ab"), [1]) + with pytest.raises((AttributeError, TypeError)): + Cmp().init() -def test_order_values_and_backwards_compatible_aliases(): - """Check the new order values and their backwards-compatible aliases.""" - assert Order.shortlex == Order.lenlex - assert Order.recursive == Order.rev_rpo - assert Order.rpo != Order.rev_rpo +############################################################################### +# Lex order +############################################################################### -@pytest.mark.parametrize( - ("old_compare", "new_compare"), - [ - (lexicographical_compare, lex_cmp), - (shortlex_compare, lenlex_cmp), - (recursive_path_compare, rev_rpo_cmp), - ], -) -def test_deprecated_comparisons(old_compare, new_compare): - """Check that legacy comparisons warn and delegate to their replacements.""" - with pytest.deprecated_call(): - result = old_compare("ab", "ba") - assert result == new_compare("ab", "ba") +def test_lex_without_alphabet(): + """Check lexicographic comparison without an alphabet.""" + assert lex_cmp("ab", "ba") + assert lex_cmp([0, 1], [1, 0]) + + cmp = LexCmp() + assert cmp("a", "b") + assert cmp([0], [1]) + assert not cmp("b", "aa") + + +def test_lex_with_alphabet(): + """Check lexicographic comparison with an alphabet.""" + alphabet = Alphabet("cba") + x = "cba" + y = "cab" + assert lex_cmp(alphabet, x, y) + + cmp = LexCmp(alphabet=alphabet) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.init(alphabet) is cmp + + with pytest.raises(LibsemigroupsError): + lex_cmp(alphabet, x, "d") + with pytest.raises(LibsemigroupsError): + cmp(x, "d") + + alphabet = Alphabet([4, 2, 7, 1]) + x = [4, 7] + y = [2, 4] + assert lex_cmp(alphabet, x, y) + + cmp = LexCmp(alphabet) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.init(alphabet) is cmp + + with pytest.raises(LibsemigroupsError): + lex_cmp(alphabet, x, [9]) + with pytest.raises(LibsemigroupsError): + cmp(x, [9]) + + +def test_lex_object_copy_and_repr(): + """Check copies and human-readable representations.""" + _check_simple_copy_and_repr(LexCmp, "LexCmp") + + +def test_lex_object_rejects_bad_constructor_arguments(): + """Check constructor validation.""" + _check_simple_bad_constructor_arguments(LexCmp) + + +############################################################################### +# RevLex order +############################################################################### + + +def test_rev_lex_without_alphabet(): + """Check reverse lexicographic comparison without an alphabet.""" + assert rev_lex_cmp("ba", "ab") + assert rev_lex_cmp([1, 0], [0, 1]) + + cmp = RevLexCmp() + assert cmp("a", "b") + assert cmp([0], [1]) + assert cmp("ba", "ab") + + +def test_rev_lex_with_alphabet(): + """Check reverse lexicographic comparison with an alphabet.""" + alphabet = Alphabet("qpr") + x = "rq" + y = "rp" + assert rev_lex_cmp(alphabet, x, y) + + cmp = RevLexCmp(alphabet=alphabet) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.init(alphabet) is cmp + + with pytest.raises(LibsemigroupsError): + rev_lex_cmp(alphabet, x, "s") + with pytest.raises(LibsemigroupsError): + cmp(x, "s") + + alphabet = Alphabet([8, 3, 5]) + x = [5, 8] + y = [5, 3] + assert rev_lex_cmp(alphabet, x, y) + + cmp = RevLexCmp(alphabet) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.init(alphabet) is cmp + + with pytest.raises(LibsemigroupsError): + rev_lex_cmp(alphabet, x, [9]) + with pytest.raises(LibsemigroupsError): + cmp(x, [9]) + + +def test_rev_lex_object_copy_and_repr(): + """Check copies and human-readable representations.""" + _check_simple_copy_and_repr(RevLexCmp, "RevLexCmp") + + +def test_rev_lex_object_rejects_bad_constructor_arguments(): + """Check constructor validation.""" + _check_simple_bad_constructor_arguments(RevLexCmp) + + +############################################################################### +# LenLex order +############################################################################### + + +def test_lenlex_without_alphabet(): + """Check length-lexicographic comparison without an alphabet.""" + assert lenlex_cmp("ba", "aaa") + assert lenlex_cmp([1, 0], [0, 0, 0]) + + cmp = LenLexCmp() + assert cmp("a", "b") + assert cmp([0], [1]) + assert not cmp("bb", "a") + + +def test_lenlex_with_alphabet(): + """Check length-lexicographic comparison with an alphabet.""" + alphabet = Alphabet("yxz") + x = "zy" + y = "zx" + assert lenlex_cmp(alphabet, x, y) + + cmp = LenLexCmp(alphabet=alphabet) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.init(alphabet) is cmp + + with pytest.raises(LibsemigroupsError): + lenlex_cmp(alphabet, x, "w") + with pytest.raises(LibsemigroupsError): + cmp(x, "w") + + alphabet = Alphabet([6, 2, 9]) + x = [9, 6] + y = [9, 2] + assert lenlex_cmp(alphabet, x, y) + + cmp = LenLexCmp(alphabet) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.init(alphabet) is cmp + + with pytest.raises(LibsemigroupsError): + lenlex_cmp(alphabet, x, [4]) + with pytest.raises(LibsemigroupsError): + cmp(x, [4]) + + +def test_lenlex_object_copy_and_repr(): + """Check copies and human-readable representations.""" + _check_simple_copy_and_repr(LenLexCmp, "LenLexCmp") + + +def test_lenlex_object_rejects_bad_constructor_arguments(): + """Check constructor validation.""" + _check_simple_bad_constructor_arguments(LenLexCmp) + + +############################################################################### +# RevLenLex order +############################################################################### + +def test_rev_lenlex_without_alphabet(): + """Check reverse length-lexicographic comparison without an alphabet.""" + assert rev_lenlex_cmp("ba", "ab") + assert rev_lenlex_cmp([1, 0], [0, 1]) -def test_wr_cmp_for_integer_words(): - """Check wreath-product comparison, validation, and its unchecked form.""" + cmp = RevLenLexCmp() + assert cmp("a", "b") + assert cmp([0], [1]) + assert cmp("ba", "ab") + + +def test_rev_lenlex_with_alphabet(): + """Check reverse length-lexicographic comparison with an alphabet.""" + alphabet = Alphabet("mkn") + x = "nm" + y = "nk" + assert rev_lenlex_cmp(alphabet, x, y) + + cmp = RevLenLexCmp(alphabet=alphabet) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.init(alphabet) is cmp + + with pytest.raises(LibsemigroupsError): + rev_lenlex_cmp(alphabet, x, "p") + with pytest.raises(LibsemigroupsError): + cmp(x, "p") + + alphabet = Alphabet([7, 4, 8]) + x = [8, 7] + y = [8, 4] + assert rev_lenlex_cmp(alphabet, x, y) + + cmp = RevLenLexCmp(alphabet) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.init(alphabet) is cmp + + with pytest.raises(LibsemigroupsError): + rev_lenlex_cmp(alphabet, x, [3]) + with pytest.raises(LibsemigroupsError): + cmp(x, [3]) + + +def test_rev_lenlex_object_copy_and_repr(): + """Check copies and human-readable representations.""" + _check_simple_copy_and_repr(RevLenLexCmp, "RevLenLexCmp") + + +def test_rev_lenlex_object_rejects_bad_constructor_arguments(): + """Check constructor validation.""" + _check_simple_bad_constructor_arguments(RevLenLexCmp) + + +############################################################################### +# RPO order +############################################################################### + + +def test_rpo_without_alphabet(): + """Check recursive-path comparison without an alphabet.""" + assert rpo_cmp("a", "b") + assert rpo_cmp([0], [1]) + + cmp = RPOCmp() + assert cmp("a", "b") + assert cmp([0], [1]) + assert not cmp("ab", "ba") + + +def test_rpo_with_alphabet(): + """Check recursive-path comparison with an alphabet.""" + alphabet = Alphabet("zxy") + x = "z" + y = "x" + assert rpo_cmp(alphabet, x, y) + + cmp = RPOCmp(alphabet=alphabet) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.init(alphabet) is cmp + + with pytest.raises(LibsemigroupsError): + rpo_cmp(alphabet, x, "w") + with pytest.raises(LibsemigroupsError): + cmp(x, "w") + + alphabet = Alphabet([8, 3, 5]) + x = [8] + y = [3] + assert rpo_cmp(alphabet, x, y) + + cmp = RPOCmp(alphabet) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.init(alphabet) is cmp + + with pytest.raises(LibsemigroupsError): + rpo_cmp(alphabet, x, [1]) + with pytest.raises(LibsemigroupsError): + cmp(x, [1]) + + +def test_rpo_object_copy_and_repr(): + """Check copies and human-readable representations.""" + _check_simple_copy_and_repr(RPOCmp, "RPOCmp") + + +def test_rpo_object_rejects_bad_constructor_arguments(): + """Check constructor validation.""" + _check_simple_bad_constructor_arguments(RPOCmp) + + +############################################################################### +# RevRPO order +############################################################################### + + +def test_rev_rpo_without_alphabet(): + """Check reverse recursive-path comparison without an alphabet.""" + assert rev_rpo_cmp("a", "b") + assert rev_rpo_cmp([0], [1]) + + cmp = RevRPOCmp() + assert cmp("a", "b") + assert cmp([0], [1]) + assert not cmp("ba", "ab") + + +def test_rev_rpo_with_alphabet(): + """Check reverse recursive-path comparison with an alphabet.""" + alphabet = Alphabet("qsr") + x = "q" + y = "s" + assert rev_rpo_cmp(alphabet, x, y) + + cmp = RevRPOCmp(alphabet=alphabet) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.init(alphabet) is cmp + + with pytest.raises(LibsemigroupsError): + rev_rpo_cmp(alphabet, x, "t") + with pytest.raises(LibsemigroupsError): + cmp(x, "t") + + alphabet = Alphabet([6, 4, 9]) + x = [6] + y = [4] + assert rev_rpo_cmp(alphabet, x, y) + + cmp = RevRPOCmp(alphabet) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.init(alphabet) is cmp + + with pytest.raises(LibsemigroupsError): + rev_rpo_cmp(alphabet, x, [2]) + with pytest.raises(LibsemigroupsError): + cmp(x, [2]) + + +def test_rev_rpo_object_copy_and_repr(): + """Check copies and human-readable representations.""" + _check_simple_copy_and_repr(RevRPOCmp, "RevRPOCmp") + + +def test_rev_rpo_object_rejects_bad_constructor_arguments(): + """Check constructor validation.""" + _check_simple_bad_constructor_arguments(RevRPOCmp) + + +############################################################################### +# Wr order +############################################################################### + + +def test_wr_without_alphabet(): + """Check wreath-product comparison without an alphabet.""" levels = [0, 0, 1] x = [0, 2] y = [1, 2] - assert wr_cmp(levels, x, y) assert not wr_cmp(levels, y, x) - with pytest.raises(LibsemigroupsError): wr_cmp([0, 1], [0, 2], [0, 1]) + cmp = WrCmp(levels) + assert cmp(x, y) + assert cmp("\x00\x02", "\x01\x02") + assert cmp.levels() == levels + assert cmp.init([1, 1, 0]) is cmp + assert cmp.levels() == [1, 1, 0] + with pytest.raises(LibsemigroupsError): + cmp([3], [0]) + with pytest.raises(LibsemigroupsError): + cmp("\x03", "\x00") + with pytest.raises(TypeError): + cmp("\x00", [1]) + -def test_wr_cmp_with_alphabet(): - """Check wreath-product comparison over an explicitly ordered alphabet.""" +def test_wr_with_alphabet(): + """Check wreath-product comparison with an alphabet.""" alphabet = Alphabet("bac") levels = [1, 1, 0] - - assert wr_cmp(alphabet, levels, "cbcc", "ccbc") + x = "cbcc" + y = "ccbc" + assert wr_cmp(alphabet, levels, x, y) assert wr_cmp(alphabet, levels, "ac", "ca") - word_alphabet = Alphabet([1, 0]) - assert wr_cmp(word_alphabet, [0, 0], [1], [0]) + cmp = WrCmp(alphabet, levels) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.levels() == levels + assert cmp.init(alphabet, levels) is cmp with pytest.raises(LibsemigroupsError): wr_cmp(alphabet, levels, "d", "b") + with pytest.raises(LibsemigroupsError): + cmp("d", "b") + + alphabet = Alphabet([9, 4, 7]) + levels = [0, 0, 0] + x = [9] + y = [4] + assert wr_cmp(alphabet, levels, x, y) + + cmp = WrCmp(alphabet, levels) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.levels() == levels + assert cmp.init(alphabet, levels) is cmp + + with pytest.raises(LibsemigroupsError): + wr_cmp(alphabet, levels, x, [2]) + with pytest.raises(LibsemigroupsError): + cmp(x, [2]) + + +def test_wr_object_copy_and_repr(): + """Check copies and human-readable representations.""" + _check_configured_copy_and_repr(WrCmp, "WrCmp", "levels", "levels") + + +def test_wr_object_rejects_bad_constructor_arguments(): + """Check constructor validation.""" + _check_configured_bad_constructor_arguments(WrCmp) + + +############################################################################### +# RevWr order +############################################################################### -@pytest.mark.parametrize("compare", [wt_lenlex_cmp, wt_lex_cmp]) -def test_weighted_comparisons_for_integer_words(compare): - """Check weighted comparisons and validation for integer words.""" +def test_rev_wr_without_alphabet(): + """Check reverse wreath-product comparison without an alphabet.""" + levels = [0, 0] + x = [1, 0] + y = [0, 1] + assert rev_wr_cmp(levels, x, y) + + cmp = RevWrCmp(levels) + assert cmp(x, y) + assert cmp("\x01\x00", "\x00\x01") + assert cmp.levels() == levels + assert cmp.init([2, 1]) is cmp + assert cmp.levels() == [2, 1] + + with pytest.raises(LibsemigroupsError): + rev_wr_cmp(levels, [2], [0]) + with pytest.raises(LibsemigroupsError): + cmp([2], [0]) + with pytest.raises(LibsemigroupsError): + cmp("\x02", "\x00") + with pytest.raises(TypeError): + cmp("\x00", [1]) + + +def test_rev_wr_with_alphabet(): + """Check reverse wreath-product comparison with an alphabet.""" + alphabet = Alphabet("cab") + levels = [0, 0, 0] + x = "bc" + y = "ba" + assert rev_wr_cmp(alphabet, levels, x, y) + + cmp = RevWrCmp(alphabet, levels) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.levels() == levels + assert cmp.init(alphabet, levels) is cmp + + with pytest.raises(LibsemigroupsError): + rev_wr_cmp(alphabet, levels, x, "d") + with pytest.raises(LibsemigroupsError): + cmp(x, "d") + + alphabet = Alphabet([7, 2, 5]) + levels = [0, 0, 0] + x = [5, 7] + y = [5, 2] + assert rev_wr_cmp(alphabet, levels, x, y) + + cmp = RevWrCmp(alphabet, levels) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.levels() == levels + assert cmp.init(alphabet, levels) is cmp + + with pytest.raises(LibsemigroupsError): + rev_wr_cmp(alphabet, levels, x, [9]) + with pytest.raises(LibsemigroupsError): + cmp(x, [9]) + + +def test_rev_wr_object_copy_and_repr(): + """Check copies and human-readable representations.""" + _check_configured_copy_and_repr(RevWrCmp, "RevWrCmp", "levels", "levels") + + +def test_rev_wr_object_rejects_bad_constructor_arguments(): + """Check constructor validation.""" + _check_configured_bad_constructor_arguments(RevWrCmp) + + +############################################################################### +# WtLenLex order +############################################################################### + + +def test_wt_lenlex_without_alphabet(): + """Check weight-length-lexicographic comparison without an alphabet.""" weights = [2, 1, 6] + x = [0, 1] + y = [2] + assert wt_lenlex_cmp(weights, x, y) + assert not wt_lenlex_cmp(weights, y, x) + assert wt_lenlex_cmp(weights, "\x00\x01", "\x02") + assert not wt_lenlex_cmp(weights, "\x02", "\x00\x01") - assert compare(weights, [0, 1], [2]) - assert not compare(weights, [2], [0, 1]) + cmp = WtLenLexCmp(weights) + assert cmp(x, y) + assert cmp("\x00\x01", "\x02") + assert cmp.weights() == weights + assert cmp.init([1, 2, 6]) is cmp + assert cmp.weights() == [1, 2, 6] with pytest.raises(LibsemigroupsError): - compare(weights, [0], [3]) + wt_lenlex_cmp(weights, [3], [0]) + with pytest.raises(LibsemigroupsError): + wt_lenlex_cmp(weights, "\x00", "\x03") + with pytest.raises(LibsemigroupsError): + cmp([3], [0]) + with pytest.raises(LibsemigroupsError): + cmp("\x00", "\x03") + with pytest.raises(TypeError): + cmp("\x00", [1]) + - assert compare(weights, chr(0) + chr(1), chr(2)) - assert not compare(weights, chr(2), chr(0) + chr(1)) +def test_wt_lenlex_with_alphabet(): + """Check weight-length-lexicographic comparison with an alphabet.""" + alphabet = Alphabet("cab") + weights = [4, 1, 3] + x = "aa" + y = "b" + assert wt_lenlex_cmp(alphabet, weights, x, y) + cmp = WtLenLexCmp(alphabet, weights) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.weights() == weights + assert cmp.init(alphabet, weights) is cmp + + with pytest.raises(LibsemigroupsError): + wt_lenlex_cmp(alphabet, weights, x, "d") with pytest.raises(LibsemigroupsError): - compare(weights, chr(0), chr(3)) + wt_lenlex_cmp(alphabet, [1, 2], x, y) + with pytest.raises(LibsemigroupsError): + cmp(x, "d") + + alphabet = Alphabet([7, 2, 5]) + weights = [4, 1, 3] + x = [2, 2] + y = [5] + assert wt_lenlex_cmp(alphabet, weights, x, y) + + cmp = WtLenLexCmp(alphabet, weights) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.weights() == weights + assert cmp.init(alphabet, weights) is cmp + with pytest.raises(LibsemigroupsError): + wt_lenlex_cmp(alphabet, weights, x, [9]) + with pytest.raises(LibsemigroupsError): + cmp(x, [9]) -def test_weighted_comparisons_use_different_tie_breakers(): - """Check the length and lexicographic tie breakers differ.""" - weights = [1, 2] - assert wt_lenlex_cmp(weights, [1], [0, 0]) - assert not wt_lex_cmp(weights, [1], [0, 0]) +def test_wt_lenlex_object_copy_and_repr(): + """Check copies and human-readable representations.""" + _check_configured_copy_and_repr(WtLenLexCmp, "WtLenLexCmp", "weights", "weights") -@pytest.mark.parametrize("compare", [wt_lenlex_cmp, wt_lex_cmp]) -def test_weighted_comparisons_with_alphabet(compare): - """Check weighted comparisons over an explicitly ordered alphabet.""" - alphabet = Alphabet("ba") +def test_wt_lenlex_object_rejects_bad_constructor_arguments(): + """Check constructor validation.""" + _check_configured_bad_constructor_arguments(WtLenLexCmp) - assert compare(alphabet, [10, 1], "a", "b") - assert compare(alphabet, [1, 1], "b", "a") - word_alphabet = Alphabet([1, 0]) - assert compare(word_alphabet, [1, 1], [1], [0]) +############################################################################### +# RevWtLenLex order +############################################################################### + + +def test_rev_wt_lenlex_without_alphabet(): + """Check reverse weight-length-lexicographic comparison without an alphabet.""" + weights = [1, 1] + x = [1, 0] + y = [0, 1] + assert rev_wt_lenlex_cmp(weights, x, y) + + cmp = RevWtLenLexCmp(weights) + assert cmp(x, y) + assert cmp("\x01\x00", "\x00\x01") + assert cmp.weights() == weights + assert cmp.init([2, 1]) is cmp + assert cmp.weights() == [2, 1] + assert rev_wt_lenlex_cmp([1, 2], [1], [0, 0]) with pytest.raises(LibsemigroupsError): - compare(alphabet, [1, 1], "c", "a") + rev_wt_lenlex_cmp(weights, [2], [0]) + with pytest.raises(LibsemigroupsError): + cmp([2], [0]) + with pytest.raises(LibsemigroupsError): + cmp("\x02", "\x00") + with pytest.raises(TypeError): + cmp("\x00", [1]) + + +def test_rev_wt_lenlex_with_alphabet(): + """Check reverse weight-length-lexicographic comparison with an alphabet.""" + alphabet = Alphabet("pqr") + weights = [1, 1, 1] + x = "rp" + y = "rq" + assert rev_wt_lenlex_cmp(alphabet, weights, x, y) + + cmp = RevWtLenLexCmp(alphabet, weights) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.weights() == weights + assert cmp.init(alphabet, weights) is cmp with pytest.raises(LibsemigroupsError): - compare(alphabet, [1], "a", "b") + rev_wt_lenlex_cmp(alphabet, weights, x, "s") + with pytest.raises(LibsemigroupsError): + cmp(x, "s") + alphabet = Alphabet([6, 1, 8]) + weights = [1, 1, 1] + x = [8, 6] + y = [8, 1] + assert rev_wt_lenlex_cmp(alphabet, weights, x, y) + + cmp = RevWtLenLexCmp(alphabet, weights) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.weights() == weights + assert cmp.init(alphabet, weights) is cmp + + with pytest.raises(LibsemigroupsError): + rev_wt_lenlex_cmp(alphabet, weights, x, [4]) + with pytest.raises(LibsemigroupsError): + cmp(x, [4]) + + +def test_rev_wt_lenlex_object_copy_and_repr(): + """Check copies and human-readable representations.""" + _check_configured_copy_and_repr(RevWtLenLexCmp, "RevWtLenLexCmp", "weights", "weights") + + +def test_rev_wt_lenlex_object_rejects_bad_constructor_arguments(): + """Check constructor validation.""" + _check_configured_bad_constructor_arguments(RevWtLenLexCmp) + + +############################################################################### +# WtLex order +############################################################################### + + +def test_wt_lex_without_alphabet(): + """Check weight-lexicographic comparison without an alphabet.""" + weights = [2, 1, 6] + x = [0, 1] + y = [2] + assert wt_lex_cmp(weights, x, y) + assert not wt_lex_cmp(weights, y, x) + assert wt_lex_cmp(weights, "\x00\x01", "\x02") + assert not wt_lex_cmp(weights, "\x02", "\x00\x01") + + cmp = WtLexCmp(weights) + assert cmp(x, y) + assert cmp("\x00\x01", "\x02") + assert cmp.weights() == weights + assert cmp.init([1, 2, 6]) is cmp + assert cmp.weights() == [1, 2, 6] + + assert not wt_lex_cmp([1, 2], [1], [0, 0]) + with pytest.raises(LibsemigroupsError): + wt_lex_cmp(weights, [3], [0]) + with pytest.raises(LibsemigroupsError): + wt_lex_cmp(weights, "\x00", "\x03") + with pytest.raises(LibsemigroupsError): + cmp([3], [0]) + with pytest.raises(LibsemigroupsError): + cmp("\x00", "\x03") + with pytest.raises(TypeError): + cmp("\x00", [1]) + + +def test_wt_lex_with_alphabet(): + """Check weight-lexicographic comparison with an alphabet.""" + alphabet = Alphabet("dbca") + weights = [5, 2, 3, 1] + x = "b" + y = "aa" + assert wt_lex_cmp(alphabet, weights, x, y) + + cmp = WtLexCmp(alphabet, weights) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.weights() == weights + assert cmp.init(alphabet, weights) is cmp + + with pytest.raises(LibsemigroupsError): + wt_lex_cmp(alphabet, weights, x, "e") + with pytest.raises(LibsemigroupsError): + wt_lex_cmp(alphabet, [1, 2, 3], x, y) + with pytest.raises(LibsemigroupsError): + cmp(x, "e") + + alphabet = Alphabet([9, 4, 7, 2]) + weights = [5, 2, 3, 1] + x = [4] + y = [2, 2] + assert wt_lex_cmp(alphabet, weights, x, y) + + cmp = WtLexCmp(alphabet, weights) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.weights() == weights + assert cmp.init(alphabet, weights) is cmp + + with pytest.raises(LibsemigroupsError): + wt_lex_cmp(alphabet, weights, x, [6]) + with pytest.raises(LibsemigroupsError): + cmp(x, [6]) -@pytest.mark.parametrize( - ("compare", "parameters"), - [ - (rev_wr_cmp, [0, 0]), - (rev_wt_lenlex_cmp, [1, 1]), - (rev_wt_lex_cmp, [1, 1]), - (rev_len_wt_lex_cmp, [1, 1]), - ], -) -def test_parameterized_reverse_comparisons(compare, parameters): - """Check parameterized reverse orders for both supported word types.""" - assert compare(parameters, [1, 0], [0, 1]) - assert compare(Alphabet("ab"), parameters, "ba", "ab") +def test_wt_lex_object_copy_and_repr(): + """Check copies and human-readable representations.""" + _check_configured_copy_and_repr(WtLexCmp, "WtLexCmp", "weights", "weights") + + +def test_wt_lex_object_rejects_bad_constructor_arguments(): + """Check constructor validation.""" + _check_configured_bad_constructor_arguments(WtLexCmp) + + +############################################################################### +# RevWtLex order +############################################################################### + + +def test_rev_wt_lex_without_alphabet(): + """Check reverse weight-lexicographic comparison without an alphabet.""" + weights = [1, 1] + x = [1, 0] + y = [0, 1] + assert rev_wt_lex_cmp(weights, x, y) + + cmp = RevWtLexCmp(weights) + assert cmp(x, y) + assert cmp("\x01\x00", "\x00\x01") + assert cmp.weights() == weights + assert cmp.init([2, 1]) is cmp + assert cmp.weights() == [2, 1] + + assert not rev_wt_lex_cmp([1, 2], [1], [0, 0]) + assert not rev_wt_lex_cmp([100, 1], [0], [1, 1]) + with pytest.raises(LibsemigroupsError): + rev_wt_lex_cmp(weights, [2], [0]) with pytest.raises(LibsemigroupsError): - compare(parameters, [2], [0, 1]) + cmp([2], [0]) + with pytest.raises(LibsemigroupsError): + cmp("\x02", "\x00") + with pytest.raises(TypeError): + cmp("\x00", [1]) + + +def test_rev_wt_lex_with_alphabet(): + """Check reverse weight-lexicographic comparison with an alphabet.""" + alphabet = Alphabet("uvw") + weights = [1, 1, 1] + x = "wu" + y = "wv" + assert rev_wt_lex_cmp(alphabet, weights, x, y) + + cmp = RevWtLexCmp(alphabet, weights) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.weights() == weights + assert cmp.init(alphabet, weights) is cmp + + with pytest.raises(LibsemigroupsError): + rev_wt_lex_cmp(alphabet, weights, x, "x") + with pytest.raises(LibsemigroupsError): + cmp(x, "x") + + alphabet = Alphabet([3, 8, 1]) + weights = [1, 1, 1] + x = [1, 3] + y = [1, 8] + assert rev_wt_lex_cmp(alphabet, weights, x, y) + cmp = RevWtLexCmp(alphabet, weights) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.weights() == weights + assert cmp.init(alphabet, weights) is cmp + + with pytest.raises(LibsemigroupsError): + rev_wt_lex_cmp(alphabet, weights, x, [6]) with pytest.raises(LibsemigroupsError): - compare(Alphabet("ab"), parameters, "c", "ab") + cmp(x, [6]) -def test_reverse_weighted_comparisons_use_the_expected_priorities(): - """Check the weight and length priorities of the reverse weighted orders.""" - weights = [1, 2] +def test_rev_wt_lex_object_copy_and_repr(): + """Check copies and human-readable representations.""" + _check_configured_copy_and_repr(RevWtLexCmp, "RevWtLexCmp", "weights", "weights") - assert rev_wt_lenlex_cmp(weights, [1], [0, 0]) - assert not rev_wt_lex_cmp(weights, [1], [0, 0]) - weights = [100, 1] - assert rev_len_wt_lex_cmp(weights, [0], [1, 1]) - assert not rev_wt_lex_cmp(weights, [0], [1, 1]) +def test_rev_wt_lex_object_rejects_bad_constructor_arguments(): + """Check constructor validation.""" + _check_configured_bad_constructor_arguments(RevWtLexCmp) -def test_len_wt_lex_cmp(): - """Check length-before-weighted-lex ordering and validation.""" +############################################################################### +# LenWtLex order +############################################################################### + + +def test_len_wt_lex_without_alphabet(): + """Check length-weight-lexicographic comparison without an alphabet.""" weights = [1, 1] + x = [0, 1] + y = [1, 0] + assert len_wt_lex_cmp(weights, x, y) - assert len_wt_lex_cmp(weights, [0, 1], [1, 0]) - assert len_wt_lex_cmp(Alphabet("ab"), weights, "ab", "ba") + cmp = LenWtLexCmp(weights) + assert cmp(x, y) + assert cmp("\x00\x01", "\x01\x00") + assert cmp.weights() == weights + assert cmp.init([2, 1]) is cmp + assert cmp.weights() == [2, 1] + assert len_wt_lex_cmp([100, 1], [0], [1, 1]) + with pytest.raises(LibsemigroupsError): + len_wt_lex_cmp(weights, [2], [0]) with pytest.raises(LibsemigroupsError): - len_wt_lex_cmp(weights, [2], [0, 1]) + cmp([2], [0]) + with pytest.raises(LibsemigroupsError): + cmp("\x02", "\x00") + with pytest.raises(TypeError): + cmp("\x00", [1]) + +def test_len_wt_lex_with_alphabet(): + """Check length-weight-lexicographic comparison with an alphabet.""" + alphabet = Alphabet("acb") + weights = [2, 1, 3] + x = "cc" + y = "aa" + assert len_wt_lex_cmp(alphabet, weights, x, y) + + cmp = LenWtLexCmp(alphabet, weights) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.weights() == weights + assert cmp.init(alphabet, weights) is cmp + + with pytest.raises(LibsemigroupsError): + len_wt_lex_cmp(alphabet, weights, x, "d") with pytest.raises(LibsemigroupsError): - len_wt_lex_cmp(Alphabet("ab"), weights, "c", "ab") + cmp(x, "d") + + alphabet = Alphabet([5, 1, 8]) + weights = [2, 1, 3] + x = [1, 1] + y = [5, 5] + assert len_wt_lex_cmp(alphabet, weights, x, y) + + cmp = LenWtLexCmp(alphabet, weights) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.weights() == weights + assert cmp.init(alphabet, weights) is cmp + + with pytest.raises(LibsemigroupsError): + len_wt_lex_cmp(alphabet, weights, x, [9]) + with pytest.raises(LibsemigroupsError): + cmp(x, [9]) + + +def test_len_wt_lex_object_copy_and_repr(): + """Check copies and human-readable representations.""" + _check_configured_copy_and_repr(LenWtLexCmp, "LenWtLexCmp", "weights", "weights") + + +def test_len_wt_lex_object_rejects_bad_constructor_arguments(): + """Check constructor validation.""" + _check_configured_bad_constructor_arguments(LenWtLexCmp) + + +############################################################################### +# RevLenWtLex order +############################################################################### - weights = [100, 1] - assert len_wt_lex_cmp(weights, [0], [1, 1]) + +def test_rev_len_wt_lex_without_alphabet(): + """Check reverse length-weight-lexicographic comparison without an alphabet.""" + weights = [1, 1] + x = [1, 0] + y = [0, 1] + assert rev_len_wt_lex_cmp(weights, x, y) + + cmp = RevLenWtLexCmp(weights) + assert cmp(x, y) + assert cmp("\x01\x00", "\x00\x01") + assert cmp.weights() == weights + assert cmp.init([2, 1]) is cmp + assert cmp.weights() == [2, 1] + + assert rev_len_wt_lex_cmp([100, 1], [0], [1, 1]) + with pytest.raises(LibsemigroupsError): + rev_len_wt_lex_cmp(weights, [2], [0]) + with pytest.raises(LibsemigroupsError): + cmp([2], [0]) + with pytest.raises(LibsemigroupsError): + cmp("\x02", "\x00") + with pytest.raises(TypeError): + cmp("\x00", [1]) + + +def test_rev_len_wt_lex_with_alphabet(): + """Check reverse length-weight-lexicographic comparison with an alphabet.""" + alphabet = Alphabet("hgf") + weights = [1, 1, 1] + x = "fh" + y = "fg" + assert rev_len_wt_lex_cmp(alphabet, weights, x, y) + + cmp = RevLenWtLexCmp(alphabet, weights) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.weights() == weights + assert cmp.init(alphabet, weights) is cmp + + with pytest.raises(LibsemigroupsError): + rev_len_wt_lex_cmp(alphabet, weights, x, "i") + with pytest.raises(LibsemigroupsError): + cmp(x, "i") + + alphabet = Alphabet([4, 6, 2]) + weights = [1, 1, 1] + x = [2, 4] + y = [2, 6] + assert rev_len_wt_lex_cmp(alphabet, weights, x, y) + + cmp = RevLenWtLexCmp(alphabet, weights) + assert cmp(x, y) + assert cmp.alphabet() == alphabet + assert cmp.weights() == weights + assert cmp.init(alphabet, weights) is cmp + + with pytest.raises(LibsemigroupsError): + rev_len_wt_lex_cmp(alphabet, weights, x, [7]) + with pytest.raises(LibsemigroupsError): + cmp(x, [7]) + + +def test_rev_len_wt_lex_object_copy_and_repr(): + """Check copies and human-readable representations.""" + _check_configured_copy_and_repr(RevLenWtLexCmp, "RevLenWtLexCmp", "weights", "weights") + + +def test_rev_len_wt_lex_object_rejects_bad_constructor_arguments(): + """Check constructor validation.""" + _check_configured_bad_constructor_arguments(RevLenWtLexCmp) + + +############################################################################### +# Backwards compatibility +############################################################################### + + +def test_order_values_and_backwards_compatible_aliases(): + """Check enum values and their backwards-compatible aliases.""" + assert Order.shortlex == Order.lenlex + assert Order.recursive == Order.rev_rpo + assert Order.rpo != Order.rev_rpo + + +def test_deprecated_comparisons(): + """Check that legacy comparisons warn and delegate to their replacements.""" + with pytest.deprecated_call(): + result = lexicographical_compare("ac", "ba") + assert result == lex_cmp("ac", "ba") + + with pytest.deprecated_call(): + result = shortlex_compare("cab", "dddd") + assert result == lenlex_cmp("cab", "dddd") + + with pytest.deprecated_call(): + result = recursive_path_compare("xy", "yx") + assert result == rev_rpo_cmp("xy", "yx")