1use crate::context::truncation_order;
11use crate::da::Da;
12use crate::error::{codes, dace_panic};
13use crate::kernels::{multiply, weighted_sum};
14
15pub(crate) fn evaluate_series(a: &Da, xf: &[f64]) -> Da {
22 let nocut = truncation_order();
23 assert!(
24 xf.len() > nocut as usize,
25 "series coefficient table too short"
26 );
27
28 let mut inon = a.clone();
30 inon.set_coefficient0(0, 0.0);
31
32 crate::context::set_truncation_order(1);
33 let mut inc = weighted_sum(&inon, xf[nocut as usize], &inon, 0.0);
34 inc = add_double(&inc, xf[(nocut - 1) as usize]);
35
36 let mut i = nocut as i64 - 2;
37 while i >= 0 {
38 crate::context::set_truncation_order(nocut - i as u32);
39 inc = multiply(&inon, &inc);
40 inc = add_double(&inc, xf[i as usize]);
41 i -= 1;
42 }
43
44 crate::context::set_truncation_order(nocut);
45 inc
46}
47
48pub(crate) fn add_double(a: &Da, c: f64) -> Da {
51 let mut r = a.clone();
52 let cons = r.cons();
53 r.set_coefficient0(0, cons + c);
54 r
55}
56
57pub(crate) fn double_subtract(a: &Da, c: f64) -> Da {
59 let mut r = weighted_sum(a, -1.0, a, 0.0);
60 let cons = r.cons();
61 r.set_coefficient0(0, cons + c);
62 r
63}
64
65pub(crate) fn subtract_double(a: &Da, c: f64) -> Da {
67 add_double(a, -c)
68}
69
70pub(crate) fn divide_double(a: &Da, c: f64) -> Da {
76 if c == 0.0 {
77 dace_panic(codes::DIVIDING_BY_ZERO, "Dividing by zero");
78 }
79 weighted_sum(a, 1.0 / c, a, 0.0)
80}
81
82pub(crate) fn minv(a: &Da) -> Da {
88 let a0 = a.cons();
89 if a0 == 0.0 {
90 dace_panic(codes::DIVIDING_BY_ZERO, "Dividing by zero");
91 }
92
93 let nocut = truncation_order();
94 if nocut < 5 {
95 minv0(a, a0)
97 } else {
98 crate::context::set_truncation_order(2);
100 let mut inc = minv0(a, a0);
101 let mut ord: u32 = 3;
102 while ord <= nocut {
103 crate::context::set_truncation_order(nocut.min(2 * ord - 1));
104 let temp = multiply(a, &inc);
105 let temp = double_subtract(&temp, 2.0);
106 inc = multiply(&inc, &temp);
107 ord *= 2;
108 }
109 crate::context::set_truncation_order(nocut);
110 inc
111 }
112}
113
114fn minv0(a: &Da, a0: f64) -> Da {
117 let scaled = weighted_sum(a, 1.0 / a0, a, 0.0);
118 let nocut = truncation_order();
119 let mut xf = vec![0.0; nocut as usize + 1];
120 xf[0] = 1.0 / a0;
121 for i in 1..xf.len() {
122 xf[i] = -xf[i - 1];
123 }
124 evaluate_series(&scaled, &xf)
125}
126
127pub fn trunc(a: &Da) -> Da {
134 let mut r = a.clone();
135 let c = r.cons().round_ties_even();
136 r.set_coefficient0(0, c);
137 r
138}
139
140pub fn round(a: &Da) -> Da {
143 let mut r = a.clone();
144 let c = r.cons().round();
145 r.set_coefficient0(0, c);
146 r
147}
148
149pub fn modulo(a: &Da, p: f64) -> Da {
152 let mut r = a.clone();
153 let c = r.cons() % p;
154 r.set_coefficient0(0, c);
155 r
156}
157
158pub fn powf(a: &Da, p: f64) -> Da {
169 if p == 0.0 {
170 return Da::constant(1.0);
171 }
172 if p.fract() == 0.0 && p.abs() <= i32::MAX as f64 {
173 return powi(a, p as i32);
174 }
175
176 let a0 = a.cons();
177 if a0 <= 0.0 {
178 dace_panic(
179 codes::NON_INTEGER_POWER_NON_POSITIVE,
180 "Non-integer power of non-positive DA",
181 );
182 }
183
184 let nocut = truncation_order();
185 let mut xf = vec![0.0; nocut as usize + 1];
186 xf[0] = a0.powf(p);
187 for i in 1..xf.len() {
188 xf[i] = xf[i - 1] / i as f64 * (p - (i - 1) as f64);
189 }
190
191 let scaled = divide_double(a, a0);
192 evaluate_series(&scaled, &xf)
193}
194
195pub fn powi(a: &Da, np: i32) -> Da {
206 match np {
207 0 => Da::constant(1.0),
208 1 => a.clone(),
209 -1 => minv(a),
210 _ => {
211 let abs_np = np.unsigned_abs();
212 let mut result = match abs_np {
213 2 => a.sqr(),
214 3 => multiply(a, &a.sqr()),
215 4 => a.sqr().sqr(),
216 _ => {
217 let mut itemp = a.clone();
219 let mut inc = Da::constant(1.0);
220 let mut inp = abs_np;
221 while inp > 0 {
222 if inp & 1 != 0 {
223 inc = multiply(&inc, &itemp);
224 }
225 inp >>= 1;
226 if inp > 0 {
227 itemp = itemp.sqr();
228 }
229 }
230 inc
231 }
232 };
233 if np < 0 {
234 result = minv(&result);
235 }
236 result
237 }
238 }
239}
240
241pub fn root(a: &Da, np: i32) -> Da {
248 if np == 0 {
249 dace_panic(codes::ZERO_TH_ROOT, "Zero-th root does not exist");
250 }
251
252 let a0 = a.cons();
253 let iodd = np.unsigned_abs() & 1;
254 if iodd == 0 && a0 <= 0.0 {
255 dace_panic(codes::EVEN_ROOT_NEGATIVE, "Even root of negative DA");
256 } else if iodd == 1 && a0 == 0.0 {
257 dace_panic(codes::ODD_ROOT_ZERO, "Odd root of zero DA");
258 }
259
260 let nocut = truncation_order();
261 let mut xf = vec![0.0; nocut as usize + 1];
262 let mut cr = 1.0 / f64::from(np);
263 xf[0] = a0.abs().powf(cr).copysign(a0);
264 for i in 1..xf.len() {
265 xf[i] = xf[i - 1] / i as f64 * cr;
266 cr -= 1.0;
267 }
268
269 let scaled = divide_double(a, a0);
270 evaluate_series(&scaled, &xf)
271}
272
273pub fn sqrt(a: &Da) -> Da {
279 root(a, 2)
280}
281
282pub fn isrt(a: &Da) -> Da {
284 root(a, -2)
285}
286
287pub fn cbrt(a: &Da) -> Da {
289 root(a, 3)
290}
291
292pub fn icrt(a: &Da) -> Da {
294 root(a, -3)
295}
296
297pub fn hypot(a: &Da, b: &Da) -> Da {
299 Da::assert_same_context(a, b);
300 root(&(a.sqr() + b.sqr()), 2)
301}
302
303pub fn exp(a: &Da) -> Da {
309 let nocut = truncation_order();
310 let mut xf = vec![0.0; nocut as usize + 1];
311 xf[0] = a.cons().exp();
312 for i in 1..xf.len() {
313 xf[i] = xf[i - 1] / i as f64;
314 }
315 evaluate_series(a, &xf)
316}
317
318pub fn log(a: &Da) -> Da {
324 let a0 = a.cons();
325 if a0 <= 0.0 {
326 dace_panic(
327 codes::LOG_NON_POSITIVE,
328 "Negative constant part in logarithm",
329 );
330 }
331
332 let nocut = truncation_order();
333 let mut xf = vec![0.0; nocut as usize + 1];
334 let scaled = divide_double(a, a0);
335 xf[0] = a0.ln();
336 xf[1] = 1.0;
337 for i in 2..xf.len() {
338 xf[i] = -xf[i - 1] / i as f64 * (i - 1) as f64;
339 }
340 evaluate_series(&scaled, &xf)
341}
342
343pub fn log_base(a: &Da, b: f64) -> Da {
349 if b <= 0.0 {
350 dace_panic(
351 codes::LOG_BASE_POSITIVE,
352 "Base of logarithm must be positive",
353 );
354 }
355 let l = log(a);
356 weighted_sum(&l, 1.0 / b.ln(), &l, 0.0)
357}
358
359pub fn log10(a: &Da) -> Da {
361 log_base(a, 10.0)
362}
363
364pub fn log2(a: &Da) -> Da {
366 log_base(a, 2.0)
367}
368
369pub fn sin(a: &Da) -> Da {
375 let nocut = truncation_order();
376 let a0 = a.cons();
377 let mut xf = vec![0.0; nocut as usize + 1];
378 xf[0] = a0.sin();
379 xf[1] = a0.cos();
380 for i in 2..xf.len() {
381 xf[i] = -xf[i - 2] / (i * (i - 1)) as f64;
382 }
383 evaluate_series(a, &xf)
384}
385
386pub fn cos(a: &Da) -> Da {
388 let nocut = truncation_order();
389 let a0 = a.cons();
390 let mut xf = vec![0.0; nocut as usize + 1];
391 xf[0] = a0.cos();
392 xf[1] = -a0.sin();
393 for i in 2..xf.len() {
394 xf[i] = -xf[i - 2] / (i * (i - 1)) as f64;
395 }
396 evaluate_series(a, &xf)
397}
398
399pub fn tan(a: &Da) -> Da {
406 if a.cons().cos() == 0.0 {
407 dace_panic(codes::COS_ZERO_IN_TANGENT, "Cosine is zero in tangent");
408 }
409 let s = sin(a);
410 let c = cos(a);
411 divide_da(&s, &c)
412}
413
414pub fn asin(a: &Da) -> Da {
420 if a.cons().abs() >= 1.0 {
421 dace_panic(codes::OUT_OF_DOMAIN, "Out of domain");
422 }
423 let d = double_subtract(&a.sqr(), 1.0);
424 let d = sqrt(&d);
425 let q = divide_da(a, &d);
426 atan(&q)
427}
428
429pub fn acos(a: &Da) -> Da {
435 if a.cons().abs() >= 1.0 {
436 dace_panic(codes::OUT_OF_DOMAIN, "Out of domain");
437 }
438 let s = asin(a);
439 double_subtract(&s, std::f64::consts::FRAC_PI_2)
440}
441
442pub fn atan(a: &Da) -> Da {
445 let nocut = truncation_order();
446 let a0 = a.cons();
447 let mut xf = vec![0.0; nocut as usize + 1];
448
449 let iarg = {
450 let denom = add_double(&weighted_sum(a, a0, a, 0.0), 1.0);
451 let num = subtract_double(a, a0);
452 divide_da(&num, &denom)
453 };
454
455 let mut s = 1.0;
456 xf[0] = a0.atan();
457 let mut i = 1;
458 while i < xf.len() {
459 xf[i] = s / i as f64;
460 s = -s;
461 i += 2;
462 }
463 evaluate_series(&iarg, &xf)
464}
465
466pub fn atan2(y: &Da, x: &Da) -> Da {
469 Da::assert_same_context(y, x);
470 let cx = x.cons();
471 let cy = y.cons();
472
473 if cx == 0.0 && cy == 0.0 {
474 return Da::constant(0.0);
475 }
476 if cy.abs() > cx.abs() {
477 let t = atan(÷_da(x, y));
478 if cy < 0.0 {
479 double_subtract(&t, -std::f64::consts::FRAC_PI_2)
480 } else {
481 double_subtract(&t, std::f64::consts::FRAC_PI_2)
482 }
483 } else {
484 let t = atan(÷_da(y, x));
485 if cx < 0.0 {
486 if cy > 0.0 {
487 add_double(&t, std::f64::consts::PI)
488 } else {
489 add_double(&t, -std::f64::consts::PI)
490 }
491 } else {
492 t
493 }
494 }
495}
496
497pub(crate) fn divide_da(a: &Da, b: &Da) -> Da {
503 multiply(a, &minv(b))
504}
505
506pub fn sinh(a: &Da) -> Da {
512 let nocut = truncation_order();
513 let a0 = a.cons();
514 let mut xf = vec![0.0; nocut as usize + 1];
515 xf[0] = a0.sinh();
516 xf[1] = a0.cosh();
517 for i in 2..xf.len() {
518 xf[i] = xf[i - 2] / (i * (i - 1)) as f64;
519 }
520 evaluate_series(a, &xf)
521}
522
523pub fn cosh(a: &Da) -> Da {
525 let nocut = truncation_order();
526 let a0 = a.cons();
527 let mut xf = vec![0.0; nocut as usize + 1];
528 xf[0] = a0.cosh();
529 xf[1] = a0.sinh();
530 for i in 2..xf.len() {
531 xf[i] = xf[i - 2] / (i * (i - 1)) as f64;
532 }
533 evaluate_series(a, &xf)
534}
535
536pub fn tanh(a: &Da) -> Da {
539 let a0 = a.cons();
540 if a0 > 0.0 {
541 let t = exp(&weighted_sum(a, -2.0, a, 0.0));
542 let denom = add_double(&t, 1.0);
543 let num = double_subtract(&t, 1.0);
544 divide_da(&num, &denom)
545 } else {
546 let t = exp(&weighted_sum(a, 2.0, a, 0.0));
547 let denom = add_double(&t, 1.0);
548 let num = add_double(&t, -1.0);
549 divide_da(&num, &denom)
550 }
551}
552
553pub fn asinh(a: &Da) -> Da {
555 let s = sqrt(&add_double(&a.sqr(), 1.0));
556 log(&(a.clone() + s))
557}
558
559pub fn acosh(a: &Da) -> Da {
565 if a.cons() <= 1.0 {
566 dace_panic(codes::OUT_OF_DOMAIN, "Out of domain");
567 }
568 let s = sqrt(&subtract_double(&a.sqr(), 1.0));
569 log(&(a.clone() + s))
570}
571
572pub fn atanh(a: &Da) -> Da {
579 if a.cons().abs() >= 1.0 {
580 dace_panic(codes::OUT_OF_DOMAIN, "Out of domain");
581 }
582 let num = add_double(a, 1.0);
583 let den = double_subtract(a, 1.0);
584 let q = log(÷_da(&num, &den));
585 weighted_sum(&q, 0.5, &q, 0.0)
586}
587
588pub fn erf(a: &Da) -> Da {
595 let nocut = truncation_order();
596 let a0 = a.cons();
597 let mut xf = vec![0.0; nocut as usize + 1];
598 let mut factor = 2.0 * (-a0 * a0).exp() / std::f64::consts::PI.sqrt();
599 xf[0] = puruspe::erf(a0);
600 xf[1] = factor;
601 let mut hi2 = 1.0; let mut hi1 = 2.0 * a0; for (i, item) in xf.iter_mut().enumerate().skip(2) {
604 factor /= -(i as f64);
605 *item = factor * hi1;
606 let temp = 2.0 * a0 * hi1 - 2.0 * (i - 1) as f64 * hi2;
607 hi2 = hi1;
608 hi1 = temp;
609 }
610 evaluate_series(a, &xf)
611}
612
613pub fn erfc(a: &Da) -> Da {
615 let nocut = truncation_order();
616 let a0 = a.cons();
617 let mut xf = vec![0.0; nocut as usize + 1];
618 let mut factor = -2.0 * (-a0 * a0).exp() / std::f64::consts::PI.sqrt();
619 xf[0] = puruspe::erfc(a0);
620 xf[1] = factor;
621 let mut hi2 = 1.0;
622 let mut hi1 = 2.0 * a0;
623 for (i, item) in xf.iter_mut().enumerate().skip(2) {
624 factor /= -(i as f64);
625 *item = factor * hi1;
626 let temp = 2.0 * a0 * hi1 - 2.0 * (i - 1) as f64 * hi2;
627 hi2 = hi1;
628 hi1 = temp;
629 }
630 evaluate_series(a, &xf)
631}
632
633macro_rules! method_form {
638 ($($name:ident),* $(,)?) => {
639 $(
640 #[doc = concat!("Method form of [`", stringify!($name), "`](crate::elementary::", stringify!($name), ").")]
641 pub fn $name(&self) -> Da {
642 $name(self)
643 }
644 )*
645 };
646}
647
648impl Da {
649 method_form!(
650 exp, log, log10, log2, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh,
651 atanh, erf, erfc, sqrt, isrt, cbrt, icrt,
652 );
653
654 pub fn powf(&self, p: f64) -> Da {
656 powf(self, p)
657 }
658
659 pub fn powi(&self, np: i32) -> Da {
661 powi(self, np)
662 }
663
664 pub fn root(&self, np: i32) -> Da {
666 root(self, np)
667 }
668
669 pub fn log_base(&self, b: f64) -> Da {
671 log_base(self, b)
672 }
673
674 pub fn hypot(&self, b: &Da) -> Da {
676 hypot(self, b)
677 }
678
679 pub fn atan2(&self, x: &Da) -> Da {
681 atan2(self, x)
682 }
683
684 pub fn modulo(&self, p: f64) -> Da {
686 modulo(self, p)
687 }
688
689 pub fn trunc(&self) -> Da {
691 trunc(self)
692 }
693
694 pub fn round(&self) -> Da {
696 round(self)
697 }
698}
699
700#[cfg(test)]
701mod tests {
702 use super::*;
703 use crate::test_support::CONTEXT_LOCK;
704
705 #[test]
706 fn taylor_coefficients() {
707 let _g = CONTEXT_LOCK.lock();
708 crate::context::init(6, 1).unwrap();
709 let x = Da::variable(1);
710
711 let s = sin(&x);
713 assert!((s.get_coefficient(&[1]) - 1.0).abs() < 1e-15);
714 assert!((s.get_coefficient(&[3]) + 1.0 / 6.0).abs() < 1e-15);
715 assert!((s.get_coefficient(&[5]) - 1.0 / 120.0).abs() < 1e-15);
716 assert_eq!(s.size(), 3);
717
718 let e = exp(&x);
720 let mut fact = 1.0;
721 for k in 0..=6u32 {
722 if k > 0 {
723 fact *= f64::from(k);
724 }
725 assert!(
726 (e.get_coefficient(&[k]) - 1.0 / fact).abs() < 1e-15,
727 "exp coeff {k}"
728 );
729 }
730
731 let a = atan(&x);
733 assert!((a.get_coefficient(&[1]) - 1.0).abs() < 1e-15);
734 assert!((a.get_coefficient(&[3]) + 1.0 / 3.0).abs() < 1e-15);
735 assert!((a.get_coefficient(&[5]) - 1.0 / 5.0).abs() < 1e-15);
736
737 let u = 1.0 + x.clone();
739 let r = sqrt(&(u.clone() * u.clone()));
740 assert!((r.cons() - 1.0).abs() < 1e-12);
741 assert!((r.get_coefficient(&[1]) - 1.0).abs() < 1e-12);
742 let p3 = x.clone().powi(3);
743 assert!((p3.get_coefficient(&[3]) - 1.0).abs() < 1e-15);
744 assert_eq!(p3.size(), 1);
745 }
746
747 #[test]
748 fn identities_two_vars() {
749 let _g = CONTEXT_LOCK.lock();
750 crate::context::init(8, 2).unwrap();
751 let x = Da::variable(1);
752 let y = Da::variable(2);
753 let f = 0.7 + 1.3 * x.clone() - 0.4 * y.clone();
754
755 let s2 = sin(&f).sqr() + cos(&f).sqr();
757 assert!((s2.cons() - 1.0).abs() < 1e-12);
758 for m in s2.iter_monomials() {
759 if m.order() == 0 {
760 continue;
761 }
762 assert!(m.c.abs() < 1e-12, "sin^2+cos^2 residual at {:?}", m.jj);
763 }
764
765 let lf = log(&exp(&f));
767 for m in lf.iter_monomials() {
768 let expect = f.get_coefficient(&m.jj);
769 assert!(
770 (m.c - expect).abs() <= 1e-12 * expect.abs().max(1.0),
771 "log(exp) at {:?}: {} vs {}",
772 m.jj,
773 m.c,
774 expect
775 );
776 }
777
778 let th = tanh(&f);
780 let sh = sinh(&f);
781 let ch = cosh(&f);
782 let q = divide_da(&sh, &ch);
783 for (m1, m2) in th.iter_monomials().zip(q.iter_monomials()) {
784 assert_eq!(m1.jj, m2.jj);
785 assert!((m1.c - m2.c).abs() <= 1e-11 * m1.c.abs().max(1.0));
786 }
787
788 let g = 0.3 * x.clone();
790 let asg = asin(&g);
791 let d = asg.deriv(1);
792 let expect = weighted_sum(&isrt(&(1.0 - g.clone() * g.clone())), 0.3, &Da::new(), 0.0);
793 for (m1, m2) in d.iter_monomials().zip(expect.iter_monomials()) {
794 assert_eq!(m1.jj, m2.jj);
795 assert!((m1.c - m2.c).abs() <= 1e-11 * m1.c.abs().max(1.0));
796 }
797
798 let ang = 0.5 + 0.2 * x.clone();
800 let r = 1.2 + 0.3 * y.clone();
801 let yv = r.clone() * sin(&ang.clone());
802 let xv = r.clone() * cos(&ang.clone());
803 let a2 = atan2(&yv, &xv);
804 for m in a2.iter_monomials() {
805 let expect = ang.get_coefficient(&m.jj);
806 assert!(
807 (m.c - expect).abs() <= 1e-11 * expect.abs().max(1.0),
808 "atan2 at {:?}: {} vs {}",
809 m.jj,
810 m.c,
811 expect
812 );
813 }
814
815 let z = 0.0 + x.clone();
817 let ez = erf(&z);
818 assert_eq!(ez.cons(), 0.0);
819 assert!((ez.get_coefficient(&[1, 0]) - 2.0 / std::f64::consts::PI.sqrt()).abs() < 1e-14);
820 let ec = erfc(&z);
821 assert!((ec.cons() - 1.0).abs() < 1e-14);
822 assert!((ec.get_coefficient(&[1, 0]) + 2.0 / std::f64::consts::PI.sqrt()).abs() < 1e-14);
823
824 let w = 2.5 + x.clone();
828 assert!((w.trunc().cons() - 2.0).abs() < 1e-15);
829 assert!((w.round().cons() - 3.0).abs() < 1e-15);
830 assert!((w.modulo(2.0).cons() - 0.5).abs() < 1e-12);
831
832 assert!((log10(&(Da::constant(100.0))).cons() - 2.0).abs() < 1e-12);
834 assert!((log2(&(Da::constant(8.0))).cons() - 3.0).abs() < 1e-12);
835 assert!((log_base(&(Da::constant(8.0)), 2.0).cons() - 3.0).abs() < 1e-12);
836
837 assert!((cbrt(&(Da::constant(27.0))).cons() - 3.0).abs() < 1e-12);
839 assert!((isrt(&(Da::constant(4.0))).cons() - 0.5).abs() < 1e-12);
840 assert!((icrt(&(Da::constant(8.0))).cons() - 0.5).abs() < 1e-12);
841 assert!((hypot(&(Da::constant(3.0)), &Da::constant(4.0)).cons() - 5.0).abs() < 1e-12);
842 assert!((powf(&(Da::constant(2.0)), 10.0).cons() - 1024.0).abs() < 1e-9);
843
844 assert!((acosh(&(Da::constant(2.0))).cons() - 2.0f64.acosh()).abs() < 1e-12);
846 assert!((asinh(&(Da::constant(1.5))).cons() - 1.5f64.asinh()).abs() < 1e-12);
847 assert!((atanh(&(Da::constant(0.5))).cons() - 0.5f64.atanh()).abs() < 1e-12);
848 }
849
850 #[test]
851 fn domain_panics() {
852 let _g = CONTEXT_LOCK.lock();
853 crate::context::init(6, 2).unwrap();
854
855 fn expect_code(f: impl FnOnce() -> Da + std::panic::UnwindSafe, code: u32) {
856 let err = std::panic::catch_unwind(f).expect_err("must panic");
857 let e = err
858 .downcast_ref::<crate::error::DaceError>()
859 .expect("DaceError payload");
860 assert_eq!(e.code, code, "{}", e);
861 }
862
863 expect_code(|| log(&Da::constant(-1.0)), codes::LOG_NON_POSITIVE);
864 expect_code(|| log(&Da::constant(0.0)), codes::LOG_NON_POSITIVE);
865 expect_code(|| Da::constant(0.0).minv(), codes::DIVIDING_BY_ZERO);
869 expect_code(|| sqrt(&Da::constant(-4.0)), codes::EVEN_ROOT_NEGATIVE);
870 expect_code(|| root(&Da::constant(1.0), 0), codes::ZERO_TH_ROOT);
871 expect_code(|| root(&Da::constant(0.0), 3), codes::ODD_ROOT_ZERO);
872 expect_code(
873 || powf(&Da::constant(-2.0), 0.5),
874 codes::NON_INTEGER_POWER_NON_POSITIVE,
875 );
876 expect_code(|| asin(&Da::constant(1.0)), codes::OUT_OF_DOMAIN);
877 expect_code(|| acos(&Da::constant(-1.5)), codes::OUT_OF_DOMAIN);
878 expect_code(|| acosh(&Da::constant(1.0)), codes::OUT_OF_DOMAIN);
879 expect_code(|| atanh(&Da::constant(-1.0)), codes::OUT_OF_DOMAIN);
880 expect_code(
881 || log_base(&Da::constant(2.0), -1.0),
882 codes::LOG_BASE_POSITIVE,
883 );
884 expect_code(
885 || Da::constant(1.0) / Da::variable(1),
886 codes::DIVIDING_BY_ZERO,
887 );
888 }
889}