1use oxinum_float::{cos, cosh, sin, sinh};
28
29use crate::{CBig, OxiNumResult};
30
31const GUARD: usize = 10;
33
34impl CBig {
35 pub fn sin(&self, precision: usize) -> OxiNumResult<CBig> {
42 let guard = precision.saturating_add(GUARD);
43 let a = &self.re;
44 let b = &self.im;
45
46 let sa = sin(a, guard)?;
47 let ca = cos(a, guard)?;
48 let shb = sinh(b, guard)?;
49 let chb = cosh(b, guard)?;
50
51 let re = &sa * &chb;
52 let im = &ca * &shb;
53 Ok(CBig::from_parts(re, im))
54 }
55
56 pub fn cos(&self, precision: usize) -> OxiNumResult<CBig> {
63 let guard = precision.saturating_add(GUARD);
64 let a = &self.re;
65 let b = &self.im;
66
67 let sa = sin(a, guard)?;
68 let ca = cos(a, guard)?;
69 let shb = sinh(b, guard)?;
70 let chb = cosh(b, guard)?;
71
72 let re = &ca * &chb;
73 let im = -(&sa * &shb);
75 Ok(CBig::from_parts(re, im))
76 }
77
78 pub fn sinh(&self, precision: usize) -> OxiNumResult<CBig> {
85 let guard = precision.saturating_add(GUARD);
86 let a = &self.re;
87 let b = &self.im;
88
89 let sha = sinh(a, guard)?;
90 let cha = cosh(a, guard)?;
91 let sb = sin(b, guard)?;
92 let cb = cos(b, guard)?;
93
94 let re = &sha * &cb;
95 let im = &cha * &sb;
96 Ok(CBig::from_parts(re, im))
97 }
98
99 pub fn cosh(&self, precision: usize) -> OxiNumResult<CBig> {
106 let guard = precision.saturating_add(GUARD);
107 let a = &self.re;
108 let b = &self.im;
109
110 let sha = sinh(a, guard)?;
111 let cha = cosh(a, guard)?;
112 let sb = sin(b, guard)?;
113 let cb = cos(b, guard)?;
114
115 let re = &cha * &cb;
116 let im = &sha * &sb;
117 Ok(CBig::from_parts(re, im))
118 }
119
120 pub fn tan(&self, precision: usize) -> OxiNumResult<CBig> {
128 self.sin(precision)?.checked_div(&self.cos(precision)?)
129 }
130
131 pub fn tanh(&self, precision: usize) -> OxiNumResult<CBig> {
140 self.sinh(precision)?.checked_div(&self.cosh(precision)?)
141 }
142}
143
144#[cfg(test)]
149mod tests {
150 use super::*;
151
152 const PREC: usize = 40;
153
154 fn c(re: f64, im: f64) -> CBig {
156 CBig::from_f64(re, im).expect("finite parts")
157 }
158
159 #[test]
160 fn sin_zero_is_zero() {
161 let s = CBig::zero().sin(PREC).expect("sin");
162 let (re, im) = s.to_f64_parts();
163 assert!(re.abs() < 1e-12, "re = {re}");
164 assert!(im.abs() < 1e-12, "im = {im}");
165 }
166
167 #[test]
168 fn cos_zero_is_one() {
169 let cz = CBig::zero().cos(PREC).expect("cos");
170 let (re, im) = cz.to_f64_parts();
171 assert!((re - 1.0).abs() < 1e-12, "re = {re}");
172 assert!(im.abs() < 1e-12, "im = {im}");
173 }
174
175 #[test]
176 fn sinh_zero_is_zero() {
177 let s = CBig::zero().sinh(PREC).expect("sinh");
178 let (re, im) = s.to_f64_parts();
179 assert!(re.abs() < 1e-12, "re = {re}");
180 assert!(im.abs() < 1e-12, "im = {im}");
181 }
182
183 #[test]
184 fn cosh_zero_is_one() {
185 let cz = CBig::zero().cosh(PREC).expect("cosh");
186 let (re, im) = cz.to_f64_parts();
187 assert!((re - 1.0).abs() < 1e-12, "re = {re}");
188 assert!(im.abs() < 1e-12, "im = {im}");
189 }
190
191 #[test]
192 fn pythagorean_identity_general() {
193 let z = c(0.5, 0.3);
195 let s = z.sin(PREC).expect("sin");
196 let co = z.cos(PREC).expect("cos");
197 let sum = &(&s * &s) + &(&co * &co);
198 let (re, im) = sum.to_f64_parts();
199 assert!((re - 1.0).abs() < 1e-9, "re(sum) = {re}");
200 assert!(im.abs() < 1e-9, "im(sum) = {im}");
201 }
202
203 #[test]
204 fn cosh_sq_minus_sinh_sq_is_one() {
205 let z = c(0.4, 0.7);
207 let ch = z.cosh(PREC).expect("cosh");
208 let sh = z.sinh(PREC).expect("sinh");
209 let diff = &(&ch * &ch) - &(&sh * &sh);
210 let (re, im) = diff.to_f64_parts();
211 assert!((re - 1.0).abs() < 1e-9, "re(diff) = {re}");
212 assert!(im.abs() < 1e-9, "im(diff) = {im}");
213 }
214
215 #[test]
216 fn tan_zero_is_zero() {
217 let t = CBig::zero().tan(PREC).expect("tan");
218 let (re, im) = t.to_f64_parts();
219 assert!(re.abs() < 1e-12, "re = {re}");
220 assert!(im.abs() < 1e-12, "im = {im}");
221 }
222
223 #[test]
224 fn tanh_zero_is_zero() {
225 let t = CBig::zero().tanh(PREC).expect("tanh");
226 let (re, im) = t.to_f64_parts();
227 assert!(re.abs() < 1e-12, "re = {re}");
228 assert!(im.abs() < 1e-12, "im = {im}");
229 }
230
231 #[test]
232 fn tan_matches_known_value() {
233 let z = c(0.5, 0.3);
236 let t = z.tan(PREC).expect("tan");
237 let (re, im) = t.to_f64_parts();
238 assert!((re - 0.487_592_316_492_138_74).abs() < 1e-9, "re = {re}");
239 assert!((im - 0.368_910_396_825_563_8).abs() < 1e-9, "im = {im}");
240 }
241
242 #[test]
243 fn tan_is_sin_over_cos() {
244 let z = c(0.5, 0.3);
246 let t = z.tan(PREC).expect("tan");
247 let q = z
248 .sin(PREC)
249 .expect("sin")
250 .checked_div(&z.cos(PREC).expect("cos"))
251 .expect("non-zero cos");
252 let (tre, tim) = t.to_f64_parts();
253 let (qre, qim) = q.to_f64_parts();
254 assert!((tre - qre).abs() < 1e-12, "re: {tre} vs {qre}");
255 assert!((tim - qim).abs() < 1e-12, "im: {tim} vs {qim}");
256 }
257}