private static Split findBestSplit(float[] sortedValues, float min_rd, int min_y, int NCount, int numThresholds)
        {
            int YCount = sortedValues.Length;

            float min = sortedValues[0];
            float max = sortedValues[sortedValues.Length - 1];

            float thresholdInterval = (max - min) / (numThresholds + 1);
            float NInterval         = (float)NCount / (numThresholds + 1);

            int[]   YCountLeft  = new int[numThresholds + 1];
            int[]   NCountLeft  = new int[numThresholds + 1];
            int[]   YCountRight = new int[numThresholds];
            int[]   NCountRight = new int[numThresholds];
            float[] thresholds  = new float[numThresholds];

            float threshold = min + thresholdInterval;
            float NLeft     = NInterval;
            int   YLeft     = 0;
            int   YRight    = YCount;

            for (int i = 0; i < numThresholds; i++)
            {
                thresholds[i] = threshold;

                for (; sortedValues[YLeft] < threshold; YLeft++, YRight--)
                {
                    ;
                }

                YCountLeft[i]  = YLeft;
                YCountRight[i] = YRight;
                NCountLeft[i]  = (int)NLeft;
                NCountRight[i] = NCount - (int)NLeft;

                threshold += thresholdInterval;
                NLeft     += NInterval;
            }
            YCountLeft[numThresholds] = YCount;
            NCountLeft[numThresholds] = NCount;

            Split best = new Split {
                Score = float.MinValue
            };

            for (int i = 0; i < numThresholds; i++)
            {
                if (YCountLeft[i] < min_y)
                {
                    continue;
                }
                float left_rd = (float)YCountLeft[i] / Math.Max(NCountLeft[i], 1);

                for (int j = i + 1; j < numThresholds; j++)
                {
                    if (YCountRight[i] < min_y)
                    {
                        continue;
                    }
                    float right_rd = (float)YCountRight[i] / Math.Max(NCountRight[i], 1);

                    float middleY   = YCountLeft[j] - YCountLeft[i];
                    float middleN   = Math.Max(NCountLeft[j] - NCountLeft[i], 1);
                    float middle_rd = middleY / middleN;
                    if (middle_rd < min_rd)
                    {
                        Split test = new Split {
                            Score = left_rd + right_rd - middle_rd
                        };

                        if (test.Score > best.Score)
                        {
                            if (j - i > 3)
                            {
                                float[] density = new float[j - i];
                                for (int k = 0; k < density.Length; k++)
                                {
                                    density[k] = (float)(YCountLeft[i + k + 1] - YCountLeft[i + k]) / (NCountLeft[i + k + 1] - NCountLeft[i + k]);
                                }
                                density = Gaussian.Convolve(density, 1);
                                int minIndex = density.MinIndex();
                                test.Threshold = thresholds[i + minIndex];
                            }
                            else
                            {
                                test.Threshold = (thresholds[i] + thresholds[j]) / 2;
                            }
                            test.NLeft = (int)(((test.Threshold - min) * NCount) / (max - min));
                            best       = test;
                        }
                    }
                }
            }

            return(best);
        }