1use crate::cbig::CBig;
8use crate::repr::{combine_parts, reborrow_cache, CfpResult, Context};
9use dashu_float::round::Round;
10use dashu_float::{ConstCache, FBig, FpError, Repr};
11use dashu_int::{IBig, Word};
12
13const TRIG_GUARD: usize = 16;
16
17impl<R: Round> Context<R> {
18 pub fn sin_cos<const B: Word>(
21 &self,
22 z: &CBig<R, B>,
23 mut cache: Option<&mut ConstCache>,
24 ) -> (CfpResult<R, B>, CfpResult<R, B>) {
25 if z.is_infinite() {
26 return (Err(FpError::Indeterminate), Err(FpError::Indeterminate));
27 }
28 if z.is_zero() {
29 let zero = Ok(crate::repr::exact(
30 FBig::from_repr(Repr::zero(), self.float()),
31 FBig::from_repr(Repr::zero(), self.float()),
32 ));
33 let one = Ok(crate::repr::exact(
34 FBig::from_repr(Repr::one(), self.float()),
35 FBig::from_repr(Repr::zero(), self.float()),
36 ));
37 return (zero, one);
38 }
39
40 let gctx = self.guard(TRIG_GUARD);
41 let p = self.precision();
42 let (sinx, cosx) = gctx.sin_cos(z.re(), reborrow_cache(&mut cache));
43 let sinx = match sinx {
44 Ok(v) => v.value(),
45 Err(e) => return (Err(e), Err(FpError::Indeterminate)),
46 };
47 let cosx = match cosx {
48 Ok(v) => v.value(),
49 Err(e) => return (Err(FpError::Indeterminate), Err(e)),
50 };
51 let (sinhy_res, coshy_res) = gctx.sinh_cosh(z.im(), reborrow_cache(&mut cache));
52 let sinhy = match sinhy_res {
53 Ok(v) => v.value(),
54 Err(e) => return (Err(e), Err(FpError::Indeterminate)),
55 };
56 let coshy = match coshy_res {
57 Ok(v) => v.value(),
58 Err(e) => return (Err(FpError::Indeterminate), Err(e)),
59 };
60
61 let prod = |a: &FBig<R, B>, b: &FBig<R, B>| -> Result<_, FpError> {
64 Ok(gctx.mul(a.repr(), b.repr())?.value().with_precision(p))
65 };
66 let sin_re = match prod(&sinx, &coshy) {
67 Ok(v) => v,
68 Err(e) => return (Err(e), Err(FpError::Indeterminate)),
69 };
70 let sin_im = match prod(&cosx, &sinhy) {
71 Ok(v) => v,
72 Err(e) => return (Err(e), Err(FpError::Indeterminate)),
73 };
74 let cos_re = match prod(&cosx, &coshy) {
75 Ok(v) => v,
76 Err(e) => return (Err(FpError::Indeterminate), Err(e)),
77 };
78 let neg_sinx = -sinx;
79 let cos_im = match prod(&neg_sinx, &sinhy) {
80 Ok(v) => v,
81 Err(e) => return (Err(FpError::Indeterminate), Err(e)),
82 };
83 (Ok(combine_parts(sin_re, sin_im)), Ok(combine_parts(cos_re, cos_im)))
84 }
85
86 #[inline]
88 pub fn sin<const B: Word>(
89 &self,
90 z: &CBig<R, B>,
91 cache: Option<&mut ConstCache>,
92 ) -> CfpResult<R, B> {
93 self.sin_cos(z, cache).0
94 }
95
96 #[inline]
98 pub fn cos<const B: Word>(
99 &self,
100 z: &CBig<R, B>,
101 cache: Option<&mut ConstCache>,
102 ) -> CfpResult<R, B> {
103 self.sin_cos(z, cache).1
104 }
105
106 pub fn tan<const B: Word>(
108 &self,
109 z: &CBig<R, B>,
110 cache: Option<&mut ConstCache>,
111 ) -> CfpResult<R, B> {
112 let (sin_z, cos_z) = self.sin_cos(z, cache);
113 let sin_z = sin_z?;
114 let cos_z = cos_z?;
115 self.div(&sin_z.value(), &cos_z.value())
116 }
117
118 pub fn asin<const B: Word>(
122 &self,
123 z: &CBig<R, B>,
124 mut cache: Option<&mut ConstCache>,
125 ) -> CfpResult<R, B> {
126 if z.is_infinite() {
127 return Err(FpError::Indeterminate);
128 }
129 let gctx = Context::new(self.precision() + ITRIG_GUARD);
130 let p = self.precision();
131 let one = CBig::ONE;
132 let z2 = gctx.sqr(z)?.value();
133 let one_m_z2 = gctx.sub(&one, &z2)?.value();
134 let sqrt_term = gctx.sqrt(&one_m_z2)?.value();
135 let iz = z.mul_i(false); let w = gctx.add(&iz, &sqrt_term)?.value();
137 let log_w = gctx.log(&w, reborrow_cache(&mut cache))?.value();
138 let asin_z = log_w.mul_i(true); let (re, im) = asin_z.into_parts();
140 Ok(combine_parts(re.with_precision(p), im.with_precision(p)))
141 }
142
143 pub fn acos<const B: Word>(
145 &self,
146 z: &CBig<R, B>,
147 mut cache: Option<&mut ConstCache>,
148 ) -> CfpResult<R, B> {
149 if z.is_infinite() {
150 return Err(FpError::Indeterminate);
151 }
152 let gctx = Context::new(self.precision() + ITRIG_GUARD);
153 let p = self.precision();
154 let one = CBig::ONE;
155 let z2 = gctx.sqr(z)?.value();
156 let one_m_z2 = gctx.sub(&one, &z2)?.value();
157 let sqrt_term = gctx.sqrt(&one_m_z2)?.value();
158 let i_sqrt = sqrt_term.mul_i(false); let w = gctx.add(z, &i_sqrt)?.value();
160 let log_w = gctx.log(&w, reborrow_cache(&mut cache))?.value();
161 let acos_z = log_w.mul_i(true); let (re, im) = acos_z.into_parts();
163 Ok(combine_parts(re.with_precision(p), im.with_precision(p)))
164 }
165
166 pub fn atan<const B: Word>(
168 &self,
169 z: &CBig<R, B>,
170 mut cache: Option<&mut ConstCache>,
171 ) -> CfpResult<R, B> {
172 if z.is_infinite() {
173 return Err(FpError::Indeterminate);
176 }
177 let gctx = Context::new(self.precision() + ITRIG_GUARD);
178 let p = self.precision();
179 let one = CBig::ONE;
180 let iz = z.mul_i(false);
181 let a = gctx.sub(&one, &iz)?.value(); let b = gctx.add(&one, &iz)?.value(); let log_a = gctx.log(&a, reborrow_cache(&mut cache))?.value();
184 let log_b = gctx.log(&b, reborrow_cache(&mut cache))?.value();
185 let diff = gctx.sub(&log_a, &log_b)?.value();
186 let i_half_diff = diff.mul_i(false); let two: CBig<R, B> = IBig::from(2).into();
188 let atan_z = gctx.div(&i_half_diff, &two)?.value();
189 let (re, im) = atan_z.into_parts();
190 Ok(combine_parts(re.with_precision(p), im.with_precision(p)))
191 }
192}
193
194const ITRIG_GUARD: usize = 18;
196
197impl<R: Round, const B: Word> CBig<R, B> {
198 #[inline]
200 pub fn sin(&self) -> Self {
201 self.context().unwrap_cfp(self.context().sin(self, None))
202 }
203
204 #[inline]
206 pub fn cos(&self) -> Self {
207 self.context().unwrap_cfp(self.context().cos(self, None))
208 }
209
210 #[inline]
212 pub fn sin_cos(&self) -> (Self, Self) {
213 let (s, c) = self.context().sin_cos(self, None);
214 (self.context().unwrap_cfp(s), self.context().unwrap_cfp(c))
215 }
216
217 #[inline]
219 pub fn tan(&self) -> Self {
220 self.context().unwrap_cfp(self.context().tan(self, None))
221 }
222
223 #[inline]
225 pub fn asin(&self) -> Self {
226 self.context().unwrap_cfp(self.context().asin(self, None))
227 }
228
229 #[inline]
231 pub fn acos(&self) -> Self {
232 self.context().unwrap_cfp(self.context().acos(self, None))
233 }
234
235 #[inline]
237 pub fn atan(&self) -> Self {
238 self.context().unwrap_cfp(self.context().atan(self, None))
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use dashu_float::round::mode;
246
247 type C = CBig<mode::HalfAway, 10>;
248 type F = FBig<mode::HalfAway, 10>;
249
250 fn c(re: i32, im: i32) -> C {
251 let mk = |v: i32| -> F { F::from(v).with_precision(53).value() };
252 CBig::from_parts(mk(re), mk(im))
253 }
254
255 #[test]
256 fn sin_zero_is_zero() {
257 assert!(C::ZERO.sin() == C::ZERO);
258 }
259
260 #[test]
261 fn cos_zero_is_one() {
262 assert!(C::ZERO.cos() == C::ONE);
263 }
264
265 #[test]
266 fn pythagorean_identity() {
267 let z = c(1, 1);
269 let s = z.sin();
270 let co = z.cos();
271 let sum = &s.sqr() + &co.sqr();
272 let (re, im) = sum.into_parts();
274 use dashu_base::{Abs, AbsOrd};
275 assert!((re.clone() - F::ONE)
276 .abs()
277 .abs_cmp(&F::from_parts(1.into(), -12))
278 .is_le());
279 assert!(im.abs_cmp(&F::from_parts(1.into(), -12)).is_le());
280 }
281
282 #[test]
283 fn sin_i_is_i_sinh_one() {
284 let s = C::I.sin();
286 assert!(s.re().significand().is_zero());
287 assert!(!s.im().significand().is_zero());
288 }
289
290 #[test]
291 fn asin_zero_is_zero() {
292 assert!(C::ZERO.asin() == C::ZERO);
293 }
294
295 #[test]
296 fn asin_one_is_half_pi() {
297 use dashu_base::{Abs, AbsOrd};
298 let (re, im) = C::ONE.asin().into_parts();
300 let half_pi = F::from_parts(15707963267948966i64.into(), -16)
301 .with_precision(60)
302 .value();
303 assert!((re.clone() - half_pi)
304 .abs()
305 .abs_cmp(&F::from_parts(1.into(), -12))
306 .is_le());
307 assert!(im.abs_cmp(&F::from_parts(1.into(), -12)).is_le());
308 }
309
310 #[test]
311 fn acos_zero_is_half_pi() {
312 use dashu_base::{Abs, AbsOrd};
313 let (re, _im) = C::ZERO.acos().into_parts();
314 let half_pi = F::from_parts(15707963267948966i64.into(), -16)
315 .with_precision(60)
316 .value();
317 assert!((re - half_pi)
318 .abs()
319 .abs_cmp(&F::from_parts(1.into(), -12))
320 .is_le());
321 }
322
323 #[test]
324 fn atan_one_is_quarter_pi() {
325 use dashu_base::{Abs, AbsOrd};
326 let (re, _im) = C::ONE.atan().into_parts();
328 let quarter_pi = F::from_parts(7853981633974483i64.into(), -16)
329 .with_precision(60)
330 .value();
331 assert!((re - quarter_pi)
332 .abs()
333 .abs_cmp(&F::from_parts(1.into(), -12))
334 .is_le());
335 }
336
337 #[test]
338 fn sin_asin_roundtrip() {
339 let z = c(1, 1);
341 let r = z.sin().asin();
342 assert!(r == z);
343 }
344}