A Pythonic class for sequences considered circular
Project description
RingSeqPy
A Pythonic class for sequences considered circular — where the element after the last wraps back to the first.
RingSeqPy is a small, zero-dependency library exposing a single generic
RingSeq[T] class that wraps any Iterable[T], stores it internally as an
immutable tuple, and implements the Python Sequence protocol circularly.
Instances are hashable, orderable, and interoperable: RingSeq('ABC'),
RingSeq(['A','B','C']), and RingSeq(('A','B','C')) all compare equal.
Installation
pip install ring-seq-py
Working for Python 3.10 and above.
Quick start
>>> from ring_seq import RingSeq
>>> # Indexing wraps around
>>> RingSeq([10, 20, 30])[4]
20
>>> # Slicing wraps around too; transformations return a new RingSeq,
>>> # unwrap with .to_list() / .to_tuple() / .to_str()
>>> RingSeq([0, 1, 2]).rotate_right(1).to_list()
[2, 0, 1]
>>> # Comparison up to rotation
>>> RingSeq([0, 1, 2]).is_rotation_of([2, 0, 1])
True
>>> # Canonical (necklace) form for deduplication
>>> RingSeq([2, 0, 1]).canonical().to_list()
[0, 1, 2]
>>> # Symmetry detection
>>> RingSeq([0, 1, 0, 1]).rotational_symmetry()
2
>>> # Strings work naturally; to_str() rejoins
>>> RingSeq('RING').rotate_right(1).to_str()
'GRIN'
Operations
Native sequence protocol (circular)
| Method | Description |
|---|---|
rs[i] |
Element at circular index (any integer wraps) |
rs[i:j], rs[i:j:k] |
Circular slice (can exceed ring length) |
len(rs), iter(rs), x in rs |
Standard protocol, no surprises |
rs == other, hash(rs), min(rings) |
Positional equality; lexicographic ordering |
index(value, start=0, stop=None) |
Circular first-occurrence lookup |
Unwrap
| Method | Description |
|---|---|
to_list() |
Return a new list |
to_tuple() |
Return the internal tuple |
to_str(sep='') |
Join elements into a str |
Indexing helper
| Method | Description |
|---|---|
index_from(i) |
Normalize a circular index to [0, len) |
Transforming
| Method | Description |
|---|---|
rotate_right(step) |
Rotate right by step (negative = left) |
rotate_left(step) |
Rotate left by step (negative = right) |
start_at(i) |
Rotate so circular index i is first |
reflect_at(i=0) |
Reflect so circular index i is the axis head |
Slicing primitives
| Method | Description |
|---|---|
take_while(p, from_=0) |
Longest prefix from from_ satisfying p |
drop_while(p, from_=0) |
Remainder after that prefix |
span(p, from_=0) |
(take_while, drop_while) in one call |
Iterating
| Method | Description |
|---|---|
rotations() |
All n rotations (lazy) |
reflections() |
Original + reflection (lazy) |
reversions() |
Original + reversal (lazy) |
rotations_and_reflections() |
All 2n variants (lazy) |
grouped(size) |
ceil(n / size) fixed-size blocks, last one wraps the seam |
zip_with_index(from_=0) |
Elements paired with their circular indices |
Comparing
| Method | Description |
|---|---|
is_rotation_of(that) |
Same elements, possibly rotated? |
is_reflection_of(that) |
Same elements, possibly reflected? |
is_reversion_of(that) |
Same elements, possibly reversed? |
is_rotation_or_reflection_of(that) |
Either of the above? |
align_to(that) |
k such that start_at(k) == that, or None |
hamming_distance(that) |
Positional mismatches (same size required) |
min_rotational_hamming_distance(that) |
Minimum distance over all rotations |
Necklace
| Method | Description |
|---|---|
canonical_index() |
Index of lex-smallest rotation (Booth's O(n)) |
canonical() |
Lex-smallest rotation (necklace form) |
bracelet() |
Lex-smallest under rotation and reflection |
Symmetry
| Method | Description |
|---|---|
rotational_symmetry() |
Order of rotational symmetry |
symmetry_indices() |
Shifts where the ring equals its reversal rotated left |
reflectional_symmetry_axes() |
Full axis geometry (Vertex / Edge pairs) |
symmetry() |
Number of reflectional symmetry axes |
Naming convention
RingSeq subclasses collections.abc.Sequence, so native Python protocols
do the work where they map cleanly — rs[i] for indexing, rs[i:j] for
slicing, x in rs for containment. A few methods are deliberately renamed
from the Scala/Rust counterparts to be Pythonic:
| This library | Elsewhere |
|---|---|
rs[i] |
apply_o |
rs[i:j], rs[i:j:k] |
slice_o |
index |
index_of_slice |
grouped |
circular_chunks |
zip_with_index |
circular_enumerate |
RingSeq.index(value, start=0, stop=None) overrides Sequence.index with
circular semantics: without a stop it searches one full revolution and
returns an index in [0, len).
Use cases
- Bioinformatics — circular DNA/RNA sequence alignment and comparison
- Graphics — polygon vertex manipulation, closed curve operations
- Procedural generation — tile rings, symmetry-aware pattern generation
- Music theory — pitch-class sets, chord inversions
- Combinatorics — necklace/bracelet enumeration, Burnside's lemma
- Embedded / robotics — circular sensor arrays, rotary encoder positions
Other languages
The same library, adapted for the specific idiom, is available also for:
- Scala — ring-seq
- Rust — ring-seq-rs
License
Licensed under either of
at your option.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ring_seq_py-2.1.1.tar.gz.
File metadata
- Download URL: ring_seq_py-2.1.1.tar.gz
- Upload date:
- Size: 17.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
767f52871f0acd8a02e71671f2b81eb74211639729e3dd606bb617aaf1596cbd
|
|
| MD5 |
1174bbdbdb11b189da3671dd687f65d7
|
|
| BLAKE2b-256 |
5c98fedeb6878a20fb71e4ff68e1a1ae486e2bef5b2b228b06a77d715223d541
|
Provenance
The following attestation bundles were made for ring_seq_py-2.1.1.tar.gz:
Publisher:
publish.yml on scala-tessella/ring-seq-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ring_seq_py-2.1.1.tar.gz -
Subject digest:
767f52871f0acd8a02e71671f2b81eb74211639729e3dd606bb617aaf1596cbd - Sigstore transparency entry: 1342670562
- Sigstore integration time:
-
Permalink:
scala-tessella/ring-seq-py@5bb6bd616f7d2f8935018447a628ef524ee3444a -
Branch / Tag:
refs/tags/v2.1.1 - Owner: https://github.com/scala-tessella
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5bb6bd616f7d2f8935018447a628ef524ee3444a -
Trigger Event:
push
-
Statement type:
File details
Details for the file ring_seq_py-2.1.1-py3-none-any.whl.
File metadata
- Download URL: ring_seq_py-2.1.1-py3-none-any.whl
- Upload date:
- Size: 16.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c87632e30f9248dbccc999884cb484299342f3aaddfd5a2d590ac4dc3c50b1d2
|
|
| MD5 |
7346c9cd088ae0b99ab4e8e107f57009
|
|
| BLAKE2b-256 |
6b7c18c2fc43f745f3a1945da3adf2c7686ceb66a0b0e845ee1a2607d02d56e7
|
Provenance
The following attestation bundles were made for ring_seq_py-2.1.1-py3-none-any.whl:
Publisher:
publish.yml on scala-tessella/ring-seq-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ring_seq_py-2.1.1-py3-none-any.whl -
Subject digest:
c87632e30f9248dbccc999884cb484299342f3aaddfd5a2d590ac4dc3c50b1d2 - Sigstore transparency entry: 1342670567
- Sigstore integration time:
-
Permalink:
scala-tessella/ring-seq-py@5bb6bd616f7d2f8935018447a628ef524ee3444a -
Branch / Tag:
refs/tags/v2.1.1 - Owner: https://github.com/scala-tessella
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5bb6bd616f7d2f8935018447a628ef524ee3444a -
Trigger Event:
push
-
Statement type: