Lazy, pull-based composition of mathematical interval sets — no_std-compatible, allocation-free.
An Intervals is an ordered, non-overlapping set of Intervals. Sets compose via set operations (intersection &, union |) and transforms (map, IntervalShift) without intermediate allocations. Each Interval carries a value, so composition can also be read as joining attributed segments.
Use odsek when you need to combine ordered interval streams while preserving the data attached to each segment: calendars, availability windows, timeline annotations, numeric domains, or other range-like datasets. It is designed for libraries and embedded/no-heap contexts that want lazy composition instead of building intermediate Vecs.
no_std— no heap allocation in the core API.- Open and closed endpoints (
EndpointOC::{Open, Closed}), or symmetric[a, b)endpoints (EndpointSymmetric). - Pull-based:
head(pos)returns the next interval whose right endpoint is> pos. Random-access friendly. - Composable:
&(intersection) yields tupled values,|(union) yields(Option<A>, Option<B>). - Built-in combinators:
IntervalsSingle,IntervalsFromArray,IntervalShift,IntervalsCache,IntervalsMap.
Build interval sets with IntervalsFromArray and compose them. Each segment in the result carries the values from whichever input(s) covered it.
use odsek::*;
fn cc<V>(a: i32, b: i32, v: V) -> Interval<EndpointOC<i32>, V> {
Interval::new(EndpointOC::Closed(a), EndpointOC::Closed(b), v)
}
fn co<V>(a: i32, b: i32, v: V) -> Interval<EndpointOC<i32>, V> {
Interval::new(EndpointOC::Closed(a), EndpointOC::Open(b), v)
}
fn oc<V>(a: i32, b: i32, v: V) -> Interval<EndpointOC<i32>, V> {
Interval::new(EndpointOC::Open(a), EndpointOC::Closed(b), v)
}
fn oo<V>(a: i32, b: i32, v: V) -> Interval<EndpointOC<i32>, V> {
Interval::new(EndpointOC::Open(a), EndpointOC::Open(b), v)
}
// A = { [1,3] "A1", [4,7) "A2", (9,11] "A3" }
let va = [cc(1, 3, "A1"), co(4, 7, "A2"), oc(9, 11, "A3")];
// B = { (2,6) "B1", [6,12] "B2", [12,14] "B3" }
let vb = [oo(2, 6, "B1"), cc(6, 12, "B2"), cc(12, 14, "B3")];
let ia = IntervalsFromArray::new(&va);
let ib = IntervalsFromArray::new(&vb);
// Intersection: four overlap segments, each tagged with the contributing pair.
let intersection = (ia & ib).into_iter().collect::<Vec<_>>();
assert_eq!(
intersection,
vec![
oc(2, 3, ("A1", "B1")),
co(4, 6, ("A2", "B1")),
co(6, 7, ("A2", "B2")),
oc(9, 11, ("A3", "B2")),
],
);More examples in examples/: intersection.rs, union.rs, shift.rs. Run with cargo run --example intersection.
EndpointOC::Closed(x) includes x; EndpointOC::Open(x) excludes it. When a boundary is reused as the end of one segment and the start of another, the crate toggles open/closed status so adjacent output intervals do not double-count the same boundary. EndpointSymmetric(x) is simpler: left endpoints are treated as closed and right endpoints as open, so intervals follow the standard [a, b) convention.
operators(default) — enables the&/|operator overloads. Disable with--no-default-featuresto use the explicitand(a, b)/or(a, b)free functions instead.
The crate is #![no_std] outside of tests. Tests opt back into std for the convenience of Vec and println!.
Rust 1.70.
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.