use crate::extended::Extended;
use crate::float::{F032, F064, N5, def_walk_helpers};
use hifitime::Duration as HD;
#[inline]
fn hd_min_nanos() -> i128 {
HD::MIN.total_nanoseconds()
}
#[inline]
fn hd_max_nanos() -> i128 {
HD::MAX.total_nanoseconds()
}
#[inline]
fn hd_min_secs() -> i64 {
(HD::MIN.total_nanoseconds() / 1_000_000_000) as i64
}
#[inline]
fn hd_max_secs() -> i64 {
(HD::MAX.total_nanoseconds() / 1_000_000_000) as i64
}
fn hdurnano_ceil(d: Extended<HD>) -> i128 {
let max_n = hd_max_nanos();
match d {
Extended::NegInf => i128::MIN,
Extended::Finite(d) => d.total_nanoseconds(),
Extended::PosInf => max_n + 1,
}
}
fn hdurnano_inner(n: i128) -> Extended<HD> {
let min_n = hd_min_nanos();
let max_n = hd_max_nanos();
if n < min_n {
Extended::NegInf
} else if n > max_n {
Extended::PosInf
} else {
Extended::Finite(HD::from_total_nanoseconds(n))
}
}
crate::conn_l! {
pub HDURNANO : Extended<HD> => i128 {
ceil: hdurnano_ceil,
inner: hdurnano_inner,
}
}
fn hdursecs_ceil(d: Extended<HD>) -> i64 {
let max_s = hd_max_secs();
match d {
Extended::NegInf => i64::MIN,
Extended::Finite(d) => {
let total_ns = d.total_nanoseconds();
let one_billion: i128 = 1_000_000_000;
let s_floor: i64 = total_ns.div_euclid(one_billion) as i64;
let sub: i128 = total_ns.rem_euclid(one_billion);
if sub > 0 {
s_floor + 1
} else {
s_floor
}
}
Extended::PosInf => max_s + 1,
}
}
fn hdursecs_inner(s: i64) -> Extended<HD> {
let min_s = hd_min_secs();
let max_s = hd_max_secs();
if s < min_s {
Extended::NegInf
} else if s > max_s {
Extended::PosInf
} else {
Extended::Finite(HD::from_total_nanoseconds((s as i128) * 1_000_000_000))
}
}
crate::conn_l! {
pub HDURSECS : Extended<HD> => i64 {
ceil: hdursecs_ceil,
inner: hdursecs_inner,
}
}
pub(crate) fn shift_hd(n: i32, d: HD) -> HD {
let cur_ns = d.total_nanoseconds();
let new_ns = cur_ns.saturating_add(n as i128);
let max_ns = hd_max_nanos();
let min_ns = hd_min_nanos();
if new_ns >= max_ns {
HD::MAX
} else if new_ns <= min_ns {
HD::MIN
} else {
HD::from_total_nanoseconds(new_ns)
}
}
#[inline]
fn hd_to_f64(d: HD) -> f64 {
d.to_seconds()
}
#[inline]
fn hd_to_f32(d: HD) -> f32 {
d.to_seconds() as f32
}
#[inline]
pub(crate) fn hd_to_ns(d: HD) -> i128 {
d.total_nanoseconds()
}
#[inline]
pub(crate) fn hd_from_ns(n: i128) -> HD {
let max_ns = hd_max_nanos();
let min_ns = hd_min_nanos();
if n >= max_ns {
HD::MAX
} else if n <= min_ns {
HD::MIN
} else {
HD::from_total_nanoseconds(n)
}
}
def_walk_helpers!(
f64_hdur_walks,
f64,
HD,
shift_hd,
hd_to_f64,
hd_to_ns,
hd_from_ns
);
def_walk_helpers!(
f32_hdur_walks,
f32,
HD,
shift_hd,
hd_to_f32,
hd_to_ns,
hd_from_ns
);
fn f064hdur_ceil(x: F064) -> Extended<HD> {
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 = hd_max_secs() as f64;
let min_secs = hd_min_secs() as f64;
if v > max_secs {
return Extended::PosInf;
}
if v <= min_secs {
return Extended::Finite(HD::MIN);
}
let est = HD::from_seconds(v);
let (z, _) = f64_hdur_walks::solve_to_ceil(est, v);
Extended::Finite(z)
}
fn f064hdur_inner(d: Extended<HD>) -> F064 {
match d {
Extended::NegInf => N5::new(f64::NEG_INFINITY),
Extended::Finite(d) => N5::new(d.to_seconds()),
Extended::PosInf => N5::new(f64::INFINITY),
}
}
crate::conn_l! {
pub F064HDUR : F064 => Extended<HD> {
ceil: f064hdur_ceil,
inner: f064hdur_inner,
}
}
fn f032hdur_ceil(x: F032) -> Extended<HD> {
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 = hd_max_secs() as f32;
let min_secs = hd_min_secs() as f32;
if v > max_secs {
return Extended::PosInf;
}
if v <= min_secs {
return Extended::Finite(HD::MIN);
}
let est = HD::from_seconds(v as f64);
let (z, _) = f32_hdur_walks::solve_to_ceil(est, v);
Extended::Finite(z)
}
fn f032hdur_inner(d: Extended<HD>) -> F032 {
match d {
Extended::NegInf => N5::new(f32::NEG_INFINITY),
Extended::Finite(d) => N5::new(d.to_seconds() as f32),
Extended::PosInf => N5::new(f32::INFINITY),
}
}
crate::conn_l! {
pub F032HDUR : F032 => Extended<HD> {
ceil: f032hdur_ceil,
inner: f032hdur_inner,
}
}
#[cfg(kani)]
pub(crate) fn f64_hdur_ceil_walk_steps_for_proof(v: f64) -> (HD, u32) {
let est = HD::from_seconds(v);
let est_widen = est.to_seconds();
if est_widen >= v {
f64_hdur_walks::descend_to_ceil(est, v)
} else {
f64_hdur_walks::ascend_to_ceil(est, v)
}
}
#[cfg(kani)]
pub(crate) fn f32_hdur_ceil_walk_steps_for_proof(v: f32) -> (HD, u32) {
let est = HD::from_seconds(v as f64);
let est_widen = est.to_seconds() as f32;
if est_widen >= v {
f32_hdur_walks::descend_to_ceil(est, v)
} else {
f32_hdur_walks::ascend_to_ceil(est, v)
}
}
#[cfg(kani)]
pub(crate) fn f64_hdur_ceil_solve_steps_for_proof(v: f64) -> (HD, u32) {
let est = HD::from_seconds(v);
f64_hdur_walks::solve_to_ceil(est, v)
}
#[cfg(kani)]
pub(crate) fn f32_hdur_ceil_solve_steps_for_proof(v: f32) -> (HD, u32) {
let est = HD::from_seconds(v as f64);
f32_hdur_walks::solve_to_ceil(est, v)
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(unused_imports)]
use crate::conn::{ConnL, ConnR};
use crate::prop::arb::{
arb_extended_hifi_duration, arb_extended_hifi_duration_bounded_f32,
arb_extended_hifi_duration_bounded_f64, arb_hifi_duration, arb_hifi_total_nanos_in_range,
arb_hifi_total_secs_in_range, extended_float_f32, extended_float_f64,
};
use crate::prop::{conn as conn_laws, lattice as lattice_laws};
use proptest::prelude::*;
#[test]
fn hd_min_max_total_ns_divisible_by_billion() {
assert_eq!(HD::MIN.total_nanoseconds() % 1_000_000_000, 0);
assert_eq!(HD::MAX.total_nanoseconds() % 1_000_000_000, 0);
}
mod hifi_duration_preorder {
use super::*;
#[allow(unused_imports)]
use crate::conn::{ConnL, ConnR};
proptest! {
#[test]
fn reflexive(x in arb_hifi_duration()) {
prop_assert!(lattice_laws::lattice_reflexive(&x));
}
#[test]
fn transitive(x in arb_hifi_duration(),
y in arb_hifi_duration(),
z in arb_hifi_duration()) {
prop_assert!(lattice_laws::lattice_transitive(&x, &y, &z));
}
#[test]
fn antisymmetric(x in arb_hifi_duration(), y in arb_hifi_duration()) {
prop_assert!(lattice_laws::lattice_antisymmetric(&x, &y));
}
#[test]
fn bot(x in arb_hifi_duration()) {
prop_assert!(lattice_laws::lattice_bot(&HD::MIN, &x));
}
#[test]
fn top(x in arb_hifi_duration()) {
prop_assert!(lattice_laws::lattice_top(&HD::MAX, &x));
}
}
}
#[test]
fn hdurnano_zero() {
let z = Extended::Finite(HD::ZERO);
assert_eq!(HDURNANO.ceil(z), 0_i128);
assert_eq!(HDURNANO.upper(0_i128), z);
}
#[test]
fn hdurnano_one_ns_round_trip() {
let one = Extended::Finite(HD::EPSILON);
assert_eq!(HDURNANO.ceil(one), 1_i128);
assert_eq!(HDURNANO.upper(1_i128), one);
}
#[test]
fn hdurnano_neg_one_ns_round_trip() {
let neg = Extended::Finite(HD::MIN_NEGATIVE);
assert_eq!(HDURNANO.ceil(neg), -1_i128);
assert_eq!(HDURNANO.upper(-1_i128), neg);
}
#[test]
fn hdurnano_min_max_round_trip() {
let min_n = HD::MIN.total_nanoseconds();
let max_n = HD::MAX.total_nanoseconds();
assert_eq!(HDURNANO.ceil(Extended::Finite(HD::MIN)), min_n);
assert_eq!(HDURNANO.ceil(Extended::Finite(HD::MAX)), max_n);
assert_eq!(HDURNANO.upper(min_n), Extended::Finite(HD::MIN));
assert_eq!(HDURNANO.upper(max_n), Extended::Finite(HD::MAX));
}
#[test]
fn hdurnano_saturation_extremes() {
assert_eq!(HDURNANO.upper(i128::MAX), Extended::PosInf);
assert_eq!(HDURNANO.upper(i128::MIN), Extended::NegInf);
let max_n = HD::MAX.total_nanoseconds();
assert_eq!(HDURNANO.ceil(Extended::PosInf), max_n + 1);
assert_eq!(HDURNANO.ceil(Extended::NegInf), i128::MIN);
}
#[test]
fn hdursecs_zero() {
let z = Extended::Finite(HD::ZERO);
assert_eq!(HDURSECS.ceil(z), 0_i64);
assert_eq!(HDURSECS.upper(0_i64), z);
}
#[test]
fn hdursecs_positive_subsec_rounds_up() {
let d = Extended::Finite(HD::from_total_nanoseconds(5 * 1_000_000_000 + 1));
assert_eq!(HDURSECS.ceil(d), 6_i64);
}
#[test]
fn hdursecs_negative_subsec_rounds_toward_zero() {
let d = Extended::Finite(HD::from_total_nanoseconds(-(5 * 1_000_000_000 + 1)));
assert_eq!(HDURSECS.ceil(d), -5_i64);
}
#[test]
fn hdursecs_synthetic_arms() {
let max_s = (HD::MAX.total_nanoseconds() / 1_000_000_000) as i64;
assert_eq!(HDURSECS.ceil(Extended::NegInf), i64::MIN);
assert_eq!(HDURSECS.ceil(Extended::PosInf), max_s + 1);
assert_eq!(HDURSECS.upper(i64::MIN), Extended::NegInf);
assert_eq!(HDURSECS.upper(i64::MAX), Extended::PosInf);
}
#[test]
fn hdursecs_inner_saturation_boundary() {
let min_s = (HD::MIN.total_nanoseconds() / 1_000_000_000) as i64;
let max_s = (HD::MAX.total_nanoseconds() / 1_000_000_000) as i64;
assert!(matches!(HDURSECS.upper(min_s), Extended::Finite(_)));
assert!(matches!(HDURSECS.upper(max_s), Extended::Finite(_)));
assert_eq!(HDURSECS.upper(min_s - 1), Extended::NegInf);
assert_eq!(HDURSECS.upper(max_s + 1), Extended::PosInf);
}
proptest! {
#[test]
fn hdurnano_galois_l(d in arb_extended_hifi_duration(), b in any::<i128>()) {
prop_assert!(conn_laws::galois_l(&HDURNANO, d, b));
}
#[test]
fn hdurnano_closure_l(d in arb_extended_hifi_duration()) {
prop_assert!(conn_laws::closure_l(&HDURNANO, d));
}
#[test]
fn hdurnano_kernel_l(b in any::<i128>()) {
prop_assert!(conn_laws::kernel_l(&HDURNANO, b));
}
#[test]
fn hdurnano_monotone_l(a in arb_extended_hifi_duration(),
b in arb_extended_hifi_duration()) {
prop_assert!(conn_laws::monotone_l(&HDURNANO, a, b));
}
#[test]
fn hdurnano_idempotent(d in arb_extended_hifi_duration()) {
prop_assert!(conn_laws::idempotent(&HDURNANO, d));
}
#[test]
fn hdurnano_roundtrip_ceil(b in arb_hifi_total_nanos_in_range()) {
prop_assert!(conn_laws::roundtrip_ceil(&HDURNANO, b));
}
}
proptest! {
#[test]
fn hdursecs_galois_l(d in arb_extended_hifi_duration(), b in any::<i64>()) {
prop_assert!(conn_laws::galois_l(&HDURSECS, d, b));
}
#[test]
fn hdursecs_closure_l(d in arb_extended_hifi_duration()) {
prop_assert!(conn_laws::closure_l(&HDURSECS, d));
}
#[test]
fn hdursecs_kernel_l(b in any::<i64>()) {
prop_assert!(conn_laws::kernel_l(&HDURSECS, b));
}
#[test]
fn hdursecs_monotone_l(a in arb_extended_hifi_duration(),
b in arb_extended_hifi_duration()) {
prop_assert!(conn_laws::monotone_l(&HDURSECS, a, b));
}
#[test]
fn hdursecs_idempotent(d in arb_extended_hifi_duration()) {
prop_assert!(conn_laws::idempotent(&HDURSECS, d));
}
#[test]
fn hdursecs_roundtrip_ceil(s in arb_hifi_total_secs_in_range()) {
prop_assert!(conn_laws::roundtrip_ceil(&HDURSECS, s));
}
}
#[test]
fn f64_hdur_zero() {
let zero = N5::new(0.0_f64);
assert_eq!(F064HDUR.ceil(zero), Extended::Finite(HD::ZERO));
assert_eq!(F064HDUR.upper(Extended::Finite(HD::ZERO)), zero);
}
#[test]
fn f64_hdur_half_second() {
let half = N5::new(0.5_f64);
let half_d = HD::from_seconds(0.5);
assert_eq!(F064HDUR.ceil(half), Extended::Finite(half_d));
assert_eq!(F064HDUR.upper(Extended::Finite(half_d)), half);
}
#[test]
fn f64_hdur_nan_arms() {
let nan = N5::new(f64::NAN);
assert_eq!(F064HDUR.ceil(nan), Extended::PosInf);
}
#[test]
fn f64_hdur_infinity_arms() {
let pos_inf = N5::new(f64::INFINITY);
assert_eq!(F064HDUR.ceil(pos_inf), Extended::PosInf);
let neg_inf = N5::new(f64::NEG_INFINITY);
assert_eq!(F064HDUR.ceil(neg_inf), Extended::NegInf);
}
#[test]
fn f64_hdur_extended_bounds_upper_to_infinities() {
assert_eq!(F064HDUR.upper(Extended::NegInf), N5::new(f64::NEG_INFINITY));
assert_eq!(F064HDUR.upper(Extended::PosInf), N5::new(f64::INFINITY));
}
#[test]
fn f64_hdur_ceil_min_secs_fast_path() {
let v_min = N5::new(hd_min_secs() as f64);
assert_eq!(F064HDUR.ceil(v_min), Extended::Finite(HD::MIN));
}
#[test]
fn f32_hdur_zero() {
let zero = N5::new(0.0_f32);
assert_eq!(F032HDUR.ceil(zero), Extended::Finite(HD::ZERO));
}
#[test]
fn f32_hdur_one_second_in_plateau() {
let one = N5::new(1.0_f32);
if let Extended::Finite(c) = F032HDUR.ceil(one) {
assert_eq!(c.to_seconds() as f32, 1.0_f32);
} else {
panic!("ceil(1.0) should be Finite");
}
}
#[test]
fn f32_hdur_nan_arms() {
let nan = N5::new(f32::NAN);
assert_eq!(F032HDUR.ceil(nan), Extended::PosInf);
}
#[test]
fn f32_hdur_infinity_arms() {
let pos_inf = N5::new(f32::INFINITY);
assert_eq!(F032HDUR.ceil(pos_inf), Extended::PosInf);
let neg_inf = N5::new(f32::NEG_INFINITY);
assert_eq!(F032HDUR.ceil(neg_inf), Extended::NegInf);
}
#[test]
fn f32_hdur_extended_bounds_upper_to_infinities() {
assert_eq!(F032HDUR.upper(Extended::NegInf), N5::new(f32::NEG_INFINITY));
assert_eq!(F032HDUR.upper(Extended::PosInf), N5::new(f32::INFINITY));
}
proptest! {
#![proptest_config(ProptestConfig { cases: 64, .. ProptestConfig::default() })]
#[test]
fn f064hdur_galois_l(a in extended_float_f64(),
b in arb_extended_hifi_duration_bounded_f64()) {
prop_assert!(conn_laws::galois_l(&F064HDUR, a, b));
}
#[test]
fn f064hdur_closure_l(a in extended_float_f64()) {
prop_assert!(conn_laws::closure_l(&F064HDUR, a));
}
#[test]
fn f064hdur_kernel_l(b in arb_extended_hifi_duration_bounded_f64()) {
prop_assert!(conn_laws::kernel_l(&F064HDUR, b));
}
#[test]
fn f064hdur_monotone_l(a1 in extended_float_f64(), a2 in extended_float_f64()) {
prop_assert!(conn_laws::monotone_l(&F064HDUR, a1, a2));
}
#[test]
fn f064hdur_idempotent(a in extended_float_f64()) {
prop_assert!(conn_laws::idempotent_l(&F064HDUR, a));
}
#[test]
fn f032hdur_galois_l(a in extended_float_f32(),
b in arb_extended_hifi_duration_bounded_f32()) {
prop_assert!(conn_laws::galois_l(&F032HDUR, a, b));
}
#[test]
fn f032hdur_closure_l(a in extended_float_f32()) {
prop_assert!(conn_laws::closure_l(&F032HDUR, a));
}
#[test]
fn f032hdur_kernel_l(b in arb_extended_hifi_duration_bounded_f32()) {
prop_assert!(conn_laws::kernel_l(&F032HDUR, b));
}
#[test]
fn f032hdur_monotone_l(a1 in extended_float_f32(), a2 in extended_float_f32()) {
prop_assert!(conn_laws::monotone_l(&F032HDUR, a1, a2));
}
#[test]
fn f032hdur_idempotent(a in extended_float_f32()) {
prop_assert!(conn_laws::idempotent_l(&F032HDUR, a));
}
#[test]
fn f64_hdur_solve_matches_walk_in_safe_range(v in -1.0e6_f64..1.0e6_f64) {
let est = HD::from_seconds(v);
let est_widen = est.to_seconds();
let (walk_z, _) = if est_widen >= v {
f64_hdur_walks::descend_to_ceil(est, v)
} else {
f64_hdur_walks::ascend_to_ceil(est, v)
};
let (solve_z, steps) = f64_hdur_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 f064_hdur_solve_terminates_at_max_rim() {
let v = N5::new(1.0e13_f64);
let _ = F064HDUR.ceil(v);
}
#[test]
fn f064_hdur_solve_terminates_at_unsigned_min_rim() {
let min_secs = hd_min_secs() as f64;
let _ = F064HDUR.ceil(N5::new(min_secs * 0.9_f64));
}
}