Skip to content

Commit d9bcf44

Browse files
committed
add Lehmer code method
1 parent 21a68d3 commit d9bcf44

File tree

6 files changed

+89
-0
lines changed

6 files changed

+89
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ The version is represented by three digits: a.b.c.
2525
FEATURE:
2626
- `symmetria.Permutation`: add `lexicographic_rank` method
2727
- `symmetria.CycleDecomposition`: add `lexicographic_rank` method
28+
- `symmetria.Permutation`: add `lehmer_code` method
29+
- `symmetria.CycleDecomposition`: add `lehmer_code` method
2830

2931
FIX:
3032
- `symmetria.Permutation`: fix small typos in class methods

docs/source/pages/API_reference/elements/index.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ Here, **P** denotes the class ``Permutation``, **C** the class ``Cycle``, and **
115115
- ✅
116116
- ❌
117117
- ✅
118+
* - ``lehmer_code``
119+
- Return the Lehmer code of the permutation
120+
- ✅
121+
- ❌
122+
- ✅
118123
* - ``lexicographic_rank``
119124
- Return the lexicographic rank of the permutation
120125
- ✅

symmetria/elements/cycle_decomposition.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,27 @@ def is_regular(self) -> bool:
764764
"""
765765
return all(len(cycle) == len(self[0]) for cycle in self)
766766

767+
def lehmer_code(self) -> List[int]:
768+
"""Return the Lehmer code of the cycle decomposition.
769+
770+
Recall that the Lehmer code of a permutation is a sequence that encodes the permutation as a series of integers.
771+
Each integer represents the number of smaller elements to the right of a given element in the permutation.
772+
773+
:return: the Lehmer code of the cycle decomposition.
774+
:rtype: List[int]
775+
776+
:example:
777+
>>> from symmetria import CycleDecomposition, Cycle
778+
...
779+
>>> CycleDecomposition(Cycle(1)).lehmer_code()
780+
[0]
781+
>>> CycleDecomposition(Cycle(1, 2), Cycle(3)).lehmer_code()
782+
[1, 0, 0]
783+
>>> CycleDecomposition(Cycle(1, 4), Cycle(2, 3)).lehmer_code()
784+
[3, 2, 1, 0]
785+
"""
786+
return symmetria.Permutation.from_cycle_decomposition(self).lehmer_code()
787+
767788
def lexicographic_rank(self) -> int:
768789
"""Return the lexicographic rank of the cycle decomposition.
769790

symmetria/elements/permutation.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,41 @@ def is_regular(self) -> bool:
877877
cycle_decomposition = self.cycle_decomposition()
878878
return all(len(cycle) == len(cycle_decomposition[0]) for cycle in cycle_decomposition)
879879

880+
def lehmer_code(self) -> List[int]:
881+
"""Return the Lehmer code of the permutation.
882+
883+
Recall that the Lehmer code of a permutation is a sequence that encodes the permutation as a series of integers.
884+
Each integer represents the number of smaller elements to the right of a given element in the permutation.
885+
886+
:return: the Lehmer code of the permutation.
887+
:rtype: List[int]
888+
889+
:example:
890+
>>> from symmetria import Permutation
891+
...
892+
>>> Permutation(1).lehmer_code()
893+
[0]
894+
>>> Permutation(2, 1, 3).lehmer_code()
895+
[1, 0, 0]
896+
>>> Permutation(4, 3, 2, 1).lehmer_code()
897+
[3, 2, 1, 0]
898+
>>> Permutation(4, 1, 3, 2, 7, 6, 5, 8).lehmer_code()
899+
[3, 0, 1, 0, 2, 1, 0, 0]
900+
"""
901+
n = len(self)
902+
lehmer_code = [0] * n
903+
stack = [] # (value, count)
904+
905+
for i in range(n, 0, -1):
906+
count = 0
907+
while stack and stack[-1][0] < self[i]:
908+
_, old_count = stack.pop()
909+
count += 1 + old_count
910+
lehmer_code[i - 1] = count
911+
stack.append((self[i], count))
912+
913+
return lehmer_code
914+
880915
def lexicographic_rank(self) -> int:
881916
"""Return the lexicographic rank of the permutation.
882917

tests/tests_elements/tests_permutation/test_cases.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,17 @@
225225
(Permutation(2, 1), True),
226226
(Permutation(2, 1, 3), False),
227227
]
228+
TEST_LEHMER_CODE = [
229+
(Permutation(1), [0]),
230+
(Permutation(2, 1), [1, 0]),
231+
(Permutation(2, 1, 3), [1, 0, 0]),
232+
(Permutation(1, 2, 3), [0, 0, 0]),
233+
(Permutation(1, 2, 3, 4), [0, 0, 0, 0]),
234+
(Permutation(2, 1, 3, 4), [1, 0, 0, 0]),
235+
(Permutation(4, 3, 2, 1), [3, 2, 1, 0]),
236+
(Permutation(4, 1, 3, 2), [3, 0, 1, 0]),
237+
(Permutation(4, 1, 3, 2, 7, 6, 5, 8), [3, 0, 1, 0, 2, 1, 0, 0]),
238+
]
228239
TEST_LEXICOGRAPHIC_RANK = [
229240
(Permutation(1), 1),
230241
(Permutation(1, 2), 1),

tests/tests_elements/tests_permutation/test_generic_methods.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
TEST_INVERSIONS,
2323
TEST_IS_REGULAR,
2424
TEST_EXCEEDANCES,
25+
TEST_LEHMER_CODE,
2526
TEST_IS_CONJUGATE,
2627
TEST_CYCLE_NOTATION,
2728
TEST_IS_DERANGEMENT,
@@ -257,6 +258,20 @@ def test_is_regular(permutation, expected_value) -> None:
257258
)
258259

259260

261+
@pytest.mark.parametrize(
262+
argnames="permutation, expected_value",
263+
argvalues=TEST_LEHMER_CODE,
264+
ids=[f"{p}.lehmer_code()={m}" for p, m in TEST_LEHMER_CODE],
265+
)
266+
def test_lehmer_core(permutation, expected_value) -> None:
267+
"""Tests for the method `lehmer_code()`."""
268+
_check_values(
269+
expression=f"{permutation.rep()}.lehmer_code()",
270+
evaluation=permutation.lehmer_code(),
271+
expected=expected_value,
272+
)
273+
274+
260275
@pytest.mark.parametrize(
261276
argnames="permutation, expected_value",
262277
argvalues=TEST_LEXICOGRAPHIC_RANK,

0 commit comments

Comments
 (0)