diff --git a/docs/source/index.rst b/docs/source/index.rst
index 6442b387..f828ded6 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -156,6 +156,7 @@ We would like to thank the authors and contributors of these projects!
main-algorithms/action/index
main-algorithms/congruence/index
main-algorithms/core-classes/index
+ main-algorithms/du-narendran-rusinowitch/index
main-algorithms/froidure-pin/index
main-algorithms/kambites/index
main-algorithms/knuth-bendix/index
@@ -165,4 +166,3 @@ We would like to thank the authors and contributors of these projects!
main-algorithms/schreier-sims/index
main-algorithms/stephen/index
main-algorithms/todd-coxeter/index
-
diff --git a/docs/source/libsemigroups.bib b/docs/source/libsemigroups.bib
index eb366783..d2f6c4c0 100644
--- a/docs/source/libsemigroups.bib
+++ b/docs/source/libsemigroups.bib
@@ -662,3 +662,13 @@ @article{Stephen1987aa
author = {Stephen, Joseph Buchanan},
month = {01},
year = {1987}}
+
+@article{Du2026aa,
+ author = {Du, Wei and Narendran, Paliath and Rusinowitch, Michael},
+ doi = {10.1016/j.jlamp.2026.101126},
+ journal = {Journal of Logical and Algebraic Methods in Programming},
+ pages = {101126},
+ title = {Inferring RPO symbol orderings},
+ volume = {150},
+ year = {2026}
+}
diff --git a/docs/source/main-algorithms/du-narendran-rusinowitch/index.rst b/docs/source/main-algorithms/du-narendran-rusinowitch/index.rst
new file mode 100644
index 00000000..8a7a800e
--- /dev/null
+++ b/docs/source/main-algorithms/du-narendran-rusinowitch/index.rst
@@ -0,0 +1,17 @@
+..
+ 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
+
+Du-Narendran-Rusinowitch
+========================
+
+This page describes the implementation of the algorithm from
+:cite:`Du2026aa` for finding an alphabet order that orients the rules of a
+presentation with respect to recursive-path ordering.
+
+.. autofunction:: du_narendran_rusinowitch
diff --git a/src/du-narendran-rusinowitch.cpp b/src/du-narendran-rusinowitch.cpp
new file mode 100644
index 00000000..64e886bb
--- /dev/null
+++ b/src/du-narendran-rusinowitch.cpp
@@ -0,0 +1,83 @@
+//
+// libsemigroups_pybind11
+// Copyright (C) 2026 James D. Mitchell
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+
+// C++ stl headers....
+#include // for string
+
+// libsemigroups....
+#include // for du_narendran_...
+#include // for Presentation
+#include // for word_type
+
+// pybind11....
+#include // for arg, module
+#include // for automatic conversions
+
+// libsemigroups_pybind11....
+#include "main.hpp" // for init_du_narendran_rusinowitch
+
+namespace libsemigroups {
+ namespace py = pybind11;
+
+ namespace {
+ template
+ void bind_du_narendran_rusinowitch(py::module& m) {
+ m.def(
+ "du_narendran_rusinowitch",
+ [](Presentation const& p) {
+ return du_narendran_rusinowitch(p);
+ },
+ py::arg("p"),
+ R"pbdoc(
+:sig=(p: Presentation) -> str | list[int]:
+:only-document-once:
+Find an alphabet order that orients every rule using recursive-path ordering.
+
+This function returns the alphabet of *p*, ordered so that every rule
+:math:`u \to v` satisfies :math:`u > v` with respect to recursive-path
+ordering. It returns an empty word if no such order exists, or if the alphabet
+of *p* is empty.
+
+:param p: the presentation whose rules are to be oriented.
+:type p: Presentation
+
+:returns: An alphabet order orienting every rule, or an empty word if none
+ exists.
+:rtype: str | list[int]
+
+:raises LibsemigroupsError: if the alphabet or rules of *p* are invalid.
+
+.. seealso::
+ :any:`rpo_cmp`
+
+.. doctest::
+
+ >>> from libsemigroups_pybind11 import Presentation, du_narendran_rusinowitch
+ >>> p = Presentation("abcd")
+ >>> p.rules = ["a", "cc", "d", "bcc", "bccb", "c", "cccb", "bccc"]
+ >>> du_narendran_rusinowitch(p)
+ 'bcda'
+)pbdoc");
+ }
+ } // namespace
+
+ void init_du_narendran_rusinowitch(py::module& m) {
+ bind_du_narendran_rusinowitch(m);
+ bind_du_narendran_rusinowitch(m);
+ }
+} // namespace libsemigroups
diff --git a/src/libsemigroups_pybind11/__init__.py b/src/libsemigroups_pybind11/__init__.py
index 4d15bbd4..e655588f 100644
--- a/src/libsemigroups_pybind11/__init__.py
+++ b/src/libsemigroups_pybind11/__init__.py
@@ -43,6 +43,7 @@
from .congruence import Congruence
from .detail.cxx_wrapper import wrap_cxx_free_fn as _wrap_cxx_free_fn
from .detail.dot import Dot
+from .du_narendran_rusinowitch import du_narendran_rusinowitch
from .forest import PathsFromRoots, PathsToRoots
from .froidure_pin import FroidurePin
from .hpcombi import LIBSEMIGROUPS_HPCOMBI_ENABLED
@@ -244,6 +245,7 @@
"ToddCoxeter",
"Transf",
# Free functions from submodules
+ "du_narendran_rusinowitch",
"is_obviously_infinite",
"to",
"validate",
diff --git a/src/libsemigroups_pybind11/du_narendran_rusinowitch.py b/src/libsemigroups_pybind11/du_narendran_rusinowitch.py
new file mode 100644
index 00000000..7fd7e3f5
--- /dev/null
+++ b/src/libsemigroups_pybind11/du_narendran_rusinowitch.py
@@ -0,0 +1,15 @@
+# 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.
+
+"""Subpackage for importing and wrapping :any:`du_narendran_rusinowitch`."""
+
+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
+
+du_narendran_rusinowitch = _wrap_cxx_free_fn(_du_narendran_rusinowitch)
+
+__all__ = ["du_narendran_rusinowitch"]
diff --git a/src/main.cpp b/src/main.cpp
index fd5c9afb..9f3ce12a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -118,6 +118,7 @@ namespace libsemigroups {
init_bipart(m);
init_blocks(m);
init_cong(m);
+ init_du_narendran_rusinowitch(m);
init_error(m);
init_freeband(m);
init_froidure_pin_base(m); // Must be before init_froidure_pin
diff --git a/src/main.hpp b/src/main.hpp
index 0f9742a4..b7ac53a9 100644
--- a/src/main.hpp
+++ b/src/main.hpp
@@ -45,6 +45,7 @@ namespace libsemigroups {
void init_cong(py::module&);
void init_constants(py::module&);
void init_dot(py::module&);
+ void init_du_narendran_rusinowitch(py::module&);
void init_error(py::module&);
void init_forest(py::module&);
void init_freeband(py::module&);
diff --git a/tests/test_du_narendran_rusinowitch.py b/tests/test_du_narendran_rusinowitch.py
new file mode 100644
index 00000000..c675553b
--- /dev/null
+++ b/tests/test_du_narendran_rusinowitch.py
@@ -0,0 +1,60 @@
+# 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.
+
+"""Tests for du_narendran_rusinowitch."""
+
+# pylint: disable=missing-function-docstring
+
+import pytest
+
+from libsemigroups_pybind11 import (
+ Alphabet,
+ LibsemigroupsError,
+ Presentation,
+ du_narendran_rusinowitch,
+ rpo_cmp,
+)
+
+
+@pytest.mark.parametrize(
+ ("alphabet", "rules", "expected"),
+ [
+ ("abcde", ["dbcbace", "cbbaec", "bcbad", "badbc"], "edcab"),
+ (
+ [0, 1, 2, 3, 4],
+ [[3, 1, 2, 1, 0, 2, 4], [2, 1, 1, 0, 4, 2], [1, 2, 1, 0, 3], [1, 0, 3, 1, 2]],
+ [4, 3, 2, 0, 1],
+ ),
+ ],
+)
+def test_du_narendran_rusinowitch(alphabet, rules, expected):
+ p = Presentation(alphabet)
+ p.rules = rules
+
+ assert du_narendran_rusinowitch(p) == expected
+ for lhs, rhs in list(zip(rules[::2], rules[1::2], strict=True)):
+ assert rpo_cmp(Alphabet(alphabet), rhs, lhs)
+
+
+def test_du_narendran_rusinowitch_no_order_exists():
+ p = Presentation("abcd")
+ p.rules = ["a", "b", "b", "c", "c", "d", "d", "a"]
+
+ assert du_narendran_rusinowitch(p) == ""
+
+
+def test_du_narendran_rusinowitch_empty_rules():
+ p = Presentation([2, 0, 1])
+
+ assert du_narendran_rusinowitch(p) == [2, 0, 1]
+
+
+def test_du_narendran_rusinowitch_invalid_presentation():
+ p = Presentation("ab")
+ p.rules = ["a", "c"]
+
+ with pytest.raises(LibsemigroupsError):
+ du_narendran_rusinowitch(p)