ring-seq
Circular (ring) sequence operations for Rust slices.
ring-seq extends [T] — and by Deref coercion Vec<T>, arrays, and
Box<[T]> — with operations that treat the sequence as circular: the
element after the last wraps back to the first.
Zero dependencies. Zero unsafe. #[must_use] on every method that returns a
value.
This is the Rust port of the Scala ring-seq library.
Quick start
Add to your Cargo.toml:
[]
= "0.1"
Then import the trait and call methods on any slice:
use RingSeq;
// Indexing wraps around
assert_eq!;
// Rotation produces a new Vec
assert_eq!;
// Comparison up to rotation
assert!;
// Canonical (necklace) form for deduplication
assert_eq!;
// Symmetry detection
assert_eq!;
Operations
Indexing
| Method | Description |
|---|---|
index_from(i) |
Normalize a circular index to [0, len) |
apply_o(i) |
Element at circular index (panics if empty) |
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 index i is first |
reflect_at(i) |
Reflect and rotate so index i is first |
Slicing
| Method | Description |
|---|---|
slice_o(from, to) |
Circular interval (can exceed ring length) |
contains_slice(s) |
Does the ring contain s circularly? |
index_of_slice(s, from) |
First circular position of s |
last_index_of_slice(s, end) |
Last circular position of s |
segment_length(pred, from) |
Length of prefix satisfying pred |
take_while(pred, from) |
Prefix satisfying pred |
drop_while(pred, from) |
Remainder after prefix |
span(pred, from) |
(take_while, drop_while) in one pass |
Iterating
| Method | Description |
|---|---|
rotations() |
All n rotations (lazy) |
reflections() |
Original + reflection (lazy) |
reversions() |
Original + reversal (lazy) |
rotations_and_reflections() |
All 2n variants (lazy) |
circular_windows(size, step) |
Sliding windows wrapping around |
circular_chunks(size) |
Fixed-size circular groups |
circular_enumerate(from) |
Elements paired with 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? |
rotation_offset(that) |
Some(k) where start_at(k) == that |
hamming_distance(that) |
Positional mismatches |
min_rotational_hamming_distance(that) |
Minimum 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 + reflection |
Symmetry
| Method | Description |
|---|---|
rotational_symmetry() |
Order of rotational symmetry |
symmetry_indices() |
Shift values for reflectional symmetry |
reflectional_symmetry_axes() |
Full axis geometry (Vertex / Edge) |
symmetry() |
Number of reflectional symmetry axes |
Naming convention
Every method on RingSeq is circular by definition, so most use plain
Rust-idiomatic names. A few carry a distinguishing name to avoid shadowing
standard-library methods:
| This crate | Standard library |
|---|---|
apply_o |
[] indexing |
slice_o |
[a..b] slicing |
circular_windows |
[T]::windows |
circular_chunks |
[T]::chunks |
circular_enumerate |
Iterator::enumerate |
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
License
Licensed under either of
at your option.