1#![allow(non_snake_case)]
2#![allow(clippy::excessive_precision)]
3
4use crate::geodesic::{CARR_SIZE, GEODESIC_ORDER};
5
6pub const DIGITS: u64 = 53;
7
8pub fn sq(x: f64) -> f64 {
10 x.powi(2)
11}
12
13pub fn norm(x: &mut f64, y: &mut f64) {
15 let r = x.hypot(*y);
16 *x /= r;
17 *y /= r;
18}
19
20pub fn sum(u: f64, v: f64) -> (f64, f64) {
22 let s = u + v;
23 let up = s - v;
24 let vpp = s - up;
25 let up = up - u;
26 let vpp = vpp - v;
27 let t = -(up + vpp);
28 (s, t)
29}
30
31pub fn polyval(n: usize, p: &[f64], x: f64) -> f64 {
33 let mut y = p[0];
34 for val in &p[1..=n] {
35 y = y * x + val;
36 }
37 y
38}
39
40pub fn ang_round(x: f64) -> f64 {
42 let z = 1.0 / 16.0;
48 let mut y = x.abs();
49 if y < z {
51 y = z - (z - y);
52 };
53 if x == 0.0 {
54 0.0
55 } else if x < 0.0 {
56 -y
57 } else {
58 y
59 }
60}
61
62fn remainder(x: f64, y: f64) -> f64 {
64 let z = if x.is_finite() { x % y } else { f64::NAN };
66
67 let z = if x == 0.0 { x } else { z };
72
73 if z < -y / 2.0 {
76 z + y
77 } else if z < y / 2.0 {
78 z
79 } else {
80 z - y
81 }
82}
83
84pub fn ang_normalize(x: f64) -> f64 {
86 let y = remainder(x, 360.0);
89 if y == -180.0 {
90 180.0
91 } else {
92 y
93 }
94}
95
96pub fn lat_fix(x: f64) -> f64 {
98 if x.abs() > 90.0 {
99 f64::NAN
100 } else {
101 x
102 }
103}
104
105pub fn ang_diff(x: f64, y: f64) -> (f64, f64) {
107 let (d, t) = sum(ang_normalize(-x), ang_normalize(y));
108 let d = ang_normalize(d);
109 if d == 180.0 && t > 0.0 {
110 sum(-180.0, t)
111 } else {
112 sum(d, t)
113 }
114}
115
116pub fn sincosd(x: f64) -> (f64, f64) {
118 let (mut r, q) = libm::remquo(x, 90.0);
119
120 r = r.to_radians();
121
122 let (mut sinx, mut cosx) = r.sin_cos();
123
124 (sinx, cosx) = match q as u32 & 3 {
125 0 => (sinx, cosx),
126 1 => (cosx, -sinx),
127 2 => (-sinx, -cosx),
128 3 => (-cosx, sinx),
129 _ => unreachable!(),
130 };
131
132 cosx += 0.0;
134
135 if sinx == 0.0 {
137 sinx = sinx.copysign(x);
138 }
139 (sinx, cosx)
140}
141
142pub fn atan2d(mut y: f64, mut x: f64) -> f64 {
144 let mut q = if y.abs() > x.abs() {
145 std::mem::swap(&mut x, &mut y);
146 2
147 } else {
148 0
149 };
150 if x < 0.0 {
151 q += 1;
152 x = -x;
153 }
154 let mut ang = y.atan2(x).to_degrees();
155 match q {
156 0 => {}
157 1 => {
158 ang = if y >= 0.0 { 180.0 - ang } else { -180.0 - ang };
159 }
160 2 => {
161 ang = 90.0 - ang;
162 }
163 3 => {
164 ang += -90.0;
165 }
166 _ => unreachable!(),
167 };
168 ang
169}
170
171pub fn eatanhe(x: f64, es: f64) -> f64 {
172 if es > 0.0 {
173 es * (es * x).atanh()
174 } else {
175 -es * (es * x).atan()
176 }
177}
178
179pub fn sin_cos_series<const N: usize>(sinp: bool, sinx: f64, cosx: f64, c: &[f64; N]) -> f64 {
181 let mut k = c.len();
182 let mut n: i64 = k as i64 - if sinp { 1 } else { 0 };
183 let ar: f64 = 2.0 * (cosx - sinx) * (cosx + sinx);
184 let mut y1 = 0.0;
185 let mut y0: f64 = if n & 1 != 0 {
186 k -= 1;
187 c[k]
188 } else {
189 0.0
190 };
191 n /= 2;
192 while n > 0 {
193 n -= 1;
194 k -= 1;
195 y1 = ar * y0 - y1 + c[k];
196 k -= 1;
197 y0 = ar * y1 - y0 + c[k];
198 }
199 if sinp {
200 2.0 * sinx * cosx * y0
201 } else {
202 cosx * (y0 - y1)
203 }
204}
205
206pub fn astroid(x: f64, y: f64) -> f64 {
208 let p = sq(x);
209 let q = sq(y);
210 let r = (p + q - 1.0) / 6.0;
211 if !(q == 0.0 && r <= 0.0) {
212 let s = p * q / 4.0;
213 let r2 = sq(r);
214 let r3 = r * r2;
215 let disc = s * (s + 2.0 * r3);
216 let mut u = r;
217 if disc >= 0.0 {
218 let mut t3 = s + r3;
219 t3 += if t3 < 0.0 { -disc.sqrt() } else { disc.sqrt() };
220 let t = t3.cbrt();
221 u += t + if t != 0.0 { r2 / t } else { 0.0 };
222 } else {
223 let ang = (-disc).sqrt().atan2(-(s + r3));
224 u += 2.0 * r * (ang / 3.0).cos();
225 }
226 let v = (sq(u) + q).sqrt();
227 let uv = if u < 0.0 { q / (v - u) } else { u + v };
228 let w = (uv - q) / (2.0 * v);
229 uv / ((uv + sq(w)).sqrt() + w)
230 } else {
231 0.0
232 }
233}
234
235pub fn _A1m1f(eps: f64) -> f64 {
236 const COEFF: [f64; 5] = [1.0, 4.0, 64.0, 0.0, 256.0];
237 let m = GEODESIC_ORDER / 2;
238 let t = polyval(m, &COEFF, sq(eps)) / COEFF[m + 1];
239 (t + eps) / (1.0 - eps)
240}
241
242pub fn _C1f(eps: f64, c: &mut [f64; CARR_SIZE]) {
243 const COEFF: [f64; 18] = [
244 -1.0, 6.0, -16.0, 32.0, -9.0, 64.0, -128.0, 2048.0, 9.0, -16.0, 768.0, 3.0, -5.0, 512.0,
245 -7.0, 1280.0, -7.0, 2048.0,
246 ];
247 let eps2 = sq(eps);
248 let mut d = eps;
249 let mut o = 0;
250 #[allow(clippy::needless_range_loop)]
253 for l in 1..=GEODESIC_ORDER {
254 let m = (GEODESIC_ORDER - l) / 2;
255 c[l] = d * polyval(m, &COEFF[o..], eps2) / COEFF[o + m + 1];
256 o += m + 2;
257 d *= eps;
258 }
259}
260
261pub fn _C1pf(eps: f64, c: &mut [f64; CARR_SIZE]) {
262 const COEFF: [f64; 18] = [
263 205.0, -432.0, 768.0, 1536.0, 4005.0, -4736.0, 3840.0, 12288.0, -225.0, 116.0, 384.0,
264 -7173.0, 2695.0, 7680.0, 3467.0, 7680.0, 38081.0, 61440.0,
265 ];
266 let eps2 = sq(eps);
267 let mut d = eps;
268 let mut o = 0;
269 #[allow(clippy::needless_range_loop)]
272 for l in 1..=GEODESIC_ORDER {
273 let m = (GEODESIC_ORDER - l) / 2;
274 c[l] = d * polyval(m, &COEFF[o..], eps2) / COEFF[o + m + 1];
275 o += m + 2;
276 d *= eps;
277 }
278}
279
280pub fn _A2m1f(eps: f64) -> f64 {
281 const COEFF: [f64; 5] = [-11.0, -28.0, -192.0, 0.0, 256.0];
282 let m = GEODESIC_ORDER / 2;
283 let t = polyval(m, &COEFF, sq(eps)) / COEFF[m + 1];
284 (t - eps) / (1.0 + eps)
285}
286
287pub fn _C2f(eps: f64, c: &mut [f64; CARR_SIZE]) {
288 const COEFF: [f64; 18] = [
289 1.0, 2.0, 16.0, 32.0, 35.0, 64.0, 384.0, 2048.0, 15.0, 80.0, 768.0, 7.0, 35.0, 512.0, 63.0,
290 1280.0, 77.0, 2048.0,
291 ];
292 let eps2 = sq(eps);
293 let mut d = eps;
294 let mut o = 0;
295 #[allow(clippy::needless_range_loop)]
298 for l in 1..=GEODESIC_ORDER {
299 let m = (GEODESIC_ORDER - l) / 2;
300 c[l] = d * polyval(m, &COEFF[o..], eps2) / COEFF[o + m + 1];
301 o += m + 2;
302 d *= eps;
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 use super::*;
309 use approx::assert_relative_eq;
310 #[test]
313 fn test_sincosd() {
314 let res = sincosd(-77.03196);
315 assert_relative_eq!(res.0, -0.9744953925159129);
316 assert_relative_eq!(res.1, 0.22440750870961693);
317
318 let res = sincosd(69.48894);
319 assert_relative_eq!(res.0, 0.9366045700708676);
320 assert_relative_eq!(res.1, 0.3503881837653281);
321 let res = sincosd(-1.0);
322 assert_relative_eq!(res.0, -0.01745240643728351);
323 assert_relative_eq!(res.1, 0.9998476951563913);
324 }
325
326 #[test]
327 fn test__C2f() {
328 let mut c = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0];
329 _C2f(0.12, &mut c);
330 assert_eq!(
331 c,
332 [
333 1.0,
334 0.0601087776,
335 0.00270653103,
336 0.000180486,
337 1.4215824e-05,
338 1.22472e-06,
339 1.12266e-07
340 ]
341 )
342 }
343
344 #[test]
345 fn test__A2m1f() {
346 assert_eq!(_A2m1f(0.12), -0.11680607884285714);
347 }
348
349 #[test]
350 fn test__C1pf() {
351 let mut c = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0];
352 _C1pf(0.12, &mut c);
353 assert_eq!(
354 c,
355 [
356 1.0,
357 0.059517321000000005,
358 0.004421053215,
359 0.0005074200000000001,
360 6.997613759999999e-05,
361 1.1233080000000001e-05,
362 1.8507366e-06
363 ]
364 )
365 }
366
367 #[test]
368 fn test__C1f() {
369 let mut c = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0];
370 _C1f(0.12, &mut c);
371 assert_eq!(
372 c,
373 [
374 1.0,
375 -0.059676777599999994,
376 -0.000893533122,
377 -3.57084e-05,
378 -2.007504e-06,
379 -1.3607999999999999e-07,
380 -1.0205999999999999e-08
381 ]
382 )
383 }
384
385 #[test]
386 fn test__A1m1f() {
387 assert_eq!(_A1m1f(0.12), 0.1404582405272727);
388 }
389
390 #[test]
391 fn test_astroid() {
392 assert_eq!(astroid(21.0, 12.0), 23.44475767500982);
393 }
394
395 #[test]
396 fn test_sin_cos_series() {
397 assert_eq!(
398 sin_cos_series(
399 false,
400 -0.8928657853278468,
401 0.45032287238256896,
402 &[
403 0.6660771734724675,
404 1.5757752625233906e-05,
405 3.8461688963148916e-09,
406 1.3040960748120204e-12,
407 5.252912023008548e-16,
408 2.367770858285795e-19
409 ],
410 ),
411 0.29993425660538664
412 );
413
414 assert_eq!(
415 sin_cos_series(
416 false,
417 -0.8928657853278468,
418 0.45032287238256896,
419 &[0., 1., 2., 3., 4., 5.],
420 ),
421 1.8998562852254026
422 );
423 assert_eq!(
424 sin_cos_series(
425 true,
426 0.2969032234925426,
427 0.9549075745221299,
428 &[
429 0.0,
430 -0.0003561309485314716,
431 -3.170731714689771e-08,
432 -7.527972480734327e-12,
433 -2.5133854116682488e-15,
434 -1.0025061462383107e-18,
435 -4.462794158625518e-22
436 ],
437 ),
438 -0.00020196665516199853
439 );
440 assert_eq!(
441 sin_cos_series(
442 true,
443 -0.8928657853278468,
444 0.45032287238256896,
445 &[
446 0.0,
447 -0.0003561309485314716,
448 -3.170731714689771e-08,
449 -7.527972480734327e-12,
450 -2.5133854116682488e-15,
451 -1.0025061462383107e-18,
452 -4.462794158625518e-22
453 ],
454 ),
455 0.00028635444718997857
456 );
457
458 assert_eq!(
459 sin_cos_series(true, 0.12, 0.21, &[1.0, 2.0]),
460 0.10079999999999999
461 );
462 assert_eq!(
463 sin_cos_series(
464 true,
465 -0.024679833885152578,
466 0.9996954065111039,
467 &[
468 0.0,
469 -0.0008355098973052918,
470 -1.7444619952659748e-07,
471 -7.286557795511902e-11,
472 -3.80472772706481e-14,
473 -2.2251271876594078e-17,
474 1.2789961247944744e-20
475 ],
476 ),
477 4.124513511893872e-05
478 );
479 }
480
481 mod sign_test {
483 use super::*;
484 fn is_equiv(x: f64, y: f64) -> bool {
485 (x.is_nan() && y.is_nan()) || (x == y && x.is_sign_positive() == y.is_sign_positive())
486 }
487
488 macro_rules! check_sincosd {
489 ($x: expr, $expected_sin: expr, $expected_cos: expr) => {
490 let (sinx, cosx) = sincosd($x);
491 assert!(
492 is_equiv(sinx, $expected_sin),
493 "sinx({}) = {}, but got {}",
494 $x,
495 $expected_sin,
496 sinx
497 );
498 assert!(
499 is_equiv(cosx, $expected_cos),
500 "cosx({}) = {}, but got {}",
501 $x,
502 $expected_cos,
503 cosx
504 );
505 };
506 }
507
508 #[test]
509 fn sin_cosd() {
510 check_sincosd!(f64::NEG_INFINITY, f64::NAN, f64::NAN);
511 check_sincosd!(-810.0, -1.0, 0.0);
512 check_sincosd!(-720.0, -0.0, 1.0);
513 check_sincosd!(-630.0, 1.0, 0.0);
514 check_sincosd!(-540.0, -0.0, -1.0);
515 check_sincosd!(-450.0, -1.0, 0.0);
516 check_sincosd!(-360.0, -0.0, 1.0);
517 check_sincosd!(-270.0, 1.0, 0.0);
518 check_sincosd!(-180.0, -0.0, -1.0);
519 check_sincosd!(-90.0, -1.0, 0.0);
520 check_sincosd!(-0.0, -0.0, 1.0);
521 check_sincosd!(0.0, 0.0, 1.0);
522 check_sincosd!(90.0, 1.0, 0.0);
523 check_sincosd!(180.0, 0.0, -1.0);
524 check_sincosd!(270.0, -1.0, 0.0);
525 check_sincosd!(360.0, 0.0, 1.0);
526 check_sincosd!(450.0, 1.0, 0.0);
527 check_sincosd!(540.0, 0.0, -1.0);
528 check_sincosd!(630.0, -1.0, 0.0);
529 check_sincosd!(720.0, 0.0, 1.0);
530 check_sincosd!(810.0, 1.0, 0.0);
531 check_sincosd!(f64::INFINITY, f64::NAN, f64::NAN);
532 check_sincosd!(f64::NAN, f64::NAN, f64::NAN);
533 }
534 }
535}