private static void Process(GrowablePeak gpeak, BinaryWriter writer, ICollection <double> centerMasses,
                                    ICollection <float> centerMassErrs, ICollection <float> intensities,
                                    ICollection <float> minTimes, ICollection <float> maxTimes,
                                    ICollection <long> filePos, int minPeaks, IRawFile rawFile, double valleyFactor,
                                    bool split, IPeakCollector collector, bool maxIntensity)
        {
            Peak peak = gpeak.ToPeak();

            if (peak.Count >= minPeaks)
            {
                peak.RemoveDoublePeaks(rawFile, maxIntensity);
                if (peak.Count >= minPeaks)
                {
                    peak.Smooth(maxIntensity);
                    if (collector != null)
                    {
                        collector.AddPeak(peak);
                    }
                    Peak[] peaks = peak.Decompose(valleyFactor, split, maxIntensity);
                    foreach (Peak p in peaks)
                    {
                        if (p.Count >= minPeaks)
                        {
                            centerMasses.Add(p.CenterMass);
                            centerMassErrs.Add(p.CenterMassError);
                            intensities.Add(p.Intensity);
                            minTimes.Add(p.GetMinTime(rawFile));
                            maxTimes.Add(p.GetMaxTime(rawFile));
                            filePos.Add(writer.BaseStream.Position);
                            p.Write(writer);
                        }
                        p.Dispose();
                    }
                }
            }
        }
        public static void Detect(string dir, int missingScans, int pointsForCentroid, CentroidPosition centroidPosition,
                                  bool subtractBackground, int backgroundQuantile, double matchPpm, int minPeaks,
                                  double valleyFactor, bool slicePeaks, double intensityThreshold, IRawFile rawFile,
                                  string basePath, bool maxIntensity, IPeakCollector collector, out double[] centerMassArray,
                                  out float[] centerMassErrorArray, out float[] intensityArray, out float[] minTimeArray,
                                  out float[] maxTimeArray, out long[] filePosArray)
        {
            int npoints = pointsForCentroid;

            FileUtil.Rmdir(dir);
            Directory.CreateDirectory(dir);
            BinaryWriter writer;
            string       peaksPath = basePath + ".peaks";

            try {
                writer = FileUtil.GetBinaryWriter(peaksPath);
            } catch (Exception) {
                throw new Exception("Cannot open file " + peaksPath + ". It may be used by another program.");
            }
            List <double>          centerMasses     = new List <double>();
            List <float>           centerMassErrors = new List <float>();
            List <float>           intensities      = new List <float>();
            List <float>           minTimes         = new List <float>();
            List <float>           maxTimes         = new List <float>();
            List <long>            filePos          = new List <long>();
            int                    oldMinInd        = -1;
            int                    oldMaxInd        = -1;
            List <Ms1CentroidList> cache            = new List <Ms1CentroidList>();
            int                    maxMissingScans  = (rawFile.NumberOfMS1MassRanges - 1) * (missingScans + 1) + missingScans;

            double[][] massRanges = new double[rawFile.MS1Count][];
            for (int i = 0; i < rawFile.MS1Count; i++)
            {
                massRanges[i] = rawFile.GetMS1MassRange(i);
            }
            double[] intensityNorm = CalcIntensityNormalization(rawFile, pointsForCentroid, centroidPosition, subtractBackground,
                                                                backgroundQuantile, maxIntensity);
            for (int i = 0; i < rawFile.MS1Count; i++)
            {
                int minInd = Math.Max(0, i - maxMissingScans - 1);
                int maxInd = Math.Min(rawFile.MS1Count - 1, i + maxMissingScans + 1);
                if (i == 0)
                {
                    for (int j = 0; j <= maxInd; j++)
                    {
                        Spectrum s = rawFile.GetMS1Spectrum(j, subtractBackground, backgroundQuantile);
                        cache.Add(DetectPeaks(s, intensityThreshold, maxIntensity, npoints, centroidPosition));
                        s.Dispose();
                    }
                }
                else
                {
                    for (int j = oldMinInd; j < minInd; j++)
                    {
                        cache[0].Dispose();
                        cache.RemoveAt(0);
                    }
                    for (int j = oldMaxInd + 1; j <= maxInd; j++)
                    {
                        Spectrum s = rawFile.GetMS1Spectrum(j, subtractBackground, backgroundQuantile);
                        cache.Add(DetectPeaks(s, intensityThreshold, maxIntensity, npoints, centroidPosition));
                        s.Dispose();
                    }
                }
                Ms1CentroidList p      = cache[i - minInd];
                int[]           valids = new int[p.Count];
                int             count  = 0;
                for (int j = 0; j < p.Count; j++)
                {
                    double cm     = p.CenterMass(j);
                    bool   match  = false;
                    int    ntries = 0;
                    for (int k = i - 1; k >= minInd; k--)
                    {
                        double[] massRange = massRanges[k];
                        if (cm >= massRange[0] && cm <= massRange[1])
                        {
                            Ms1CentroidList q   = cache[k - minInd];
                            int             ind = q.GetClosestIndex(cm);
                            double          m   = q.CenterMass(ind);
                            if (ind != -1 && MassMatch(cm, m, matchPpm))
                            {
                                match = true;
                                break;
                            }
                            ntries++;
                            if (ntries > missingScans)
                            {
                                break;
                            }
                        }
                    }
                    if (!match)
                    {
                        ntries = 0;
                        for (int k = i + 1; k <= maxInd; k++)
                        {
                            double[] massRange = massRanges[k];
                            if (cm >= massRange[0] && cm <= massRange[1])
                            {
                                Ms1CentroidList q   = cache[k - minInd];
                                int             ind = q.GetClosestIndex(cm);
                                double          m   = q.CenterMass(ind);
                                if (ind != -1 && MassMatch(cm, m, matchPpm))
                                {
                                    match = true;
                                    break;
                                }
                                ntries++;
                                if (ntries > missingScans)
                                {
                                    break;
                                }
                            }
                        }
                    }
                    if (match)
                    {
                        valids[count++] = j;
                    }
                }
                valids = ArrayUtil.SubArray(valids, count);
                Ms1CentroidList reduced = p.Extract(valids);
                cache[i - minInd] = reduced;
                byte range = rawFile.GetMS1MassRangeIndex(i);
                for (int j = 0; j < reduced.Count; j++)
                {
                    double cm     = reduced.CenterMass(j);
                    bool   match  = false;
                    int    ntries = 0;
                    for (int k = i - 1; k >= minInd; k--)
                    {
                        double[] massRange = massRanges[k];
                        if (cm >= massRange[0] && cm <= massRange[1])
                        {
                            Ms1CentroidList q   = cache[k - minInd];
                            int             ind = q.GetClosestIndex(cm);
                            double          m   = q.CenterMass(ind);
                            if (ind != -1 && MassMatch(cm, m, matchPpm))
                            {
                                GrowablePeak peak = q.GetPeak(ind);
                                peak.Add(i, j, reduced, range, intensityNorm[range]);
                                match = true;
                                break;
                            }
                            ntries++;
                            if (ntries > missingScans)
                            {
                                break;
                            }
                        }
                    }
                    if (!match)
                    {
                        GrowablePeak peak = new GrowablePeak();
                        peak.Add(i, j, reduced, range, intensityNorm[range]);
                    }
                }
                Ms1CentroidList last       = cache[0];
                int             nextMinInd = Math.Max(0, i - maxMissingScans);
                for (int j = 0; j < last.Count; j++)
                {
                    GrowablePeak peak = last.GetPeak(j);
                    if (peak.IsDisposed())
                    {
                        continue;
                    }
                    if (peak.LastScanIndex < nextMinInd)
                    {
                        Process(peak, writer, centerMasses, centerMassErrors, intensities, minTimes, maxTimes, filePos, minPeaks,
                                rawFile, valleyFactor, slicePeaks, collector, maxIntensity);
                        peak.Dispose();
                    }
                }
                oldMinInd = minInd;
                oldMaxInd = maxInd;
                if (i % 100 == 0)
                {
                    Thread.Sleep(100);
                }
            }
            for (int i = rawFile.MS1Count - maxMissingScans - 1; i < rawFile.MS1Count; i++)
            {
                Ms1CentroidList last = cache[i - oldMinInd];
                for (int j = 0; j < last.Count; j++)
                {
                    GrowablePeak peak = last.GetPeak(j);
                    if (peak.IsDisposed())
                    {
                        continue;
                    }
                    if (peak.LastScanIndex == i)
                    {
                        Process(peak, writer, centerMasses, centerMassErrors, intensities, minTimes,
                                maxTimes, filePos, minPeaks, rawFile, valleyFactor, slicePeaks, collector, maxIntensity);
                        peak.Dispose();
                    }
                }
            }
            writer.Close();
            centerMassArray = centerMasses.ToArray();
            int[] o = ArrayUtil.Order(centerMassArray);
            centerMassArray      = ArrayUtil.SubArray(centerMassArray, o);
            centerMassErrorArray = ArrayUtil.SubArray(centerMassErrors, o);
            intensityArray       = ArrayUtil.SubArray(intensities, o);
            minTimeArray         = ArrayUtil.SubArray(minTimes, o);
            maxTimeArray         = ArrayUtil.SubArray(maxTimes, o);
            filePosArray         = ArrayUtil.SubArray(filePos, o);
        }