Skip to main content

rival/interval/
boolean.rs

1use super::value::{Endpoint, ErrorFlags, Ival};
2use rug::Assign;
3use std::cmp::Ordering;
4
5impl Ival {
6    /// Compute the interval logical NOT of `input`.
7    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    /// Compute the interval logical AND of `lhs` and `rhs`.
17    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    /// Compute the interval logical OR of `lhs` and `rhs`.
33    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    /// Compute the interval equality comparison `lhs == rhs`.
49    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    /// Compute the interval inequality comparison `lhs != rhs`.
66    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    /// Compute the interval less-than comparison `lhs < rhs`.
83    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    /// Compute the interval less-than-or-equal comparison `lhs <= rhs`.
90    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    /// Compute the interval greater-than comparison `lhs > rhs`.
101    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    /// Compute the interval greater-than-or-equal comparison `lhs >= rhs`.
108    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    /// Compute an interval conditional: if `cond` then `when_true` else `when_false`.
119    ///
120    /// Here, `cond` must be a boolean interval, while `when_true` and
121    /// `when_false` should be intervals of the same type, either both
122    /// real or both boolean. If `cond` is uncertain, the union of
123    /// `when_true` and `when_false` is returned.
124    ///
125    /// Note that typically, uses of `if_assign` are incomplete because
126    /// they are not flow-sensitive. For example, `if (x < 0) then (-x)
127    /// else x` is always non-negative, but both branches evaluate to
128    /// the same interval because the condition does not refine the value
129    /// of `x` in either branch.
130    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    /// Returns a boolean interval indicating whether any inputs
155    /// were discarded when computing `input`.
156    ///
157    /// These flags are "sticky": further computations on `input`
158    /// will maintain the already-set error flags.
159    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    /// Returns an illegal interval if `cond` is false, a legal interval
169    /// if `cond` is true, and a partially-legal one if `cond`'s truth
170    /// value is unknown. The value of the output interval is always true.
171    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    // Helper to assign endpoint based on comparison.
224    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}