oxinum_complex/
inverse_trig.rs1use core::str::FromStr;
25
26use crate::{CBig, DBig, OxiNumError, OxiNumResult};
27
28const GUARD: usize = 10;
30
31fn make_dbig(s: &str) -> OxiNumResult<DBig> {
34 DBig::from_str(s).map_err(|e| OxiNumError::Parse(format!("{e}").into()))
35}
36
37#[inline]
39fn mul_i(z: CBig) -> CBig {
40 CBig::from_parts(-z.im, z.re)
41}
42
43#[inline]
45fn mul_neg_i(z: CBig) -> CBig {
46 CBig::from_parts(z.im, -z.re)
47}
48
49impl CBig {
50 pub fn asin(&self, precision: usize) -> OxiNumResult<CBig> {
58 if self.is_zero() {
59 return Ok(CBig::zero());
60 }
61 let guard = precision.saturating_add(GUARD);
62
63 let iz = mul_i(self.clone());
65 let z_sq = self * self;
67 let one_minus_z2 = &CBig::one() - &z_sq;
69 let sqrt_val = one_minus_z2.sqrt(guard)?;
71 let arg = &iz + &sqrt_val;
73 let ln_val = arg.ln(guard)?;
75 Ok(mul_neg_i(ln_val))
77 }
78
79 pub fn acos(&self, precision: usize) -> OxiNumResult<CBig> {
87 let guard = precision.saturating_add(GUARD);
88
89 let z_sq = self * self;
91 let one_minus_z2 = &CBig::one() - &z_sq;
93 let sqrt_val = one_minus_z2.sqrt(guard)?;
95 let i_sqrt = mul_i(sqrt_val);
97 let arg = self + &i_sqrt;
99 let ln_val = arg.ln(guard)?;
101 Ok(mul_neg_i(ln_val))
103 }
104
105 pub fn atan(&self, precision: usize) -> OxiNumResult<CBig> {
113 if self.is_zero() {
114 return Ok(CBig::zero());
115 }
116 let guard = precision.saturating_add(GUARD);
117 let half = make_dbig("0.5")?;
118
119 let iz = mul_i(self.clone());
121 let one = CBig::one();
122 let ln_minus = (&one - &iz).ln(guard)?;
124 let ln_plus = (&one + &iz).ln(guard)?;
126 let diff = &ln_minus - &ln_plus;
128 let i_diff = mul_i(diff);
130 let re = &i_diff.re * ½
131 let im = &i_diff.im * ½
132 Ok(CBig::from_parts(re, im))
133 }
134
135 pub fn asinh(&self, precision: usize) -> OxiNumResult<CBig> {
143 if self.is_zero() {
144 return Ok(CBig::zero());
145 }
146 let guard = precision.saturating_add(GUARD);
147
148 let z_sq_plus_one = &(self * self) + &CBig::one();
150 let sqrt_val = z_sq_plus_one.sqrt(guard)?;
152 let arg = self + &sqrt_val;
154 arg.ln(guard)
155 }
156
157 pub fn acosh(&self, precision: usize) -> OxiNumResult<CBig> {
166 let guard = precision.saturating_add(GUARD);
167
168 let one = CBig::one();
169 let sq1 = (self - &one).sqrt(guard)?;
171 let sq2 = (self + &one).sqrt(guard)?;
173 let product = &sq1 * &sq2;
175 let arg = self + &product;
176 arg.ln(guard)
177 }
178
179 pub fn atanh(&self, precision: usize) -> OxiNumResult<CBig> {
187 if self.is_zero() {
188 return Ok(CBig::zero());
189 }
190 let guard = precision.saturating_add(GUARD);
191 let half = make_dbig("0.5")?;
192
193 let one = CBig::one();
194 let ln_plus = (&one + self).ln(guard)?;
196 let ln_minus = (&one - self).ln(guard)?;
198 let diff = &ln_plus - &ln_minus;
200 let re = &diff.re * ½
202 let im = &diff.im * ½
203 Ok(CBig::from_parts(re, im))
204 }
205}
206
207#[cfg(test)]
212mod tests {
213 use super::*;
214 use oxinum_float::compute_pi;
215
216 const PREC: usize = 40;
217 const TOL: f64 = 1e-9;
218
219 fn c(re: f64, im: f64) -> CBig {
220 CBig::from_f64(re, im).expect("finite parts")
221 }
222
223 fn pi() -> DBig {
224 compute_pi(PREC + 10)
225 }
226
227 #[test]
230 fn asin_zero_is_zero() {
231 let r = CBig::zero().asin(PREC).expect("asin");
232 let (re, im) = r.to_f64_parts();
233 assert!(re.abs() < TOL, "re = {re}");
234 assert!(im.abs() < TOL, "im = {im}");
235 }
236
237 #[test]
238 fn atan_zero_is_zero() {
239 let r = CBig::zero().atan(PREC).expect("atan");
240 let (re, im) = r.to_f64_parts();
241 assert!(re.abs() < TOL, "re = {re}");
242 assert!(im.abs() < TOL, "im = {im}");
243 }
244
245 #[test]
246 fn asinh_zero_is_zero() {
247 let r = CBig::zero().asinh(PREC).expect("asinh");
248 let (re, im) = r.to_f64_parts();
249 assert!(re.abs() < TOL, "re = {re}");
250 assert!(im.abs() < TOL, "im = {im}");
251 }
252
253 #[test]
254 fn atanh_zero_is_zero() {
255 let r = CBig::zero().atanh(PREC).expect("atanh");
256 let (re, im) = r.to_f64_parts();
257 assert!(re.abs() < TOL, "re = {re}");
258 assert!(im.abs() < TOL, "im = {im}");
259 }
260
261 #[test]
264 fn asin_one_is_half_pi() {
265 let r = CBig::from_real(DBig::from(1u32)).asin(PREC).expect("asin");
267 let (re, im) = r.to_f64_parts();
268 let half_pi = pi().to_f64().value() / 2.0;
269 assert!(
270 (re - half_pi).abs() < TOL,
271 "re = {re}, expected π/2 ≈ {half_pi}"
272 );
273 assert!(im.abs() < TOL, "im = {im}");
274 }
275
276 #[test]
277 fn atan_one_is_quarter_pi() {
278 let r = CBig::from_real(DBig::from(1u32)).atan(PREC).expect("atan");
280 let (re, im) = r.to_f64_parts();
281 let quarter_pi = pi().to_f64().value() / 4.0;
282 assert!(
283 (re - quarter_pi).abs() < TOL,
284 "re = {re}, expected π/4 ≈ {quarter_pi}"
285 );
286 assert!(im.abs() < TOL, "im = {im}");
287 }
288
289 #[test]
290 fn acosh_one_is_zero() {
291 let r = CBig::from_real(DBig::from(1u32))
293 .acosh(PREC)
294 .expect("acosh");
295 let (re, im) = r.to_f64_parts();
296 assert!(re.abs() < TOL, "re = {re}");
297 assert!(im.abs() < TOL, "im = {im}");
298 }
299
300 #[test]
303 fn sin_asin_roundtrip() {
304 let z = c(0.3, 0.4);
306 let asin_z = z.asin(PREC).expect("asin");
307 let r = asin_z.sin(PREC).expect("sin");
308 let (re, im) = r.to_f64_parts();
309 assert!((re - 0.3).abs() < TOL, "re = {re}");
310 assert!((im - 0.4).abs() < TOL, "im = {im}");
311 }
312
313 #[test]
314 fn cos_acos_roundtrip() {
315 let z = c(0.3, 0.4);
317 let acos_z = z.acos(PREC).expect("acos");
318 let r = acos_z.cos(PREC).expect("cos");
319 let (re, im) = r.to_f64_parts();
320 assert!((re - 0.3).abs() < TOL, "re = {re}");
321 assert!((im - 0.4).abs() < TOL, "im = {im}");
322 }
323
324 #[test]
325 fn tanh_atanh_roundtrip() {
326 let z = c(0.2, 0.1);
328 let atanh_z = z.atanh(PREC).expect("atanh");
329 let r = atanh_z.tanh(PREC).expect("tanh");
330 let (re, im) = r.to_f64_parts();
331 assert!((re - 0.2).abs() < TOL, "re = {re}");
332 assert!((im - 0.1).abs() < TOL, "im = {im}");
333 }
334
335 #[test]
336 fn sinh_asinh_roundtrip() {
337 let z = c(0.3, 0.4);
339 let asinh_z = z.asinh(PREC).expect("asinh");
340 let r = asinh_z.sinh(PREC).expect("sinh");
341 let (re, im) = r.to_f64_parts();
342 assert!((re - 0.3).abs() < TOL, "re = {re}");
343 assert!((im - 0.4).abs() < TOL, "im = {im}");
344 }
345}