mspeak.cpp
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:26k
源码类别:

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: mspeak.cpp,v $
  4.  * PRODUCTION Revision 1000.4  2004/06/01 18:09:04  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.13
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /* $Id: mspeak.cpp,v 1000.4 2004/06/01 18:09:04 gouriano Exp $
  10.  * ===========================================================================
  11.  *
  12.  *                            PUBLIC DOMAIN NOTICE
  13.  *               National Center for Biotechnology Information
  14.  *
  15.  *  This software/database is a "United States Government Work" under the
  16.  *  terms of the United States Copyright Act.  It was written as part of
  17.  *  the author's official duties as a United States Government employee and
  18.  *  thus cannot be copyrighted.  This software/database is freely available
  19.  *  to the public for use. The National Library of Medicine and the U.S.
  20.  *  Government have not placed any restriction on its use or reproduction.
  21.  *
  22.  *  Although all reasonable efforts have been taken to ensure the accuracy
  23.  *  and reliability of the software and data, the NLM and the U.S.
  24.  *  Government do not and cannot warrant the performance or results that
  25.  *  may be obtained by using this software or data. The NLM and the U.S.
  26.  *  Government disclaim all warranties, express or implied, including
  27.  *  warranties of performance, merchantability or fitness for any particular
  28.  *  purpose.
  29.  *
  30.  *  Please cite the authors in any work or product based on this material.
  31.  *
  32.  * ===========================================================================
  33.  *
  34.  * Author:  Lewis Y. Geer
  35.  *  
  36.  *
  37.  * File Description:
  38.  *    code to deal with spectra and m/z ladders
  39.  *
  40.  * ===========================================================================
  41.  */
  42. #include <ncbi_pch.hpp>
  43. #include <corelib/ncbistd.hpp>
  44. #include <corelib/ncbi_limits.h>
  45. #include <algorithm>
  46. #include "mspeak.hpp"
  47. USING_NCBI_SCOPE;
  48. USING_SCOPE(objects);
  49. USING_SCOPE(omssa);
  50. /////////////////////////////////////////////////////////////////////////////
  51. //
  52. //  CMSHit::
  53. //
  54. //  Used by CMSPeak class to hold hits
  55. //
  56. // helper function for RecordHits that scans thru a single ladder
  57. void CMSHit::RecordMatchesScan(CLadder& Ladder, int& iHitInfo, CMSPeak *Peaks,
  58.        int Which)
  59. {
  60.     try {
  61. TIntensity Intensity(new unsigned [Ladder.size()]);
  62. Peaks->CompareSorted(Ladder, Which, &Intensity);
  63. // examine hits array
  64. unsigned i;
  65. for(i = 0; i < Ladder.size(); i++) {
  66.     // if hit, add to hitlist
  67.     if(Ladder.GetHit()[i] > 0) {
  68. SetHitInfo(iHitInfo).SetCharge() = (char) Ladder.GetCharge();
  69. SetHitInfo(iHitInfo).SetIon() = (char) Ladder.GetType();
  70. SetHitInfo(iHitInfo).SetNumber() = (short) i;
  71. SetHitInfo(iHitInfo).SetIntensity() = *(Intensity.get() + i);
  72. iHitInfo++;
  73.     }
  74. }
  75.     } catch (NCBI_NS_STD::exception& e) {
  76. ERR_POST(Info << "Exception caught in CMSHit::RecordMatchesScan: " << e.what());
  77. throw;
  78.     }
  79. }
  80. // make a record of the ions hit
  81. void CMSHit::RecordMatches(CLadder& BLadder, CLadder& YLadder,
  82.    CLadder& B2Ladder,
  83.    CLadder& Y2Ladder, CMSPeak *Peaks)
  84. {
  85.     // create hitlist.  note that this is deleted in the copy operator
  86.     HitInfo.reset(new CMSHitInfo[Hits]);
  87.     // increment thru hithist
  88.     int iHitInfo(0); 
  89.     int Which = Peaks->GetWhich(Charge);
  90.     // scan thru each ladder
  91.     if(Charge >= kConsiderMult) {
  92. RecordMatchesScan(BLadder, iHitInfo, Peaks, Which);
  93. RecordMatchesScan(YLadder, iHitInfo, Peaks, Which);
  94. RecordMatchesScan(B2Ladder, iHitInfo, Peaks, Which);
  95. RecordMatchesScan(Y2Ladder, iHitInfo, Peaks, Which);
  96.     }
  97.     else {
  98. RecordMatchesScan(BLadder, iHitInfo, Peaks, Which);
  99. RecordMatchesScan(YLadder, iHitInfo, Peaks, Which);
  100.     }
  101. }
  102. // return number of hits above threshold
  103. int CMSHit::GetHits(double Threshold, int MaxI)
  104. {
  105.     int i, retval(0);
  106.     for(i = 0; i < Hits; i++)
  107.         if(SetHitInfo(i).GetIntensity() > MaxI*Threshold)
  108.             retval++;
  109.     return retval;
  110. }
  111. /////////////////////////////////////////////////////////////////////////////
  112. //
  113. //  Comparison functions used in CMSPeak for sorting
  114. //
  115. // compares m/z.  Lower m/z first in sort.
  116. struct CMZICompare {
  117.     bool operator() (CMZI x, CMZI y)
  118.     {
  119. if(x.MZ < y.MZ) return true;
  120. return false;
  121.     }
  122. };
  123. // compares m/z.  High m/z first in sort.
  124. struct CMZICompareHigh {
  125.     bool operator() (CMZI x, CMZI y)
  126.     {
  127. if(x.MZ > y.MZ) return true;
  128. return false;
  129.     }
  130. };
  131. // compares intensity.  Higher intensities first in sort.
  132. struct CMZICompareIntensity {
  133.     bool operator() (CMZI x, CMZI y)
  134.     {
  135. if(x.Intensity > y.Intensity) return true;
  136. return false;
  137.     }
  138. };
  139. /////////////////////////////////////////////////////////////////////////////
  140. //
  141. //  CMSPeak::
  142. //
  143. //  Class used to hold spectra and convert to mass ladders
  144. //
  145. void CMSPeak::xCMSPeak(void)
  146. {
  147.     AAMap = AA.GetMap();
  148.     Sorted[0] = Sorted[1] = false;
  149.     MZI[MSCULLED1] = 0;
  150.     MZI[MSCULLED2] = 0;
  151.     MZI[MSORIGINAL] = 0;
  152.     MZI[MSTOPHITS] = 0;
  153.     Used[MSCULLED1] = 0;
  154.     Used[MSCULLED2] = 0;
  155.     Used[MSORIGINAL] = 0;    
  156.     Used[MSTOPHITS] = 0;    
  157.     PlusOne = 0.8L;
  158.     ComputedCharge = eChargeUnknown;
  159.     Error = eMSHitError_none;
  160. }
  161. CMSPeak::CMSPeak(void)
  162.     xCMSPeak(); 
  163. }
  164. CMSPeak::CMSPeak(int HitListSizeIn): HitListSize(HitListSizeIn)
  165. {  
  166.     xCMSPeak();
  167. }
  168. CMSPeak::~CMSPeak(void)
  169. {  
  170.     delete [] MZI[MSCULLED1];
  171.     delete [] Used[MSCULLED1];
  172.     delete [] MZI[MSCULLED2];
  173.     delete [] Used[MSCULLED2];
  174.     delete [] MZI[MSORIGINAL];
  175.     delete [] Used[MSTOPHITS];
  176.     delete [] MZI[MSTOPHITS];
  177.     int iCharges;
  178.     for(iCharges = 0; iCharges < GetNumCharges(); iCharges++)
  179. delete [] HitList[iCharges];
  180. }
  181. // add hit to hitlist.  returns true if successful
  182. bool CMSPeak::AddHit(CMSHit& in, CMSHit *& out)
  183. {
  184.     out = 0;
  185.     // initialize index using charge
  186.     int Index = in.GetCharge() - Charges[0];
  187.     // check to see if hitlist is full
  188.     if(HitListIndex[Index] >= HitListSize) {
  189. // if less or equal hits than recorded min, don't bother
  190. if(in.GetHits() <= LastHitNum[Index]) return false;  
  191. int i, min(HitList[Index][0].GetHits()); //the minimum number of hits in the list
  192. int minpos(0);     // the position of min
  193. // find the minimum in the hitslist
  194. for(i = 1; i < HitListSize; i++) {
  195.     if(min > HitList[Index][i].GetHits()) {
  196. min = HitList[Index][i].GetHits();
  197. minpos = i;
  198.     }
  199. }
  200. // keep record of min
  201. LastHitNum[Index] = min;
  202. // replace in list
  203. HitList[Index][minpos] = in;
  204. out =  & (HitList[Index][minpos]);
  205. return true;
  206.     } 
  207.     else {
  208. // add to end of list
  209. HitList[Index][HitListIndex[Index]] = in;
  210. out = & (HitList[Index][HitListIndex[Index]]);
  211. HitListIndex[Index]++;
  212. return true;
  213.     }
  214. }
  215. void CMSPeak::AddTotalMass(int massin, int tolin)
  216. {
  217.     TotalMass = massin;
  218.     tol = tolin;
  219. }
  220. void CMSPeak::Sort(int Which)
  221. {
  222.     if(Which < MSORIGINAL || Which >=  MSNUMDATA) return;
  223.     sort(MZI[Which], MZI[Which] + Num[Which], CMZICompare());
  224.     Sorted[Which] = true;
  225. }
  226. bool CMSPeak::Contains(int value, int Which)
  227. {
  228.     CMZI precursor;
  229.     precursor.MZ = value -  tol; // /2;
  230.     CMZI *p = lower_bound(MZI[Which], MZI[Which] + Num[Which], precursor, CMZICompare());
  231.     if(p == MZI[Which] + Num[Which]) return false;
  232.     if(p->MZ < value + tol/*/2*/) return true;
  233.     return false;
  234. }
  235. bool CMSPeak::ContainsFast(int value, int Which)
  236. {
  237.     int x, l, r;
  238.     
  239.     l = 0;
  240.     r = Num[Which] - 1;
  241.     while(l <= r) {
  242.         x = (l + r)/2;
  243.         if (MZI[Which][x].MZ < value - tol) 
  244.     l = x + 1;
  245.         else if (MZI[Which][x].MZ > value + tol)
  246.     r = x - 1;
  247. else return true;
  248.     } 
  249.     
  250.     if (x < Num[Which] - 1 && 
  251. MZI[Which][x+1].MZ < value + tol && MZI[Which][x+1].MZ > value - tol) 
  252. return true;
  253.     return false;
  254. }
  255. // compare assuming all lists are sorted
  256. // the intensity array holds the intensity if there is a match to the ladder
  257. // returns total number of matches, which may be more than is recorded in the ladder due to overlap
  258. int CMSPeak::CompareSorted(CLadder& Ladder, int Which, TIntensity* Intensity)
  259. {
  260.     unsigned i(0), j(0);
  261.     int retval(0);
  262.     if(Ladder.size() == 0 ||  Num[Which] == 0) return 0;
  263.     do {
  264. if (MZI[Which][j].MZ < Ladder[i] - tol) {
  265.     j++;
  266.     if(j >= Num[Which]) break;
  267.     continue;
  268. }
  269. else if(MZI[Which][j].MZ > Ladder[i] + tol) {
  270.     i++;
  271.     if(i >= Ladder.size()) break;
  272.     continue;
  273. }
  274. else {
  275.     if(Used[Which][j] == 0) {
  276. Used[Which][j] = 1;
  277. Ladder.GetHit()[i] = Ladder.GetHit()[i] + 1;
  278.     }
  279.     retval++;
  280.     // record the intensity if requested, used for auto adjust
  281.     if(Intensity) {
  282. *(Intensity->get() + i) = MZI[Which][j].Intensity;
  283.     }
  284.     j++;
  285.     if(j >= Num[Which]) break;
  286.     i++;
  287.     if(i >= Ladder.size()) break;
  288. }
  289.     } while(true);
  290.     return retval;
  291. }
  292. int CMSPeak::Compare(CLadder& Ladder, int Which)
  293. {
  294.     unsigned i;
  295.     int retval(0);
  296.     for(i = 0; i < Ladder.size(); i++) {
  297. if(ContainsFast(Ladder[i], Which)) {
  298.     retval++;
  299.     Ladder.GetHit()[i] = Ladder.GetHit()[i] + 1;
  300. }
  301.     }
  302.     return retval;
  303. }
  304. // compares the top peaks to the ladder.  returns immediately when finding a hit
  305. bool CMSPeak::CompareTop(CLadder& Ladder)
  306. {
  307.     unsigned i;
  308.     for(i = 0; i < Num[MSTOPHITS]; i++) {
  309. if(Ladder.ContainsFast(MZI[MSTOPHITS][i].MZ, tol)) return true;
  310.     }
  311.     return false;
  312. }
  313. // read in an asn.1 spectrum and initialize peak values
  314. int CMSPeak::Read(CMSSpectrum& Spectrum, double MSMSTolerance)
  315. {
  316.     try {
  317. int Scale = Spectrum.GetScale();
  318. TotalMass = Spectrum.GetPrecursormz()*MSSCALE/Scale;
  319. SetTolerance(MSMSTolerance);
  320. Charge = *(Spectrum.GetCharge().begin());
  321. Num[MSORIGINAL] = 0;   
  322.     
  323. const CMSSpectrum::TMz& Mz = Spectrum.GetMz();
  324. const CMSSpectrum::TAbundance& Abundance = Spectrum.GetAbundance();
  325. MZI[MSORIGINAL] = new CMZI [Mz.size()];
  326. Num[MSORIGINAL] = Mz.size();
  327. int i;
  328. for(i = 0; i < Num[MSORIGINAL]; i++) {
  329.     MZI[MSORIGINAL][i].MZ = Mz[i]*MSSCALE/Scale;
  330.     MZI[MSORIGINAL][i].Intensity = Abundance[i]/Scale;
  331. }
  332. Sort(MSORIGINAL);
  333.     } catch (NCBI_NS_STD::exception& e) {
  334. ERR_POST(Info << "Exception in CMSPeak::Read: " << e.what());
  335. throw;
  336.     }
  337.     return 0;
  338. }
  339. // return the number of peaks in a range
  340. int CMSPeak::CountRange(double StartFraction, double StopFraction)
  341. {
  342.     CMZI Start, Stop;
  343.     int Precursor = static_cast <int> (TotalMass/Charge + tol/2.0);
  344.     Start.MZ = static_cast <int> (StartFraction * Precursor);
  345.     Stop.MZ = static_cast <int> (StopFraction * Precursor);
  346.     CMZI *LoHit = lower_bound(MZI[MSORIGINAL], MZI[MSORIGINAL] +
  347.       Num[MSORIGINAL], Start, CMZICompare());
  348.     CMZI *HiHit = upper_bound(MZI[MSORIGINAL], MZI[MSORIGINAL] + 
  349.       Num[MSORIGINAL], Stop, CMZICompare());
  350.     return HiHit - LoHit;
  351. }
  352. // return the percentage of peaks below and at the precursor
  353. int CMSPeak::PercentBelow(void)
  354. {
  355.     CMZI precursor;
  356.     precursor.MZ = static_cast <int> (TotalMass/Charge + tol/2.0);
  357.     CMZI *Hit = upper_bound(MZI[MSORIGINAL], MZI[MSORIGINAL] + Num[MSORIGINAL],
  358.     precursor, CMZICompare());
  359.     Hit++;  // go above the peak
  360.     if(Hit >= MZI[MSORIGINAL] + Num[MSORIGINAL]) return Num[MSORIGINAL];
  361.     return Hit-MZI[MSORIGINAL];
  362. }
  363. // is the data charge +1?
  364. bool CMSPeak::IsPlus1(double PercentBelowIn)
  365. {
  366.     if(PercentBelowIn/Num[MSORIGINAL] > PlusOne) return true;
  367.     return false;
  368. }
  369. // takes the ratio, low/high, of two ranges in the spectrum
  370. double CMSPeak::RangeRatio(double Start, double Middle, double Stop)
  371. {
  372.     int Lo = CountRange(Start, Middle);
  373.     int Hi = CountRange(Middle, Stop);
  374.     return (double)Lo/Hi;
  375. }
  376. // calculate the charge
  377. void CMSPeak::SetComputedCharge(int MaxCharge)
  378. {
  379.     if(IsPlus1(PercentBelow())) {
  380. ComputedCharge = eCharge1;
  381. Charges[0] = 1;
  382. NumCharges = 1;
  383.     }
  384.     else {
  385. #if 0
  386. NumCharges = 1;
  387. if(RangeRatio(0.5, 1.0, 1.5) > 1.25 ) {
  388.     Charges[0] = 2;
  389.     ComputedCharge = eCharge2;
  390. }
  391. else if(RangeRatio(1.0, 1.5, 2.0) > 1.25) {
  392.     Charges[0] = 3;
  393.     ComputedCharge = eCharge3;
  394. }
  395. else {  // assume +4
  396.     Charges[0] = 4;
  397.     ComputedCharge = eCharge4;
  398. }
  399. #endif
  400. ComputedCharge = eChargeNot1; 
  401. Charges[0] = 2;
  402. Charges[1] = 3;
  403. NumCharges = 2;
  404.     }
  405. }
  406. // initializes arrays used to track hits
  407. void CMSPeak::InitHitList(void)
  408. {
  409.     int iCharges;
  410.     for(iCharges = 0; iCharges < GetNumCharges(); iCharges++) {
  411. LastHitNum[iCharges] = MSHITMIN - 1;
  412. HitListIndex[iCharges] = 0;
  413. HitList[iCharges] = new CMSHit[HitListSize];
  414. PeptidesExamined[iCharges] = 0;
  415.     }
  416. }
  417. void CMSPeak::xWrite(std::ostream& FileOut, CMZI *Temp, int Num)
  418. {
  419.     FileOut << (double)TotalMass/MSSCALE << " " << Charge << endl;
  420.     int i;
  421.     unsigned Intensity;
  422.     for(i = 0; i < Num; i++) {
  423.         Intensity = Temp[i].Intensity;
  424.         if(Intensity == 0.0) Intensity = 1;  // make Mascot happy
  425.         FileOut << (double)Temp[i].MZ/MSSCALE << " " << 
  426.     Intensity << endl;
  427.     }
  428. }
  429. void CMSPeak::Write(std::ostream& FileOut, CMSPeak::EFileType FileType, int Which)
  430. {
  431.     if(!FileOut || FileType != CMSPeak::eDTA) return;
  432.     xWrite(FileOut, MZI[Which], Num[Which]);
  433. }
  434. // counts the number of peaks above % of maximum peak
  435. int CMSPeak::AboveThresh(double Threshold, int Which)
  436. {
  437.     CMZI *MZISort = new CMZI[Num[Which]];
  438.     unsigned i;
  439.     for(i = 0; i < Num[Which]; i++) MZISort[i] = MZI[Which][i];
  440.     // sort so higher intensities first
  441.     sort(MZISort, MZISort+Num[Which], CMZICompareIntensity());
  442.     for(i = 1; i < Num[Which] &&
  443.     (double)MZISort[i].Intensity/MZISort[0].Intensity > Threshold; i++);
  444.     delete [] MZISort;
  445.     return i;
  446. }
  447. // Truncate the at the precursor if plus one and set charge to 1
  448. // if charge is erronously set to 1, set it to 2
  449. void CMSPeak::TruncatePlus1(void)
  450. {
  451.     int PercentBelowVal = PercentBelow();
  452.     if(IsPlus1(PercentBelowVal)) {
  453.         Num[MSORIGINAL] = PercentBelowVal;
  454.         Charge = 1;
  455.     }
  456.     else if(Charge == 1) Charge = 2;
  457. }
  458. // functions used in SmartCull
  459. // take out original peaks below a threshold.  assumes original peaks sorted by intensity
  460. void CMSPeak::CullBaseLine(double Threshold, CMZI *Temp, int& TempLen)
  461. {
  462.     unsigned iMZI;
  463.     for(iMZI = 0; iMZI < Num[MSORIGINAL] && MZI[MSORIGINAL][iMZI].Intensity > Threshold * MZI[MSORIGINAL][0].Intensity; iMZI++);
  464.     copy(&MZI[MSORIGINAL][0], &MZI[MSORIGINAL][iMZI], Temp);
  465.     TempLen = iMZI;
  466. }
  467. // cull precursors
  468. void CMSPeak::CullPrecursor(CMZI *Temp, int& TempLen, double Precursor)
  469. {
  470.     // chop out precursors
  471.     int iTemp(0), iMZI;
  472.     
  473.     for(iMZI = 0; iMZI < TempLen; iMZI++) { 
  474. if(Temp[iMZI].MZ > Precursor - tol && 
  475.     Temp[iMZI].MZ < Precursor + tol) continue;
  476. Temp[iTemp] = Temp[iMZI];
  477. iTemp++;
  478.     }
  479.     TempLen = iTemp;
  480. }
  481. // iterate thru peaks, deleting ones that pass the test
  482. void CMSPeak::CullIterate(CMZI *Temp, int& TempLen, TMZIbool FCN)
  483. {
  484.     if(!FCN) return;
  485.     int iTemp(0), iMZI, jMZI;
  486.     set <int> Deleted;
  487.     
  488.     // scan for isotopes, starting at highest peak
  489.     for(iMZI = 0; iMZI < TempLen - 1; iMZI++) { 
  490. if(Deleted.count(iMZI) != 0) continue;
  491. for(jMZI = iMZI + 1; jMZI < TempLen; jMZI++) { 
  492.     if(Deleted.count(jMZI) != 0) continue;
  493.     if((*FCN)(Temp[iMZI], Temp[jMZI], tol)) {
  494. Deleted.insert(jMZI);
  495. // Temp[iMZI].Intensity += Temp[jMZI].Intensity; // keep the intensity
  496.     }
  497. }
  498.     }
  499.     
  500.     for(iMZI = 0; iMZI < TempLen; iMZI++) {
  501. if(Deleted.count(iMZI) == 0) {
  502.     Temp[iTemp] = Temp[iMZI];
  503.     iTemp++;
  504. }
  505.     }
  506.     TempLen = iTemp;
  507. }
  508. // object for looking for isotopes.  true if isotope
  509. static bool FCompareIsotope(const CMZI& x, const CMZI& y, int tol)
  510. {
  511.     if(y.MZ < x.MZ + 2*MSSCALE + tol && y.MZ > x.MZ - tol) return true;
  512.     return false;
  513. }
  514. // cull isotopes using the Markey Method
  515. void CMSPeak::CullIsotope(CMZI *Temp, int& TempLen)
  516. {
  517.     CullIterate(Temp, TempLen, &FCompareIsotope);
  518. }
  519. // function for looking for H2O or NH3.  true if is.
  520. static bool FCompareH2ONH3(const CMZI& x, const CMZI& y, int tol)
  521. {
  522.     if((y.MZ > x.MZ - 18*MSSCALE - tol && y.MZ < x.MZ - 18*MSSCALE + tol ) ||
  523.        (y.MZ > x.MZ - 17*MSSCALE - tol && y.MZ < x.MZ - 17*MSSCALE + tol))
  524. return true;
  525.     return false;
  526. }
  527. // cull peaks that are water or ammonia loss
  528. // note that this only culls the water or ammonia loss if these peaks have a lesser
  529. // less intensity
  530. void CMSPeak::CullH20NH3(CMZI *Temp, int& TempLen)
  531. {
  532.     // need to start at top of m/z ladder, work way down.  sort
  533.     CullIterate(Temp, TempLen, &FCompareH2ONH3);
  534. }
  535. // use smartcull on all charges
  536. void CMSPeak::CullAll(double Threshold, int SingleWindow,
  537.       int DoubleWindow, int SingleHit, int DoubleHit)
  538. {    
  539.     int iCharges;
  540.     for(iCharges = 0; iCharges < GetNumCharges(); iCharges++)
  541. SmartCull(Threshold, GetCharges()[iCharges], SingleWindow,
  542.   DoubleWindow, SingleHit, DoubleHit);
  543.     // make the high intensity list
  544.     iCharges = GetNumCharges() - 1;
  545.     int Which = GetWhich(GetCharges()[iCharges]);
  546.     CMZI *Temp = new CMZI [Num[Which]]; // temporary holder
  547.     copy(MZI[Which], MZI[Which] + Num[Which], Temp);
  548.     sort(Temp, Temp + Num[Which], CMZICompareIntensity());
  549.     if(Num[Which] > MSNUMTOP)  Num[MSTOPHITS] = MSNUMTOP;
  550.     else  Num[MSTOPHITS] = Num[Which];
  551.     MZI[MSTOPHITS] = new CMZI [Num[MSTOPHITS]]; // holds highest hits
  552.     Used[MSTOPHITS] = new char [Num[MSTOPHITS]];
  553.     ClearUsed(MSTOPHITS);
  554.     Sorted[MSTOPHITS] = false;
  555.     copy(Temp, Temp + Num[MSTOPHITS], MZI[MSTOPHITS]);
  556.     Sort(MSTOPHITS);
  557.     delete [] Temp;
  558. }
  559. // check to see if TestMZ is Diff away from BigMZ
  560. bool CMSPeak::IsAtMZ(int BigMZ, int TestMZ, int Diff, int tol)
  561. {
  562.     if(TestMZ < BigMZ - Diff*MSSCALE + tol && 
  563.        TestMZ > BigMZ - Diff*MSSCALE - tol)
  564. return true;
  565.     return false;
  566. }
  567. // see if TestMZ can be associated with BigMZ, e.g. water loss, etc.
  568. bool CMSPeak::IsMajorPeak(int BigMZ, int TestMZ, int tol)
  569. {
  570.     if (IsAtMZ(BigMZ, TestMZ, 1, tol) || 
  571. IsAtMZ(BigMZ, TestMZ, 16, tol) ||
  572. IsAtMZ(BigMZ, TestMZ, 17, tol) ||
  573. IsAtMZ(BigMZ, TestMZ, 18, tol)) return true;
  574.     return false;
  575. }
  576. // recursively culls the peaks
  577. void CMSPeak::SmartCull(double Threshold, int Charge, int SingleWindow,
  578. int DoubleWindow, int SingleNum, int DoubleNum)
  579. {
  580.     //       sort(MZI[MSORIGINAL], MZI[MSORIGINAL] + Num[MSORIGINAL], CMZICompare());
  581.     sort(MZI[MSORIGINAL], MZI[MSORIGINAL] + Num[MSORIGINAL], CMZICompareIntensity());
  582.     Sorted[MSORIGINAL] = false;
  583.     double Precursor;
  584.     int Which = GetWhich(Charge);
  585.     Precursor = GetMass()/(double)Charge;
  586.     int iMZI = 0;  // starting point
  587.     int TempLen(0);
  588.     CMZI *Temp = new CMZI [Num[MSORIGINAL]]; // temporary holder
  589.     // prep the data
  590.     CullBaseLine(Threshold, Temp, TempLen);
  591. #ifdef DEBUG_PEAKS1
  592.     {
  593. sort(Temp, Temp+TempLen , CMZICompare());
  594. ofstream FileOut("afterbase.dta");
  595. xWrite(FileOut, Temp, TempLen);
  596. sort(Temp, Temp+TempLen , CMZICompareIntensity());
  597.     }
  598. #endif
  599.     CullPrecursor(Temp, TempLen, Precursor);
  600. #ifdef DEBUG_PEAKS1
  601.     {
  602. sort(Temp, Temp+TempLen , CMZICompare());
  603. ofstream FileOut("afterprecurse.dta");
  604. xWrite(FileOut, Temp, TempLen);
  605. sort(Temp, Temp+TempLen , CMZICompareIntensity());
  606.     }
  607. #endif
  608.     CullIsotope(Temp, TempLen);
  609. #ifdef DEBUG_PEAKS1
  610.     {
  611. sort(Temp, Temp+TempLen , CMZICompare());
  612. ofstream FileOut("afteriso.dta");
  613. xWrite(FileOut, Temp, TempLen);
  614. sort(Temp, Temp+TempLen , CMZICompareIntensity());
  615.     }
  616. #endif
  617.     // h20 and nh3 loss ignored until this cut can be studied
  618. #if 0
  619.     sort(Temp, Temp + TempLen, CMZICompareHigh());
  620.     CullH20NH3(Temp, TempLen);
  621.     sort(Temp, Temp + TempLen, CMZICompareIntensity());
  622. #endif
  623.     // now do the recursion
  624.     int iTemp(0), jMZI;
  625.     set <int> Deleted;
  626.     map <int, int> MajorPeak; // maps a peak to it's "major peak"
  627.     int HitsAllowed; // the number of hits allowed in a window
  628.     int Window; // the m/z range used to limit the number of peaks
  629.     int HitCount;  // number of nearby peaks
  630.    
  631.     // scan for isotopes, starting at highest peak
  632.     for(iMZI = 0; iMZI < TempLen - 1; iMZI++) { 
  633. if(Deleted.count(iMZI) != 0) continue;
  634. HitCount = 0;
  635. if(Charge <  kConsiderMult || Temp[iMZI].MZ > Precursor) {
  636.     // if charge 1 region, allow fewer peaks
  637.     Window = SingleWindow; //27;
  638.     HitsAllowed = SingleNum;
  639. }
  640. else {
  641.     // otherwise allow a greater number
  642.     Window = DoubleWindow; //14;
  643.     HitsAllowed = DoubleNum;
  644. }
  645. // go over smaller peaks
  646. set <int> Considered;
  647. for(jMZI = iMZI + 1; jMZI < TempLen; jMZI++) { 
  648.     if(Deleted.count(jMZI) != 0) continue;
  649.     if(Temp[jMZI].MZ < Temp[iMZI].MZ + Window*MSSCALE + tol &&
  650.        Temp[jMZI].MZ > Temp[iMZI].MZ - Window*MSSCALE - tol) {
  651. if(IsMajorPeak(Temp[iMZI].MZ, Temp[jMZI].MZ, tol)) {
  652.     // link little peak to big peak
  653.     MajorPeak[jMZI] = iMZI;
  654.     // ignore for deletion
  655.     continue;
  656. }
  657. // if linked to a major peak, skip
  658. if(MajorPeak.find(jMZI) != MajorPeak.end()) {
  659.     if(Considered.find(jMZI) != Considered.end())
  660. continue;
  661.     Considered.insert(jMZI);
  662.     HitCount++;
  663.     if(HitCount <= HitsAllowed)  continue;
  664. }
  665. // if the first peak, skip
  666. HitCount++;
  667. if(HitCount <= HitsAllowed)  continue;
  668. Deleted.insert(jMZI);
  669.     }
  670. }
  671.     }
  672.     
  673.     // delete the culled peaks
  674.     for(iMZI = 0; iMZI < TempLen; iMZI++) {
  675. if(Deleted.count(iMZI) == 0) {
  676.     Temp[iTemp] = Temp[iMZI];
  677.     iTemp++;
  678. }
  679.     }
  680.     TempLen = iTemp;
  681.     // make the array of culled peaks
  682.     if(MZI[Which]) delete [] MZI[Which];
  683.     if(Used[Which]) delete [] Used[Which];
  684.     Num[Which] = TempLen;
  685.     MZI[Which] = new CMZI [TempLen];
  686.     Used[Which] = new char [TempLen];
  687.     ClearUsed(Which);
  688.     copy(Temp, Temp+TempLen, MZI[Which]);
  689.     Sort(Which);
  690. #ifdef DEBUG_PEAKS1
  691.     {
  692. ofstream FileOut("aftercull.dta");
  693. xWrite(FileOut, MZI[Which], Num[Which]);
  694.     }
  695. #endif
  696.     delete [] Temp;
  697. }
  698. // return the lowest culled peak and the highest culled peak less than the
  699. // +1 precursor mass
  700. void CMSPeak::HighLow(int& High, int& Low, int& NumPeaks, int PrecursorMass,
  701.       int Charge, double Threshold, int& NumLo, int& NumHi)
  702. {
  703.     int Which = GetWhich(Charge);
  704.     if(!Sorted[Which]) Sort(Which);
  705.     if(Num[Which] < 2) {
  706. High = Low = -1;
  707. NumPeaks = NumLo = NumHi = 0;
  708. return;
  709.     }
  710.     Low = PrecursorMass;
  711.     High = 0;
  712.     NumPeaks = 0;
  713.     NumLo = 0;
  714.     NumHi = 0;
  715.     int MaxI = GetMaxI(Which);
  716.     unsigned iMZI;
  717.     for(iMZI = 0; iMZI < Num[Which]; iMZI++) {
  718. if(MZI[Which][iMZI].Intensity > Threshold*MaxI &&
  719.    MZI[Which][iMZI].MZ <= PrecursorMass) {
  720.     if(MZI[Which][iMZI].MZ > High) {
  721. High = MZI[Which][iMZI].MZ;
  722.     }
  723.     if(MZI[Which][iMZI].MZ < Low) {
  724. Low = MZI[Which][iMZI].MZ;
  725.     }
  726.     NumPeaks++;
  727.     if(MZI[Which][iMZI].MZ < PrecursorMass/2.0) NumLo++;
  728.     else NumHi++;
  729. }
  730.     }
  731. }
  732. // count the number of putative AA intervals.
  733. // Nodup true means don't count the peaks twice
  734. int CMSPeak::CountAAIntervals(CMassArray& MassArray, bool Nodup, int Which)
  735. {
  736.     unsigned ipeaks, low;
  737.     int i;
  738.     int PeakCount(0);
  739.     if(!Sorted[Which]) return -1;
  740.     const int *IntMassArray = MassArray.GetIntMass();
  741.     // list <int> intensity;
  742.     // int maxintensity = 0;
  743.     for(ipeaks = 0 ; ipeaks < Num[Which] - 1; ipeaks++) {
  744. // if(maxintensity < MZI[ipeaks].Intensity) maxintensity = MZI[ipeaks].Intensity;
  745. low = ipeaks;
  746. low++;
  747. // if(maxintensity < MZI[low].Intensity) maxintensity = MZI[low].Intensity;
  748. for(; low < Num[Which]; low++) {
  749.     for(i = 0; i < kNumUniqueAA; i++) {
  750. if(IntMassArray[i] == 0) continue;  // skip gaps, etc.
  751. if(MZI[Which][low].MZ- MZI[Which][ipeaks].MZ < IntMassArray[i] + tol/2.0 &&
  752.    MZI[Which][low].MZ - MZI[Which][ipeaks].MZ > IntMassArray[i] - tol/2.0 ) {   
  753.     PeakCount++;
  754.     // intensity.push_back(MZI[ipeaks].Intensity);
  755.     if(Nodup) goto newpeak;
  756.     else goto oldpeak;
  757. }
  758.     }
  759. oldpeak:
  760.     i = i;
  761. }
  762.     newpeak:
  763. i = i;
  764.     }
  765.     return PeakCount;
  766. }
  767. // return maximum intensity value
  768. int CMSPeak::GetMaxI(int Which)
  769. {
  770.     unsigned Intensity(0), i;
  771.     for(i = 0; i < Num[Which]; i++) {
  772.         if(Intensity < MZI[Which][i].Intensity)
  773.     Intensity = MZI[Which][i].Intensity;
  774.     }
  775.     return Intensity;
  776. }
  777. /////////////////////////////////////////////////////////////////////////////
  778. //
  779. //  CMSPeakSet::
  780. //
  781. //  Class used to hold sets of CMSPeak and access them quickly
  782. //
  783. CMSPeakSet::~CMSPeakSet() 
  784. {
  785.     while(!PeakSet.empty()) {
  786. delete *PeakSet.begin();
  787. PeakSet.pop_front();
  788.     }
  789. }
  790. // compares m/z.  Lower m/z first in sort.
  791. struct CMassPeakCompareHi {
  792.     bool operator() (TMassPeak x, TMassPeak y)
  793.     {
  794. if(x.Mass + x.Peptol < y.Mass + y.Peptol) return true;
  795. return false;
  796.     }
  797. };
  798. void CMSPeakSet::SortPeaks(int Peptol)
  799. {
  800.     int iCharges;
  801.     CMSPeak* Peaks;
  802.     TPeakSet::iterator iPeakSet;
  803.     int CalcMass; // the calculated mass
  804.     TMassPeak temp;
  805.     int ptol; // charge corrected mass tolerance
  806.     // first sort
  807.     for(iPeakSet = GetPeaks().begin();
  808. iPeakSet != GetPeaks().end();
  809. iPeakSet++ ) {
  810. Peaks = *iPeakSet;
  811. // skip empty spectra
  812. if(Peaks->GetError() == eMSHitError_notenuffpeaks) continue;
  813. // loop thru possible charges
  814. for(iCharges = 0; iCharges < Peaks->GetNumCharges(); iCharges++) {
  815.     // correction for incorrect charge determination.
  816.     // see 12/13/02 notebook, pg. 135
  817.     ptol = Peaks->GetCharges()[iCharges] * Peptol;
  818.     CalcMass = static_cast <int> ((Peaks->GetMass() +
  819.    Peaks->GetCharge()*kProton*MSSCALE) * 
  820.   Peaks->GetCharges()[iCharges]/(double)(Peaks->GetCharge()) - 
  821.   Peaks->GetCharges()[iCharges]*kProton*MSSCALE);
  822.     temp.Mass = CalcMass;
  823.     temp.Peptol = ptol;
  824.     temp.Charge = Peaks->GetCharges()[iCharges];
  825.     temp.Peak = Peaks;
  826.     // order by upper bound
  827.     MassMap.insert(pair <const int, TMassPeak>(temp.Mass + temp.Peptol, temp)); 
  828. }
  829.     } 
  830.     // then create static array
  831.     ArraySize = MassMap.size();
  832.     MassPeak.reset(new TMassPeak[ArraySize]);
  833.     TMassPeakMap::iterator iMassMap;
  834.     int i(0);
  835.     for(iMassMap = MassMap.begin(); iMassMap != MassMap.end(); iMassMap++, i++) {
  836. GetMassPeak(i).Mass =
  837.  iMassMap->second.Mass;
  838. GetMassPeak(i).Peptol = iMassMap->second.Peptol;
  839. GetMassPeak(i).Peak = iMassMap->second.Peak;
  840. GetMassPeak(i).Charge = iMassMap->second.Charge;
  841.     }
  842.     MassMap.clear();
  843. }
  844. // Get the first index into the sorted array where the mass
  845. // + tolerance is >= the given calculated mass. 
  846. TMassPeak *CMSPeakSet::GetIndexLo(int Mass)
  847. {
  848.     TMassPeak temp;
  849.     TMassPeak *retval;
  850.     temp.Mass = Mass;
  851.     temp.Peptol = 0;
  852.     // look for first spectrum whose upper mass value + tolerance exceeds calculated mass
  853.     retval = lower_bound(MassPeak.get(), MassPeak.get() + ArraySize, temp, 
  854.  CMassPeakCompareHi());
  855.     return retval;
  856. }
  857. // get peak for sorted list by index into list
  858. CMSPeak *CMSPeakSet::GetPeak(int Index)
  859. {
  860.     if(Index < 0 || Index >= ArraySize) return 0;
  861.     return GetMassPeak(Index).Peak;
  862. }