1use oxinum_core::OxiNumResult;
13use oxinum_float::native::{BigFloat, RoundingMode};
14
15use super::BigComplex;
16
17const GUARD: u32 = 10;
19
20#[inline]
22fn mul_i(z: BigComplex) -> BigComplex {
23 BigComplex {
24 re: -&z.im,
25 im: z.re,
26 }
27}
28
29#[inline]
31fn mul_neg_i(z: BigComplex) -> BigComplex {
32 BigComplex {
33 re: z.im,
34 im: -&z.re,
35 }
36}
37
38impl BigComplex {
39 pub fn asin(&self, prec: u32, mode: RoundingMode) -> OxiNumResult<BigComplex> {
47 if self.is_zero() {
48 return Ok(BigComplex::zero(prec));
49 }
50 let guard = prec.saturating_add(GUARD);
51
52 let iz = mul_i(self.clone());
54 let z_sq = self.mul_core(self);
56 let one_c = BigComplex::one(guard, mode);
58 let one_minus_z2 = &one_c - &z_sq;
59 let sqrt_val = one_minus_z2.sqrt(guard, mode)?;
61 let arg = &iz + &sqrt_val;
63 let ln_val = arg.ln(guard, mode)?;
65 Ok(mul_neg_i(ln_val))
67 }
68
69 pub fn acos(&self, prec: u32, mode: RoundingMode) -> OxiNumResult<BigComplex> {
77 let guard = prec.saturating_add(GUARD);
78
79 let z_sq = self.mul_core(self);
81 let one_c = BigComplex::one(guard, mode);
83 let one_minus_z2 = &one_c - &z_sq;
84 let sqrt_val = one_minus_z2.sqrt(guard, mode)?;
86 let i_sqrt = mul_i(sqrt_val);
88 let arg = self + &i_sqrt;
90 let ln_val = arg.ln(guard, mode)?;
92 Ok(mul_neg_i(ln_val))
94 }
95
96 pub fn atan(&self, prec: u32, mode: RoundingMode) -> OxiNumResult<BigComplex> {
104 if self.is_zero() {
105 return Ok(BigComplex::zero(prec));
106 }
107 let guard = prec.saturating_add(GUARD);
108 let half = BigFloat::from_f64(0.5, guard)?;
109
110 let iz = mul_i(self.clone());
112 let one_c = BigComplex::one(guard, mode);
113 let ln_minus = (&one_c - &iz).ln(guard, mode)?;
115 let ln_plus = (&one_c + &iz).ln(guard, mode)?;
117 let diff = &ln_minus - &ln_plus;
119 let i_diff = mul_i(diff);
121 let re = (&i_diff.re * &half).with_precision(prec, mode);
122 let im = (&i_diff.im * &half).with_precision(prec, mode);
123 Ok(BigComplex { re, im })
124 }
125
126 pub fn asinh(&self, prec: u32, mode: RoundingMode) -> OxiNumResult<BigComplex> {
134 if self.is_zero() {
135 return Ok(BigComplex::zero(prec));
136 }
137 let guard = prec.saturating_add(GUARD);
138
139 let one_c = BigComplex::one(guard, mode);
140 let z_sq_plus_one = &self.mul_core(self) + &one_c;
142 let sqrt_val = z_sq_plus_one.sqrt(guard, mode)?;
144 let arg = self + &sqrt_val;
146 arg.ln(guard, mode)
147 }
148
149 pub fn acosh(&self, prec: u32, mode: RoundingMode) -> OxiNumResult<BigComplex> {
158 let guard = prec.saturating_add(GUARD);
159
160 let one_c = BigComplex::one(guard, mode);
161 let sq1 = (self - &one_c).sqrt(guard, mode)?;
163 let sq2 = (self + &one_c).sqrt(guard, mode)?;
165 let product = sq1.mul_core(&sq2);
167 let arg = self + &product;
168 arg.ln(guard, mode)
169 }
170
171 pub fn atanh(&self, prec: u32, mode: RoundingMode) -> OxiNumResult<BigComplex> {
179 if self.is_zero() {
180 return Ok(BigComplex::zero(prec));
181 }
182 let guard = prec.saturating_add(GUARD);
183 let half = BigFloat::from_f64(0.5, guard)?;
184
185 let one_c = BigComplex::one(guard, mode);
186 let ln_plus = (&one_c + self).ln(guard, mode)?;
188 let ln_minus = (&one_c - self).ln(guard, mode)?;
190 let diff = &ln_plus - &ln_minus;
192 let re = (&diff.re * &half).with_precision(prec, mode);
194 let im = (&diff.im * &half).with_precision(prec, mode);
195 Ok(BigComplex { re, im })
196 }
197}
198
199#[cfg(test)]
204mod tests {
205 use super::*;
206
207 const PREC: u32 = 80;
208 const MODE: RoundingMode = RoundingMode::HalfEven;
209 const TOL: f64 = 1e-9;
210
211 fn c(re: f64, im: f64) -> BigComplex {
212 BigComplex::from_f64(re, im, PREC).expect("finite parts")
213 }
214
215 #[test]
218 fn asin_zero_is_zero() {
219 let r = BigComplex::zero(PREC).asin(PREC, MODE).expect("asin");
220 assert!(r.re().to_f64().abs() < TOL, "re = {}", r.re().to_f64());
221 assert!(r.im().to_f64().abs() < TOL, "im = {}", r.im().to_f64());
222 }
223
224 #[test]
225 fn atan_zero_is_zero() {
226 let r = BigComplex::zero(PREC).atan(PREC, MODE).expect("atan");
227 assert!(r.re().to_f64().abs() < TOL, "re = {}", r.re().to_f64());
228 assert!(r.im().to_f64().abs() < TOL, "im = {}", r.im().to_f64());
229 }
230
231 #[test]
232 fn asinh_zero_is_zero() {
233 let r = BigComplex::zero(PREC).asinh(PREC, MODE).expect("asinh");
234 assert!(r.re().to_f64().abs() < TOL, "re = {}", r.re().to_f64());
235 assert!(r.im().to_f64().abs() < TOL, "im = {}", r.im().to_f64());
236 }
237
238 #[test]
239 fn atanh_zero_is_zero() {
240 let r = BigComplex::zero(PREC).atanh(PREC, MODE).expect("atanh");
241 assert!(r.re().to_f64().abs() < TOL, "re = {}", r.re().to_f64());
242 assert!(r.im().to_f64().abs() < TOL, "im = {}", r.im().to_f64());
243 }
244
245 #[test]
248 fn asin_one_is_half_pi() {
249 let r = c(1.0, 0.0).asin(PREC, MODE).expect("asin");
251 let half_pi = std::f64::consts::FRAC_PI_2;
252 assert!(
253 (r.re().to_f64() - half_pi).abs() < TOL,
254 "re = {}, expected π/2 ≈ {half_pi}",
255 r.re().to_f64()
256 );
257 assert!(r.im().to_f64().abs() < TOL, "im = {}", r.im().to_f64());
258 }
259
260 #[test]
261 fn atan_one_is_quarter_pi() {
262 let r = c(1.0, 0.0).atan(PREC, MODE).expect("atan");
264 let quarter_pi = std::f64::consts::FRAC_PI_4;
265 assert!(
266 (r.re().to_f64() - quarter_pi).abs() < TOL,
267 "re = {}, expected π/4 ≈ {quarter_pi}",
268 r.re().to_f64()
269 );
270 assert!(r.im().to_f64().abs() < TOL, "im = {}", r.im().to_f64());
271 }
272
273 #[test]
274 fn acosh_one_is_zero() {
275 let r = c(1.0, 0.0).acosh(PREC, MODE).expect("acosh");
277 assert!(r.re().to_f64().abs() < TOL, "re = {}", r.re().to_f64());
278 assert!(r.im().to_f64().abs() < TOL, "im = {}", r.im().to_f64());
279 }
280
281 #[test]
284 fn sin_asin_roundtrip() {
285 let z = c(0.3, 0.4);
287 let asin_z = z.asin(PREC, MODE).expect("asin");
288 let r = asin_z.sin(PREC, MODE).expect("sin");
289 assert!(
290 (r.re().to_f64() - 0.3).abs() < TOL,
291 "re = {}",
292 r.re().to_f64()
293 );
294 assert!(
295 (r.im().to_f64() - 0.4).abs() < TOL,
296 "im = {}",
297 r.im().to_f64()
298 );
299 }
300
301 #[test]
302 fn cos_acos_roundtrip() {
303 let z = c(0.3, 0.4);
305 let acos_z = z.acos(PREC, MODE).expect("acos");
306 let r = acos_z.cos(PREC, MODE).expect("cos");
307 assert!(
308 (r.re().to_f64() - 0.3).abs() < TOL,
309 "re = {}",
310 r.re().to_f64()
311 );
312 assert!(
313 (r.im().to_f64() - 0.4).abs() < TOL,
314 "im = {}",
315 r.im().to_f64()
316 );
317 }
318
319 #[test]
320 fn tanh_atanh_roundtrip() {
321 let z = c(0.2, 0.1);
323 let atanh_z = z.atanh(PREC, MODE).expect("atanh");
324 let r = atanh_z.tanh(PREC, MODE).expect("tanh");
325 assert!(
326 (r.re().to_f64() - 0.2).abs() < TOL,
327 "re = {}",
328 r.re().to_f64()
329 );
330 assert!(
331 (r.im().to_f64() - 0.1).abs() < TOL,
332 "im = {}",
333 r.im().to_f64()
334 );
335 }
336
337 #[test]
338 fn sinh_asinh_roundtrip() {
339 let z = c(0.3, 0.4);
341 let asinh_z = z.asinh(PREC, MODE).expect("asinh");
342 let r = asinh_z.sinh(PREC, MODE).expect("sinh");
343 assert!(
344 (r.re().to_f64() - 0.3).abs() < TOL,
345 "re = {}",
346 r.re().to_f64()
347 );
348 assert!(
349 (r.im().to_f64() - 0.4).abs() < TOL,
350 "im = {}",
351 r.im().to_f64()
352 );
353 }
354}