Skip to main content

rival/interval/
core.rs

1//! Core interval operations.
2
3use super::value::{Endpoint, ErrorFlags, Ival, IvalClass, classify};
4use crate::mpfr::{
5    acosh_overflow_threshold, exp_overflow_threshold, exp2_overflow_threshold, inf, mpfr_abs,
6    mpfr_acos, mpfr_acosh, mpfr_asin, mpfr_asinh, mpfr_atan, mpfr_atan2, mpfr_atanh, mpfr_cbrt,
7    mpfr_ceil, mpfr_cmpabs, mpfr_cosh, mpfr_erf, mpfr_erfc, mpfr_exp, mpfr_exp2, mpfr_expm1,
8    mpfr_floor, mpfr_log, mpfr_log1p, mpfr_log2, mpfr_log10, mpfr_max, mpfr_min, mpfr_neg, mpfr_pi,
9    mpfr_rint, mpfr_round, mpfr_sign, mpfr_sinh, mpfr_sqrt, mpfr_tanh, mpfr_trunc,
10    sinh_overflow_threshold, zero,
11};
12use rug::{Assign, Float, float::Round, ops::NegAssign};
13
14impl Ival {
15    /// Lift a (weakly) monotonic MPFR function to a function on intervals.
16    ///
17    /// A weakly monotonic function is one where larger inputs produce
18    /// larger (or equal) outputs. Note that if a non-monotonic function
19    /// is passed, the results will not be sound.
20    pub fn monotonic_assign<F>(&mut self, mpfr_func: &F, a: &Ival)
21    where
22        F: Fn(&Float, &mut Float, Round) -> bool,
23    {
24        self.lo.immovable = endpoint_unary(mpfr_func, &a.lo, self.lo.as_float_mut(), Round::Down);
25        self.hi.immovable = endpoint_unary(mpfr_func, &a.hi, self.hi.as_float_mut(), Round::Up);
26        self.err = a.err;
27    }
28
29    /// Lift a (weakly) co-monotonic MPFR function to a function on intervals.
30    ///
31    /// A weakly co-monotonic function is one where larger inputs produce
32    /// smaller (or equal) outputs. Note that if a non-co-monotonic function
33    /// is passed, the results will not be sound.
34    pub fn comonotonic_assign<F>(&mut self, mpfr_func: &F, a: &Ival)
35    where
36        F: Fn(&Float, &mut Float, Round) -> bool,
37    {
38        self.lo.immovable = endpoint_unary(mpfr_func, &a.hi, self.lo.as_float_mut(), Round::Down);
39        self.hi.immovable = endpoint_unary(mpfr_func, &a.lo, self.hi.as_float_mut(), Round::Up);
40        self.err = a.err;
41    }
42
43    pub(crate) fn overflows_loose_at(&mut self, a: &Ival, lo: Float, hi: Float) {
44        let x_lo = a.lo.as_float();
45        let x_hi = a.hi.as_float();
46
47        self.lo.immovable = self.lo.immovable || x_hi <= &lo || (x_lo <= &lo && a.lo.immovable);
48        self.hi.immovable = self.hi.immovable || x_lo >= &hi || (x_hi >= &hi && a.hi.immovable);
49        self.err = a.err;
50    }
51
52    /// Compute the interval negation of `a`.
53    pub fn neg_assign(&mut self, a: &Ival) {
54        self.comonotonic_assign(&mpfr_neg, a);
55    }
56
57    pub(crate) fn exact_neg_assign(&mut self, a: &Ival) {
58        let prec = a.max_prec();
59        self.set_prec(prec);
60        self.neg_assign(a);
61    }
62
63    /// Compute the interval absolute value of `x`.
64    pub fn fabs_assign(&mut self, x: &Ival) {
65        match classify(x, false) {
66            IvalClass::Neg => self.comonotonic_assign(&mpfr_abs, x),
67            IvalClass::Pos => self.monotonic_assign(&mpfr_abs, x),
68            IvalClass::Mix => {
69                let prec = x.prec();
70                let mut tmp1 = zero(prec);
71                let mut tmp2 = zero(prec);
72
73                let abs_lo_imm = endpoint_unary(mpfr_abs, &x.lo, &mut tmp1, Round::Up);
74                let abs_hi_imm = endpoint_unary(mpfr_abs, &x.hi, &mut tmp2, Round::Up);
75
76                self.lo.as_float_mut().assign(0);
77                self.lo.immovable = x.lo.immovable && x.hi.immovable;
78
79                if tmp1 > tmp2 {
80                    self.hi.as_float_mut().assign(&tmp1);
81                    self.hi.immovable = abs_lo_imm;
82                } else {
83                    self.hi.as_float_mut().assign(&tmp2);
84                    self.hi.immovable = if tmp1 == tmp2 {
85                        abs_lo_imm || abs_hi_imm
86                    } else {
87                        abs_hi_imm
88                    };
89                }
90
91                self.err = x.err;
92            }
93        }
94    }
95
96    pub(crate) fn exact_fabs_assign(&mut self, a: &Ival) {
97        let prec = a.max_prec();
98        self.set_prec(prec);
99        self.fabs_assign(a);
100    }
101
102    pub(crate) fn pre_fabs_assign(&mut self, x: &Ival) {
103        match classify(x, false) {
104            IvalClass::Pos => {
105                self.assign_from(x);
106            }
107            IvalClass::Neg => {
108                self.lo.as_float_mut().assign(x.hi.as_float());
109                self.lo.immovable = x.hi.immovable;
110                self.hi.as_float_mut().assign(x.lo.as_float());
111                self.hi.immovable = x.lo.immovable;
112                self.err = x.err;
113            }
114            IvalClass::Mix => {
115                self.lo.as_float_mut().assign(0);
116                self.lo.immovable = x.lo.immovable && x.hi.immovable;
117
118                if mpfr_cmpabs(x.lo.as_float(), x.hi.as_float()) > 0 {
119                    self.hi.as_float_mut().assign(x.lo.as_float());
120                    self.hi.immovable = x.lo.immovable;
121                } else {
122                    self.hi.as_float_mut().assign(x.hi.as_float());
123                    self.hi.immovable = x.hi.immovable;
124                }
125                self.err = x.err;
126            }
127        }
128    }
129
130    /// Compute the interval square root of `a`.
131    pub fn sqrt_assign(&mut self, a: &Ival) {
132        // TODO: To get rid of all these clones when clamping, we can add inplace operators to mpfr.rs
133        let mut clamped = a.clone();
134        clamped.clamp(zero(self.prec()), inf(self.prec()));
135        self.monotonic_assign(&mpfr_sqrt, &clamped);
136    }
137
138    /// Compute the interval cube root of `a`.
139    pub fn cbrt_assign(&mut self, a: &Ival) {
140        self.monotonic_assign(&mpfr_cbrt, a);
141    }
142
143    /// Compute the interval exponential of `a`.
144    pub fn exp_assign(&mut self, a: &Ival) {
145        self.monotonic_assign(&mpfr_exp, a);
146        let thresh = exp_overflow_threshold(self.prec());
147        let mut neg = thresh.clone();
148        neg.neg_assign();
149        self.overflows_loose_at(a, neg, thresh);
150    }
151
152    /// Compute the interval base-2 exponential of `a`.
153    pub fn exp2_assign(&mut self, a: &Ival) {
154        self.monotonic_assign(&mpfr_exp2, a);
155        let thresh = exp2_overflow_threshold(self.prec());
156        let mut neg = thresh.clone();
157        neg.neg_assign();
158        self.overflows_loose_at(a, neg, thresh);
159    }
160
161    /// Compute the interval `exp(a) - 1`.
162    pub fn expm1_assign(&mut self, a: &Ival) {
163        self.monotonic_assign(&mpfr_expm1, a);
164        let thresh = exp_overflow_threshold(self.prec());
165        let mut neg = thresh.clone();
166        neg.neg_assign();
167        self.overflows_at(a, neg, thresh);
168    }
169
170    /// Compute the interval natural logarithm of `a`.
171    pub fn log_assign(&mut self, a: &Ival) {
172        let mut clamped = a.clone();
173        clamped.clamp_strict(zero(self.prec()), inf(self.prec()));
174        self.monotonic_assign(&mpfr_log, &clamped);
175    }
176
177    /// Compute the interval base-2 logarithm of `a`.
178    pub fn log2_assign(&mut self, a: &Ival) {
179        let mut clamped = a.clone();
180        clamped.clamp_strict(zero(self.prec()), inf(self.prec()));
181        self.monotonic_assign(&mpfr_log2, &clamped);
182    }
183
184    /// Compute the interval base-10 logarithm of `a`.
185    pub fn log10_assign(&mut self, a: &Ival) {
186        let mut clamped = a.clone();
187        clamped.clamp_strict(zero(self.prec()), inf(self.prec()));
188        self.monotonic_assign(&mpfr_log10, &clamped);
189    }
190
191    /// Compute the interval `log(1 + a)`.
192    pub fn log1p_assign(&mut self, a: &Ival) {
193        let mut clamped = a.clone();
194        let neg_one = Float::with_val(self.prec(), -1);
195        clamped.clamp_strict(neg_one, inf(self.prec()));
196        self.monotonic_assign(&mpfr_log1p, &clamped);
197    }
198
199    /// Compute the interval `logb` (exponent extraction) of `a`.
200    pub fn logb_assign(&mut self, a: &Ival) {
201        let mut abs_a = Ival::zero(a.max_prec());
202        abs_a.exact_fabs_assign(a);
203
204        let mut tmp = Ival::zero(self.prec());
205        tmp.log2_assign(&abs_a);
206        self.floor_assign(&tmp);
207    }
208
209    /// Compute the interval arcsine of `a`.
210    pub fn asin_assign(&mut self, a: &Ival) {
211        let mut clamped = a.clone();
212        let one = Float::with_val(self.prec(), 1);
213        let neg_one = Float::with_val(self.prec(), -1);
214        clamped.clamp(neg_one, one);
215        self.monotonic_assign(&mpfr_asin, &clamped);
216    }
217
218    /// Compute the interval arccosine of `a`.
219    pub fn acos_assign(&mut self, a: &Ival) {
220        let mut clamped = a.clone();
221        let one = Float::with_val(self.prec(), 1);
222        let neg_one = Float::with_val(self.prec(), -1);
223        clamped.clamp(neg_one, one);
224        self.comonotonic_assign(&mpfr_acos, &clamped);
225    }
226
227    /// Compute the interval arctangent of `a`.
228    pub fn atan_assign(&mut self, a: &Ival) {
229        self.monotonic_assign(&mpfr_atan, a);
230    }
231
232    /// Compute the interval two-argument arctangent `atan2(y, x)`.
233    pub fn atan2_assign(&mut self, y: &Ival, x: &Ival) {
234        let class_x = classify(x, true);
235        let class_y = classify(y, true);
236
237        let err = y.err.union(&x.err);
238        self.err = err;
239
240        let mut mkatan = |a: &Endpoint, b: &Endpoint, c: &Endpoint, d: &Endpoint| {
241            let prec = self.prec();
242            let mut lo_out = zero(prec);
243            let mut hi_out = zero(prec);
244
245            self.lo.immovable = endpoint_binary(mpfr_atan2, a, b, &mut lo_out, Round::Down);
246            self.hi.immovable = endpoint_binary(mpfr_atan2, c, d, &mut hi_out, Round::Up);
247            self.lo.as_float_mut().assign(&lo_out);
248            self.hi.as_float_mut().assign(&hi_out);
249            self.err = err;
250        };
251
252        match (class_x, class_y) {
253            (IvalClass::Neg, IvalClass::Neg) => mkatan(&y.hi, &x.lo, &y.lo, &x.hi),
254            (IvalClass::Mix, IvalClass::Neg) => mkatan(&y.hi, &x.lo, &y.hi, &x.hi),
255            (IvalClass::Pos, IvalClass::Neg) => mkatan(&y.lo, &x.lo, &y.hi, &x.hi),
256            (IvalClass::Pos, IvalClass::Mix) => mkatan(&y.lo, &x.lo, &y.hi, &x.lo),
257            (IvalClass::Pos, IvalClass::Pos) => mkatan(&y.lo, &x.hi, &y.hi, &x.lo),
258            (IvalClass::Mix, IvalClass::Pos) => mkatan(&y.lo, &x.hi, &y.lo, &x.lo),
259            (IvalClass::Neg, IvalClass::Pos) => mkatan(&y.hi, &x.hi, &y.lo, &x.lo),
260            (_, IvalClass::Mix) => {
261                let prec = self.prec();
262                let mut pi_lo = zero(prec);
263                let mut pi_hi = zero(prec);
264                mpfr_pi(&mut pi_lo, Round::Down);
265                mpfr_pi(&mut pi_hi, Round::Up);
266
267                let mut neg_pi = zero(prec);
268                neg_pi.assign(&pi_hi);
269                neg_pi.neg_assign();
270                self.lo.as_float_mut().assign(&neg_pi);
271                self.hi.as_float_mut().assign(&pi_hi);
272                self.lo.immovable = false;
273                self.hi.immovable = false;
274
275                let x_lo = x.lo.as_float();
276                let x_hi = x.hi.as_float();
277                let y_lo = y.lo.as_float();
278                let y_hi = y.hi.as_float();
279
280                self.err.partial = err.partial || x_hi >= &zero(prec);
281                self.err.total = err.total
282                    || (x_lo.is_zero() && x_hi.is_zero() && y_lo.is_zero() && y_hi.is_zero());
283            }
284        }
285    }
286
287    /// Compute the interval hyperbolic sine of `a`.
288    pub fn sinh_assign(&mut self, a: &Ival) {
289        self.monotonic_assign(&mpfr_sinh, a);
290        let thresh = sinh_overflow_threshold(self.prec());
291        let mut neg = thresh.clone();
292        neg.neg_assign();
293        self.overflows_at(a, neg, thresh);
294    }
295
296    /// Compute the interval hyperbolic cosine of `a`.
297    pub fn cosh_assign(&mut self, a: &Ival) {
298        let mut abs_a = Ival::zero(a.max_prec());
299        abs_a.exact_fabs_assign(a);
300        self.monotonic_assign(&mpfr_cosh, &abs_a);
301        let thresh = acosh_overflow_threshold(self.prec());
302        let mut neg = thresh.clone();
303        neg.neg_assign();
304        self.overflows_at(&abs_a, neg, thresh);
305    }
306
307    /// Compute the interval hyperbolic tangent of `a`.
308    pub fn tanh_assign(&mut self, a: &Ival) {
309        self.monotonic_assign(&mpfr_tanh, a);
310    }
311
312    /// Compute the interval inverse hyperbolic sine of `a`.
313    pub fn asinh_assign(&mut self, a: &Ival) {
314        self.monotonic_assign(&mpfr_asinh, a);
315    }
316
317    /// Compute the interval inverse hyperbolic cosine of `a`.
318    pub fn acosh_assign(&mut self, a: &Ival) {
319        let mut clamped = a.clone();
320        let one = Float::with_val(self.prec(), 1);
321        clamped.clamp(one, inf(self.prec()));
322        self.monotonic_assign(&mpfr_acosh, &clamped);
323    }
324
325    /// Compute the interval inverse hyperbolic tangent of `a`.
326    pub fn atanh_assign(&mut self, a: &Ival) {
327        let mut clamped = a.clone();
328        let one = Float::with_val(self.prec(), 1);
329        let neg_one = Float::with_val(self.prec(), -1);
330        clamped.clamp_strict(neg_one, one);
331        self.monotonic_assign(&mpfr_atanh, &clamped);
332    }
333
334    /// Compute the interval error function of `a`.
335    pub fn erf_assign(&mut self, a: &Ival) {
336        self.monotonic_assign(&mpfr_erf, a);
337    }
338
339    /// Compute the interval complementary error function of `a`.
340    pub fn erfc_assign(&mut self, a: &Ival) {
341        self.comonotonic_assign(&mpfr_erfc, a);
342    }
343
344    /// Compute the interval round-to-integer of `a`.
345    pub fn rint_assign(&mut self, a: &Ival) {
346        self.monotonic_assign(&mpfr_rint, a);
347    }
348
349    /// Compute the interval round-to-integer of `a`.
350    pub fn round_assign(&mut self, a: &Ival) {
351        self.monotonic_assign(&mpfr_round, a);
352    }
353
354    /// Compute the interval ceiling of `a`.
355    pub fn ceil_assign(&mut self, a: &Ival) {
356        self.monotonic_assign(&mpfr_ceil, a);
357    }
358
359    /// Compute the interval floor of `a`.
360    pub fn floor_assign(&mut self, a: &Ival) {
361        self.monotonic_assign(&mpfr_floor, a);
362    }
363
364    /// Compute the interval truncation of `a`.
365    pub fn trunc_assign(&mut self, a: &Ival) {
366        self.monotonic_assign(&mpfr_trunc, a);
367    }
368
369    /// Compute the interval minimum of `a` and `b`.
370    pub fn fmin_assign(&mut self, a: &Ival, b: &Ival) {
371        let lo_exact = mpfr_min(
372            a.lo.as_float(),
373            b.lo.as_float(),
374            self.lo.as_float_mut(),
375            Round::Down,
376        );
377        let hi_exact = mpfr_min(
378            a.hi.as_float(),
379            b.hi.as_float(),
380            self.hi.as_float_mut(),
381            Round::Up,
382        );
383
384        let lo_stable = if a.lo.immovable && b.lo.immovable {
385            true
386        } else if a.lo.immovable {
387            a.lo.as_float() <= b.lo.as_float()
388        } else if b.lo.immovable {
389            b.lo.as_float() <= a.lo.as_float()
390        } else {
391            false
392        };
393
394        self.lo.immovable = lo_exact && lo_stable;
395        self.hi.immovable = hi_exact && a.hi.immovable && b.hi.immovable;
396        self.err = a.err.union(&b.err);
397    }
398
399    /// Compute the interval maximum of `a` and `b`.
400    pub fn fmax_assign(&mut self, a: &Ival, b: &Ival) {
401        let lo_exact = mpfr_max(
402            a.lo.as_float(),
403            b.lo.as_float(),
404            self.lo.as_float_mut(),
405            Round::Down,
406        );
407        let hi_exact = mpfr_max(
408            a.hi.as_float(),
409            b.hi.as_float(),
410            self.hi.as_float_mut(),
411            Round::Up,
412        );
413
414        let hi_stable = if a.hi.immovable && b.hi.immovable {
415            true
416        } else if a.hi.immovable {
417            a.hi.as_float() >= b.hi.as_float()
418        } else if b.hi.immovable {
419            b.hi.as_float() >= a.hi.as_float()
420        } else {
421            false
422        };
423
424        self.lo.immovable = lo_exact && a.lo.immovable && b.lo.immovable;
425        self.hi.immovable = hi_exact && hi_stable;
426        self.err = a.err.union(&b.err);
427    }
428
429    /// Compute the interval `copysign(x, y)`.
430    pub fn copysign_assign(&mut self, x: &Ival, y: &Ival) {
431        let mut abs_x = Ival::zero(self.prec());
432        abs_x.fabs_assign(x);
433
434        let y_lo = y.lo.as_float();
435        let y_hi = y.hi.as_float();
436
437        let can_zero = y_lo.is_zero() || y_hi.is_zero();
438        let can_neg = mpfr_sign(y_lo) == -1 || can_zero;
439        let can_pos = mpfr_sign(y_hi) == 1 || can_zero;
440        let sign_immovable = can_neg && can_pos && y.lo.immovable && y.hi.immovable;
441
442        let err = y.err.union(&abs_x.err);
443
444        match (can_neg, can_pos) {
445            (true, true) => {
446                let prec = self.prec();
447                let mut neg_hi = zero(prec);
448                let imm_lo = endpoint_unary(mpfr_neg, &abs_x.hi, &mut neg_hi, Round::Down);
449
450                self.lo.as_float_mut().assign(&neg_hi);
451                self.lo.immovable = imm_lo;
452                self.hi.as_float_mut().assign(abs_x.hi.as_float());
453                self.hi.immovable = abs_x.hi.immovable;
454                self.err = err;
455                if !sign_immovable {
456                    self.lo.immovable = false;
457                    self.hi.immovable = false;
458                }
459            }
460            (true, false) => {
461                let prec = self.prec();
462                let mut neg_hi = zero(prec);
463                let mut neg_lo = zero(prec);
464                let imm_lo = endpoint_unary(mpfr_neg, &abs_x.hi, &mut neg_hi, Round::Down);
465                let imm_hi = endpoint_unary(mpfr_neg, &abs_x.lo, &mut neg_lo, Round::Up);
466
467                self.lo.as_float_mut().assign(&neg_hi);
468                self.lo.immovable = imm_lo;
469                self.hi.as_float_mut().assign(&neg_lo);
470                self.hi.immovable = imm_hi;
471                self.err = err;
472            }
473            (false, true) => {
474                self.assign_from(&abs_x);
475                self.err = err;
476            }
477            (false, false) => {
478                self.lo.as_float_mut().assign(f64::NAN);
479                self.hi.as_float_mut().assign(f64::NAN);
480                self.lo.immovable = true;
481                self.hi.immovable = true;
482                self.err = ErrorFlags::error();
483            }
484        }
485    }
486
487    fn overflows_at(&mut self, a: &Ival, lo: Float, hi: Float) {
488        let x_lo = a.lo.as_float();
489        let x_hi = a.hi.as_float();
490
491        self.lo.immovable = self.lo.immovable || (x_hi <= &lo && a.lo.immovable);
492        self.hi.immovable = self.hi.immovable || (x_lo >= &hi && a.hi.immovable);
493    }
494}
495
496#[must_use]
497pub(crate) fn endpoint_unary(
498    f: impl FnOnce(&Float, &mut Float, Round) -> bool,
499    ep: &Endpoint,
500    out: &mut Float,
501    rnd: Round,
502) -> bool {
503    let v = ep.as_float();
504    let exact = f(v, out, rnd);
505    ep.immovable && exact
506}
507
508#[must_use]
509pub(crate) fn endpoint_binary(
510    f: impl FnOnce(&Float, &Float, &mut Float, Round) -> bool,
511    ep1: &Endpoint,
512    ep2: &Endpoint,
513    out: &mut Float,
514    rnd: Round,
515) -> bool {
516    let v1 = ep1.as_float();
517    let v2 = ep2.as_float();
518    let exact = f(v1, v2, out, rnd);
519    (ep1.immovable && v1.is_infinite())
520        || (ep2.immovable && v2.is_infinite())
521        || (ep1.immovable && ep2.immovable && exact)
522}