pub const DEFAULT_FLOAT_PRECISION_DIGITS: i32 = 6;
pub const fn precision_to_step_size(precision_digits: i32) -> f64 {
match precision_digits {
1 => 1e-1,
2 => 1e-2,
3 => 1e-3,
4 => 1e-4,
5 => 1e-5,
6 => 1e-6,
7 => 1e-7,
8 => 1e-8,
9 => 1e-9,
10 => 1e-10,
11 => 1e-11,
12 => 1e-12,
_ => 1e-6, }
}
pub const FLOAT_STEP_SIZE: f64 = precision_to_step_size(DEFAULT_FLOAT_PRECISION_DIGITS);
pub const MAX_REASONABLE_STEPS: usize = 1_000_000;
#[derive(Debug, Clone, PartialEq)]
pub struct FloatIntervalState {
pub min: f64,
pub max: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FloatInterval {
pub min: f64,
pub max: f64,
pub step: f64,
}
impl FloatInterval {
pub fn new(min: f64, max: f64) -> Self {
if min > max {
return Self::new(max, min);
}
let domain_range = max - min;
let step = if domain_range >= 1048576.0 { domain_range / 512.0 } else if domain_range >= 16384.0 { 32.0 } else if domain_range >= 512.0 { 1.0 } else if domain_range >= 16.0 { 0.03125 } else if domain_range >= 0.5 { 0.0009765625 } else if domain_range >= 0.00048828125 { 0.00000095367432 } else {
0.00000000093132257 };
FloatInterval { min, max, step }
}
pub fn with_step(min: f64, max: f64, step: f64) -> Self {
if min > max {
return Self::with_step(max, min, step);
}
FloatInterval { min, max, step }
}
pub fn with_step_unchecked(min: f64, max: f64, step: f64) -> Self {
FloatInterval { min, max, step }
}
pub fn next(&self, value: f64) -> f64 {
let ulp = crate::optimization::ulp_utils::UlpUtils::ulp(value);
let effective_step = if self.step < ulp {
let next = crate::optimization::ulp_utils::UlpUtils::next_float(value);
if next > self.max {
return self.max;
}
return next;
} else {
self.step
};
let next_val = value + effective_step;
if next_val > self.max {
self.max
} else {
next_val
}
}
pub fn prev(&self, value: f64) -> f64 {
let ulp = crate::optimization::ulp_utils::UlpUtils::ulp(value);
let effective_step = if self.step < ulp {
let prev = crate::optimization::ulp_utils::UlpUtils::prev_float(value);
if prev < self.min {
return self.min;
}
return prev;
} else {
self.step
};
let prev_val = value - effective_step;
if prev_val < self.min {
self.min
} else {
prev_val
}
}
pub fn contains(&self, value: f64) -> bool {
let tolerance = self.step / 2.0;
value >= self.min - tolerance && value <= self.max + tolerance
}
pub fn is_empty(&self) -> bool {
self.min > self.max
}
pub fn is_fixed(&self) -> bool {
self.step_count() <= 1
}
pub fn size(&self) -> f64 {
if self.is_empty() {
0.0
} else {
self.max - self.min
}
}
pub fn step_count(&self) -> usize {
if self.is_empty() {
0
} else {
((self.max - self.min) / self.step).round() as usize
}
}
pub fn round_to_step(&self, value: f64) -> f64 {
let steps_from_min = ((value - self.min) / self.step).round();
let rounded = self.min + steps_from_min * self.step;
rounded.clamp(self.min, self.max)
}
pub fn floor_to_step(&self, value: f64) -> f64 {
let steps_from_min = ((value - self.min) / self.step).floor();
let rounded = self.min + steps_from_min * self.step;
rounded.clamp(self.min, self.max)
}
pub fn ceil_to_step(&self, value: f64) -> f64 {
let steps_from_min = ((value - self.min) / self.step).ceil();
let rounded = self.min + steps_from_min * self.step;
rounded.clamp(self.min, self.max)
}
pub fn intersect(&self, other: &FloatInterval) -> FloatInterval {
let min = self.min.max(other.min);
let max = self.max.min(other.max);
let step = self.step.min(other.step);
FloatInterval { min, max, step }
}
pub fn intersects(&self, other: &FloatInterval) -> bool {
self.min <= other.max && self.max >= other.min
}
pub fn assign(&mut self, value: f64) {
let rounded_value = self.round_to_step(value);
self.min = rounded_value;
self.max = rounded_value;
}
pub fn remove_below(&mut self, threshold: f64) {
let tolerance = self.step / 2.0;
if threshold > self.max + tolerance {
self.max = self.min - 1.0;
} else if threshold > self.min + tolerance {
self.min = self.ceil_to_step(threshold);
if self.min > self.max + tolerance {
self.max = self.min - 1.0; }
}
}
pub fn remove_above(&mut self, threshold: f64) {
let tolerance = self.step / 2.0;
if threshold < self.min - tolerance {
self.max = self.min - 1.0;
} else if threshold < self.max - tolerance {
self.max = self.floor_to_step(threshold);
if self.max < self.min - tolerance {
self.max = self.min - 1.0; }
}
}
pub fn mid(&self) -> f64 {
if self.is_empty() {
return self.min; }
if self.is_fixed() {
return self.min; }
let rough_mid = if self.min.is_infinite() && self.max.is_infinite() {
0.0
} else if self.min.is_infinite() {
self.max - 1.0
} else if self.max.is_infinite() {
self.min + 1.0
} else {
self.min + (self.max - self.min) / 2.0
};
self.round_to_step(rough_mid)
}
}
impl std::fmt::Display for FloatInterval {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_empty() {
write!(f, "[]")
} else if self.is_fixed() {
write!(f, "[{}]", self.min)
} else {
write!(f, "[{}, {}] (step: {})", self.min, self.max, self.step)
}
}
}
impl FloatInterval {
pub fn save_state(&self) -> FloatIntervalState {
FloatIntervalState {
min: self.min,
max: self.max,
}
}
pub fn restore_state(&mut self, state: &FloatIntervalState) {
self.min = state.min;
self.max = state.max;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_interval() {
let interval = FloatInterval::new(0.0, 1.0);
assert_eq!(interval.min, 0.0);
assert_eq!(interval.max, 1.0);
assert_eq!(interval.step, 0.0009765625);
}
#[test]
fn test_new_interval_swapped() {
let interval = FloatInterval::new(1.0, 0.0);
assert_eq!(interval.min, 0.0);
assert_eq!(interval.max, 1.0);
}
#[test]
fn test_with_step() {
let interval = FloatInterval::with_step(0.0, 10.0, 0.1);
assert_eq!(interval.step, 0.1);
}
#[test]
fn test_next() {
let interval = FloatInterval::with_step(0.0, 1.0, 0.1);
assert_eq!(interval.next(0.5), 0.6);
assert_eq!(interval.next(0.95), 1.0); assert_eq!(interval.next(1.0), 1.0); }
#[test]
fn test_prev() {
let interval = FloatInterval::with_step(0.0, 1.0, 0.1);
assert_eq!(interval.prev(0.5), 0.4);
assert_eq!(interval.prev(0.05), 0.0); assert_eq!(interval.prev(0.0), 0.0); }
#[test]
fn test_contains() {
let interval = FloatInterval::new(0.0, 1.0);
assert!(interval.contains(0.5));
assert!(interval.contains(0.0));
assert!(interval.contains(1.0));
assert!(!interval.contains(-0.1));
assert!(!interval.contains(1.1));
}
#[test]
fn test_is_fixed() {
let interval = FloatInterval::with_step(0.5, 0.5, 0.1);
assert!(interval.is_fixed());
let interval2 = FloatInterval::with_step(0.0, 0.1, 0.1);
assert!(interval2.is_fixed());
let interval3 = FloatInterval::with_step(0.0, 1.0, 0.1);
assert!(!interval3.is_fixed());
}
#[test]
fn test_step_count() {
let interval = FloatInterval::with_step(0.0, 1.0, 0.1);
assert_eq!(interval.step_count(), 10);
let interval2 = FloatInterval::with_step(0.0, 0.5, 0.1);
assert_eq!(interval2.step_count(), 5);
}
#[test]
fn test_round_to_step() {
let interval = FloatInterval::with_step(0.0, 1.0, 0.1);
assert!((interval.round_to_step(0.23) - 0.2).abs() < interval.step * 0.01);
assert!((interval.round_to_step(0.27) - 0.3).abs() < interval.step * 0.01);
}
#[test]
fn test_intersect() {
let interval1 = FloatInterval::with_step(0.0, 1.0, 0.1);
let interval2 = FloatInterval::with_step(0.5, 1.5, 0.05);
let intersection = interval1.intersect(&interval2);
assert_eq!(intersection.min, 0.5);
assert_eq!(intersection.max, 1.0);
assert_eq!(intersection.step, 0.05); }
#[test]
fn test_assign() {
let mut interval = FloatInterval::with_step(0.0, 1.0, 0.1);
interval.assign(0.37);
assert!(interval.is_fixed());
assert!((interval.min - 0.4).abs() < interval.step * 0.01); assert!((interval.max - 0.4).abs() < interval.step * 0.01);
}
#[test]
fn test_remove_below() {
let mut interval = FloatInterval::with_step(0.0, 1.0, 0.1);
interval.remove_below(0.35);
assert!((interval.min - 0.4).abs() < interval.step * 0.01); assert_eq!(interval.max, 1.0);
}
#[test]
fn test_remove_above() {
let mut interval = FloatInterval::with_step(0.0, 1.0, 0.1);
interval.remove_above(0.65);
println!("Debug: interval.max = {}, expected = 0.6, diff = {}",
interval.max, (interval.max - 0.6).abs());
println!("Debug: step size = {}, tolerance = {}", interval.step, interval.step * 0.01);
assert_eq!(interval.min, 0.0);
assert!((interval.max - 0.6).abs() < interval.step * 0.01); }
#[test]
fn test_display() {
let interval = FloatInterval::with_step(0.0, 1.0, 0.1);
let display = format!("{}", interval);
assert!(display.contains("[0"));
assert!(display.contains("1"));
assert!(display.contains("0.1"));
let fixed = FloatInterval::with_step(0.5, 0.5, 0.1);
assert_eq!(format!("{}", fixed), "[0.5]");
}
#[test]
fn test_empty_interval() {
let mut interval = FloatInterval::with_step(0.0, 1.0, 0.1);
interval.min = 1.0;
interval.max = 0.0;
assert!(interval.is_empty());
assert_eq!(interval.size(), 0.0);
assert_eq!(interval.step_count(), 0);
assert_eq!(format!("{}", interval), "[]");
}
#[test]
fn test_very_small_intervals() {
let interval = FloatInterval::with_step(1e-6, 2e-6, 1e-8);
assert!(!interval.is_empty());
assert_eq!(interval.step_count(), 100);
let next_val = interval.next(1.5e-6);
assert!((next_val - 1.51e-6).abs() < 1e-10);
}
#[test]
fn test_large_intervals() {
let interval = FloatInterval::with_step(1e3, 1e6, 1e1);
assert_eq!(interval.step_count(), 99900);
let next_val = interval.next(5e5);
assert_eq!(next_val, 5e5 + 1e1);
}
#[test]
fn test_negative_intervals() {
let interval = FloatInterval::with_step(-10.0, -1.0, 0.5);
assert_eq!(interval.step_count(), 18);
let next_val = interval.next(-5.0);
assert_eq!(next_val, -4.5);
let prev_val = interval.prev(-5.0);
assert_eq!(prev_val, -5.5);
}
#[test]
fn test_mixed_sign_intervals() {
let interval = FloatInterval::with_step(-5.0, 5.0, 1.0);
assert_eq!(interval.step_count(), 10);
assert!(interval.contains(0.0));
assert_eq!(interval.next(-0.5), 0.5);
assert_eq!(interval.prev(0.5), -0.5);
}
#[test]
fn test_very_fine_steps() {
let interval = FloatInterval::with_step(0.0, 1.0, 1e-9);
assert_eq!(interval.step_count(), 1_000_000_000);
let val = 0.123456789;
let next_val = interval.next(val);
assert!((next_val - val - 1e-9).abs() < 1e-8);
}
#[test]
fn test_very_coarse_steps() {
let interval = FloatInterval::with_step(0.0, 1000.0, 100.0);
assert_eq!(interval.step_count(), 10);
let rounded = interval.round_to_step(237.0);
assert_eq!(rounded, 200.0);
let rounded2 = interval.round_to_step(278.0);
assert_eq!(rounded2, 300.0); }
#[test]
fn test_single_step_intervals() {
let interval = FloatInterval::with_step(1.0, 2.0, 1.0);
assert_eq!(interval.step_count(), 1);
assert!(interval.is_fixed());
assert_eq!(interval.next(1.0), 2.0);
assert_eq!(interval.next(1.5), 2.0); assert_eq!(interval.prev(2.0), 1.0);
assert_eq!(interval.prev(1.5), 1.0); }
#[test]
fn test_zero_width_intervals() {
let interval = FloatInterval::with_step(5.0, 5.0, 0.1);
assert_eq!(interval.step_count(), 0);
assert!(interval.is_fixed());
assert_eq!(interval.next(5.0), 5.0);
assert_eq!(interval.prev(5.0), 5.0);
}
#[test]
fn test_rounding_precision() {
let cases = vec![
(0.0, 1.0, 0.1, 0.23, 0.2),
(0.0, 1.0, 0.1, 0.27, 0.3),
(0.0, 100.0, 10.0, 23.0, 20.0),
(0.0, 100.0, 10.0, 27.0, 30.0),
(-5.0, 5.0, 0.5, 1.23, 1.0),
(-5.0, 5.0, 0.5, 1.77, 2.0),
];
for (min, max, step, input, expected) in cases {
let interval = FloatInterval::with_step(min, max, step);
let rounded = interval.round_to_step(input);
assert!((rounded - expected).abs() < 1e-6,
"Failed for input {} with step {}: got {}, expected {}",
input, step, rounded, expected);
}
}
#[test]
fn test_boundary_conditions() {
let interval = FloatInterval::with_step(0.0, 1.0, 0.1);
assert_eq!(interval.next(1.0), 1.0); assert_eq!(interval.prev(0.0), 0.0);
assert_eq!(interval.round_to_step(-0.05), 0.0); assert_eq!(interval.round_to_step(1.05), 1.0); }
#[test]
fn test_step_alignment() {
let interval = FloatInterval::with_step(0.0, 1.0, 0.1);
let val = 0.3;
let next_val = interval.next(val);
let prev_of_next = interval.prev(next_val);
assert!((prev_of_next - val).abs() < 1e-6);
}
#[test]
fn test_intersection_edge_cases() {
let interval1 = FloatInterval::with_step(0.0, 1.0, 0.1);
let interval2 = FloatInterval::with_step(2.0, 3.0, 0.1);
let intersection = interval1.intersect(&interval2);
assert!(intersection.is_empty());
assert!(!interval1.intersects(&interval2));
let interval3 = FloatInterval::with_step(1.0, 2.0, 0.1);
let intersection2 = interval1.intersect(&interval3);
assert!(intersection2.is_fixed()); }
#[test]
fn test_remove_operations_edge_cases() {
let mut interval = FloatInterval::with_step(0.0, 1.0, 0.1);
interval.remove_above(-0.5);
assert!(interval.is_empty());
let mut interval2 = FloatInterval::with_step(0.0, 1.0, 0.1);
interval2.remove_below(1.5);
assert!(interval2.is_empty());
let mut interval3 = FloatInterval::with_step(0.0, 1.0, 0.1);
interval3.remove_below(0.45);
interval3.remove_above(0.55);
assert!(interval3.is_fixed());
assert!((interval3.min - 0.5).abs() < 1e-6);
}
#[test]
fn test_step_size_consistency() {
let interval = FloatInterval::with_step(0.0, 10.0, 0.5);
let mut interval_copy = interval.clone();
interval_copy.assign(3.7);
assert_eq!(interval_copy.step, 0.5);
assert!((interval_copy.min - 3.5).abs() < 1e-6);
let mut interval_copy2 = interval.clone();
interval_copy2.remove_below(2.3);
interval_copy2.remove_above(7.7);
assert_eq!(interval_copy2.step, 0.5);
assert!((interval_copy2.min - 2.5).abs() < 1e-6); assert!((interval_copy2.max - 7.5).abs() < 1e-6); }
#[test]
fn test_floating_point_precision_robustness() {
let interval = FloatInterval::with_step(0.1, 0.9, 0.1);
let mut val = 0.1;
for _ in 0..8 {
val = interval.next(val);
}
assert!((val - 0.9).abs() < 1e-5);
assert!(interval.contains(val));
}
#[test]
fn test_performance_scenarios() {
let large_interval = FloatInterval::with_step(0.0, 1e6, 1.0);
assert_eq!(large_interval.step_count(), 1_000_000);
let next_val = large_interval.next(500000.0);
assert_eq!(next_val, 500001.0);
let rounded = large_interval.round_to_step(500000.7);
assert_eq!(rounded, 500001.0);
}
#[test]
fn test_default_step_size_behavior() {
let interval = FloatInterval::new(0.0, 1.0);
assert_eq!(interval.step, 0.0009765625);
let small_interval = FloatInterval::new(0.0, 0.01);
let large_interval = FloatInterval::new(0.0, 100.0);
assert_eq!(small_interval.step, 0.00000095367432);
assert_eq!(large_interval.step, 0.03125);
assert_ne!(interval.step, small_interval.step);
assert_ne!(interval.step, large_interval.step);
assert!(interval.step_count() <= 2048); assert!(small_interval.step_count() <= 20000); assert!(large_interval.step_count() <= 4096);
let mid = (interval.min + interval.max) / 2.0;
let next_mid = interval.next(mid);
assert!(next_mid > mid);
assert!((next_mid - mid - interval.step).abs() < f64::EPSILON);
}
#[test]
fn test_mid_respects_step_boundaries() {
let interval = FloatInterval::with_step(0.0, 1.0, 0.1);
let mid_val = interval.mid();
assert!((mid_val - interval.round_to_step(mid_val)).abs() < 1e-10);
assert!(interval.contains(mid_val));
let interval2 = FloatInterval::with_step(0.0, 1.0, 0.3);
let mid_val2 = interval2.mid();
assert!((mid_val2 - interval2.round_to_step(mid_val2)).abs() < 1e-10);
assert!(interval2.contains(mid_val2));
let single_point = FloatInterval::with_step(5.0, 5.0, 0.1);
assert_eq!(single_point.mid(), 5.0);
let tiny_interval = FloatInterval::with_step(0.0, 0.1, 0.1);
assert!(tiny_interval.is_fixed());
assert_eq!(tiny_interval.mid(), 0.0); }
#[test]
fn test_save_restore_state() {
let mut interval = FloatInterval::with_step(0.0, 10.0, 0.1);
let initial_state = interval.save_state();
assert_eq!(initial_state.min, 0.0);
assert_eq!(initial_state.max, 10.0);
interval.remove_below(3.5);
interval.remove_above(7.2);
assert_ne!(interval.min, 0.0);
assert_ne!(interval.max, 10.0);
interval.restore_state(&initial_state);
assert_eq!(interval.min, 0.0);
assert_eq!(interval.max, 10.0);
assert_eq!(interval.step, 0.1);
let level1_state = interval.save_state();
interval.remove_below(2.0);
let level2_state = interval.save_state();
interval.remove_above(8.0);
interval.restore_state(&level2_state);
assert_eq!(interval.min, 2.0);
assert_eq!(interval.max, 10.0);
interval.restore_state(&level1_state);
assert_eq!(interval.min, 0.0);
assert_eq!(interval.max, 10.0);
}
}