/////////////////////////////////////////////////////////////////////////////
// merge the target to the search feature
  void MS1FeatureMerger::mergeFeatures(SHFeature * target, SHFeature * toMerge)
  {

    double TOT_AREA = target->get_peak_area() + toMerge->get_peak_area();
    // merge the m/z:
    target->set_MZ(
      (target->get_peak_area() * target->get_MZ() + toMerge->get_peak_area() * toMerge->get_MZ()) / TOT_AREA);
    // merge S/N:
    target->setSignalToNoise(
      (target->getSignalToNoise() * target->get_peak_area() + toMerge->getSignalToNoise() * toMerge->get_peak_area())
      / TOT_AREA);
    // merge score:
    target->set_peak_score(
      (target->get_peak_score() * target->get_peak_area() + toMerge->get_peak_score() * toMerge->get_peak_area())
      / TOT_AREA);

    // merge first the elution profiles:
    FeatureLCProfile * targetLC = target->getLCelutionProfile();
    FeatureLCProfile * mergeLC = toMerge->getLCelutionProfile();

    // add points of the toMerge to the target:
    map<int, MS1Signal>::iterator LC = mergeLC->getLCelutionSignalsStart();
    while (LC != mergeLC->getLCelutionSignalsEnd())
    {
      targetLC->addMS1elutionSignal(&(LC->second));
      ++LC;
    }

    // possible extra info:
    if (target->getFeatureExtraInformation().empty())
    {
      target->setFeatureExtraInformation(toMerge->getFeatureExtraInformation());
    }

    // compute new parameters
    computeNewMS1FeatureParameters(target);

    // copy MS/MS information:
    if (toMerge->get_MS2_info(-3.0))
    {
      target->add_MS2_info(toMerge->get_MS2_SCAN_MAP());
    }

  }
//////////////////////////////////////////////////////////////////
// Compute a varietiy of parameters for the LC elution peak
  void MS1FeatureMerger::computeNewMS1FeatureParameters(SHFeature * in)
  {

    FeatureLCProfile * lcProfile = in->getLCelutionProfile();

    // define the apex treshold:
    double maxIntens = -1;
    map<int, MS1Signal>::iterator LC = lcProfile->getLCelutionSignalsStart();
    while (LC != lcProfile->getLCelutionSignalsEnd())
    {
      if (maxIntens < (*LC).second.intensity)
      {
        maxIntens = (*LC).second.intensity;
      }
      ++LC;
    }

    // get the MS peak above noise to copmute:
    double THRESHOLD = maxIntens / in->getSignalToNoise();
    vector<MS1Signal *> computeMap;
    LC = lcProfile->getLCelutionSignalsStart();

    in->set_scan_start((*LC).second.scan);
    in->set_retention_time_START((*LC).second.TR);

    while (LC != lcProfile->getLCelutionSignalsEnd())
    {
      if ((*LC).second.intensity >= THRESHOLD)
      {
        computeMap.push_back(&(LC->second));
      }
      ++LC;
    }
    --LC;
    in->set_scan_end((*LC).second.scan);
    in->set_retention_time_END((*LC).second.TR);

    if (!computeMap.empty())
    {

      vector<MS1Signal *>::iterator P = computeMap.begin();

      double TOT_AREA = 0;
      double start_TR = 0;
      double start_int = 0;
      double apexScan = 0;
      double apexTr = 0;
      double end_TR = 0;
      double end_int = 0;

      start_TR = (*P)->TR;
      start_int = (*P)->intensity;
      ++P;
      // go through all peaks in the LC elution profile:
      while (P != computeMap.end())
      {

        if ((*P)->intensity >= THRESHOLD)
        {

          end_TR = (*P)->TR;
          end_int = (*P)->intensity;

          // compute an area between local start / end ms peak:
          double area = computeDeltaArea(start_TR, start_int - THRESHOLD, end_TR, end_int - THRESHOLD);
          TOT_AREA += area;
          apexScan += (double) ((*P)->scan) * area;
          apexTr += start_TR * area;

          // next scan:
          start_TR = end_TR;
          start_int = end_int;
        }

        ++P;
      }

      // if contained only one peak!
      if (computeMap.size() == 1)
      {
        in->set_peak_area((float) start_int);
        in->set_retention_time(in->get_retention_time_START());
        in->set_scan_number(in->get_scan_start());
      }
      else
      {
        in->set_peak_area((float) TOT_AREA);
        apexScan /= TOT_AREA;
        in->set_scan_number((int) apexScan);
        apexTr /= TOT_AREA;
        in->set_retention_time(apexTr);
      }

      // set the apex ms peak:
      LC = lcProfile->getLCelutionSignalMap()->lower_bound(in->get_scan_number());
      in->set_apex_peak_intensity((*LC).second.intensity);
    }
    else
    {
      // no good peak above threshold, so reset all the features parameters to remove the feature
      in->set_peak_area(0);
      in->set_scan_number(0);
      in->set_retention_time(0);

    }

  }
//////////////////////////////////////////////////
// find to this feature the features which should be merged
  vector<SHFeature *>::iterator MS1FeatureMerger::findFeaturesToMerge(SHFeature * search,
                                                                      vector<SHFeature *>::iterator mapCurrent, vector<SHFeature *> * Fmap)
  {

    bool log10Intens = true;

    // get the elution profile:
    FeatureLCProfile * searchLC = search->getLCelutionProfile();
    while (mapCurrent != Fmap->end())
    {

      // check absolute retention time difference:
      bool toMerge = false;
      SHFeature * mergedTarget = (*mapCurrent);
      double deltaTr = fabs(search->get_retention_time() - mergedTarget->get_retention_time());
      if (deltaTr <= SuperHirnParameters::instance()->getInitialTrTolerance())
      {

        // compare the end / start of the elution peak:
        MS1Signal * start;
        MS1Signal * end;
        if (search->get_retention_time() < mergedTarget->get_retention_time())
        {
          start = &(searchLC->getLastLCelutionSignal()->second);
          end = &(mergedTarget->getLCelutionProfile()->getLCelutionSignalsStart()->second);
        }
        else
        {
          end = &(mergedTarget->getLCelutionProfile()->getLastLCelutionSignal()->second);
          start = &(searchLC->getLCelutionSignalsStart()->second);
        }
        double startIntens = start->intensity;
        if (log10Intens)
        {
          startIntens = log10(startIntens);
        }
        double endIntens = end->intensity;
        if (log10Intens)
        {
          endIntens = log10(endIntens);
        }

        double deltaIntens = fabs(startIntens - endIntens);
        deltaIntens /= startIntens;
        deltaTr = fabs(start->TR - end->TR);
        if ((deltaTr <= SuperHirnParameters::instance()->getMs1FeatureMergingTrTolerance())
           && (deltaIntens <= SuperHirnParameters::instance()->getPercentageIntensityElutionBorderVariation()))
        {
          toMerge = true;
        }

      }

      if (toMerge)
      {
        // search->show_info();(*mapCurrent)->show_info();
        mergeFeatures(search, mergedTarget);
        idsToRemove.push_back(mergedTarget->get_feature_ID());
        mapCurrent = Fmap->erase(mapCurrent);

        if (search->get_peak_area() == 0)
        {
          idsToRemove.push_back(search->get_feature_ID());
          mapCurrent++;
          break;
        }
      }
      else
      {
        mapCurrent++;
      }
    }

    return mapCurrent;
  }