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.


//using System;
using System.Collections.Generic; //For List
//using System.Text;

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 file MSQuantCommon2.vb.
    /****************************************************************************
     *    <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 file MSQuantCommon2.vb.
    ////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 ionSeriesID;
        public string ionSeriesName;
        public string shortName; //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 MSQuant (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 startAA; //1 for first. Negative: from C-terminal.
        public int endAA;    //1 for first. Negative: from C-terminal.

        //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.

        //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;

        public string matchString; //Regular expression.

    } //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> rules; //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.

        //A number of ion series for this ion table.
        public List<singleIonSeriesStructure> ionSeries;
    } //struct ionSeriesTableStructure


    /****************************************************************************
     *    <placeholder for header>                                              *
     ****************************************************************************/
    public class PILgeneralisedIonSeries
    {

        private List<ionSeriesTableStructure> mDefaultIonTables;

        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)

            : base()
        {
            mQuantObject = anInQuantObject;


            //For now until we can read in the definitions from an XML file.
            mDefaultIonTables = defaultIonTables();

            {
                int len = 20;
                mChargeStrings = new List<string>(len);
                string chstr = "";
                for (int i = 0; i < len; i++)
                {
                    chstr += "+";
                    mChargeStrings.Add(chstr);
                }
            }

            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 yIonArray(
            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);

            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.
        } //yIonArray


        //****************************************************************************
        //* <placeholder for header> *
        //* anInModificationsList: type is simpleModificationSpecification *
        //****************************************************************************
        public static void bIonArray(
            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);

            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.
        } //bIonArray


        //****************************************************************************
        //*  SUBROUTINE NAME:   addSingleChargedYionSeries
        //d$ <summary>Add definition for common ion-series: single charged y-ions</summary>
        //
        //****************************************************************************
        private static void addSingleChargedYionSeries(
            ref List<singleIonSeriesStructure> anInOutIonTables
            )
        {
            { //Single charged y-ions.
                singleIonSeriesStructure fullYseries;
                fullYseries.ionSeriesID = 1201;
                fullYseries.ionSeriesName = "Full y series";
                fullYseries.shortName = "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.startAA = 1 + 0; //Start with y1.

                fullYseries.endAA = -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, double aMinMass
            )
        {
            { //Mass restricted double charged y series.
                singleIonSeriesStructure YdoubleSeries;
                YdoubleSeries.ionSeriesID = 1210;
                YdoubleSeries.ionSeriesName = "Mass restricted double charged y series";
                YdoubleSeries.shortName = "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.startAA = 1 + 0; //Start with y1.

                YdoubleSeries.endAA = -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;
                YdoubleSeries.yDisplayLevelForMatching4 = 0; //3

                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
            )
        {
            //Single charged b-ions.
            singleIonSeriesStructure fullBseries;
            fullBseries.ionSeriesID = 2001;
            fullBseries.ionSeriesName = "Full b series";
            fullBseries.shortName = "b";
            fullBseries.postFix = "";

            fullBseries.offsetFromResidueToNeutral = 0.0;

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

            fullBseries.charge = 1;

            fullBseries.startAA = 1 + 0; //Start with b1.
            fullBseries.endAA = -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
          )
        {
            { //Single charged a2 ion.
                singleIonSeriesStructure a2series;
                a2series.ionSeriesID = 2001;
                a2series.ionSeriesName = "a2 only... (as in a2/b2 pair)";
                a2series.shortName = "a";
                a2series.postFix = "";

                a2series.offsetFromResidueToNeutral = -MSconstants.CO_MASS_MONO;

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

                a2series.charge = 1;

                //a2 only!
                a2series.startAA = 2;
                a2series.endAA = 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 aCharge2,
            int aPhosLosses2
            )
        {
            //Single charged y-ions with phos-loss.
            singleIonSeriesStructure YphosSeries;
            YphosSeries.ionSeriesID = 9999;

            int basePhos = 98;
            int nomimalMassLoss = basePhos * aPhosLosses2;
            string nomimalMassLossStr = nomimalMassLoss.ToString();
            YphosSeries.ionSeriesName =
              "Full y series with " + nomimalMassLossStr + " Da loss";
            YphosSeries.shortName = "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 = aCharge2;

            YphosSeries.startAA = 1;
            YphosSeries.endAA = -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 (aCharge2 == 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 aCharge2,
            int aPhosLosses2
            )
        {
            //Single charged b-ions with phos-loss.
            singleIonSeriesStructure BphosSeries;
            BphosSeries.ionSeriesID = 9999;

            int basePhos = 98;
            int nomimalMassLoss = basePhos * aPhosLosses2;
            string nomimalMassLossStr = nomimalMassLoss.ToString();
            BphosSeries.ionSeriesName =
              "Full b series with " + nomimalMassLossStr + " Da loss";
            BphosSeries.shortName = "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 = aCharge2;

            BphosSeries.startAA = 1;
            BphosSeries.endAA = -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 (aCharge2 == 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 aCharge2,
            int aPhosLosses2
            )
        {
            //Charged precursor with possible phos-loss.
            singleIonSeriesStructure precursor;
            precursor.ionSeriesID = 9999;

            int basePhos = 98;
            int nomimalMassLoss = basePhos * aPhosLosses2;
            string nomimalMassLossStr = nomimalMassLoss.ToString();
            precursor.ionSeriesName =
              aCharge2 + " charged precursor with " + 
              nomimalMassLossStr + " Da loss.";
            precursor.shortName = "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 = aCharge2;

            //yN.
            precursor.startAA = -1;
            precursor.endAA = -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:   defaultIonTables
        //d$ <summary>Returns the default set of ions series tables</summary>
        //
        //****************************************************************************
        public static List<ionSeriesTableStructure> defaultIonTables()
        {
            //Note.
            List<ionSeriesTableStructure> tables =
              new List<ionSeriesTableStructure>();


            { //Normal ion series plus phos losses for S and T phophorylations.
                ionSeriesTableStructure someionSeriesTable;
                someionSeriesTable.ionTableID = 3410;
                someionSeriesTable.ionTableName =
                  "MSQ classic - plus phos losses for Serine phophorylations";
                someionSeriesTable.rules = new List<matchRuleStructure>();
                someionSeriesTable.ionSeries = new List<singleIonSeriesStructure>();

                matchRuleStructure rule2;

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


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

                rule2.matchString = null; //Keep compiler happy.
                someionSeriesTable.rules.Add(rule2);

                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);



                //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);



                tables.Add(someionSeriesTable);

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

            { //Normal ion series table, as in older versions of MSQuant.
                ionSeriesTableStructure someionSeriesTable;
                someionSeriesTable.ionTableID = 3400;
                someionSeriesTable.ionTableName = "MSQ classic";
                someionSeriesTable.rules = new List<matchRuleStructure>();
                someionSeriesTable.ionSeries = new List<singleIonSeriesStructure>();

                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.

                rule1.matchString = null; //Keep compiler happy.
                someionSeriesTable.rules.Add(rule1);

                addSingleChargedYionSeries(ref someionSeriesTable.ionSeries);
                addDoubleChargedYionSeries(ref someionSeriesTable.ionSeries, 700.0);
                addSingleChargedBionSeries(ref someionSeriesTable.ionSeries);
                addA2ionSeries(ref someionSeriesTable.ionSeries);

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

            return tables;
        } //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()


        //****************************************************************************
        //*  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
            )
        {
            //Later: mass adjustments. Just refactor
            //       from CalcFragIons() (file peptideFragments.vb),
            //       block "If useMassAdjustment Then".

            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.ionSeries = null; //Keep compiler happy.
            ionTableToUse.rules = null; //Keep compiler happy.
            foreach (ionSeriesTableStructure someIonTable in mDefaultIonTables)
            {
                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.rules)
                {
                    if (someRule.matchModID<0)
                    {
                        matchedThisTable = true;
                        break; //No need to continue.
                    }
                    else
                    {
                        if (anInModHits != null)
                        {
                            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.
                                    //}

                                    matchedThisTable = true;
                                    break; //No need to continue.
                                }
                            } //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...
                        }

                    }
                } //Through rules.

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

            } //Through mDefaultIonTables

            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.
            foreach ( singleIonSeriesStructure someIonSeriesDef in
                        ionTableToUse.ionSeries)
            {

                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.shortName;
                    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.startAA,
                          someIonSeriesDef.endAA, 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()


    } //class PILgeneralisedIonSeries


} //namespace massSpectrometryBase


    

    

Generated by script codePublish.pl at 2008-09-23T11:59:18.