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. }