#![forbid(unsafe_code)]
use num_traits::cast::NumCast;
use rand::random;
#[must_use]
pub(crate) fn usize_to_f64(n: usize) -> Option<f64> {
NumCast::from(n)
}
#[must_use]
pub fn y_to_time_bucket(y: f64, max_t: u32) -> Option<u32> {
let rounded = y.round();
num_traits::ToPrimitive::to_u32(&rounded).map(|t| t.min(max_t))
}
#[must_use]
pub fn f64_band_to_u32(band_index: f64, max_t: u32) -> u32 {
num_traits::ToPrimitive::to_u32(&band_index)
.unwrap_or(0)
.min(max_t)
}
#[must_use]
pub fn generate_random_float() -> f64 {
random::<f64>()
}
#[cfg(test)]
mod tests {
use super::*;
use approx::abs_diff_eq;
#[test]
fn test_generate_random_float() {
let result = generate_random_float();
assert!(result >= 0.0, "Random float should be >= 0.0");
assert!(result < 1.0, "Random float should be < 1.0");
}
#[test]
fn test_generate_random_float_multiple_calls() {
let results: Vec<f64> = (0..10).map(|_| generate_random_float()).collect();
for result in &results {
assert!(
(&0.0..&1.0).contains(&result),
"Random float {result} out of range"
);
}
let first = results[0];
let all_same = results
.iter()
.all(|&x| abs_diff_eq!(x, first, epsilon = f64::EPSILON));
assert!(!all_same, "All random values should not be identical");
}
#[test]
fn test_y_to_time_bucket_exact_integers() {
assert_eq!(y_to_time_bucket(0.0, 5), Some(0));
assert_eq!(y_to_time_bucket(1.0, 5), Some(1));
assert_eq!(y_to_time_bucket(3.0, 5), Some(3));
}
#[test]
fn test_y_to_time_bucket_rounding() {
assert_eq!(y_to_time_bucket(0.4, 5), Some(0));
assert_eq!(y_to_time_bucket(0.6, 5), Some(1));
assert_eq!(y_to_time_bucket(2.499, 5), Some(2));
assert_eq!(y_to_time_bucket(2.501, 5), Some(3));
}
#[test]
fn test_y_to_time_bucket_clamping() {
assert_eq!(y_to_time_bucket(10.0, 3), Some(3));
assert_eq!(y_to_time_bucket(100.0, 0), Some(0));
}
#[test]
fn test_y_to_time_bucket_negative() {
assert_eq!(y_to_time_bucket(-1.0, 5), None);
assert_eq!(y_to_time_bucket(-0.6, 5), None);
}
#[test]
fn test_y_to_time_bucket_nan_inf() {
assert_eq!(y_to_time_bucket(f64::NAN, 5), None);
assert_eq!(y_to_time_bucket(f64::INFINITY, 5), None);
assert_eq!(y_to_time_bucket(f64::NEG_INFINITY, 5), None);
}
#[test]
fn test_f64_band_to_u32_normal() {
assert_eq!(f64_band_to_u32(0.0, 5), 0);
assert_eq!(f64_band_to_u32(2.0, 5), 2);
assert_eq!(f64_band_to_u32(5.0, 5), 5);
}
#[test]
fn test_f64_band_to_u32_clamping() {
assert_eq!(f64_band_to_u32(10.0, 3), 3);
}
#[test]
fn test_f64_band_to_u32_negative_and_nan() {
assert_eq!(f64_band_to_u32(-1.0, 5), 0);
assert_eq!(f64_band_to_u32(f64::NAN, 5), 0);
assert_eq!(f64_band_to_u32(f64::NEG_INFINITY, 5), 0);
}
}