Source code for MSQuant: PILgeneralisedIonSeries.cs, MSQlib1/src/massspec/PILgeneralisedIonSeries.cs.

Table of contents page.

Home page for MSQuant.

/****************************************************************************
 * Copyright (C) 2008 Peter Mortensen and Matthias Mann                     *
 * This file is part of MSQuant.                                            *
 *                                                                          *
 * MSQuant is distributed under the terms of                                *
 * the GNU General Public License. See src/COPYING.TXT or                   *
 * <http://www.gnu.org/licenses/gpl.txt> for details.                       *
 *                                                                          *
 * MSQuant is free software; you can redistribute it                        *
 * and/or modify it under the terms of the GNU                              *
 * General Public License as published by the Free                          *
 * Software Foundation; either version 2 of the                             *
 * License, or (at your option) any later version.                          *
 *                                                                          *
 * MSQuant is distributed in the hope that it will be                       *
 * useful, but WITHOUT ANY WARRANTY; without even the                       *
 * implied warranty of MERCHANTABILITY or FITNESS FOR                       *
 * A PARTICULAR PURPOSE.  See the GNU General Public                        *
 * License for more details.                                                *
 *                                                                          *
 * You should have received a copy of the GNU General                       *
 * Public License along with MSQuant; if not, write to                      *
 * the Free Software Foundation, Inc., 59 Temple                            *
 * Place, Suite 330, Boston, MA  02111-1307  USA                            *
 *                                                                          *
 * Purpose: ion series are computed based on a data                         *
 *          driven approach. The data may be provided from                  *
 *          the outside, e.g. an XML file edited by a user.                 *
 *          This class does not know about any particular                   *
 *          ion-series, e.g. y or b ions... - this is specified             *
 *                                                                          *
 *          Also handles a number of ion series tables where                *
 *          the effective table is dependent on some rules.                 *
 *          E.g. one table with some neutral loses if the                   *
 *          peptide has a phosphorylation modification.                     *
 *                                                                          *
 ****************************************************************************/

/****************************************************************************
 *                               CEBI                                       *
 *                    Software Development Group                            *
 *                         Peter Mortensen                                  *
 *                E-mail: NUKESPAMMERSdrmortensen@get2netZZZZZZ.dk          *
 *                 WWW: http://www.cebi.sdu.dk/                             *
 *                                                                          *
 *  Program for post-processing of result from search in mass               *
 *    spectrometric data.                                                   *
 *                                                                          *
 *    FILENAME:   PILgeneralisedIonSeries.cs                                *
 *    TYPE:       CSHARP                                                    *
 *                                                                          *
 * CREATED: PM 2008-05-08   Vrs 1.0.                                        *
 * UPDATED: PM 2008-xx-xx                                                   *
 *                                                                          *
 *                                                                          *
 ****************************************************************************/

//Future:
//  1. Some rules (specified in datastructures or in the class itself)
//     about some physically impossible ions, e.g. b1 ions for some
//     amino acids.
//
//  2. Proline feaure...
//
//  3. Presense of precursor. Special case of yN? (start, end) = (-1, -1)
//
//  4. Rename yIonArray() and bIonArray() to a more general name 
//     as e.g. z- og c-ions are now handled. Rename to backwardIonArray() 
//     and forwardIonArray().
//
//  5. 


using System.Collections.Generic; //For List and Dictionary.
using System.Diagnostics; //For Trace. And its Assert.

//using MolecularSharedStructures; //For modificationCountStruct.
using massSpectrometryBase.quantitation;


/****************************************************************************
 *    <placeholder for header>                                              *
 ****************************************************************************/
namespace massSpectrometryBase
{


    //Changed PM_REFACTOR 2008-05-10. Moved from somewhere else.
    /****************************************************************************
     *    <placeholder for header>                                              *
     ****************************************************************************/
    namespace quantitation
    {
        public struct AAsetStruct
        {

            public string AAs;
            public double diffFromBase3;

            //Changed PM_TERMMOD_FRAGMENTMASS_TROUBLE 2007-09-14
            //Peptide modification dependent helper field.
            public int INTERNAL_startPosition3;
            public int INTERNAL_endPosition3;
            //Note: even though this information is also in the
            // container structure, quantModificationStructure, there
            // are places in the program where only a list of
            // AAsetStructure is available.

            //Later perhaps:
            // Dim absoluteMass As Double
            // Dim absoluteMassByComposition As xyz By integer ID?
            // Dim compositionDiffFromBase As xyz By integer ID?

            //Selector for the type of mass?: dim massType as enumMassType
        } //struct AAsetStruct

    } //namespace quantitation


    //Changed PM_REFACTOR 2008-05-10. Moved from file peptideFragments.vb.
    //Changed PM_DTASC_COMPILE 2006-07-04. Moved here to avoid
    //dependency in DTASC.
    public struct ionSetStruct
    {
        public double[] forwardIonDiffs;
        public double[] backwardIonDiffs;

        //Index into list of combItemStructure
        public int compVectorIndex;

        //So client can later return to a return item.
        public int ID;

        //Dragged along here. For convenience.
        public double score;
    } //ionSetStruct


    //Changed PM_REFACTOR 2008-05-08 Moved from somewhere else.
    ////Changed PM_REFACTOR 2005-01-26
    /****************************************************************************
     *    <placeholder for header>                                              *
     ****************************************************************************/
    public enum ionTypeEnum
    {
        enumYion2 = 257,
        enumBion2,
        enumAion2,
        enumPrecursorIon2

        //Not yet..
        //'Changed PM_QUANT_CHARGE_DISPLAY 2005-03-14
        //eCustom 'For overriding colour and other visual display things.
    } //ionTypeEnum


    //Changed PM_REFACTOR 2008-05-08. Moved from file MMaaSequence.vb.
    // //Changed PM_DTASC_COMPILE 2008-02-11. Moved to here
    // // from peptideFragments.vb to avoid dependencies
    // // with DTASC. But is this a good place???
    // // In DTASC the dependency is from class SDUPspectrumMarking
    // // and class frmSpectrumDisplay.
    /****************************************************************************
     *    <placeholder for header>                                              *
     ****************************************************************************/
    public struct fragmentExStructure
    {
        public double MCRcalcLoc;

        //Changed PM_REFACTOR 2005-02-24. Now computed on the client side.
        //Dim yLoc As Double

        //Not cuurently used.
        //Dim MCRobsLoc As Double
        //Dim deltaMass As Double

        public string seq;
        public string descStr;
        public int charge;

        ////Enumeration instead of Bion and Yion
        //Dim Yion As Boolean
        //Dim Bion As Boolean
        //Dim precursorIon As Boolean
        public ionTypeEnum ionType; //Note: we keep it for now. Works
        //  OK for special things for precursors and for colouring.

        //Helper field.
        public bool ionMatched;

        //Changed PM_FRAGMENTDISPLAY 2008-05-18
        public int yDisplayLevel2;
        public int yDisplayLevelForMatching2;

    } //fragmentExStructure


    /****************************************************************************
     *    Purpose: represents a single ion series
     *
     ****************************************************************************/
    public struct singleIonSeriesStructure
    {
        public bool useModSeqRule; //Whether or not fragment amino acids 
        //  sequences for this ion series need to contain one or more of 
        //  the amino acids affected by the matched modification (for the 
        //  entire ion series table).

        public int ionSeriesID2;
        public string ionSeriesName;
        public string shortName2; //E.g. "y", "b", "c" or "z".
        public string postFix; //E.g. "-98", "-phos", "-H20" or "-NH2".

        //Also allow specification by composition?
        public double offsetFromResidueToNeutral; //E.g. +18 Da for y-series.
                                                  //The charge transform is done
                                                  //in code, not by specification.
                                                  //But any value can be specified,
                                                  //it just need some

        public bool forward; //true: forward. false: backward. Use enum instead?

        public int colour; //For display and currently also implies
                           //forward/backward (e.g. for MS3 scoring - in
                           //matchFragmentIons()).
                           //Some code for colour. Use RGB?
                           //
                           //For now: internal code:
                           //  419   y
                           //  421   b
                           //  431   a
                           //  433   precursor

        //Changed PM_FRAGMENTDISPLAY 2008-05-18
        public int yDisplayLevel4;
          //Effective values for ***older*** versions of the
          //application (in code):
          //  0: b-ions
          //  1: y-ions
          //  2: y++ -ions
        public int yDisplayLevelForMatching4;

        public bool isPrecursorDerived;

        public int charge;

        //Constraints for using a subset of the whole ion series - 
        //corresponding to subset of the peptide.
        public int startIonNumber; //1 for first ion, e.g. y1.
        //  Negative: from N. -3 for yN-3, y14 for a 17 long peptide.

        public int endIonNumber; //1 for first ion, e.g. y1.
        //  Negative: from N. -3 for yN-3, y14 for a 17 long peptide.

        //Neutral mass constraint. Using a subset of the whole ion
        //series - corresponding to subset of the peptide.
        public double startMass; //Positive for absolute mass. Negative for
                                 //difference from neutral precursor mass.
        public double endMass;   //Positive for absolute mass. Negative for
                                 //difference from neutral precursor mass.

        //Constraints for the number of modifications. This is a condition 
        //whether the whole ion series should be used or not. E.g. only
        //use y-196 (2 x phosphorylation loss) if the number of
        //modifications (the matched one) is 2 or more.
        public int startMods2; //
        public int endMods2; //
    } //struct singleIonSeriesStructure


    /****************************************************************************
     *    Purpose: a single rule for matching an ion series table.
     *
     ****************************************************************************/
    public struct matchRuleStructure
    {
        public int matchModID; //Reference to a modification. -1 means
        //  anything, even non-modified. That is: it will always match and
        //  as such the default ion set can be specified by this way.

        //The number of modifications in the peptide (for the match
        //modification - field above) must be within this range:

        //Not yet.
        //public int startMods7;
        //public int endMods7;

        //What was this for??
        //public string matchString3; //Regular expression.


        //Changed PM_GENERALISED_IONSERIES_SPECTRUMCLASSIFICATION 2008-06-23
        public spectrumSubTypeEnum MSMSspectrumClassification;


        //Changed PM_MARKER_SPECTRUMCLASSIFICATION 2008-06-23
        //We need to refactor, possibly the whole 
        //  of spectrumClassifier.vb. At least spectrumTypeEnum and 
        //  spectrumSubTypeEnum.

    } //struct matchRuleStructure


    /****************************************************************************
     *    Purpose: represents a particular set of ions (e.g. only a2, all
     *             single charged y-ions, double charged y-ions with
     *             neutral mass less than 700 Da  or another set, e.g. single
     *             charged y-ions and single charged y-ions with 98 Da loss.
     *
     *    Used for specifying .
     *
     ****************************************************************************/
    public struct ionSeriesTableStructure
    {
        public int ionTableID;
        public string ionTableName;

        public List<matchRuleStructure> rules2; //Rules as to whether this
        //table should be used for computing the set of ions. E.g. a
        //particular one if a peptide has a phosphorylation modification.

        //Changed PM_REFACTOR 2008-06-23. To reduce redundancy (several 
        //  rules using the same ion-series (e.g. y-series) we now have
        //  the ion series in a separate place and refer to them here 
        //  by an ID (integer))
        //
        ////A number of ion series for this ion table.
        //public List<singleIonSeriesStructure> ionSeries;
        public List<int> ionSeries2;
    } //struct ionSeriesTableStructure


    //Changed PM_REFACTOR 2008-10-09
    /****************************************************************************
     *    Purpose: have all related to ion series in a single structure
     *
     *    Used for specifying .
     *
     ****************************************************************************/
    public struct ionDefinitionsStructure
    {
        public List<ionSeriesTableStructure> ionTables;
              
        public List<singleIonSeriesStructure> singleIonSeries;
    } //struct ionDefinitionsStructure


    /****************************************************************************
     *    <placeholder for header>                                              *
     ****************************************************************************/
    public class PILgeneralisedIonSeries
    {
        //Changed PM_REFACTOR 2008-10-09
        // private List<ionSeriesTableStructure> mIonTables2;
        //
        // //Changed PM_REFACTOR 2008-06-23. Now two level defition (to reduce
        // //redundancy).
        // private List<singleIonSeriesStructure> mSingleIonSeries;
        private ionDefinitionsStructure mIonDefs;


        private List<string> mChargeStrings;

        //Outer pair is (modification code, [info]).
        //[info]: list of amino acids that the modification affects.
        private Dictionary<int, List<string>> mModHash;
        private quantitation.QuantitationModes_moreGeneral mQuantObject;


        //Changed PM_CODEBRANCH 2007-10-26.
        const bool CODEBRANCH_FIXED = true;
        //Normally True. Can be set to false to reproduce a bug... This
        //constant should be removed soon.



        //****************************************************************************
        //*  SUBROUTINE NAME:   New                                                  *
        //d$ <summary>Constructor</summary>
        public PILgeneralisedIonSeries(
          ref quantitation.QuantitationModes_moreGeneral anInQuantObject,
          ionDefinitionsStructure anInIonDefs
          )

            : base()
        {
            mQuantObject = anInQuantObject;


            //Build strings for charge notation, for easy lookup (and saving memory!!).
            {
                int len = 20;
                mChargeStrings = new List<string>(len);
                string chstr = "";
                for (int i = 0; i < len; i++)
                {
                    chstr += "+";
                    mChargeStrings.Add(chstr);
                }
            }


            Trace.Assert(
              anInIonDefs.ionTables != null &&
              anInIonDefs.ionTables.Count > 0, 
              "PIL ASSERT. anInIonDefs.ionTables is undefined or empty.");

            Trace.Assert(
              anInIonDefs.singleIonSeries != null &&
              anInIonDefs.singleIonSeries.Count > 0,
              "PIL ASSERT. anInIonDefs.singleIonSeries is undefined or empty.");

            //Changed PM_PERSIST_IONSERIES 2008-10-09
            ////For now until we can read in the definitions from an XML file.
            ////Changed PM_REFACTOR 2008-10-09
            ////defaultIonTables(out mIonTables2, out mSingleIonSeries);
            //mIonDefs = defaultIonTables();
            mIonDefs = anInIonDefs;

            mModHash = new Dictionary<int, List<string>>();
        } //Constructor.


        //****************************************************************************
        //* <placeholder for header> *
        //* anInModificationsList: type is simpleModificationSpecification *
        //* *
        //****************************************************************************
        private static List<double> constructMassTable(
          ref List<List<AAsetStruct>> anInModificationsList)
        {
            //Changed PM_QUANTGENERAL_PREPARATION 2006-07-14
            //Old:
            // ByRef anInModificationsList As ArrayList

            //Changed PM_MEMORY_EFFICIENCY_256 2006-10-31
            //Dim massTable(256) As Double
            int len = 124;
            List<double> massTable = new List<double>();
            //Corresponding to z. Could it even
            // be less, 91 for Z ?

            //Changed PM_REFACTOR 2008-05-17
            for (int i = 0; i < len; i++)
            {
                massTable.Add(0.0);
            }
            
            //Note: this is repeated in SetAAMassTable()s!!!!
            {
                //Default masses
                massTable[(int)('A')] = MSconstants.ALA_MONO_MASS;
                massTable[(int)('C')] = MSconstants.CYS_CARBAMIDOMETHYL_MONO_MASS;
                //We assume Cys-carbamido as a fixed modication in the Mascot search.
                massTable[(int)('D')] = MSconstants.ASP_MONO_MASS;
                massTable[(int)('E')] = MSconstants.GLU_MONO_MASS;
                massTable[(int)('F')] = MSconstants.PHE_MONO_MASS;
                massTable[(int)('G')] = MSconstants.GLY_MONO_MASS;
                massTable[(int)('H')] = MSconstants.HIS_MONO_MASS;
                massTable[(int)('I')] = MSconstants.IIE_MONO_MASS;
                massTable[(int)('K')] = MSconstants.LYS_MONO_MASS;
                massTable[(int)('L')] = MSconstants.LEU_MONO_MASS_NORMAL;
                massTable[(int)('M')] = MSconstants.MET_MONO_MASS_NORMAL;
                massTable[(int)('N')] = MSconstants.ASN_MONO_MASS;
                massTable[(int)('P')] = MSconstants.PRO_MONO_MASS;
                massTable[(int)('Q')] = MSconstants.GLN_MONO_MASS;
                massTable[(int)('R')] = MSconstants.ARG_MONO_MASS_NORMAL;
                massTable[(int)('S')] = MSconstants.SER_MONO_MASS;
                massTable[(int)('T')] = MSconstants.THR_MONO_MASS;
                massTable[(int)('V')] = MSconstants.VAL_MONO_MASS;
                massTable[(int)('W')] = MSconstants.TRP_MONO_MASS;
                massTable[(int)('Y')] = MSconstants.TYR_MONO_MASS;

                //Changed PM_SNIPS_OUJ_RKN 2006-11-29
                massTable[(int)('U')] = MSconstants.LYS_MONO_MASS; //As K
                massTable[(int)('O')] = MSconstants.ARG_MONO_MASS_NORMAL; //As R
                massTable[(int)('J')] = MSconstants.ASN_MONO_MASS; //As N
            }

            {
                //Apply modifications
                //Outer loop: for each modification
                foreach ( List<AAsetStruct> someModItem in anInModificationsList)
                {

                    //Changed PM_QUANTGENERAL_PREPARATION 2006-07-14
                    //Dim someModItem As quantitation.simpleModificationSpecification

                    //Inner loop: for each AA set (usually only one, but 20 for N15)
                    foreach (AAsetStruct someAAset in someModItem)
                    {
                        //Changed PM_MULTIPLE_AA_FOR_MODIFICATION 2005-08-10

                        //Changed PM_QUANTGENERAL_PREPARATION 2006-07-14
                        //Dim AA As String = someModItem.AA
                        string AA = someAAset.AAs;

                        int lastIndex = someAAset.AAs.Length - 1;
                        int j;
                        for (j = 0; j <= lastIndex; j++)
                        {
                            if (lastIndex > 0)
                            {
                                //Optimisation as for most
                                // modifications there is only one AA (in
                                // that case it is already set).
                                AA = someAAset.AAs.Substring(j, 1);
                            }

                            //int index = (int)((char)AA);
                            int index = (int)(AA[0]);
                            
                            double oldValue = massTable[index];

                            double diffFromBase = someAAset.diffFromBase3;
                            double newValue = oldValue + diffFromBase;

                            //Changed PM_HYSTAG_BADMASS 2005-07-07. For now: until we
                            //get rid of explicit Carbamido-Cys in the code we assume
                            //that .
                            // The condition is effectively detection of
                            // baseModification greater than 0. Or in order
                            // words HysTag/ICAT like quantitation modes.
                            //

                            //Changed PM_120DA_RULE 2007-09-12. Partly fixed, but
                            // not a fundamental fix. This will wait until fixed
                            // modificatios are read in from the Mascot result
                            // file and the Carbamido-Cys assumption can be removed
                            // all over the code.
                            //
                            //'Changed PM_UBIK_ASSERT 2006-03-28
                            //'Changed PM_NOT_HYSTAG_FOR_PHOSPHO 2005-07-20
                            //'If someModItem.diffFromBase > 30.0 Then
                            //'If someModItem.diffFromBase > 100.0 Then
                            //If diffFromBase > 120.0 Then
                            //If diffFromBase > 30.0 AndAlso (AA = "C" OrElse AA = "c") Then
                            if (diffFromBase > 100.0 && (AA == "C" || AA == "c"))
                            {
                                // This is a detection of wether fixed mod Carbamido has
                                // been used or not.
                                // Not 30.0: Acetyl N-term is 42 Da!
                                //
                                //The mass limit is to allow lower mass modifications to
                                //act on Cysteine, e.g. N15 on Carbamido-Cys.

                                //This is for ICAT, HysTag and similar. In this case
                                //it is assumed that Carbamidomethyl is ***not***
                                //a fixed modification. In all other cases this
                                //fixed modification for Cys is assumed.

                                //newValue = diffFromBase This made the diff specification
                                // into an absolute value!
                                newValue =
                                  MSconstants.CYS_MONO_MASS_NORMAL + diffFromBase;
                            }

                            massTable[index] = newValue;
                        } //Through AA set.
                    } //Through someModItem.
                } //Through modifications.
            }
            return massTable;
        } //constructMassTable()


        //Changed PM_REFACTOR 2008-05-17. Moved from file MMaaSequence.vb
        //Changed PM_REFACTOR_GLOBALS 2003-10-09
        //****************************************************************************
        //* <placeholder for header> *
        //****************************************************************************
        public static void effectiveMasses(
          string aModStr,
          out double anOutArgMass,
          out double anOutLeuMass,
          out double anOutMetMass,
          out double anOutCysMass)
        {
            anOutArgMass = MSconstants.ARG_MONO_MASS_NORMAL;
            anOutLeuMass = MSconstants.LEU_MONO_MASS_NORMAL;
            anOutMetMass = MSconstants.MET_MONO_MASS_NORMAL;

            //Changed PM_CYS_TROUBLE 2003-11-14
            //anOutCysMass = CYS_MONO_MASS_NORMAL
            anOutCysMass = -1000000000.0;

            string lowerModStr = aModStr.ToLower();

            if (lowerModStr.IndexOf("arg") >= 0)
            {
                anOutArgMass = MSconstants.ARG_C6_MONO_MASS;
            }

            if (lowerModStr.IndexOf("leu") >= 0)
            {
                //assume single D3 modification for now
                anOutLeuMass = MSconstants.LEU_D3_MONO_MASS;
            }

            if (lowerModStr.IndexOf("ox") >= 0)
            {
                anOutMetMass = MSconstants.MET_OX_MONO_MASS;
            }

            //Changed PM_CYS_TROUBLE 2003-11-20
            bool someCys = false;
            if (lowerModStr.IndexOf("(c)") >= 0)
            {
                someCys = true;
            }
            if (lowerModStr.IndexOf("cys") >= 0)
            {
                someCys = true;
            }

            if (someCys)
            {
                if (lowerModStr.IndexOf("carbamido") >= 0)
                {
                    anOutCysMass = MSconstants.CYS_CARBAMIDOMETHYL_MONO_MASS;
                }
                else
                {
                    //Changed PM_CYS_TROUBLE 2003-11-14
                    if (lowerModStr.IndexOf("nocysmod") >= 0)
                    {
                        anOutCysMass = MSconstants.CYS_MONO_MASS_NORMAL;
                    }
                    else
                    {
                        if (lowerModStr.IndexOf("cyshy0") >= 0)
                        {
                            //Changed PM_HYSTAG_BADMASS 2005-07-07
                            //anOutCysMass = CYS_CARBAMIDOMETHYL_MONO_MASS + CYS_HYS_ALA_D0
                            anOutCysMass = MSconstants.CYS_HYS_ALA_D0;
                        }
                        else
                        {
                            if (lowerModStr.IndexOf("cyshy4") >= 0)
                            {
                                //Changed PM_HYSTAG_BADMASS 2005-07-07
                                //anOutCysMass = CYS_CARBAMIDOMETHYL_MONO_MASS + CYS_HYS_ALA_D4
                                anOutCysMass = MSconstants.CYS_HYS_ALA_D4;
                            }
                            else
                            {
                                Trace.Assert(false, "PIL ASSERT. Cysteine modification not explicit!.");
                            }
                        }
                    }
                }
            }
            else
            {
                int peter4 = 4;
                anOutCysMass = MSconstants.CYS_CARBAMIDOMETHYL_MONO_MASS;
            }
        } //effectiveMasses()


        //Changed PM_REFACTOR 2008-05-16. Moved from peptideFragments.vb.
        //****************************************************************************
        //* <placeholder for header> *
        //* anInModificationsList: type is simpleModificationSpecification *
        //* *
        //****************************************************************************
        public static void backwardIonArray(
            ref string anInSeq,
            out List<double> anOutFragMasses2,
            out int aOutMaxCalcArrIdx,
            string aModStr,
            ref List<List<AAsetStruct>> anInModificationsList, 
            bool aMS3_BionFlag)
        {
            //Old type for anOutFragMasses:
            // anOutFragMasses() As Double

            //This function takes the peptide sequence as input and fills an
            //allocated array with doubles for Y ion masses.

            //'Changed PM_GENERALISED_QUANT_MODE 2003-12-10
            //'Changed PM_CYS_TROUBLE 2003-11-14
            //Dim modStrToUse As String = aModStr
            //If modStrToUse = "" Then
            // modStrToUse = "noCysMod"
            //End If
            //'Changed PM_REFACTOR_GLOBALS 2003-10-09
            //Dim argEffectiveMass As Double
            //Dim leuEffectiveMass As Double
            //Dim metEffectiveMass As Double
            //Dim cysEffectiveMass As Double
            //effectiveMasses(modStrToUse, _
            // argEffectiveMass, leuEffectiveMass, metEffectiveMass,
            // cysEffectiveMass)

            //Why is effectiveMasses not called ??? (it is for b-ions...)

            //Changed PM_GENERALISED_QUANT_MODE 2003-12-10
            List<double> massTable = constructMassTable(ref anInModificationsList);

            //Name it backwardIonMass?
            double yIonMass = MSconstants.Y_ION_OFFSET;

            //Changed PM_MS3_DTASUPERCHARGE 2004-04-15
            if (aMS3_BionFlag)
            {
                yIonMass = MSconstants.B_ION_OFFSET;
                //In the case of y ions from b-ion
                // precusors, the mass is like an internal fragment.
            }

            //Changed PM_BAD_PRECURSORMASS 2006-10-31.
            //'Changed PM_INFINITE_FRAGMENTS 2005-08-09
            // '' It is aSeq.Length - 2 because zero based array and n-1 y ions
            // '' for seq of length n.
            //'aMaxCalcArrIdx = _
            //' Math.Min(aSeq.Length - 2, MAX_Y_IONS_TO_DISPLAY - 1) 'Limit to an
            //' array of currently 100 Y ions.
            //aOutMaxCalcArrIdx = aSeq.Length - 2
            int plen = anInSeq.Length;

            //Changed PM_REFACTOR 2008-05-08
            anOutFragMasses2 = new List<double>(plen);

            int lastIndex = plen - 1;
            aOutMaxCalcArrIdx = lastIndex;
            //Include yN in the computation

            int i;

            //Changed PM_BAD_PRECURSORMASS 2006-10-31
            //For i = aOutMaxCalcArrIdx + 1 To 1 Step -1
            // Fill the array
            // from y1 to yn-1 (if we did it to 0 then we would get
            // the single protonated mass as well.
            for (i = lastIndex; i >= 0; i += -1)
            {
                //Include yN.

                //Changed PM_REFACTOR 2008-05-17
                //char curAA = anInSeq.Chars(i);
                char curAA = anInSeq[i];

                //Changed PM_REFACTOR 2008-05-17
                //double curMass = massTable(Strings.AscW(curAA));
                double curMass = massTable[(int)curAA];

                yIonMass += curMass;

                //Changed PM_REFACTOR 2008-05-08
                //anOutFragMasses(lastIndex - i) = yIonMass
                anOutFragMasses2.Add(yIonMass);
            }

            //Changed PM_TERMMOD_FRAGMENTMASS_TROUBLE 2007-09-14
            {
                //Undo for modifications that do not span the entire
                // peptide sequence, e.g. terminal modifications. In this
                // case we can still use our mass table above for majority of
                // the calculations.

                foreach (
                  List<AAsetStruct> someModItem in anInModificationsList)
                {
                    bool allPositions = false; //Keep compiler happy.
                    int startPos0 = -1; //Flag for below.
                    int endPos0 = -1; //Keep compiler happy.

                    foreach ( AAsetStruct someAAset in someModItem)
                    {
                        if (startPos0 == -1)
                        {
                            //-1 is a flag - we only use the
                            // first item in the someModItem list. It is the only
                            // item for which the INTERNAL fields are defined.

                            int maxMods;

                            PILpeptide.findPositionsEtc(
                              someAAset.INTERNAL_startPosition3,
                              someAAset.INTERNAL_endPosition3,
                              plen,
                              out allPositions,
                              out startPos0,
                              out endPos0,
                              out maxMods);
                        }
                        else
                        {
                            int peter2 = 2;
                            //E.g. for N15
                        }

                        if (!allPositions)
                        {
                            foreach (char someAA in someAAset.AAs)
                            {
                                double corrMass = 0.0;
                                bool corrMassIsZero = true;

                                for (i = lastIndex; i >= 0; i += -1)
                                {
                                    //Include yN.

                                    //Dim oneBaseIndex As Integer = i + 1
                                    bool insideRange = i >= startPos0 && 
                                                       i <= endPos0;

                                    if (!insideRange)
                                    {
                                        //Changed PM_REFACTOR 2008-05-17
                                        //char curAA = anInSeq.Chars(i);
                                        char curAA = anInSeq[i];

                                        if (someAA == curAA)
                                        {
                                            //New correction mass.
                                            corrMass += someAAset.diffFromBase3;
                                            corrMassIsZero = false;
                                        }

                                        //Changed PM_CODEBRANCH 2007-10-26.
                                        if (!CODEBRANCH_FIXED)
                                        {

                                            //Changed PM_IMPOSSIBLELOWMASS_ASSERT 2007-10-26. Old location.
                                            if (!corrMassIsZero)
                                            {
                                                anOutFragMasses2[lastIndex - i] -= corrMass;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        //Inside range. The correction mass should
                                        //not be updated, but we still need to
                                        //correct the fragment mass (done just
                                        //below) as fragment masses are
                                        //accumulative - as the mass of an amino
                                        //acid residue affects ALL higher mass
                                        //fragments.
                                        int peter3 = 3;
                                    }

                                    //Changed PM_CODEBRANCH 2007-10-26.
                                    if (CODEBRANCH_FIXED)
                                    {

                                        //Changed PM_IMPOSSIBLELOWMASS_ASSERT 2007-10-26.
                                        //Moved out to this level.
                                        if (!corrMassIsZero)
                                        {
                                            anOutFragMasses2[lastIndex - i] -= corrMass;
                                        }
                                    }
                                }
                                //Through peptide sequence.
                            }
                            //Through affected AAs for a modification, usually
                            // only one. But 20 for N15.
                        }
                        //Modification with not all positions in peptide.
                    }
                    //Through one modification - through sets of ***different***
                    // mass diffs.
                }
                //Through modifications.
            } //Block, undoing some fragment massses.
        } //backwardIonArray()


        //****************************************************************************
        //* <placeholder for header> *
        //* anInModificationsList: type is simpleModificationSpecification *
        //****************************************************************************
        public static void forwardIonArray(
            ref string anInSeq,
            out List<double> anOutFragMasses3,
            out int anOutMaxCalcArrIdx,
            string aModStr,
            ref List<List<AAsetStruct>> anInModificationsList)
        {
            //Old type for anOutFragMasses2:
            // anOutFragMasses2() As Double

            //This function takes the peptide sequence as input and fills an
            //allocated array with doubles for b ion masses up to a maximum of 100 b ions.
            //this is so that no array needs to created or re-dimensioned which would
            //take time

            //Changed PM_CYS_TROUBLE 2003-11-14
            string modStrToUse = aModStr;
            if (modStrToUse == "")
            {
                modStrToUse = "noCysMod";
            }

            //Changed PM_REFACTOR_GLOBALS 2003-10-09
            double argEffectiveMass;
            double leuEffectiveMass;
            double metEffectiveMass;
            double cysEffectiveMass;
            effectiveMasses(
              modStrToUse,
              out argEffectiveMass, 
              out leuEffectiveMass, 
              out metEffectiveMass,
              out cysEffectiveMass);

            //Changed PM_GENERALISED_QUANT_MODE 2003-12-10
            List<double> massTable = constructMassTable(ref anInModificationsList);

            //Name it forwardIonMass?
            double BionMass = MSconstants.B_ION_OFFSET; 
 
            int i;
            // It is aSeq.Length - 2 because zero based array and n-1 y ions
            // for seq of length n.

            //Changed PM_INFINITE_FRAGMENTS 2005-08-09
            //'Limit to an array of currently 100 b ions.
            //maxCalcArrIdx = Math.Min(aSeq.Length - 2, MAX_Y_IONS_TO_DISPLAY - 1)
            int plen = anInSeq.Length;

            //'Changed PM_UNITTESTS 2008-05-20Changed PM_UNITTESTS 2008-05-20
            //anOutMaxCalcArrIdx = plen - 2;
            anOutMaxCalcArrIdx = plen - 1; //Do include bN. For generalised ion series.

            //Changed PM_REFACTOR 2008-05-08
            anOutFragMasses3 = new List<double>(plen);

            for (i = 0; i <= anOutMaxCalcArrIdx; i++)
            {
                // + 1
                //Changed PM_REFACTOR 2008-05-17
                //char curAA = anInSeq.Chars(i);
                char curAA = anInSeq[i];

                //Changed PM_REFACTOR 2008-05-17
                //double curMass = massTable[Strings.AscW(curAA)];
                double curMass = massTable[(int)curAA];

                BionMass += curMass;

                //Changed PM_REFACTOR 2008-05-08
                //anOutFragMasses2(i) = BionMass
                anOutFragMasses3.Add(BionMass);
            }

            //Changed PM_TERMMOD_FRAGMENTMASS_TROUBLE 2007-09-14
            {
                //Undo for modifications that do not span the entire
                // peptide sequence, e.g. terminal modifications. In this
                // case we can still use our mass table above for majority of
                // the calculations.

                foreach ( List<AAsetStruct> someModItem in anInModificationsList)
                {
                    int startPos0 = -1; //Flag for below.
                    int endPos0 = -1; //Keep compiler happy.
                    //Just in case.
                    bool allPositions = false; //Keep compiler happy.

                    foreach (
                      AAsetStruct someAAset in someModItem)
                    {

                        if (startPos0 == -1)
                        {
                            //-1 is a flag - we only use the
                            // first item in the someModItem list. It is the only
                            // item for which the INTERNAL fields are defined.

                            int maxMods;
                            PILpeptide.findPositionsEtc(
                              someAAset.INTERNAL_startPosition3,
                              someAAset.INTERNAL_endPosition3,
                              plen,
                              out allPositions,
                              out startPos0,
                              out endPos0,
                              out maxMods);
                        }
                        else
                        {
                            int peter2 = 2;
                            //E.g. for N15
                        }

                        if (!allPositions)
                        {
                            foreach (char someAA in someAAset.AAs)
                            {

                                double corrMass = 0.0;
                                bool corrMassIsZero = true;

                                for (i = 0; i <= anOutMaxCalcArrIdx; i++)
                                {

                                    bool insideRange = i >= startPos0 && i <= endPos0;

                                    if (!insideRange)
                                    {
                                        //Changed PM_REFACTOR 2008-05-17
                                        //char curAA = anInSeq.Chars(i);
                                        char curAA = anInSeq[i];

                                        if (someAA == curAA)
                                        {
                                            //New correction mass.
                                            corrMass += someAAset.diffFromBase3;
                                            corrMassIsZero = false;
                                        }

                                        //Changed PM_CODEBRANCH 2007-10-26.
                                        if (!CODEBRANCH_FIXED)
                                        {

                                            //Changed PM_IMPOSSIBLELOWMASS_ASSERT 2007-10-26. Old location.
                                            if (!corrMassIsZero)
                                            {
                                                anOutFragMasses3[i] -= corrMass;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        //Inside range. The correction mass should
                                        //not be updated, but we still need to
                                        //correct the fragment mass (done just
                                        //below) as fragment masses are
                                        //accumulative - as the mass of an amino
                                        //acid residue affects ALL higher mass
                                        //fragments.
                                        int peter3 = 3;
                                    }

                                    //Changed PM_CODEBRANCH 2007-10-26.
                                    if (CODEBRANCH_FIXED)
                                    {

                                        //Changed PM_CODEBRANCH 2007-10-26. Moved out to this level.
                                        if (!corrMassIsZero)
                                        {
                                            anOutFragMasses3[i] -= corrMass;
                                        }
                                    }

                                } //Through peptide sequence.
                            } //Through affected AAs for a modification, usually
                              // only one. But 20 for N15.
                        } //Modification with not all positions in peptide.
                    } //Through one modification - through sets of ***different***
                      // mass diffs.
                } //Through modifications.
            } //Block, undoing some fragment massses.
        } //forwardIonArray()


        //Changed PM_PERSIST_IONSERIES 2008-10-10
        //****************************************************************************
        //*  SUBROUTINE NAME:   addSingleChargedYionSeries
        //d$ <summary>Helper function for the functions below, e.g. 
        //            returns "triple" for an input of 3. </summary>
        //
        //****************************************************************************
        private static string chargeWord(int aCharge)
        {
            //Use (static) hash instead of this function??

            string toReturn = ""; //Safe value.
            switch (aCharge)
            {
                case 0:
                    toReturn = "Neutral";
                    break;
                case 1:
                    toReturn = "Single";
                    break;
                case 2:
                    toReturn = "Double";
                    break;
                case 3:
                    toReturn = "Triple";
                    break;
                case 4:
                    toReturn = "Quadrouple";
                    break;
                default:
                    toReturn = "Multiple";
                    break;
            } //switch
            toReturn += " charged";
            return toReturn;
        }


        //****************************************************************************
        //*  SUBROUTINE NAME:   addSingleChargedYionSeries
        //d$ <summary>Add definition for common ion-series: single charged y-ions</summary>
        //
        //****************************************************************************
        private static void addSingleChargedYionSeries(
            ref List<singleIonSeriesStructure> anInOutIonTables,
            int anIonSeriesID
            )
        {
            { //Single charged y-ions.
                singleIonSeriesStructure fullYseries;

                //Changed PM_REFACTOR 2008-06-23
                //fullYseries.ionSeriesID = 1201;
                fullYseries.ionSeriesID2 = anIonSeriesID;

                fullYseries.ionSeriesName = "Single charged full y series.";
                fullYseries.shortName2 = "y";
                fullYseries.postFix = "";

                fullYseries.offsetFromResidueToNeutral =
                  MSconstants.NORMAL_C_TERMINUS_MONO +
                  MSconstants.NORMAL_N_TERMINUS_MONO; //18 Da, Water.

                fullYseries.forward = false;
                fullYseries.colour = 419;
                fullYseries.isPrecursorDerived = false;

                fullYseries.charge = 1;

                //fullYseries.startAA = 1 + 4; //Start with y5.
                fullYseries.startIonNumber = 1 + 0; //Start with y1.

                fullYseries.endIonNumber = -1 - 1; //Not yN.
                fullYseries.startMass = 0.1;
                fullYseries.endMass = -0.1;

                //No rule for number of modifications.
                fullYseries.startMods2 = 0;
                fullYseries.endMods2 = 999;

                fullYseries.useModSeqRule = false;

                //Changed PM_FRAGMENTDISPLAY 2008-05-18
                fullYseries.yDisplayLevel4 = 0; //2-1
                fullYseries.yDisplayLevelForMatching4 = 4;

                anInOutIonTables.Add(fullYseries);
            }
        } //addSingleChargedYionSeries().


        //Later: refactor; merge with addSingleChargedYionSeries().
        //****************************************************************************
        //*  SUBROUTINE NAME:   addDoubleChargedYionSeries
        //d$ <summary>Add definition for common ion-series: double charged
        //            y-ions below a certain mass limit</summary>
        //
        //****************************************************************************
        private static void addDoubleChargedYionSeries(
            ref List<singleIonSeriesStructure> anInOutIonTables, 
            int anIonSeriesID,
            double aMinMass
            )
        {
            { //Mass restricted double charged y series.
                singleIonSeriesStructure YdoubleSeries;

                //Changed PM_REFACTOR 2008-06-23
                //YdoubleSeries.ionSeriesID = 1210;
                YdoubleSeries.ionSeriesID2 = anIonSeriesID;

                YdoubleSeries.ionSeriesName = "Double charged mass restricted y series.";
                YdoubleSeries.shortName2 = "y";
                YdoubleSeries.postFix = "";

                YdoubleSeries.offsetFromResidueToNeutral =
                  MSconstants.NORMAL_C_TERMINUS_MONO +
                  MSconstants.NORMAL_N_TERMINUS_MONO; //18 Da, Water.

                YdoubleSeries.forward = false;
                YdoubleSeries.colour = 419;
                YdoubleSeries.isPrecursorDerived = false;

                YdoubleSeries.charge = 2;

                //YdoubleSeries.startAA = 1 + 4; //Start with y5.
                YdoubleSeries.startIonNumber = 1 + 0; //Start with y1.

                YdoubleSeries.endIonNumber = -1 - 1; //Not yN.
                YdoubleSeries.startMass = aMinMass;
                YdoubleSeries.endMass = -0.1;

                //No rule for number of modifications.
                YdoubleSeries.startMods2 = 0;
                YdoubleSeries.endMods2 = 999;

                YdoubleSeries.useModSeqRule = false;

                //Changed PM_FRAGMENTDISPLAY 2008-05-18
                YdoubleSeries.yDisplayLevel4 = 0;

                //Changed PM_LEVEL_FOR_MATCHED_DOUBLE_YIONS 2008-10-22
                //YdoubleSeries.yDisplayLevelForMatching4 = 0; //3
                YdoubleSeries.yDisplayLevelForMatching4 = 1;

                anInOutIonTables.Add(YdoubleSeries);
            }
        } //addDoubleChargedYionSeries().


        //****************************************************************************
        //*  SUBROUTINE NAME:   addSingleChargedBionSeries
        //d$ <summary>Add definition for common ion-series: single charged b-ions</summary>
        //
        //****************************************************************************
        private static void addSingleChargedBionSeries(
            ref List<singleIonSeriesStructure> anInOutIonTables,
            int anIonSeriesID
            )
        {
            //Single charged b-ions.
            singleIonSeriesStructure fullBseries;

            //Changed PM_REFACTOR 2008-06-23
            //fullBseries.ionSeriesID = 2001;
            fullBseries.ionSeriesID2 = anIonSeriesID;

            fullBseries.ionSeriesName = "Single charged full b series.";
            fullBseries.shortName2 = "b";
            fullBseries.postFix = "";

            fullBseries.offsetFromResidueToNeutral = 0.0;

            fullBseries.forward = true;
            fullBseries.colour = 421;
            fullBseries.isPrecursorDerived = false;

            fullBseries.charge = 1;

            fullBseries.startIonNumber = 1 + 0; //Start with b1.
            fullBseries.endIonNumber = -1 - 1; //Not bN.

            fullBseries.startMass = 0.1;
            fullBseries.endMass = -0.1;

            //No rule for number of modifications.
            fullBseries.startMods2 = 0;
            fullBseries.endMods2 = 999;

            fullBseries.useModSeqRule = false;

            //Changed PM_FRAGMENTDISPLAY 2008-05-18
            fullBseries.yDisplayLevel4 = 0; //1
            fullBseries.yDisplayLevelForMatching4 = 4;

            anInOutIonTables.Add(fullBseries);
        } //addSingleChargedBionSeries().


        //****************************************************************************
        //*  SUBROUTINE NAME:   addA2ionSeries
        //d$ <summary>Add definition for common ion-series: single charged b-ions</summary>
        //
        //****************************************************************************
        private static void addA2ionSeries(
          ref List<singleIonSeriesStructure> anInOutIonTables,
          int anIonSeriesID
          )
        {
            { //Single charged a2 ion.
                singleIonSeriesStructure a2series;

                //Changed PM_REFACTOR 2008-06-23
                //a2series.ionSeriesID = 3001;
                a2series.ionSeriesID2 = anIonSeriesID;

                a2series.ionSeriesName = "Single charged a2 only... (as in a2/b2 pair).";
                a2series.shortName2 = "a";
                a2series.postFix = "";

                a2series.offsetFromResidueToNeutral = -MSconstants.CO_MASS_MONO;

                a2series.forward = true;
                a2series.colour = 431;
                a2series.isPrecursorDerived = false;

                a2series.charge = 1;

                //a2 only!
                a2series.startIonNumber = 2;
                a2series.endIonNumber = 2;

                a2series.startMass = 0.1;
                a2series.endMass = -0.1;

                //No rule for number of modifications.
                a2series.startMods2 = 0;
                a2series.endMods2 = 999;

                a2series.useModSeqRule = false;

                //Changed PM_FRAGMENTDISPLAY 2008-05-18
                a2series.yDisplayLevel4 = 0; //As B-ions. //1
                a2series.yDisplayLevelForMatching4 = 4; //As B-ions.

                anInOutIonTables.Add(a2series);
            }
        } //addA2ionSeries().


        //****************************************************************************
        //*  SUBROUTINE NAME:   addYionsWithPhosLoss
        //d$ <summary>Add definition for common ion-series: y-ions with some
        //            number of phos loss and a certain charge.  </summary>
        //
        //****************************************************************************
        private static void addYionsWithPhosLoss(
            ref List<singleIonSeriesStructure> anInOutIonTables,
            int anIonSeriesID,
            int aCharge3,
            int aPhosLosses2
            )
        {
            //Single charged y-ions with phos-loss.
            singleIonSeriesStructure YphosSeries;

            //Changed PM_REFACTOR 2008-06-23
            //YphosSeries.ionSeriesID = 5001 + xyz; -variable! 
            YphosSeries.ionSeriesID2 = anIonSeriesID;

            int basePhos = 98;
            int nomimalMassLoss = basePhos * aPhosLosses2;
            string nomimalMassLossStr = nomimalMassLoss.ToString();
            YphosSeries.ionSeriesName =
              chargeWord(aCharge3) + " full y series with " + 
              nomimalMassLossStr + " Da loss.";
            YphosSeries.shortName2 = "y";
            YphosSeries.postFix = "-" + nomimalMassLossStr;

            //Later: use constant or similar...
            YphosSeries.offsetFromResidueToNeutral =
                ////aPhosLosses2 * -79.96633093 //Phos80, -98+18, -Phos98 + water
                //+18.0105647 - aPhosLosses2 * 97.97689563
                //aPhosLosses2 * -18.0105647; //We don't know anything (about phos and such - we just marked something that 98 Da lower, wether it is phosporylated or not).
                //aPhosLosses2 * -18.0105647 //No phos anywhere when it has been lost!!!!
                //0.0 //!!!! - only true if the site is NOT phosphorylated.

                //This is to get -98 or -196 with respect to the standard y-series.
                //-80 for one loss is +18-98 = -80. 18 is the offset for standard y.
                //-178 for two losses is +18-196 = -178. 18 is the offset for standard y.
                +18.0105647 - aPhosLosses2 * 97.97689563
                ;

            YphosSeries.forward = false;
            YphosSeries.colour = 419;

            YphosSeries.isPrecursorDerived = false;
            YphosSeries.charge = aCharge3;

            YphosSeries.startIonNumber = 1;
            YphosSeries.endIonNumber = -1 - 1; //Not yN.
            YphosSeries.startMass = 0.1;
            YphosSeries.endMass = -0.1;

            //At least one, but that should already be coverered by matching
            //the phos modification.
            YphosSeries.startMods2 = aPhosLosses2;
            YphosSeries.endMods2 = 999;

            YphosSeries.useModSeqRule = true;

            //Changed PM_NEW_DISPLAYLEVEL_ION_SERIES 2008-05-26
            int matchLevel=0;
            if (aCharge3 == 1)
            {
                matchLevel = 3;
            }
            else
            {
                if (aPhosLosses2 == 1)
                {
                    matchLevel = 1;
                }
            }

            //Changed PM_FRAGMENTDISPLAY 2008-05-18
            YphosSeries.yDisplayLevel4 = 0;

            //Changed PM_NEW_DISPLAYLEVEL_ION_SERIES 2008-05-26
            //YphosSeries.yDisplayLevelForMatching4 = 3;
            YphosSeries.yDisplayLevelForMatching4 = matchLevel;

            anInOutIonTables.Add(YphosSeries);
        } //addYionsWithPhosLoss()


        //****************************************************************************
        //*  SUBROUTINE NAME:   addBionsWithPhosLoss
        //d$ <summary>Add definition for common ion-series: y-ions with some
        //            number of phos loss and a certain charge.  </summary>
        //
        //****************************************************************************
        private static void addBionsWithPhosLoss(
            ref List<singleIonSeriesStructure> anInOutIonTables,
            int anIonSeriesID,
            int aCharge3,
            int aPhosLosses2
            )
        {
            //Single charged b-ions with phos-loss.
            singleIonSeriesStructure BphosSeries;

            //Changed PM_REFACTOR 2008-06-23
            //BphosSeries.ionSeriesID = 5101 + xyz; -variable! ;
            BphosSeries.ionSeriesID2 = anIonSeriesID;

            int basePhos = 98;
            int nomimalMassLoss = basePhos * aPhosLosses2;
            string nomimalMassLossStr = nomimalMassLoss.ToString();
            BphosSeries.ionSeriesName =
              chargeWord(aCharge3) + " full b series with " + 
              nomimalMassLossStr + " Da loss.";
            BphosSeries.shortName2 = "b";
            BphosSeries.postFix = "-" + nomimalMassLossStr;

            //Later: use constant or similar...
            BphosSeries.offsetFromResidueToNeutral =
              ////aPhosLosses2 * -79.96633093 //Phos80, -98+18, -Phos98 + water
              //aPhosLosses2 * -18.0105647 //No phos anywhere when it has been lost!!!!

              //This is to get -98 or -196 with respect to the standard b-series.
              //-98 for one loss is +0-98 = -98. 0 is the offset for standard b.
              //-196 for two losses is +0-196 = -196. 0 is the offset for standard b.
              -aPhosLosses2 * 97.97689563
              ;

            BphosSeries.forward = true;
            BphosSeries.colour = 421;

            BphosSeries.isPrecursorDerived = false;
            BphosSeries.charge = aCharge3;

            BphosSeries.startIonNumber = 1;
            BphosSeries.endIonNumber = -1 - 1; //Not bN.
            BphosSeries.startMass = 0.1;
            BphosSeries.endMass = -0.1;

            //At least one, but that should already be coverered by matching
            //the phos modification.
            BphosSeries.startMods2 = aPhosLosses2;
            BphosSeries.endMods2 = 999;

            BphosSeries.useModSeqRule = true;


            //Changed PM_NEW_DISPLAYLEVEL_ION_SERIES 2008-05-26
            int matchLevel=0;
            if (aCharge3 == 1)
            {
                matchLevel = 2;
            }

            //Changed PM_FRAGMENTDISPLAY 2008-05-18
            BphosSeries.yDisplayLevel4 = 0;

            //Changed PM_NEW_DISPLAYLEVEL_ION_SERIES 2008-05-26
            //BphosSeries.yDisplayLevelForMatching4 = 3-1;
            BphosSeries.yDisplayLevelForMatching4 = matchLevel;

            anInOutIonTables.Add(BphosSeries);
        } //addBionsWithPhosLoss()


        //****************************************************************************
        //*  SUBROUTINE NAME:   addPrecursor
        //d$ <summary>Add definition for precursor; with some
        //            number of phos losses and a certain charge.  </summary>
        //
        //****************************************************************************
        private static void addPrecursor(
            ref List<singleIonSeriesStructure> anInOutIonTables,
            int anIonSeriesID,
            int aCharge3,
            int aPhosLosses2
            )
        {
            //Charged precursor with possible phos-loss.
            singleIonSeriesStructure precursor;

            //Changed PM_REFACTOR 2008-06-23
            //precursor.ionSeriesID = 7001 + xyz; -variable
            precursor.ionSeriesID2 = anIonSeriesID;

            int basePhos = 98;
            int nomimalMassLoss = basePhos * aPhosLosses2;
            string nomimalMassLossStr = nomimalMassLoss.ToString();
            precursor.ionSeriesName =
              chargeWord(aCharge3) + " precursor with " + 
              nomimalMassLossStr + " Da loss.";
            precursor.shortName2 = "prec";

            if (aPhosLosses2 == 0)
            {
                precursor.postFix = "";
            }
            else
            {
                precursor.postFix = "-" + nomimalMassLossStr;
            }

            //Later: use constant or similar...
            precursor.offsetFromResidueToNeutral =
                +18.0105647 - aPhosLosses2 * 97.97689563
                ;

            precursor.forward = false; //As we use yN - it has the
            //  same mass as the precursor.
            precursor.colour = 433;

            precursor.isPrecursorDerived = true;
            precursor.charge = aCharge3;

            //yN.
            precursor.startIonNumber = -1;
            precursor.endIonNumber = -1; 

            precursor.startMass = 0.1;
            precursor.endMass = -0.1;

            precursor.startMods2 = 0;
            precursor.endMods2 = 999;

            precursor.useModSeqRule = false;

            //Changed PM_FRAGMENTDISPLAY 2008-05-18
            precursor.yDisplayLevel4 = 0;
            precursor.yDisplayLevelForMatching4 = 2;

            anInOutIonTables.Add(precursor);
        } //addPrecursor()


        //****************************************************************************
        //*  SUBROUTINE NAME:   addChargedLittleZionSeries
        //d$ <summary>Add definition for common ion-series: z-ions</summary>
        //
        //****************************************************************************
        private static void addChargedLittleZionSeries(
            ref List<singleIonSeriesStructure> anInOutIonTables,
            int anIonSeriesID,
            int aCharge2
            )
        {
            { //Single charged z-ions.
                singleIonSeriesStructure fullZseries;

                fullZseries.ionSeriesID2 = anIonSeriesID;

                fullZseries.ionSeriesName =
                  chargeWord(aCharge2) + " full z series.";
                fullZseries.shortName2 = "z";
                fullZseries.postFix = "";

                fullZseries.offsetFromResidueToNeutral =
                  MSconstants.NORMAL_C_TERMINUS_MONO +
                  MSconstants.NORMAL_N_TERMINUS_MONO -

                  (MSconstants.N_MASS_MONO +
                   2 * MSconstants.HYDROGEN_MASS)

                   ; //+2 Da. H20 - NH2 = O - N

                fullZseries.forward = false;
                fullZseries.colour = 419;
                fullZseries.isPrecursorDerived = false;

                fullZseries.charge = aCharge2;

                fullZseries.startIonNumber = 1 + 0; //Start with z1.

                fullZseries.endIonNumber = -1 - 1; //Not zN.
                fullZseries.startMass = 0.1;
                fullZseries.endMass = -0.1;

                //No rule for number of modifications.
                fullZseries.startMods2 = 0;
                fullZseries.endMods2 = 999;

                fullZseries.useModSeqRule = false;

                fullZseries.yDisplayLevel4 = 0; //2-1
                fullZseries.yDisplayLevelForMatching4 = 4;

                anInOutIonTables.Add(fullZseries);
            }
        } //addChargedLittleZionSeries().


        //****************************************************************************
        //*  SUBROUTINE NAME:   addChargedCionSeries
        //d$ <summary>Add definition for common ion-series: c-ions</summary>
        //
        //****************************************************************************
        private static void addChargedCionSeries(
            ref List<singleIonSeriesStructure> anInOutIonTables,
            int anIonSeriesID,
            int aCharge2
            )
        {
            { //Single charged c-ions.
                singleIonSeriesStructure fullCseries;

                fullCseries.ionSeriesID2 = anIonSeriesID;

                fullCseries.ionSeriesName =
                  chargeWord(aCharge2) + " full c series.";
                fullCseries.shortName2 = "c";
                fullCseries.postFix = "";

                fullCseries.offsetFromResidueToNeutral =
                  MSconstants.N_MASS_MONO +
                  3 * MSconstants.HYDROGEN_MASS
                  ; //+17 Da, NH3.

                fullCseries.forward = true;
                fullCseries.colour = 421;
                fullCseries.isPrecursorDerived = false;

                fullCseries.charge = aCharge2;

                fullCseries.startIonNumber = 1 + 0; //Start with c1.

                fullCseries.endIonNumber = -1 - 1; //Not cN.
                fullCseries.startMass = 0.1;
                fullCseries.endMass = -0.1;

                //No rule for number of modifications.
                fullCseries.startMods2 = 0;
                fullCseries.endMods2 = 999;

                fullCseries.useModSeqRule = false;

                fullCseries.yDisplayLevel4 = 0; //2-1
                fullCseries.yDisplayLevelForMatching4 = 4;

                anInOutIonTables.Add(fullCseries);
            }
        } //addChargedCionSeries().


        //****************************************************************************
        //*  SUBROUTINE NAME:   addChargedBigZionSeries
        //d$ <summary>Add definition for common ion-series: Z-ions</summary>
        //
        //****************************************************************************
        private static void addChargedBigZionSeries(
            ref List<singleIonSeriesStructure> anInOutIonTables,
            int anIonSeriesID,
            int aCharge2
            )
        {
            { //Single charged z-ions.
                singleIonSeriesStructure fullZseries;

                fullZseries.ionSeriesID2 = anIonSeriesID;

                fullZseries.ionSeriesName =
                  chargeWord(aCharge2) + " full Z series.";
                fullZseries.shortName2 = "Z_H";
                fullZseries.postFix = "";

                fullZseries.offsetFromResidueToNeutral =
                  MSconstants.NORMAL_C_TERMINUS_MONO +
                  MSconstants.NORMAL_N_TERMINUS_MONO -

                  (MSconstants.N_MASS_MONO +
                   2 * MSconstants.HYDROGEN_MASS) +

                  MSconstants.HYDROGEN_MASS //

                   ; //+2 Da. H20 - NH2 + H = O - N + H

                fullZseries.forward = false;
                fullZseries.colour = 431; //For z: 419
                fullZseries.isPrecursorDerived = false;

                fullZseries.charge = aCharge2;

                fullZseries.startIonNumber = 1 + 0; //Start with z1.

                fullZseries.endIonNumber = -1 - 1; //Not zN.
                fullZseries.startMass = 0.1;
                fullZseries.endMass = -0.1;

                //No rule for number of modifications.
                fullZseries.startMods2 = 0;
                fullZseries.endMods2 = 999;

                fullZseries.useModSeqRule = false;

                fullZseries.yDisplayLevel4 = 0; //2-1
                fullZseries.yDisplayLevelForMatching4 = 3;

                anInOutIonTables.Add(fullZseries);
            }
        } //addChargedBigZionSeries().


        //****************************************************************************
        //*  SUBROUTINE NAME:   defaultIonTables
        //d$ <summary>Returns the default set of ions series tables</summary>
        //
        //****************************************************************************
        public static ionDefinitionsStructure defaultIonTables()
        {
            //Changed PM_REFACTOR 2008-10-09. We now again return values instead
            //  of using output parameters.
            //Old parameters:
            //  out List<ionSeriesTableStructure>  anOutIonTables,
            //  out List<singleIonSeriesStructure> anOutSingleIonSeries)

            //Changed PM_REFACTOR 2008-10-09 (renaming, par -> local)
            List<ionSeriesTableStructure> outIonTables = 
              new List<ionSeriesTableStructure>();

            //Changed PM_REFACTOR 2008-10-09 (renaming, par -> local)
            //Changed PM_REFACTOR 2008-06-23
            List<singleIonSeriesStructure> outSingleIonSeries = 
              new List<singleIonSeriesStructure>();

            //Changed PM_MARKER 2008-06-23
            //
            //Reduce redundancy here and even more in XML file by splitting into
            //two levels: simple ion series and higher level with rules, etc.
            //
            //The simple ion series are referred to by an ID (integer), like for
            //quantitation modes/ modifications.


            //Changed PM_REFACTOR 2008-06-23
            //Single ion series defition (lower level in the new two 
            //level defintion.)
            //
            //Note: this does ***not*** constitute an ion table. It is
            //      just a list of ion series; with a unique ID for 
            //      each ion series. Below we then refer to some ion 
            //      series by the unique ID. In this way we avoid 
            //      redundant definitions of ion-series.
            {
                addSingleChargedYionSeries( ref outSingleIonSeries, 1201);
                addDoubleChargedYionSeries( ref outSingleIonSeries, 1210, 700.0);
                addSingleChargedBionSeries( ref outSingleIonSeries, 2001);
                addA2ionSeries( ref outSingleIonSeries, 3001);

                addYionsWithPhosLoss(ref outSingleIonSeries, 5001, 1, 1);
                addYionsWithPhosLoss(ref outSingleIonSeries, 5002, 2, 1);
                addBionsWithPhosLoss(ref outSingleIonSeries, 5101, 1, 1);
                addBionsWithPhosLoss(ref outSingleIonSeries, 5102, 2, 1);

                addYionsWithPhosLoss(ref outSingleIonSeries, 5003, 1, 2);
                addYionsWithPhosLoss(ref outSingleIonSeries, 5004, 2, 2);
                addBionsWithPhosLoss(ref outSingleIonSeries, 5103, 1, 2);
                addBionsWithPhosLoss(ref outSingleIonSeries, 5104, 2, 2);

                addPrecursor(ref outSingleIonSeries, 7001, 2, 0);
                addPrecursor(ref outSingleIonSeries, 7002, 2, 1);
                addPrecursor(ref outSingleIonSeries, 7003, 1, 1);

                //Changed PM_ETD_SUPPORT 2008-06-24. Single charged...
                addChargedLittleZionSeries(ref outSingleIonSeries, 8001, 1);
                addChargedCionSeries(ref outSingleIonSeries, 8501, 1);

                addChargedBigZionSeries(ref outSingleIonSeries, 8011, 1);


                //Double charged...
                addChargedLittleZionSeries(ref outSingleIonSeries, 8002, 2);
                addChargedCionSeries(ref outSingleIonSeries, 8502, 2);

                addChargedBigZionSeries(ref outSingleIonSeries, 8012, 2);
            }

            { //Normal ion series plus phos losses for S and T phophorylations.
                ionSeriesTableStructure someionSeriesTable;
                someionSeriesTable.ionTableID = 3410;
                someionSeriesTable.ionTableName =
                  AppConstants.SHORT_APP + 
                  " classic - plus phos losses for Serine phophorylations";
                someionSeriesTable.rules2 = new List<matchRuleStructure>();
                someionSeriesTable.ionSeries2 = new List<int>();

                matchRuleStructure rule3;

                //rule2.matchModID = 5045; //"Oxidation (M)". Mox.
                //rule2.matchModID = 5051; //"phospho(STY)". E.g. Cell paper data.
                rule3.matchModID = 5048; //"Phospho (ST)"

                //Changed PM_MARKER_SPECTRUMCLASSIFICATION 2008-06-23
                //Add rules for spectrum classification, e.g. spectrum
                //must be ETD.

                //Changed PM_MARKER_PRECURSORCHARGE_SPECIFICATION 2008-06-23
                //Add precursor charge rules, e.g. equal to precursor, or one less...

                //Not yet.
                //rule2.minMods = 1;
                //rule2.maxMods = 99;

                //Changed PM_GENERALISED_IONSERIES_SPECTRUMCLASSIFICATION 2008-06-24
                rule3.MSMSspectrumClassification = 
                  spectrumSubTypeEnum.enumIsFragmentCID;

                someionSeriesTable.rules2.Add(rule3);

                //Changed PM_MS3_PHOSLOSS_FRAGMENTS 2008-07-23. Now also match 
                //in MS3 spectra, in addition to normal MS2.
                rule3.MSMSspectrumClassification = 
                    spectrumSubTypeEnum.enumIsFragmentMS3;
                someionSeriesTable.rules2.Add(rule3);


                //Add check for uniqueness of ID (refs added to ionSeries2 below)???
                //Add check for exisistence. This could work here to detect 
                //internal problems in the code and when reading in from the XML file
                //(e.g. pass a hash that the functions can use to check
                //uniqueness of ID.)


                //Changed PM_REFACTOR 2008-06-23
                // addSingleChargedYionSeries(ref someionSeriesTable.ionSeries);
                // addDoubleChargedYionSeries(ref someionSeriesTable.ionSeries, 700.0);
                // 
                // addSingleChargedBionSeries(ref someionSeriesTable.ionSeries);
                // addA2ionSeries(ref someionSeriesTable.ionSeries);
                // 
                // //Precursor, various versions.
                // addPrecursor(ref someionSeriesTable.ionSeries, 2, 0);
                // addPrecursor(ref someionSeriesTable.ionSeries, 2, 1);
                // addPrecursor(ref someionSeriesTable.ionSeries, 1, 1);
                someionSeriesTable.ionSeries2.Add(1201); //y single
                someionSeriesTable.ionSeries2.Add(1210); //y double
                someionSeriesTable.ionSeries2.Add(2001); //b single
                someionSeriesTable.ionSeries2.Add(3001); //a2               
                someionSeriesTable.ionSeries2.Add(7001); //Precursor, double
                //  charge - only suitable for triple charged precursor??

                someionSeriesTable.ionSeries2.Add(7002); //Precursor, double
                //  charge, one phos loss - only suitable for triple charged precursor??

                someionSeriesTable.ionSeries2.Add(7003); //Precursor, single
                //  charge, one phos loss - suitable for what??


                //Changed PM_REFACTOR 2008-06-23
                // //Single phos loss
                // addYionsWithPhosLoss(ref someionSeriesTable.ionSeries, 1, 1);
                // addYionsWithPhosLoss(ref someionSeriesTable.ionSeries, 2, 1);
                // addBionsWithPhosLoss(ref someionSeriesTable.ionSeries, 1, 1);
                // addBionsWithPhosLoss(ref someionSeriesTable.ionSeries, 2, 1);
                // 
                // //Double phos loss
                // addYionsWithPhosLoss(ref someionSeriesTable.ionSeries, 1, 2);
                // addYionsWithPhosLoss(ref someionSeriesTable.ionSeries, 2, 2);
                // addBionsWithPhosLoss(ref someionSeriesTable.ionSeries, 1, 2);
                // addBionsWithPhosLoss(ref someionSeriesTable.ionSeries, 2, 2);

                someionSeriesTable.ionSeries2.Add(5001); //y single, one phos loss.
                someionSeriesTable.ionSeries2.Add(5002); //y double, one phos loss.
                someionSeriesTable.ionSeries2.Add(5101); //b single, one phos loss.
                someionSeriesTable.ionSeries2.Add(5102); //b double, one phos loss.

                someionSeriesTable.ionSeries2.Add(5003); //y single, double phos loss.
                someionSeriesTable.ionSeries2.Add(5004); //y double, double phos loss.
                someionSeriesTable.ionSeries2.Add(5103); //b single, double phos loss.
                someionSeriesTable.ionSeries2.Add(5104); //b double, double phos loss.

                outIonTables.Add(someionSeriesTable);

            } //Block. Normal ion series + phos losses for Serine phophorylations.


            { //For ECD/ETD, c/z ion series table.
                ionSeriesTableStructure someionSeriesTable;
                someionSeriesTable.ionTableID = 3600;
                someionSeriesTable.ionTableName = "ETD/ECD";
                someionSeriesTable.rules2 = new List<matchRuleStructure>();

                someionSeriesTable.ionSeries2 = new List<int>();

                matchRuleStructure rule1;
                rule1.matchModID = -1; //Match anything. Thus this table
                //  should be added last (until we sort by some match order field).

                //Not yet.
                //rule1.minMods = 1; //Just in case.
                //rule1.maxMods = 99; //Just in case.

                //Changed PM_GENERALISED_IONSERIES_SPECTRUMCLASSIFICATION 2008-06-24
                rule1.MSMSspectrumClassification =
                  spectrumSubTypeEnum.enumIsFragmentECD;

                someionSeriesTable.rules2.Add(rule1);

                someionSeriesTable.ionSeries2.Add(8001); //z single
                someionSeriesTable.ionSeries2.Add(8501); //c single

                someionSeriesTable.ionSeries2.Add(8002); //z double
                someionSeriesTable.ionSeries2.Add(8502); //c double

                someionSeriesTable.ionSeries2.Add(8011); //Z single
                someionSeriesTable.ionSeries2.Add(8012); //Z double

                outIonTables.Add(someionSeriesTable);
            } //Block. ECD/ETD ion series: c, z.


            { //Normal ion series table, as in older versions of the application.
                ionSeriesTableStructure someionSeriesTable;
                someionSeriesTable.ionTableID = 3400;
                someionSeriesTable.ionTableName = 
                  AppConstants.SHORT_APP + " classic";
                someionSeriesTable.rules2 = new List<matchRuleStructure>();

                someionSeriesTable.ionSeries2 = new List<int>();

                matchRuleStructure rule1;
                rule1.matchModID = -1; //Match anything. Thus this table
                //  should be added last (until we sort by some match order field).

                //Not yet.
                //rule1.minMods = 1; //Just in case.
                //rule1.maxMods = 99; //Just in case.

                //Changed PM_GENERALISED_IONSERIES_SPECTRUMCLASSIFICATION 2008-06-24
                rule1.MSMSspectrumClassification =
                  spectrumSubTypeEnum.enumIsFragmentCID;
              
                someionSeriesTable.rules2.Add(rule1);

                //Changed PM_MS3_DISPLAY_BROKEN 2008-07-22. Symptom was
                //  NullReferenceException, "Problem opening the raw data 
                //  file (wiff, raw or idx). Likely reasons: ..."
                rule1.MSMSspectrumClassification = 
                    spectrumSubTypeEnum.enumIsFragmentMS3;
                someionSeriesTable.rules2.Add(rule1);

                //Changed PM_REFACTOR 2008-06-23
                // addSingleChargedYionSeries(ref someionSeriesTable.ionSeries);
                // addDoubleChargedYionSeries(ref someionSeriesTable.ionSeries, 700.0);
                // addSingleChargedBionSeries(ref someionSeriesTable.ionSeries);
                // addA2ionSeries(ref someionSeriesTable.ionSeries);
                someionSeriesTable.ionSeries2.Add(1201); //y single
                someionSeriesTable.ionSeries2.Add(1210); //y double
                someionSeriesTable.ionSeries2.Add(2001); //b single
                someionSeriesTable.ionSeries2.Add(3001); //a2

                outIonTables.Add(someionSeriesTable);
            } //Block. Standard ion series: a2, b, y, y++ less than 700 Da.


            //Changed PM_REFACTOR 2008-10-09
            ionDefinitionsStructure toReturn;
            toReturn.ionTables = outIonTables;
            toReturn.singleIonSeries = outSingleIonSeries;

            return toReturn;
        } //defaultIonTables()


        /****************************************************************************
         *    <placeholder for header>                                              *
         ****************************************************************************/
        private void extractModInfo(int aModID)
        {
            quantModificationStructure modInfo =
                mQuantObject.getModification(aModID);

            Dictionary<string, int> AAhash = new Dictionary<string, int>();

            //This code is probably also somewhere else!!!! Do refactor when
            //we find it...
            //Flatten specification with respect to affected amino acids.
            foreach (AAsetStruct someAAset in modInfo.affectedAAs)
            {
                string AAs = someAAset.AAs;

                int len = AAs.Length;
                for (int i = 0; i < len; i++)
                {
                    string AA = AAs.Substring( i, 1);

                    int count;
                    if ( AAhash.TryGetValue(AA,out count))
                    {
                        //We have already seen the amino acid before. This is
                        //unexpected. Should we issue some kind of error
                        //message or warning?
                        count++;
                        AAhash[AA] = count;
                    }
                    else
                    {
                        AAhash.Add(AA, 1);
                    }
                }
            } //Through modInfo.affectedAAs

            int len2 = AAhash.Count;
            List<string> AAlist = new List<string>(len2);

            Dictionary<string, int>.Enumerator hashEnumerator2 =
                AAhash.GetEnumerator();
            while (hashEnumerator2.MoveNext())
            {
                string curKey = hashEnumerator2.Current.Key;
                int curValue = hashEnumerator2.Current.Value;

                AAlist.Add(curKey);
            } //Hash iteration.

            mModHash.Add(aModID, AAlist);
        } //extractModInfo


        /****************************************************************************
         *    <placeholder for header>                                              *
         ****************************************************************************/
        private bool modInSequence(
          ref List<string> aParticularModAAs,
          ref string anInAAsequence,
          bool aUseSeqRule,
          int aMinimumCountInFragmentSequence)
        {
            bool doAdd = true;
            if (aParticularModAAs != null &&
                aUseSeqRule == true)
            {
                //Fragment amino acids must contain at least
                //one of the amino acids that the
                //modification affects.

                bool AAaffectedinFragment = false;
                foreach (string someAAbyMod in aParticularModAAs)
                {
                    int countForOneModAA = 0;
                    int searchIndex = 0;
                    //This search is an
                    bool end = false;
                    while ( ! end)
                    {
                        int idx = anInAAsequence.IndexOf(someAAbyMod, searchIndex);
                        if (idx < 0)
                        {
                            end = true;
                        }
                        else
                        {
                            countForOneModAA++;
                            if (countForOneModAA >= aMinimumCountInFragmentSequence)
                            {
                                AAaffectedinFragment = true;
                                end = true; //No need to continue. Fragment amino
                                //  acid sequence contains enough amino acids that
                                //  are affected by the modification that made
                                //  a match for an ion table.

                            }
                            else
                            {
                                //Prepare for searching on.
                                searchIndex = idx + 1; //Jump over the single matched AA.
                            }
                        }
                    }
                } //Through aParticularModAAs
                doAdd = AAaffectedinFragment;

            } //Mod AAs in effect.
            else
            {
                int peter2 = 2;
            }

            //For breakpoints.
            if (aUseSeqRule)
            {
                int peter2 = 2;
            }
            else
            {
                int peter3 = 3;
            }

            return doAdd;
        } //modInSequence()


        //Changed PM_REFACTOR 2008-06-23
        //****************************************************************************
        //*  SUBROUTINE NAME:   getIonSeriesDef                                                  *
        //d$   <summary>XYZ</summary>
        //
        //****************************************************************************
        private static singleIonSeriesStructure getIonSeriesDef(
          int anIonSeriesDefID, ref List<singleIonSeriesStructure> aSingleIonSeries)
        {
            //Detect if it does not exist... If it happens we can say that this
            //condition should have been detected earlier by the program, e.g.
            //when the XML settings file was read...

            bool found = false;
            singleIonSeriesStructure toReturn;

            //Keep compiler happy...
            toReturn.ionSeriesName              = null;
            toReturn.postFix                    = null;
            toReturn.shortName2                 = null;
            toReturn.charge                     = -1;
            toReturn.colour                     = -1;
            toReturn.endIonNumber = -1;
            toReturn.endMass                    = -1.0;
            toReturn.endMods2                   = -1;
            toReturn.forward                    = false;
            toReturn.ionSeriesID2               = -1;
            toReturn.isPrecursorDerived         = false;
            toReturn.offsetFromResidueToNeutral = -1.0;
            toReturn.startIonNumber             = -1;
            toReturn.startMass                  = -1.0;
            toReturn.startMods2                 = -1;
            toReturn.useModSeqRule              = false;
            toReturn.yDisplayLevel4             = -1;
            toReturn.yDisplayLevelForMatching4  = -1;

            //Linear search, but we have an the order of 10 elements.
            foreach (singleIonSeriesStructure someSingleIonSeries in aSingleIonSeries)
            {
                if (someSingleIonSeries.ionSeriesID2 == anIonSeriesDefID)
                {
                    found = true;
                    toReturn = someSingleIonSeries;
                    break;
                }
            } //Through set of defined single ion series.

            if (!found)
            {
                string msgStr = 
                  "The ion series with ID " + anIonSeriesDefID + 
                  " could not be found. It does not exist in definitions of the " +
                  "set of ion series (defined in an XML settings file). Edit this " +
                  "file as appropriate or delete the file to let the program " +
                  "create a new version with default content.";
                System.Windows.Forms.MessageBox.Show(msgStr);
            }

            return toReturn;
        } //getIonSeriesDef


        //****************************************************************************
        //*  SUBROUTINE NAME:   computeFragments                                                  *
        //d$   <summary>XYZ</summary>
        //
        //****************************************************************************
        public List<fragmentExStructure> computeFragments(
            ref string anInPeptideSequence,
            ref List<double> anInYarray,
            ref List<double> anInBarray,
            ref List<List<AAsetStruct>> anInModificationsDefsList,
            ionSetStruct anInMassAdjustments,
            ref List<modificationCountStruct> anInModHits, 
            spectrumSubTypeEnum anInMSMStype
            )
        {
            //Later: mass adjustments. Just refactor
            //       from CalcFragIons() (file peptideFragments.vb),
            //       block "If useMassAdjustment Then".
            
            Trace.Assert(
              anInMSMStype != spectrumSubTypeEnum.enumIsNormalMS &&
              anInMSMStype != spectrumSubTypeEnum.enumIsZoomMS,
              "PIL ASSERT. Spectrum type, " + anInMSMStype.ToString() + 
              ", for finding a set of ion series is a precursor spectrum type " +
              "and not a fragment " +
              "spectrum type. This can happen if the Mascot result file has " +
              "not been associated with the correct raw spectrum file.");

            List<fragmentExStructure> toReturn =
              new List<fragmentExStructure>(10);

            int particularModID = -1; //Will become positive if we match
            //  a particular ion table due to a particular peptide modification.
            //matchRuleStructure matchedRule;
            int modsForMatched = -1;


            //First find which ion table to use... It depends on some rules, e.g.
            //if the peptide has a Phospho ST modification.
            ionSeriesTableStructure ionTableToUse;
            ionTableToUse.ionSeries2 = null; //Keep compiler happy.
            ionTableToUse.rules2 = null; //Keep compiler happy.
            foreach (ionSeriesTableStructure someIonTable in mIonDefs.ionTables)
            {
                bool matchedThisTable = false;

                //Go through the rules. We use the first that match.
                //
                //Add ASSERT that check we always match something? (to avoid
                //using an empty set of ions - no ions!)
                foreach (matchRuleStructure someRule in someIonTable.rules2)
                {
                    bool matchedMod = true;
                    bool matchedSpectrumType = true;

                    if (someRule.matchModID >= 0)
                    {
                        matchedMod = false;
                        if (anInModHits != null) //Is peptide modified?
                        {
                            foreach (modificationCountStruct someModHit in anInModHits)
                            {
                                //As we go through the peptide's modification: use
                                //the opportunity to cache information about
                                //defined modifications. We need it later in
                                //this function.

                                int modID = someModHit.quantModificationID;
                                List<string> someInfo;
                                if (!mModHash.TryGetValue(modID, out someInfo))
                                {
                                    extractModInfo(modID);
                                }

                                if (modID == someRule.matchModID)
                                {
                                    int modHits = someModHit.count3;

                                    particularModID = modID;
                                    modsForMatched = modHits;

                                    //Use number of modifications as a
                                    //filter, someRule.minMods and someRule.maxMods
                                    //Those two fields should also be initialised.

                                    //Not yet.
                                    //if (modHits >= someRule.minMods &&
                                    //    modHits <= someRule.maxMods)
                                    //{
                                    //    //Not yet.
                                    //}

                                    matchedMod = true;
                                }
                            } //Through peptide modifications.
                        }
                        else
                        {
                            int peter2 = 2; //No modifications in peptide. Let
                            //  us hope we have a rule that always match. Otherwise
                            //  we get no ions...
                        }
                    } //Modification rule specified.
                    else
                    {
                        int peter7 = 7; //For breakpoints. Modification rule
                        //  not specified.
                    }

                    //What is this check for? Answer: this is for our second
                    //kind of rules; MS/MS type. E.g. one kind of fragments
                    //for ECD, another for MS2 CID, a third for MS3.
                    if (someRule.MSMSspectrumClassification != 
                        spectrumSubTypeEnum.enumST_DoesNotApply)
                    {
                        matchedSpectrumType = false;

                        if (someRule.MSMSspectrumClassification == anInMSMStype)
                        {
                            matchedSpectrumType = true;
                        }
                        else
                        {
                            int peter9 = 9; //For breakpoints. Modification rule
                            //  did not match.
                        }
                    }
                    else
                    {
                        int peter8 = 8; //For breakpoints. Spectrum type
                        //  not specified.
                    }

                    if (matchedMod && matchedSpectrumType)
                    {
                        matchedThisTable = true;
                        break; //No need to continue.
                    }
                
                } //Through rules.

                if ( matchedThisTable)
                {
                    ionTableToUse = someIonTable;
                    break;
                }
                else
                {
                    int peter2 = 2; //For breakpoints. Did not match any
                    //                rules. Try next ion table.
                }

            } //Through mIonTables, finding a particular ion table (with some set
              //of ion series).

            int plen = anInPeptideSequence.Length;
            fragmentExStructure someFragmentItem; //Scratch.
            { //Block.
                someFragmentItem.ionMatched = false; //Just in case.
            } //Block.

            List<string> particularModAAs = null; //Flag if should be used or not.
            if (particularModID >= 0)
            {
                //This means a particular ion table was matched due to a
                //particular peptide modification, e.g. a phosphorylation.
                particularModAAs = mModHash[particularModID];
            }

            //Next: go through the different defined ion series in turn.

            //Fired because no rules were specified for MS3 spectra...
            //Symptom before the assert (exception because ionTableToUse.ionSeries2 
            //was null):  "Problem opening the raw data file (wiff, raw or 
            //idx). Likely reasons: ...".
            Trace.Assert( 
              ionTableToUse.ionSeries2 != null, 
              "PIL ASSERT. ionTableToUse.ionSeries2 is null.");

            //Changed PM_REFACTOR 2008-06-23
            // foreach ( singleIonSeriesStructure someIonSeriesDef in
            //             ionTableToUse.ionSeries)
            foreach (int someIonSeriesDefID in ionTableToUse.ionSeries2)
            {

                //Perhaps later: cache result of this lookup. To save on the
                //               memory cost of keeping the compiler happy.
                singleIonSeriesStructure someIonSeriesDef =
                  getIonSeriesDef(
                    someIonSeriesDefID, ref mIonDefs.singleIonSeries);

                bool doUseIonSeries = true;
                if (modsForMatched >= 0) //Is negative if no rule was 
                                         //matched - e.g. using a default rule.
                {

                    if (modsForMatched < someIonSeriesDef.startMods2 ||
                        modsForMatched > someIonSeriesDef.endMods2)
                    {
                        doUseIonSeries = false; //E.g. y-198 series is not computed
                        //  if modifications for matching pST is 1.
                    }
                }

                if (doUseIonSeries)
                {
                    bool useModSeqRule = someIonSeriesDef.useModSeqRule;

                    //Common setup (for backward and forward ion series).
                    int startIonNumber0;
                    int endIonNumber0;
                    string shortName = someIonSeriesDef.shortName2;
                    string postFix = someIonSeriesDef.postFix;
                    int charge = someIonSeriesDef.charge;
                    int chargeIndex0 = charge - 1;

                    bool outputIonNumber = ! someIonSeriesDef.isPrecursorDerived;

                    double startMass = someIonSeriesDef.startMass;
                    if (startMass < 0.0)
                    {
                        //Later: actual difference from precursor.
                        startMass = 333333.3;
                    }
                    double endMass = someIonSeriesDef.endMass;
                    if (endMass < 0.0)
                    {
                        //Later: actual difference from precursor.
                        endMass = 333333.3;
                    }

                    double massOffset = someIonSeriesDef.offsetFromResidueToNeutral;
                    {
                        bool allPositions;
                        int maxMods;
                        PILpeptide.findPositionsEtc(
                          someIonSeriesDef.startIonNumber,
                          someIonSeriesDef.endIonNumber, plen,
                          out allPositions,
                          out startIonNumber0, out endIonNumber0, out maxMods);

                        //Translation to old datastructure.
                        someFragmentItem.charge = someIonSeriesDef.charge;
                        ionTypeEnum ionType = ionTypeEnum.enumPrecursorIon2;

                        //Changed PM_MATCH_PRECURSOR 2008-05-20
                        ////For now.
                        //if (!someIonSeriesDef.isPrecursorDerived)
                        if (someIonSeriesDef.isPrecursorDerived)
                        {
                            int peter3 = 3;
                        }
                        else
                        {
                            int peter4 = 4;
                        }
                        if (true)
                        {
                            //Later: case on "someIonSeriesDef.colour"

                            //Changed PM_GENERALISED_IONSERIES 2008-05-12
                            switch (someIonSeriesDef.colour)
                            {
                                case 419: //y
                                    ionType = ionTypeEnum.enumYion2;
                                    break;

                                case 421: //b
                                    ionType = ionTypeEnum.enumBion2;
                                    break;

                                case 431: //a
                                    ionType = ionTypeEnum.enumAion2;
                                    break;

                                //Changed PM_MATCH_PRECURSOR 2008-05-20
                                case 433: //Precursor
                                    //Disguise as y, so it is matched.
                                    ionType = ionTypeEnum.enumYion2;
                                    break;

                                default:
                                    Trace.Assert(
                                        false, "PIL ASSERT. Select Case never fall-through");
                                    break;
                            }
                        }

                        someFragmentItem.ionType = ionType;
                        someFragmentItem.ionMatched = false; //Reset every time. Just
                        //  in case.

                        //Changed PM_FRAGMENTDISPLAY 2008-05-18
                        someFragmentItem.yDisplayLevel2 = 
                          someIonSeriesDef.yDisplayLevel4;
                        someFragmentItem.yDisplayLevelForMatching2 = 
                          someIonSeriesDef.yDisplayLevelForMatching4;

                        //Shift all series one up. For the default ion 
                        //series set this means e.g. y++ series is in
                        //the x-axis area of the spectrum.
                        int shiftUpCount = 1;

                        someFragmentItem.yDisplayLevel2 -= shiftUpCount;
                        someFragmentItem.yDisplayLevelForMatching2 -= shiftUpCount;

                    } //Block. Setting of common fields.

                    if (someIonSeriesDef.forward == true)
                    {
                        //Forwards series, e.g. b-ions.

                        //int len = mSequence.Length();
                        for (int i = startIonNumber0; i <= endIonNumber0; i++)
                        {
                            int indexOneBased = i + 1;
                            double bMass = anInBarray[i];

                            //Refactor this inner part?? It is almost identical
                            //to the backwards part.

                            //Later: combine B_ION_OFFSET and massOffset, outside loop.
                            double residueMass = bMass - MSconstants.B_ION_OFFSET;
                            double neutralFragmentMass = residueMass + massOffset;

                            if (neutralFragmentMass >= startMass &&
                                neutralFragmentMass <= endMass)
                            {
                                someFragmentItem.MCRcalcLoc =
                                  PILmassCalc.chargeTransform(
                                    neutralFragmentMass, 0, charge);

                                string chstr = mChargeStrings[chargeIndex0];
                                if (chstr.Length < 2)
                                {
                                    //Only indicate charge for multiple charged.
                                    chstr = "";
                                }
                                string ionNumberStr = "";
                                if (outputIonNumber)
                                {
                                    ionNumberStr = indexOneBased.ToString();
                                }
                                someFragmentItem.descStr =
                                  shortName +
                                  chstr +
                                  ionNumberStr +
                                  postFix;

                                int idx1 = 0; //plen - 1 - i;
                                int len1 = indexOneBased; //Happens to be the case.
                                someFragmentItem.seq =
                                  anInPeptideSequence.Substring(idx1, len1);

                                bool doAdd =
                                  modInSequence(
                                    ref particularModAAs,
                                    ref someFragmentItem.seq,
                                    useModSeqRule,
                                    modsForMatched);
                                if (doAdd)
                                {
                                    toReturn.Add(someFragmentItem);
                                }
                                else
                                {
                                    int peter3 = 3; //For breakpoints.
                                }
                            } //Within specified mass range.
                            else
                            {
                                int peter2 = 2; //Outside mass range.
                            }

                        } //Through forward ion series

                    }
                    else
                    {
                        //Backwards series, e.g. y-ions.

                        //int len = mSequence.Length();
                        for (int i = startIonNumber0; i <= endIonNumber0; i++)
                        {
                            int indexOneBased = i + 1;
                            double yMass = anInYarray[i];

                            //Later: combine Y_ION_OFFSET and massOffset, outside loop.
                            double residueMass = yMass - MSconstants.Y_ION_OFFSET;
                            double neutralFragmentMass = residueMass + massOffset;

                            if (neutralFragmentMass >= startMass &&
                                neutralFragmentMass <= endMass)
                            {
                                someFragmentItem.MCRcalcLoc =
                                  PILmassCalc.chargeTransform(
                                    neutralFragmentMass, 0, charge);

                                string chstr = mChargeStrings[chargeIndex0];
                                if (chstr.Length < 2)
                                {
                                    //Only indicate charge for multiple charged.
                                    chstr = "";
                                }
                                string ionNumberStr = "";
                                if (outputIonNumber)
                                {
                                    ionNumberStr = indexOneBased.ToString();
                                }
                                else
                                {
                                    int peter2 = 2; //For breakpoints.
                                }
                                someFragmentItem.descStr =
                                  shortName +
                                  chstr +
                                  ionNumberStr +
                                  postFix;

                                //Later: proper frag sequence.
                                //someFragmentItem.seq=anInPeptideSequence.Substring(i,
                                //someFragmentItem.seq = "ACD";
                                int idx1 = plen - 1 - i;
                                int len1 = indexOneBased; //Happens to be the case.
                                someFragmentItem.seq =
                                  anInPeptideSequence.Substring(idx1, len1);

                                //Check that the fragment sequence actually
                                //  contains at least one amino acid that is
                                //  affected by the modification (if the current
                                //  ion series tables is matched by a modification).

                                bool doAdd =
                                  modInSequence(
                                    ref particularModAAs,
                                    ref someFragmentItem.seq,
                                    useModSeqRule,
                                    modsForMatched);
                                if (doAdd)
                                {
                                    toReturn.Add(someFragmentItem);
                                }
                                else
                                {
                                    int peter3 = 3; //For breakpoints.
                                }

                            } //Within specified mass range.
                            else
                            {
                                int peter2 = 2; //Outside mass range.
                            }

                        } //Through backward ion series

                    } //else, for backwards series.


                } //if doUseIonSeries

            } //Through ionTableToUse.ionTables

            return toReturn;
        } //computeFragments()


        //Changed PM_DTASC_COMPILE 2008-09-15. Moved here to remove
        //other dependencies...
        ////Changed PM_DTASC_COMPILE 2008-02-11. Moved to here
        //// from peptideFragments.vb. To remove difficult dependencies.
        //****************************************************************************
        //* <placeholder for header> *
        //****************************************************************************
        public static bool isForwardIon(ionTypeEnum anIonType)
        {

            bool toReturn = true;

            switch (anIonType)
            {
                case ionTypeEnum.enumYion2:
                    toReturn = false;
                    break;
                case ionTypeEnum.enumBion2:
                    break;

                case ionTypeEnum.enumAion2:
                    break;

                case ionTypeEnum.enumPrecursorIon2:
                    toReturn = false;
                    break;

                default:
                    Trace.Assert(false, "PIL ASSERT. Select Case never fall-through");
                    break;
            }
            return toReturn;
        } //isForwardIon()


    } //class PILgeneralisedIonSeries


} //namespace massSpectrometryBase


    

    

Generated by script codePublish.pl at 2009-01-05T15:20:59.