1use 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 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 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 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 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 pub fn sqrt_assign(&mut self, a: &Ival) {
132 let mut clamped = a.clone();
134 clamped.clamp(zero(self.prec()), inf(self.prec()));
135 self.monotonic_assign(&mpfr_sqrt, &clamped);
136 }
137
138 pub fn cbrt_assign(&mut self, a: &Ival) {
140 self.monotonic_assign(&mpfr_cbrt, a);
141 }
142
143 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 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 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 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 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 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 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 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 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 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 pub fn atan_assign(&mut self, a: &Ival) {
229 self.monotonic_assign(&mpfr_atan, a);
230 }
231
232 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 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 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 pub fn tanh_assign(&mut self, a: &Ival) {
309 self.monotonic_assign(&mpfr_tanh, a);
310 }
311
312 pub fn asinh_assign(&mut self, a: &Ival) {
314 self.monotonic_assign(&mpfr_asinh, a);
315 }
316
317 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 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 pub fn erf_assign(&mut self, a: &Ival) {
336 self.monotonic_assign(&mpfr_erf, a);
337 }
338
339 pub fn erfc_assign(&mut self, a: &Ival) {
341 self.comonotonic_assign(&mpfr_erfc, a);
342 }
343
344 pub fn rint_assign(&mut self, a: &Ival) {
346 self.monotonic_assign(&mpfr_rint, a);
347 }
348
349 pub fn round_assign(&mut self, a: &Ival) {
351 self.monotonic_assign(&mpfr_round, a);
352 }
353
354 pub fn ceil_assign(&mut self, a: &Ival) {
356 self.monotonic_assign(&mpfr_ceil, a);
357 }
358
359 pub fn floor_assign(&mut self, a: &Ival) {
361 self.monotonic_assign(&mpfr_floor, a);
362 }
363
364 pub fn trunc_assign(&mut self, a: &Ival) {
366 self.monotonic_assign(&mpfr_trunc, a);
367 }
368
369 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 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 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}