/// <summary>
        /// Initializes a new instance of the <see cref="ExtractedIonChromatogram"/> class. 
        /// Get the extracted ion chromatogram from the whole drift time scan range.
        /// </summary>
        /// <param name="uimfReader">
        /// The UIMF reader.
        /// </param>
        /// <param name="frameNumber">
        /// The frame number.
        /// </param>
        /// <param name="centerMz">
        /// The MZ.
        /// </param>
        /// <param name="massToleranceInPpm">
        /// The mass Tolerance In Ppm.
        /// </param>
        public ExtractedIonChromatogram(DataReader uimfReader, int frameNumber, double centerMz, double massToleranceInPpm)
        {
            FrameParams param = uimfReader.GetFrameParams(frameNumber);

            this.IntensityPoints = uimfReader.GetXic(
                    centerMz,
                    massToleranceInPpm,
                    frameNumber,
                    frameNumber,
                    1,
                    param.Scans,
                    DataReader.FrameType.MS1,
                    DataReader.ToleranceType.PPM);

            this.CenterMz = centerMz;
            this.MassToleranceInPpm = massToleranceInPpm;
            this.NumberOfMobilityScans = param.Scans;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ExtractedIonChromatogram"/> class.
        /// Get the extracted ion chromatogram from the a particular drift time scan range.
        /// </summary>
        /// <param name="uimfReader">
        /// The uimf reader.
        /// </param>
        /// <param name="frameNumber">
        /// The frame number.
        /// </param>
        /// <param name="centerMz">
        /// The center mz.
        /// </param>
        /// <param name="massToleranceInPpm">
        /// The mass tolerance in ppm.
        /// </param>
        /// <param name="centerDriftTimeInMs">
        /// The center drift time.
        /// </param>
        /// <param name="driftTimeErrorInMs">
        /// The drift time error in ms.
        /// </param>
        public ExtractedIonChromatogram(DataReader uimfReader, int frameNumber, double centerMz, double massToleranceInPpm, double centerDriftTimeInMs, double driftTimeErrorInMs)
        {
            FrameParams param = uimfReader.GetFrameParams(frameNumber);

            double driftTimeMin = centerDriftTimeInMs - driftTimeErrorInMs;

            double driftTimeMax = centerDriftTimeInMs + driftTimeErrorInMs;

            double scanWidthInSeconds = param.GetValueDouble(FrameParamKeyType.AverageTOFLength) / 1000000000;

            int scanNumberMin = Metrics.DriftTimeInMsToNearestImsScanNumber(driftTimeMin, scanWidthInSeconds, param.Scans);
            int scanNumberMax = Metrics.DriftTimeInMsToNearestImsScanNumber(driftTimeMax, scanWidthInSeconds, param.Scans);

            this.IntensityPoints = uimfReader.GetXic(
                    centerMz,
                    massToleranceInPpm,
                    frameNumber,
                    frameNumber,
                    scanNumberMin,
                    scanNumberMax,
                    DataReader.FrameType.MS1,
                    DataReader.ToleranceType.PPM);

            this.CenterMz = centerMz;
            this.MassToleranceInPpm = massToleranceInPpm;
            this.NumberOfMobilityScans = param.Scans;
        }
        /// <summary>
        /// The score feature using isotopic profile.
        /// </summary>
        /// <param name="imsPeak">
        /// The ims Peak.
        /// </param>
        /// <param name="reader">
        /// The reader.
        /// </param>
        /// <param name="target">
        /// The Target.
        /// </param>
        /// <param name="isotopicPeakList">
        /// The isotopic peak list.
        /// </param>
        /// <param name="voltageGroup">
        /// The voltage Group.
        /// </param>
        /// <param name="selectedMethod">
        /// The selected Method.
        /// </param>
        /// <param name="globalMaxIntensities">
        /// </param>
        /// <returns>
        /// The <see cref="double"/>.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// </exception>
        public static double IsotopicProfileScore(StandardImsPeak imsPeak, DataReader reader, IImsTarget target, List<Peak> isotopicPeakList, VoltageGroup voltageGroup, IsotopicScoreMethod selectedMethod, double globalMaxIntensities, double totalScans)
        {
            // No need to move on if the isotopic profile is not found
            // if (observedIsotopicProfile == null || observedIsotopicProfile.MonoIsotopicMass < 1)
            // {
            // result.AnalysisStatus = AnalysisStatus.IsotopicProfileNotFound;
            // continue;
            // }

            // Find Isotopic Profile
            // List<Peak> massSpectrumPeaks;
            // IsotopicProfile observedIsotopicProfile = _msFeatureFinder.IterativelyFindMSFeature(massSpectrum, theoreticalIsotopicProfile, out massSpectrumPeaks);
            if (target.CompositionWithoutAdduct == null)
            {
                throw new InvalidOperationException("Cannot score feature using isotopic profile for Ims Target without CompositionWithoutAdduct provided.");
            }

            // Bad Feature, so get out
            if (imsPeak == null)
            {
                return 0;
            }

            // Get the scanWindow size
            int scanNumberMax = imsPeak.PeakApex.DriftTimeFullWidthHalfMaxHigherBondInScanNumber;
            int scanNumberMin = imsPeak.PeakApex.DriftTimeFullWidthHalfMaxLowerBondInScanNumber;
            if ((scanNumberMin < 0) || (scanNumberMax > totalScans - 1))
            {
                return 0;
            }

            // Get the mass error from the observed feature peak from the Target theoretical peak
            double mzOffset = imsPeak.PeakApex.MzCenterInDalton - target.MassWithAdduct;

            List<double> observedIsotopicPeakList = new List<double>();

            int totalIsotopicIndex = isotopicPeakList.Count;
            int[] isotopicIndexMask = new int[totalIsotopicIndex];

            // Find an unsaturated peak in the isotopic profile
            for (int i = 0; i < totalIsotopicIndex; i++)
            {
                // Isotopic centerMz
                double Mz = isotopicPeakList[i].XValue;

                var peakList = reader.GetXic(Mz + mzOffset,
                    imsPeak.PeakApex.MzWindowToleranceInPpm,
                    voltageGroup.FirstFrameNumber,
                    voltageGroup.LastFrameNumber,
                    scanNumberMin,
                    scanNumberMax,
                    DataReader.FrameType.MS1,
                    DataReader.ToleranceType.PPM);

                // Sum the intensities
                double sumIntensities = 0;
                foreach (var point in peakList)
                {
                    sumIntensities += point.Intensity;
                    if (point.IsSaturated)
                    {
                        isotopicIndexMask[i] = 1;
                    }
                }

                sumIntensities /= voltageGroup.FrameAccumulationCount;
                observedIsotopicPeakList.Add(sumIntensities);
            }

            // Return 0 if the intensity sum is really small
            if (observedIsotopicPeakList.Sum() < globalMaxIntensities * 0.0003)
            {
                return 0;
            }

            // If the unsaturated isotopes are below a certain threshold
            if (totalIsotopicIndex - isotopicIndexMask.Sum() <= 1)
            {
                return 0;
            }

            if (selectedMethod == IsotopicScoreMethod.Angle)
            {
                return IsotopicProfileScoreAngle(observedIsotopicPeakList, isotopicPeakList);
            }
            else if (selectedMethod == IsotopicScoreMethod.EuclideanDistance)
            {
                return IsotopicProfileScoreEuclidean(observedIsotopicPeakList, isotopicPeakList);
            }
            else if (selectedMethod == IsotopicScoreMethod.PearsonCorrelation)
            {
                return PearsonCorrelation(observedIsotopicPeakList, isotopicPeakList);
            }
            else if (selectedMethod == IsotopicScoreMethod.Bhattacharyya)
            {
                return BhattacharyyaDistance(observedIsotopicPeakList, isotopicPeakList);
            }
            else if (selectedMethod == IsotopicScoreMethod.EuclideanDistanceAlternative)
            {
                return EuclideanAlternative(observedIsotopicPeakList, isotopicPeakList);
            }
            else
            {
                throw new NotImplementedException();
            }
        }