Skip to main content

A Pythonic class for sequences considered circular

Project description

RingSeqPy

PyPI - Version CI Python Version from PEP 621 TOML

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:

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

ring_seq_py-2.1.1.tar.gz (17.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ring_seq_py-2.1.1-py3-none-any.whl (16.0 kB view details)

Uploaded Python 3

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

Hashes for ring_seq_py-2.1.1.tar.gz
Algorithm Hash digest
SHA256 767f52871f0acd8a02e71671f2b81eb74211639729e3dd606bb617aaf1596cbd
MD5 1174bbdbdb11b189da3671dd687f65d7
BLAKE2b-256 5c98fedeb6878a20fb71e4ff68e1a1ae486e2bef5b2b228b06a77d715223d541

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for ring_seq_py-2.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c87632e30f9248dbccc999884cb484299342f3aaddfd5a2d590ac4dc3c50b1d2
MD5 7346c9cd088ae0b99ab4e8e107f57009
BLAKE2b-256 6b7c18c2fc43f745f3a1945da3adf2c7686ceb66a0b0e845ee1a2607d02d56e7

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page