int-interval 0.8.0

A small, no_std half-open interval algebra library for primitive integer types.
Documentation
use std::{ops::Range, sync::OnceLock};

use divan::{Bencher, black_box};
use int_interval::U64CO;
use rust_intervals::Interval as Ri;

const CASES: usize = 16_384;
const DOMAIN: u64 = 1 << 20;
const MAX_LEN: u64 = 1 << 10;

static SPANS: OnceLock<Box<[(u64, u64)]>> = OnceLock::new();

fn main() {
    divan::main();
}

fn spans() -> &'static [(u64, u64)] {
    SPANS.get_or_init(|| {
        let mut rng = XorShift64::new(0xD1CE_BA5E_F00D_1234);

        (0..CASES)
            .map(|_| gen_non_empty_span(&mut rng))
            .collect::<Vec<_>>()
            .into_boxed_slice()
    })
}

#[derive(Clone, Copy)]
struct XorShift64(u64);

impl XorShift64 {
    #[inline]
    const fn new(seed: u64) -> Self {
        Self(seed)
    }

    #[inline]
    fn next(&mut self) -> u64 {
        let mut x = self.0;
        x ^= x << 13;
        x ^= x >> 7;
        x ^= x << 17;
        self.0 = x;
        x
    }
}

#[inline]
fn gen_non_empty_span(rng: &mut XorShift64) -> (u64, u64) {
    let start = rng.next() % (DOMAIN - MAX_LEN - 1);
    let len = (rng.next() % MAX_LEN) + 1;
    (start, start + len)
}

#[inline]
fn mix(x: u64) -> u64 {
    x.wrapping_mul(0x9E37_79B9_7F4A_7C15).rotate_left(17)
}

#[inline]
fn digest_range(r: &Range<u64>) -> u64 {
    mix(r.start) ^ mix(r.end)
}

#[inline]
fn digest_co(x: U64CO) -> u64 {
    mix(x.start()) ^ mix(x.end_excl())
}

#[inline]
fn digest_ri(x: &Ri<u64>) -> u64 {
    let lo = x.lower().copied().unwrap_or(0);
    let hi = x.upper().copied().unwrap_or(0);
    let li = x.lower_inclusive() as u64;
    let ui = x.upper_inclusive() as u64;

    mix(lo) ^ mix(hi).rotate_left(5) ^ mix((li << 1) | ui)
}

#[divan::bench]
fn co(bencher: Bencher) {
    let spans = spans();

    bencher.bench_local(|| {
        spans.iter().fold(0_u64, |acc, &(start, end_excl)| {
            let x = U64CO::try_new(black_box(start), black_box(end_excl)).unwrap();
            acc ^ digest_co(black_box(x))
        })
    });
}

#[divan::bench]
fn std_range(bencher: Bencher) {
    let spans = spans();

    bencher.bench_local(|| {
        spans.iter().fold(0_u64, |acc, &(start, end_excl)| {
            let x = black_box(start)..black_box(end_excl);
            acc ^ digest_range(black_box(&x))
        })
    });
}

#[divan::bench]
fn rust_intervals(bencher: Bencher) {
    let spans = spans();

    bencher.bench_local(|| {
        spans.iter().fold(0_u64, |acc, &(start, end_excl)| {
            let x = Ri::new_closed_open(black_box(start), black_box(end_excl));
            acc ^ digest_ri(black_box(&x))
        })
    });
}