1use super::value::{Endpoint, ErrorFlags, Ival};
2use rug::Assign;
3use std::cmp::Ordering;
4
5impl Ival {
6 pub fn not_assign(&mut self, input: &Ival) {
8 set_bool_result(
9 self,
10 BoolFlag::new(!endpoint_truth(&input.hi), input.hi.immovable),
11 BoolFlag::new(!endpoint_truth(&input.lo), input.lo.immovable),
12 );
13 self.err = input.err;
14 }
15
16 pub fn and_assign(&mut self, lhs: &Ival, rhs: &Ival) {
18 set_bool_result(
19 self,
20 BoolFlag::new(
21 endpoint_truth(&lhs.lo) && endpoint_truth(&rhs.lo),
22 lhs.lo.immovable && rhs.lo.immovable,
23 ),
24 BoolFlag::new(
25 endpoint_truth(&lhs.hi) && endpoint_truth(&rhs.hi),
26 lhs.hi.immovable && rhs.hi.immovable,
27 ),
28 );
29 self.err = lhs.err.union(&rhs.err);
30 }
31
32 pub fn or_assign(&mut self, lhs: &Ival, rhs: &Ival) {
34 set_bool_result(
35 self,
36 BoolFlag::new(
37 endpoint_truth(&lhs.lo) || endpoint_truth(&rhs.lo),
38 lhs.lo.immovable && rhs.lo.immovable,
39 ),
40 BoolFlag::new(
41 endpoint_truth(&lhs.hi) || endpoint_truth(&rhs.hi),
42 lhs.hi.immovable && rhs.hi.immovable,
43 ),
44 );
45 self.err = lhs.err.union(&rhs.err);
46 }
47
48 pub fn eq_assign(&mut self, lhs: &Ival, rhs: &Ival) {
50 let (can_lt, must_lt, can_gt, must_gt) = cmp_flags(lhs, rhs);
51 set_bool_result(
52 self,
53 BoolFlag::new(
54 !can_lt.value && !can_gt.value,
55 can_lt.immovable && can_gt.immovable,
56 ),
57 BoolFlag::new(
58 !must_lt.value && !must_gt.value,
59 must_lt.immovable && must_gt.immovable,
60 ),
61 );
62 self.err = lhs.err.union(&rhs.err);
63 }
64
65 pub fn ne_assign(&mut self, lhs: &Ival, rhs: &Ival) {
67 let (can_lt, must_lt, can_gt, must_gt) = cmp_flags(lhs, rhs);
68 set_bool_result(
69 self,
70 BoolFlag::new(
71 must_lt.value || must_gt.value,
72 must_lt.immovable && must_gt.immovable,
73 ),
74 BoolFlag::new(
75 can_lt.value || can_gt.value,
76 can_lt.immovable && can_gt.immovable,
77 ),
78 );
79 self.err = lhs.err.union(&rhs.err);
80 }
81
82 pub fn lt_assign(&mut self, lhs: &Ival, rhs: &Ival) {
84 let (can_lt, must_lt, _, _) = cmp_flags(lhs, rhs);
85 set_bool_result(self, must_lt, can_lt);
86 self.err = lhs.err.union(&rhs.err);
87 }
88
89 pub fn le_assign(&mut self, lhs: &Ival, rhs: &Ival) {
91 let (_, _, can_gt, must_gt) = cmp_flags(lhs, rhs);
92 set_bool_result(
93 self,
94 BoolFlag::new(!can_gt.value, can_gt.immovable),
95 BoolFlag::new(!must_gt.value, must_gt.immovable),
96 );
97 self.err = lhs.err.union(&rhs.err);
98 }
99
100 pub fn gt_assign(&mut self, lhs: &Ival, rhs: &Ival) {
102 let (_, _, can_gt, must_gt) = cmp_flags(lhs, rhs);
103 set_bool_result(self, must_gt, can_gt);
104 self.err = lhs.err.union(&rhs.err);
105 }
106
107 pub fn ge_assign(&mut self, lhs: &Ival, rhs: &Ival) {
109 let (can_lt, must_lt, _, _) = cmp_flags(lhs, rhs);
110 set_bool_result(
111 self,
112 BoolFlag::new(!can_lt.value, can_lt.immovable),
113 BoolFlag::new(!must_lt.value, must_lt.immovable),
114 );
115 self.err = lhs.err.union(&rhs.err);
116 }
117
118 pub fn if_assign(&mut self, cond: &Ival, when_true: &Ival, when_false: &Ival) {
131 let cond_lo_true = endpoint_truth(&cond.lo);
132 let cond_hi_true = endpoint_truth(&cond.hi);
133 let cond_err = cond.err;
134
135 let mut assign_branch = |branch: &Ival| {
136 self.assign_from(branch);
137 self.err = cond_err.union(&branch.err);
138 };
139
140 match (cond_lo_true, cond_hi_true) {
141 (true, _) => assign_branch(when_true),
142 (_, false) => assign_branch(when_false),
143 _ => {
144 assign_union(self, when_true, when_false);
145 self.err = cond_err.union(&self.err);
146 if !(cond.lo.immovable && cond.hi.immovable) {
147 self.lo.immovable = false;
148 self.hi.immovable = false;
149 }
150 }
151 }
152 }
153
154 pub fn error_assign(&mut self, input: &Ival) {
160 set_bool_result(
161 self,
162 BoolFlag::new(input.err.total, false),
163 BoolFlag::new(input.err.partial, false),
164 );
165 self.err = ErrorFlags::none();
166 }
167
168 pub fn assert_assign(&mut self, cond: &Ival) {
172 set_bool_result(self, BoolFlag::always_true(), BoolFlag::always_true());
173 self.err = ErrorFlags::new(
174 cond.err.partial || !endpoint_truth(&cond.lo),
175 cond.err.total || !endpoint_truth(&cond.hi),
176 );
177 }
178}
179
180#[derive(Clone, Copy)]
181struct BoolFlag {
182 value: bool,
183 immovable: bool,
184}
185
186impl BoolFlag {
187 fn new(value: bool, immovable: bool) -> Self {
188 BoolFlag { value, immovable }
189 }
190 fn always_true() -> Self {
191 BoolFlag::new(true, true)
192 }
193 fn assign(self, endpoint: &mut Endpoint) {
194 endpoint
195 .as_float_mut()
196 .assign(if self.value { 1 } else { 0 });
197 endpoint.immovable = self.immovable;
198 }
199}
200
201fn set_bool_result(out: &mut Ival, lo: BoolFlag, hi: BoolFlag) {
202 lo.assign(&mut out.lo);
203 hi.assign(&mut out.hi);
204}
205
206fn endpoint_truth(ep: &Endpoint) -> bool {
207 !ep.as_float().is_zero()
208}
209
210fn assign_union(out: &mut Ival, a: &Ival, b: &Ival) {
211 if a.err.total {
212 out.assign_from(b);
213 out.err.partial = true;
214 return;
215 }
216
217 if b.err.total {
218 out.assign_from(a);
219 out.err.partial = true;
220 return;
221 }
222
223 let assign_endpoint =
225 |out_ep: &mut Endpoint, a_ep: &Endpoint, b_ep: &Endpoint, prefer_smaller: bool| match a_ep
226 .val
227 .cmp(&b_ep.val)
228 {
229 Ordering::Equal => {
230 out_ep.as_float_mut().assign(a_ep.as_float());
231 out_ep.immovable = a_ep.immovable || b_ep.immovable;
232 }
233 Ordering::Less if prefer_smaller => {
234 out_ep.as_float_mut().assign(a_ep.as_float());
235 out_ep.immovable = a_ep.immovable;
236 }
237 Ordering::Greater if !prefer_smaller => {
238 out_ep.as_float_mut().assign(a_ep.as_float());
239 out_ep.immovable = a_ep.immovable;
240 }
241 _ => {
242 out_ep.as_float_mut().assign(b_ep.as_float());
243 out_ep.immovable = b_ep.immovable;
244 }
245 };
246
247 assign_endpoint(&mut out.lo, &a.lo, &b.lo, true);
248 assign_endpoint(&mut out.hi, &a.hi, &b.hi, false);
249
250 out.err = a.err.union_disjoint(&b.err);
251}
252
253fn cmp_flags(lhs: &Ival, rhs: &Ival) -> (BoolFlag, BoolFlag, BoolFlag, BoolFlag) {
254 let can_lt = BoolFlag::new(
255 lhs.lo.as_float() < rhs.hi.as_float(),
256 lhs.lo.immovable && rhs.hi.immovable,
257 );
258 let must_lt = BoolFlag::new(
259 lhs.hi.as_float() < rhs.lo.as_float(),
260 lhs.hi.immovable && rhs.lo.immovable,
261 );
262 let can_gt = BoolFlag::new(
263 lhs.hi.as_float() > rhs.lo.as_float(),
264 lhs.hi.immovable && rhs.lo.immovable,
265 );
266 let must_gt = BoolFlag::new(
267 lhs.lo.as_float() > rhs.hi.as_float(),
268 lhs.lo.immovable && rhs.hi.immovable,
269 );
270
271 (can_lt, must_lt, can_gt, must_gt)
272}