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