void TheoreticalSpectrumGenerator::addLosses_(RichPeakSpectrum & spectrum, const AASequence & ion, double intensity, Residue::ResidueType res_type, int charge) const 
  {
    RichPeak1D p;

    set<String> losses;
    for (AASequence::ConstIterator it = ion.begin(); it != ion.end(); ++it)
    {
      if (it->hasNeutralLoss())
      {
        vector<EmpiricalFormula> loss_formulas = it->getLossFormulas();
        for (Size i = 0; i != loss_formulas.size(); ++i)
        {
          losses.insert(loss_formulas[i].toString());
        }
      }
    }

    if (!add_isotopes_)
    {
      p.setIntensity(intensity * rel_loss_intensity_);
    }

    for (set<String>::const_iterator it = losses.begin(); it != losses.end(); ++it)
    {
      EmpiricalFormula loss_ion = ion.getFormula(res_type, charge) - EmpiricalFormula(*it);
      // thanks to Chris and Sandro
      // check for negative element frequencies (might happen if losses are not allowed for specific ions)
      bool negative_elements(false);
      for (EmpiricalFormula::ConstIterator eit = loss_ion.begin(); eit != loss_ion.end(); ++eit)
      {
        if (eit->second < 0)
        {
          negative_elements = true;
          break;
        }
      }
      if (negative_elements)
      {
        continue;
      }
      double loss_pos = loss_ion.getMonoWeight() / (double)charge;
      const String& loss_name = *it;

      if (add_isotopes_)
      {
        IsotopeDistribution dist = loss_ion.getIsotopeDistribution(max_isotope_);
        UInt j(0);
        for (IsotopeDistribution::ConstIterator iso = dist.begin(); iso != dist.end(); ++iso)
        {
          p.setMZ((double)(loss_pos + j) / (double)charge);
          p.setIntensity(intensity * rel_loss_intensity_ * iso->second);
          if (add_metainfo_ && j == 0)
          {
            // note: important to construct a string from char. If omitted it will perform pointer arithmetics on the "-" string literal
            String ion_name = String(residueTypeToIonLetter_(res_type)) + String(ion.size()) + "-" + loss_name + String(charge, '+');
            p.setMetaValue("IonName", ion_name);
          }
          spectrum.push_back(p);
        }
      }
      else
      {
        p.setMZ(loss_pos);
        if (add_metainfo_)
        {
          // note: important to construct a string from char. If omitted it will perform pointer arithmetics on the "-" string literal
          String ion_name = String(residueTypeToIonLetter_(res_type)) + String(ion.size()) + "-" + loss_name + String(charge, '+');
          p.setMetaValue("IonName", ion_name);
        }
        spectrum.push_back(p);
      }
    }

  }
  void TheoreticalSpectrumGenerator::addPeaks(RichPeakSpectrum & spectrum, const AASequence & peptide, Residue::ResidueType res_type, Int charge)
  {
    if (peptide.empty())
    {
      return;
    }

    Map<DoubleReal, AASequence> ions;
    Map<DoubleReal, String> names;
    AASequence ion;
    DoubleReal intensity(0);
    bool add_first_prefix_ion(param_.getValue("add_first_prefix_ion").toBool());

    // generate the ion peaks
    switch (res_type)
    {
    case Residue::AIon:
    {
      Size i = 1;
      if (!add_first_prefix_ion)
      {
        i = 2;
      }
      for (; i < peptide.size(); ++i)
      {
        ion = peptide.getPrefix(i);
        DoubleReal pos = ion.getMonoWeight(Residue::AIon, charge) / (DoubleReal)charge;
        ions[pos] = ion;
        names[pos] = "a" + String(i) + String(charge, '+');
      }
      intensity = (DoubleReal)param_.getValue("a_intensity");
      break;
    }

    case Residue::BIon:
    {
      Size i = 1;
      if (!add_first_prefix_ion)
      {
        i = 2;
      }
      for (; i < peptide.size(); ++i)
      {
        ion = peptide.getPrefix(i);
        DoubleReal pos = ion.getMonoWeight(Residue::BIon, charge) / (DoubleReal)charge;
        ions[pos] = ion;
        names[pos] = "b" + String(i) + String(charge, '+');
      }
      intensity = (DoubleReal)param_.getValue("b_intensity");
      break;
    }

    case Residue::CIon:
    {
      Size i = 1;
      if (!add_first_prefix_ion)
      {
        i = 2;
      }
      for (; i < peptide.size(); ++i)
      {
        ion = peptide.getPrefix(i);
        DoubleReal pos = ion.getMonoWeight(Residue::CIon, charge) / (DoubleReal)charge;
        ions[pos] = ion;
        names[pos] = "c" + String(i) + String(charge, '+');
      }
      intensity = (DoubleReal)param_.getValue("c_intensity");
      break;
    }

    case Residue::XIon:
    {
      for (Size i = 1; i < peptide.size(); ++i)
      {
        ion = peptide.getSuffix(i);
        DoubleReal pos = ion.getMonoWeight(Residue::XIon, charge) / (DoubleReal)charge;
        ions[pos] = ion;
        names[pos] = "x" + String(i) + String(charge, '+');
      }
      intensity = (DoubleReal)param_.getValue("x_intensity");
      break;
    }

    case Residue::YIon:
    {
      for (Size i = 1; i < peptide.size(); ++i)
      {
        ion = peptide.getSuffix(i);
        DoubleReal pos = ion.getMonoWeight(Residue::YIon, charge) / (DoubleReal)charge;
        ions[pos] = ion;
        names[pos] = "y" + String(i) + String(charge, '+');
      }
      intensity = (DoubleReal)param_.getValue("y_intensity");
      break;
    }

    case Residue::ZIon:
    {
      for (Size i = 1; i < peptide.size(); ++i)
      {
        ion = peptide.getSuffix(i);
        DoubleReal pos = ion.getMonoWeight(Residue::ZIon, charge) / (DoubleReal)charge;
        ions[pos] = ion;
        names[pos] = "z" + String(i) + String(charge, '+');
      }
      intensity = (DoubleReal)param_.getValue("z_intensity");
      break;
    }

    default:
      cerr << "Cannot create peaks of that ion type" << endl;
    }

    // get the params
    bool add_losses(param_.getValue("add_losses").toBool());
    bool add_metainfo(param_.getValue("add_metainfo").toBool());
    bool add_isotopes(param_.getValue("add_isotopes").toBool());
    Int max_isotope((Int)param_.getValue("max_isotope"));
    DoubleReal rel_loss_intensity((DoubleReal)param_.getValue("relative_loss_intensity"));

    for (Map<DoubleReal, AASequence>::ConstIterator cit = ions.begin(); cit != ions.end(); ++cit)
    {
      ion = cit->second;
      DoubleReal pos = cit->first;
      String ion_name = names[pos];
      if (add_isotopes)
      {
        IsotopeDistribution dist = ion.getFormula(res_type, charge).getIsotopeDistribution(max_isotope);
        UInt j(0);
        for (IsotopeDistribution::ConstIterator it = dist.begin(); it != dist.end(); ++it, ++j)
        {
          p_.setMZ((DoubleReal)(pos + (DoubleReal)j * Constants::NEUTRON_MASS_U) / (DoubleReal)charge);
          p_.setIntensity(intensity * it->second);
          if (add_metainfo && j == 0)
          {
            p_.setMetaValue("IonName", ion_name);
          }
          spectrum.push_back(p_);
        }
      }
      else
      {
        p_.setMZ(pos);
        p_.setIntensity(intensity);
        if (add_metainfo)
        {
          p_.setMetaValue("IonName", ion_name);
        }
        spectrum.push_back(p_);
      }

      if (add_losses)
      {
        set<String> losses;
        for (AASequence::ConstIterator it = cit->second.begin(); it != cit->second.end(); ++it)
        {
          if (it->hasNeutralLoss())
          {
            vector<EmpiricalFormula> loss_formulas = it->getLossFormulas();
            for (Size i = 0; i != loss_formulas.size(); ++i)
            {
              losses.insert(loss_formulas[i].toString());
            }
          }
        }


        if (!add_isotopes)
        {
          p_.setIntensity(intensity * rel_loss_intensity);
        }

        for (set<String>::const_iterator it = losses.begin(); it != losses.end(); ++it)
        {
          EmpiricalFormula loss_ion = ion.getFormula(res_type, charge) - EmpiricalFormula(*it);
          // thanks to Chris and Sandro
          // check for negative element frequencies (might happen if losses are not allowed for specific ions)
          bool negative_elements(false);
          for (EmpiricalFormula::ConstIterator eit = loss_ion.begin(); eit != loss_ion.end(); ++eit)
          {
            if (eit->second < 0)
            {
              negative_elements = true;
              break;
            }
          }
          if (negative_elements)
          {
            continue;
          }
          DoubleReal loss_pos = loss_ion.getMonoWeight() / (DoubleReal)charge;
          String loss_name = *it;

          if (add_isotopes)
          {
            IsotopeDistribution dist = loss_ion.getIsotopeDistribution(max_isotope);
            UInt j(0);
            for (IsotopeDistribution::ConstIterator iso = dist.begin(); iso != dist.end(); ++iso)
            {
              p_.setMZ((DoubleReal)(loss_pos + j) / (DoubleReal)charge);
              p_.setIntensity(intensity * rel_loss_intensity * iso->second);
              if (add_metainfo && j == 0)
              {
                p_.setMetaValue("IonName", ion_name + "-" + loss_name);
              }
              spectrum.push_back(p_);
            }
          }
          else
          {
            p_.setMZ(loss_pos);
            if (add_metainfo)
            {
              p_.setMetaValue("IonName", ion_name + "-" + loss_name);
            }
            spectrum.push_back(p_);
          }
        }
      }
    }

    if (add_metainfo)
    {
      p_.setMetaValue("IonName", String(""));
    }

    spectrum.sortByPosition();

    return;
  }