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) = {
484 let min_val = bounds[0].0.clone();
485 let is_open = bounds
486 .iter()
487 .take_while(|(b, _)| b.cmp_bound(&min_val) == Ordering::Equal)
488 .all(|(_, open)| *open);
489 (min_val, is_open)
490 };
491 let (hi, hi_open) = {
492 let max_val = bounds[bounds.len() - 1].0.clone();
493 let is_open = bounds
494 .iter()
495 .rev()
496 .take_while(|(b, _)| b.cmp_bound(&max_val) == Ordering::Equal)
497 .all(|(_, open)| *open);
498 (max_val, is_open)
499 };
500
501 Interval {
502 lo,
503 hi,
504 lo_open,
505 hi_open,
506 }
507 }
508
509 pub fn div(&self, other: &Interval) -> Option<Interval> {
512 if self.is_empty() || other.is_empty() {
513 return Some(Interval::empty());
514 }
515
516 if other.contains_zero() {
518 return None;
522 }
523
524 let recip_lo = match &other.hi {
529 Bound::NegInf | Bound::PosInf => Bound::Finite(BigRational::zero()),
530 Bound::Finite(r) => {
531 if r.is_zero() {
532 return None; }
534 Bound::Finite(BigRational::one() / r)
535 }
536 };
537
538 let recip_hi = match &other.lo {
539 Bound::NegInf | Bound::PosInf => Bound::Finite(BigRational::zero()),
540 Bound::Finite(r) => {
541 if r.is_zero() {
542 return None; }
544 Bound::Finite(BigRational::one() / r)
545 }
546 };
547
548 let recip = Interval {
549 lo: recip_lo,
550 hi: recip_hi,
551 lo_open: other.hi_open,
552 hi_open: other.lo_open,
553 };
554
555 Some(self.mul(&recip))
556 }
557
558 pub fn intersect(&self, other: &Interval) -> Interval {
560 if self.is_empty() || other.is_empty() {
561 return Interval::empty();
562 }
563
564 let (lo, lo_open) = match self.lo.cmp_bound(&other.lo) {
565 Ordering::Less => (other.lo.clone(), other.lo_open),
566 Ordering::Greater => (self.lo.clone(), self.lo_open),
567 Ordering::Equal => (self.lo.clone(), self.lo_open || other.lo_open),
568 };
569
570 let (hi, hi_open) = match self.hi.cmp_bound(&other.hi) {
571 Ordering::Less => (self.hi.clone(), self.hi_open),
572 Ordering::Greater => (other.hi.clone(), other.hi_open),
573 Ordering::Equal => (self.hi.clone(), self.hi_open || other.hi_open),
574 };
575
576 Interval {
577 lo,
578 hi,
579 lo_open,
580 hi_open,
581 }
582 }
583
584 pub fn union(&self, other: &Interval) -> Option<Interval> {
587 if self.is_empty() {
588 return Some(other.clone());
589 }
590 if other.is_empty() {
591 return Some(self.clone());
592 }
593
594 let intersects = !self.intersect(other).is_empty();
596 let adjacent_left = self.hi == other.lo && (!self.hi_open || !other.lo_open);
597 let adjacent_right = other.hi == self.lo && (!other.hi_open || !self.lo_open);
598
599 if !intersects && !adjacent_left && !adjacent_right {
600 return None;
601 }
602
603 let (lo, lo_open) = match self.lo.cmp_bound(&other.lo) {
604 Ordering::Less => (self.lo.clone(), self.lo_open),
605 Ordering::Greater => (other.lo.clone(), other.lo_open),
606 Ordering::Equal => (self.lo.clone(), self.lo_open && other.lo_open),
607 };
608
609 let (hi, hi_open) = match self.hi.cmp_bound(&other.hi) {
610 Ordering::Greater => (self.hi.clone(), self.hi_open),
611 Ordering::Less => (other.hi.clone(), other.hi_open),
612 Ordering::Equal => (self.hi.clone(), self.hi_open && other.hi_open),
613 };
614
615 Some(Interval {
616 lo,
617 hi,
618 lo_open,
619 hi_open,
620 })
621 }
622
623 pub fn pow(&self, n: u32) -> Interval {
625 if self.is_empty() {
626 return Interval::empty();
627 }
628 if n == 0 {
629 return Interval::point(BigRational::one());
630 }
631 if n == 1 {
632 return self.clone();
633 }
634
635 if n.is_multiple_of(2) {
636 if self.contains_zero() {
639 let lo_abs = match &self.lo {
641 Bound::Finite(r) => r.abs(),
642 Bound::NegInf => return Interval::at_least(BigRational::zero()),
643 Bound::PosInf => unreachable!(),
644 };
645 let hi_abs = match &self.hi {
646 Bound::Finite(r) => r.abs(),
647 Bound::PosInf => return Interval::at_least(BigRational::zero()),
648 Bound::NegInf => unreachable!(),
649 };
650 let max_abs = lo_abs.max(hi_abs);
651 Interval {
652 lo: Bound::Finite(BigRational::zero()),
653 hi: Bound::Finite(pow_rational(&max_abs, n)),
654 lo_open: false,
655 hi_open: self.lo_open && self.hi_open,
656 }
657 } else if self.is_positive() {
658 let lo_val = match &self.lo {
660 Bound::Finite(r) => pow_rational(r, n),
661 _ => return Interval::at_least(BigRational::zero()),
662 };
663 let hi_val = match &self.hi {
664 Bound::Finite(r) => pow_rational(r, n),
665 _ => return Interval::at_least(lo_val),
666 };
667 Interval {
668 lo: Bound::Finite(lo_val),
669 hi: Bound::Finite(hi_val),
670 lo_open: self.lo_open,
671 hi_open: self.hi_open,
672 }
673 } else {
674 let lo_val = match &self.hi {
676 Bound::Finite(r) => pow_rational(r, n),
677 _ => return Interval::at_least(BigRational::zero()),
678 };
679 let hi_val = match &self.lo {
680 Bound::Finite(r) => pow_rational(r, n),
681 _ => return Interval::at_least(lo_val),
682 };
683 Interval {
684 lo: Bound::Finite(lo_val),
685 hi: Bound::Finite(hi_val),
686 lo_open: self.hi_open,
687 hi_open: self.lo_open,
688 }
689 }
690 } else {
691 let lo_val = match &self.lo {
694 Bound::NegInf => Bound::NegInf,
695 Bound::PosInf => Bound::PosInf,
696 Bound::Finite(r) => Bound::Finite(pow_rational(r, n)),
697 };
698 let hi_val = match &self.hi {
699 Bound::NegInf => Bound::NegInf,
700 Bound::PosInf => Bound::PosInf,
701 Bound::Finite(r) => Bound::Finite(pow_rational(r, n)),
702 };
703 Interval {
704 lo: lo_val,
705 hi: hi_val,
706 lo_open: self.lo_open,
707 hi_open: self.hi_open,
708 }
709 }
710 }
711
712 pub fn midpoint(&self) -> Option<BigRational> {
714 match (&self.lo, &self.hi) {
715 (Bound::Finite(a), Bound::Finite(b)) => {
716 Some((a + b) / BigRational::from_integer(BigInt::from(2)))
717 }
718 _ => None,
719 }
720 }
721
722 pub fn width(&self) -> Option<BigRational> {
724 match (&self.lo, &self.hi) {
725 (Bound::Finite(a), Bound::Finite(b)) => Some(b - a),
726 _ => None,
727 }
728 }
729
730 pub fn split(&self, at: &BigRational) -> (Interval, Interval) {
732 if !self.contains(at) {
733 return (self.clone(), Interval::empty());
734 }
735
736 let left = Interval {
737 lo: self.lo.clone(),
738 hi: Bound::Finite(at.clone()),
739 lo_open: self.lo_open,
740 hi_open: false,
741 };
742
743 let right = Interval {
744 lo: Bound::Finite(at.clone()),
745 hi: self.hi.clone(),
746 lo_open: false,
747 hi_open: self.hi_open,
748 };
749
750 (left, right)
751 }
752
753 pub fn hull(&self, other: &Interval) -> Interval {
756 if self.is_empty() {
757 return other.clone();
758 }
759 if other.is_empty() {
760 return self.clone();
761 }
762
763 let (lo, lo_open) = match self.lo.cmp_bound(&other.lo) {
764 Ordering::Less => (self.lo.clone(), self.lo_open),
765 Ordering::Greater => (other.lo.clone(), other.lo_open),
766 Ordering::Equal => (self.lo.clone(), self.lo_open && other.lo_open),
767 };
768
769 let (hi, hi_open) = match self.hi.cmp_bound(&other.hi) {
770 Ordering::Greater => (self.hi.clone(), self.hi_open),
771 Ordering::Less => (other.hi.clone(), other.hi_open),
772 Ordering::Equal => (self.hi.clone(), self.hi_open && other.hi_open),
773 };
774
775 Interval {
776 lo,
777 hi,
778 lo_open,
779 hi_open,
780 }
781 }
782
783 pub fn tighten_lower(&self, new_lo: Bound, new_lo_open: bool) -> Interval {
786 if self.is_empty() {
787 return Interval::empty();
788 }
789
790 let (lo, lo_open) = match self.lo.cmp_bound(&new_lo) {
791 Ordering::Less => (new_lo, new_lo_open),
792 Ordering::Greater => (self.lo.clone(), self.lo_open),
793 Ordering::Equal => (self.lo.clone(), self.lo_open || new_lo_open),
794 };
795
796 Interval {
797 lo,
798 hi: self.hi.clone(),
799 lo_open,
800 hi_open: self.hi_open,
801 }
802 }
803
804 pub fn tighten_upper(&self, new_hi: Bound, new_hi_open: bool) -> Interval {
807 if self.is_empty() {
808 return Interval::empty();
809 }
810
811 let (hi, hi_open) = match self.hi.cmp_bound(&new_hi) {
812 Ordering::Greater => (new_hi, new_hi_open),
813 Ordering::Less => (self.hi.clone(), self.hi_open),
814 Ordering::Equal => (self.hi.clone(), self.hi_open || new_hi_open),
815 };
816
817 Interval {
818 lo: self.lo.clone(),
819 hi,
820 lo_open: self.lo_open,
821 hi_open,
822 }
823 }
824
825 pub fn propagate_add(x: &Interval, result: &Interval) -> Interval {
829 result.sub(x)
831 }
832
833 pub fn propagate_sub_left(x: &Interval, result: &Interval) -> Interval {
837 x.sub(result)
839 }
840
841 pub fn propagate_sub_right(y: &Interval, result: &Interval) -> Interval {
845 result.add(y)
847 }
848
849 pub fn propagate_mul(x: &Interval, result: &Interval) -> Option<Interval> {
853 Interval::div(result, x)
855 }
856
857 pub fn propagate_div_left(x: &Interval, result: &Interval) -> Option<Interval> {
861 Interval::div(x, result)
863 }
864
865 pub fn propagate_div_right(y: &Interval, result: &Interval) -> Interval {
869 result.mul(y)
871 }
872
873 pub fn is_subset_of(&self, other: &Interval) -> bool {
875 if self.is_empty() {
876 return true;
877 }
878 if other.is_empty() {
879 return false;
880 }
881
882 let lo_ok = match self.lo.cmp_bound(&other.lo) {
884 Ordering::Less => false,
885 Ordering::Greater => true,
886 Ordering::Equal => !self.lo_open || other.lo_open,
887 };
888
889 let hi_ok = match self.hi.cmp_bound(&other.hi) {
891 Ordering::Greater => false,
892 Ordering::Less => true,
893 Ordering::Equal => !self.hi_open || other.hi_open,
894 };
895
896 lo_ok && hi_ok
897 }
898
899 pub fn overlaps(&self, other: &Interval) -> bool {
901 !self.intersect(other).is_empty()
902 }
903
904 pub fn widen(&self, amount: &BigRational) -> Interval {
906 if self.is_empty() {
907 return Interval::empty();
908 }
909
910 let lo = match &self.lo {
911 Bound::Finite(r) => Bound::Finite(r - amount),
912 bound => bound.clone(),
913 };
914
915 let hi = match &self.hi {
916 Bound::Finite(r) => Bound::Finite(r + amount),
917 bound => bound.clone(),
918 };
919
920 Interval {
921 lo,
922 hi,
923 lo_open: self.lo_open,
924 hi_open: self.hi_open,
925 }
926 }
927}
928
929fn pow_rational(r: &BigRational, n: u32) -> BigRational {
931 if n == 0 {
932 return BigRational::one();
933 }
934
935 let mut result = BigRational::one();
936 let mut base = r.clone();
937 let mut exp = n;
938
939 while exp > 0 {
940 if exp & 1 == 1 {
941 result = &result * &base;
942 }
943 base = &base * &base;
944 exp >>= 1;
945 }
946
947 result
948}
949
950impl fmt::Display for Interval {
951 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
952 if self.is_empty() {
953 write!(f, "∅")
954 } else {
955 let lo_bracket = if self.lo_open { '(' } else { '[' };
956 let hi_bracket = if self.hi_open { ')' } else { ']' };
957 write!(f, "{}{}, {}{}", lo_bracket, self.lo, self.hi, hi_bracket)
958 }
959 }
960}
961
962impl Default for Interval {
963 fn default() -> Self {
964 Self::reals()
965 }
966}
967
968impl Neg for Interval {
969 type Output = Interval;
970
971 fn neg(self) -> Self::Output {
972 self.negate()
973 }
974}
975
976impl Neg for &Interval {
977 type Output = Interval;
978
979 fn neg(self) -> Self::Output {
980 self.negate()
981 }
982}
983
984impl Add for Interval {
985 type Output = Interval;
986
987 fn add(self, rhs: Self) -> Self::Output {
988 Interval::add(&self, &rhs)
989 }
990}
991
992impl Add<&Interval> for &Interval {
993 type Output = Interval;
994
995 fn add(self, rhs: &Interval) -> Self::Output {
996 Interval::add(self, rhs)
997 }
998}
999
1000impl Sub for Interval {
1001 type Output = Interval;
1002
1003 fn sub(self, rhs: Self) -> Self::Output {
1004 Interval::sub(&self, &rhs)
1005 }
1006}
1007
1008impl Sub<&Interval> for &Interval {
1009 type Output = Interval;
1010
1011 fn sub(self, rhs: &Interval) -> Self::Output {
1012 Interval::sub(self, rhs)
1013 }
1014}
1015
1016impl Mul for Interval {
1017 type Output = Interval;
1018
1019 fn mul(self, rhs: Self) -> Self::Output {
1020 Interval::mul(&self, &rhs)
1021 }
1022}
1023
1024impl Mul<&Interval> for &Interval {
1025 type Output = Interval;
1026
1027 fn mul(self, rhs: &Interval) -> Self::Output {
1028 Interval::mul(self, rhs)
1029 }
1030}
1031
1032impl Div for Interval {
1033 type Output = Option<Interval>;
1034
1035 fn div(self, rhs: Self) -> Self::Output {
1036 Interval::div(&self, &rhs)
1037 }
1038}
1039
1040impl Div<&Interval> for &Interval {
1041 type Output = Option<Interval>;
1042
1043 fn div(self, rhs: &Interval) -> Self::Output {
1044 Interval::div(self, rhs)
1045 }
1046}
1047
1048#[cfg(test)]
1049mod tests {
1050 use super::*;
1051
1052 fn rat(n: i64) -> BigRational {
1053 BigRational::from_integer(BigInt::from(n))
1054 }
1055
1056 #[test]
1057 fn test_interval_empty() {
1058 let i = Interval::empty();
1059 assert!(i.is_empty());
1060 assert!(!i.contains(&rat(0)));
1061 }
1062
1063 #[test]
1064 fn test_interval_point() {
1065 let i = Interval::point(rat(5));
1066 assert!(i.is_point());
1067 assert!(i.contains(&rat(5)));
1068 assert!(!i.contains(&rat(4)));
1069 assert!(i.is_positive());
1070 }
1071
1072 #[test]
1073 fn test_interval_closed() {
1074 let i = Interval::closed(rat(1), rat(5));
1075 assert!(i.contains(&rat(1)));
1076 assert!(i.contains(&rat(3)));
1077 assert!(i.contains(&rat(5)));
1078 assert!(!i.contains(&rat(0)));
1079 assert!(!i.contains(&rat(6)));
1080 }
1081
1082 #[test]
1083 fn test_interval_open() {
1084 let i = Interval::open(rat(1), rat(5));
1085 assert!(!i.contains(&rat(1)));
1086 assert!(i.contains(&rat(3)));
1087 assert!(!i.contains(&rat(5)));
1088 }
1089
1090 #[test]
1091 fn test_interval_add() {
1092 let a = Interval::closed(rat(1), rat(3));
1093 let b = Interval::closed(rat(2), rat(4));
1094 let c = Interval::add(&a, &b);
1095 assert!(c.contains(&rat(3)));
1096 assert!(c.contains(&rat(7)));
1097 assert!(!c.contains(&rat(2)));
1098 assert!(!c.contains(&rat(8)));
1099 }
1100
1101 #[test]
1102 fn test_interval_mul() {
1103 let a = Interval::closed(rat(2), rat(3));
1104 let b = Interval::closed(rat(4), rat(5));
1105 let c = Interval::mul(&a, &b);
1106 assert!(c.contains(&rat(8)));
1108 assert!(c.contains(&rat(15)));
1109 assert!(!c.contains(&rat(7)));
1110 assert!(!c.contains(&rat(16)));
1111 }
1112
1113 #[test]
1114 fn test_interval_mul_mixed_signs() {
1115 let a = Interval::closed(rat(-2), rat(3));
1116 let b = Interval::closed(rat(-1), rat(2));
1117 let c = Interval::mul(&a, &b);
1118 assert!(c.contains(&rat(-4)));
1121 assert!(c.contains(&rat(6)));
1122 assert!(c.contains(&rat(0)));
1123 assert!(!c.contains(&rat(-5)));
1124 assert!(!c.contains(&rat(7)));
1125 }
1126
1127 #[test]
1128 fn test_interval_negate() {
1129 let a = Interval::closed(rat(1), rat(5));
1130 let b = a.negate();
1131 assert!(b.contains(&rat(-5)));
1132 assert!(b.contains(&rat(-1)));
1133 assert!(!b.contains(&rat(0)));
1134 }
1135
1136 #[test]
1137 fn test_interval_intersect() {
1138 let a = Interval::closed(rat(1), rat(5));
1139 let b = Interval::closed(rat(3), rat(7));
1140 let c = a.intersect(&b);
1141 assert!(c.contains(&rat(3)));
1143 assert!(c.contains(&rat(5)));
1144 assert!(!c.contains(&rat(2)));
1145 assert!(!c.contains(&rat(6)));
1146 }
1147
1148 #[test]
1149 fn test_interval_pow_even() {
1150 let a = Interval::closed(rat(-2), rat(3));
1151 let b = a.pow(2);
1152 assert!(b.contains(&rat(0)));
1154 assert!(b.contains(&rat(9)));
1155 assert!(!b.contains(&rat(-1)));
1156 assert!(!b.contains(&rat(10)));
1157 }
1158
1159 #[test]
1160 fn test_interval_pow_odd() {
1161 let a = Interval::closed(rat(-2), rat(3));
1162 let b = a.pow(3);
1163 assert!(b.contains(&rat(-8)));
1165 assert!(b.contains(&rat(27)));
1166 assert!(!b.contains(&rat(-9)));
1167 assert!(!b.contains(&rat(28)));
1168 }
1169
1170 #[test]
1171 fn test_interval_sign() {
1172 assert_eq!(Interval::closed(rat(1), rat(5)).sign(), Some(1));
1173 assert_eq!(Interval::closed(rat(-5), rat(-1)).sign(), Some(-1));
1174 assert_eq!(Interval::closed(rat(-1), rat(1)).sign(), None);
1175 assert_eq!(Interval::point(rat(0)).sign(), Some(0));
1176 }
1177
1178 #[test]
1179 fn test_interval_unbounded() {
1180 let a = Interval::at_least(rat(0));
1181 assert!(a.is_non_negative());
1182 assert!(a.contains(&rat(0)));
1183 assert!(a.contains(&rat(1000)));
1184 assert!(!a.contains(&rat(-1)));
1185
1186 let b = Interval::less_than(rat(0));
1187 assert!(b.is_negative());
1188 assert!(!b.contains(&rat(0)));
1189 assert!(b.contains(&rat(-1)));
1190 }
1191
1192 #[test]
1193 fn test_interval_midpoint() {
1194 let i = Interval::closed(rat(2), rat(8));
1195 assert_eq!(i.midpoint(), Some(rat(5)));
1196 }
1197
1198 #[test]
1199 fn test_interval_div() {
1200 let a = Interval::closed(rat(4), rat(12));
1202 let b = Interval::closed(rat(2), rat(3));
1203 let c = Interval::div(&a, &b).expect("Division should succeed");
1204 assert!(c.contains(&BigRational::new(BigInt::from(4), BigInt::from(3))));
1206 assert!(c.contains(&rat(6)));
1207 }
1208
1209 #[test]
1210 fn test_interval_div_by_zero() {
1211 let a = Interval::closed(rat(1), rat(2));
1213 let b = Interval::closed(rat(-1), rat(1)); assert!(Interval::div(&a, &b).is_none());
1215 }
1216
1217 #[test]
1218 fn test_interval_div_positive() {
1219 let a = Interval::closed(rat(6), rat(12));
1221 let b = Interval::closed(rat(2), rat(3));
1222 let c = Interval::div(&a, &b).expect("Division should succeed");
1223 assert!(c.contains(&rat(2)));
1224 assert!(c.contains(&rat(6)));
1225 }
1226
1227 #[test]
1228 fn test_interval_hull() {
1229 let a = Interval::closed(rat(1), rat(3));
1230 let b = Interval::closed(rat(5), rat(7));
1231 let hull = a.hull(&b);
1232 assert_eq!(hull.lo, Bound::Finite(rat(1)));
1234 assert_eq!(hull.hi, Bound::Finite(rat(7)));
1235 assert!(hull.contains(&rat(1)));
1236 assert!(hull.contains(&rat(4)));
1237 assert!(hull.contains(&rat(7)));
1238 }
1239
1240 #[test]
1241 fn test_interval_tighten_lower() {
1242 let i = Interval::closed(rat(1), rat(10));
1243 let tightened = i.tighten_lower(Bound::Finite(rat(5)), false);
1244 assert_eq!(tightened.lo, Bound::Finite(rat(5)));
1246 assert_eq!(tightened.hi, Bound::Finite(rat(10)));
1247 assert!(!tightened.contains(&rat(4)));
1248 assert!(tightened.contains(&rat(5)));
1249 }
1250
1251 #[test]
1252 fn test_interval_tighten_upper() {
1253 let i = Interval::closed(rat(1), rat(10));
1254 let tightened = i.tighten_upper(Bound::Finite(rat(6)), false);
1255 assert_eq!(tightened.lo, Bound::Finite(rat(1)));
1257 assert_eq!(tightened.hi, Bound::Finite(rat(6)));
1258 assert!(tightened.contains(&rat(6)));
1259 assert!(!tightened.contains(&rat(7)));
1260 }
1261
1262 #[test]
1263 fn test_interval_propagate_add() {
1264 let x = Interval::closed(rat(1), rat(2));
1267 let result = Interval::closed(rat(5), rat(8));
1268 let y = Interval::propagate_add(&x, &result);
1269 assert!(y.contains(&rat(3)));
1270 assert!(y.contains(&rat(7)));
1271 assert!(!y.contains(&rat(2)));
1272 assert!(!y.contains(&rat(8)));
1273 }
1274
1275 #[test]
1276 fn test_interval_propagate_sub() {
1277 let x = Interval::closed(rat(10), rat(15));
1280 let result = Interval::closed(rat(2), rat(5));
1281 let y = Interval::propagate_sub_left(&x, &result);
1282 assert!(y.contains(&rat(5)));
1283 assert!(y.contains(&rat(13)));
1284 }
1285
1286 #[test]
1287 fn test_interval_propagate_mul() {
1288 let x = Interval::closed(rat(2), rat(3));
1291 let result = Interval::closed(rat(10), rat(18));
1292 let y = Interval::propagate_mul(&x, &result).expect("Propagation should succeed");
1293 assert!(y.contains(&rat(5)));
1295 }
1296
1297 #[test]
1298 fn test_interval_is_subset_of() {
1299 let a = Interval::closed(rat(2), rat(5));
1300 let b = Interval::closed(rat(1), rat(10));
1301 assert!(a.is_subset_of(&b));
1302 assert!(!b.is_subset_of(&a));
1303
1304 let c = Interval::closed(rat(1), rat(10));
1305 assert!(c.is_subset_of(&c));
1306 }
1307
1308 #[test]
1309 fn test_interval_overlaps() {
1310 let a = Interval::closed(rat(1), rat(5));
1311 let b = Interval::closed(rat(3), rat(7));
1312 assert!(a.overlaps(&b));
1313 assert!(b.overlaps(&a));
1314
1315 let c = Interval::closed(rat(10), rat(15));
1316 assert!(!a.overlaps(&c));
1317 }
1318
1319 #[test]
1320 fn test_interval_widen() {
1321 let i = Interval::closed(rat(5), rat(10));
1322 let widened = i.widen(&rat(2));
1323 assert_eq!(widened.lo, Bound::Finite(rat(3)));
1325 assert_eq!(widened.hi, Bound::Finite(rat(12)));
1326 assert!(widened.contains(&rat(3)));
1327 assert!(widened.contains(&rat(12)));
1328 assert!(!widened.contains(&rat(2)));
1329 assert!(!widened.contains(&rat(13)));
1330 }
1331}