private void InitInterpolationWithBoundedError(double maxPermittedError)
        {
            int rank = 1, startRank = 1, midPointRank = 1;
            var prevPdf     = PDF(rank);
            var prevCdf     = prevPdf;
            var midPointCdf = prevCdf;

            InterpolationCDFRanks.Add(1);
            InterpolationCDFValues.Add(prevCdf);

            // Advance three pointers, one at the start of the segment, one at the midpoint, and one at the end.
            // The midpoint advances at half the speed of the end of the segment.
            for (rank = 2; rank <= N - 1; rank++)
            {
                var currPdf = PDF(rank);
                var currCdf = Min(1.0, prevCdf + currPdf);
                if ((rank - startRank) % 2 == 0)
                {
                    midPointRank++;
                    midPointCdf += PDF(midPointRank);
                }

                // No need to interpolate if the segment has no gap that needs interpolating.
                if (rank - startRank >= 2)
                {
                    // Add a provisional interpolation point.
                    InterpolationCDFRanks.Add(rank);
                    InterpolationCDFValues.Add(currCdf);

                    // Calculate the interpolation error for the midpoint.
                    // If it is still small enough, remove the provisional interpolation point.
                    var endPointPosition     = InterpolationSize - 1;
                    var cdfInterpolator      = CDFInterpolator(endPointPosition);
                    var estimatedMidpointCdf = Min(1.0, cdfInterpolator.Y(midPointRank));
                    var relativeError        = Abs(midPointCdf - estimatedMidpointCdf) / midPointCdf;

                    InterpolationCDFRanks.RemoveAt(endPointPosition);
                    InterpolationCDFValues.RemoveAt(endPointPosition);

                    // It is not guaranteed that the midpoint of the segment is where the worst error would be found.
                    // Thus permit less error than requested in the hope that this will keep the true maximum error
                    // below maxPermittedError as well.
                    if (relativeError >= maxPermittedError / 7)
                    {
                        // We decided we need an interpolation point, thus are starting a new segment.
                        midPointRank = startRank = rank - 1;
                        midPointCdf  = prevCdf;
                        InterpolationCDFRanks.Add(rank - 1);
                        InterpolationCDFValues.Add(prevCdf);
                    }
                }
                prevPdf = currPdf;
                prevCdf = currCdf;
            }
            // Close out the last segment.
            InterpolationCDFRanks.Add(N);
            InterpolationCDFValues.Add(1);
        }
        /// <summary>
        /// Perform no interpolation; store CDF values for all ranks, which may consume much memory.
        /// </summary>
        private void InitInterpolationWithZeroError()
        {
            var cdf = 0.0;

            for (var rank = 1; rank <= N; rank++)
            {
                cdf += PDF(rank);
                InterpolationCDFRanks.Add(rank);
                InterpolationCDFValues.Add(cdf);
            }
        }