{"id":49037138,"url":"https://github.com/scala-tessella/ring-seq-py","last_synced_at":"2026-04-19T12:02:02.539Z","repository":{"id":219071536,"uuid":"746169325","full_name":"scala-tessella/ring-seq-py","owner":"scala-tessella","description":"Extends Python list, str and tuple with ring (circular) methods ","archived":false,"fork":false,"pushed_at":"2024-01-31T07:24:57.000Z","size":668,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-04-17T04:17:31.888Z","etag":null,"topics":["circular","collections","python","ring"],"latest_commit_sha":null,"homepage":"https://scala-tessella.github.io/ring-seq-py","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/scala-tessella.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2024-01-21T09:19:00.000Z","updated_at":"2024-01-25T11:31:24.000Z","dependencies_parsed_at":"2024-01-31T08:31:41.512Z","dependency_job_id":"93bdd1fe-50a4-4b05-ac29-0a09331c2bd1","html_url":"https://github.com/scala-tessella/ring-seq-py","commit_stats":null,"previous_names":["scala-tessella/ring-seq-py"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/scala-tessella/ring-seq-py","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scala-tessella%2Fring-seq-py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scala-tessella%2Fring-seq-py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scala-tessella%2Fring-seq-py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scala-tessella%2Fring-seq-py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scala-tessella","download_url":"https://codeload.github.com/scala-tessella/ring-seq-py/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scala-tessella%2Fring-seq-py/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32005831,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T20:23:30.271Z","status":"online","status_checked_at":"2026-04-19T02:00:07.110Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["circular","collections","python","ring"],"created_at":"2026-04-19T12:02:01.330Z","updated_at":"2026-04-19T12:02:02.508Z","avatar_url":"https://github.com/scala-tessella.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# **RingSeqPy**\n\n[![PyPI - Version](https://img.shields.io/pypi/v/ring-seq-py?style=plastic\u0026logo=python\u0026logoColor=%234B8BBE\u0026link=https%3A%2F%2Fpypi.org%2Fproject%2Fring-seq-py%2F)](https://pypi.python.org/pypi/ring-seq-py)\n[![CI](https://github.com/scala-tessella/ring-seq-py/actions/workflows/python-package.yml/badge.svg)](https://github.com/scala-tessella/ring-seq-py/actions/workflows/python-package.yml)\n[![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fscala-tessella%2Fring-seq-py%2Fmaster%2Fpyproject.toml\u0026style=plastic\u0026logo=python\u0026logoColor=%23FFD43B)](https://pypi.python.org/pypi/ring-seq-py)\n\nA Pythonic class for sequences considered [**circular**](https://scala-tessella.github.io/ring-seq-py/what-is/) —\nwhere the element after the last wraps back to the first.\n\n`RingSeqPy` is a small, zero-dependency library exposing a single generic\n`RingSeq[T]` class that wraps any `Iterable[T]`, stores it internally as an\nimmutable tuple, and implements the Python `Sequence` protocol circularly.\nInstances are hashable, orderable, and interoperable: `RingSeq('ABC')`,\n`RingSeq(['A','B','C'])`, and `RingSeq(('A','B','C'))` all compare equal.\n\n## Installation\n\n```\npip install ring-seq-py\n```\n\nWorking for Python `3.10` and above.\n\n## Quick start\n\n```pycon\n\u003e\u003e\u003e from ring_seq import RingSeq\n\n\u003e\u003e\u003e # Indexing wraps around\n\u003e\u003e\u003e RingSeq([10, 20, 30])[4]\n20\n\n\u003e\u003e\u003e # Slicing wraps around too; transformations return a new RingSeq,\n\u003e\u003e\u003e # unwrap with .to_list() / .to_tuple() / .to_str()\n\u003e\u003e\u003e RingSeq([0, 1, 2]).rotate_right(1).to_list()\n[2, 0, 1]\n\n\u003e\u003e\u003e # Comparison up to rotation\n\u003e\u003e\u003e RingSeq([0, 1, 2]).is_rotation_of([2, 0, 1])\nTrue\n\n\u003e\u003e\u003e # Canonical (necklace) form for deduplication\n\u003e\u003e\u003e RingSeq([2, 0, 1]).canonical().to_list()\n[0, 1, 2]\n\n\u003e\u003e\u003e # Symmetry detection\n\u003e\u003e\u003e RingSeq([0, 1, 0, 1]).rotational_symmetry()\n2\n\n\u003e\u003e\u003e # Strings work naturally; to_str() rejoins\n\u003e\u003e\u003e RingSeq('RING').rotate_right(1).to_str()\n'GRIN'\n```\n\n## Operations\n\n### Native sequence protocol (circular)\n\n| Method | Description |\n|---|---|\n| `rs[i]` | Element at circular index (any integer wraps) |\n| `rs[i:j]`, `rs[i:j:k]` | Circular slice (can exceed ring length) |\n| `len(rs)`, `iter(rs)`, `x in rs` | Standard protocol, no surprises |\n| `rs == other`, `hash(rs)`, `min(rings)` | Positional equality; lexicographic ordering |\n| `index(value, start=0, stop=None)` | Circular first-occurrence lookup |\n\n### Unwrap\n\n| Method | Description |\n|---|---|\n| `to_list()` | Return a new `list` |\n| `to_tuple()` | Return the internal tuple |\n| `to_str(sep='')` | Join elements into a `str` |\n\n### Indexing helper\n\n| Method | Description |\n|---|---|\n| `index_from(i)` | Normalize a circular index to `[0, len)` |\n\n### Transforming\n\n| Method | Description |\n|---|---|\n| `rotate_right(step)` | Rotate right by `step` (negative = left) |\n| `rotate_left(step)` | Rotate left by `step` (negative = right) |\n| `start_at(i)` | Rotate so circular index `i` is first |\n| `reflect_at(i=0)` | Reflect so circular index `i` is the axis head |\n\n### Slicing primitives\n\n| Method | Description |\n|---|---|\n| `take_while(p, from_=0)` | Longest prefix from `from_` satisfying `p` |\n| `drop_while(p, from_=0)` | Remainder after that prefix |\n| `span(p, from_=0)` | `(take_while, drop_while)` in one call |\n\n### Iterating\n\n| Method | Description |\n|---|---|\n| `rotations()` | All `n` rotations (lazy) |\n| `reflections()` | Original + reflection (lazy) |\n| `reversions()` | Original + reversal (lazy) |\n| `rotations_and_reflections()` | All `2n` variants (lazy) |\n| `grouped(size)` | `n` fixed-size circular groups |\n| `zip_with_index(from_=0)` | Elements paired with their circular indices |\n\n### Comparing\n\n| Method | Description |\n|---|---|\n| `is_rotation_of(that)` | Same elements, possibly rotated? |\n| `is_reflection_of(that)` | Same elements, possibly reflected? |\n| `is_reversion_of(that)` | Same elements, possibly reversed? |\n| `is_rotation_or_reflection_of(that)` | Either of the above? |\n| `align_to(that)` | `k` such that `start_at(k) == that`, or `None` |\n| `hamming_distance(that)` | Positional mismatches (same size required) |\n| `min_rotational_hamming_distance(that)` | Minimum distance over all rotations |\n\n### Necklace\n\n| Method | Description |\n|---|---|\n| `canonical_index()` | Index of lex-smallest rotation (Booth's *O(n)*) |\n| `canonical()` | Lex-smallest rotation (necklace form) |\n| `bracelet()` | Lex-smallest under rotation *and* reflection |\n\n### Symmetry\n\n| Method | Description |\n|---|---|\n| `rotational_symmetry()` | Order of rotational symmetry |\n| `symmetry_indices()` | Shifts where the ring equals its reversal rotated left |\n| `reflectional_symmetry_axes()` | Full axis geometry (`Vertex` / `Edge` pairs) |\n| `symmetry()` | Number of reflectional symmetry axes |\n\n## Naming convention\n\n`RingSeq` subclasses `collections.abc.Sequence`, so native Python protocols\ndo the work where they map cleanly — `rs[i]` for indexing, `rs[i:j]` for\nslicing, `x in rs` for containment. A few methods are deliberately renamed\nfrom the Scala/Rust counterparts to be Pythonic:\n\n| This library | Elsewhere |\n|---|---|\n| `rs[i]` | `apply_o`  |\n| `rs[i:j]`, `rs[i:j:k]` | `slice_o` |\n| `index` | `index_of_slice` |\n| `grouped` | `circular_chunks` |\n| `zip_with_index` | `circular_enumerate` |\n\n`RingSeq.index(value, start=0, stop=None)` overrides `Sequence.index` with\n*circular* semantics: without a `stop` it searches one full revolution and\nreturns an index in `[0, len)`.\n\n## Use cases\n\n- **Bioinformatics** — circular DNA/RNA sequence alignment and comparison\n- **Graphics** — polygon vertex manipulation, closed curve operations\n- **Procedural generation** — tile rings, symmetry-aware pattern generation\n- **Music theory** — pitch-class sets, chord inversions\n- **Combinatorics** — necklace/bracelet enumeration, Burnside's lemma\n- **Embedded / robotics** — circular sensor arrays, rotary encoder positions\n\n## Other languages\n\nThe same library, adapted for the specific idiom, is available also for:\n- Scala — [ring-seq](https://github.com/scala-tessella/ring-seq)\n- Rust — [ring-seq-rs](https://github.com/scala-tessella/ring-seq-rs)\n\n## License\n\nLicensed under either of\n\n- [Apache License, Version 2.0](LICENSE-APACHE)\n- [MIT License](LICENSE-MIT)\n\nat your option.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscala-tessella%2Fring-seq-py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscala-tessella%2Fring-seq-py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscala-tessella%2Fring-seq-py/lists"}