1use crate::{
16 error::{assert_limited_precision, FpError},
17 fbig::FBig,
18 math::{
19 cache::{reborrow_cache, ConstCache},
20 FpResult,
21 },
22 repr::{Context, Repr, Word},
23 round::Round,
24};
25use dashu_base::{Abs, AbsOrd, Approximation::Exact, Sign};
26
27impl<R: Round> Context<R> {
28 pub fn sinh<const B: Word>(
30 &self,
31 x: &Repr<B>,
32 mut cache: Option<&mut ConstCache>,
33 ) -> FpResult<FBig<R, B>> {
34 if x.is_infinite() {
35 return Ok(Exact(FBig::new(Repr::infinity_with_sign(x.sign()), *self)));
36 }
37 assert_limited_precision(self.precision);
38 if x.significand.is_zero() {
39 return Ok(Exact(FBig::new(signed_zero_repr(x), *self)));
41 }
42
43 let work = Context::<R>::new(self.precision + 50);
45 let x_f = FBig::<R, B>::new(work.repr_round_ref(x).value(), work);
46 let neg_x = -x_f.clone();
47 let ep = work.exp_m1(&x_f.repr, reborrow_cache(&mut cache));
48 let em = work.exp_m1(&neg_x.repr, reborrow_cache(&mut cache));
49 match (ep, em) {
50 (Ok(ep), Ok(em)) => {
51 Ok(((ep.value() - em.value()) / 2i32).with_precision(self.precision))
52 }
53 _ => Err(FpError::Overflow(x.sign())),
55 }
56 }
57
58 pub fn cosh<const B: Word>(
60 &self,
61 x: &Repr<B>,
62 mut cache: Option<&mut ConstCache>,
63 ) -> FpResult<FBig<R, B>> {
64 if x.is_infinite() {
65 return Ok(Exact(FBig::new(Repr::infinity(), *self)));
67 }
68 assert_limited_precision(self.precision);
69 if x.significand.is_zero() {
70 return Ok(Exact(FBig::new(Repr::one(), *self)));
72 }
73
74 let work = Context::<R>::new(self.precision + 50);
76 let x_f = FBig::<R, B>::new(work.repr_round_ref(x).value(), work);
77 let neg_x = -x_f.clone();
78 let ep = work.exp_m1(&x_f.repr, reborrow_cache(&mut cache));
79 let em = work.exp_m1(&neg_x.repr, reborrow_cache(&mut cache));
80 match (ep, em) {
81 (Ok(ep), Ok(em)) => Ok(((ep.value() + em.value()) / 2i32 + FBig::<R, B>::ONE)
82 .with_precision(self.precision)),
83 _ => Err(FpError::Overflow(Sign::Positive)),
85 }
86 }
87
88 pub fn sinh_cosh<const B: Word>(
94 &self,
95 x: &Repr<B>,
96 mut cache: Option<&mut ConstCache>,
97 ) -> (FpResult<FBig<R, B>>, FpResult<FBig<R, B>>) {
98 if x.is_infinite() {
99 return (
100 Ok(Exact(FBig::new(Repr::infinity_with_sign(x.sign()), *self))),
101 Ok(Exact(FBig::new(Repr::infinity(), *self))),
102 );
103 }
104 assert_limited_precision(self.precision);
105 if x.significand.is_zero() {
106 return (
107 Ok(Exact(FBig::new(signed_zero_repr(x), *self))),
108 Ok(Exact(FBig::new(Repr::one(), *self))),
109 );
110 }
111
112 let work = Context::<R>::new(self.precision + 50);
114 let x_f = FBig::<R, B>::new(work.repr_round_ref(x).value(), work);
115 let neg_x = -x_f.clone();
116 let ep = work.exp_m1(&x_f.repr, reborrow_cache(&mut cache));
117 let em = work.exp_m1(&neg_x.repr, reborrow_cache(&mut cache));
118 match (ep, em) {
119 (Ok(ep), Ok(em)) => {
120 let ep = ep.value();
121 let em = em.value();
122 let sinh_val = ((ep.clone() - em.clone()) / 2i32).with_precision(self.precision);
123 let cosh_val =
124 ((ep + em) / 2i32 + FBig::<R, B>::ONE).with_precision(self.precision);
125 (Ok(sinh_val), Ok(cosh_val))
126 }
127 _ => (Err(FpError::Overflow(x.sign())), Err(FpError::Overflow(Sign::Positive))),
130 }
131 }
132
133 pub fn tanh<const B: Word>(
135 &self,
136 x: &Repr<B>,
137 mut cache: Option<&mut ConstCache>,
138 ) -> FpResult<FBig<R, B>> {
139 if x.is_infinite() {
140 let one = FBig::new(Repr::one(), *self);
142 return Ok(Exact(if x.sign() == Sign::Negative {
143 -one
144 } else {
145 one
146 }));
147 }
148 assert_limited_precision(self.precision);
149 if x.significand.is_zero() {
150 return Ok(Exact(FBig::new(signed_zero_repr(x), *self)));
152 }
153
154 let work = Context::<R>::new(self.precision + 50);
158 let x_f = FBig::<R, B>::new(work.repr_round_ref(x).value(), work);
159 let two_x = x_f.clone() * 2i32;
160 match work.exp_m1(&two_x.repr, reborrow_cache(&mut cache)) {
161 Err(FpError::Overflow(_)) => Ok(FBig::ONE.with_precision(self.precision)),
162 Ok(e) => {
163 let e = e.value();
164 Ok((e.clone() / (e.clone() + 2i32)).with_precision(self.precision))
165 }
166 Err(other) => Err(other),
167 }
168 }
169
170 pub fn asinh<const B: Word>(
172 &self,
173 x: &Repr<B>,
174 mut cache: Option<&mut ConstCache>,
175 ) -> FpResult<FBig<R, B>> {
176 if x.is_infinite() {
177 return Ok(Exact(FBig::new(Repr::infinity_with_sign(x.sign()), *self)));
178 }
179 assert_limited_precision(self.precision);
180 if x.significand.is_zero() {
181 return Ok(Exact(FBig::new(signed_zero_repr(x), *self)));
183 }
184
185 let work = Context::<R>::new(self.precision + 50);
188 let x_f = FBig::<R, B>::new(work.repr_round_ref(x).value(), work);
189 let sign = x_f.sign();
190 let abs_x = x_f.abs();
191 let arg = match work.sqr(&abs_x.repr) {
192 Ok(x_sq) => {
193 let x_sq = x_sq.value();
194 let sqrt_plus_one = work.sqrt(&(x_sq.clone() + FBig::<R, B>::ONE).repr)?.value()
195 + FBig::<R, B>::ONE;
196 abs_x.clone() + x_sq / sqrt_plus_one
197 }
198 Err(FpError::Overflow(_)) => {
201 let ln_val = work
202 .ln(&(abs_x.clone() * 2i32).repr, reborrow_cache(&mut cache))?
203 .value();
204 return Ok(apply_sign(ln_val, sign).with_precision(self.precision));
205 }
206 Err(other) => return Err(other),
207 };
208 let res = work.ln_1p(&arg.repr, reborrow_cache(&mut cache))?.value();
209 Ok(apply_sign(res, sign).with_precision(self.precision))
210 }
211
212 pub fn acosh<const B: Word>(
214 &self,
215 x: &Repr<B>,
216 mut cache: Option<&mut ConstCache>,
217 ) -> FpResult<FBig<R, B>> {
218 if x.is_infinite() {
219 if x.sign() == Sign::Negative {
220 return Err(FpError::OutOfDomain);
221 }
222 return Ok(Exact(FBig::new(Repr::infinity(), *self)));
223 }
224 assert_limited_precision(self.precision);
225 if x.sign() == Sign::Negative
227 || FBig::<R, B>::new(x.clone(), *self)
228 .abs_cmp(&FBig::ONE)
229 .is_lt()
230 {
231 return Err(FpError::OutOfDomain);
232 }
233 if x.is_one() {
234 return Ok(Exact(FBig::new(Repr::zero(), *self)));
235 }
236
237 let work = Context::<R>::new(self.precision + 50);
240 let x_f = FBig::<R, B>::new(work.repr_round_ref(x).value(), work);
241 let xm1 = &x_f - FBig::<R, B>::ONE;
242 let xp1 = &x_f + FBig::<R, B>::ONE;
243 let arg = match work.mul(&xm1.repr, &xp1.repr) {
244 Ok(prod) => xm1.clone() + work.sqrt(&prod.value().repr)?.value(),
245 Err(FpError::Overflow(_)) => {
247 let ln_val = work
248 .ln(&(x_f.clone() * 2i32).repr, reborrow_cache(&mut cache))?
249 .value();
250 return Ok(ln_val.with_precision(self.precision));
251 }
252 Err(other) => return Err(other),
253 };
254 let res = work.ln_1p(&arg.repr, reborrow_cache(&mut cache))?.value();
255 Ok(res.with_precision(self.precision))
256 }
257
258 pub fn atanh<const B: Word>(
260 &self,
261 x: &Repr<B>,
262 mut cache: Option<&mut ConstCache>,
263 ) -> FpResult<FBig<R, B>> {
264 if x.is_infinite() {
265 return Err(FpError::OutOfDomain);
266 }
267 assert_limited_precision(self.precision);
268 if x.significand.is_zero() {
269 return Ok(Exact(FBig::new(signed_zero_repr(x), *self)));
271 }
272 match FBig::<R, B>::new(x.clone(), *self).abs_cmp(&FBig::ONE) {
274 core::cmp::Ordering::Greater => return Err(FpError::OutOfDomain),
275 core::cmp::Ordering::Equal => {
276 return Ok(Exact(FBig::new(Repr::infinity_with_sign(x.sign()), *self)));
277 }
278 _ => {}
279 }
280
281 let work = Context::<R>::new(self.precision + 50);
283 let x_f = FBig::<R, B>::new(work.repr_round_ref(x).value(), work);
284 let ratio = (x_f.clone() * 2i32) / (FBig::<R, B>::ONE - &x_f);
285 let res = work.ln_1p(&ratio.repr, reborrow_cache(&mut cache))?.value();
286 Ok((res / 2i32).with_precision(self.precision))
287 }
288}
289
290impl<R: Round, const B: Word> FBig<R, B> {
291 #[inline]
304 pub fn sinh(&self) -> Self {
305 self.context.unwrap_fp(self.context.sinh(&self.repr, None))
306 }
307
308 #[inline]
321 pub fn cosh(&self) -> Self {
322 self.context.unwrap_fp(self.context.cosh(&self.repr, None))
323 }
324
325 #[inline]
343 pub fn sinh_cosh(&self) -> (Self, Self) {
344 let (s, c) = self.context.sinh_cosh(&self.repr, None);
345 (self.context.unwrap_fp(s), self.context.unwrap_fp(c))
346 }
347
348 #[inline]
361 pub fn tanh(&self) -> Self {
362 self.context.unwrap_fp(self.context.tanh(&self.repr, None))
363 }
364
365 #[inline]
378 pub fn asinh(&self) -> Self {
379 self.context.unwrap_fp(self.context.asinh(&self.repr, None))
380 }
381
382 #[inline]
399 pub fn acosh(&self) -> Self {
400 self.context.unwrap_fp(self.context.acosh(&self.repr, None))
401 }
402
403 #[inline]
421 pub fn atanh(&self) -> Self {
422 self.context.unwrap_fp(self.context.atanh(&self.repr, None))
423 }
424}
425
426fn signed_zero_repr<const B: Word>(x: &Repr<B>) -> Repr<B> {
428 if x.is_neg_zero() {
429 Repr::neg_zero()
430 } else {
431 Repr::zero()
432 }
433}
434
435fn apply_sign<R: Round, const B: Word>(v: FBig<R, B>, sign: Sign) -> FBig<R, B> {
437 if sign == Sign::Negative {
438 -v
439 } else {
440 v
441 }
442}