1use core::f64::consts::{FRAC_1_SQRT_2, PI};
2use libm::{
3 atan, atan2, atanh, cbrt, copysign, cos, fabs, fmax, fmin, hypot, remainder, remquo, sin, sqrt,
4};
5
6const GEOGRAPHICLIB_GEODESIC_ORDER: usize = 6;
7const N_A1: usize = GEOGRAPHICLIB_GEODESIC_ORDER;
8const N_A2: usize = GEOGRAPHICLIB_GEODESIC_ORDER;
9const N_A3: usize = GEOGRAPHICLIB_GEODESIC_ORDER;
10const N_C: usize = GEOGRAPHICLIB_GEODESIC_ORDER + 1;
11const N_C1: usize = GEOGRAPHICLIB_GEODESIC_ORDER;
12const N_C1_P: usize = GEOGRAPHICLIB_GEODESIC_ORDER;
13const N_C2: usize = GEOGRAPHICLIB_GEODESIC_ORDER;
14const N_C3: usize = GEOGRAPHICLIB_GEODESIC_ORDER;
15const N_C4: usize = GEOGRAPHICLIB_GEODESIC_ORDER;
16const TOL0: f64 = f64::EPSILON;
17const TOL1: f64 = 200. * TOL0;
18const TOL2: f64 = 1.4901161193847656e-8; const TOLB: f64 = TOL0;
20const DEGREE: f64 = core::f64::consts::PI / 180.0;
21const QD: f64 = 90.0;
22const HD: f64 = 180.0;
23const TD: f64 = 360.0;
24const TINY: f64 = 1.4916681462400413e-154; const DIGITS: u32 = f64::MANTISSA_DIGITS;
26const MAXIT1: u32 = 20;
27const MAXIT2: u32 = MAXIT1 + DIGITS + 10;
28const XTHRESH: f64 = 1000. * TOL2;
29
30#[derive(Debug, Default, Clone, Copy, PartialEq)]
32pub enum GeodMask {
33 #[default]
35 GeodNone = 0, GeodLatitude = 1 << 7, GeodLongitude = 1 << 8 | 1 << 3, GeodAzimuth = 1 << 9, GeodDistance = 1 << 10 | 1 << 0, GeodDistanceIn = 1 << 11 | 1 << 0 | 1 << 1, GeodReducedlength = 1 << 12 | 1 << 0 | 1 << 2, GeodGeodesicScale = 1 << 13 | 1 << 0 | 1 << 2, GeodArea = 1 << 14 | 1 << 4, GeodAll = 0x7F80 | 0x1F, }
55
56#[derive(Debug, Default, Clone, Copy, PartialEq)]
58pub enum GeodFlags {
59 #[default]
61 GeodNoflags = 0, GeodArcmode = 1 << 0, GeodLongUnroll = 1 << 15, }
67
68#[derive(Debug, Default, Clone, Copy, PartialEq)]
70pub enum CapType {
71 #[default]
73 CapNone = 0,
74 CapC1 = 1 << 0,
76 CapC1p = 1 << 1,
78 CapC2 = 1 << 2,
80 CapC3 = 1 << 3,
82 CapC4 = 1 << 4,
84 CapAll = 0x1F,
86 OutAll = 0x7F80,
88}
89
90#[derive(Debug, Default, Clone, Copy, PartialEq)]
93pub struct GeodGeodesic {
94 pub a: f64,
96 pub f: f64,
98 pub f1: f64,
100 pub e2: f64,
102 pub ep2: f64,
104 pub n: f64,
106 pub b: f64,
108 pub c2: f64,
110 pub etol2: f64,
112 pub a3x: [f64; 6],
114 pub c3x: [f64; 15],
116 pub c4x: [f64; 21],
118}
119
120#[derive(Debug, Default, Clone, Copy, PartialEq)]
124pub struct GeodGeodesicline {
125 pub lat1: f64,
127 pub lon1: f64,
129 pub azi1: f64,
131 pub a: f64,
133 pub f: f64,
135 pub salp1: f64,
137 pub calp1: f64,
139 pub a13: f64,
141 pub s13: f64,
143 pub b: f64,
145 pub c2: f64,
147 pub f1: f64,
149 pub salp0: f64,
151 pub calp0: f64,
153 pub k2: f64,
155 pub ssig1: f64,
157 pub csig1: f64,
159 pub dn1: f64,
161 pub stau1: f64,
163 pub ctau1: f64,
165 pub somg1: f64,
167 pub comg1: f64,
169 pub a1m1: f64,
171 pub a2m1: f64,
173 pub a3c: f64,
175 pub b11: f64,
177 pub b21: f64,
179 pub b31: f64,
181 pub a4: f64,
183 pub b41: f64,
185 pub c1a: [f64; 7],
187 pub c1pa: [f64; 7],
189 pub c2a: [f64; 7],
191 pub c3a: [f64; 6],
193 pub c4a: [f64; 6],
195 caps: u32,
198}
199
200fn sq(x: f64) -> f64 {
201 x * x
202}
203
204fn sumx(u: f64, v: f64, t: &mut f64) -> f64 {
205 let s = u + v;
206 let mut up = s - v;
207 let mut vpp = s - up;
208 up -= u;
209 vpp -= v;
210 if *t != 0. {
211 *t = if s != 0. { 0. - (up + vpp) } else { s };
212 }
213 s
217}
218
219pub fn geod_init(g: &mut GeodGeodesic, a: f64, f: f64) {
221 g.a = a;
222 g.f = f;
223 g.f1 = 1. - g.f;
224 g.e2 = g.f * (2. - g.f);
225 g.ep2 = g.e2 / sq(g.f1); g.n = g.f / (2. - g.f);
227 g.b = g.a * g.f1;
228 g.c2 = (sq(g.a)
229 + sq(g.b)
230 * (if g.e2 == 0. {
231 1.
232 } else {
233 (if g.e2 > 0. { atanh(sqrt(g.e2)) } else { atan(sqrt(-g.e2)) }) / sqrt(fabs(g.e2))
234 }))
235 / 2.; g.etol2 = 0.1 * TOL2 / sqrt(fmax(0.001, fabs(g.f)) * fmin(1.0, 1. - g.f / 2.) / 2.);
246
247 a3coeff(g);
248 c3coeff(g);
249 c4coeff(g);
250}
251
252#[allow(clippy::too_many_arguments)]
254pub fn geod_inverse(
255 g: &mut GeodGeodesic,
256 lat1: f64,
257 lon1: f64,
258 lat2: f64,
259 lon2: f64,
260 ps12: &mut f64,
261 pazi1: &mut f64,
262 pazi2: &mut f64,
263) {
264 geod_geninverse(
265 g, lat1, lon1, lat2, lon2, ps12, pazi1, pazi2, &mut 0.0, &mut 0.0, &mut 0.0, &mut 0.0,
266 );
267}
268
269#[allow(clippy::too_many_arguments)]
271pub fn geod_gendirect(
272 g: &mut GeodGeodesic,
273 lat1: f64,
274 lon1: f64,
275 azi1: f64,
276 flags: u32,
277 s12_a12: f64,
278 plat2: &mut f64,
279 plon2: &mut f64,
280 pazi2: &mut f64,
281 ps12: &mut f64,
282 pm12: &mut f64,
283 p_m12: &mut f64,
284 p_m21: &mut f64,
285 p_s12: &mut f64,
286) -> f64 {
287 let mut l = GeodGeodesicline::default();
288 let outmask: u32 =
289 (if *plat2 != 0. { GeodMask::GeodLatitude as u32 } else { GeodMask::GeodNone as u32 })
290 | (if *plon2 != 0. {
291 GeodMask::GeodLongitude as u32
292 } else {
293 GeodMask::GeodNone as u32
294 })
295 | (if *pazi2 != 0. { GeodMask::GeodAzimuth as u32 } else { GeodMask::GeodNone as u32 })
296 | (if *ps12 != 0. { GeodMask::GeodDistance as u32 } else { GeodMask::GeodNone as u32 })
297 | (if *pm12 != 0. {
298 GeodMask::GeodReducedlength as u32
299 } else {
300 GeodMask::GeodNone as u32
301 })
302 | (if *p_m12 != 0. || *p_m21 != 0. {
303 GeodMask::GeodGeodesicScale as u32
304 } else {
305 GeodMask::GeodNone as u32
306 })
307 | (if *p_s12 != 0. { GeodMask::GeodArea as u32 } else { GeodMask::GeodNone as u32 });
308
309 geod_lineinit(
310 &mut l,
311 g,
312 lat1,
313 lon1,
314 azi1,
315 outmask
317 | (if flags & (GeodFlags::GeodArcmode as u32) != 0 {
318 GeodMask::GeodNone as u32
319 } else {
320 GeodMask::GeodDistanceIn as u32
321 }),
322 );
323 geod_genposition(&l, flags, s12_a12, plat2, plon2, pazi2, ps12, pm12, p_m12, p_m21, p_s12)
324}
325
326pub fn geod_lineinit(
328 l: &mut GeodGeodesicline,
329 g: &GeodGeodesic,
330 lat1: f64,
331 lon1: f64,
332 mut azi1: f64,
333 caps: u32,
334) {
335 let mut salp1 = 0.0;
337 let mut calp1 = 0.0;
338 azi1 = ang_normalize(azi1);
339 sincosdx(ang_round(azi1), &mut salp1, &mut calp1);
341 geod_lineinit_int(l, g, lat1, lon1, azi1, salp1, calp1, caps);
342}
343
344#[allow(clippy::too_many_arguments)]
346pub fn geod_direct(
347 g: &mut GeodGeodesic,
348 lat1: f64,
349 lon1: f64,
350 azi1: f64,
351 s12: f64,
352 plat2: &mut f64,
353 plon2: &mut f64,
354 pazi2: &mut f64,
355) {
356 geod_gendirect(
357 g,
358 lat1,
359 lon1,
360 azi1,
361 GeodFlags::GeodNoflags as u32,
362 s12,
363 plat2,
364 plon2,
365 pazi2,
366 &mut 0.0,
367 &mut 0.0,
368 &mut 0.0,
369 &mut 0.0,
370 &mut 0.0,
371 );
372}
373
374fn a3coeff(g: &mut GeodGeodesic) {
376 let coeff: [f64; 18] = [
377 -3., 128., -2., -3., 64., -1., -3., -1., 16., 3., -1., -2., 8., 1., -1., 2., 1., 1.,
384 ];
385 let mut o = 0;
386 for (k, j) in (0..N_A3).rev().enumerate() {
388 let m = if N_A3 - j - 1 < j { N_A3 - j - 1 } else { j }; g.a3x[k] = polyvalx(m, &coeff[o..], g.n) / coeff[o + m + 1];
390 o += m + 2;
391 }
392}
393
394fn c3coeff(g: &mut GeodGeodesic) {
396 let coeff: [f64; 45] = [
397 3., 128., 2., 5., 128., -1., 3., 3., 64., -1., 0., 1., 8., -1., 1., 4., 5., 256., 1., 3., 128., -3., -2., 3., 64., 1., -3., 2., 32., 7., 512., -10., 9., 384., 5., -9., 5., 192., 7., 512., -14., 7., 512., 21., 2560.,
413 ];
414 let mut o = 0;
415 let mut k = 0;
416 for l in 1..N_C3 {
418 for j in (l..N_C3).rev() {
420 let m = if N_C3 - j - 1 < j { N_C3 - j - 1 } else { j }; g.c3x[k] = polyvalx(m, &coeff[o..], g.n) / coeff[o + m + 1];
422 k += 1;
423 o += m + 2;
424 }
425 }
426}
427
428fn c4coeff(g: &mut GeodGeodesic) {
430 let coeff: [f64; 77] = [
431 97., 15015., 1088., 156., 45045., -224., -4784., 1573., 45045.,
435 -10656., 14144., -4576., -858., 45045.,
437 64., 624., -4576., 6864., -3003., 15015.,
439 100., 208., 572., 3432., -12012., 30030., 45045.,
441 1., 9009., -2944., 468., 135135., 5792., 1040., -1287., 135135.,
445 5952., -11648., 9152., -2574., 135135.,
447 -64., -624., 4576., -6864., 3003., 135135.,
449 8., 10725., 1856., -936., 225225., -8448., 4992., -1144., 225225.,
453 -1440., 4160., -4576., 1716., 225225.,
455 -136., 63063., 1024., -208., 105105., 3584., -3328., 1144., 315315.,
459 -128., 135135., -2560., 832., 405405., 128., 99099.,
463 ];
464 let mut o = 0;
465 let mut k = 0;
466 for l in 0..N_C4 {
468 for j in (l..N_C4).rev() {
470 let m = N_C4 - j - 1; g.c4x[k] = polyvalx(m, &coeff[o..], g.n) / coeff[o + m + 1];
472 k += 1;
473 o += m + 2;
474 }
475 }
476}
477
478pub fn polyvalx(n: usize, p: &[f64], x: f64) -> f64 {
480 if n == 0 {
481 return 0.0;
482 }
483 let mut y = p[0];
484 for val in p.iter().take(n).skip(1) {
485 y = y * x + val;
486 }
487
488 y
489}
490
491pub fn ang_normalize(x: f64) -> f64 {
493 let y = x % TD;
494 if fabs(y) == HD {
495 return copysign(HD, x);
496 }
497 y
498}
499
500pub fn ang_round(x: f64) -> f64 {
502 let z = 1.0 / 16.0;
504 let mut y = fabs(x);
505 let w = z - y;
506 y = if w > 0. { z - w } else { y };
508 copysign(y, x)
509}
510
511pub fn ang_diff(x: f64, y: f64, e: &mut f64) -> f64 {
513 let mut t = 0.;
516 let mut d = sumx(remainder(-x, TD), remainder(y, TD), &mut t);
517 d = sumx(remainder(d, TD), t, &mut t);
520 if d == 0. || fabs(d) == HD {
522 d = copysign(d, if t == 0. { y - x } else { -t });
525 }
526 if *e != 0. {
527 *e = t;
528 }
529 d
530}
531
532pub fn sincosdx(x: f64, sinx: &mut f64, cosx: &mut f64) {
535 let mut r = remquo(x, QD);
536 r.0 *= DEGREE;
537 let s = sin(r.0);
539 let c = cos(r.0);
540 match r.1 & 3 {
541 0 => {
542 *sinx = s;
543 *cosx = c;
544 }
545 1 => {
546 *sinx = c;
547 *cosx = -s;
548 }
549 2 => {
550 *sinx = -s;
551 *cosx = -c;
552 }
553 _ => {
554 *sinx = -c;
555 *cosx = s;
556 }
557 }
558 *cosx += 0.; if *sinx == 0. {
562 *sinx = copysign(*sinx, x);
563 }
564}
565
566pub fn lat_fix(x: f64) -> f64 {
568 if fabs(x) > QD { f64::NAN } else { x }
569}
570
571pub fn swapx(x: &mut f64, y: &mut f64) {
573 core::mem::swap(&mut (*x), &mut (*y));
574}
575
576pub fn norm2(sinx: &mut f64, cosx: &mut f64) {
578 let r = hypot(*sinx, *cosx);
579 *sinx /= r;
580 *cosx /= r;
581}
582
583pub fn sin_cos_series(sinp: bool, sinx: f64, cosx: f64, c: &[f64], mut n: usize) -> f64 {
592 let mut c_index = if sinp { 1 } else { 0 } + n - 1;
594 let ar = 2. * (cosx - sinx) * (cosx + sinx); c_index -= 1;
596 let mut y0 = if n & 1 != 0 { c[c_index] } else { 0. };
597 let mut y1 = 0.;
598 n /= 2;
600 for _ in 0..n {
601 c_index -= 1;
603 y1 = ar * y0 - y1 + c[c_index];
604 c_index -= 1;
605 y0 = ar * y1 - y0 + c[c_index];
606 }
607
608 if sinp
609 { 2. * sinx * cosx * y0 } else { cosx * (y0 - y1) } }
612
613pub fn a1m1f(eps: f64) -> f64 {
615 let coeff: [f64; 5] = [1., 4., 64., 0., 256.];
617 let m = N_A1 / 2;
618 let t = polyvalx(m, &coeff, sq(eps)) / coeff[m + 1];
619 (t + eps) / (1. - eps)
620}
621
622pub fn atan2dx(mut y: f64, mut x: f64) -> f64 {
627 let mut q = 0;
628 if fabs(y) > fabs(x) {
629 swapx(&mut x, &mut y);
630 q = 2;
631 }
632 if x.is_sign_negative() {
633 x = -x;
634 q += 1;
635 }
636 let mut ang = atan2(y, x) / DEGREE;
638 match q {
639 1 => {
640 ang = copysign(HD, y) - ang;
641 }
642 2 => {
643 ang = QD - ang;
644 }
645 3 => {
646 ang += -QD;
647 }
648 _ => {}
649 }
650 ang
651}
652
653#[allow(clippy::too_many_arguments)]
655pub fn geod_geninverse(
656 geod_geodesic: &mut GeodGeodesic,
657 lat1: f64,
658 lon1: f64,
659 lat2: f64,
660 lon2: f64,
661 ps12: &mut f64,
662 pazi1: &mut f64,
663 pazi2: &mut f64,
664 pm12: &mut f64,
665 p_m12: &mut f64,
666 p_m21: &mut f64,
667 p_s12: &mut f64,
668) -> f64 {
669 let mut salp1 = 0.0;
670 let mut calp1 = 0.0;
671 let mut salp2 = 0.0;
672 let mut calp2 = 0.0;
673 let a12 = geod_geninverse_int(
674 geod_geodesic,
675 lat1,
676 lon1,
677 lat2,
678 lon2,
679 ps12,
680 &mut salp1,
681 &mut calp1,
682 &mut salp2,
683 &mut calp2,
684 pm12,
685 p_m12,
686 p_m21,
687 p_s12,
688 );
689 if *pazi1 != 0. {
690 *pazi1 = atan2dx(salp1, calp1);
691 }
692 if *pazi2 != 0. {
693 *pazi2 = atan2dx(salp2, calp2);
694 }
695 a12
696}
697
698#[allow(clippy::too_many_arguments)]
700pub fn geod_geninverse_int(
701 g: &mut GeodGeodesic,
702 mut lat1: f64,
703 lon1: f64,
704 mut lat2: f64,
705 lon2: f64,
706 ps12: &mut f64,
707 psalp1: &mut f64,
708 pcalp1: &mut f64,
709 psalp2: &mut f64,
710 pcalp2: &mut f64,
711 pm12: &mut f64,
712 p_m12: &mut f64,
713 p_m21: &mut f64,
714 p_s12: &mut f64,
715) -> f64 {
716 let mut s12 = 0.0;
718 let mut m12 = 0.0;
719 let mut _m12: f64 = 0.0;
720 let mut _m21 = 0.0;
721 let mut _s12 = 0.0;
722 let mut lon12s = 0.0;
724 let mut sbet1 = 0.0;
727 let mut cbet1 = 0.0;
728 let mut sbet2 = 0.0;
729 let mut cbet2 = 0.0;
730 let mut s12x = 0.0;
731 let mut m12x = 0.0;
732 let mut slam12 = 0.0;
734 let mut clam12 = 0.0;
735 let mut a12 = 0.0;
737 let mut sig12;
738 let mut calp1 = 0.0;
739 let mut salp1 = 0.0;
740 let mut calp2 = 0.0;
741 let mut salp2 = 0.0;
742 let mut ca = [0.0; N_C];
744 let mut omg12 = 0.;
747 let mut somg12 = 2.;
748 let mut comg12 = 0.;
749
750 let mut outmask: u32 =
751 (if *ps12 != 0. { GeodMask::GeodDistance as u32 } else { GeodMask::GeodNone as u32 })
752 | (if *pm12 != 0. {
753 GeodMask::GeodReducedlength as u32
754 } else {
755 GeodMask::GeodNone as u32
756 })
757 | (if *p_m12 != 0. || *p_m21 != 0. {
758 GeodMask::GeodGeodesicScale as u32
759 } else {
760 GeodMask::GeodNone as u32
761 })
762 | (if *p_s12 != 0. {
763 GeodFlags::GeodLongUnroll as u32
764 } else {
765 GeodMask::GeodNone as u32
766 });
767
768 outmask &= CapType::OutAll as u32;
769 let mut lon12 = ang_diff(lon1, lon2, &mut lon12s);
773 let mut lonsign = if lon12.is_sign_positive() { -1. } else { 1. };
775 lon12 *= lonsign;
776 lon12s *= lonsign;
777 let lam12 = lon12 * DEGREE;
778 sincosde(lon12, lon12s, &mut slam12, &mut clam12);
780 lon12s = (HD - lon12) - lon12s; lat1 = ang_round(lat_fix(lat1));
784 lat2 = ang_round(lat_fix(lat2));
785 let swapp = if fabs(lat1) < fabs(lat2) { -1. } else { 1. };
788 if swapp < 0. {
789 lonsign *= -1.;
790 swapx(&mut lat1, &mut lat2);
791 }
792 let latsign = if lat1.is_sign_positive() { 1. } else { -1. };
794 lat1 *= latsign;
795 lat2 *= latsign;
796 sincosdx(lat1, &mut sbet1, &mut cbet1);
809 sbet1 *= g.f1;
810 norm2(&mut sbet1, &mut cbet1);
812 cbet1 = fmax(TINY, cbet1);
813
814 sincosdx(lat2, &mut sbet2, &mut cbet2);
815 sbet2 *= g.f1;
816 norm2(&mut sbet2, &mut cbet2);
818 cbet2 = fmax(TINY, cbet2);
819
820 if cbet1 < -sbet1 {
829 if cbet2 == cbet1 {
830 sbet2 = copysign(sbet1, sbet2);
831 }
832 } else if fabs(sbet2) == -sbet1 {
833 cbet2 = cbet1;
834 }
835
836 let dn1 = sqrt(1. + g.ep2 * sq(sbet1));
837 let dn2 = sqrt(1. + g.ep2 * sq(sbet2));
838
839 let mut meridian = lat1 == -QD || slam12 == 0.;
840
841 if meridian {
842 calp1 = clam12;
846 salp1 = slam12; calp2 = 1.;
848 salp2 = 0.; let ssig1 = sbet1;
851 let csig1 = calp1 * cbet1;
852 let ssig2 = sbet2;
853 let csig2 = calp2 * cbet2;
854
855 sig12 = atan2(fmax(0.0, csig1 * ssig2 - ssig1 * csig2) + 0., csig1 * csig2 + ssig1 * ssig2);
857 let mut _tmp_m12 = 0.;
858 let mut _tmp_m21 = 0.;
859 lengths(
860 g,
861 g.n,
862 sig12,
863 ssig1,
864 csig1,
865 dn1,
866 ssig2,
867 csig2,
868 dn2,
869 cbet1,
870 cbet2,
871 &mut s12x,
872 &mut m12x,
873 &mut 0.,
874 if (outmask & GeodMask::GeodGeodesicScale as u32) != 0 {
875 &mut _m12
876 } else {
877 &mut _tmp_m12
878 },
879 if (outmask & GeodMask::GeodGeodesicScale as u32) != 0 {
880 &mut _m21
881 } else {
882 &mut _tmp_m21
883 },
884 &mut ca,
885 );
886 if sig12 < 1. || m12x >= 0. {
894 if sig12 < 3. * TINY ||
896 (sig12 < TOL0 && (s12x < 0. || m12x < 0.))
898 {
899 s12x = 0.;
900 m12x = s12x;
901 sig12 = m12x;
902 }
903 m12x *= g.b;
904 s12x *= g.b;
905 a12 = sig12 / DEGREE;
906 } else {
907 meridian = false;
909 }
910 }
911
912 if !meridian &&
913 sbet1 == 0. && (g.f <= 0. || lon12s >= g.f * HD)
916 {
917 calp2 = 0.;
919 calp1 = calp2;
920 salp2 = 1.;
921 salp1 = salp2;
922 s12x = g.a * lam12;
923 omg12 = lam12 / g.f1;
924 sig12 = omg12;
925 m12x = g.b * sin(sig12);
926 if (outmask & GeodMask::GeodGeodesicScale as u32) != 0 {
927 _m21 = cos(sig12);
928 _m12 = _m21;
929 }
930 a12 = lon12 / g.f1;
931 } else if !meridian {
932 let mut dnm = 0.;
937 sig12 = inverse_start(
938 g, sbet1, cbet1, dn1, sbet2, cbet2, dn2, lam12, slam12, clam12, &mut salp1, &mut calp1,
939 &mut salp2, &mut calp2, &mut dnm, &mut ca,
940 );
941
942 if sig12 >= 0. {
943 s12x = sig12 * g.b * dnm;
945 m12x = sq(dnm) * g.b * sin(sig12 / dnm);
946 if (outmask & GeodMask::GeodGeodesicScale as u32) != 0 {
947 _m21 = cos(sig12 / dnm);
948 _m12 = _m21;
949 }
950 a12 = sig12 / DEGREE;
951 omg12 = lam12 / (g.f1 * dnm);
952 } else {
953 let mut ssig1 = 0.;
965 let mut csig1 = 0.;
966 let mut ssig2 = 0.;
967 let mut csig2 = 0.;
968 let mut eps = 0.;
969 let mut domg12 = 0.;
970 let mut numit: u32 = 0;
971 let mut salp1a = TINY;
973 let mut calp1a = 1.;
974 let mut salp1b = TINY;
975 let mut calp1b = -1.;
976 let mut tripn = false;
977 let mut tripb = false;
978 loop {
980 numit += 1;
981 let mut dv = 0.;
984 let v = lambda12(
985 g,
986 sbet1,
987 cbet1,
988 dn1,
989 sbet2,
990 cbet2,
991 dn2,
992 salp1,
993 calp1,
994 slam12,
995 clam12,
996 &mut salp2,
997 &mut calp2,
998 &mut sig12,
999 &mut ssig1,
1000 &mut csig1,
1001 &mut ssig2,
1002 &mut csig2,
1003 &mut eps,
1004 &mut domg12,
1005 numit < MAXIT1,
1006 &mut dv,
1007 &mut ca,
1008 );
1009 if tripb ||
1010 (fabs(v) < (if tripn { 8. } else { 1. }) * TOL0) ||
1012 numit == MAXIT2
1014 {
1015 break;
1016 }
1017 if v > 0. && (numit > MAXIT1 || calp1 / salp1 > calp1b / salp1b) {
1019 salp1b = salp1;
1020 calp1b = calp1;
1021 } else if v < 0. && (numit > MAXIT1 || calp1 / salp1 < calp1a / salp1a) {
1022 salp1a = salp1;
1023 calp1a = calp1;
1024 }
1025 if numit < MAXIT1 && dv > 0. {
1026 let dalp1 = -v / dv;
1027 if fabs(dalp1) < PI {
1028 let sdalp1 = sin(dalp1);
1029 let cdalp1 = cos(dalp1);
1030 let nsalp1 = salp1 * cdalp1 + calp1 * sdalp1;
1031 if nsalp1 > 0. {
1032 calp1 = calp1 * cdalp1 - salp1 * sdalp1;
1033 salp1 = nsalp1;
1034 norm2(&mut salp1, &mut calp1);
1035 tripn = fabs(v) <= 16. * TOL0;
1039 continue;
1040 }
1041 }
1042 }
1043 salp1 = (salp1a + salp1b) / 2.;
1052 calp1 = (calp1a + calp1b) / 2.;
1053 norm2(&mut salp1, &mut calp1);
1054 tripn = false;
1055 tripb = fabs(salp1a - salp1) + (calp1a - calp1) < TOLB
1056 || fabs(salp1 - salp1b) + (calp1 - calp1b) < TOLB;
1057 }
1058 let mut _tmp_m12 = 0.;
1059 let mut _tmp_m21 = 0.;
1060 lengths(
1061 g,
1062 eps,
1063 sig12,
1064 ssig1,
1065 csig1,
1066 dn1,
1067 ssig2,
1068 csig2,
1069 dn2,
1070 cbet1,
1071 cbet2,
1072 &mut s12x,
1073 &mut m12x,
1074 &mut 0.,
1075 if (outmask & GeodMask::GeodGeodesicScale as u32) != 0 {
1076 &mut _m12
1077 } else {
1078 &mut _tmp_m12
1079 },
1080 if (outmask & GeodMask::GeodGeodesicScale as u32) != 0 {
1081 &mut _m21
1082 } else {
1083 &mut _tmp_m21
1084 },
1085 &mut ca,
1086 );
1087 m12x *= g.b;
1088 s12x *= g.b;
1089 a12 = sig12 / DEGREE;
1090 if (outmask & GeodFlags::GeodLongUnroll as u32) != 0 {
1091 let sdomg12 = sin(domg12);
1093 let cdomg12 = cos(domg12);
1094 somg12 = slam12 * cdomg12 - clam12 * sdomg12;
1095 comg12 = clam12 * cdomg12 + slam12 * sdomg12;
1096 }
1097 }
1098 }
1099
1100 if (outmask & GeodMask::GeodDistance as u32) != 0 {
1101 s12 = 0. + s12x; }
1103
1104 if (outmask & GeodMask::GeodReducedlength as u32) != 0 {
1105 m12 = 0. + m12x; }
1107
1108 if (outmask & GeodFlags::GeodLongUnroll as u32) != 0 {
1109 let
1110 salp0 = salp1 * cbet1;
1112 let calp0 = hypot(calp1, salp1 * sbet1); let alp12;
1114 if calp0 != 0. && salp0 != 0. {
1115 let mut ssig1 = sbet1;
1117 let mut csig1 = calp1 * cbet1;
1118 let mut ssig2 = sbet2;
1119 let mut csig2 = calp2 * cbet2;
1120 let k2 = sq(calp0) * g.ep2;
1121 let eps = k2 / (2. * (1. + sqrt(1. + k2)) + k2);
1122 let a4 = sq(g.a) * calp0 * salp0 * g.e2;
1124
1125 norm2(&mut ssig1, &mut csig1);
1126 norm2(&mut ssig2, &mut csig2);
1127 c4f(g, eps, &mut ca);
1128 let b41 = sin_cos_series(false, ssig1, csig1, &ca, N_C4);
1129 let b42 = sin_cos_series(false, ssig2, csig2, &ca, N_C4);
1130 _s12 = a4 * (b42 - b41);
1131 } else {
1132 _s12 = 0.;
1134 }
1135
1136 if !meridian && somg12 == 2. {
1137 somg12 = sin(omg12);
1138 comg12 = cos(omg12);
1139 }
1140
1141 if !meridian &&
1142 comg12 > FRAC_1_SQRT_2 && sbet2 - sbet1 < 1.75
1145 {
1146 let domg12 = 1. + comg12;
1151 let dbet1 = 1. + cbet1;
1152 let dbet2 = 1. + cbet2;
1153 alp12 = 2.
1154 * atan2(
1155 somg12 * (sbet1 * dbet2 + sbet2 * dbet1),
1156 domg12 * (sbet1 * sbet2 + dbet1 * dbet2),
1157 );
1158 } else {
1159 let mut salp12 = salp2 * calp1 - calp2 * salp1;
1161 let mut calp12 = calp2 * calp1 + salp2 * salp1;
1162 if salp12 == 0. && calp12 < 0. {
1167 salp12 = TINY * calp1;
1168 calp12 = -1.;
1169 }
1170 alp12 = atan2(salp12, calp12);
1171 }
1172 _s12 += g.c2 * alp12;
1173 _s12 *= swapp * lonsign * latsign;
1174 _s12 += 0.;
1176 }
1177
1178 if swapp < 0. {
1180 swapx(&mut salp1, &mut salp2);
1181 swapx(&mut calp1, &mut calp2);
1182 if (outmask & GeodMask::GeodGeodesicScale as u32) != 0 {
1183 swapx(&mut _m12, &mut _m21);
1184 }
1185 }
1186
1187 salp1 *= swapp * lonsign;
1188 calp1 *= swapp * latsign;
1189 salp2 *= swapp * lonsign;
1190 calp2 *= swapp * latsign;
1191
1192 if *psalp1 != 0. {
1193 *psalp1 = salp1;
1194 }
1195 if *pcalp1 != 0. {
1196 *pcalp1 = calp1;
1197 }
1198 if *psalp2 != 0. {
1199 *psalp2 = salp2;
1200 }
1201 if *pcalp2 != 0. {
1202 *pcalp2 = calp2;
1203 }
1204
1205 if (outmask & GeodMask::GeodDistance as u32) != 0 {
1206 *ps12 = s12;
1207 }
1208 if (outmask & GeodMask::GeodReducedlength as u32) != 0 {
1209 *pm12 = m12;
1210 }
1211 if (outmask & GeodMask::GeodGeodesicScale as u32) != 0 {
1212 if *p_m12 != 0. {
1213 *p_m12 = _m12;
1214 }
1215 if *p_m21 != 0. {
1216 *p_m21 = _m21;
1217 }
1218 }
1219 if (outmask & GeodFlags::GeodLongUnroll as u32) != 0 {
1220 *p_s12 = _s12;
1221 }
1222
1223 a12
1225}
1226
1227#[allow(clippy::too_many_arguments)]
1229pub fn geod_genposition(
1230 l: &GeodGeodesicline,
1231 flags: u32,
1232 s12_a12: f64,
1233 plat2: &mut f64,
1234 plon2: &mut f64,
1235 pazi2: &mut f64,
1236 ps12: &mut f64,
1237 pm12: &mut f64,
1238 p_m12: &mut f64,
1239 p_m21: &mut f64,
1240 p_s12: &mut f64,
1241) -> f64 {
1242 let mut lat2 = 0.0;
1243 let mut lon2 = 0.0;
1244 let mut azi2 = 0.0;
1245 let mut s12 = 0.0;
1246 let mut m12 = 0.0;
1247 let mut _m12 = 0.0;
1248 let mut _m21 = 0.0;
1249 let mut _s12 = 0.0;
1250 let mut sig12;
1252 let mut ssig12 = 0.;
1253 let mut csig12 = 0.;
1254 let mut b12 = 0.;
1255 let mut ab1 = 0.;
1256 let omg12;
1257 let lam12;
1258 let lon12;
1259 let mut ssig2;
1260 let mut csig2;
1261 let mut cbet2;
1262 let somg2;
1263 let comg2;
1264
1265 let mut outmask: u32 =
1266 (if *plat2 != 0. { GeodMask::GeodLatitude as u32 } else { GeodMask::GeodNone as u32 })
1267 | (if *plon2 != 0. {
1268 GeodMask::GeodLongitude as u32
1269 } else {
1270 GeodMask::GeodNone as u32
1271 })
1272 | (if *pazi2 != 0. { GeodMask::GeodAzimuth as u32 } else { GeodMask::GeodNone as u32 })
1273 | (if *ps12 != 0. { GeodMask::GeodDistance as u32 } else { GeodMask::GeodNone as u32 })
1274 | (if *pm12 != 0. {
1275 GeodMask::GeodReducedlength as u32
1276 } else {
1277 GeodMask::GeodNone as u32
1278 })
1279 | (if *p_m12 != 0. || *p_m21 != 0. {
1280 GeodMask::GeodGeodesicScale as u32
1281 } else {
1282 GeodMask::GeodNone as u32
1283 })
1284 | (if *p_s12 != 0. { GeodMask::GeodArea as u32 } else { GeodMask::GeodNone as u32 });
1285
1286 outmask &= l.caps & CapType::OutAll as u32;
1287 if !(((flags & GeodFlags::GeodArcmode as u32) != 0)
1288 || (l.caps & (GeodMask::GeodDistanceIn as u32 & CapType::OutAll as u32)) != 0)
1289 {
1290 return f64::NAN;
1292 }
1293
1294 if (flags & GeodFlags::GeodArcmode as u32) != 0 {
1295 sig12 = s12_a12 * DEGREE;
1297 sincosdx(s12_a12, &mut ssig12, &mut csig12);
1298 } else {
1299 let tau12 = s12_a12 / (l.b * (1. + l.a1m1));
1301 let s = sin(tau12);
1302 let c = cos(tau12);
1303 b12 = -sin_cos_series(
1305 true,
1306 l.stau1 * c + l.ctau1 * s,
1307 l.ctau1 * c - l.stau1 * s,
1308 &l.c1pa,
1309 N_C1_P,
1310 );
1311 sig12 = tau12 - (b12 - l.b11);
1312 ssig12 = sin(sig12);
1313 csig12 = cos(sig12);
1314 if fabs(l.f) > 0.01 {
1315 ssig2 = l.ssig1 * csig12 + l.csig1 * ssig12;
1338 csig2 = l.csig1 * csig12 - l.ssig1 * ssig12;
1339 b12 = sin_cos_series(true, ssig2, csig2, &l.c1a, N_C1);
1340 let serr = (1. + l.a1m1) * (sig12 + (b12 - l.b11)) - s12_a12 / l.b;
1341 sig12 -= serr / sqrt(1. + l.k2 * sq(ssig2));
1342 ssig12 = sin(sig12);
1343 csig12 = cos(sig12);
1344 }
1346 }
1347
1348 ssig2 = l.ssig1 * csig12 + l.csig1 * ssig12;
1350 csig2 = l.csig1 * csig12 - l.ssig1 * ssig12;
1351 let dn2 = sqrt(1. + l.k2 * sq(ssig2));
1352 if (outmask
1353 & (GeodMask::GeodDistance as u32
1354 | GeodMask::GeodReducedlength as u32
1355 | GeodMask::GeodGeodesicScale as u32))
1356 != 0
1357 {
1358 if (flags & GeodFlags::GeodArcmode as u32 != 0) || fabs(l.f) > 0.01 {
1359 b12 = sin_cos_series(true, ssig2, csig2, &l.c1a, N_C1);
1360 }
1361 ab1 = (1. + l.a1m1) * (b12 - l.b11);
1362 }
1363 let sbet2 = l.calp0 * ssig2;
1365 cbet2 = hypot(l.salp0, l.calp0 * csig2);
1367 if cbet2 == 0. {
1368 csig2 = TINY;
1370 cbet2 = csig2;
1371 }
1372 let salp2 = l.salp0;
1374 let calp2 = l.calp0 * csig2; if (outmask & GeodMask::GeodDistance as u32) != 0 {
1377 s12 = if (flags & GeodFlags::GeodArcmode as u32) != 0 {
1378 l.b * ((1. + l.a1m1) * sig12 + ab1)
1379 } else {
1380 s12_a12
1381 };
1382 }
1383
1384 if (outmask & GeodMask::GeodLongitude as u32) != 0 {
1385 let e = copysign(1., l.salp0); somg2 = l.salp0 * ssig2;
1388 comg2 = csig2; omg12 = if (flags & GeodFlags::GeodLongUnroll as u32) != 0 {
1391 e * (sig12 - (atan2(ssig2, csig2) - atan2(l.ssig1, l.csig1))
1392 + (atan2(e * somg2, comg2) - atan2(e * l.somg1, l.comg1)))
1393 } else {
1394 atan2(somg2 * l.comg1 - comg2 * l.somg1, comg2 * l.comg1 + somg2 * l.somg1)
1395 };
1396 lam12 = omg12
1397 + l.a3c * (sig12 + (sin_cos_series(true, ssig2, csig2, &l.c3a, N_C3 - 1) - l.b31));
1398 lon12 = lam12 / DEGREE;
1399 lon2 = if (flags & GeodFlags::GeodLongUnroll as u32) != 0 {
1400 l.lon1 + lon12
1401 } else {
1402 ang_normalize(ang_normalize(l.lon1) + ang_normalize(lon12))
1403 };
1404 }
1405
1406 if (outmask & GeodMask::GeodLatitude as u32) != 0 {
1407 lat2 = atan2dx(sbet2, l.f1 * cbet2);
1408 }
1409
1410 if (outmask & GeodMask::GeodAzimuth as u32) != 0 {
1411 azi2 = atan2dx(salp2, calp2);
1412 }
1413
1414 if (outmask & (GeodMask::GeodReducedlength as u32 | GeodMask::GeodGeodesicScale as u32)) != 0 {
1415 let b22 = sin_cos_series(true, ssig2, csig2, &l.c2a, N_C2);
1416 let ab2 = (1. + l.a2m1) * (b22 - l.b21);
1417 let j12 = (l.a1m1 - l.a2m1) * sig12 + (ab1 - ab2);
1418 if (outmask & GeodMask::GeodReducedlength as u32) != 0 {
1419 m12 = l.b
1422 * ((dn2 * (l.csig1 * ssig2) - l.dn1 * (l.ssig1 * csig2)) - l.csig1 * csig2 * j12);
1423 }
1424 if (outmask & GeodMask::GeodGeodesicScale as u32) != 0 {
1425 let t = l.k2 * (ssig2 - l.ssig1) * (ssig2 + l.ssig1) / (l.dn1 + dn2);
1426 _m12 = csig12 + (t * ssig2 - csig2 * j12) * l.ssig1 / l.dn1;
1427 _m21 = csig12 - (t * l.ssig1 - l.csig1 * j12) * ssig2 / dn2;
1428 }
1429 }
1430
1431 if (outmask & GeodFlags::GeodLongUnroll as u32) != 0 {
1432 let b42 = sin_cos_series(false, ssig2, csig2, &l.c4a, N_C4);
1433 let salp12;
1434 let calp12;
1435 if l.calp0 == 0. || l.salp0 == 0. {
1436 salp12 = salp2 * l.calp1 - calp2 * l.salp1;
1438 calp12 = calp2 * l.calp1 + salp2 * l.salp1;
1439 } else {
1440 salp12 = l.calp0
1449 * l.salp0
1450 * (if csig12 <= 0. {
1451 l.csig1 * (1. - csig12) + ssig12 * l.ssig1
1452 } else {
1453 ssig12 * (l.csig1 * ssig12 / (1. + csig12) + l.ssig1)
1454 });
1455 calp12 = sq(l.salp0) + sq(l.calp0) * l.csig1 * csig2;
1456 }
1457 _s12 = l.c2 * atan2(salp12, calp12) + l.a4 * (b42 - l.b41);
1458 }
1459
1460 if ((outmask & GeodMask::GeodLatitude as u32) != 0) && *plat2 != 0. {
1468 *plat2 = lat2;
1469 }
1470 if ((outmask & GeodMask::GeodLongitude as u32) != 0) && *plon2 != 0. {
1471 *plon2 = lon2;
1472 }
1473 if ((outmask & GeodMask::GeodAzimuth as u32) != 0) && *pazi2 != 0. {
1474 *pazi2 = azi2;
1475 }
1476 if (outmask & GeodMask::GeodDistance as u32 != 0) && *ps12 != 0. {
1477 *ps12 = s12;
1478 }
1479 if ((outmask & GeodMask::GeodReducedlength as u32) != 0) && *pm12 != 0. {
1480 *pm12 = m12;
1481 }
1482 if (outmask & GeodMask::GeodGeodesicScale as u32) != 0 {
1483 if *p_m12 != 0. {
1484 *p_m12 = _m12;
1485 }
1486 if *p_m21 != 0. {
1487 *p_m21 = _m21;
1488 }
1489 }
1490 if ((outmask & GeodFlags::GeodLongUnroll as u32) != 0) && *p_s12 != 0. {
1491 *p_s12 = _s12;
1492 }
1493
1494 if (flags & GeodFlags::GeodArcmode as u32) != 0 { s12_a12 } else { sig12 / DEGREE }
1495}
1496
1497#[allow(clippy::too_many_arguments)]
1499pub fn geod_lineinit_int(
1500 l: &mut GeodGeodesicline,
1501 g: &GeodGeodesic,
1502 lat1: f64,
1503 lon1: f64,
1504 azi1: f64,
1505 salp1: f64,
1506 calp1: f64,
1507 caps: u32,
1508) {
1509 let mut cbet1 = 0.0;
1510 let mut sbet1 = 0.0;
1511 l.a = g.a;
1512 l.f = g.f;
1513 l.b = g.b;
1514 l.c2 = g.c2;
1515 l.f1 = g.f1;
1516 l.caps = (if caps != 0 { caps } else { GeodMask::GeodDistanceIn as u32 | GeodMask::GeodLongitude as u32 }) |
1518 (GeodMask::GeodLatitude as u32 | GeodMask::GeodAzimuth as u32 | GeodFlags::GeodLongUnroll as u32);
1520
1521 l.lat1 = lat_fix(lat1);
1522 l.lon1 = lon1;
1523 l.azi1 = azi1;
1524 l.salp1 = salp1;
1525 l.calp1 = calp1;
1526
1527 sincosdx(ang_round(l.lat1), &mut sbet1, &mut cbet1);
1528 sbet1 *= l.f1;
1529 norm2(&mut sbet1, &mut cbet1);
1531 cbet1 = fmax(TINY, cbet1);
1532 l.dn1 = sqrt(1. + g.ep2 * sq(sbet1));
1533
1534 l.salp0 = l.salp1 * cbet1; l.calp0 = hypot(l.calp1, l.salp1 * sbet1);
1539 l.ssig1 = sbet1;
1549 l.somg1 = l.salp0 * sbet1;
1550 l.comg1 = sbet1;
1551 l.csig1 = if l.comg1 != 0. || l.calp1 != 0. { cbet1 * l.calp1 } else { 1. };
1552 norm2(&mut l.ssig1, &mut l.csig1); l.k2 = sq(l.calp0) * g.ep2;
1556 let eps = l.k2 / (2. * (1. + sqrt(1. + l.k2)) + l.k2);
1557
1558 if (l.caps & CapType::CapC1 as u32) != 0 {
1559 l.a1m1 = a1m1f(eps);
1560 c1f(eps, &mut l.c1a);
1561 l.b11 = sin_cos_series(true, l.ssig1, l.csig1, &l.c1a, N_C1);
1562 let s = sin(l.b11);
1563 let c = cos(l.b11);
1564 l.stau1 = l.ssig1 * c + l.csig1 * s;
1566 l.ctau1 = l.csig1 * c - l.ssig1 * s;
1567 }
1570
1571 if (l.caps & CapType::CapC1p as u32) != 0 {
1572 c1pf(eps, &mut l.c1pa);
1573 }
1574
1575 if (l.caps & CapType::CapC2 as u32) != 0 {
1576 l.a2m1 = a2m1f(eps);
1577 c2f(eps, &mut l.c2a);
1578 l.b21 = sin_cos_series(true, l.ssig1, l.csig1, &l.c2a, N_C2);
1579 }
1580
1581 if (l.caps & CapType::CapC3 as u32) != 0 {
1582 c3f(g, eps, &mut l.c3a);
1583 l.a3c = -l.f * l.salp0 * a3f(g, eps);
1584 l.b31 = sin_cos_series(true, l.ssig1, l.csig1, &l.c3a, N_C3 - 1);
1585 }
1586
1587 if (l.caps & CapType::CapC4 as u32) != 0 {
1588 c4f(g, eps, &mut l.c4a);
1589 l.a4 = sq(l.a) * l.calp0 * l.salp0 * g.e2;
1591 l.b41 = sin_cos_series(false, l.ssig1, l.csig1, &l.c4a, N_C4);
1592 }
1593
1594 l.s13 = f64::NAN;
1595 l.a13 = f64::NAN;
1596}
1597
1598fn a3f(g: &GeodGeodesic, eps: f64) -> f64 {
1600 polyvalx(N_A3 - 1, &g.a3x, eps)
1602}
1603
1604fn c3f(g: &GeodGeodesic, eps: f64, c: &mut [f64]) {
1606 let mut mult = 1.;
1608 let mut o = 0;
1609 #[allow(clippy::needless_range_loop)]
1610 for l in 1..N_C3 {
1611 let m = N_C3 - l - 1; mult *= eps;
1614 c[l] = mult * polyvalx(m, &g.c3x[o..], eps);
1615 o += m + 1;
1616 }
1617}
1618
1619fn c4f(g: &GeodGeodesic, eps: f64, c: &mut [f64]) {
1621 let mut mult = 1.;
1623 let mut o = 0;
1624 #[allow(clippy::needless_range_loop)]
1625 for l in 0..N_C4 {
1626 let m = N_C4 - l - 1; c[l] = mult * polyvalx(m, &g.c4x[o..], eps);
1629 o += m + 1;
1630 mult *= eps;
1631 }
1632}
1633
1634fn c1f(eps: f64, c: &mut [f64]) {
1636 let coeff: [f64; 18] = [
1637 -1., 6., -16., 32., -9., 64., -128., 2048., 9., -16., 768., 3., -5., 512., -7., 1280., -7., 2048.,
1644 ];
1645 let eps2 = sq(eps);
1646 let mut d = eps;
1647 let mut o = 0;
1648 #[allow(clippy::needless_range_loop)]
1649 for l in 1..=N_C1 {
1650 let m = (N_C1 - l) / 2; c[l] = d * polyvalx(m, &coeff[o..], eps2) / coeff[o + m + 1];
1653 o += m + 2;
1654 d *= eps;
1655 }
1656}
1657
1658fn c1pf(eps: f64, c: &mut [f64]) {
1660 let coeff: [f64; 18] = [
1661 205., -432., 768., 1536., 4005., -4736., 3840., 12288., -225., 116., 384., -7173., 2695., 7680., 3467., 7680., 38081., 61440.,
1668 ];
1669 let eps2 = sq(eps);
1670 let mut d = eps;
1671 let mut o = 0;
1672 #[allow(clippy::needless_range_loop)]
1673 for l in 1..=N_C1_P {
1674 let m = (N_C1_P - l) / 2; c[l] = d * polyvalx(m, &coeff[o..], eps2) / coeff[o + m + 1];
1677 o += m + 2;
1678 d *= eps;
1679 }
1680}
1681
1682fn a2m1f(eps: f64) -> f64 {
1684 let coeff: [f64; 5] = [-11., -28., -192., 0., 256.];
1686 let m = N_A2 / 2;
1687 let t = polyvalx(m, &coeff, sq(eps)) / coeff[m + 1];
1688 (t - eps) / (1. + eps)
1689}
1690
1691fn c2f(eps: f64, c: &mut [f64]) {
1693 let coeff: [f64; 18] = [
1694 1., 2., 16., 32., 35., 64., 384., 2048., 15., 80., 768., 7., 35., 512., 63., 1280., 77., 2048.,
1701 ];
1702 let eps2 = sq(eps);
1703 let mut d = eps;
1704 let mut o = 0;
1705 #[allow(clippy::needless_range_loop)]
1706 for l in 1..=N_C2 {
1707 let m = (N_C2 - l) / 2; c[l] = d * polyvalx(m, &coeff[o..], eps2) / coeff[o + m + 1];
1710 o += m + 2;
1711 d *= eps;
1712 }
1713}
1714
1715#[allow(clippy::too_many_arguments)]
1717pub fn inverse_start(
1718 g: &GeodGeodesic,
1719 sbet1: f64,
1720 cbet1: f64,
1721 dn1: f64,
1722 sbet2: f64,
1723 cbet2: f64,
1724 dn2: f64,
1725 lam12: f64,
1726 slam12: f64,
1727 clam12: f64,
1728 psalp1: &mut f64,
1729 pcalp1: &mut f64,
1730 psalp2: &mut f64,
1732 pcalp2: &mut f64,
1733 pdnm: &mut f64,
1735 ca: &mut [f64],
1737) -> f64 {
1738 let mut salp1;
1739 let mut calp1;
1740 let mut salp2 = 0.;
1741 let mut calp2 = 0.;
1742 let mut dnm = 0.;
1743
1744 let mut sig12 = -1.; let sbet12 = sbet2 * cbet1 - cbet2 * sbet1;
1750 let cbet12 = cbet2 * cbet1 + sbet2 * sbet1;
1751
1752 let shortline = cbet12 >= 0. && sbet12 < 0.5 && cbet2 * lam12 < 0.5;
1753 let mut somg12;
1754 let mut comg12;
1755
1756 let sbet12a = sbet2 * cbet1 + cbet2 * sbet1;
1757 if shortline {
1758 let mut sbetm2 = sq(sbet1 + sbet2);
1759
1760 sbetm2 /= sbetm2 + sq(cbet1 + cbet2);
1763 dnm = sqrt(1. + g.ep2 * sbetm2);
1764 let omg12 = lam12 / (g.f1 * dnm);
1765 somg12 = sin(omg12);
1766 comg12 = cos(omg12);
1767 } else {
1768 somg12 = slam12;
1769 comg12 = clam12;
1770 }
1771
1772 salp1 = cbet2 * somg12;
1773 calp1 = if comg12 >= 0. {
1774 sbet12 + cbet2 * sbet1 * sq(somg12) / (1. + comg12)
1775 } else {
1776 sbet12a - cbet2 * sbet1 * sq(somg12) / (1. - comg12)
1777 };
1778
1779 let ssig12 = hypot(salp1, calp1);
1780 let csig12 = sbet1 * sbet2 + cbet1 * cbet2 * comg12;
1781
1782 if shortline && ssig12 < g.etol2 {
1783 salp2 = cbet1 * somg12;
1785 calp2 = sbet12
1786 - cbet1 * sbet2 * (if comg12 >= 0. { sq(somg12) / (1. + comg12) } else { 1. - comg12 });
1787 norm2(&mut salp2, &mut calp2);
1788 sig12 = atan2(ssig12, csig12);
1790 } else if fabs(g.n) > 0.1 || csig12 >= 0. ||
1792 ssig12 >= 6. * fabs(g.n) * PI * sq(cbet1)
1793 {
1794 } else {
1796 let x;
1799 let y;
1800 let lamscale;
1801 let betscale;
1802 let lam12x = atan2(-slam12, -clam12); if g.f >= 0. {
1804 {
1807 let k2 = sq(sbet1) * g.ep2;
1808 let eps = k2 / (2. * (1. + sqrt(1. + k2)) + k2);
1809 lamscale = g.f * cbet1 * a3f(g, eps) * PI;
1810 }
1811 betscale = lamscale * cbet1;
1812
1813 x = lam12x / lamscale;
1814 y = sbet12a / betscale;
1815 } else {
1816 let cbet12a = cbet2 * cbet1 - sbet2 * sbet1;
1819 let bet12a = atan2(sbet12a, cbet12a);
1820 let mut m12b = 0.;
1821 let mut m0 = 0.;
1822 lengths(
1825 g,
1826 g.n,
1827 PI + bet12a,
1828 sbet1,
1829 -cbet1,
1830 dn1,
1831 sbet2,
1832 cbet2,
1833 dn2,
1834 cbet1,
1835 cbet2,
1836 &mut 0.,
1837 &mut m12b,
1838 &mut m0,
1839 &mut 0.,
1840 &mut 0.,
1841 ca,
1842 );
1843 x = -1. + m12b / (cbet1 * cbet2 * m0 * PI);
1844 betscale = if x < -0.01 { sbet12a / x } else { -g.f * sq(cbet1) * PI };
1845 lamscale = betscale / cbet1;
1846 y = lam12x / lamscale;
1847 }
1848
1849 if y > -TOL1 && x > -1. - XTHRESH {
1850 if g.f >= 0. {
1852 salp1 = fmin(1.0, -x);
1853 calp1 = -sqrt(1. - sq(salp1));
1854 } else {
1855 calp1 = fmax(if x > -TOL1 { 0.0 } else { -1.0 }, x);
1856 salp1 = sqrt(1. - sq(calp1));
1857 }
1858 } else {
1859 let k = astroid(x, y);
1894 let omg12a = lamscale * (if g.f >= 0. { -x * k / (1. + k) } else { -y * (1. + k) / k });
1895 somg12 = sin(omg12a);
1896 comg12 = -cos(omg12a);
1897 salp1 = cbet2 * somg12;
1899 calp1 = sbet12a - cbet2 * sbet1 * sq(somg12) / (1. - comg12);
1900 }
1901 }
1902 if salp1 > 0. {
1904 norm2(&mut salp1, &mut calp1);
1905 } else {
1906 salp1 = 1.;
1907 calp1 = 0.;
1908 }
1909
1910 *psalp1 = salp1;
1911 *pcalp1 = calp1;
1912 if shortline {
1913 *pdnm = dnm;
1914 }
1915 if sig12 >= 0. {
1916 *psalp2 = salp2;
1917 *pcalp2 = calp2;
1918 }
1919
1920 sig12
1921}
1922
1923#[allow(clippy::too_many_arguments)]
1925pub fn lambda12(
1926 g: &GeodGeodesic,
1927 sbet1: f64,
1928 cbet1: f64,
1929 dn1: f64,
1930 sbet2: f64,
1931 cbet2: f64,
1932 dn2: f64,
1933 salp1: f64,
1934 mut calp1: f64,
1935 slam120: f64,
1936 clam120: f64,
1937 psalp2: &mut f64,
1938 pcalp2: &mut f64,
1939 psig12: &mut f64,
1940 pssig1: &mut f64,
1941 pcsig1: &mut f64,
1942 pssig2: &mut f64,
1943 pcsig2: &mut f64,
1944 peps: &mut f64,
1945 pdomg12: &mut f64,
1946 diffp: bool,
1947 pdlam12: &mut f64,
1948 ca: &mut [f64],
1950) -> f64 {
1951 let mut ssig1;
1952 let mut csig1;
1953 let mut ssig2;
1954 let mut csig2;
1955 let mut dlam12 = 0.0;
1956
1957 if sbet1 == 0. && calp1 == 0. {
1958 calp1 = -TINY;
1960 }
1961
1962 let salp0 = salp1 * cbet1;
1964 let calp0 = hypot(calp1, salp1 * sbet1); ssig1 = sbet1;
1969 let somg1 = salp0 * sbet1;
1970 let comg1 = calp1 * cbet1;
1971 csig1 = comg1;
1972 norm2(&mut ssig1, &mut csig1);
1973 let salp2 = if cbet2 != cbet1 { salp0 / cbet2 } else { salp1 };
1980 let calp2 = if cbet2 != cbet1 || fabs(sbet2) != -sbet1 {
1986 sqrt(
1987 sq(calp1 * cbet1)
1988 + (if cbet1 < -sbet1 {
1989 (cbet2 - cbet1) * (cbet1 + cbet2)
1990 } else {
1991 (sbet1 - sbet2) * (sbet1 + sbet2)
1992 }),
1993 ) / cbet2
1994 } else {
1995 fabs(calp1)
1996 };
1997
1998 ssig2 = sbet2;
2001 let somg2 = salp0 * sbet2;
2002 let comg2 = calp2 * cbet2;
2003 csig2 = comg2;
2004 norm2(&mut ssig2, &mut csig2);
2005 let sig12 = atan2(fmax(0.0, csig1 * ssig2 - ssig1 * csig2) + 0., csig1 * csig2 + ssig1 * ssig2);
2009
2010 let somg12 = fmax(0.0, comg1 * somg2 - somg1 * comg2) + 0.;
2012 let comg12 = comg1 * comg2 + somg1 * somg2;
2013 let eta = atan2(somg12 * clam120 - comg12 * slam120, comg12 * clam120 + somg12 * slam120);
2015 let k2 = sq(calp0) * g.ep2;
2016 let eps = k2 / (2. * (1. + sqrt(1. + k2)) + k2);
2017 c3f(g, eps, ca);
2018 let b312 = sin_cos_series(true, ssig2, csig2, ca, N_C3 - 1)
2019 - sin_cos_series(true, ssig1, csig1, ca, N_C3 - 1);
2020 let domg12 = -g.f * a3f(g, eps) * salp0 * (sig12 + b312);
2021 let lam12 = eta + domg12;
2022
2023 if diffp {
2024 if calp2 == 0. {
2025 dlam12 = -2. * g.f1 * dn1 / sbet1;
2026 } else {
2027 lengths(
2028 g,
2029 eps,
2030 sig12,
2031 ssig1,
2032 csig1,
2033 dn1,
2034 ssig2,
2035 csig2,
2036 dn2,
2037 cbet1,
2038 cbet2,
2039 &mut 0.,
2040 &mut dlam12,
2041 &mut 0.,
2042 &mut 0.,
2043 &mut 0.,
2044 ca,
2045 );
2046 dlam12 *= g.f1 / (calp2 * cbet2);
2047 }
2048 }
2049
2050 *psalp2 = salp2;
2051 *pcalp2 = calp2;
2052 *psig12 = sig12;
2053 *pssig1 = ssig1;
2054 *pcsig1 = csig1;
2055 *pssig2 = ssig2;
2056 *pcsig2 = csig2;
2057 *peps = eps;
2058 *pdomg12 = domg12;
2059 if diffp {
2060 *pdlam12 = dlam12;
2061 }
2062
2063 lam12
2064}
2065
2066#[allow(clippy::too_many_arguments)]
2068fn lengths(
2069 g: &GeodGeodesic,
2070 eps: f64,
2071 sig12: f64,
2072 ssig1: f64,
2073 csig1: f64,
2074 dn1: f64,
2075 ssig2: f64,
2076 csig2: f64,
2077 dn2: f64,
2078 cbet1: f64,
2079 cbet2: f64,
2080 ps12b: &mut f64,
2081 pm12b: &mut f64,
2082 pm0: &mut f64,
2083 p_m12: &mut f64,
2084 p_m21: &mut f64,
2085 ca: &mut [f64],
2087) {
2088 let mut m0 = 0.0;
2090 let mut j12 = 0.0;
2091 let mut a1 = 0.0;
2092 let mut a2 = 0.0;
2093 let mut cb = [0.0; N_C];
2094
2095 let redlp = *pm12b != 0. || *pm0 != 0. || *p_m12 != 0. || *p_m21 != 0.;
2098 if *ps12b != 0. || redlp {
2099 a1 = a1m1f(eps);
2100 c1f(eps, ca);
2101 if redlp {
2102 a2 = a2m1f(eps);
2103 c2f(eps, &mut cb);
2104 m0 = a1 - a2;
2105 a2 += 1.;
2106 }
2107 a1 += 1.;
2108 }
2109 if *ps12b != 0. {
2110 let b1 = sin_cos_series(true, ssig2, csig2, ca, N_C1)
2111 - sin_cos_series(true, ssig1, csig1, ca, N_C1);
2112 *ps12b = a1 * (sig12 + b1);
2114 } else if redlp {
2120 for l in 1..N_C2 {
2124 cb[l] = a1 * ca[l] - a2 * cb[l];
2125 j12 = m0 * sig12
2126 + (sin_cos_series(true, ssig2, csig2, &cb, N_C2)
2127 - sin_cos_series(true, ssig1, csig1, &cb, N_C2));
2128 }
2129 if *pm0 != 0. {
2130 *pm0 = m0;
2131 }
2132 if *pm12b != 0. {
2133 *pm12b = dn2 * (csig1 * ssig2) - dn1 * (ssig1 * csig2) - csig1 * csig2 * j12;
2137 }
2138 if *p_m12 != 0. || *p_m21 != 0. {
2139 let csig12 = csig1 * csig2 + ssig1 * ssig2;
2140 let t = g.ep2 * (cbet1 - cbet2) * (cbet1 + cbet2) / (dn1 + dn2);
2141 if *p_m12 != 0. {
2142 *p_m12 = csig12 + (t * ssig2 - csig2 * j12) * ssig1 / dn1;
2143 }
2144 if *p_m21 != 0. {
2145 *p_m21 = csig12 - (t * ssig1 - csig1 * j12) * ssig2 / dn2;
2146 }
2147 }
2148 }
2149}
2150
2151fn sincosde(x: f64, t: f64, sinx: &mut f64, cosx: &mut f64) {
2152 let q = remquo(x, QD);
2156 let mut r = ang_round(q.0 + t);
2157 r *= DEGREE;
2159 let s = sin(r);
2161 let c = cos(r);
2162 match q.1 & 3 {
2163 0 => {
2164 *sinx = s;
2165 *cosx = c;
2166 }
2167 1 => {
2168 *sinx = c;
2169 *cosx = -s;
2170 }
2171 2 => {
2172 *sinx = -s;
2173 *cosx = -c;
2174 }
2175 _ => {
2176 *sinx = -c;
2177 *cosx = s;
2178 } }
2180 *cosx += 0.; if *sinx == 0. {
2184 *sinx = copysign(*sinx, x);
2185 }
2186}
2187
2188pub fn astroid(x: f64, y: f64) -> f64 {
2191 let p = sq(x);
2192 let q = sq(y);
2193 let r = (p + q - 1.) / 6.;
2194 if !(q == 0. && r <= 0.) {
2195 let _s = p * q / 4.; let r2 = sq(r);
2199 let r3 = r * r2;
2200 let disc = _s * (_s + 2. * r3);
2203 let mut u = r;
2204
2205 if disc >= 0. {
2206 let mut _t3 = _s + r3;
2207 _t3 += if _t3 < 0. { -sqrt(disc) } else { sqrt(disc) }; let t = cbrt(_t3); u += t + (if t != 0. { r2 / t } else { 0. });
2215 } else {
2216 let ang = atan2(sqrt(-disc), -(_s + r3));
2218 u += 2. * r * cos(ang / 3.);
2221 }
2222 let v = sqrt(sq(u) + q); let uv = if u < 0. { q / (v - u) } else { u + v }; let w = (uv - q) / (2. * v); uv / (sqrt(uv + sq(w)) + w) } else {
2230 0.
2234 }
2235}