1use super::types::{Interval, IntervalNewtonResult};
6
7pub fn sqrt_interval(a: Interval) -> Interval {
11 assert!(a.lo >= 0.0, "sqrt_interval: lo must be ≥ 0, got {}", a.lo);
12 Interval {
13 lo: a.lo.sqrt(),
14 hi: a.hi.sqrt(),
15 }
16}
17pub fn abs_interval(a: Interval) -> Interval {
23 if a.lo >= 0.0 {
24 a
25 } else if a.hi <= 0.0 {
26 Interval {
27 lo: -a.hi,
28 hi: -a.lo,
29 }
30 } else {
31 Interval {
32 lo: 0.0,
33 hi: (-a.lo).max(a.hi),
34 }
35 }
36}
37pub fn square(a: Interval) -> Interval {
42 let lo_sq = a.lo * a.lo;
43 let hi_sq = a.hi * a.hi;
44 if a.lo <= 0.0 && a.hi >= 0.0 {
45 Interval {
46 lo: 0.0,
47 hi: lo_sq.max(hi_sq),
48 }
49 } else {
50 Interval {
51 lo: lo_sq.min(hi_sq),
52 hi: lo_sq.max(hi_sq),
53 }
54 }
55}
56pub fn pow_interval(a: Interval, n: u32) -> Interval {
58 match n {
59 0 => Interval::point(1.0),
60 1 => a,
61 2 => square(a),
62 _ => {
63 if n.is_multiple_of(2) {
64 let abs_a = abs_interval(a);
65 Interval {
66 lo: abs_a.lo.powi(n as i32),
67 hi: abs_a.hi.powi(n as i32),
68 }
69 } else {
70 Interval {
71 lo: a.lo.powi(n as i32),
72 hi: a.hi.powi(n as i32),
73 }
74 }
75 }
76 }
77}
78pub fn sin_interval(a: Interval) -> Interval {
80 let sin_lo = a.lo.sin();
81 let sin_hi = a.hi.sin();
82 let mut lo = sin_lo.min(sin_hi);
83 let mut hi = sin_lo.max(sin_hi);
84 let two_pi = 2.0 * std::f64::consts::PI;
85 let half_pi = std::f64::consts::FRAC_PI_2;
86 let k_start = ((a.lo - half_pi) / two_pi).ceil() as i64;
87 let k_end = ((a.hi - half_pi) / two_pi).floor() as i64;
88 if k_start <= k_end {
89 hi = 1.0;
90 }
91 let k_start2 = ((a.lo + half_pi) / two_pi).ceil() as i64;
92 let k_end2 = ((a.hi + half_pi) / two_pi).floor() as i64;
93 if k_start2 <= k_end2 {
94 lo = -1.0;
95 }
96 Interval { lo, hi }
97}
98pub fn cos_interval(a: Interval) -> Interval {
100 let shifted = Interval::new(
101 a.lo + std::f64::consts::FRAC_PI_2,
102 a.hi + std::f64::consts::FRAC_PI_2,
103 );
104 sin_interval(shifted)
105}
106pub fn exp_interval(a: Interval) -> Interval {
108 Interval {
109 lo: a.lo.exp(),
110 hi: a.hi.exp(),
111 }
112}
113pub fn ln_interval(a: Interval) -> Interval {
115 assert!(a.lo > 0.0, "ln_interval: lo must be > 0, got {}", a.lo);
116 Interval {
117 lo: a.lo.ln(),
118 hi: a.hi.ln(),
119 }
120}
121pub fn interval_newton(
136 f: &dyn Fn(f64) -> f64,
137 _f_interval: &dyn Fn(Interval) -> Interval,
138 df_interval: &dyn Fn(Interval) -> Interval,
139 initial: Interval,
140 tol: f64,
141 max_iter: usize,
142) -> IntervalNewtonResult {
143 let mut stack: Vec<(Interval, usize)> = vec![(initial, max_iter)];
144 let mut results: Vec<Interval> = Vec::new();
145 while let Some((mut x, iters_left)) = stack.pop() {
146 let mut converged = false;
147 for _ in 0..iters_left {
148 if x.width() < tol {
149 results.push(x);
150 converged = true;
151 break;
152 }
153 let m = x.midpoint();
154 let fm = f(m);
155 let df = df_interval(x);
156 if df.contains(0.0) {
157 let (left, right) = x.bisect();
158 let sub_iter = iters_left / 2;
159 if sub_iter > 0 {
160 stack.push((left, sub_iter));
161 stack.push((right, sub_iter));
162 }
163 converged = true;
164 break;
165 }
166 let correction = Interval::point(fm) / df;
167 let newton_step = Interval::point(m) - correction;
168 match Interval::intersection(x, newton_step) {
169 Some(new_x) => {
170 if new_x.width() >= x.width() * 0.99 {
171 let (left, right) = x.bisect();
172 let sub_iter = iters_left / 2;
173 if sub_iter > 0 {
174 stack.push((left, sub_iter));
175 stack.push((right, sub_iter));
176 }
177 converged = true;
178 break;
179 }
180 x = new_x;
181 }
182 None => {
183 converged = true;
184 break;
185 }
186 }
187 }
188 if !converged {
189 results.push(x);
190 }
191 }
192 if results.is_empty() {
193 IntervalNewtonResult::NoRoot
194 } else if results.len() == 1 {
195 IntervalNewtonResult::Root(
196 results
197 .into_iter()
198 .next()
199 .expect("results has exactly one element"),
200 )
201 } else {
202 IntervalNewtonResult::Multiple(results)
203 }
204}
205pub fn interval_bisection<F>(
210 f_interval: F,
211 initial: Interval,
212 tol: f64,
213 max_depth: usize,
214) -> Vec<Interval>
215where
216 F: Fn(Interval) -> Interval,
217{
218 let mut stack = vec![(initial, 0usize)];
219 let mut results = Vec::new();
220 while let Some((iv, depth)) = stack.pop() {
221 let fiv = f_interval(iv);
222 if !fiv.contains(0.0) {
223 continue;
224 }
225 if iv.width() <= tol || depth >= max_depth {
226 results.push(iv);
227 continue;
228 }
229 let (left, right) = iv.bisect();
230 stack.push((left, depth + 1));
231 stack.push((right, depth + 1));
232 }
233 results
234}
235pub(super) static NOISE_COUNTER: std::sync::atomic::AtomicUsize =
237 std::sync::atomic::AtomicUsize::new(0);
238pub(super) fn new_noise_id() -> usize {
240 NOISE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
241}
242pub trait IntervalConstraint {
247 fn propagate(&self, box_: &mut [Interval]) -> bool;
249}
250pub fn propagate_constraints(
252 constraints: &[&dyn IntervalConstraint],
253 box_: &mut [Interval],
254 max_iter: usize,
255) -> usize {
256 let mut total_iters = 0;
257 for _ in 0..max_iter {
258 let mut any_changed = false;
259 for c in constraints {
260 if c.propagate(box_) {
261 any_changed = true;
262 }
263 }
264 total_iters += 1;
265 if !any_changed {
266 break;
267 }
268 }
269 total_iters
270}
271pub fn tan_interval(a: Interval) -> Interval {
276 use std::f64::consts::FRAC_PI_2;
277 let lo_k = (a.lo / std::f64::consts::PI - 0.5).ceil() as i64;
278 let hi_k = (a.hi / std::f64::consts::PI - 0.5).floor() as i64;
279 if lo_k <= hi_k {
280 return Interval {
281 lo: f64::NEG_INFINITY,
282 hi: f64::INFINITY,
283 };
284 }
285 let t_lo = a.lo.tan();
286 let t_hi = a.hi.tan();
287 let _ = FRAC_PI_2;
288 Interval {
289 lo: t_lo.min(t_hi),
290 hi: t_lo.max(t_hi),
291 }
292}
293pub fn atan_interval(a: Interval) -> Interval {
297 Interval {
298 lo: a.lo.atan(),
299 hi: a.hi.atan(),
300 }
301}
302pub fn interval_min(a: Interval, b: Interval) -> Interval {
304 Interval {
305 lo: a.lo.min(b.lo),
306 hi: a.hi.min(b.hi),
307 }
308}
309pub fn interval_max(a: Interval, b: Interval) -> Interval {
311 Interval {
312 lo: a.lo.max(b.lo),
313 hi: a.hi.max(b.hi),
314 }
315}
316pub fn sample_enclosure(f: impl Fn(f64) -> f64, a: Interval, n: usize) -> Interval {
323 assert!(n >= 2, "need at least 2 sample points");
324 let step = a.width() / (n - 1) as f64;
325 let mut lo = f64::INFINITY;
326 let mut hi = f64::NEG_INFINITY;
327 for i in 0..n {
328 let x = a.lo + i as f64 * step;
329 let y = f(x);
330 if y < lo {
331 lo = y;
332 }
333 if y > hi {
334 hi = y;
335 }
336 }
337 Interval { lo, hi }
338}
339pub fn subdivide(iv: Interval, max_width: f64) -> Vec<Interval> {
343 if iv.width() <= max_width {
344 return vec![iv];
345 }
346 let (left, right) = iv.bisect();
347 let mut result = subdivide(left, max_width);
348 result.extend(subdivide(right, max_width));
349 result
350}
351pub fn range_by_subdivision(
356 f: impl Fn(Interval) -> Interval,
357 iv: Interval,
358 max_width: f64,
359) -> Interval {
360 let parts = subdivide(iv, max_width);
361 parts.iter().fold(Interval::empty(), |acc, &sub| {
362 let fv = f(sub);
363 if acc.is_empty() {
364 fv
365 } else {
366 Interval::hull(acc, fv)
367 }
368 })
369}
370pub fn taylor_enclosure(f_mid: f64, df_iv: Interval, iv: Interval) -> Interval {
376 let m = iv.midpoint();
377 let delta = iv - Interval::point(m);
378 Interval::point(f_mid) + df_iv * delta
379}
380pub fn krawczyk_operator(f_mid: f64, y: f64, df_iv: Interval, iv: Interval) -> Interval {
388 let m = iv.midpoint();
389 let radius = iv - Interval::point(m);
390 let coeff = Interval::point(1.0) - Interval::point(y) * df_iv;
391 Interval::point(m - y * f_mid) + coeff * radius
392}
393#[cfg(test)]
394mod tests {
395 use super::*;
396 use crate::interval::AffineForm;
397 use crate::interval::BoundConstraint;
398 use crate::interval::Interval3;
399 use crate::interval::IntervalMatrix;
400 use crate::interval::IntervalMatrix2;
401 use crate::interval::IntervalMatrix3;
402 use crate::interval::IntervalVec;
403 use crate::interval::LinearConstraint;
404 use crate::interval::TaylorModel;
405 #[test]
406 fn test_add() {
407 let a = Interval::new(1.0, 2.0);
408 let b = Interval::new(3.0, 4.0);
409 assert_eq!(a + b, Interval::new(4.0, 6.0));
410 }
411 #[test]
412 fn test_mul_positive() {
413 let a = Interval::new(1.0, 2.0);
414 let b = Interval::new(3.0, 4.0);
415 assert_eq!(a * b, Interval::new(3.0, 8.0));
416 }
417 #[test]
418 fn test_mul_mixed_signs() {
419 let a = Interval::new(-2.0, 1.0);
420 let b = Interval::new(-2.0, 1.0);
421 let result = a * b;
422 assert_eq!(result, Interval::new(-2.0, 4.0));
423 }
424 #[test]
425 fn test_sqrt_interval() {
426 let a = Interval::new(4.0, 9.0);
427 assert_eq!(sqrt_interval(a), Interval::new(2.0, 3.0));
428 }
429 #[test]
430 fn test_overlaps_true() {
431 let a = Interval::new(1.0, 3.0);
432 let b = Interval::new(2.0, 4.0);
433 assert!(a.overlaps(&b));
434 }
435 #[test]
436 fn test_overlaps_false() {
437 let a = Interval::new(1.0, 2.0);
438 let b = Interval::new(3.0, 4.0);
439 assert!(!a.overlaps(&b));
440 }
441 #[test]
442 fn test_interval3_overlap() {
443 let a = Interval3::from_aabb([0.0, 0.0, 0.0], [2.0, 2.0, 2.0]);
444 let b = Interval3::from_aabb([1.0, 1.0, 1.0], [3.0, 3.0, 3.0]);
445 assert!(a.overlaps_3d(&b));
446 let c = Interval3::from_aabb([3.0, 0.0, 0.0], [5.0, 2.0, 2.0]);
447 assert!(!a.overlaps_3d(&c));
448 }
449 #[test]
450 fn test_sub() {
451 let a = Interval::new(3.0, 5.0);
452 let b = Interval::new(1.0, 2.0);
453 assert_eq!(a - b, Interval::new(1.0, 4.0));
454 }
455 #[test]
456 fn test_neg() {
457 let a = Interval::new(1.0, 3.0);
458 assert_eq!(-a, Interval::new(-3.0, -1.0));
459 }
460 #[test]
461 fn test_square() {
462 let a = Interval::new(-2.0, 1.0);
463 assert_eq!(square(a), Interval::new(0.0, 4.0));
464 let b = Interval::new(1.0, 2.0);
465 assert_eq!(square(b), Interval::new(1.0, 4.0));
466 let c = Interval::new(-2.0, -1.0);
467 assert_eq!(square(c), Interval::new(1.0, 4.0));
468 }
469 #[test]
470 fn test_abs_interval() {
471 assert_eq!(
472 abs_interval(Interval::new(1.0, 3.0)),
473 Interval::new(1.0, 3.0)
474 );
475 assert_eq!(
476 abs_interval(Interval::new(-3.0, -1.0)),
477 Interval::new(1.0, 3.0)
478 );
479 assert_eq!(
480 abs_interval(Interval::new(-2.0, 3.0)),
481 Interval::new(0.0, 3.0)
482 );
483 }
484 #[test]
485 fn test_hull() {
486 let a = Interval::new(1.0, 3.0);
487 let b = Interval::new(2.0, 5.0);
488 assert_eq!(Interval::hull(a, b), Interval::new(1.0, 5.0));
489 }
490 #[test]
491 fn test_width_midpoint_contains() {
492 let a = Interval::new(2.0, 6.0);
493 assert_eq!(a.width(), 4.0);
494 assert_eq!(a.midpoint(), 4.0);
495 assert!(a.contains(3.0));
496 assert!(!a.contains(7.0));
497 }
498 #[test]
499 fn test_length_sq_bounds() {
500 let iv = Interval3::from_aabb([0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
501 let bounds = iv.length_sq_bounds();
502 assert_eq!(bounds.lo, 0.0);
503 assert_eq!(bounds.hi, 3.0);
504 }
505 #[test]
506 fn test_from_to_aabb() {
507 let min = [1.0, 2.0, 3.0];
508 let max = [4.0, 5.0, 6.0];
509 let iv = Interval3::from_aabb(min, max);
510 let (out_min, out_max) = iv.to_aabb();
511 assert_eq!(out_min, min);
512 assert_eq!(out_max, max);
513 }
514 #[test]
515 fn test_div() {
516 let a = Interval::new(2.0, 6.0);
517 let b = Interval::new(1.0, 2.0);
518 let r = a / b;
519 assert_eq!(r, Interval::new(1.0, 6.0));
520 }
521 #[test]
522 fn test_scalar_mul() {
523 let a = Interval::new(1.0, 3.0);
524 assert_eq!(a * 2.0, Interval::new(2.0, 6.0));
525 assert_eq!(a * (-1.0), Interval::new(-3.0, -1.0));
526 assert_eq!(a * 0.0, Interval::new(0.0, 0.0));
527 }
528 #[test]
529 fn test_reciprocal() {
530 let a = Interval::new(2.0, 4.0);
531 let r = a.reciprocal();
532 assert!((r.lo - 0.25).abs() < 1e-15);
533 assert!((r.hi - 0.5).abs() < 1e-15);
534 }
535 #[test]
536 fn test_intersection() {
537 let a = Interval::new(1.0, 4.0);
538 let b = Interval::new(2.0, 5.0);
539 let c = Interval::intersection(a, b).unwrap();
540 assert_eq!(c, Interval::new(2.0, 4.0));
541 let d = Interval::new(5.0, 6.0);
542 assert!(Interval::intersection(a, d).is_none());
543 }
544 #[test]
545 fn test_bisect() {
546 let a = Interval::new(0.0, 4.0);
547 let (left, right) = a.bisect();
548 assert_eq!(left, Interval::new(0.0, 2.0));
549 assert_eq!(right, Interval::new(2.0, 4.0));
550 }
551 #[test]
552 fn test_split_at() {
553 let a = Interval::new(0.0, 10.0);
554 let (l, r) = a.split_at(3.0);
555 assert_eq!(l, Interval::new(0.0, 3.0));
556 assert_eq!(r, Interval::new(3.0, 10.0));
557 }
558 #[test]
559 fn test_inflate() {
560 let a = Interval::new(1.0, 3.0);
561 let b = a.inflate(0.5);
562 assert_eq!(b, Interval::new(0.5, 3.5));
563 }
564 #[test]
565 fn test_radius() {
566 let a = Interval::new(1.0, 5.0);
567 assert!((a.radius() - 2.0).abs() < 1e-15);
568 }
569 #[test]
570 fn test_contains_interval() {
571 let a = Interval::new(0.0, 10.0);
572 let b = Interval::new(2.0, 8.0);
573 assert!(a.contains_interval(&b));
574 assert!(!b.contains_interval(&a));
575 }
576 #[test]
577 fn test_hull_many() {
578 let intervals = vec![
579 Interval::new(1.0, 3.0),
580 Interval::new(5.0, 7.0),
581 Interval::new(-1.0, 2.0),
582 ];
583 let h = Interval::hull_many(&intervals);
584 assert_eq!(h, Interval::new(-1.0, 7.0));
585 }
586 #[test]
587 fn test_empty_interval() {
588 let e = Interval::empty();
589 assert!(e.is_empty());
590 let a = Interval::new(1.0, 2.0);
591 assert!(!a.is_empty());
592 }
593 #[test]
594 fn test_pow_interval() {
595 let a = Interval::new(-2.0, 3.0);
596 assert_eq!(pow_interval(a, 0), Interval::point(1.0));
597 assert_eq!(pow_interval(a, 1), a);
598 let sq = pow_interval(a, 2);
599 assert!((sq.lo - 0.0).abs() < 1e-15);
600 assert!((sq.hi - 9.0).abs() < 1e-15);
601 }
602 #[test]
603 fn test_exp_interval() {
604 let a = Interval::new(0.0, 1.0);
605 let e = exp_interval(a);
606 assert!((e.lo - 1.0).abs() < 1e-10);
607 assert!((e.hi - std::f64::consts::E).abs() < 1e-10);
608 }
609 #[test]
610 fn test_ln_interval() {
611 let a = Interval::new(1.0, std::f64::consts::E);
612 let l = ln_interval(a);
613 assert!((l.lo - 0.0).abs() < 1e-10);
614 assert!((l.hi - 1.0).abs() < 1e-10);
615 }
616 #[test]
617 fn test_sin_interval_full_period() {
618 let a = Interval::new(0.0, 2.0 * std::f64::consts::PI);
619 let s = sin_interval(a);
620 assert!((s.lo - (-1.0)).abs() < 1e-10);
621 assert!((s.hi - 1.0).abs() < 1e-10);
622 }
623 #[test]
624 fn test_sin_interval_small() {
625 let a = Interval::new(0.0, std::f64::consts::FRAC_PI_6);
626 let s = sin_interval(a);
627 assert!(s.lo <= 0.0 + 1e-10);
628 assert!((s.hi - 0.5).abs() < 1e-10);
629 }
630 #[test]
631 fn test_cos_interval() {
632 let a = Interval::new(0.0, std::f64::consts::PI);
633 let c = cos_interval(a);
634 assert!((c.lo - (-1.0)).abs() < 1e-10);
635 assert!((c.hi - 1.0).abs() < 1e-10);
636 }
637 #[test]
638 fn test_interval_bisection_finds_root() {
639 let f_iv = |iv: Interval| square(iv) - Interval::point(2.0);
640 let roots = interval_bisection(f_iv, Interval::new(0.0, 2.0), 0.01, 20);
641 assert!(!roots.is_empty());
642 let sqrt2 = 2.0_f64.sqrt();
643 let found = roots.iter().any(|r| r.contains(sqrt2));
644 assert!(found, "sqrt(2) not found in any root interval: {:?}", roots);
645 }
646 #[test]
647 fn test_interval_bisection_no_root() {
648 let f_iv = |iv: Interval| square(iv) + Interval::point(1.0);
649 let roots = interval_bisection(f_iv, Interval::new(-5.0, 5.0), 0.01, 20);
650 assert!(roots.is_empty(), "should find no roots, got {:?}", roots);
651 }
652 #[test]
653 fn test_interval_newton_basic() {
654 let f = |x: f64| x * x - 4.0;
655 let f_iv = |iv: Interval| square(iv) - Interval::point(4.0);
656 let df_iv = |iv: Interval| iv * 2.0;
657 let result = interval_newton(&f, &f_iv, &df_iv, Interval::new(1.0, 3.0), 1e-10, 50);
658 match result {
659 IntervalNewtonResult::Root(r) => {
660 assert!(r.contains(2.0), "root interval {:?} should contain 2.0", r);
661 assert!(
662 r.width() < 1e-8,
663 "root should be tight, width={}",
664 r.width()
665 );
666 }
667 other => panic!("expected Root, got {:?}", other),
668 }
669 }
670 #[test]
671 fn test_interval_vec_operations() {
672 let a = IntervalVec::from_slice(&[Interval::new(1.0, 2.0), Interval::new(3.0, 4.0)]);
673 let b = IntervalVec::from_slice(&[Interval::new(0.5, 1.5), Interval::new(2.0, 3.0)]);
674 let sum = a.add(&b);
675 assert_eq!(sum.data[0], Interval::new(1.5, 3.5));
676 assert_eq!(sum.data[1], Interval::new(5.0, 7.0));
677 let diff = a.sub(&b);
678 assert_eq!(diff.data[0], Interval::new(-0.5, 1.5));
679 }
680 #[test]
681 fn test_interval_vec_dot() {
682 let a = IntervalVec::from_slice(&[Interval::point(1.0), Interval::point(2.0)]);
683 let b = IntervalVec::from_slice(&[Interval::point(3.0), Interval::point(4.0)]);
684 let d = a.dot(&b);
685 assert!((d.lo - 11.0).abs() < 1e-15);
686 assert!((d.hi - 11.0).abs() < 1e-15);
687 }
688 #[test]
689 fn test_interval_vec_hull() {
690 let a = IntervalVec::from_slice(&[Interval::new(1.0, 3.0)]);
691 let b = IntervalVec::from_slice(&[Interval::new(2.0, 5.0)]);
692 let h = a.hull(&b);
693 assert_eq!(h.data[0], Interval::new(1.0, 5.0));
694 }
695 #[test]
696 fn test_interval_vec_max_width() {
697 let v = IntervalVec::from_slice(&[
698 Interval::new(0.0, 1.0),
699 Interval::new(0.0, 3.0),
700 Interval::new(0.0, 2.0),
701 ]);
702 assert!((v.max_width() - 3.0).abs() < 1e-15);
703 }
704 #[test]
705 fn test_interval_vec_midpoint() {
706 let v = IntervalVec::from_slice(&[Interval::new(0.0, 4.0), Interval::new(2.0, 6.0)]);
707 let mid = v.midpoint();
708 assert!((mid[0] - 2.0).abs() < 1e-15);
709 assert!((mid[1] - 4.0).abs() < 1e-15);
710 }
711 #[test]
712 fn test_interval_matrix_identity() {
713 let id = IntervalMatrix::identity(3);
714 assert_eq!(id.get(0, 0), Interval::point(1.0));
715 assert_eq!(id.get(0, 1), Interval::point(0.0));
716 assert_eq!(id.get(1, 1), Interval::point(1.0));
717 }
718 #[test]
719 fn test_interval_matrix_mul_vec() {
720 let mut m = IntervalMatrix::zeros(2, 2);
721 m.set(0, 0, Interval::point(1.0));
722 m.set(0, 1, Interval::point(2.0));
723 m.set(1, 0, Interval::point(3.0));
724 m.set(1, 1, Interval::point(4.0));
725 let v = IntervalVec::from_slice(&[Interval::point(1.0), Interval::point(1.0)]);
726 let r = m.mul_vec(&v);
727 assert!((r.data[0].lo - 3.0).abs() < 1e-15);
728 assert!((r.data[1].lo - 7.0).abs() < 1e-15);
729 }
730 #[test]
731 fn test_interval_matrix_mul_mat() {
732 let id = IntervalMatrix::identity(2);
733 let mut m = IntervalMatrix::zeros(2, 2);
734 m.set(0, 0, Interval::point(5.0));
735 m.set(0, 1, Interval::point(6.0));
736 m.set(1, 0, Interval::point(7.0));
737 m.set(1, 1, Interval::point(8.0));
738 let r = id.mul_mat(&m);
739 assert_eq!(r.get(0, 0), Interval::point(5.0));
740 assert_eq!(r.get(1, 1), Interval::point(8.0));
741 }
742 #[test]
743 fn test_interval_matrix_transpose() {
744 let mut m = IntervalMatrix::zeros(2, 3);
745 m.set(0, 2, Interval::point(42.0));
746 let t = m.transpose();
747 assert_eq!(t.rows, 3);
748 assert_eq!(t.cols, 2);
749 assert_eq!(t.get(2, 0), Interval::point(42.0));
750 }
751 #[test]
752 fn test_affine_form_constant() {
753 let a = AffineForm::constant(3.0);
754 let iv = a.to_interval();
755 assert!((iv.lo - 3.0).abs() < 1e-15);
756 assert!((iv.hi - 3.0).abs() < 1e-15);
757 }
758 #[test]
759 fn test_affine_from_interval() {
760 let iv = Interval::new(1.0, 5.0);
761 let a = AffineForm::from_interval(iv);
762 let back = a.to_interval();
763 assert!((back.lo - 1.0).abs() < 1e-10);
764 assert!((back.hi - 5.0).abs() < 1e-10);
765 }
766 #[test]
767 fn test_affine_add_sub() {
768 let iv1 = Interval::new(1.0, 3.0);
769 let iv2 = Interval::new(2.0, 4.0);
770 let a = AffineForm::from_interval(iv1);
771 let b = AffineForm::from_interval(iv2);
772 let sum = a.add(&b);
773 let sum_iv = sum.to_interval();
774 assert!(sum_iv.lo <= 3.0 + 1e-10);
775 assert!(sum_iv.hi >= 7.0 - 1e-10);
776 let diff = a.sub(&b);
777 let diff_iv = diff.to_interval();
778 assert!(diff_iv.lo <= -3.0 + 1e-10);
779 assert!(diff_iv.hi >= 1.0 - 1e-10);
780 }
781 #[test]
782 fn test_affine_correlation_tighter() {
783 let iv = Interval::new(1.0, 3.0);
784 let a = AffineForm::from_interval(iv);
785 let diff = a.sub(&a);
786 let diff_iv = diff.to_interval();
787 assert!(
788 diff_iv.width() < 1e-10,
789 "x - x should be tight, got {:?}",
790 diff_iv
791 );
792 }
793 #[test]
794 fn test_affine_scale() {
795 let a = AffineForm::from_interval(Interval::new(1.0, 3.0));
796 let scaled = a.scale(2.0);
797 let iv = scaled.to_interval();
798 assert!((iv.lo - 2.0).abs() < 1e-10);
799 assert!((iv.hi - 6.0).abs() < 1e-10);
800 }
801 #[test]
802 fn test_affine_mul() {
803 let a = AffineForm::from_interval(Interval::new(1.0, 3.0));
804 let b = AffineForm::from_interval(Interval::new(2.0, 4.0));
805 let prod = a.mul(&b);
806 let iv = prod.to_interval();
807 assert!(iv.lo <= 2.0 + 1e-10, "lo={} should be <= 2", iv.lo);
808 assert!(iv.hi >= 12.0 - 1e-10, "hi={} should be >= 12", iv.hi);
809 }
810 #[test]
811 fn test_constraint_propagation_bound() {
812 let mut box_ = vec![Interval::new(-10.0, 10.0), Interval::new(-10.0, 10.0)];
813 let c = BoundConstraint {
814 idx: 0,
815 bounds: Interval::new(0.0, 5.0),
816 };
817 let changed = c.propagate(&mut box_);
818 assert!(changed);
819 assert_eq!(box_[0], Interval::new(0.0, 5.0));
820 assert_eq!(box_[1], Interval::new(-10.0, 10.0));
821 }
822 #[test]
823 fn test_constraint_propagation_linear() {
824 let mut box_ = vec![Interval::new(0.0, 10.0), Interval::new(0.0, 10.0)];
825 let c = LinearConstraint {
826 coeffs: vec![(0, 1.0), (1, 1.0)],
827 rhs: Interval::new(3.0, 3.0),
828 };
829 c.propagate(&mut box_);
830 assert!(box_[0].lo >= -0.01);
831 assert!(box_[0].hi <= 3.01);
832 }
833 #[test]
834 fn test_propagate_constraints_fixed_point() {
835 let mut box_ = vec![Interval::new(-10.0, 10.0)];
836 let c1 = BoundConstraint {
837 idx: 0,
838 bounds: Interval::new(0.0, 5.0),
839 };
840 let c2 = BoundConstraint {
841 idx: 0,
842 bounds: Interval::new(2.0, 8.0),
843 };
844 let constraints: Vec<&dyn IntervalConstraint> = vec![&c1, &c2];
845 let iters = propagate_constraints(&constraints, &mut box_, 100);
846 assert!(iters <= 3);
847 assert!((box_[0].lo - 2.0).abs() < 1e-10);
848 assert!((box_[0].hi - 5.0).abs() < 1e-10);
849 }
850 #[test]
851 fn test_interval3_hull() {
852 let a = Interval3::from_aabb([0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
853 let b = Interval3::from_aabb([2.0, 2.0, 2.0], [3.0, 3.0, 3.0]);
854 let h = Interval3::hull_3d(a, b);
855 let (min, max) = h.to_aabb();
856 assert_eq!(min, [0.0, 0.0, 0.0]);
857 assert_eq!(max, [3.0, 3.0, 3.0]);
858 }
859 #[test]
860 fn test_interval3_intersection() {
861 let a = Interval3::from_aabb([0.0, 0.0, 0.0], [2.0, 2.0, 2.0]);
862 let b = Interval3::from_aabb([1.0, 1.0, 1.0], [3.0, 3.0, 3.0]);
863 let c = Interval3::intersection_3d(a, b).unwrap();
864 let (min, max) = c.to_aabb();
865 assert_eq!(min, [1.0, 1.0, 1.0]);
866 assert_eq!(max, [2.0, 2.0, 2.0]);
867 let d = Interval3::from_aabb([5.0, 5.0, 5.0], [6.0, 6.0, 6.0]);
868 assert!(Interval3::intersection_3d(a, d).is_none());
869 }
870 #[test]
871 fn test_interval3_volume_surface_area() {
872 let iv = Interval3::from_aabb([0.0, 0.0, 0.0], [2.0, 3.0, 4.0]);
873 assert!((iv.volume() - 24.0).abs() < 1e-15);
874 assert!((iv.surface_area() - 52.0).abs() < 1e-15);
875 }
876 #[test]
877 fn test_interval3_center() {
878 let iv = Interval3::from_aabb([1.0, 2.0, 3.0], [5.0, 6.0, 7.0]);
879 let c = iv.center();
880 assert_eq!(c, [3.0, 4.0, 5.0]);
881 }
882 #[test]
883 fn test_interval3_inflate() {
884 let iv = Interval3::from_aabb([1.0, 1.0, 1.0], [2.0, 2.0, 2.0]);
885 let inf = iv.inflate(0.5);
886 let (min, max) = inf.to_aabb();
887 assert_eq!(min, [0.5, 0.5, 0.5]);
888 assert_eq!(max, [2.5, 2.5, 2.5]);
889 }
890 #[test]
891 fn test_interval3_contains_point() {
892 let iv = Interval3::from_aabb([0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
893 assert!(iv.contains_point([0.5, 0.5, 0.5]));
894 assert!(!iv.contains_point([1.5, 0.5, 0.5]));
895 }
896 #[test]
897 fn test_tan_interval_monotone() {
898 let a = Interval::new(0.0, std::f64::consts::FRAC_PI_4);
899 let t = tan_interval(a);
900 assert!(t.lo <= 0.0 + 1e-10);
901 assert!((t.hi - 1.0).abs() < 1e-10);
902 }
903 #[test]
904 fn test_tan_interval_with_pole() {
905 let a = Interval::new(0.0, std::f64::consts::PI);
906 let t = tan_interval(a);
907 assert_eq!(t.lo, f64::NEG_INFINITY);
908 assert_eq!(t.hi, f64::INFINITY);
909 }
910 #[test]
911 fn test_atan_interval_monotone() {
912 let a = Interval::new(-1.0, 1.0);
913 let t = atan_interval(a);
914 let expected_lo = (-1.0_f64).atan();
915 let expected_hi = 1.0_f64.atan();
916 assert!((t.lo - expected_lo).abs() < 1e-12);
917 assert!((t.hi - expected_hi).abs() < 1e-12);
918 }
919 #[test]
920 fn test_interval_min() {
921 let a = Interval::new(1.0, 5.0);
922 let b = Interval::new(3.0, 7.0);
923 let m = interval_min(a, b);
924 assert_eq!(m, Interval::new(1.0, 5.0));
925 }
926 #[test]
927 fn test_interval_max() {
928 let a = Interval::new(1.0, 5.0);
929 let b = Interval::new(3.0, 7.0);
930 let m = interval_max(a, b);
931 assert_eq!(m, Interval::new(3.0, 7.0));
932 }
933 #[test]
934 fn test_interval_matrix2_identity_mul_vec() {
935 let id = IntervalMatrix2::identity();
936 let v = [Interval::point(3.0), Interval::point(5.0)];
937 let r = id.mul_vec2(v);
938 assert!((r[0].lo - 3.0).abs() < 1e-15);
939 assert!((r[1].lo - 5.0).abs() < 1e-15);
940 }
941 #[test]
942 fn test_interval_matrix2_mul_mat_identity() {
943 let id = IntervalMatrix2::identity();
944 let m = IntervalMatrix2::from_f64(1.0, 2.0, 3.0, 4.0);
945 let r = id.mul_mat2(&m);
946 assert!((r.row0[0].lo - 1.0).abs() < 1e-15);
947 assert!((r.row0[1].lo - 2.0).abs() < 1e-15);
948 assert!((r.row1[0].lo - 3.0).abs() < 1e-15);
949 assert!((r.row1[1].lo - 4.0).abs() < 1e-15);
950 }
951 #[test]
952 fn test_interval_matrix2_mul_vec_general() {
953 let m = IntervalMatrix2::from_f64(2.0, 0.0, 0.0, 3.0);
954 let v = [Interval::point(1.0), Interval::point(2.0)];
955 let r = m.mul_vec2(v);
956 assert!((r[0].lo - 2.0).abs() < 1e-15);
957 assert!((r[1].lo - 6.0).abs() < 1e-15);
958 }
959 #[test]
960 fn test_interval_matrix2_transpose() {
961 let m = IntervalMatrix2::from_f64(1.0, 2.0, 3.0, 4.0);
962 let t = m.transpose2();
963 assert!((t.row0[1].lo - 3.0).abs() < 1e-15);
964 assert!((t.row1[0].lo - 2.0).abs() < 1e-15);
965 }
966 #[test]
967 fn test_interval_matrix3_identity_mul_vec() {
968 let id = IntervalMatrix3::identity();
969 let v = [
970 Interval::point(1.0),
971 Interval::point(2.0),
972 Interval::point(3.0),
973 ];
974 let r = id.mul_vec3(v);
975 assert!((r[0].lo - 1.0).abs() < 1e-15);
976 assert!((r[1].lo - 2.0).abs() < 1e-15);
977 assert!((r[2].lo - 3.0).abs() < 1e-15);
978 }
979 #[test]
980 fn test_interval_matrix3_mul_mat_identity() {
981 let id = IntervalMatrix3::identity();
982 let r = id.mul_mat3(&id);
983 for i in 0..3 {
984 for j in 0..3 {
985 let expected = if i == j { 1.0 } else { 0.0 };
986 assert!(
987 (r.rows[i][j].lo - expected).abs() < 1e-12,
988 "r[{i}][{j}].lo={}",
989 r.rows[i][j].lo
990 );
991 }
992 }
993 }
994 #[test]
995 fn test_interval_matrix3_transpose() {
996 let id = IntervalMatrix3::identity();
997 let t = id.transpose3();
998 for i in 0..3 {
999 for j in 0..3 {
1000 assert!((t.rows[i][j].lo - id.rows[i][j].lo).abs() < 1e-12);
1001 }
1002 }
1003 }
1004 #[test]
1005 fn test_subdivide_width_limit() {
1006 let iv = Interval::new(0.0, 1.0);
1007 let parts = subdivide(iv, 0.25);
1008 for p in &parts {
1009 assert!(p.width() <= 0.25 + 1e-12, "width={}", p.width());
1010 }
1011 let hull = parts.iter().fold(Interval::empty(), |acc, &p| {
1012 if acc.is_empty() {
1013 p
1014 } else {
1015 Interval::hull(acc, p)
1016 }
1017 });
1018 assert!((hull.lo - 0.0).abs() < 1e-12);
1019 assert!((hull.hi - 1.0).abs() < 1e-12);
1020 }
1021 #[test]
1022 fn test_range_by_subdivision_tighter_than_naive() {
1023 let iv = Interval::new(0.0, 1.0);
1024 let f_iv = |x: Interval| x * (Interval::point(1.0) - x);
1025 let naive = f_iv(iv);
1026 let tight = range_by_subdivision(f_iv, iv, 0.1);
1027 assert!(
1028 tight.hi <= naive.hi + 1e-10,
1029 "tight.hi={} naive.hi={}",
1030 tight.hi,
1031 naive.hi
1032 );
1033 }
1034 #[test]
1035 fn test_taylor_enclosure_linear_exact() {
1036 let iv = Interval::new(1.0, 3.0);
1037 let enc = taylor_enclosure(4.0, Interval::point(2.0), iv);
1038 assert!((enc.lo - 2.0).abs() < 1e-12);
1039 assert!((enc.hi - 6.0).abs() < 1e-12);
1040 }
1041 #[test]
1042 fn test_taylor_enclosure_contains_true_values() {
1043 let iv = Interval::new(1.0, 3.0);
1044 let df_iv = Interval::new(2.0, 6.0);
1045 let enc = taylor_enclosure(4.0, df_iv, iv);
1046 assert!(enc.lo <= 1.0, "enc.lo={} should be ≤ 1", enc.lo);
1047 assert!(enc.hi >= 9.0, "enc.hi={} should be ≥ 9", enc.hi);
1048 }
1049 #[test]
1050 fn test_krawczyk_contains_root() {
1051 let iv = Interval::new(1.5, 2.5);
1052 let m = iv.midpoint();
1053 let f_mid = m * m - 4.0;
1054 let y = 1.0 / (2.0 * m);
1055 let df_iv = Interval::new(2.0 * 1.5, 2.0 * 2.5);
1056 let k = krawczyk_operator(f_mid, y, df_iv, iv);
1057 assert!(
1058 k.lo <= 2.0 + 1e-10 && k.hi >= 2.0 - 1e-10,
1059 "Krawczyk interval {:?} should contain root 2",
1060 k
1061 );
1062 }
1063 #[test]
1064 fn test_krawczyk_subset_of_iv() {
1065 let iv = Interval::new(1.5, 2.5);
1066 let m = iv.midpoint();
1067 let f_mid = m * m - 4.0;
1068 let y = 1.0 / (2.0 * m);
1069 let df_iv = Interval::new(2.0 * 1.5, 2.0 * 2.5);
1070 let k = krawczyk_operator(f_mid, y, df_iv, iv);
1071 assert!(
1072 k.lo >= iv.lo - 1e-10 && k.hi <= iv.hi + 1e-10,
1073 "K={:?} should be ⊆ iv={:?}",
1074 k,
1075 iv
1076 );
1077 }
1078 #[test]
1079 fn test_sample_enclosure_monotone() {
1080 let iv = Interval::new(0.0, 1.0);
1081 let enc = sample_enclosure(|x| x, iv, 10);
1082 assert!((enc.lo - 0.0).abs() < 1e-10);
1083 assert!((enc.hi - 1.0).abs() < 1e-10);
1084 }
1085 #[test]
1086 fn test_sample_enclosure_parabola() {
1087 let iv = Interval::new(0.0, 3.0);
1088 let enc = sample_enclosure(|x| x * x, iv, 100);
1089 assert!(enc.lo <= 0.01);
1090 assert!((enc.hi - 9.0).abs() < 0.1);
1091 }
1092 #[test]
1093 fn test_interval_add_basic() {
1094 let a = Interval::new(1.0, 2.0);
1095 let b = Interval::new(3.0, 4.0);
1096 let c = a + b;
1097 assert!((c.lo - 4.0).abs() < 1e-12);
1098 assert!((c.hi - 6.0).abs() < 1e-12);
1099 }
1100 #[test]
1101 fn test_interval_sub_basic() {
1102 let a = Interval::new(5.0, 7.0);
1103 let b = Interval::new(1.0, 2.0);
1104 let c = a - b;
1105 assert!((c.lo - 3.0).abs() < 1e-12);
1106 assert!((c.hi - 6.0).abs() < 1e-12);
1107 }
1108 #[test]
1109 fn test_interval_mul_positive() {
1110 let a = Interval::new(2.0, 3.0);
1111 let b = Interval::new(4.0, 5.0);
1112 let c = a * b;
1113 assert!((c.lo - 8.0).abs() < 1e-12);
1114 assert!((c.hi - 15.0).abs() < 1e-12);
1115 }
1116 #[test]
1117 fn test_interval_mul_mixed_sign() {
1118 let a = Interval::new(-1.0, 2.0);
1119 let b = Interval::new(-1.0, 3.0);
1120 let c = a * b;
1121 assert!(
1122 c.lo <= -2.0 - 1e-12 || (c.lo + 3.0).abs() < 1e-12,
1123 "lo should cover -3, got {}",
1124 c.lo
1125 );
1126 assert!(c.hi >= 6.0 - 1e-12, "hi should be >= 6, got {}", c.hi);
1127 }
1128 #[test]
1129 fn test_interval_div_basic() {
1130 let a = Interval::new(6.0, 8.0);
1131 let b = Interval::new(2.0, 4.0);
1132 let c = a / b;
1133 assert!(c.lo <= 1.5 + 1e-12);
1134 assert!(c.hi >= 4.0 - 1e-12);
1135 }
1136 #[test]
1137 fn test_interval_neg() {
1138 let a = Interval::new(-1.0, 3.0);
1139 let b = -a;
1140 assert!((b.lo - (-3.0)).abs() < 1e-12);
1141 assert!((b.hi - 1.0).abs() < 1e-12);
1142 }
1143 #[test]
1144 fn test_interval_contains_point() {
1145 let a = Interval::new(1.0, 5.0);
1146 assert!(a.contains(3.0));
1147 assert!(!a.contains(6.0));
1148 assert!(a.contains(1.0));
1149 assert!(a.contains(5.0));
1150 }
1151 #[test]
1152 fn test_interval_overlaps() {
1153 let a = Interval::new(1.0, 3.0);
1154 let b = Interval::new(2.0, 5.0);
1155 let c = Interval::new(4.0, 6.0);
1156 assert!(a.overlaps(&b));
1157 assert!(!a.overlaps(&c));
1158 }
1159 #[test]
1160 fn test_interval_hull() {
1161 let a = Interval::new(1.0, 3.0);
1162 let b = Interval::new(5.0, 7.0);
1163 let h = Interval::hull(a, b);
1164 assert!((h.lo - 1.0).abs() < 1e-12);
1165 assert!((h.hi - 7.0).abs() < 1e-12);
1166 }
1167 #[test]
1168 fn test_interval_intersection_overlapping() {
1169 let a = Interval::new(1.0, 5.0);
1170 let b = Interval::new(3.0, 7.0);
1171 let c = Interval::intersection(a, b).expect("should intersect");
1172 assert!((c.lo - 3.0).abs() < 1e-12);
1173 assert!((c.hi - 5.0).abs() < 1e-12);
1174 }
1175 #[test]
1176 fn test_interval_intersection_disjoint() {
1177 let a = Interval::new(1.0, 2.0);
1178 let b = Interval::new(3.0, 4.0);
1179 assert!(Interval::intersection(a, b).is_none());
1180 }
1181 #[test]
1182 fn test_interval_powi_even() {
1183 let a = Interval::new(-2.0, 3.0);
1184 let p = a.powi(2);
1185 assert!(p.lo <= 1e-10, "lo={}", p.lo);
1186 assert!((p.hi - 9.0).abs() < 1e-10, "hi={}", p.hi);
1187 }
1188 #[test]
1189 fn test_interval_powi_odd() {
1190 let a = Interval::new(-1.0, 2.0);
1191 let p = a.powi(3);
1192 assert!((p.lo - (-1.0)).abs() < 1e-10);
1193 assert!((p.hi - 8.0).abs() < 1e-10);
1194 }
1195 #[test]
1196 fn test_interval_sqrt() {
1197 let a = Interval::new(4.0, 9.0);
1198 let s = a.sqrt();
1199 assert!((s.lo - 2.0).abs() < 1e-10);
1200 assert!((s.hi - 3.0).abs() < 1e-10);
1201 }
1202 #[test]
1203 fn test_interval_abs() {
1204 let a = Interval::new(-3.0, 2.0);
1205 let b = a.abs();
1206 assert!(b.lo >= 0.0, "abs lo should be >= 0");
1207 assert!(b.hi >= 2.0, "abs hi should be >= 2.0, got {}", b.hi);
1208 }
1209 #[test]
1210 fn test_interval_midpoint() {
1211 let a = Interval::new(2.0, 6.0);
1212 assert!((a.midpoint() - 4.0).abs() < 1e-12);
1213 }
1214 #[test]
1215 fn test_interval_radius() {
1216 let a = Interval::new(2.0, 6.0);
1217 assert!((a.radius() - 2.0).abs() < 1e-12);
1218 }
1219 #[test]
1220 fn test_interval_bisect() {
1221 let a = Interval::new(0.0, 4.0);
1222 let (left, right) = a.bisect();
1223 assert!((left.lo - 0.0).abs() < 1e-12);
1224 assert!((left.hi - 2.0).abs() < 1e-12);
1225 assert!((right.lo - 2.0).abs() < 1e-12);
1226 assert!((right.hi - 4.0).abs() < 1e-12);
1227 }
1228 #[test]
1229 fn test_interval_inflate() {
1230 let a = Interval::new(1.0, 3.0);
1231 let b = a.inflate(0.5);
1232 assert!((b.lo - 0.5).abs() < 1e-12);
1233 assert!((b.hi - 3.5).abs() < 1e-12);
1234 }
1235 #[test]
1236 fn test_cos_interval_at_zero() {
1237 let a = Interval::new(0.0, 0.0);
1238 let c = cos_interval(a);
1239 assert!((c.lo - 1.0).abs() < 1e-10);
1240 assert!((c.hi - 1.0).abs() < 1e-10);
1241 }
1242 #[test]
1243 fn test_exp_interval_basic() {
1244 let a = Interval::new(0.0, 1.0);
1245 let e = exp_interval(a);
1246 assert!((e.lo - 1.0).abs() < 1e-10);
1247 assert!((e.hi - std::f64::consts::E).abs() < 1e-8);
1248 }
1249 #[test]
1250 fn test_ln_interval_basic() {
1251 let a = Interval::new(1.0, std::f64::consts::E);
1252 let l = ln_interval(a);
1253 assert!((l.lo - 0.0).abs() < 1e-10);
1254 assert!((l.hi - 1.0).abs() < 1e-8);
1255 }
1256 #[test]
1257 fn test_interval_newton_x_squared_minus_2() {
1258 let result = interval_newton(
1259 &|x| x * x - 2.0,
1260 &|iv: Interval| iv * iv - Interval::point(2.0),
1261 &|iv: Interval| Interval::point(2.0) * iv,
1262 Interval::new(1.0, 2.0),
1263 1e-8,
1264 50,
1265 );
1266 match result {
1267 IntervalNewtonResult::Root(r) => {
1268 assert!(
1269 r.contains(2.0_f64.sqrt()),
1270 "root interval should contain sqrt(2)"
1271 );
1272 }
1273 IntervalNewtonResult::Multiple(v) => {
1274 let any_contains = v.iter().any(|iv| iv.contains(2.0_f64.sqrt()));
1275 assert!(any_contains, "one interval should contain sqrt(2)");
1276 }
1277 IntervalNewtonResult::NoRoot => panic!("expected a root"),
1278 }
1279 }
1280 #[test]
1281 fn test_interval3_overlaps() {
1282 let a = Interval3::from_aabb([0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
1283 let b = Interval3::from_aabb([0.5, 0.5, 0.5], [2.0, 2.0, 2.0]);
1284 assert!(a.overlaps_3d(&b));
1285 }
1286 #[test]
1287 fn test_interval3_no_overlap() {
1288 let a = Interval3::from_aabb([0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
1289 let b = Interval3::from_aabb([2.0, 0.0, 0.0], [3.0, 1.0, 1.0]);
1290 assert!(!a.overlaps_3d(&b));
1291 }
1292 #[test]
1293 fn test_interval3_volume_exact() {
1294 let a = Interval3::from_aabb([0.0, 0.0, 0.0], [2.0, 3.0, 4.0]);
1295 assert!((a.volume() - 24.0).abs() < 1e-12, "volume={}", a.volume());
1296 }
1297 #[test]
1298 fn test_taylor_model_evaluate() {
1299 let tm = TaylorModel::new(vec![1.0, 1.0], Interval::new(-0.01, 0.01));
1300 let ev = tm.evaluate(Interval::point(0.0));
1301 assert!(ev.contains(1.0), "Taylor model at 0 should contain 1");
1302 }
1303 #[test]
1304 fn test_taylor_model_add() {
1305 let a = TaylorModel::new(vec![1.0, 2.0], Interval::new(-0.01, 0.01));
1306 let b = TaylorModel::new(vec![3.0, 1.0], Interval::new(-0.01, 0.01));
1307 let c = a.add(&b);
1308 assert!((c.poly[0] - 4.0).abs() < 1e-12, "constant term 1+3=4");
1309 assert!((c.poly[1] - 3.0).abs() < 1e-12, "linear term 2+1=3");
1310 }
1311 #[test]
1312 fn test_taylor_model_scale() {
1313 let tm = TaylorModel::new(vec![2.0, 4.0], Interval::new(-0.1, 0.1));
1314 let scaled = tm.scale(3.0);
1315 assert!((scaled.poly[0] - 6.0).abs() < 1e-12);
1316 assert!((scaled.poly[1] - 12.0).abs() < 1e-12);
1317 }
1318 #[test]
1319 fn test_interval_bisection_x_squared_minus_1() {
1320 let roots = interval_bisection(
1321 |iv: Interval| iv * iv - Interval::point(1.0),
1322 Interval::new(-2.0, 0.0),
1323 0.01,
1324 20,
1325 );
1326 assert!(!roots.is_empty(), "should find root of x^2-1 near -1");
1327 let any_contains = roots.iter().any(|iv| iv.contains(-1.0));
1328 assert!(any_contains, "a root interval should contain -1");
1329 }
1330 #[test]
1331 fn test_affine_form_from_interval_roundtrip() {
1332 let iv = Interval::new(1.0, 3.0);
1333 let af = AffineForm::from_interval(iv);
1334 let back = af.to_interval();
1335 assert!(back.lo <= 1.0 + 1e-10, "lo={}", back.lo);
1336 assert!(back.hi >= 3.0 - 1e-10, "hi={}", back.hi);
1337 }
1338 #[test]
1339 fn test_affine_form_add_constants() {
1340 let a = AffineForm::constant(2.0);
1341 let b = AffineForm::constant(3.0);
1342 let c = a.add(&b);
1343 let iv = c.to_interval();
1344 assert!((iv.lo - 5.0).abs() < 1e-12);
1345 assert!((iv.hi - 5.0).abs() < 1e-12);
1346 }
1347 #[test]
1348 fn test_affine_form_scale_constant() {
1349 let a = AffineForm::constant(4.0);
1350 let b = a.scale(2.5);
1351 let iv = b.to_interval();
1352 assert!((iv.lo - 10.0).abs() < 1e-12);
1353 assert!((iv.hi - 10.0).abs() < 1e-12);
1354 }
1355 #[test]
1356 fn test_interval_matrix2_diagonal_mul_mat() {
1357 let a = IntervalMatrix2::from_f64(2.0, 0.0, 0.0, 3.0);
1358 let b = IntervalMatrix2::from_f64(1.0, 0.0, 0.0, 1.0);
1359 let c = a.mul_mat2(&b);
1360 assert!((c.row0[0].lo - 2.0).abs() < 1e-12);
1361 assert!((c.row1[1].lo - 3.0).abs() < 1e-12);
1362 }
1363 #[test]
1364 fn test_interval_matrix3_identity_vec3_passthrough() {
1365 let m = IntervalMatrix3::identity();
1366 let v = [
1367 Interval::new(1.0, 2.0),
1368 Interval::new(3.0, 4.0),
1369 Interval::new(5.0, 6.0),
1370 ];
1371 let result = m.mul_vec3(v);
1372 for i in 0..3 {
1373 assert!((result[i].lo - v[i].lo).abs() < 1e-12);
1374 assert!((result[i].hi - v[i].hi).abs() < 1e-12);
1375 }
1376 }
1377}