woof_engine/interval/integer/
mul.rs1use std::ops::Mul;
2
3use itertools::{Itertools, MinMaxResult};
4use num::BigInt;
5
6use super::IntegerInterval;
7
8impl<'a> Mul<&'a IntegerInterval> for &'a IntegerInterval {
9 type Output = IntegerInterval;
10
11 fn mul(self, rhs: Self) -> Self::Output {
12 fn min_max_to_interval<T>(arr: T) -> IntegerInterval
13 where
14 T: IntoIterator<Item = BigInt>,
15 {
16 match arr.into_iter().minmax() {
17 MinMaxResult::NoElements => unreachable!(),
18 MinMaxResult::OneElement(x) => IntegerInterval::Single(x),
19 MinMaxResult::MinMax(start, end) => IntegerInterval::Bound { start, end },
20 }
21 }
22
23 use IntegerInterval::*;
24 match (self, rhs) {
25 (Empty, _) | (_, Empty) => Empty,
27
28 (Single(x), Single(y)) => Single(x * y),
30
31 (Single(x), Bound { start: c, end: d }) => {
33 min_max_to_interval([x * c, x * d])
34 }
35 (Bound { start: a, end: b }, Single(y)) => {
36 min_max_to_interval([a * y, b * y])
37 }
38 (Bound { start: a, end: b }, Bound { start: c, end: d }) => {
39 min_max_to_interval([a * c, a * d, b * c, b * d])
40 }
41
42 (Single(x), OpenRight { start: c }) => {
44 if x != &0.into() {
45 OpenRight { start: x * c }
46 } else {
47 Single(0.into())
48 }
49 },
50 (OpenRight { start: a }, Single(y)) => {
51 if y != &0.into() {
52 OpenRight { start: a * y }
53 } else {
54 Single(0.into())
55 }
56 },
57 (Bound { start: a, end: b }, OpenRight { start: c }) => {
58 let start = [a * c, b * c].into_iter().min().unwrap();
59 OpenRight { start }
60 }
61 (OpenRight { start: a }, Bound { start: c, end: d }) => {
62 let start = [a * c, a * d].into_iter().min().unwrap();
63 OpenRight { start }
64 }
65 (OpenRight { start: a }, OpenRight { start: c }) => {
66 OpenRight { start: a * c }
67 }
68
69 (Single(x), OpenLeft { end: d }) => OpenLeft { end: x * d },
71 (OpenLeft { end: b }, Single(y)) => OpenLeft { end: b * y },
72 (Bound { start: a, end: b }, OpenLeft { end: d }) => {
73 let end = [a * d, b * d].into_iter().max().unwrap();
74 OpenLeft { end }
75 }
76 (OpenLeft { end: b }, Bound { start: c, end: d }) => {
77 let end = [b * c, b * d].into_iter().max().unwrap();
78 OpenLeft { end }
79 }
80 (OpenLeft { end: b }, OpenLeft { end: d }) => OpenLeft { end: b * d },
81
82 (Open, _) | (_, Open) => Open,
85 (OpenRight { .. }, OpenLeft { .. }) => Open,
86 (OpenLeft { .. }, OpenRight { .. }) => Open,
87 }
88 .normalize()
89 }
90}
91
92#[test]
93fn mul_examples() {
94 }
96
97#[test]
98fn mul() {
99 use super::test::*;
100
101 fn do_mul(a: IntegerInterval, b: IntegerInterval) {
102 let c = &a * &b;
104
105 let assert_in_c = |x, y| {
106 use Integer::*;
107 match (x, y) {
108 (Finite(x), Finite(y)) => assert_in_interval(x * y, &c),
109 (Finite(_), NegativeInfinity) => assert_neg_infinity_in_interval(&c),
110 (Finite(_), PositiveInfinity) => assert_infinity_in_interval(&c),
111 (NegativeInfinity, Finite(_)) => assert_neg_infinity_in_interval(&c),
112 (PositiveInfinity, Finite(_)) => assert_infinity_in_interval(&c),
113 (NegativeInfinity, NegativeInfinity) => {
114 assert_neg_infinity_in_interval(&c)
115 }
116 (PositiveInfinity, PositiveInfinity) => assert_infinity_in_interval(&c),
117 (PositiveInfinity, NegativeInfinity) => {
118 assert_neg_infinity_in_interval(&c);
119 assert_infinity_in_interval(&c);
120 }
121 (NegativeInfinity, PositiveInfinity) => {
122 assert_neg_infinity_in_interval(&c);
123 assert_infinity_in_interval(&c);
124 }
125 }
126 };
127
128 if (matches!(&a, IntegerInterval::Empty) || matches!(&b, IntegerInterval::Empty))
129 {
130 assert!(matches!(c, IntegerInterval::Empty));
131 } else {
132 assert_in_c(
134 interval_into_integers(a.clone()).0,
135 interval_into_integers(b.clone()).0,
136 );
137
138 assert_in_c(
140 interval_into_integers(a.clone()).0,
141 interval_into_integers(b.clone()).1,
142 );
143
144 assert_in_c(
146 interval_into_integers(a.clone()).1,
147 interval_into_integers(b.clone()).0,
148 );
149
150 assert_in_c(
152 interval_into_integers(a.clone()).1,
153 interval_into_integers(b.clone()).1,
154 );
155 }
156 }
157
158 proptest!(|(a in interval_strategy(), b in interval_strategy())| {
159 do_mul(a, b)
160 });
161
162 proptest!(|(a in interval_strategy(), b in interval_strategy())| {
163 let c = &(&a * &b) / &b;
164 if matches!(a, IntegerInterval::Empty) || matches!(b, IntegerInterval::Empty) {
165 assert_eq!(c, IntegerInterval::Empty)
166 } else if matches!(b, IntegerInterval::Open) {
167 assert_eq!(c, IntegerInterval::Single(0.into()))
168 } else {
169 assert_eq!(c, a);
170 }
171 });
172}