1#[allow(unused_imports)]
11use crate::prelude::*;
12use core::cmp::Ordering;
13use core::fmt;
14use core::ops::{Add, Div, Mul, Neg, Sub};
15use num_bigint::BigInt;
16use num_rational::BigRational;
17use num_traits::{One, Signed, Zero};
18
19#[derive(Clone, Debug, PartialEq, Eq)]
21pub enum Bound {
22 NegInf,
24 Finite(BigRational),
26 PosInf,
28}
29
30impl Bound {
31 #[inline]
33 pub fn finite(r: BigRational) -> Self {
34 Self::Finite(r)
35 }
36
37 #[inline]
39 pub fn from_int(n: i64) -> Self {
40 Self::Finite(BigRational::from_integer(BigInt::from(n)))
41 }
42
43 #[inline]
45 pub fn is_finite(&self) -> bool {
46 matches!(self, Self::Finite(_))
47 }
48
49 #[inline]
51 pub fn is_infinite(&self) -> bool {
52 !self.is_finite()
53 }
54
55 #[inline]
57 pub fn is_neg_inf(&self) -> bool {
58 matches!(self, Self::NegInf)
59 }
60
61 #[inline]
63 pub fn is_pos_inf(&self) -> bool {
64 matches!(self, Self::PosInf)
65 }
66
67 pub fn as_finite(&self) -> Option<&BigRational> {
69 match self {
70 Self::Finite(r) => Some(r),
71 _ => None,
72 }
73 }
74
75 pub fn negate(&self) -> Bound {
77 match self {
78 Self::NegInf => Self::PosInf,
79 Self::PosInf => Self::NegInf,
80 Self::Finite(r) => Self::Finite(-r),
81 }
82 }
83
84 pub fn add(&self, other: &Bound) -> Bound {
86 match (self, other) {
87 (Self::NegInf, Self::PosInf) | (Self::PosInf, Self::NegInf) => {
88 panic!("Undefined: inf + (-inf)")
89 }
90 (Self::NegInf, _) | (_, Self::NegInf) => Self::NegInf,
91 (Self::PosInf, _) | (_, Self::PosInf) => Self::PosInf,
92 (Self::Finite(a), Self::Finite(b)) => Self::Finite(a + b),
93 }
94 }
95
96 pub fn sub(&self, other: &Bound) -> Bound {
98 self.add(&other.negate())
99 }
100
101 pub fn mul(&self, other: &Bound) -> Bound {
103 match (self, other) {
104 (Self::Finite(a), Self::Finite(b)) => Self::Finite(a * b),
105 (Self::Finite(a), inf) | (inf, Self::Finite(a)) => {
106 if a.is_zero() {
107 Self::Finite(BigRational::zero())
109 } else if a.is_positive() {
110 inf.clone()
111 } else {
112 inf.negate()
113 }
114 }
115 (Self::NegInf, Self::NegInf) | (Self::PosInf, Self::PosInf) => Self::PosInf,
116 (Self::NegInf, Self::PosInf) | (Self::PosInf, Self::NegInf) => Self::NegInf,
117 }
118 }
119
120 pub fn cmp_bound(&self, other: &Bound) -> Ordering {
122 match (self, other) {
123 (Self::NegInf, Self::NegInf) => Ordering::Equal,
124 (Self::NegInf, _) => Ordering::Less,
125 (_, Self::NegInf) => Ordering::Greater,
126 (Self::PosInf, Self::PosInf) => Ordering::Equal,
127 (Self::PosInf, _) => Ordering::Greater,
128 (_, Self::PosInf) => Ordering::Less,
129 (Self::Finite(a), Self::Finite(b)) => a.cmp(b),
130 }
131 }
132
133 pub fn min(&self, other: &Bound) -> Bound {
135 if self.cmp_bound(other) == Ordering::Less {
136 self.clone()
137 } else {
138 other.clone()
139 }
140 }
141
142 pub fn max(&self, other: &Bound) -> Bound {
144 if self.cmp_bound(other) == Ordering::Greater {
145 self.clone()
146 } else {
147 other.clone()
148 }
149 }
150}
151
152impl PartialOrd for Bound {
153 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
154 Some(core::cmp::Ord::cmp(self, other))
155 }
156}
157
158impl Ord for Bound {
159 fn cmp(&self, other: &Self) -> Ordering {
160 self.cmp_bound(other)
161 }
162}
163
164impl fmt::Display for Bound {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 match self {
167 Self::NegInf => write!(f, "-∞"),
168 Self::PosInf => write!(f, "+∞"),
169 Self::Finite(r) => write!(f, "{}", r),
170 }
171 }
172}
173
174#[derive(Clone, Debug, PartialEq, Eq)]
178pub struct Interval {
179 pub lo: Bound,
181 pub hi: Bound,
183 pub lo_open: bool,
185 pub hi_open: bool,
187}
188
189impl Interval {
190 pub fn empty() -> Self {
192 Self {
193 lo: Bound::PosInf,
194 hi: Bound::NegInf,
195 lo_open: true,
196 hi_open: true,
197 }
198 }
199
200 pub fn reals() -> Self {
202 Self {
203 lo: Bound::NegInf,
204 hi: Bound::PosInf,
205 lo_open: true,
206 hi_open: true,
207 }
208 }
209
210 pub fn point(a: BigRational) -> Self {
212 Self {
213 lo: Bound::Finite(a.clone()),
214 hi: Bound::Finite(a),
215 lo_open: false,
216 hi_open: false,
217 }
218 }
219
220 pub fn closed(a: BigRational, b: BigRational) -> Self {
222 Self {
223 lo: Bound::Finite(a),
224 hi: Bound::Finite(b),
225 lo_open: false,
226 hi_open: false,
227 }
228 }
229
230 pub fn open(a: BigRational, b: BigRational) -> Self {
232 Self {
233 lo: Bound::Finite(a),
234 hi: Bound::Finite(b),
235 lo_open: true,
236 hi_open: true,
237 }
238 }
239
240 pub fn half_open_right(a: BigRational, b: BigRational) -> Self {
242 Self {
243 lo: Bound::Finite(a),
244 hi: Bound::Finite(b),
245 lo_open: false,
246 hi_open: true,
247 }
248 }
249
250 pub fn half_open_left(a: BigRational, b: BigRational) -> Self {
252 Self {
253 lo: Bound::Finite(a),
254 hi: Bound::Finite(b),
255 lo_open: true,
256 hi_open: false,
257 }
258 }
259
260 pub fn at_most(b: BigRational) -> Self {
262 Self {
263 lo: Bound::NegInf,
264 hi: Bound::Finite(b),
265 lo_open: true,
266 hi_open: false,
267 }
268 }
269
270 pub fn less_than(b: BigRational) -> Self {
272 Self {
273 lo: Bound::NegInf,
274 hi: Bound::Finite(b),
275 lo_open: true,
276 hi_open: true,
277 }
278 }
279
280 pub fn at_least(a: BigRational) -> Self {
282 Self {
283 lo: Bound::Finite(a),
284 hi: Bound::PosInf,
285 lo_open: false,
286 hi_open: true,
287 }
288 }
289
290 pub fn greater_than(a: BigRational) -> Self {
292 Self {
293 lo: Bound::Finite(a),
294 hi: Bound::PosInf,
295 lo_open: true,
296 hi_open: true,
297 }
298 }
299
300 pub fn is_empty(&self) -> bool {
302 match self.lo.cmp_bound(&self.hi) {
303 Ordering::Greater => true,
304 Ordering::Equal => self.lo_open || self.hi_open,
305 Ordering::Less => false,
306 }
307 }
308
309 pub fn is_point(&self) -> bool {
311 !self.is_empty() && self.lo == self.hi && !self.lo_open && !self.hi_open
312 }
313
314 pub fn is_bounded(&self) -> bool {
316 self.lo.is_finite() && self.hi.is_finite()
317 }
318
319 pub fn contains(&self, x: &BigRational) -> bool {
321 let x_bound = Bound::Finite(x.clone());
322 let lo_ok = match self.lo.cmp_bound(&x_bound) {
323 Ordering::Less => true,
324 Ordering::Equal => !self.lo_open,
325 Ordering::Greater => false,
326 };
327 let hi_ok = match x_bound.cmp_bound(&self.hi) {
328 Ordering::Less => true,
329 Ordering::Equal => !self.hi_open,
330 Ordering::Greater => false,
331 };
332 lo_ok && hi_ok
333 }
334
335 pub fn contains_zero(&self) -> bool {
337 self.contains(&BigRational::zero())
338 }
339
340 pub fn is_positive(&self) -> bool {
342 match &self.lo {
343 Bound::NegInf => false,
344 Bound::PosInf => self.is_empty(),
345 Bound::Finite(r) => {
346 if r.is_positive() {
347 true
348 } else if r.is_zero() {
349 self.lo_open
350 } else {
351 false
352 }
353 }
354 }
355 }
356
357 pub fn is_negative(&self) -> bool {
359 match &self.hi {
360 Bound::PosInf => false,
361 Bound::NegInf => self.is_empty(),
362 Bound::Finite(r) => {
363 if r.is_negative() {
364 true
365 } else if r.is_zero() {
366 self.hi_open
367 } else {
368 false
369 }
370 }
371 }
372 }
373
374 pub fn is_non_negative(&self) -> bool {
376 match &self.lo {
377 Bound::NegInf => false,
378 Bound::PosInf => true,
379 Bound::Finite(r) => !r.is_negative(),
380 }
381 }
382
383 pub fn is_non_positive(&self) -> bool {
385 match &self.hi {
386 Bound::PosInf => false,
387 Bound::NegInf => true,
388 Bound::Finite(r) => !r.is_positive(),
389 }
390 }
391
392 pub fn sign(&self) -> Option<i8> {
396 if self.is_empty() {
397 return None;
398 }
399 if self.is_point()
400 && let Bound::Finite(r) = &self.lo
401 {
402 return Some(if r.is_positive() {
403 1
404 } else if r.is_negative() {
405 -1
406 } else {
407 0
408 });
409 }
410 if self.is_positive() {
411 Some(1)
412 } else if self.is_negative() {
413 Some(-1)
414 } else {
415 None
416 }
417 }
418
419 pub fn negate(&self) -> Interval {
421 Interval {
422 lo: self.hi.negate(),
423 hi: self.lo.negate(),
424 lo_open: self.hi_open,
425 hi_open: self.lo_open,
426 }
427 }
428
429 pub fn add(&self, other: &Interval) -> Interval {
431 if self.is_empty() || other.is_empty() {
432 return Interval::empty();
433 }
434 Interval {
435 lo: self.lo.add(&other.lo),
436 hi: self.hi.add(&other.hi),
437 lo_open: self.lo_open || other.lo_open,
438 hi_open: self.hi_open || other.hi_open,
439 }
440 }
441
442 pub fn sub(&self, other: &Interval) -> Interval {
444 self.add(&other.negate())
445 }
446
447 pub fn mul(&self, other: &Interval) -> Interval {
449 if self.is_empty() || other.is_empty() {
450 return Interval::empty();
451 }
452
453 let products = [
459 (&self.lo, &other.lo, self.lo_open || other.lo_open),
460 (&self.lo, &other.hi, self.lo_open || other.hi_open),
461 (&self.hi, &other.lo, self.hi_open || other.lo_open),
462 (&self.hi, &other.hi, self.hi_open || other.hi_open),
463 ];
464
465 let mut bounds: Vec<(Bound, bool)> = products
467 .iter()
468 .map(|(a, b, open)| (a.mul(b), *open))
469 .collect();
470
471 bounds.sort_by(|a, b| a.0.cmp_bound(&b.0));
473
474 let (lo, lo_open) = bounds
475 .first()
476 .expect("collection validated to be non-empty")
477 .clone();
478 let (hi, hi_open) = bounds
479 .last()
480 .expect("collection validated to be non-empty")
481 .clone();
482
483 Interval {
484 lo,
485 hi,
486 lo_open,
487 hi_open,
488 }
489 }
490
491 pub fn div(&self, other: &Interval) -> Option<Interval> {
494 if self.is_empty() || other.is_empty() {
495 return Some(Interval::empty());
496 }
497
498 if other.contains_zero() {
500 return None;
504 }
505
506 let recip_lo = match &other.hi {
511 Bound::NegInf | Bound::PosInf => Bound::Finite(BigRational::zero()),
512 Bound::Finite(r) => {
513 if r.is_zero() {
514 return None; }
516 Bound::Finite(BigRational::one() / r)
517 }
518 };
519
520 let recip_hi = match &other.lo {
521 Bound::NegInf | Bound::PosInf => Bound::Finite(BigRational::zero()),
522 Bound::Finite(r) => {
523 if r.is_zero() {
524 return None; }
526 Bound::Finite(BigRational::one() / r)
527 }
528 };
529
530 let recip = Interval {
531 lo: recip_lo,
532 hi: recip_hi,
533 lo_open: other.hi_open,
534 hi_open: other.lo_open,
535 };
536
537 Some(self.mul(&recip))
538 }
539
540 pub fn intersect(&self, other: &Interval) -> Interval {
542 if self.is_empty() || other.is_empty() {
543 return Interval::empty();
544 }
545
546 let (lo, lo_open) = match self.lo.cmp_bound(&other.lo) {
547 Ordering::Less => (other.lo.clone(), other.lo_open),
548 Ordering::Greater => (self.lo.clone(), self.lo_open),
549 Ordering::Equal => (self.lo.clone(), self.lo_open || other.lo_open),
550 };
551
552 let (hi, hi_open) = match self.hi.cmp_bound(&other.hi) {
553 Ordering::Less => (self.hi.clone(), self.hi_open),
554 Ordering::Greater => (other.hi.clone(), other.hi_open),
555 Ordering::Equal => (self.hi.clone(), self.hi_open || other.hi_open),
556 };
557
558 Interval {
559 lo,
560 hi,
561 lo_open,
562 hi_open,
563 }
564 }
565
566 pub fn union(&self, other: &Interval) -> Option<Interval> {
569 if self.is_empty() {
570 return Some(other.clone());
571 }
572 if other.is_empty() {
573 return Some(self.clone());
574 }
575
576 let intersects = !self.intersect(other).is_empty();
578 let adjacent_left = self.hi == other.lo && (!self.hi_open || !other.lo_open);
579 let adjacent_right = other.hi == self.lo && (!other.hi_open || !self.lo_open);
580
581 if !intersects && !adjacent_left && !adjacent_right {
582 return None;
583 }
584
585 let (lo, lo_open) = match self.lo.cmp_bound(&other.lo) {
586 Ordering::Less => (self.lo.clone(), self.lo_open),
587 Ordering::Greater => (other.lo.clone(), other.lo_open),
588 Ordering::Equal => (self.lo.clone(), self.lo_open && other.lo_open),
589 };
590
591 let (hi, hi_open) = match self.hi.cmp_bound(&other.hi) {
592 Ordering::Greater => (self.hi.clone(), self.hi_open),
593 Ordering::Less => (other.hi.clone(), other.hi_open),
594 Ordering::Equal => (self.hi.clone(), self.hi_open && other.hi_open),
595 };
596
597 Some(Interval {
598 lo,
599 hi,
600 lo_open,
601 hi_open,
602 })
603 }
604
605 pub fn pow(&self, n: u32) -> Interval {
607 if self.is_empty() {
608 return Interval::empty();
609 }
610 if n == 0 {
611 return Interval::point(BigRational::one());
612 }
613 if n == 1 {
614 return self.clone();
615 }
616
617 if n.is_multiple_of(2) {
618 if self.contains_zero() {
621 let lo_abs = match &self.lo {
623 Bound::Finite(r) => r.abs(),
624 Bound::NegInf => return Interval::at_least(BigRational::zero()),
625 Bound::PosInf => unreachable!(),
626 };
627 let hi_abs = match &self.hi {
628 Bound::Finite(r) => r.abs(),
629 Bound::PosInf => return Interval::at_least(BigRational::zero()),
630 Bound::NegInf => unreachable!(),
631 };
632 let max_abs = lo_abs.max(hi_abs);
633 Interval {
634 lo: Bound::Finite(BigRational::zero()),
635 hi: Bound::Finite(pow_rational(&max_abs, n)),
636 lo_open: false,
637 hi_open: self.lo_open && self.hi_open,
638 }
639 } else if self.is_positive() {
640 let lo_val = match &self.lo {
642 Bound::Finite(r) => pow_rational(r, n),
643 _ => return Interval::at_least(BigRational::zero()),
644 };
645 let hi_val = match &self.hi {
646 Bound::Finite(r) => pow_rational(r, n),
647 _ => return Interval::at_least(lo_val),
648 };
649 Interval {
650 lo: Bound::Finite(lo_val),
651 hi: Bound::Finite(hi_val),
652 lo_open: self.lo_open,
653 hi_open: self.hi_open,
654 }
655 } else {
656 let lo_val = match &self.hi {
658 Bound::Finite(r) => pow_rational(r, n),
659 _ => return Interval::at_least(BigRational::zero()),
660 };
661 let hi_val = match &self.lo {
662 Bound::Finite(r) => pow_rational(r, n),
663 _ => return Interval::at_least(lo_val),
664 };
665 Interval {
666 lo: Bound::Finite(lo_val),
667 hi: Bound::Finite(hi_val),
668 lo_open: self.hi_open,
669 hi_open: self.lo_open,
670 }
671 }
672 } else {
673 let lo_val = match &self.lo {
676 Bound::NegInf => Bound::NegInf,
677 Bound::PosInf => Bound::PosInf,
678 Bound::Finite(r) => Bound::Finite(pow_rational(r, n)),
679 };
680 let hi_val = match &self.hi {
681 Bound::NegInf => Bound::NegInf,
682 Bound::PosInf => Bound::PosInf,
683 Bound::Finite(r) => Bound::Finite(pow_rational(r, n)),
684 };
685 Interval {
686 lo: lo_val,
687 hi: hi_val,
688 lo_open: self.lo_open,
689 hi_open: self.hi_open,
690 }
691 }
692 }
693
694 pub fn midpoint(&self) -> Option<BigRational> {
696 match (&self.lo, &self.hi) {
697 (Bound::Finite(a), Bound::Finite(b)) => {
698 Some((a + b) / BigRational::from_integer(BigInt::from(2)))
699 }
700 _ => None,
701 }
702 }
703
704 pub fn width(&self) -> Option<BigRational> {
706 match (&self.lo, &self.hi) {
707 (Bound::Finite(a), Bound::Finite(b)) => Some(b - a),
708 _ => None,
709 }
710 }
711
712 pub fn split(&self, at: &BigRational) -> (Interval, Interval) {
714 if !self.contains(at) {
715 return (self.clone(), Interval::empty());
716 }
717
718 let left = Interval {
719 lo: self.lo.clone(),
720 hi: Bound::Finite(at.clone()),
721 lo_open: self.lo_open,
722 hi_open: false,
723 };
724
725 let right = Interval {
726 lo: Bound::Finite(at.clone()),
727 hi: self.hi.clone(),
728 lo_open: false,
729 hi_open: self.hi_open,
730 };
731
732 (left, right)
733 }
734
735 pub fn hull(&self, other: &Interval) -> Interval {
738 if self.is_empty() {
739 return other.clone();
740 }
741 if other.is_empty() {
742 return self.clone();
743 }
744
745 let (lo, lo_open) = match self.lo.cmp_bound(&other.lo) {
746 Ordering::Less => (self.lo.clone(), self.lo_open),
747 Ordering::Greater => (other.lo.clone(), other.lo_open),
748 Ordering::Equal => (self.lo.clone(), self.lo_open && other.lo_open),
749 };
750
751 let (hi, hi_open) = match self.hi.cmp_bound(&other.hi) {
752 Ordering::Greater => (self.hi.clone(), self.hi_open),
753 Ordering::Less => (other.hi.clone(), other.hi_open),
754 Ordering::Equal => (self.hi.clone(), self.hi_open && other.hi_open),
755 };
756
757 Interval {
758 lo,
759 hi,
760 lo_open,
761 hi_open,
762 }
763 }
764
765 pub fn tighten_lower(&self, new_lo: Bound, new_lo_open: bool) -> Interval {
768 if self.is_empty() {
769 return Interval::empty();
770 }
771
772 let (lo, lo_open) = match self.lo.cmp_bound(&new_lo) {
773 Ordering::Less => (new_lo, new_lo_open),
774 Ordering::Greater => (self.lo.clone(), self.lo_open),
775 Ordering::Equal => (self.lo.clone(), self.lo_open || new_lo_open),
776 };
777
778 Interval {
779 lo,
780 hi: self.hi.clone(),
781 lo_open,
782 hi_open: self.hi_open,
783 }
784 }
785
786 pub fn tighten_upper(&self, new_hi: Bound, new_hi_open: bool) -> Interval {
789 if self.is_empty() {
790 return Interval::empty();
791 }
792
793 let (hi, hi_open) = match self.hi.cmp_bound(&new_hi) {
794 Ordering::Greater => (new_hi, new_hi_open),
795 Ordering::Less => (self.hi.clone(), self.hi_open),
796 Ordering::Equal => (self.hi.clone(), self.hi_open || new_hi_open),
797 };
798
799 Interval {
800 lo: self.lo.clone(),
801 hi,
802 lo_open: self.lo_open,
803 hi_open,
804 }
805 }
806
807 pub fn propagate_add(x: &Interval, result: &Interval) -> Interval {
811 result.sub(x)
813 }
814
815 pub fn propagate_sub_left(x: &Interval, result: &Interval) -> Interval {
819 x.sub(result)
821 }
822
823 pub fn propagate_sub_right(y: &Interval, result: &Interval) -> Interval {
827 result.add(y)
829 }
830
831 pub fn propagate_mul(x: &Interval, result: &Interval) -> Option<Interval> {
835 Interval::div(result, x)
837 }
838
839 pub fn propagate_div_left(x: &Interval, result: &Interval) -> Option<Interval> {
843 Interval::div(x, result)
845 }
846
847 pub fn propagate_div_right(y: &Interval, result: &Interval) -> Interval {
851 result.mul(y)
853 }
854
855 pub fn is_subset_of(&self, other: &Interval) -> bool {
857 if self.is_empty() {
858 return true;
859 }
860 if other.is_empty() {
861 return false;
862 }
863
864 let lo_ok = match self.lo.cmp_bound(&other.lo) {
866 Ordering::Less => false,
867 Ordering::Greater => true,
868 Ordering::Equal => !self.lo_open || other.lo_open,
869 };
870
871 let hi_ok = match self.hi.cmp_bound(&other.hi) {
873 Ordering::Greater => false,
874 Ordering::Less => true,
875 Ordering::Equal => !self.hi_open || other.hi_open,
876 };
877
878 lo_ok && hi_ok
879 }
880
881 pub fn overlaps(&self, other: &Interval) -> bool {
883 !self.intersect(other).is_empty()
884 }
885
886 pub fn widen(&self, amount: &BigRational) -> Interval {
888 if self.is_empty() {
889 return Interval::empty();
890 }
891
892 let lo = match &self.lo {
893 Bound::Finite(r) => Bound::Finite(r - amount),
894 bound => bound.clone(),
895 };
896
897 let hi = match &self.hi {
898 Bound::Finite(r) => Bound::Finite(r + amount),
899 bound => bound.clone(),
900 };
901
902 Interval {
903 lo,
904 hi,
905 lo_open: self.lo_open,
906 hi_open: self.hi_open,
907 }
908 }
909}
910
911fn pow_rational(r: &BigRational, n: u32) -> BigRational {
913 if n == 0 {
914 return BigRational::one();
915 }
916
917 let mut result = BigRational::one();
918 let mut base = r.clone();
919 let mut exp = n;
920
921 while exp > 0 {
922 if exp & 1 == 1 {
923 result = &result * &base;
924 }
925 base = &base * &base;
926 exp >>= 1;
927 }
928
929 result
930}
931
932impl fmt::Display for Interval {
933 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
934 if self.is_empty() {
935 write!(f, "∅")
936 } else {
937 let lo_bracket = if self.lo_open { '(' } else { '[' };
938 let hi_bracket = if self.hi_open { ')' } else { ']' };
939 write!(f, "{}{}, {}{}", lo_bracket, self.lo, self.hi, hi_bracket)
940 }
941 }
942}
943
944impl Default for Interval {
945 fn default() -> Self {
946 Self::reals()
947 }
948}
949
950impl Neg for Interval {
951 type Output = Interval;
952
953 fn neg(self) -> Self::Output {
954 self.negate()
955 }
956}
957
958impl Neg for &Interval {
959 type Output = Interval;
960
961 fn neg(self) -> Self::Output {
962 self.negate()
963 }
964}
965
966impl Add for Interval {
967 type Output = Interval;
968
969 fn add(self, rhs: Self) -> Self::Output {
970 Interval::add(&self, &rhs)
971 }
972}
973
974impl Add<&Interval> for &Interval {
975 type Output = Interval;
976
977 fn add(self, rhs: &Interval) -> Self::Output {
978 Interval::add(self, rhs)
979 }
980}
981
982impl Sub for Interval {
983 type Output = Interval;
984
985 fn sub(self, rhs: Self) -> Self::Output {
986 Interval::sub(&self, &rhs)
987 }
988}
989
990impl Sub<&Interval> for &Interval {
991 type Output = Interval;
992
993 fn sub(self, rhs: &Interval) -> Self::Output {
994 Interval::sub(self, rhs)
995 }
996}
997
998impl Mul for Interval {
999 type Output = Interval;
1000
1001 fn mul(self, rhs: Self) -> Self::Output {
1002 Interval::mul(&self, &rhs)
1003 }
1004}
1005
1006impl Mul<&Interval> for &Interval {
1007 type Output = Interval;
1008
1009 fn mul(self, rhs: &Interval) -> Self::Output {
1010 Interval::mul(self, rhs)
1011 }
1012}
1013
1014impl Div for Interval {
1015 type Output = Option<Interval>;
1016
1017 fn div(self, rhs: Self) -> Self::Output {
1018 Interval::div(&self, &rhs)
1019 }
1020}
1021
1022impl Div<&Interval> for &Interval {
1023 type Output = Option<Interval>;
1024
1025 fn div(self, rhs: &Interval) -> Self::Output {
1026 Interval::div(self, rhs)
1027 }
1028}
1029
1030#[cfg(test)]
1031mod tests {
1032 use super::*;
1033
1034 fn rat(n: i64) -> BigRational {
1035 BigRational::from_integer(BigInt::from(n))
1036 }
1037
1038 #[test]
1039 fn test_interval_empty() {
1040 let i = Interval::empty();
1041 assert!(i.is_empty());
1042 assert!(!i.contains(&rat(0)));
1043 }
1044
1045 #[test]
1046 fn test_interval_point() {
1047 let i = Interval::point(rat(5));
1048 assert!(i.is_point());
1049 assert!(i.contains(&rat(5)));
1050 assert!(!i.contains(&rat(4)));
1051 assert!(i.is_positive());
1052 }
1053
1054 #[test]
1055 fn test_interval_closed() {
1056 let i = Interval::closed(rat(1), rat(5));
1057 assert!(i.contains(&rat(1)));
1058 assert!(i.contains(&rat(3)));
1059 assert!(i.contains(&rat(5)));
1060 assert!(!i.contains(&rat(0)));
1061 assert!(!i.contains(&rat(6)));
1062 }
1063
1064 #[test]
1065 fn test_interval_open() {
1066 let i = Interval::open(rat(1), rat(5));
1067 assert!(!i.contains(&rat(1)));
1068 assert!(i.contains(&rat(3)));
1069 assert!(!i.contains(&rat(5)));
1070 }
1071
1072 #[test]
1073 fn test_interval_add() {
1074 let a = Interval::closed(rat(1), rat(3));
1075 let b = Interval::closed(rat(2), rat(4));
1076 let c = Interval::add(&a, &b);
1077 assert!(c.contains(&rat(3)));
1078 assert!(c.contains(&rat(7)));
1079 assert!(!c.contains(&rat(2)));
1080 assert!(!c.contains(&rat(8)));
1081 }
1082
1083 #[test]
1084 fn test_interval_mul() {
1085 let a = Interval::closed(rat(2), rat(3));
1086 let b = Interval::closed(rat(4), rat(5));
1087 let c = Interval::mul(&a, &b);
1088 assert!(c.contains(&rat(8)));
1090 assert!(c.contains(&rat(15)));
1091 assert!(!c.contains(&rat(7)));
1092 assert!(!c.contains(&rat(16)));
1093 }
1094
1095 #[test]
1096 fn test_interval_mul_mixed_signs() {
1097 let a = Interval::closed(rat(-2), rat(3));
1098 let b = Interval::closed(rat(-1), rat(2));
1099 let c = Interval::mul(&a, &b);
1100 assert!(c.contains(&rat(-4)));
1103 assert!(c.contains(&rat(6)));
1104 assert!(c.contains(&rat(0)));
1105 assert!(!c.contains(&rat(-5)));
1106 assert!(!c.contains(&rat(7)));
1107 }
1108
1109 #[test]
1110 fn test_interval_negate() {
1111 let a = Interval::closed(rat(1), rat(5));
1112 let b = a.negate();
1113 assert!(b.contains(&rat(-5)));
1114 assert!(b.contains(&rat(-1)));
1115 assert!(!b.contains(&rat(0)));
1116 }
1117
1118 #[test]
1119 fn test_interval_intersect() {
1120 let a = Interval::closed(rat(1), rat(5));
1121 let b = Interval::closed(rat(3), rat(7));
1122 let c = a.intersect(&b);
1123 assert!(c.contains(&rat(3)));
1125 assert!(c.contains(&rat(5)));
1126 assert!(!c.contains(&rat(2)));
1127 assert!(!c.contains(&rat(6)));
1128 }
1129
1130 #[test]
1131 fn test_interval_pow_even() {
1132 let a = Interval::closed(rat(-2), rat(3));
1133 let b = a.pow(2);
1134 assert!(b.contains(&rat(0)));
1136 assert!(b.contains(&rat(9)));
1137 assert!(!b.contains(&rat(-1)));
1138 assert!(!b.contains(&rat(10)));
1139 }
1140
1141 #[test]
1142 fn test_interval_pow_odd() {
1143 let a = Interval::closed(rat(-2), rat(3));
1144 let b = a.pow(3);
1145 assert!(b.contains(&rat(-8)));
1147 assert!(b.contains(&rat(27)));
1148 assert!(!b.contains(&rat(-9)));
1149 assert!(!b.contains(&rat(28)));
1150 }
1151
1152 #[test]
1153 fn test_interval_sign() {
1154 assert_eq!(Interval::closed(rat(1), rat(5)).sign(), Some(1));
1155 assert_eq!(Interval::closed(rat(-5), rat(-1)).sign(), Some(-1));
1156 assert_eq!(Interval::closed(rat(-1), rat(1)).sign(), None);
1157 assert_eq!(Interval::point(rat(0)).sign(), Some(0));
1158 }
1159
1160 #[test]
1161 fn test_interval_unbounded() {
1162 let a = Interval::at_least(rat(0));
1163 assert!(a.is_non_negative());
1164 assert!(a.contains(&rat(0)));
1165 assert!(a.contains(&rat(1000)));
1166 assert!(!a.contains(&rat(-1)));
1167
1168 let b = Interval::less_than(rat(0));
1169 assert!(b.is_negative());
1170 assert!(!b.contains(&rat(0)));
1171 assert!(b.contains(&rat(-1)));
1172 }
1173
1174 #[test]
1175 fn test_interval_midpoint() {
1176 let i = Interval::closed(rat(2), rat(8));
1177 assert_eq!(i.midpoint(), Some(rat(5)));
1178 }
1179
1180 #[test]
1181 fn test_interval_div() {
1182 let a = Interval::closed(rat(4), rat(12));
1184 let b = Interval::closed(rat(2), rat(3));
1185 let c = Interval::div(&a, &b).expect("Division should succeed");
1186 assert!(c.contains(&BigRational::new(BigInt::from(4), BigInt::from(3))));
1188 assert!(c.contains(&rat(6)));
1189 }
1190
1191 #[test]
1192 fn test_interval_div_by_zero() {
1193 let a = Interval::closed(rat(1), rat(2));
1195 let b = Interval::closed(rat(-1), rat(1)); assert!(Interval::div(&a, &b).is_none());
1197 }
1198
1199 #[test]
1200 fn test_interval_div_positive() {
1201 let a = Interval::closed(rat(6), rat(12));
1203 let b = Interval::closed(rat(2), rat(3));
1204 let c = Interval::div(&a, &b).expect("Division should succeed");
1205 assert!(c.contains(&rat(2)));
1206 assert!(c.contains(&rat(6)));
1207 }
1208
1209 #[test]
1210 fn test_interval_hull() {
1211 let a = Interval::closed(rat(1), rat(3));
1212 let b = Interval::closed(rat(5), rat(7));
1213 let hull = a.hull(&b);
1214 assert_eq!(hull.lo, Bound::Finite(rat(1)));
1216 assert_eq!(hull.hi, Bound::Finite(rat(7)));
1217 assert!(hull.contains(&rat(1)));
1218 assert!(hull.contains(&rat(4)));
1219 assert!(hull.contains(&rat(7)));
1220 }
1221
1222 #[test]
1223 fn test_interval_tighten_lower() {
1224 let i = Interval::closed(rat(1), rat(10));
1225 let tightened = i.tighten_lower(Bound::Finite(rat(5)), false);
1226 assert_eq!(tightened.lo, Bound::Finite(rat(5)));
1228 assert_eq!(tightened.hi, Bound::Finite(rat(10)));
1229 assert!(!tightened.contains(&rat(4)));
1230 assert!(tightened.contains(&rat(5)));
1231 }
1232
1233 #[test]
1234 fn test_interval_tighten_upper() {
1235 let i = Interval::closed(rat(1), rat(10));
1236 let tightened = i.tighten_upper(Bound::Finite(rat(6)), false);
1237 assert_eq!(tightened.lo, Bound::Finite(rat(1)));
1239 assert_eq!(tightened.hi, Bound::Finite(rat(6)));
1240 assert!(tightened.contains(&rat(6)));
1241 assert!(!tightened.contains(&rat(7)));
1242 }
1243
1244 #[test]
1245 fn test_interval_propagate_add() {
1246 let x = Interval::closed(rat(1), rat(2));
1249 let result = Interval::closed(rat(5), rat(8));
1250 let y = Interval::propagate_add(&x, &result);
1251 assert!(y.contains(&rat(3)));
1252 assert!(y.contains(&rat(7)));
1253 assert!(!y.contains(&rat(2)));
1254 assert!(!y.contains(&rat(8)));
1255 }
1256
1257 #[test]
1258 fn test_interval_propagate_sub() {
1259 let x = Interval::closed(rat(10), rat(15));
1262 let result = Interval::closed(rat(2), rat(5));
1263 let y = Interval::propagate_sub_left(&x, &result);
1264 assert!(y.contains(&rat(5)));
1265 assert!(y.contains(&rat(13)));
1266 }
1267
1268 #[test]
1269 fn test_interval_propagate_mul() {
1270 let x = Interval::closed(rat(2), rat(3));
1273 let result = Interval::closed(rat(10), rat(18));
1274 let y = Interval::propagate_mul(&x, &result).expect("Propagation should succeed");
1275 assert!(y.contains(&rat(5)));
1277 }
1278
1279 #[test]
1280 fn test_interval_is_subset_of() {
1281 let a = Interval::closed(rat(2), rat(5));
1282 let b = Interval::closed(rat(1), rat(10));
1283 assert!(a.is_subset_of(&b));
1284 assert!(!b.is_subset_of(&a));
1285
1286 let c = Interval::closed(rat(1), rat(10));
1287 assert!(c.is_subset_of(&c));
1288 }
1289
1290 #[test]
1291 fn test_interval_overlaps() {
1292 let a = Interval::closed(rat(1), rat(5));
1293 let b = Interval::closed(rat(3), rat(7));
1294 assert!(a.overlaps(&b));
1295 assert!(b.overlaps(&a));
1296
1297 let c = Interval::closed(rat(10), rat(15));
1298 assert!(!a.overlaps(&c));
1299 }
1300
1301 #[test]
1302 fn test_interval_widen() {
1303 let i = Interval::closed(rat(5), rat(10));
1304 let widened = i.widen(&rat(2));
1305 assert_eq!(widened.lo, Bound::Finite(rat(3)));
1307 assert_eq!(widened.hi, Bound::Finite(rat(12)));
1308 assert!(widened.contains(&rat(3)));
1309 assert!(widened.contains(&rat(12)));
1310 assert!(!widened.contains(&rat(2)));
1311 assert!(!widened.contains(&rat(13)));
1312 }
1313}