/**
         * Reverse projection, from Lambert conformal conic to geographic.
         *
         * @param[in] lon0 central meridian longitude (degrees).
         * @param[in] x easting of point (meters).
         * @param[in] y northing of point (meters).
         * @param[out] lat latitude of point (degrees).
         * @param[out] lon longitude of point (degrees).
         * @param[out] gamma meridian convergence at point (degrees).
         * @param[out] k azimuthal scale of projection at point; the radial
         *   scale is the 1/\e k.
         *
         * The latitude origin is given by AlbersEqualArea::LatitudeOrigin().  No
         * false easting or northing is added.  The value of \e lon returned is in
         * the range [−180°, 180°].  The value of \e lat returned is
         * in the range [−90°, 90°].  If the input point is outside
         * the legal projected space the nearest pole is returned.
         **********************************************************************/
        public void Reverse(double lon0, double x, double y,
                            out double lat, out double lon, out double gamma, out double k)
        {
            y *= _sign;
            double
                nx = _k0 * _n0 * x, ny = _k0 * _n0 * y, y1 = _nrho0 - ny,
                den  = GeoMath.Hypot(nx, y1) + _nrho0, // 0 implies origin with polar aspect
                drho = den != 0 ? (_k0 * x * nx - 2 * _k0 * y * _nrho0 + _k0 * y * ny) / den : 0,
            // dsxia = scxi0 * dsxi
                dsxia = -_scxi0 * (2 * _nrho0 + _n0 * drho) * drho /
                        (GeoMath.Square(_a) * _qZ),
                txi   = (_txi0 + dsxia) / Math.Sqrt(Math.Max(1 - dsxia * (2 * _txi0 + dsxia), epsx2_)),
                tphi  = tphif(txi),
                theta = Math.Atan2(nx, y1),
                lam   = _n0 != 0 ? theta / (_k2 * _n0) : x / (y1 * _k0);

            gamma = _sign * theta / GeoMath.Degree;
            lat   = GeoMath.Atand(_sign * tphi);
            lon   = lam / GeoMath.Degree;
            lon   = GeoMath.AngNormalize(lon + GeoMath.AngNormalize(lon0));
            k     = _k0 * (den != 0 ? (_nrho0 + _n0 * drho) * hyp(_fm * tphi) / _a : 1);
        }
        /**
         * Reverse projection, from transverse Mercator to geographic.
         *
         * @param[in] lon0 central meridian of the projection (degrees).
         * @param[in] x easting of point (meters).
         * @param[in] y northing of point (meters).
         * @param[out] lat latitude of point (degrees).
         * @param[out] lon longitude of point (degrees).
         * @param[out] gamma meridian convergence at point (degrees).
         * @param[out] k scale of projection at point.
         *
         * No false easting or northing is added.  The value of \e lon returned is
         * in the range [−180°, 180°].
         **********************************************************************/
        public void Reverse(double lon0, double x, double y,
                            out double lat, out double lon, out double gamma, out double k)
        {
            // This undoes the steps in Forward.  The wrinkles are: (1) Use of the
            // reverted series to express zeta' in terms of zeta. (2) Newton's method
            // to solve for phi in terms of tan(phi).
            double
                xi  = y / (_a1 * _k0),
                eta = x / (_a1 * _k0);
            // Explicitly enforce the parity
            int
                xisign  = (xi < 0) ? -1 : 1,
                etasign = (eta < 0) ? -1 : 1;

            xi  *= xisign;
            eta *= etasign;
            bool backside = xi > Math.PI / 2;

            if (backside)
            {
                xi = Math.PI - xi;
            }
            double
                c0 = Math.Cos(2 * xi), ch0 = Math.Cosh(2 * eta),
                s0 = Math.Sin(2 * xi), sh0 = Math.Sinh(2 * eta);


            int     n  = maxpow_;
            Complex a  = new Complex(2 * c0 * ch0, -2 * s0 * sh0); // 2 * Math.Cos(2*zeta')
            Complex y0 = new Complex((n & 1) != 0 ? -_bet[n] : 0, 0);
            Complex y1;                                            // default initializer is 0+i0
            Complex z0 = new Complex((n & 1) != 0 ? -2 * n * _bet[n] : 0, 0);
            Complex z1;

            if ((n & 1) != 0)
            {
                --n;
            }
            while (n > 0)
            {
                y1 = a * y0 - y1 - _bet[n];
                z1 = a * z0 - z1 - 2 * n * _bet[n];
                --n;
                y0 = a * y1 - y0 - _bet[n];
                z0 = a * z1 - z0 - 2 * n * _bet[n];
                --n;
            }
            a /= 2;                               // Math.Cos(2*zeta)
            z1 = 1 - z1 + a * z0;
            a  = new Complex(s0 * ch0, c0 * sh0); // Math.Sin(2*zeta)
            y1 = new Complex(xi, eta) + a * y0;
            // Convergence and scale for Gauss-Schreiber TM to Gauss-Krueger TM.
            gamma = GeoMath.Atan2d(z1.Imaginary, z1.Real);
            k     = _b1 / Complex.Abs(z1);
            // JHS 154 has
            //
            //   phi' = asin(Math.Sin(xi') / Math.Cosh(eta')) (Krueger p 17 (25))
            //   lam = asin(tanh(eta') / Math.Cos(phi')
            //   psi = asinh(tan(phi'))
            double
                xip = y1.Real, etap = y1.Imaginary,
                s = Math.Sinh(etap),
                c = Math.Max(0, Math.Cos(xip)), // Math.Cos(pi/2) might be negative
                r = GeoMath.Hypot(s, c);

            if (r != 0)
            {
                lon = GeoMath.Atan2d(s, c); // Krueger p 17 (25)
                                            // Use Newton's method to solve for tau
                double
                    sxip = Math.Sin(xip),
                    tau  = GeoMath.Tauf(sxip / r, _es);
                gamma += GeoMath.Atan2d(sxip * Math.Tanh(etap), c); // Krueger p 19 (31)
                lat    = GeoMath.Atand(tau);
                // Note Math.Cos(phi') * Math.Cosh(eta') = r
                k *= Math.Sqrt(_e2m + _e2 / (1 + GeoMath.Square(tau))) *
                     GeoMath.Hypot(1, tau) * r;
            }
            else
            {
                lat = 90;
                lon = 0;
                k  *= _c;
            }
            lat *= xisign;
            if (backside)
            {
                lon = 180 - lon;
            }
            lon *= etasign;
            lon  = GeoMath.AngNormalize(lon + lon0);
            if (backside)
            {
                gamma = 180 - gamma;
            }
            gamma *= xisign * etasign;
            gamma  = GeoMath.AngNormalize(gamma);
            k     *= _k0;
        }