Exemple #1
0
        private static double JacobiSn_ViaRangeReduction(double u, double k, out long u0, out double u1)
        {
            Debug.Assert(u >= 0.0);

            double m = k * k;

            // First we compare u to a quick-to-compute lower bound for K / 2.
            // If it's below the bound, we can move directly to series for sn without having
            // to compute K or perform range reduction.
            double K = LowerBoundK(m);

            if (u <= K / 2.0)
            {
                u0 = 0;
                u1 = u;
            }
            else
            {
                // Too bad, we need to actually compute K and range reduce.
                K = AdvancedMath.EllipticK(k);
                double v  = u / K;
                double v0 = Math.Round(v);
                if (v < Int64.MaxValue)
                {
                    u0 = (long)v0;
                    u1 = (v - v0) * K;
                }
                else
                {
                    u0 = Int64.MaxValue;
                    u1 = 0.0;
                }
            }
            Debug.Assert(Math.Abs(u1) <= K / 2.0);
            // Note that for u >> K, this has the same problem as naïve trig function calculations
            // for x >> 2 \pi: we loose a lot of accuracy for u1 because it is computed via subtraction.
            // There is not much we can do about this, though, short of moving to arbitrary-precision
            // arithmetic, because K is different for each value of m.

            // Compute sn of the reduced -K/2 < u1 < K/2
            return(JacobiSn_ReduceToSeries(u1, m));

            // We should be able to do even better: |u1| <= K / 4. Our first attempt had some problems,
            // and the series still work for the larger range, so leave it as is for now.
        }