pub const MAX_SEQ_BITS: u32 = 31;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SeqSpace {
bits: u32,
}
impl SeqSpace {
#[must_use]
pub const fn new(bits: u32) -> Self {
assert!(
bits >= 1 && bits <= MAX_SEQ_BITS,
"SeqSpace bits out of range"
);
Self { bits }
}
#[must_use]
pub const fn modulus(self) -> u64 {
1u64 << self.bits
}
#[must_use]
pub const fn mask(self) -> u32 {
(self.modulus() - 1) as u32
}
#[must_use]
pub const fn add(self, seq: u32, n: u32) -> u32 {
((seq as u64 + n as u64) % self.modulus()) as u32
}
#[must_use]
pub const fn diff(self, a: u32, b: u32) -> i32 {
let modulus = self.modulus() as i64;
let half = modulus / 2;
let raw = ((a & self.mask()) as i64 - (b & self.mask()) as i64).rem_euclid(modulus);
(if raw > half { raw - modulus } else { raw }) as i32
}
#[must_use]
pub const fn lt(self, a: u32, b: u32) -> bool {
self.diff(a, b) < 0
}
#[must_use]
pub const fn leq(self, a: u32, b: u32) -> bool {
self.diff(a, b) <= 0
}
#[must_use]
pub const fn gt(self, a: u32, b: u32) -> bool {
self.diff(a, b) > 0
}
#[must_use]
pub const fn geq(self, a: u32, b: u32) -> bool {
self.diff(a, b) >= 0
}
#[must_use]
pub const fn in_closed_range(self, seq: u32, first: u32, last: u32) -> bool {
let span = self.diff(last, first);
if span < 0 {
return false;
}
let offset = self.diff(seq, first);
offset >= 0 && offset <= span
}
}
#[cfg(test)]
mod tests {
use super::*;
const S16: SeqSpace = SeqSpace::new(16);
const S31: SeqSpace = SeqSpace::new(31);
#[test]
fn wraps_at_the_space_boundary() {
assert_eq!(S16.add(0xFFFF, 1), 0);
assert_eq!(S16.add(0xFFFF, 5), 4);
assert_eq!(S31.add(0x7FFF_FFFF, 1), 0);
assert_eq!(S31.add(u32::MAX, u32::MAX), 0x7FFF_FFFE);
}
#[test]
fn ordering_is_circular_and_antisymmetric() {
assert!(S16.lt(0xFFFF, 0) && S16.gt(0, 0xFFFF));
assert_eq!(S16.diff(0, 0xFFFF), 1);
assert!(S31.lt(0x7FFF_FFFF, 0) && S31.geq(5, 5) && S31.leq(5, 5));
for (a, b) in [(0u32, 0u32), (5, 100), (0xFFFF, 3), (12345, 12340)] {
assert_eq!(S16.diff(a, b), -S16.diff(b, a));
}
assert_eq!(S16.diff(0x8000, 0), 0x8000);
assert_eq!(S16.diff(0, 0x8000), 0x8000);
}
#[test]
fn closed_range_walks_forward_across_the_wrap() {
assert!(S16.in_closed_range(0xFFFE, 0xFFFD, 1));
assert!(S16.in_closed_range(0, 0xFFFD, 1));
assert!(!S16.in_closed_range(2, 0xFFFD, 1));
assert!(!S16.in_closed_range(5, 10, 3));
}
#[test]
fn sixteen_bit_diff_matches_the_pre_consolidation_formula() {
fn reference(a: u16, b: u16) -> i32 {
let raw = (i32::from(a) - i32::from(b)).rem_euclid(1 << 16);
if raw > 1 << 15 { raw - (1 << 16) } else { raw }
}
for a in (0..=u16::MAX).step_by(257) {
for b in (0..=u16::MAX).step_by(251) {
assert_eq!(S16.diff(u32::from(a), u32::from(b)), reference(a, b));
}
}
}
#[test]
fn diff_masks_out_of_range_inputs() {
assert_eq!(S31.diff(0x8000_0001, 0), 1);
}
}