#[cfg(test)]
#[allow(unused_imports)]
use crate::conn::Conn;
use crate::extended::Extended;
use crate::float::{F032, F064, N5, def_walk_helpers};
use std::time::Duration as StdDuration;
use time::Duration;
pub(crate) fn shift_duration(n: i32, d: Duration) -> Duration {
let cur_ns = d.whole_nanoseconds();
let new_ns = cur_ns.saturating_add(n as i128);
let max_ns = Duration::MAX.whole_nanoseconds();
let min_ns = Duration::MIN.whole_nanoseconds();
if new_ns >= max_ns {
Duration::MAX
} else if new_ns <= min_ns {
Duration::MIN
} else {
let secs = (new_ns / 1_000_000_000) as i64;
let subsec = (new_ns % 1_000_000_000) as i32;
Duration::new(secs, subsec)
}
}
#[inline]
fn duration_to_f64(d: Duration) -> f64 {
d.as_seconds_f64()
}
#[inline]
fn duration_to_f32(d: Duration) -> f32 {
d.as_seconds_f32()
}
#[inline]
fn tdur_to_ns(d: Duration) -> i128 {
d.whole_nanoseconds()
}
#[inline]
fn tdur_from_ns(n: i128) -> Duration {
let max_ns = Duration::MAX.whole_nanoseconds();
let min_ns = Duration::MIN.whole_nanoseconds();
if n >= max_ns {
Duration::MAX
} else if n <= min_ns {
Duration::MIN
} else {
let secs = (n / 1_000_000_000) as i64;
let subsec = (n % 1_000_000_000) as i32;
Duration::new(secs, subsec)
}
}
def_walk_helpers!(
f64_tdur_walks,
f64,
Duration,
shift_duration,
duration_to_f64,
tdur_to_ns,
tdur_from_ns
);
def_walk_helpers!(
f32_tdur_walks,
f32,
Duration,
shift_duration,
duration_to_f32,
tdur_to_ns,
tdur_from_ns
);
fn tdursecs_ceil(d: Duration) -> Extended<i64> {
if d.eq(&Duration::MIN) {
return Extended::NegInf;
}
let w = d.whole_seconds();
let n = d.subsec_nanoseconds();
if n > 0 {
match w.checked_add(1) {
Some(s) => Extended::Finite(s),
None => Extended::PosInf,
}
} else {
Extended::Finite(w)
}
}
fn tdursecs_inner(b: Extended<i64>) -> Duration {
match b {
Extended::NegInf => Duration::MIN,
Extended::Finite(s) => Duration::seconds(s),
Extended::PosInf => Duration::MAX,
}
}
fn tdursecs_floor(d: Duration) -> Extended<i64> {
if d.eq(&Duration::MAX) {
return Extended::PosInf;
}
let w = d.whole_seconds();
let n = d.subsec_nanoseconds();
if n < 0 {
match w.checked_sub(1) {
Some(s) => Extended::Finite(s),
None => Extended::NegInf,
}
} else {
Extended::Finite(w)
}
}
crate::conn_k! {
pub TDURSECS : Duration => Extended<i64> {
ceil: tdursecs_ceil,
inner: tdursecs_inner,
floor: tdursecs_floor,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prop::arb::{arb_duration, arb_extended_i64};
use crate::prop::{conn as conn_laws, lattice as lattice_laws};
use proptest::prelude::*;
mod duration_preorder {
use super::*;
#[allow(unused_imports)]
use crate::conn::{ConnL, ConnR};
proptest! {
#[test]
fn reflexive(x in arb_duration()) {
prop_assert!(lattice_laws::lattice_reflexive(&x));
}
#[test]
fn transitive(x in arb_duration(), y in arb_duration(), z in arb_duration()) {
prop_assert!(lattice_laws::lattice_transitive(&x, &y, &z));
}
#[test]
fn antisymmetric(x in arb_duration(), y in arb_duration()) {
prop_assert!(lattice_laws::lattice_antisymmetric(&x, &y));
}
#[test]
fn bot(x in arb_duration()) {
prop_assert!(lattice_laws::lattice_bot(&Duration::MIN, &x));
}
#[test]
fn top(x in arb_duration()) {
prop_assert!(lattice_laws::lattice_top(&Duration::MAX, &x));
}
}
}
#[test]
fn zero_is_zero() {
assert_eq!(TDURSECS.view_l().ceil(Duration::ZERO), Extended::Finite(0));
assert_eq!(TDURSECS.view_r().floor(Duration::ZERO), Extended::Finite(0));
assert_eq!(TDURSECS.view_l().upper(Extended::Finite(0)), Duration::ZERO);
}
#[test]
fn positive_subsec_rounds_up() {
let d = Duration::seconds(5) + Duration::nanoseconds(1);
assert_eq!(TDURSECS.view_l().ceil(d), Extended::Finite(6));
assert_eq!(TDURSECS.view_r().floor(d), Extended::Finite(5));
}
#[test]
fn negative_subsec_rounds_toward_zero() {
let d = Duration::seconds(-5) - Duration::nanoseconds(1);
assert_eq!(TDURSECS.view_l().ceil(d), Extended::Finite(-5));
assert_eq!(TDURSECS.view_r().floor(d), Extended::Finite(-6));
}
#[test]
fn extreme_durations() {
assert_eq!(TDURSECS.view_l().ceil(Duration::MAX), Extended::PosInf);
assert_eq!(TDURSECS.view_r().floor(Duration::MAX), Extended::PosInf);
assert_eq!(TDURSECS.view_l().ceil(Duration::MIN), Extended::NegInf);
assert_eq!(TDURSECS.view_r().floor(Duration::MIN), Extended::NegInf);
}
#[test]
fn inner_saturates_extended() {
assert_eq!(TDURSECS.view_l().upper(Extended::NegInf), Duration::MIN);
assert_eq!(TDURSECS.view_l().upper(Extended::PosInf), Duration::MAX);
}
crate::law_battery! {
mod tdursecs_laws,
conn: TDURSECS,
fine: arb_duration(),
coarse: arb_extended_i64(),
}
proptest! {
#[test]
fn ulp_bound(d in arb_duration()) {
let extractor = |b: Extended<i64>| -> i64 {
match b {
Extended::NegInf => i64::MIN,
Extended::Finite(s) => s,
Extended::PosInf => i64::MAX,
}
};
prop_assert!(conn_laws::ulp_bound(&TDURSECS, d, extractor));
}
#[test]
fn roundtrip_ceil(s in any::<i64>()) {
prop_assert!(conn_laws::roundtrip_ceil(&TDURSECS.view_l(), Extended::Finite(s)));
}
#[test]
fn roundtrip_floor(s in any::<i64>()) {
prop_assert!(conn_laws::roundtrip_floor(&TDURSECS.view_r(), Extended::Finite(s)));
}
}
}
fn f064tdur_ceil(x: F064) -> Extended<Duration> {
let v = x.into_inner();
if v.is_nan() {
return Extended::PosInf;
}
if v == f64::INFINITY {
return Extended::PosInf;
}
if v == f64::NEG_INFINITY {
return Extended::NegInf;
}
let max_secs = Duration::MAX.as_seconds_f64();
let min_secs = Duration::MIN.as_seconds_f64();
if v > max_secs {
return Extended::PosInf;
}
if v <= min_secs {
return Extended::Finite(Duration::MIN);
}
let est = Duration::saturating_seconds_f64(v);
let (z, _) = f64_tdur_walks::solve_to_ceil(est, v);
Extended::Finite(z)
}
fn f064tdur_inner(d: Extended<Duration>) -> F064 {
match d {
Extended::NegInf => N5::new(f64::NEG_INFINITY),
Extended::Finite(dur) => N5::new(dur.as_seconds_f64()),
Extended::PosInf => N5::new(f64::INFINITY),
}
}
crate::conn_l! {
pub F064TDUR : F064 => Extended<Duration> {
ceil: f064tdur_ceil,
inner: f064tdur_inner,
}
}
fn f032tdur_ceil(x: F032) -> Extended<Duration> {
let v = x.into_inner();
if v.is_nan() {
return Extended::PosInf;
}
if v == f32::INFINITY {
return Extended::PosInf;
}
if v == f32::NEG_INFINITY {
return Extended::NegInf;
}
let max_secs = Duration::MAX.as_seconds_f32();
let min_secs = Duration::MIN.as_seconds_f32();
if v > max_secs {
return Extended::PosInf;
}
if v <= min_secs {
return Extended::Finite(Duration::MIN);
}
let est = Duration::saturating_seconds_f32(v);
let (z, _) = f32_tdur_walks::solve_to_ceil(est, v);
Extended::Finite(z)
}
fn f032tdur_inner(d: Extended<Duration>) -> F032 {
match d {
Extended::NegInf => N5::new(f32::NEG_INFINITY),
Extended::Finite(dur) => N5::new(dur.as_seconds_f32()),
Extended::PosInf => N5::new(f32::INFINITY),
}
}
crate::conn_l! {
pub F032TDUR : F032 => Extended<Duration> {
ceil: f032tdur_ceil,
inner: f032tdur_inner,
}
}
pub(crate) fn shift_std_duration(n: i32, d: StdDuration) -> StdDuration {
let cur = d.as_nanos();
let max = StdDuration::MAX.as_nanos();
let new = if n >= 0 {
cur.saturating_add(n as u128)
} else {
cur.saturating_sub(n.unsigned_abs() as u128)
};
if new >= max {
return StdDuration::MAX;
}
let secs = (new / 1_000_000_000) as u64;
let subsec = (new % 1_000_000_000) as u32;
StdDuration::new(secs, subsec)
}
#[inline]
fn std_duration_to_f64(d: StdDuration) -> f64 {
d.as_secs_f64()
}
#[inline]
fn std_duration_to_f32(d: StdDuration) -> f32 {
d.as_secs_f32()
}
#[inline]
fn sdur_to_ns(d: StdDuration) -> i128 {
d.as_nanos() as i128
}
#[inline]
fn sdur_from_ns(n: i128) -> StdDuration {
if n <= 0 {
return StdDuration::ZERO;
}
let n_u = n as u128;
if n_u >= StdDuration::MAX.as_nanos() {
return StdDuration::MAX;
}
let secs = (n_u / 1_000_000_000) as u64;
let subsec = (n_u % 1_000_000_000) as u32;
StdDuration::new(secs, subsec)
}
def_walk_helpers!(
f64_sdur_walks,
f64,
StdDuration,
shift_std_duration,
std_duration_to_f64,
sdur_to_ns,
sdur_from_ns
);
def_walk_helpers!(
f32_sdur_walks,
f32,
StdDuration,
shift_std_duration,
std_duration_to_f32,
sdur_to_ns,
sdur_from_ns
);
fn sduru064_ceil(d: Extended<StdDuration>) -> Extended<u64> {
match d {
Extended::NegInf => Extended::NegInf,
Extended::PosInf => Extended::PosInf,
Extended::Finite(d) => {
let w = d.as_secs();
let n = d.subsec_nanos();
if n > 0 {
match w.checked_add(1) {
Some(s) => Extended::Finite(s),
None => Extended::PosInf,
}
} else {
Extended::Finite(w)
}
}
}
}
fn sduru064_inner(b: Extended<u64>) -> Extended<StdDuration> {
match b {
Extended::NegInf => Extended::NegInf,
Extended::PosInf => Extended::PosInf,
Extended::Finite(s) => Extended::Finite(StdDuration::from_secs(s)),
}
}
crate::conn_l! {
pub SDURU064 : Extended<StdDuration> => Extended<u64> {
ceil: sduru064_ceil,
inner: sduru064_inner,
}
}
fn sduru128_ceil(d: Extended<StdDuration>) -> Extended<u128> {
match d {
Extended::NegInf => Extended::NegInf,
Extended::PosInf => Extended::PosInf,
Extended::Finite(d) => Extended::Finite(d.as_nanos()),
}
}
fn sduru128_inner(b: Extended<u128>) -> Extended<StdDuration> {
match b {
Extended::NegInf => Extended::NegInf,
Extended::PosInf => Extended::PosInf,
Extended::Finite(n) => {
let max_nanos = StdDuration::MAX.as_nanos();
if n > max_nanos {
return Extended::Finite(StdDuration::MAX);
}
let secs = (n / 1_000_000_000) as u64;
let subsec = (n % 1_000_000_000) as u32;
Extended::Finite(StdDuration::new(secs, subsec))
}
}
}
crate::conn_l! {
pub SDURU128 : Extended<StdDuration> => Extended<u128> {
ceil: sduru128_ceil,
inner: sduru128_inner,
}
}
fn f064sdur_ceil(x: F064) -> Extended<StdDuration> {
let v = x.into_inner();
if v.is_nan() {
return Extended::PosInf;
}
if v == f64::INFINITY {
return Extended::PosInf;
}
if v == f64::NEG_INFINITY {
return Extended::NegInf;
}
if v <= 0.0 {
return Extended::Finite(StdDuration::ZERO);
}
let max_secs = StdDuration::MAX.as_secs_f64();
if v >= max_secs {
return if v == max_secs {
Extended::Finite(StdDuration::MAX)
} else {
Extended::PosInf
};
}
let est = StdDuration::from_secs_f64(v);
let (z, _) = f64_sdur_walks::solve_to_ceil(est, v);
Extended::Finite(z)
}
fn f064sdur_inner(d: Extended<StdDuration>) -> F064 {
match d {
Extended::NegInf => N5::new(f64::NEG_INFINITY),
Extended::Finite(dur) => N5::new(dur.as_secs_f64()),
Extended::PosInf => N5::new(f64::INFINITY),
}
}
crate::conn_l! {
pub F064SDUR : F064 => Extended<StdDuration> {
ceil: f064sdur_ceil,
inner: f064sdur_inner,
}
}
fn f032sdur_ceil(x: F032) -> Extended<StdDuration> {
let v = x.into_inner();
if v.is_nan() {
return Extended::PosInf;
}
if v == f32::INFINITY {
return Extended::PosInf;
}
if v == f32::NEG_INFINITY {
return Extended::NegInf;
}
if v <= 0.0 {
return Extended::Finite(StdDuration::ZERO);
}
let max_secs = StdDuration::MAX.as_secs_f32();
if v >= max_secs {
return if v == max_secs {
Extended::Finite(StdDuration::MAX)
} else {
Extended::PosInf
};
}
let est = StdDuration::from_secs_f32(v);
let (z, _) = f32_sdur_walks::solve_to_ceil(est, v);
Extended::Finite(z)
}
fn f032sdur_inner(d: Extended<StdDuration>) -> F032 {
match d {
Extended::NegInf => N5::new(f32::NEG_INFINITY),
Extended::Finite(dur) => N5::new(dur.as_secs_f32()),
Extended::PosInf => N5::new(f32::INFINITY),
}
}
crate::conn_l! {
pub F032SDUR : F032 => Extended<StdDuration> {
ceil: f032sdur_ceil,
inner: f032sdur_inner,
}
}
#[cfg(kani)]
pub(crate) fn f64_tdur_ceil_walk_steps_for_proof(v: f64) -> (Duration, u32) {
let est = Duration::saturating_seconds_f64(v);
let est_widen = est.as_seconds_f64();
if est_widen >= v {
f64_tdur_walks::descend_to_ceil(est, v)
} else {
f64_tdur_walks::ascend_to_ceil(est, v)
}
}
#[cfg(kani)]
pub(crate) fn f32_tdur_ceil_walk_steps_for_proof(v: f32) -> (Duration, u32) {
let est = Duration::saturating_seconds_f32(v);
let est_widen = est.as_seconds_f32();
if est_widen >= v {
f32_tdur_walks::descend_to_ceil(est, v)
} else {
f32_tdur_walks::ascend_to_ceil(est, v)
}
}
#[cfg(kani)]
pub(crate) fn f64_sdur_ceil_walk_steps_for_proof(v: f64) -> (StdDuration, u32) {
if v <= 0.0 {
return (StdDuration::ZERO, 0);
}
let est = StdDuration::from_secs_f64(v);
let est_widen = est.as_secs_f64();
if est_widen >= v {
f64_sdur_walks::descend_to_ceil(est, v)
} else {
f64_sdur_walks::ascend_to_ceil(est, v)
}
}
#[cfg(kani)]
pub(crate) fn f32_sdur_ceil_walk_steps_for_proof(v: f32) -> (StdDuration, u32) {
if v <= 0.0 {
return (StdDuration::ZERO, 0);
}
let est = StdDuration::from_secs_f32(v);
let est_widen = est.as_secs_f32();
if est_widen >= v {
f32_sdur_walks::descend_to_ceil(est, v)
} else {
f32_sdur_walks::ascend_to_ceil(est, v)
}
}
#[cfg(kani)]
pub(crate) fn f64_tdur_ceil_solve_steps_for_proof(v: f64) -> (Duration, u32) {
let est = Duration::saturating_seconds_f64(v);
f64_tdur_walks::solve_to_ceil(est, v)
}
#[cfg(kani)]
pub(crate) fn f32_tdur_ceil_solve_steps_for_proof(v: f32) -> (Duration, u32) {
let est = Duration::saturating_seconds_f32(v);
f32_tdur_walks::solve_to_ceil(est, v)
}
#[cfg(kani)]
pub(crate) fn f64_sdur_ceil_solve_steps_for_proof(v: f64) -> (StdDuration, u32) {
if v <= 0.0 {
return (StdDuration::ZERO, 0);
}
let est = StdDuration::from_secs_f64(v);
f64_sdur_walks::solve_to_ceil(est, v)
}
#[cfg(kani)]
pub(crate) fn f32_sdur_ceil_solve_steps_for_proof(v: f32) -> (StdDuration, u32) {
if v <= 0.0 {
return (StdDuration::ZERO, 0);
}
let est = StdDuration::from_secs_f32(v);
f32_sdur_walks::solve_to_ceil(est, v)
}
#[cfg(test)]
mod float_tdur_tests {
use super::*;
#[allow(unused_imports)]
use crate::conn::ConnL;
use crate::prop::arb::{
arb_extended_duration_bounded_f32, arb_extended_duration_bounded_f64, extended_float_f32,
extended_float_f64,
};
#[test]
fn f64_zero() {
let zero = N5::new(0.0_f64);
assert_eq!(F064TDUR.ceil(zero), Extended::Finite(Duration::ZERO));
assert_eq!(F064TDUR.upper(Extended::Finite(Duration::ZERO)), zero);
}
#[test]
fn f64_half_second() {
let half = N5::new(0.5_f64);
let half_d = Duration::milliseconds(500);
assert_eq!(F064TDUR.ceil(half), Extended::Finite(half_d));
assert_eq!(F064TDUR.upper(Extended::Finite(half_d)), half);
}
#[test]
fn f64_nan_arms() {
let nan = N5::new(f64::NAN);
assert_eq!(F064TDUR.ceil(nan), Extended::PosInf);
}
#[test]
fn f64_infinity_arms() {
let pos_inf = N5::new(f64::INFINITY);
assert_eq!(F064TDUR.ceil(pos_inf), Extended::PosInf);
let neg_inf = N5::new(f64::NEG_INFINITY);
assert_eq!(F064TDUR.ceil(neg_inf), Extended::NegInf);
}
#[test]
fn f64_extended_bounds_upper_to_infinities() {
assert_eq!(F064TDUR.upper(Extended::NegInf), N5::new(f64::NEG_INFINITY));
assert_eq!(F064TDUR.upper(Extended::PosInf), N5::new(f64::INFINITY));
}
#[test]
fn f64_ceil_min_secs_fast_path() {
let v_min = N5::new(Duration::MIN.as_seconds_f64());
assert_eq!(F064TDUR.ceil(v_min), Extended::Finite(Duration::MIN));
}
#[test]
fn f32_zero() {
let zero = N5::new(0.0_f32);
assert_eq!(F032TDUR.ceil(zero), Extended::Finite(Duration::ZERO));
assert_eq!(F032TDUR.upper(Extended::Finite(Duration::ZERO)), zero);
}
#[test]
fn f32_one_second_in_plateau() {
let one = N5::new(1.0_f32);
if let Extended::Finite(cd) = F032TDUR.ceil(one) {
assert_eq!(cd.as_seconds_f32(), 1.0_f32);
} else {
panic!("ceil(1.0) should be Finite");
}
}
#[test]
fn f32_nan_arms() {
let nan = N5::new(f32::NAN);
assert_eq!(F032TDUR.ceil(nan), Extended::PosInf);
}
#[test]
fn f32_infinity_arms() {
let pos_inf = N5::new(f32::INFINITY);
assert_eq!(F032TDUR.ceil(pos_inf), Extended::PosInf);
let neg_inf = N5::new(f32::NEG_INFINITY);
assert_eq!(F032TDUR.ceil(neg_inf), Extended::NegInf);
}
#[test]
fn f32_ceil_min_secs_fast_path() {
let v_min = N5::new(Duration::MIN.as_seconds_f32());
assert_eq!(F032TDUR.ceil(v_min), Extended::Finite(Duration::MIN));
}
#[test]
fn f32_extended_bounds_upper_to_infinities() {
assert_eq!(F032TDUR.upper(Extended::NegInf), N5::new(f32::NEG_INFINITY));
assert_eq!(F032TDUR.upper(Extended::PosInf), N5::new(f32::INFINITY));
}
use crate::prop::conn as float_tdur_conn_laws;
use ::proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig { cases: 64, .. ProptestConfig::default() })]
#[test]
fn f064tdur_galois_l(a in extended_float_f64(),
b in arb_extended_duration_bounded_f64()) {
prop_assert!(float_tdur_conn_laws::galois_l(&F064TDUR, a, b));
}
#[test]
fn f064tdur_closure_l(a in extended_float_f64()) {
prop_assert!(float_tdur_conn_laws::closure_l(&F064TDUR, a));
}
#[test]
fn f064tdur_kernel_l(b in arb_extended_duration_bounded_f64()) {
prop_assert!(float_tdur_conn_laws::kernel_l(&F064TDUR, b));
}
#[test]
fn f064tdur_monotone_l(a1 in extended_float_f64(), a2 in extended_float_f64()) {
prop_assert!(float_tdur_conn_laws::monotone_l(&F064TDUR, a1, a2));
}
#[test]
fn f064tdur_idempotent(a in extended_float_f64()) {
prop_assert!(float_tdur_conn_laws::idempotent_l(&F064TDUR, a));
}
#[test]
fn f032tdur_galois_l(a in extended_float_f32(),
b in arb_extended_duration_bounded_f32()) {
prop_assert!(float_tdur_conn_laws::galois_l(&F032TDUR, a, b));
}
#[test]
fn f032tdur_closure_l(a in extended_float_f32()) {
prop_assert!(float_tdur_conn_laws::closure_l(&F032TDUR, a));
}
#[test]
fn f032tdur_kernel_l(b in arb_extended_duration_bounded_f32()) {
prop_assert!(float_tdur_conn_laws::kernel_l(&F032TDUR, b));
}
#[test]
fn f032tdur_monotone_l(a1 in extended_float_f32(), a2 in extended_float_f32()) {
prop_assert!(float_tdur_conn_laws::monotone_l(&F032TDUR, a1, a2));
}
#[test]
fn f032tdur_idempotent(a in extended_float_f32()) {
prop_assert!(float_tdur_conn_laws::idempotent_l(&F032TDUR, a));
}
}
#[test]
fn f64_tdur_solve_terminates_at_max_rim() {
let max_secs = Duration::MAX.as_seconds_f64();
let _ = F064TDUR.ceil(N5::new(max_secs));
}
#[test]
fn f32_tdur_solve_terminates_at_max_rim() {
let max_secs = Duration::MAX.as_seconds_f32();
let _ = F032TDUR.ceil(N5::new(max_secs));
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(256))]
#[test]
fn f64_tdur_solve_matches_walk_in_safe_range(v in -1.0e6_f64..1.0e6_f64) {
let est = Duration::saturating_seconds_f64(v);
let est_widen = est.as_seconds_f64();
let (walk_z, _) = if est_widen >= v {
f64_tdur_walks::descend_to_ceil(est, v)
} else {
f64_tdur_walks::ascend_to_ceil(est, v)
};
let (solve_z, steps) = f64_tdur_walks::solve_to_ceil(est, v);
prop_assert_eq!(solve_z, walk_z);
prop_assert!(steps <= 52, "solve_to_ceil took {steps} steps");
}
#[test]
fn f32_tdur_solve_matches_walk_in_safe_range(v in -1.0_f32..1.0_f32) {
let est = Duration::saturating_seconds_f32(v);
let est_widen = est.as_seconds_f32();
let (walk_z, _) = if est_widen >= v {
f32_tdur_walks::descend_to_ceil(est, v)
} else {
f32_tdur_walks::ascend_to_ceil(est, v)
};
let (solve_z, steps) = f32_tdur_walks::solve_to_ceil(est, v);
prop_assert_eq!(solve_z, walk_z);
prop_assert!(steps <= 52, "solve_to_ceil took {steps} steps");
}
}
}
#[cfg(test)]
mod sdur_tests {
use super::*;
#[allow(unused_imports)]
use crate::conn::ConnL;
use crate::prop::arb::{
arb_extended_std_duration, arb_extended_std_duration_bounded_f32,
arb_extended_std_duration_bounded_f64, arb_extended_u64, arb_extended_u128,
extended_float_f32, extended_float_f64,
};
use crate::prop::conn as conn_laws;
use proptest::prelude::*;
#[test]
fn sduru064_zero() {
let z = Extended::Finite(StdDuration::ZERO);
assert_eq!(SDURU064.ceil(z), Extended::Finite(0_u64));
assert_eq!(SDURU064.upper(Extended::Finite(0_u64)), z);
}
#[test]
fn sduru064_positive_subsec_rounds_up() {
let d = Extended::Finite(StdDuration::from_secs(5) + StdDuration::from_nanos(1));
assert_eq!(SDURU064.ceil(d), Extended::Finite(6_u64));
}
#[test]
fn sduru064_max_overflows_ceil() {
let m = Extended::Finite(StdDuration::MAX);
assert_eq!(SDURU064.ceil(m), Extended::PosInf);
}
#[test]
fn sduru064_synthetic_arms() {
assert_eq!(SDURU064.ceil(Extended::NegInf), Extended::NegInf);
assert_eq!(SDURU064.ceil(Extended::PosInf), Extended::PosInf);
assert_eq!(SDURU064.upper(Extended::NegInf), Extended::NegInf);
assert_eq!(SDURU064.upper(Extended::PosInf), Extended::PosInf);
}
#[test]
fn sduru128_one_and_a_half() {
let d = Extended::Finite(StdDuration::from_nanos(1_500_000_000));
assert_eq!(SDURU128.ceil(d), Extended::Finite(1_500_000_000_u128));
}
#[test]
fn sduru128_max_no_overflow() {
let m = Extended::Finite(StdDuration::MAX);
let m_nanos = StdDuration::MAX.as_nanos();
assert_eq!(SDURU128.ceil(m), Extended::Finite(m_nanos));
}
#[test]
fn sduru128_inner_saturates_above_max() {
assert_eq!(
SDURU128.upper(Extended::Finite(u128::MAX)),
Extended::Finite(StdDuration::MAX)
);
let above = StdDuration::MAX.as_nanos() + 1;
assert_eq!(
SDURU128.upper(Extended::Finite(above)),
Extended::Finite(StdDuration::MAX)
);
}
#[test]
fn sduru128_synthetic_arms() {
assert_eq!(SDURU128.ceil(Extended::NegInf), Extended::NegInf);
assert_eq!(SDURU128.ceil(Extended::PosInf), Extended::PosInf);
assert_eq!(SDURU128.upper(Extended::NegInf), Extended::NegInf);
assert_eq!(SDURU128.upper(Extended::PosInf), Extended::PosInf);
}
#[test]
fn sduru128_kernel_l_at_above_max_boundary() {
let max_nanos = StdDuration::MAX.as_nanos();
for b in [
Extended::Finite(0_u128),
Extended::Finite(max_nanos),
Extended::Finite(max_nanos + 1),
Extended::Finite(u128::MAX),
] {
assert!(
conn_laws::kernel_l(&SDURU128, b),
"kernel_l violated at b = {b:?}"
);
}
}
crate::law_battery! {
mod sduru064_laws,
conn: SDURU064,
fine: arb_extended_std_duration(),
coarse: arb_extended_u64(),
subset: l_only,
}
proptest! {
#[test]
fn sduru128_galois_l(d in arb_extended_std_duration(), b in arb_extended_u128()) {
prop_assert!(conn_laws::galois_l(&SDURU128, d, b));
}
#[test]
fn sduru128_closure_l(d in arb_extended_std_duration()) {
prop_assert!(conn_laws::closure_l(&SDURU128, d));
}
#[test]
fn sduru128_kernel_l(b in arb_extended_u128()) {
prop_assert!(conn_laws::kernel_l(&SDURU128, b));
}
#[test]
fn sduru128_monotone_l(a in arb_extended_std_duration(),
b in arb_extended_std_duration()) {
prop_assert!(conn_laws::monotone_l(&SDURU128, a, b));
}
#[test]
fn sduru128_idempotent(d in arb_extended_std_duration()) {
prop_assert!(conn_laws::idempotent(&SDURU128, d));
}
#[test]
fn sduru128_round_trip(n in 0_u128..=StdDuration::MAX.as_nanos()) {
prop_assert!(conn_laws::roundtrip_ceil(&SDURU128, Extended::Finite(n)));
}
}
#[test]
fn f64_sdur_zero() {
let zero = N5::new(0.0_f64);
assert_eq!(F064SDUR.ceil(zero), Extended::Finite(StdDuration::ZERO));
assert_eq!(F064SDUR.upper(Extended::Finite(StdDuration::ZERO)), zero);
}
#[test]
fn f64_sdur_half_second() {
let half = N5::new(0.5_f64);
let half_d = StdDuration::from_millis(500);
assert_eq!(F064SDUR.ceil(half), Extended::Finite(half_d));
assert_eq!(F064SDUR.upper(Extended::Finite(half_d)), half);
}
#[test]
fn f64_sdur_inner_one_second() {
let one_d = Extended::Finite(StdDuration::from_secs(1));
assert_eq!(F064SDUR.upper(one_d), N5::new(1.0_f64));
}
#[test]
fn f64_sdur_negative_input() {
let neg = N5::new(-0.5_f64);
assert_eq!(F064SDUR.ceil(neg), Extended::Finite(StdDuration::ZERO));
}
#[test]
fn f64_sdur_nan_arms() {
let nan = N5::new(f64::NAN);
assert_eq!(F064SDUR.ceil(nan), Extended::PosInf);
}
#[test]
fn f64_sdur_infinity_arms() {
let pos_inf = N5::new(f64::INFINITY);
assert_eq!(F064SDUR.ceil(pos_inf), Extended::PosInf);
let neg_inf = N5::new(f64::NEG_INFINITY);
assert_eq!(F064SDUR.ceil(neg_inf), Extended::NegInf);
}
#[test]
fn f64_sdur_extended_bounds_upper_to_infinities() {
assert_eq!(F064SDUR.upper(Extended::NegInf), N5::new(f64::NEG_INFINITY));
assert_eq!(F064SDUR.upper(Extended::PosInf), N5::new(f64::INFINITY));
}
#[test]
fn f64_sdur_max_sentinel_is_total() {
let max = Extended::Finite(StdDuration::MAX);
let sentinel = N5::new(StdDuration::MAX.as_secs_f64());
assert_eq!(F064SDUR.upper(max), sentinel);
assert_eq!(F064SDUR.ceil(sentinel), max);
assert!(conn_laws::kernel_l(&F064SDUR, max));
}
#[test]
fn f64_sdur_max_float_sentinel_is_total() {
let sentinel = N5::new(StdDuration::MAX.as_secs_f64());
assert_eq!(F064SDUR.ceil(sentinel), Extended::Finite(StdDuration::MAX));
assert!(conn_laws::kernel_l(
&F064SDUR,
Extended::Finite(StdDuration::MAX)
));
}
#[test]
fn f32_sdur_zero() {
let zero = N5::new(0.0_f32);
assert_eq!(F032SDUR.ceil(zero), Extended::Finite(StdDuration::ZERO));
}
#[test]
fn f32_sdur_negative_input() {
let neg = N5::new(-0.5_f32);
assert_eq!(F032SDUR.ceil(neg), Extended::Finite(StdDuration::ZERO));
}
#[test]
fn f32_sdur_nan_arms() {
let nan = N5::new(f32::NAN);
assert_eq!(F032SDUR.ceil(nan), Extended::PosInf);
}
#[test]
fn f32_sdur_infinity_arms() {
let pos_inf = N5::new(f32::INFINITY);
assert_eq!(F032SDUR.ceil(pos_inf), Extended::PosInf);
let neg_inf = N5::new(f32::NEG_INFINITY);
assert_eq!(F032SDUR.ceil(neg_inf), Extended::NegInf);
}
#[test]
fn f32_sdur_max_sentinel_is_total() {
let max = Extended::Finite(StdDuration::MAX);
let sentinel = N5::new(StdDuration::MAX.as_secs_f32());
assert_eq!(F032SDUR.upper(max), sentinel);
assert_eq!(F032SDUR.ceil(sentinel), max);
assert!(conn_laws::kernel_l(&F032SDUR, max));
}
#[test]
fn f32_sdur_max_float_sentinel_is_total() {
let sentinel = N5::new(StdDuration::MAX.as_secs_f32());
assert_eq!(F032SDUR.ceil(sentinel), Extended::Finite(StdDuration::MAX));
assert!(conn_laws::kernel_l(
&F032SDUR,
Extended::Finite(StdDuration::MAX)
));
}
proptest! {
#![proptest_config(ProptestConfig { cases: 64, .. ProptestConfig::default() })]
#[test]
fn f064sdur_galois_l(a in extended_float_f64(),
b in arb_extended_std_duration_bounded_f64()) {
prop_assert!(conn_laws::galois_l(&F064SDUR, a, b));
}
#[test]
fn f064sdur_closure_l(a in extended_float_f64()) {
prop_assert!(conn_laws::closure_l(&F064SDUR, a));
}
#[test]
fn f064sdur_kernel_l(b in arb_extended_std_duration_bounded_f64()) {
prop_assert!(conn_laws::kernel_l(&F064SDUR, b));
}
#[test]
fn f064sdur_monotone_l(a1 in extended_float_f64(), a2 in extended_float_f64()) {
prop_assert!(conn_laws::monotone_l(&F064SDUR, a1, a2));
}
#[test]
fn f064sdur_idempotent(a in extended_float_f64()) {
prop_assert!(conn_laws::idempotent_l(&F064SDUR, a));
}
#[test]
fn f032sdur_galois_l(a in extended_float_f32(),
b in arb_extended_std_duration_bounded_f32()) {
prop_assert!(conn_laws::galois_l(&F032SDUR, a, b));
}
#[test]
fn f032sdur_closure_l(a in extended_float_f32()) {
prop_assert!(conn_laws::closure_l(&F032SDUR, a));
}
#[test]
fn f032sdur_kernel_l(b in arb_extended_std_duration_bounded_f32()) {
prop_assert!(conn_laws::kernel_l(&F032SDUR, b));
}
#[test]
fn f032sdur_monotone_l(a1 in extended_float_f32(), a2 in extended_float_f32()) {
prop_assert!(conn_laws::monotone_l(&F032SDUR, a1, a2));
}
#[test]
fn f032sdur_idempotent(a in extended_float_f32()) {
prop_assert!(conn_laws::idempotent_l(&F032SDUR, a));
}
}
proptest! {
#![proptest_config(ProptestConfig {
cases: 64,
max_shrink_iters: 200,
.. ProptestConfig::default()
})]
#[test]
fn f64_sdur_negative_input_ceil_to_zero(v in -1.0e9_f64..0.0_f64) {
let a = N5::new(v);
prop_assert_eq!(F064SDUR.ceil(a), Extended::Finite(StdDuration::ZERO));
}
#[test]
fn f64_sdur_plateau(s in 0_u64..=1_000_000_000_u64) {
let d = StdDuration::from_secs(s);
let v = d.as_secs_f64();
let a = N5::new(v);
if let Extended::Finite(c) = F064SDUR.ceil(a) {
prop_assert!(c <= d, "ceil={c:?} d={d:?}");
prop_assert_eq!(c.as_secs_f64(), v);
} else {
panic!("expected Finite ceil at v={v}");
}
}
#[test]
fn f64_sdur_solve_matches_walk_in_safe_range(v in 1.0e-3_f64..1.0e6_f64) {
let est = StdDuration::from_secs_f64(v);
let est_widen = est.as_secs_f64();
let (walk_z, _) = if est_widen >= v {
f64_sdur_walks::descend_to_ceil(est, v)
} else {
f64_sdur_walks::ascend_to_ceil(est, v)
};
let (solve_z, steps) = f64_sdur_walks::solve_to_ceil(est, v);
prop_assert_eq!(solve_z, walk_z);
prop_assert!(steps <= 52, "solve_to_ceil took {steps} steps");
}
#[test]
fn f32_sdur_solve_matches_walk_in_safe_range(v in 1.0e-3_f32..1.0_f32) {
let est = StdDuration::from_secs_f32(v);
let est_widen = est.as_secs_f32();
let (walk_z, _) = if est_widen >= v {
f32_sdur_walks::descend_to_ceil(est, v)
} else {
f32_sdur_walks::ascend_to_ceil(est, v)
};
let (solve_z, steps) = f32_sdur_walks::solve_to_ceil(est, v);
prop_assert_eq!(solve_z, walk_z);
prop_assert!(steps <= 52, "solve_to_ceil took {steps} steps");
}
}
#[test]
fn f64_sdur_solve_terminates_walk_pathological_magnitude() {
let _ = F064SDUR.ceil(N5::new(1.0e10_f64));
}
#[test]
fn f32_sdur_solve_terminates_walk_pathological_magnitude() {
let _ = F032SDUR.ceil(N5::new(1.0e6_f32));
}
}