use super::*;
mod about_start_len {
use super::*;
mod unit_tests {
use super::*;
const MIN: u64 = u64::MIN;
const MAX: u64 = u64::MAX;
const ONE: u64 = 1;
fn span(lo: u64, hi: u64) -> U64CO {
U64CO::try_new(lo, hi).unwrap()
}
#[test]
fn checked_from_start_len_basic() {
let iv = U64CO::checked_from_start_len(10, 5).unwrap();
assert_eq!(iv, span(10, 15));
assert_eq!(iv.start(), 10);
assert_eq!(iv.end_excl(), 15);
assert_eq!(iv.len(), 5);
assert!(U64CO::checked_from_start_len(10, 0).is_none());
}
#[test]
fn checked_from_start_len_accepts_max_end_excl() {
let len = 5;
let start = MAX - len;
let iv = U64CO::checked_from_start_len(start, len).unwrap();
assert_eq!(iv, span(start, MAX));
assert_eq!(iv.start(), start);
assert_eq!(iv.end_excl(), MAX);
assert_eq!(iv.len(), len);
}
#[test]
fn checked_from_start_len_rejects_overflow() {
assert!(U64CO::checked_from_start_len(MAX, ONE).is_none());
assert!(U64CO::checked_from_start_len(MAX - ONE, 2).is_none());
assert!(U64CO::checked_from_start_len(MAX - 5, 6).is_none());
}
#[test]
fn saturating_from_start_len_basic() {
let iv = U64CO::saturating_from_start_len(10, 5).unwrap();
assert_eq!(iv, span(10, 15));
assert_eq!(iv.start(), 10);
assert_eq!(iv.end_excl(), 15);
assert_eq!(iv.len(), 5);
assert!(U64CO::saturating_from_start_len(10, 0).is_none());
}
#[test]
fn saturating_from_start_len_accepts_exact_max_end_excl() {
let len = 5;
let start = MAX - len;
let iv = U64CO::saturating_from_start_len(start, len).unwrap();
assert_eq!(iv, span(start, MAX));
assert_eq!(iv.start(), start);
assert_eq!(iv.end_excl(), MAX);
assert_eq!(iv.len(), len);
}
#[test]
fn saturating_from_start_len_clamps_end() {
let exact_len = 5;
let requested_len = exact_len + ONE;
let start = MAX - exact_len;
let iv = U64CO::saturating_from_start_len(start, requested_len).unwrap();
assert_eq!(iv, span(start, MAX));
assert_eq!(iv.start(), start);
assert_eq!(iv.end_excl(), MAX);
assert_eq!(iv.len(), exact_len);
assert!(iv.len() < requested_len);
}
#[test]
fn saturating_from_start_len_rejects_empty_after_saturation() {
assert!(U64CO::saturating_from_start_len(MAX, ONE).is_none());
assert!(U64CO::saturating_from_start_len(MAX, MAX).is_none());
}
#[test]
fn checked_from_start_len_reconstructs_existing_interval() {
let cases = [
(MIN, MIN + ONE),
(MIN, 10),
(10, 20),
(MAX - 10, MAX),
(MAX - ONE, MAX),
];
for (start, end_excl) in cases {
let base = span(start, end_excl);
let rebuilt = U64CO::checked_from_start_len(start, base.len()).unwrap();
assert_eq!(rebuilt, base, "case [{start}, {end_excl})");
}
}
}
mod prop_tests {
use std::{vec, vec::Vec};
use super::*;
use proptest::prelude::*;
fn edge_values() -> Vec<u64> {
let mut v = vec![
u64::MIN,
u64::MIN.saturating_add(1),
1,
u64::MAX.saturating_sub(1),
u64::MAX,
];
v.sort_unstable();
v.dedup();
v
}
fn mixed_scalar() -> impl Strategy<Value = u64> {
prop_oneof! {
3 => prop::sample::select(edge_values()),
7 => any::<u64>(),
}
}
proptest! {
#[test]
fn checked_from_start_len_matches_checked_add(
start in mixed_scalar(),
len in mixed_scalar(),
) {
let got = U64CO::checked_from_start_len(start, len);
let expected = if len == 0 {
None
} else {
start
.checked_add(len)
.and_then(|end_excl| U64CO::try_new(start, end_excl))
};
prop_assert_eq!(got, expected);
}
#[test]
fn checked_from_start_len_preserves_exact_len(
start in mixed_scalar(),
len in mixed_scalar(),
) {
if let Some(iv) = U64CO::checked_from_start_len(start, len) {
prop_assert_eq!(iv.start(), start);
prop_assert_eq!(iv.len(), len);
prop_assert_eq!(iv.end_excl(), start + len);
prop_assert!(iv.start() < iv.end_excl());
}
}
#[test]
fn checked_from_start_len_reconstructs_existing_interval(
start in mixed_scalar(),
end_excl in mixed_scalar(),
) {
if let Some(base) = U64CO::try_new(start, end_excl) {
let rebuilt = U64CO::checked_from_start_len(base.start(), base.len());
prop_assert_eq!(rebuilt, Some(base));
}
}
#[test]
fn checked_from_start_len_rejects_zero_len(start in mixed_scalar()) {
prop_assert_eq!(U64CO::checked_from_start_len(start, 0), None);
}
#[test]
fn saturating_from_start_len_matches_saturating_add(
start in mixed_scalar(),
len in mixed_scalar(),
) {
let got = U64CO::saturating_from_start_len(start, len);
let expected = if len == 0 {
None
} else {
U64CO::try_new(start, start.saturating_add(len))
};
prop_assert_eq!(got, expected);
}
#[test]
fn saturating_from_start_len_preserves_invariant(
start in mixed_scalar(),
len in mixed_scalar(),
) {
if let Some(iv) = U64CO::saturating_from_start_len(start, len) {
prop_assert_eq!(iv.start(), start);
prop_assert!(iv.start() < iv.end_excl());
prop_assert!(iv.len() <= len);
}
}
#[test]
fn saturating_from_start_len_matches_checked_when_checked_succeeds(
start in mixed_scalar(),
len in mixed_scalar(),
) {
let checked = U64CO::checked_from_start_len(start, len);
let saturated = U64CO::saturating_from_start_len(start, len);
if checked.is_some() {
prop_assert_eq!(saturated, checked);
}
}
#[test]
fn saturating_from_start_len_rejects_zero_len(start in mixed_scalar()) {
prop_assert_eq!(U64CO::saturating_from_start_len(start, 0), None);
}
}
}
}
mod about_midpoint {
use super::*;
mod unit_tests {
use super::*;
fn span(lo: u64, hi: u64) -> U64CO {
U64CO::try_new(lo, hi).unwrap()
}
#[test]
fn midpoint_basic() {
let iv = span(10, 20);
assert_eq!(iv.midpoint(), 15);
let iv2 = span(0, 2);
assert_eq!(iv2.midpoint(), 1);
let iv3 = span(0, u64::MAX);
assert_eq!(iv3.midpoint(), (0 + ((u64::MAX - 0) / 2)));
}
#[test]
fn checked_from_midpoint_len_basic() {
let mid = 10;
let len = 5;
let iv = U64CO::checked_from_midpoint_len(mid, len).unwrap();
assert_eq!(iv.len(), len);
assert_eq!(iv.midpoint(), mid);
assert!(U64CO::checked_from_midpoint_len(mid, 0).is_none());
assert!(U64CO::checked_from_midpoint_len(0, 5).is_none());
assert!(U64CO::checked_from_midpoint_len(u64::MAX, 10).is_none());
}
#[test]
fn saturating_from_midpoint_len_basic() {
let mid = 10;
let len = 5;
let iv = U64CO::saturating_from_midpoint_len(mid, len).unwrap();
assert_eq!(iv.len(), len);
assert_eq!(iv.midpoint(), mid);
assert!(U64CO::saturating_from_midpoint_len(mid, 0).is_none());
let iv2 = U64CO::saturating_from_midpoint_len(0, 5).unwrap();
assert_eq!(iv2.start(), 0);
assert_eq!(iv2.len(), 5);
let iv3 = U64CO::saturating_from_midpoint_len(u64::MAX, 10).unwrap();
assert!(iv3.end_excl() <= u64::MAX);
}
}
mod prop_tests {
use std::{vec, vec::Vec};
use super::*;
use proptest::prelude::*;
fn edge_values() -> Vec<u64> {
let mut v = vec![u64::MIN, u64::MAX, 0, 1];
if u64::MIN < u64::MAX {
v.push(u64::MIN.saturating_add(1));
v.push(u64::MAX.saturating_sub(1));
}
v.sort_unstable();
v.dedup();
v
}
fn edge_scalar() -> impl Strategy<Value = u64> {
prop::sample::select(edge_values())
}
fn mixed_scalar() -> impl Strategy<Value = u64> {
prop_oneof! {
3 => edge_scalar(),
7 => any::<u64>(),
}
}
proptest! {
#[test]
fn midpoint_rounds_down(x in mixed_scalar(), y in mixed_scalar()) {
let lo = x.min(y);
let hi = x.max(y).saturating_add(1); if let Some(iv) = U64CO::try_new(lo, hi) {
let mp = iv.midpoint();
prop_assert!(mp >= iv.start() && mp <= iv.end_incl());
prop_assert_eq!(mp, iv.start() + (iv.len() / 2));
}
}
#[test]
fn checked_from_midpoint_len_inverse(mid in mixed_scalar(), len in 1u64..20) {
if let Some(iv) = U64CO::checked_from_midpoint_len(mid, len) {
prop_assert_eq!(iv.len(), len);
prop_assert_eq!(iv.midpoint(), mid);
}
}
#[test]
fn saturating_from_midpoint_len_safety(mid in mixed_scalar(), len in 1u64..20) {
if let Some(iv) = U64CO::saturating_from_midpoint_len(mid, len) {
prop_assert!(iv.start() < iv.end_excl());
prop_assert_eq!(iv.midpoint(), iv.start() + (iv.len()/2));
}
}
}
}
}