private double Lambda12(double sbet1, double cbet1, double dn1,
                                double sbet2, double cbet2, double dn2,
                                double salp1, double calp1, double slam120, double clam120,
                                out double salp2, out double calp2, out double sig12,
                                out double ssig1, out double csig1, out double ssig2, out double csig2,
                                EllipticFunction E,
                                out double domg12, bool diffp, out double dlam12)
        {
            dlam12 = double.NaN;

            if (sbet1 == 0 && calp1 == 0)
            {
                // Break degeneracy of equatorial line.  This case has already been
                // handled.
                calp1 = -tiny_;
            }

            double
            // sin(alp1) * cos(bet1) = sin(alp0)
                salp0 = salp1 * cbet1,
                calp0 = Hypot(calp1, salp1 * sbet1); // calp0 > 0

            double somg1, comg1, somg2, comg2, somg12, comg12, cchi1, cchi2, lam12;

            // tan(bet1) = tan(sig1) * cos(alp1)
            // tan(omg1) = sin(alp0) * tan(sig1) = tan(omg1)=tan(alp1)*sin(bet1)
            ssig1 = sbet1; somg1 = salp0 * sbet1;
            csig1 = comg1 = calp1 * cbet1;
            // Without normalization we have schi1 = somg1.
            cchi1 = _f1 * dn1 * comg1;
            Norm(ref ssig1, ref csig1);
            // Math::norm(somg1, comg1); -- don't need to normalize!
            // Math::norm(schi1, cchi1); -- don't need to normalize!

            // Enforce symmetries in the case abs(bet2) = -bet1.  Need to be careful
            // about this case, since this can yield singularities in the Newton
            // iteration.
            // sin(alp2) * cos(bet2) = sin(alp0)
            salp2 = cbet2 != cbet1 ? salp0 / cbet2 : salp1;
            // calp2 = sqrt(1 - sq(salp2))
            //       = sqrt(sq(calp0) - sq(sbet2)) / cbet2
            // and subst for calp0 and rearrange to give (choose positive sqrt
            // to give alp2 in [0, pi/2]).
            calp2 = cbet2 != cbet1 || Abs(sbet2) != -sbet1?
                    Sqrt(Sq(calp1 *cbet1) +
                         (cbet1 < -sbet1 ?
                          (cbet2 - cbet1) * (cbet1 + cbet2) :
                          (sbet1 - sbet2) * (sbet1 + sbet2))) / cbet2:
                    Abs(calp1);

            // tan(bet2) = tan(sig2) * cos(alp2)
            // tan(omg2) = sin(alp0) * tan(sig2).
            ssig2 = sbet2; somg2 = salp0 * sbet2;
            csig2 = comg2 = calp2 * cbet2;
            // Without normalization we have schi2 = somg2.
            cchi2 = _f1 * dn2 * comg2;
            Norm(ref ssig2, ref csig2);
            // Math::norm(somg2, comg2); -- don't need to normalize!
            // Math::norm(schi2, cchi2); -- don't need to normalize!

            // sig12 = sig2 - sig1, limit to [0, pi]
            sig12 = Atan2(Max(0, csig1 * ssig2 - ssig1 * csig2),
                          csig1 * csig2 + ssig1 * ssig2);

            // omg12 = omg2 - omg1, limit to [0, pi]
            somg12 = Max(0, comg1 * somg2 - somg1 * comg2);
            comg12 = comg1 * comg2 + somg1 * somg2;
            var k2 = Sq(calp0) * _ep2;

            E.Reset(-k2, -_ep2, 1 + k2, 1 + _ep2);
            // chi12 = chi2 - chi1, limit to [0, pi]
            double
                schi12 = Max(0, cchi1 * somg2 - somg1 * cchi2),
                cchi12 = cchi1 * cchi2 + somg1 * somg2;
            // eta = chi12 - lam120
            var eta = Atan2(schi12 * clam120 - cchi12 * slam120,
                            cchi12 * clam120 + schi12 * slam120);
            var deta12 = -_e2 / _f1 * salp0 * E.H() / (PI / 2) *
                         (sig12 + (E.DeltaH(ssig2, csig2, dn2) - E.DeltaH(ssig1, csig1, dn1)));

            lam12 = eta + deta12;
            // domg12 = deta12 + chi12 - omg12
            domg12 = deta12 + Atan2(schi12 * comg12 - cchi12 * somg12,
                                    cchi12 * comg12 + schi12 * somg12);
            if (diffp)
            {
                if (calp2 == 0)
                {
                    dlam12 = -2 * _f1 * dn1 / sbet1;
                }
                else
                {
                    Lengths(E, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2,
                            cbet1, cbet2, GeodesicFlags.ReducedLength,
                            out _, out dlam12, out _, out _, out _);
                    dlam12 *= _f1 / (calp2 * cbet2);
                }
            }

            return(lam12);
        }
        private double InverseStart(EllipticFunction E,
                                    double sbet1, double cbet1, double dn1,
                                    double sbet2, double cbet2, double dn2,
                                    double lam12, double slam12, double clam12,
                                    out double salp1, out double calp1,
                                    out double salp2, out double calp2, out double dnm)
        {
            salp2 = calp2 = dnm = double.NaN;
            // Return a starting point for Newton's method in salp1 and calp1 (function
            // value is -1).  If Newton's method doesn't need to be used, return also
            // salp2 and calp2 and function value is sig12.
            double
                sig12 = -1,             // Return value
                                        // bet12 = bet2 - bet1 in [0, pi); bet12a = bet2 + bet1 in (-pi, 0]
                sbet12     = sbet2 * cbet1 - cbet2 * sbet1,
                cbet12     = cbet2 * cbet1 + sbet2 * sbet1;
            var  sbet12a   = sbet2 * cbet1 + cbet2 * sbet1;
            bool shortline = cbet12 >= 0 && sbet12 < 0.5 &&
                             cbet2 * lam12 < 0.5;
            double somg12, comg12;

            if (shortline)
            {
                var sbetm2 = Sq(sbet1 + sbet2);
                // sin((bet1+bet2)/2)^2
                // =  (sbet1 + sbet2)^2 / ((sbet1 + sbet2)^2 + (cbet1 + cbet2)^2)
                sbetm2 /= sbetm2 + Sq(cbet1 + cbet2);
                dnm     = Sqrt(1 + _ep2 * sbetm2);
                var omg12 = lam12 / (_f1 * dnm);
                somg12 = Sin(omg12); comg12 = Cos(omg12);
            }
            else
            {
                somg12 = slam12; comg12 = clam12;
            }

            salp1 = cbet2 * somg12;
            calp1 = comg12 >= 0 ?
                    sbet12 + cbet2 * sbet1 * Sq(somg12) / (1 + comg12) :
                    sbet12a - cbet2 * sbet1 * Sq(somg12) / (1 - comg12);

            double
                ssig12 = Hypot(salp1, calp1),
                csig12 = sbet1 * sbet2 + cbet1 * cbet2 * comg12;

            if (shortline && ssig12 < _etol2)
            {
                // really short lines
                salp2 = cbet1 * somg12;
                calp2 = sbet12 - cbet1 * sbet2 *
                        (comg12 >= 0 ? Sq(somg12) / (1 + comg12) : 1 - comg12);
                Norm(ref salp2, ref calp2);
                // Set return value
                sig12 = Atan2(ssig12, csig12);
            }
            else if (Abs(_n) > 0.1 || // Skip astroid calc if too eccentric
                     csig12 >= 0 ||
                     ssig12 >= 6 * Abs(_n) * PI * Sq(cbet1))
            {
                // Nothing to do, zeroth order spherical approximation is OK
            }
            else
            {
                // Scale lam12 and bet2 to x, y coordinate system where antipodal point
                // is at origin and singular point is at y = 0, x = -1.
                double y, lamscale, betscale;
                // Volatile declaration needed to fix inverse case
                // 56.320923501171 0 -56.320923501171 179.664747671772880215
                // which otherwise fails with g++ 4.4.4 x86 -O3
                /*GEOGRAPHICLIB_VOLATILE*/
                double x;
                var    lam12x = Atan2(-slam12, -clam12); // lam12 - pi
                if (_f >= 0)
                {                                        // In fact f == 0 does not get here
                                                         // x = dlong, y = dlat
                    {
                        var k2 = Sq(sbet1) * _ep2;
                        E.Reset(-k2, -_ep2, 1 + k2, 1 + _ep2);
                        lamscale = _e2 / _f1 * cbet1 * 2 * E.H();
                    }
                    betscale = lamscale * cbet1;

                    x = lam12x / lamscale;
                    y = sbet12a / betscale;
                }
                else
                {                  // _f < 0
                                   // x = dlat, y = dlong
                    double
                        cbet12a = cbet2 * cbet1 - sbet2 * sbet1,
                        bet12a  = Atan2(sbet12a, cbet12a);
                    // In the case of lon12 = 180, this repeats a calculation made in
                    // Inverse.
                    Lengths(E, PI + bet12a,
                            sbet1, -cbet1, dn1, sbet2, cbet2, dn2,
                            cbet1, cbet2, GeodesicFlags.ReducedLength, out _, out var m12b, out var m0, out _, out _);
                    x        = -1 + m12b / (cbet1 * cbet2 * m0 * PI);
                    betscale = x < -0.01 ? sbet12a / x :
                               -_f *Sq(cbet1) * PI;

                    lamscale = betscale / cbet1;
                    y        = lam12x / lamscale;
                }

                if (y > -tol1_ && x > -1 - xthresh_)
                {
                    // strip near cut
                    // Need real(x) here to cast away the volatility of x for min/max
                    if (_f >= 0)
                    {
                        salp1 = Min(1, -x); calp1 = -Sqrt(1 - Sq(salp1));
                    }
                    else
                    {
                        calp1 = Max(x > -tol1_ ? 0d : -1d, x);
                        salp1 = Sqrt(1 - Sq(calp1));
                    }
                }
                else
                {
                    // Estimate alp1, by solving the astroid problem.
                    //
                    // Could estimate alpha1 = theta + pi/2, directly, i.e.,
                    //   calp1 = y/k; salp1 = -x/(1+k);  for _f >= 0
                    //   calp1 = x/(1+k); salp1 = -y/k;  for _f < 0 (need to check)
                    //
                    // However, it's better to estimate omg12 from astroid and use
                    // spherical formula to compute alp1.  This reduces the mean number of
                    // Newton iterations for astroid cases from 2.24 (min 0, max 6) to 2.12
                    // (min 0 max 5).  The changes in the number of iterations are as
                    // follows:
                    //
                    // change percent
                    //    1       5
                    //    0      78
                    //   -1      16
                    //   -2       0.6
                    //   -3       0.04
                    //   -4       0.002
                    //
                    // The histogram of iterations is (m = number of iterations estimating
                    // alp1 directly, n = number of iterations estimating via omg12, total
                    // number of trials = 148605):
                    //
                    //  iter    m      n
                    //    0   148    186
                    //    1 13046  13845
                    //    2 93315 102225
                    //    3 36189  32341
                    //    4  5396      7
                    //    5   455      1
                    //    6    56      0
                    //
                    // Because omg12 is near pi, estimate work with omg12a = pi - omg12
                    var k = Astroid(x, y);
                    var
                        omg12a = lamscale * (_f >= 0 ? -x * k / (1 + k) : -y * (1 + k) / k);
                    somg12 = Sin(omg12a); comg12 = -Cos(omg12a);
                    // Update spherical estimate of alp1 using omg12 instead of lam12
                    salp1 = cbet2 * somg12;
                    calp1 = sbet12a - cbet2 * sbet1 * Sq(somg12) / (1 - comg12);
                }
            }
            // Sanity check on starting guess.  Backwards check allows NaN through.
            if (!(salp1 <= 0))
            {
                Norm(ref salp1, ref calp1);
            }
            else
            {
                salp1 = 1; calp1 = 0;
            }
            return(sig12);
        }