1use crate::types::Number;
10use std::cell::Cell;
11use std::sync::OnceLock;
12use std::time::{Instant, SystemTime, UNIX_EPOCH};
13
14#[inline]
16pub fn is_finite_number(val: Number) -> bool {
17 val.is_finite()
18}
19
20#[inline]
24pub fn compare_le(lhs: Number, rhs: Number, bas_val: Number) -> bool {
25 let tol = 10.0 * Number::EPSILON * bas_val.abs().max(1.0);
26 lhs - rhs <= tol
27}
28
29pub fn ip_random_01() -> Number {
33 LCG_STATE.with(|s| {
34 let mut x = s.get();
35 x = x.wrapping_mul(LCG_A).wrapping_add(LCG_C) & LCG_MASK;
36 s.set(x);
37 let x0 = (x & 0xFFFF) as f64;
43 let x1 = ((x >> 16) & 0xFFFF) as f64;
44 let x2 = ((x >> 32) & 0xFFFF) as f64;
45 x2 / 65536.0 + x1 / (65536.0 * 65536.0) + x0 / (65536.0 * 65536.0 * 65536.0)
46 })
47}
48
49pub fn ip_reset_random_01() {
52 LCG_STATE.with(|s| s.set(LCG_DEFAULT_SEED));
53}
54
55const LCG_A: u64 = 0x5DEECE66D;
56const LCG_C: u64 = 0xB;
57const LCG_MASK: u64 = (1 << 48) - 1;
58const LCG_DEFAULT_SEED: u64 = 0x1234_ABCD_330E;
59
60thread_local! {
61 static LCG_STATE: Cell<u64> = const { Cell::new(LCG_DEFAULT_SEED) };
62}
63
64pub fn wallclock_time() -> Number {
67 static EPOCH: OnceLock<Instant> = OnceLock::new();
68 let e = EPOCH.get_or_init(Instant::now);
69 e.elapsed().as_secs_f64()
70}
71
72pub fn cpu_time() -> Number {
80 wallclock_time()
81}
82
83pub fn sys_time() -> Number {
86 SystemTime::now()
87 .duration_since(UNIX_EPOCH)
88 .map(|d| d.as_secs_f64())
89 .unwrap_or(0.0)
90}
91
92pub fn compute_mem_increase_i64(
97 len: &mut i64,
98 recommended: f64,
99 min: i64,
100 context: &str,
101) -> Result<(), String> {
102 if recommended >= i64::MAX as f64 {
103 if *len < i64::MAX {
104 *len = i64::MAX;
105 Ok(())
106 } else {
107 Err(format!(
108 "Cannot allocate more than {} bytes for {} due to integer-type limit",
109 (i64::MAX as f64) * 8.0,
110 context
111 ))
112 }
113 } else {
114 *len = min.max(recommended as i64);
115 Ok(())
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn finite_check() {
125 assert!(is_finite_number(0.0));
126 assert!(is_finite_number(1e300));
127 assert!(!is_finite_number(f64::NAN));
128 assert!(!is_finite_number(f64::INFINITY));
129 }
130
131 #[test]
132 fn compare_le_tolerates_eps() {
133 let v = 1.0;
134 let near = v + 5.0 * Number::EPSILON;
135 assert!(compare_le(near, v, v));
136 }
137
138 #[test]
139 fn random_01_in_range_and_deterministic() {
140 ip_reset_random_01();
141 let a: Vec<f64> = (0..16).map(|_| ip_random_01()).collect();
142 ip_reset_random_01();
143 let b: Vec<f64> = (0..16).map(|_| ip_random_01()).collect();
144 assert_eq!(a, b);
145 for v in a {
146 assert!((0.0..1.0).contains(&v), "{v}");
147 }
148 }
149
150 #[test]
151 fn wallclock_monotonic() {
152 let a = wallclock_time();
153 let b = wallclock_time();
154 assert!(b >= a);
155 }
156}