ring-seq
Circular (ring) sequence operations for Rust slices.
Treat any [T], Vec<T>, array, or Box<[T]> as a circular sequence
— the element after the last wraps back to the first.
- Zero dependencies. Zero
unsafe.#![no_std]with a defaultallocfeature. - All transforms return lazy views or iterators; allocation is opt-in.
- Single entry point:
slice.circular()gives you aCircular<T>wrapper that hosts every operation.
Quick start
[]
= "0.3"
use AsCircular;
let r = .circular;
// Indexing wraps in both directions
assert_eq!;
assert_eq!;
// Reindexed views — no allocation
let rotated: = r.rotate_right.iter.copied.collect;
assert_eq!;
// Comparison up to rotation / reflection
assert!;
assert!;
// Canonical (necklace) form — Booth's O(n)
assert_eq!;
// Lazy iterators of views compose naturally
let firsts: = r.rotations.map.collect;
assert_eq!;
// Symmetry detection
assert_eq!;
Operations on Circular<T>
Indexing & iteration
| Method | Returns | Description |
|---|---|---|
apply(i) |
&T |
Element at circular index (panics if empty) |
index_from(i) |
usize |
Normalize a circular index to [0, len) |
iter() |
CircularIter |
Walk the view's elements (lazy) |
Reindexed views (lazy)
| Method | Returns | Description |
|---|---|---|
start_at(i) |
Circular |
View starts at circular index i |
rotate_left(step) |
Circular |
Shift left by step (negative = right) |
rotate_right(step) |
Circular |
Shift right by step (negative = left) |
reflect_at(i) |
Circular |
Reflect around index i |
Bounded iteration (lazy)
| Method | Returns | Description |
|---|---|---|
slice(from, to) |
CircularIter |
max(to - from, 0) elements, wrapping |
take_while(pred, from) |
impl Iterator |
Prefix satisfying pred (≤ one lap) |
drop_while(pred, from) |
impl Iterator |
Remainder after the prefix |
enumerate(from) |
Enumerate |
(&T, ring_index) pairs |
Iterators of views (lazy)
| Method | Yields | Description |
|---|---|---|
rotations() |
Circular × n |
Every rotation |
reflections() |
Circular × 2 |
Original + reflect_at(0) |
reversions() |
Circular × 2 |
Original + reverse |
rotations_and_reflections() |
Circular × 2n |
All dihedral variants |
windows(size) |
CircularIter × n |
Sliding windows, step 1 |
chunks(size) |
CircularIter × ceil(n/size) |
Non-overlapping chunks |
Comparison
| Method | Description |
|---|---|
is_rotation_of(other) |
Same elements, possibly rotated? |
is_reflection_of(other) |
Equals self or self.reflect_at(0) |
is_reversion_of(other) |
Equals self or its reverse |
is_rotation_or_reflection_of(other) |
Any dihedral variant |
rotation_offset(other) |
Some(k) where self.start_at(k) == other |
hamming_distance(other) |
Positional mismatches |
min_rotational_hamming_distance(other) |
Minimum over all rotations |
contains_slice(needle) |
Does needle appear circularly? |
index_of_slice(needle, from) |
First circular index where needle matches |
Necklace & symmetry
| Method | Returns | Notes |
|---|---|---|
canonical_index() |
usize |
Index of lex-smallest rotation (Booth's O(n); alloc) |
canonical() |
Vec<T> |
Lex-smallest rotation (alloc) |
bracelet() |
Vec<T> |
Lex-smallest under rotation + reflection (alloc) |
rotational_symmetry() |
usize |
Order of rotational symmetry |
symmetry() |
usize |
Number of reflectional axes |
symmetry_indices() |
Vec<usize> |
Shifts where the view equals its reverse (alloc) |
reflectional_symmetry_axes() |
Vec<(AxisLocation, AxisLocation)> |
Full axis geometry (alloc) |
Materialization
| Method | Returns | Notes |
|---|---|---|
to_vec() |
Vec<T> |
Materialize the view (alloc, T: Clone) |
no_std
[]
= { = "0.3", = false }
Disabling the default alloc feature drops the methods that return owned
collections (canonical, bracelet, symmetry_indices,
reflectional_symmetry_axes, to_vec) and canonical_index (Booth's
algorithm needs an internal Vec). Everything else — the Circular
wrapper, every reindexed-view method, every iterator — depends only on
core.
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
Minimum Rust version
1.63
Other languages
The same library, adapted for the specific idiom, is available also for:
- Python — ring-seq-py
- Scala — ring-seq
License
Licensed under either of
at your option.