/* massXpert - the true massist's program.
   --------------------------------------
   Copyright(C) 2006,2007 Filippo Rusconi

   http://www.filomace.org/massXpert

   This file is part of the massXpert project.

   The massxpert project is the successor to the "GNU polyxmass"
   project that is an official GNU project package(see
   www.gnu.org). The massXpert project is not endorsed by the GNU
   project, although it is released ---in its entirety--- under the
   GNU General Public License. A huge part of the code in massXpert
   is actually a C++ rewrite of code in GNU polyxmass. As such
   massXpert was started at the Centre National de la Recherche
   Scientifique(FRANCE), that granted me the formal authorization to
   publish it under this Free Software License.

   This software is free software; you can redistribute it and/or
   modify it under the terms of the GNU  General Public
   License version 3, as published by the Free Software Foundation.
   

   This software 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 this software; if not, write to the

   Free Software Foundation, Inc.,

   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

  
   
   NOTE : This work is based on a previous work by Dirk Nolting,
   licensed under the GNU General Public License:
   
   ***************************************************************************
   *  file                 :  ipc.c                                          *
   *  copyright            :(C) 2001-2005 by Dirk Nolting                   *
   *  email                : nolting@uni-duesseldorf.de                      *
   ***************************************************************************

   ***************************************************************************
   *                                                                         *
   *   This program 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.                                   *
   *                                                                         *
   ***************************************************************************
   *
   */


/////////////////////// Qt includes
#include <QtGui>


/////////////////////// Std includes
#include <math.h>
#include <algorithm>
#include <limits> // for std::numeric_limits

using namespace std;

/////////////////////// Local includes
#include "isotopicPatternCalculator.hpp"


namespace massXpert
{
  IsotopicPatternCalculator::IsotopicPatternCalculator
  (Formula formula,
   int charge,
   int maxPeakCentroids,
   double minProb,
   const QList<Atom *> &atomRefList,
   PeakShapeConfig config)
    : m_formula(formula), 
      m_charge(charge),
      m_maxPeakCentroids(maxPeakCentroids),
      m_minProb(minProb),
      m_atomRefList(atomRefList),
      m_config(config)
  {
    if(m_atomRefList.isEmpty())
      qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);
    
    if(!m_formula.validate(m_atomRefList, true, true))
      qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);
    
    if(m_charge <= 0)
      qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);
    
    if(m_config.fwhm() <= 0)
      qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);
    
    if(m_maxPeakCentroids < 1)
      qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);

    m_sumProbabilities = 0;
    m_greatestProbability = 0;
    
    m_aborted = false;
    // Check every n isotopes if we should abort.
    m_abortCheckCount = 100;
    
    m_progressValueOld = 0;
    m_progressValueNew = 0;

    m_formula.deepAtomCopy(m_atomRefList);
    
    // Debug stuff: make sure we get a formula that's correct.
    // const QList<AtomCount *> &atomCountList = m_formula.atomCountList();

    // qDebug() << "m_formula:" << m_formula.formula() << "\n";
    
    // for(int iter = 0; iter < atomCountList.size(); ++iter)
    //   {
    //     AtomCount *atomCount = atomCountList.at(iter);
        
    //     qDebug() << "atomCount name:" << atomCount->name()
    //              << "symbol:" << atomCount->symbol();
        
    //     const QList<Isotope *> &isotopeList = atomCount->isotopeList();

    //     for(int jter = 0; jter < isotopeList.size(); ++jter)
    //       {
    //         Isotope *isotope = isotopeList.at(jter);
            
            
    //         qDebug() << "isotope mass:" << isotope->mass();
    //       }
    //   }
  }
  

  IsotopicPatternCalculator::~IsotopicPatternCalculator()
  {
    freeClearPeakCentroidList();
    freeClearTempPeakCentroidList();
    freeClearPeakShapeList();
    freeClearPointList();
  }


  void
  IsotopicPatternCalculator::isotopicCalculationAborted()
  {
    m_aborted = true;
    emit(isotopicCalculationMessageChanged(tr("Aborting...")));
  }
  
  
  const QList<PeakCentroid *> &
  IsotopicPatternCalculator::peakCentroidList() const
  {
    return m_peakCentroidList;
  }


  const QList<QPointF *> &
  IsotopicPatternCalculator::pointList() const
  {
    return m_pointList;
  }
  

  QString *
  IsotopicPatternCalculator::peakCentroidListAsString() const
  {
    QString *text = new QString();
    
    for(int iter = 0, size = m_peakCentroidList.size();
        iter < size; ++iter)
      {
        PeakCentroid *peakCentroid = m_peakCentroidList.at(iter);
        text->append(QString().setNum(peakCentroid->mz(), 'f', 6));
        text->append(" ");
        text->append(QString().setNum(peakCentroid->relativeIntensity(), 
                                      'f', 15));
        text->append("\n");
      }
    
    return text;
  }
  
  

  bool 
  IsotopicPatternCalculator::seedPeakCentroidList()
  {
    // Let's seed all the calculations with the first centroid peak.
    PeakCentroid *firstPeakCentroid = new PeakCentroid(0, 0, 0);
    firstPeakCentroid->setMz(0);
    firstPeakCentroid->setProbability(1);
    
    freeClearPeakCentroidList();
    
    m_peakCentroidList.append(firstPeakCentroid);

    return true;
  }
  

  void
  IsotopicPatternCalculator::freeClearPeakCentroidList()
  {
    while(!m_peakCentroidList.isEmpty())
      delete m_peakCentroidList.takeFirst();
  }
  
  
  void
  IsotopicPatternCalculator::freeClearTempPeakCentroidList()
  {
    while(!m_tempPeakCentroidList.isEmpty())
      delete m_tempPeakCentroidList.takeFirst();

    m_tempPeakCentroidList.clear();
  }
  
  
  void
  IsotopicPatternCalculator::freeClearPeakShapeList()
  {
    while(!m_peakShapeList.isEmpty())
      delete m_peakShapeList.takeFirst();

    m_peakShapeList.clear();
  }
  
  
  void
  IsotopicPatternCalculator::freeClearPointList()
  {
    while(!m_pointList.isEmpty())
      delete m_pointList.takeFirst();

    m_pointList.clear();
  }
  
  
  int
  IsotopicPatternCalculator::calculatePeakCentroids()
  {
    // Get a shortcut handle to the list of AtomCount object that were
    // set in the formula during the validation in the constructor
    // this *this object.

    // One atom count instance is made of two entities : an atom and
    // the number of times that atom occurs in the formula. In a
    // formula, thus, there can be atomCounts [C:4] [H:10] and so
    // on. For the atomCount [C:4] (4 atoms of carbon in the formula),
    // we'll have to account for all the carbon isotopes, 4 times in
    // sequence. That is handled in the function call
    // accountAtomCount() below.

    freeClearTempPeakCentroidList();

    seedPeakCentroidList();
    
    // Set the m_progressValue to 0, as we are going to make a
    // calculation. Each time 10 new isotopes are added, we increment
    // that value and emit a signal so that the progress bar can be
    // updated in the calling dialog window.
    m_progressValueOld = 0;
    m_progressValueNew = 0;
    
    emit(isotopicCalculationMessageChanged(tr("Starting calculations.")));
    qApp->processEvents();
    
    const QList<AtomCount *> &atomCountList = m_formula.atomCountList();

    for (int iter = 0, atomCountCount = atomCountList.size() ;
         iter < atomCountCount ; ++iter)
      {
        // Get the currently iterated atom count instance ([C:4], for
        // example).
        AtomCount *atomCount = atomCountList.at(iter);
 
        // Account for these atoms, in sequence.
        accountAtomCount(atomCount);
      }

    emit(isotopicCalculationMessageChanged
         (tr("Calculating sum of probabilities")));
    calculateSumOfProbabilities();
    
    emit(isotopicCalculationMessageChanged
         (tr("Calculating relative intensities")));
    calculateRelativeIntensity();

    emit(isotopicCalculationMessageChanged
         (tr("Removing too low probability peaks")));
    removeTooLowProbPeaks();
    
    if(m_aborted)
      emit(isotopicCalculationMessageChanged
           (tr("Aborted")));
    else
      emit(isotopicCalculationMessageChanged
           (tr("Done computing the isotopic peaks.")));
    
    qApp->processEvents();
    
    return 1;
  }
  

  int 
  IsotopicPatternCalculator::calculatePeakShapes()
  {
    // Once all the peak centroids have been computed, it necessary to
    // produce the peak shapes for each one.

    if(m_peakCentroidList.isEmpty())
      {
        int res = calculatePeakCentroids();
    
        if(!res)
          return 0;
      }
    
    for (int iter = 0, peakCentroidListSize = m_peakCentroidList.size();
         iter < peakCentroidListSize; ++iter)
      {
        PeakCentroid *peakCentroid = m_peakCentroidList.at(iter);
        
	double mz = peakCentroid->mz();
	double relInt = peakCentroid->relativeIntensity();
        
        PeakShape *peakShape = new PeakShape(mz, 
                                             relInt,
                                             0 /* probability */,
                                             m_config);
                
        peakShape->calculatePeakShape();

        m_peakShapeList.append(peakShape);
      }

    return m_peakShapeList.size();
  }
  

  const QList<QPointF *>  &
  IsotopicPatternCalculator::sumPeakShapes()
  {
    // Clear the point list.
    freeClearPointList();
    
    // Make sure that we have computed each individual peak shape for
    // the centroids. If not, then do it first.

    int peakShapeListSize = m_peakShapeList.size();

    if(!peakShapeListSize)
      {
        if(!calculatePeakShapes())
          return m_pointList;

        peakShapeListSize = m_peakShapeList.size();
      }
    
    // At this point, each centroid peak, that is each
    // mzRatio/intensity pair in m_peakCentroidList has given rise to
    // a full gaussian/lorentzian curve that's been appended in the
    // list of peak shapes.

    // We now have to perform the sum of all these peak shapes so as
    // to generate a single (x,y) curve that encompasses all of the
    // isotopic cluster.

    // We need to know the min and max mzRatio values of all the
    // curves.

    // Peak shapes should be generated from left to right in the
    // pointList spectral view, that is from lowest mz to higest
    // mz. So, logically, the first point of the first peak shape
    // should be the point of lowest mz. Likewise, the last point of
    // the last peak shape should be the highest mz.
    
    PeakShape *peakShape = 0;
    QPointF *point = 0;
    
    // Get first point of the first peakShape and its x value.

    peakShape = m_peakShapeList.first();
    point = peakShape->pointList().first();
    double minMz = point->x();
    
    peakShape = m_peakShapeList.last();
    point = peakShape->pointList().last();
    double maxMz = point->x();


#ifdef NOT_DEFINED

    // Old version that is more CPU-consuming.

    double tempMz = 0;
    double maxMz = 0;
    double minMz = 1000000000;

    for(int iter = 0; iter < peakShapeListSize ; ++iter)
      {
        PeakShape *peakShape = m_peakShapeList.at(iter);
      
        // Get to the list of points in the peak shape
        const QList<QPointF *> &peakCentroidList = peakShape->pointList();
        
        // Get first point of the peakShape.
        QPointF *point = peakCentroidList.first();

        // Get the minimum mz
        tempMz = point->x();
      
        if(tempMz < minMz)
          minMz = tempMz;

        // Get last point of the peakShape.
        point = peakCentroidList.last();

        // Get the maximum mz
        tempMz = point->x();
      
        if(tempMz > maxMz)
          maxMz = tempMz;
      }
#endif

    // At this point we know that our mass point list (the spectral
    // view of the isotopic cluster) should range [minMz -- maxMz]. We
    // can perform the actual sum of all the peak shapes. The result
    // of the sum should go into the spectral list of points.

    double curMz = minMz;
  
    while(curMz <= maxMz)
      {
        // curMz now has a mz value for which we have to sum all the
        // intensities in each curve.
      
        double summedIntensity = 0;
      
        for(int iter = 0; iter < peakShapeListSize ; ++iter)
          {
            PeakShape *peakShape = m_peakShapeList.at(iter);
          
            bool ok = false;
          
            summedIntensity += peakShape->intensityAt(curMz, 
                                                      m_config.increment(),
                                                      &ok);
          }
      
        QPointF *point = new QPointF(curMz, summedIntensity);
        
        m_pointList.append(point);

        curMz += m_config.increment();
      }
    
    return m_pointList;
  }
  
  
  QString *
  IsotopicPatternCalculator::pointListAsString() const
  {
    QString *text = new QString();
    
    for(int iter = 0, size = m_pointList.size();
        iter < size ; ++iter)
      {
        QPointF *point = m_pointList.at(iter);
        
        text->append(QString().setNum(point->x(), 'f', 10));
        text->append(" ");
        text->append(QString().setNum(point->y(), 'f', 10));
        text->append("\n");
      }
    
    return text;
  }
  
  
  QList<QPointF *> *
  IsotopicPatternCalculator::duplicatePointList() const
  {
    QList<QPointF *> *pointList = new QList<QPointF *>;
    
    for(int iter = 0, size = m_pointList.size();
        iter < size ; ++iter)
      {
        QPointF *point = new QPointF(*(m_pointList.at(iter)));
        pointList->append(point);
      }
    
    return pointList;
  }
  

  int
  IsotopicPatternCalculator::transferPoints(QList<QPointF *> *pointList)
  {
    if(!pointList)
      qFatal("Fatal error at %s@%d.Aborting.", __FILE__, __LINE__);

    int count = 0;
    
    while(m_pointList.size())
      {
        pointList->append(m_pointList.takeFirst());
        ++count;
      }
    
    return count;
  }
  
  
  QPointF *
  IsotopicPatternCalculator::firstPoint() const
  {
    return m_pointList.first();
  }
  

  QPointF *
  IsotopicPatternCalculator::lastPoint() const
  {
    return m_pointList.last();
  }
  

  int
  IsotopicPatternCalculator::accountAtomCount(const AtomCount *atomCount)
  {
    if(!atomCount)
      qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);
    
    // One atom count instance is made of two entities : an atom and
    // the number of times that atom occurs in the formula. Such pair
    // of values can be represented this way : [C:4], for example, to
    // say that there are 4 Carbon atoms in the formula.
    
    // Each atom, in turn has a list of isotopes (2 isotopes for
    // Carbon). We have to account in sequence for each atom in the
    // atomCount instance.
    
    QString message;
    message += tr("Accounting atom: ");
    message += atomCount->symbol();
    
    emit(isotopicCalculationMessageChanged(message));

    // qDebug() << __FILE__ << __LINE__
    //          << message
    //          << "m_peakCentroidList size:" << m_peakCentroidList.size();
    
    int count = atomCount->count();
    
    while(count)
      {
        // We check if abortion was requested every m_abortCheckCount
        // isotopes.
        if(m_progressValueNew && 
           (m_progressValueNew % m_abortCheckCount == 0) &&
           m_aborted)
          break;
                  
        updatePeakCentroidListWithAtom(atomCount);
        
        // Each time a new atomCount count is dealt with, we increment
        // that progress report value and emit a signal so that the
        // progress bar can be updated in the calling dialog window.
        emit(isotopicCalculationProgressValueChanged(++m_progressValueNew));

        --count;
      }

    // qDebug() << __FILE__ << __LINE__
    //          << "Done accouting atomCount" << atomCount->symbol()
    //          << "m_peakCentroidList size:" << m_peakCentroidList.size();
    
    // return the number of atomCount instances actually accounted
    // for.
    return  (atomCount->count() - count);
  }
  

  int
  IsotopicPatternCalculator::updatePeakCentroidListWithAtom(const Atom *atom)
  {
   if(!atom)
      qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);

    // For each peak in the centroid peak list, we'll increment it
    // with the mass and probability of each isotope of the new
    // atom. Each time a peak in the peak list is incremented, we'll
    // create a brand new peak that we'll set in a new peak list
    // called m_tempPeakCentroidList.

   for (int iter = 0, peakCount = m_peakCentroidList.size();
        iter < peakCount ; ++iter)
     {
       PeakCentroid *peakCentroid = m_peakCentroidList.at(iter);
       
       for(int jter = 0, isotopeCount = atom->isotopeList().size() ;
           jter < isotopeCount; ++jter)
         {
           Isotope *isotope = atom->isotopeList().at(jter);
           
           updatePeakCentroidWithIsotope(peakCentroid, isotope);
         }
     }
   
   // Now that we have updated all the centroid peaks with all the
   // isotopes of the atom, we can make some housekeeping work. In
   // particular, when doing the updating of all the isotopic peaks
   // in m_peakCentroidList, we actually created a new list of
   // peaks. We want to get rid of all the old peaks and set the
   // m_peakCentroidList reference to point to the new list of peaks.
   
   freeClearPeakCentroidList();
   
   m_peakCentroidList = m_tempPeakCentroidList;
   m_tempPeakCentroidList.clear();
   
   mergePeakCentroidsWithSameMz();
   
   removePeakCentroidsInExcess();
   
   return m_peakCentroidList.size();
  }
  

  int
  IsotopicPatternCalculator::updatePeakCentroidWithIsotope
  (PeakCentroid *peakCentroid,
   const Isotope *isotope)
  {
    if(!peakCentroid)
      qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);
 
    if(!isotope)
      qFatal("Fatal error at %s@%d. Aborting.",__FILE__, __LINE__);
 
    PeakCentroid *newPeakCentroid = 
      new PeakCentroid(peakCentroid->mz() +
                       isotope->mass() / m_charge,
                       0,
                       peakCentroid->probability() *
                       (isotope->abundance() / 100));
                
    // Get the mass of the new peak. We'll use it massively later.
    double newPeakCentroidMz = newPeakCentroid->mz();
    
    if (m_tempPeakCentroidList.isEmpty())
      {
        // First iteration, the m_tempPeakCentroidList is empty, thus simply
        // append to the list the newly allocated peak list instance.

        m_tempPeakCentroidList.append(newPeakCentroid);
                        
        // qDebug() << "Appended new peak:"
        //          << newPeakCentroid->mass() << "--"
        //          << newPeakCentroid->probability();
      }
    else // m_tempPeakCentroidList is not empty
      {
        // The m_tempPeakCentroidList has items in it. We want to insert the
        // newly allocated peak at the proper location, mass-based,
        // that is right after the peak item that has a mass just less
        // than ours.

        bool inserted = false;
        
        for(int iter = 0; iter < m_tempPeakCentroidList.size(); ++iter)
          {
            PeakCentroid *iteratedPeakCentroid = 
              m_tempPeakCentroidList.at(iter);
		
            // The iterated peak has a mass greater than ours, thus go
            // on to next iteration.
            if (newPeakCentroidMz > iteratedPeakCentroid->mz())
              continue;
			  
            // Ah, finally, we found a place to insert our peak.
            m_tempPeakCentroidList.insert(iter, newPeakCentroid);
                            
            // qDebug() << "Inserted new peak:"
            //          << newPeakCentroid->mass() << "--"
            //          << newPeakCentroid->probability();
                            
            inserted = true;

            break;
          }
		      
        // If we did not insert the peak, then that means that we
        // arrived here because we finished iterating in the
        // list... Thus append the peak.
        if(!inserted)
          {
            m_tempPeakCentroidList.append(newPeakCentroid);
                            
            // qDebug() << "Appended peak:"
            //          << newPeakCentroid->mass() << "--"
            //          << newPeakCentroid->probability();	  
          }
        else
          {
            // Reinitialize the bool to false.
            inserted = false;
          }
		      
        // qDebug() << "List of peaks:";
        //
        // for (int zter = 0; zter < m_tempPeakCentroidList.size(); ++zter)
        //   qDebug() << m_tempPeakCentroidList.at(zter)->mass()
        //            << "--" 
        //            << m_tempPeakCentroidList.at(zter)->probability();
      }
    // End of
    // else m_tempPeakCentroidList is not empty

    // So we have dealt with one isotope. Just return.

    return 1;
  }
  
  int 
  IsotopicPatternCalculator::mergePeakCentroidsWithSameMz()
  {
    // There might be some peaks very near one to the other in mass in
    // the m_peakCentroidList. Our criterium to "merge" two peaks
    // which are too near in mass is that if the masses differ by less
    // than the FWHM, then we'll merge them adding the probabilities
    // together.

    int removedPeaks = 0;

    for (int iter = 0; iter < m_peakCentroidList.size() - 1; ++iter)
      {
        PeakCentroid *curPeakCentroid = m_peakCentroidList.at(iter);
        PeakCentroid *nextPeakCentroid = m_peakCentroidList.at(iter + 1);
        
        // qDebug() << __FILE__ << __LINE__ << "\n"
        //          << "\tCentroid peak at" << iter 
        //          << ":" << curPeak->mass() << "\n\t"
        //          << "\tCentroid peak at" << (iter + 1)
        //          << ":" << nextPeak->mass() << "\n";
                  
        double absDiff = 
          fabs(curPeakCentroid->mz() - nextPeakCentroid->mz());
        
        // qDebug() << __FILE__ << __LINE__ << "\n"
        //          << "\tabsDiff is" << absDiff << "with m_fwhm:" << m_fwhm;
        
        // This dividing m_fwhm by three was added to successfully fix
        // the calculation bug that would center the shape of a
        // non-resolved mass peak (too low a resolution setting) at a
        // mass very much less than average mass !
        if(absDiff <= m_config.fwhm() / 3)
          {
		  
            // Remove the forward (next) peak that is too close to
            // current one, but do not forget to account for that one
            // from the abundance point of vue.
            
            curPeakCentroid->incrementProbability
              (nextPeakCentroid->probability());

            delete m_peakCentroidList.takeAt(iter + 1);

            // qDebug() << __FILE__ << __LINE__ << "\n"
            //          << "\nRemoved mass-similar peak.";
            
            ++removedPeaks;
            
            --iter;
          } 
      }

    return removedPeaks;
  }
  
  
  int 
  IsotopicPatternCalculator::removePeakCentroidsInExcess()
  {
    // The maximum number of peaks that are allowed by the user is
    // m_maxPeaks. Remove all the peaks that are found in the
    // m_peakCentroidList after that count.
    
    int removedPeaks = 0;
    
    int peakCount = m_peakCentroidList.size();

    // qDebug() << __FILE__ << __LINE__ << "\n"
    //          << "\tm_peakCentroidList size:" << peakCount
    //          << "and m_maxPeaks is:" << m_maxPeaks;
    
    while(peakCount > m_maxPeakCentroids)
      {
        delete m_peakCentroidList.takeLast();

        // qDebug() << __FILE__ << __LINE__ << "\n"
        //          << "\tRemoved peak.";
        
        ++removedPeaks;
        
        --peakCount;
      }
    
    return removedPeaks;
  }
  
  
  int
  IsotopicPatternCalculator::calculateSumOfProbabilities()
  {
    PeakCentroid *peakCentroid = 0;
    int count = 0;
    
    foreach (peakCentroid, m_peakCentroidList)
      {
        double probability = peakCentroid->probability();
        
	m_sumProbabilities += probability;
        
        m_greatestProbability = (probability > m_greatestProbability ?
                                 probability : m_greatestProbability);
        ++count;
      }
    
    return count;
  }
  

  int 
  IsotopicPatternCalculator::calculateRelativeIntensity()
  {
    calculateSumOfProbabilities();
    
    for (int iter = 0, peakCount = m_peakCentroidList.size();
         iter < peakCount; ++iter)
      {
	PeakCentroid *peakCentroid = m_peakCentroidList.at(iter);
      
	double relativeIntensity = 
          (peakCentroid->probability() / m_greatestProbability) * 100;
        
	if(relativeIntensity > std::numeric_limits<double>::min())
	  peakCentroid->setRelativeIntensity(relativeIntensity);
      }

    return 1;
  }
  
  
  int 
  IsotopicPatternCalculator::removeTooLowProbPeaks()
  {
    int peakCount = m_peakCentroidList.size();
    
    if(!peakCount)
      return 1;
    
    for(int iter = peakCount - 1; iter >= 0 ; --iter)
      {
        PeakCentroid *peakCentroid = m_peakCentroidList.at(iter);

	if(peakCentroid->probability() < m_minProb)
          {
            // The peak has too low a probability. Remove it.

            // qDebug() << __FILE__ << __LINE__
            //          << "Removing too low prob peak at: " << iter
            //          << "With mass:" << peakCentroid->mass()
            //          << "Prob:" << peakCentroid->probability();
            
            delete m_peakCentroidList.takeAt(iter);
          }
      }
  
    return 1;
  }

} // namespace massXpert



#if 0
  /***************************************************************************
   *  file                 :  ipc.c                                          *
   *  copyright            :(C) 2001-2005 by Dirk Nolting                   *
   *  email                : nolting@uni-duesseldorf.de                      *
   ***************************************************************************/

  /***************************************************************************
   *                                                                         *
   *   This program 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.                                   *
   *                                                                         *
   ***************************************************************************/

#include "global.h"
#include "ipc.h"
#include "pars.h"
#include "element.h"
#include "gp_out.h"
#include <time.h>
#include <signal.h>
#include <math.h>

//#define SUMMARIZE_LEVEL 0.00000001
//#define SUMMARIZE_LEVEL 0.0001
//#define SUMMARIZE_LEVEL 0.001
#define SUMMARIZE_LEVEL 0.01

  compound *verbindung=NULL;
  isotope *m_peakList;
  int fast_calc=0;

  void free_list(isotope *target)
  {
    while(target->next)
      {
	target=target->next;
	free(target->previous);
      }
    free(target);
  }

  void cut_peaks(isotope *spectrum)
  {
    int dummy=1;

    while((spectrum->next) &&(dummy<fast_calc) )
      {
	++dummy;
	spectrum=spectrum->next;
      }

    if(spectrum->next)
      {
	free_list(spectrum->next);
	spectrum->next=NULL;
      }
  }

  void summarize_peaks()
  {  isotope *dummy,*d2;

    for(dummy=m_peakList;dummy;dummy=dummy->next)
      /* Differenz wegen Rundungsfehlern */
      while( dummy->next &&(dummy->next->mass - dummy->mass < SUMMARIZE_LEVEL) )
	{
	  d2=dummy->next;
	  dummy->next=d2->next;
	  if(dummy->next)
	    dummy->next->previous=dummy;
	  dummy->p+=d2->p;
	  free(d2);
	}
  }

  isotope *add_peak(isotope *base,isotope *peak)
  {
    static isotope *IsotopeIterator;

    if(!(base->mass))
      {
	peak->next=NULL;
	peak->previous=NULL;
	IsotopeIterator = peak;
	return peak;
      }

    if( peak->mass >= IsotopeIterator->mass )
      while((IsotopeIterator->next) &&(IsotopeIterator->mass < peak->mass))
	IsotopeIterator=IsotopeIterator->next;
    else
      {
	while((IsotopeIterator->previous) &&(IsotopeIterator->mass > peak->mass) )
	  IsotopeIterator=IsotopeIterator->previous;
	IsotopeIterator=IsotopeIterator->next;
      }

    if((IsotopeIterator->mass) >=(peak->mass) )
      {
	peak->next=IsotopeIterator;
	peak->previous=IsotopeIterator->previous;
	peak->previous->next=peak;
	IsotopeIterator->previous=peak;
	return base;
      }
    else
      {
	IsotopeIterator->next=peak;
	peak->next=NULL;
	peak->previous=IsotopeIterator;
	return base;
      }
    return 0;
  }

  int calculate_peaks(){
    compound *c;
    isotope *newPeakCentroidList,*p,*i,*np1;
    int amount;

    if(!(m_peakList=malloc(sizeof(isotope))))
      return 0;
    m_peakList->mass=0;
    m_peakList->p=1;
    m_peakList->previous=NULL;
    m_peakList->next=NULL;

    for (c = verbindung; c; c = c->next)
      {
	for(amount = 0; amount < c->amount; ++amount)
	  {
	    if (!(newPeakCentroidList=malloc(sizeof(isotope))))
	      return 0;
	  
	    newPeakCentroidList->mass = 0;
	  
	    for (p = m_peakList; p; p = p->next)
	      {
		for(i = c->isotopes; i; i = i->next)
		  {
		    //printf("working on isotope of mass %f\n", i->mass);

		    if (!(np1 = malloc(sizeof(isotope))))
		      return 0;
		  
		    np1->mass=p->mass + i->mass;
		    np1->p=p->p * i->p;
		  
		    if(!(newPeakCentroidList = 
                         add_peak(newPeakCentroidList,np1)))
		      return 0;
		  }
	      }
            
	    free_list(m_peakList);
	    m_peakList = newPeakCentroidList;
	    summarize_peaks();
            
	    if (fast_calc)
	      cut_peaks(m_peakList);
	  }
      }
  
    return 1;
  }


  void print_result(int digits,int charge){
    isotope *d;
    double maxp=0,relint=0,sump=0;
    int permutationen=0;

    printf("\n");
 
    for(d=m_peakList;d;d=d->next)
      {
	++permutationen;
	sump+=d->p;
	d->mass=d->mass / charge;
	d->mass=(rint( d->mass * pow(10,digits) ) / pow(10,digits) );
      }

    summarize_peaks();
    for(d=m_peakList;d;d=d->next)
      if(d->p > maxp)
	maxp=d->p;

    for(d=m_peakList;d;d=d->next)
      {
	if(( relint=(d->p/maxp)*100) > MIN_INT )
	  printf("M= %f, p= %e, rel. Int.= %f%%\n",
		 d->mass,d->p,relint);
      }
    if(!(fast_calc))
      printf("\nNumber of  permutations: %i\n",permutationen);
    else
      {
	sump=(rint(sump*10000)/100); 
	printf("\nCovered Intensity: %2.2f%% \n",sump);
      }
  }

  int main(int argc,char **argv){
    long seconds;
    int d=1,zeig_summenformel=0,calc_peaks=1,gnuplot=0,use_digits=USE_DIGITS,charge=1;
    char *gnuplotfile=NULL;
  
    if(!argv[d])
      {
	usage();
	return 1;
      }

#ifdef HAVE_SIGNAL
    signal(SIGHUP,SIG_IGN);
#endif

    if(!init_elements()){
      printf("Error in init_elements\n");
      return 1;
    }
    while(argv[d])
      {
	if(!strcmp(argv[d],"-f"))
	  {
	    ++d;
	    if(!argv[d])
	      {
		printf("Missing argument for -f\n");
		return 1;
	      }
	    fast_calc=strtol(argv[d],NULL,10);
	  }

	else if(!strcmp(argv[d],"-z"))
	  {
	    ++d;
	    if(!argv[d])
	      {
		printf("Missing argument for -z\n");
		return 1;
	      }
	    charge=strtol(argv[d],NULL,10);
	  }

	else if(!strcmp(argv[d],"-d"))
	  {
	    ++d;
	    if(!argv[d])
	      {
		printf("Missing argument for -d\n");
		return 1;
	      }
	    use_digits=strtol(argv[d],NULL,10);
	  }

	else if(!strcmp(argv[d],"-c")){
	  ++d;
	  if(!argv[d])
	    {
	      printf("Missing argument for -c\n");
	      return 1;
	    }
	  if(!pars_chem_form(argv[d])){
	    printf("Parser error.\n");
	    return 1;
	  }
	}

	else if(!strcmp(argv[d],"-g")){
	  ++d;
	  if(!argv[d]){
	    printf("Missing argument for -g\n");
	    return 1;
	  }
	  gnuplot=1;
	  if(!(gnuplotfile=strdup(argv[d]))){
	    printf("Not enough memory\n");
	    return 1;
	  }
	}


	else if(!strcmp(argv[d],"-p")){
	  ++d;
	  if(!argv[d]){
	    printf("Missing argument for -p\n");
	    return 1;
	  }
	  if(!(pars_peptid(argv[d]))){
	    printf("Error in peptid parser.\n");
	    return 1;
	  }
	}

	else if(!strcmp(argv[d],"-a")){
	  ++d;
	  if(!argv[d]){
	    printf("Missing argument for -a\n");
	    return 1;
	  }
	  if(!pars_amino_acid(argv[d])){
	    printf("Error in pars_amino_acid.\n");
	    return 1;
	  }
	}

	else if(!strcmp(argv[d],"-s"))
	  zeig_summenformel=1;

	else if(!strcmp(argv[d],"-h"))
	  {
	    usage();
	    return 1;
	  }

	else if(!strcmp(argv[d],"-x"))
	  calc_peaks=0;

	else
	  {
	    printf("Unknown flag: %s\n",argv[d]);
	    usage();
	    return 1;
	  }
	++d;
      }

    if(zeig_summenformel)
      {
	if(!print_sum())
	  {
	    printf("error while showing chemical formula.\n");
	    return 1;
	  }
      }
#ifdef HAVE_TIME
    seconds=time(0);
#endif

    if(calc_peaks)
      if(!calculate_peaks()){
	printf("Error in calculate_peaks\n");
	return 1;
      }

    print_result(use_digits,charge);

#ifdef HAVE_TIME
    seconds=time(0)-seconds;
    printf("Computing time: %li seconds.\n",seconds);
#endif

    if(gnuplot)
      if(!(make_gnuplot_output(gnuplotfile))){
	printf("Error while generating gnuplot file\n");
	return 1;
      }

    return 0;
  }


  void usage()
  {
    printf("\nThis is IPC v %s\nCopyright Dirk Nolting 2001-2005\n\n",VERSION);
    printf("\nSynopsis:\n ipc -d <int> -z <int> -f <int> <-a <amino acid> -c <chemical formula> -p <File> -g <name> -s -x -h\n\n");

    printf("-c <chemical formula> calculates the isotopic pattern of the \n");
    printf("   given chemical formula. Additive with -p\n");

    printf("-p <File> reads peptide sequenz(one letter notation) from the \n");
    printf("   given file and calculates the isotopic pattern. Additive with -c\n");

    printf("-a <amino acids> calculate isotopic pattern from given amino acids\n");
    printf("   in one letter notation\n");

    printf("-s gives the chemical formula. Usefull for -a or -p\n");
    printf("-g creates gnuplot output and stores it in the file <name> and <name>.gnu\n");

    printf("-x no calculation of the isotopic pattern. Usefull for -s\n");
    printf("-f fast calc, calculates only first <int> peaks\n");
    printf("-d <int> digits significant\n");
    printf("-z assume <int> charges on ion \n");
    printf("-h show this text\n\n");
  }
#endif
