1use crate::cbig::CBig;
11use crate::repr::{combine_parts, exact, reborrow_cache, riemann, CfpResult, Context};
12use dashu_base::Approximation::*;
13use dashu_base::{BitTest, Sign};
14use dashu_float::round::Round;
15use dashu_float::{ConstCache, FBig, FpError};
16use dashu_int::{IBig, Word};
17
18const EXP_GUARD: usize = 14;
20
21const POWF_GUARD: usize = 22;
24
25impl<R: Round> Context<R> {
26 pub fn exp<const B: Word>(
33 &self,
34 z: &CBig<R, B>,
35 mut cache: Option<&mut ConstCache>,
36 ) -> CfpResult<R, B> {
37 if z.is_zero() {
38 return Ok(exact(FBig::ONE, FBig::ZERO));
39 }
40 if z.is_infinite() {
41 if z.im().is_infinite() {
42 return Err(FpError::Indeterminate); }
44 return if z.re().sign() == Sign::Positive {
45 Ok(riemann(*self))
46 } else {
47 Ok(exact(FBig::ZERO, FBig::ZERO))
48 };
49 }
50
51 let gctx = self.guard(EXP_GUARD);
52 let p = self.precision();
53 let ex = gctx.exp(z.re(), reborrow_cache(&mut cache))?.value();
54 let (sin_y, cos_y) = gctx.sin_cos(z.im(), reborrow_cache(&mut cache));
55 let cos_y = cos_y?.value();
56 let sin_y = sin_y?.value();
57 let re = gctx.mul(ex.repr(), cos_y.repr())?.value().with_precision(p);
58 let im = gctx.mul(ex.repr(), sin_y.repr())?.value().with_precision(p);
59 Ok(combine_parts(re, im))
60 }
61
62 pub fn powi<const B: Word>(&self, z: &CBig<R, B>, exp: IBig) -> CfpResult<R, B> {
67 let (sign, n) = exp.into_parts();
68 if n.is_zero() {
69 return Ok(Exact(CBig::ONE));
70 }
71 let negative = sign == Sign::Negative;
72 let bitlen = n.bit_len();
73 let mut acc = z.clone();
75 for i in (0..bitlen - 1).rev() {
76 acc = self.sqr(&acc)?.value();
77 if n.bit(i) {
78 acc = self.mul(&acc, z)?.value();
79 }
80 }
81 if negative {
84 self.inv(&acc)
85 } else {
86 Ok(Exact(acc))
87 }
88 }
89
90 pub fn powf<const B: Word>(
98 &self,
99 base: &CBig<R, B>,
100 w: &CBig<R, B>,
101 mut cache: Option<&mut ConstCache>,
102 ) -> CfpResult<R, B> {
103 if w.is_zero() {
104 return Ok(Exact(CBig::ONE)); }
106 let gctx = Context::new(self.precision() + POWF_GUARD);
107 let log_z = gctx.log(base, reborrow_cache(&mut cache))?.value();
108 let wlogz = gctx.mul(w, &log_z)?.value();
109 let hi = gctx.exp(&wlogz, reborrow_cache(&mut cache))?.value();
110 let p = self.precision();
111 let (re, im) = hi.into_parts();
112 Ok(combine_parts(re.with_precision(p), im.with_precision(p)))
113 }
114}
115
116impl<R: Round, const B: Word> CBig<R, B> {
117 #[inline]
123 pub fn exp(&self) -> Self {
124 self.context().unwrap_cfp(self.context().exp(self, None))
125 }
126
127 #[inline]
133 pub fn powi(&self, exp: IBig) -> Self {
134 self.context().unwrap_cfp(self.context().powi(self, exp))
135 }
136
137 #[inline]
142 pub fn powf(&self, w: &Self) -> Self {
143 self.context()
144 .unwrap_cfp(self.context().powf(self, w, None))
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use dashu_float::round::mode;
152
153 type C = CBig<mode::HalfAway, 10>;
154 type F = FBig<mode::HalfAway, 10>;
155
156 fn c(re: i32, im: i32) -> C {
157 let mk = |v: i32| -> F { F::from(v).with_precision(53).value() };
158 CBig::from_parts(mk(re), mk(im))
159 }
160
161 #[test]
162 fn exp_zero_is_one() {
163 assert!(C::ZERO.exp() == C::ONE);
164 }
165
166 #[test]
167 fn exp_one_is_e() {
168 let e = C::ONE.exp();
170 let (re, _im) = e.into_parts();
171 assert!(re > F::from(2));
172 assert!(re < F::from(3));
173 }
174
175 #[test]
176 fn exp_pi_i_is_neg_one() {
177 use dashu_base::{Abs, AbsOrd};
178 let pi = F::from_parts(31415926535897932i64.into(), -16)
180 .with_precision(60)
181 .value();
182 let z = CBig::from_parts(F::ZERO, pi);
183 let (re, im) = z.exp().into_parts();
184 let re_err = (re + F::ONE).abs();
185 let tol = F::from_parts(1.into(), -12);
186 assert!(re_err.abs_cmp(&tol).is_le());
187 assert!(im.abs_cmp(&tol).is_le());
188 }
189
190 #[test]
191 fn exp_pos_infinity_is_riemann() {
192 let inf = CBig::from(F::INFINITY);
193 let r = inf.exp();
194 assert!(r.re().is_infinite());
195 assert!(r.im().is_pos_zero());
196 }
197
198 #[test]
199 fn powi_zero_is_one() {
200 assert!(c(3, 4).powi(0.into()) == C::ONE);
201 }
202
203 #[test]
204 fn powi_one_is_self() {
205 let z = c(3, 4);
206 assert!(z.powi(1.into()) == z);
207 }
208
209 #[test]
210 fn powi_two_is_sqr() {
211 let z = c(1, 2);
212 assert!(z.powi(2.into()) == z.sqr());
213 }
214
215 #[test]
216 fn powi_negative_is_inv() {
217 let z = c(3, 4);
219 let r = z.powi((-1).into());
220 let one = &z * &r;
221 assert!(one == C::ONE);
222 }
223
224 #[test]
225 fn powf_zero_exponent_is_one() {
226 assert!(c(3, 4).powf(&C::ZERO) == C::ONE);
228 assert!(C::ZERO.powf(&C::ZERO) == C::ONE);
229 }
230
231 #[test]
232 fn powf_one_exponent_is_self() {
233 let z = c(2, 1);
234 assert!(z.powf(&C::ONE) == z);
235 }
236}