}//*/

        public IEnumerable <ProductMatch> GetProductMZs(DBOptions options, GraphML_List <MsMsPeak> peaks)//, List<double> theoretical_product_mzs = null)
        {
            // speed optimizations
            //double[] experimental_masses = Query.spectrum.Masses;
            //GraphML_List<MSPeak> peaks = Query.spectrum.Peaks;
            int num_experimental_peaks = peaks.Count;

            TotalTheoreticalProducts = 0;
            TotalWeightedProducts    = 0;
            //New version that should include charged ions

            //foreach (ProductMatch matchTheo in AllFragmentSearch.ComputeAllFragments(Peptide, Query.precursor.Charge, options))
            foreach (ProductMatch matchTheo in options.fragments.ComputeFragmentsFast(Peptide.GetMasses(), Query.precursor.Charge, options))
            //foreach (ProductMatch matchTheo in options.fragments.ComputeFragments(Peptide, Query.precursor.Charge, options))
            {
                TotalTheoreticalProducts++;
                TotalWeightedProducts += matchTheo.weight;//++;
                //TODO test what is most common between charged ions, and uncharged ions
                //for (int charge = 1; charge <= Query.precursor.Charge; charge++)
                //for (int charge = Query.precursor.Charge; charge >= 1; charge--)
                //{
                double massDiff = options.productMassTolerance.Value;
                double bestMz   = -1;
                double bestInt  = 0;

                foreach (int index in Query.spectrum.GetIndexOfMZInRange(matchTheo.theoMz, options.productMassTolerance))
                {
                    if (peaks[index].Charge <= 0 || peaks[index].Charge == matchTheo.charge)
                    {
                        double diff = Numerics.CalculateMassError(peaks[index].MZ, matchTheo.theoMz, options.productMassTolerance.Units);
                        //double diff = matchTheo.theoMz - peaks[index].MZ;// experimental_masses[index];//TODO DALTON ONLY : add product mass tolerance unit test
                        if (Math.Abs(diff) < options.productMassTolerance.Value)
                        {
                            if (Math.Abs(diff) < Math.Abs(massDiff))//TODO Priority to intensity, or precision?
                            {
                                massDiff = diff;
                                bestMz   = peaks[index].MZ;
                            }
                            //if (peaks[index].Intensity > bestInt)
                            bestInt += peaks[index].Intensity;
                        }
                    }
                }
                if (bestMz >= 0)
                {
                    ProductMatch pMatch = new ProductMatch();
                    pMatch.weight              = matchTheo.weight;
                    pMatch.theoMz              = matchTheo.theoMz; // Utilities.MZFromMzSingleCharge(theoMass, charge);
                    pMatch.obsMz               = bestMz;           // experimental_masses[bestIndex];
                    pMatch.mass_diff           = massDiff;
                    pMatch.obsIntensity        = bestInt;          // Intensities[bestIndex];
                    pMatch.charge              = matchTheo.charge; // peaks[bestIndex].Charge;
                    pMatch.Fragment            = matchTheo.Fragment;
                    pMatch.fragmentPos         = matchTheo.fragmentPos;
                    pMatch.normalizedIntensity = pMatch.obsIntensity / (Query.spectrum.InjectionTime * Query.spectrum.PrecursorIntensityPerMilliSecond);
                    yield return(pMatch);
                    //break;
                }
            }
        }
        }//*/

        /*
         * private static IEnumerable<double> fragmentsTypesCTerm(double cTermCumul)
         * {
         *  yield return cTermCumul + Constants.OXYGEN_MASS * 2 + Constants.HYDROGEN_MASS + Constants.CARBON_MASS;//X
         *  yield return cTermCumul + Constants.OXYGEN_MASS + Constants.HYDROGEN_MASS * 3;//Y
         *  yield return cTermCumul + Constants.OXYGEN_MASS + Constants.HYDROGEN_MASS - Constants.NITROGEN_MASS - Constants.HYDROGEN_MASS;//Z
         * }    //*/
        public static IEnumerable <ProductMatch> ComputeAllFragments(Peptide peptide, int precursorKnownCharge, DBOptions dbOptions)
        {
            int maxCharge;

            if (precursorKnownCharge > 1)
            {
                maxCharge = precursorKnownCharge - 1;
            }
            else
            {
                maxCharge = 1;
            }
            FragmentA fragNTerm = new FragmentA();
            FragmentX fragCTerm = new FragmentX();

            double[]     masses = peptide.GetMasses();
            ProductMatch match  = new ProductMatch();

            match.weight = 1;
            double cumulN = 0.0; // Constants.WATER_MONOISOTOPIC_MASS;
            double cumulC = 0.0; // Constants.WATER_MONOISOTOPIC_MASS;

            for (int r = 0; r < masses.Length; r++)
            {
                cumulN += masses[r];
                cumulC += masses[masses.Length - r - 1];

                foreach (double product_mass in fragmentsTypesCTerm(cumulC))
                {
                    match.fragmentPos = masses.Length - r;
                    match.Fragment    = fragCTerm;
                    for (int c = maxCharge; c > 0; c--)
                    {
                        match.theoMz = Numerics.MZFromMass(product_mass, c);
                        match.charge = c;
                        yield return(match);
                        //match.theoMz = Numerics.MZFromMass(product_mass - Constants.WATER_MONOISOTOPIC_MASS, c);
                        //yield return match;
                    }
                }

                foreach (double product_mass in fragmentsTypesNTerm(cumulN))
                {
                    match.fragmentPos = r + 1;
                    match.Fragment    = fragNTerm;
                    for (int c = maxCharge; c > 0; c--)
                    {
                        match.theoMz = Numerics.MZFromMass(product_mass, c);
                        match.charge = c;
                        yield return(match);

                        match.theoMz = Numerics.MZFromMass(product_mass - Constants.WATER_MONOISOTOPIC_MASS, c);
                        yield return(match);

                        match.theoMz = Numerics.MZFromMass(product_mass - Constants.AMONIA_MASS, c);
                        yield return(match);
                    }
                }
            }
            //Single charge immonium ions
            //Water Loss
            //Amonia Loss
            //Internal fragments
            //Fragment types
        }