//
// File: test_trace_filters.cpp
// Created by: Olivier Langella
// Created on: 28/04/2019
//
/*******************************************************************************
 * Copyright (c) 2019 Olivier Langella <Olivier.Langella@u-psud.fr>.
 *
 * This file is part of the PAPPSOms++ library.
 *
 *     PAPPSOms++ 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 3 of the License, or
 *     (at your option) any later version.
 *
 *     PAPPSOms++ 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 PAPPSOms++.  If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/


// make test ARGS="-V -I 1,1"

// ./tests/catch2-only-tests [filters] -s
// ./tests/catch2-only-tests [filterpeakdelta] -s

// #define CATCH_CONFIG_MAIN


#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>

#include <QDebug>
#include <QString>
#include <iostream>
#include <QFile>
#include <pappsomspp/core/precision.h>
#include <pappsomspp/core/msrun/output/mgfoutput.h>
#include <pappsomspp/core/processing/compartraces/cosinesimilarity.h>
#include <pappsomspp/core/processing/filters/filterpeakdelta.h>
#include <pappsomspp/core/processing/filters/filterresample.h>
#include <pappsomspp/core/processing/filters/filterpass.h>
#include <pappsomspp/core/processing/filters/filtermorpho.h>
#include <pappsomspp/core/processing/filters/filtertandemremovec13.h>
#include <pappsomspp/core/processing/filters/filterremovec13.h>
#include <pappsomspp/core/processing/filters/filtercomplementionenhancer.h>
#include <pappsomspp/core/processing/filters/filterchargedeconvolution.h>
#include <pappsomspp/core/psm/xtandem/xtandemhyperscore.h>
#include <pappsomspp/core/processing/filters/filtersuitestring.h>
#include <pappsomspp/core/processing/filters/filterchargedeconvolution.h>
#include <pappsomspp/core/amino_acid/aastringcodemassmatching.h>
#include <pappsomspp/core/protein/proteinintegercode.h>
#include "common.h"
#include "tests/tests-config.h"


using namespace pappso;
using namespace std;

TEST_CASE("test cosine similarity.", "[cosinesim]")
{
  // Set the debugging message formatting pattern.
  qSetMessagePattern(QString("%{file}@%{line}, %{function}(): %{message}"));

  // QCoreApplication a(argc, argv);
  SECTION("..:: test trace filters ::..", "[cosinesim]")
  {
    //   OboPsiMod test;
    MassSpectrum spectrum_simple =
      readMgf(QString(CMAKE_SOURCE_DIR)
                .append("/tests/data/peaklist_15046_simple_xt.mgf"));


    CosineSimilarity cosine(pappso::PrecisionFactory::getDaltonInstance(0.02));

    REQUIRE(cosine.similarity(spectrum_simple, spectrum_simple) ==
            Catch::Approx(1.0).epsilon(0.004));

    MassSpectrum orig = spectrum_simple;
    pappso::FilterGreatestY(10).filter(spectrum_simple);

    REQUIRE(cosine.similarity(orig, spectrum_simple) ==
            Catch::Approx(0.963327).epsilon(0.004));

    // AIADGSLLDLLR
  }
}

TEST_CASE("trace filter peak delta.", "[filterpeakdelta]")
{
  // Set the debugging message formatting pattern.
  qSetMessagePattern(QString("%{file}@%{line}, %{function}(): %{message}"));

  // QCoreApplication a(argc, argv);
  SECTION("..:: test trace filters ::..", "[filterpeakdelta]")
  {
    //   OboPsiMod test;
    Trace result;
    //      185.127999999999986 42.000000000000000
    //  186.122999999999990 18.000000000000000

    result.push_back({186.12299999 - 185.1279999, 42 + 18 - (42 - 18)});
    MassSpectrum spectrum_simple =
      readMgf(QString(CMAKE_SOURCE_DIR)
                .append("/tests/data/peaklist_15046_simple_xt.mgf"));
    //.applyCutOff(150).takeNmostIntense(100).applyDynamicRange(100);
    pappso::FilterGreatestY(10).filter(spectrum_simple);
    WARN(spectrum_simple.toString().toStdString());


    pappso::FilterPeakDelta filter_peak_delta;
    filter_peak_delta.filter(spectrum_simple);

    REQUIRE(spectrum_simple.size() == 45);
    REQUIRE(spectrum_simple.at(0).x ==
            Catch::Approx(result.at(0).x).epsilon(0.001));
    REQUIRE(spectrum_simple.at(0).y == result.at(0).y);

    // AIADGSLLDLLR
  }
  // QCoreApplication a(argc, argv);
  SECTION("..:: test trace filters ::..", "[filterpeakdelta15046]")
  {
    MassSpectrum spectrum_simple = readMgf(
      QString(CMAKE_SOURCE_DIR).append("/tests/data/peaklist_15046.mgf"));
    pappso::FilterResampleKeepGreater(160).filter(spectrum_simple);
    pappso::FilterChargeDeconvolution(
      pappso::PrecisionFactory::getDaltonInstance(0.02))
      .filter(spectrum_simple);
    //.applyCutOff(150).takeNmostIntense(100).applyDynamicRange(100);
    pappso::FilterGreatestY(100).filter(spectrum_simple);
    WARN(spectrum_simple.toString().toStdString());


    pappso::FilterPeakDelta filter_peak_delta;
    filter_peak_delta.filter(spectrum_simple);

    REQUIRE(spectrum_simple.size() == 4950);
    pappso::FilterGreatestY(60).filter(spectrum_simple);
    REQUIRE(spectrum_simple.size() == 60);

    AaCode aa_code;
    aa_code.addAaModification('C', AaModification::getInstance("MOD:00397"));
    pappso::AaStringCodeMassMatching aaMatching(
      aa_code, 4, pappso::PrecisionFactory::getDaltonInstance(0.03));

    std::vector<double> mass_list = spectrum_simple.xValues();
    std::vector<uint32_t> code_list =
      aaMatching.getAaCodeFromMassList(mass_list);
    REQUIRE(code_list.size() == 334);

    code_list = aaMatching.filterCodeList(code_list);
    REQUIRE(code_list.size() == 51);
    // AIADGSLLDLLR
    AaStringCodec aa_codec(aa_code);
    REQUIRE(aa_codec.decode(code_list[0]).toStdString() == "G");
    REQUIRE(aa_codec.decode(code_list[1]).toStdString() == "A");
    REQUIRE(aa_codec.decode(code_list[2]).toStdString() == "S");
    REQUIRE(aa_codec.decode(code_list[3]).toStdString() == "V"); // false
    REQUIRE(aa_codec.decode(code_list[4]).toStdString() == "I");
    REQUIRE(aa_codec.decode(code_list[5]).toStdString() == "D");
    REQUIRE(aa_codec.decode(code_list[6]).toStdString() == "W"); // false
    REQUIRE(aa_codec.decode(code_list[7]).toStdString() == "SG");
    REQUIRE(aa_codec.decode(code_list[8]).toStdString() == "DG");
    REQUIRE(aa_codec.decode(code_list[9]).toStdString() == "WG"); // false
    REQUIRE(aa_codec.decode(code_list[10]).toStdString() == "DA");
    REQUIRE(aa_codec.decode(code_list[11]).toStdString() == "WA"); // false
    REQUIRE(aa_codec.decode(code_list[12]).toStdString() == "IS");
    REQUIRE(aa_codec.decode(code_list[13]).toStdString() == "DV");
    REQUIRE(aa_codec.decode(code_list[14]).toStdString() == "II");
    REQUIRE(aa_codec.decode(code_list[15]).toStdString() == "DI");
    REQUIRE(aa_codec.decode(code_list[16]).toStdString() == "WW");
    REQUIRE(aa_codec.decode(code_list[17]).toStdString() == "DAG");
    REQUIRE(aa_codec.decode(code_list[18]).toStdString() == "ISG");
    REQUIRE(aa_codec.decode(code_list[19]).toStdString() == "DSG");
    REQUIRE(aa_codec.decode(code_list[20]).toStdString() == "WSG"); // false
    REQUIRE(aa_codec.decode(code_list[21]).toStdString() == "VSA"); // false
    REQUIRE(aa_codec.decode(code_list[22]).toStdString() == "ISA"); // false
    REQUIRE(aa_codec.decode(code_list[23]).toStdString() == "WIA"); // false
    REQUIRE(aa_codec.decode(code_list[24]).toStdString() == "WDA"); // false
    REQUIRE(aa_codec.decode(code_list[25]).toStdString() == "WWA"); // false
    REQUIRE(aa_codec.decode(code_list[26]).toStdString() == "WVS"); // false
    REQUIRE(aa_codec.decode(code_list[27]).toStdString() == "IIS");
    REQUIRE(aa_codec.decode(code_list[28]).toStdString() == "WIS"); // false
    REQUIRE(aa_codec.decode(code_list[29]).toStdString() == "DII");
    REQUIRE(aa_codec.decode(code_list[30]).toStdString() == "WWI");  // false
    REQUIRE(aa_codec.decode(code_list[31]).toStdString() == "GGGG"); // false
    REQUIRE(aa_codec.decode(code_list[32]).toStdString() == "DSAG");
    REQUIRE(aa_codec.decode(code_list[33]).toStdString() == "IISG");
    REQUIRE(aa_codec.decode(code_list[34]).toStdString() == "DISG");
    REQUIRE(aa_codec.decode(code_list[35]).toStdString() == "WISG"); // false
    REQUIRE(aa_codec.decode(code_list[36]).toStdString() == "WDAA"); // false


    pappso::FilterGreatestY(50).filter(spectrum_simple);
    REQUIRE(spectrum_simple.size() == 50);

    WARN(spectrum_simple.toString().toStdString());
    mass_list = spectrum_simple.xValues();
    code_list = aaMatching.getAaCodeFromMassList(mass_list);
    code_list = aaMatching.filterCodeList(code_list);
    REQUIRE(code_list.size() == 46);

    // AIADGSLLDLLR

    REQUIRE(aa_codec.decode(code_list[0]).toStdString() == "G");
    REQUIRE(aa_codec.decode(code_list[1]).toStdString() == "A");
    REQUIRE(aa_codec.decode(code_list[2]).toStdString() == "S");
    REQUIRE(aa_codec.decode(code_list[3]).toStdString() == "V"); // false
    REQUIRE(aa_codec.decode(code_list[4]).toStdString() == "I");
    REQUIRE(aa_codec.decode(code_list[5]).toStdString() == "D");
    REQUIRE(aa_codec.decode(code_list[6]).toStdString() == "W");
    REQUIRE(aa_codec.decode(code_list[7]).toStdString() == "SG");
    REQUIRE(aa_codec.decode(code_list[8]).toStdString() == "DA");
    REQUIRE(aa_codec.decode(code_list[9]).toStdString() == "WA");  // false
    REQUIRE(aa_codec.decode(code_list[10]).toStdString() == "DV"); // false
    REQUIRE(aa_codec.decode(code_list[11]).toStdString() == "II");
    REQUIRE(aa_codec.decode(code_list[12]).toStdString() == "DI");
    REQUIRE(aa_codec.decode(code_list[13]).toStdString() == "WW"); // false
    REQUIRE(aa_codec.decode(code_list[14]).toStdString() == "ISG");
    REQUIRE(aa_codec.decode(code_list[15]).toStdString() == "DSG");
    REQUIRE(aa_codec.decode(code_list[16]).toStdString() == "WSG"); // false
    REQUIRE(aa_codec.decode(code_list[17]).toStdString() == "VSA"); // false
    REQUIRE(aa_codec.decode(code_list[18]).toStdString() == "ISA"); // false
    REQUIRE(aa_codec.decode(code_list[19]).toStdString() == "WIA"); // false
    REQUIRE(aa_codec.decode(code_list[20]).toStdString() == "WDA"); // false
    REQUIRE(aa_codec.decode(code_list[21]).toStdString() == "WWA"); // false
    REQUIRE(aa_codec.decode(code_list[22]).toStdString() == "WVS"); // false
    REQUIRE(aa_codec.decode(code_list[23]).toStdString() == "WIS"); // false
    REQUIRE(aa_codec.decode(code_list[24]).toStdString() == "DII");
    REQUIRE(aa_codec.decode(code_list[25]).toStdString() == "WWI");  // false
    REQUIRE(aa_codec.decode(code_list[26]).toStdString() == "GGGG"); // false
    REQUIRE(aa_codec.decode(code_list[27]).toStdString() == "DSAG");
    REQUIRE(aa_codec.decode(code_list[28]).toStdString() == "IISG");
    REQUIRE(aa_codec.decode(code_list[29]).toStdString() == "DISG");


    pappso::FilterGreatestY(30).filter(spectrum_simple);
    REQUIRE(spectrum_simple.size() == 30);

    mass_list = spectrum_simple.xValues();
    code_list = aaMatching.getAaCodeFromMassList(mass_list);
    code_list = aaMatching.filterCodeList(code_list);
    // AIADGSLLDLLR
    REQUIRE(code_list.size() == 17);
    REQUIRE(aa_codec.decode(code_list[0]).toStdString() == "A");
    REQUIRE(aa_codec.decode(code_list[1]).toStdString() == "I");
    REQUIRE(aa_codec.decode(code_list[2]).toStdString() == "D");
    REQUIRE(aa_codec.decode(code_list[3]).toStdString() == "W"); // false
    REQUIRE(aa_codec.decode(code_list[4]).toStdString() == "DA");
    REQUIRE(aa_codec.decode(code_list[5]).toStdString() == "WA"); // false
    REQUIRE(aa_codec.decode(code_list[6]).toStdString() == "II");
    REQUIRE(aa_codec.decode(code_list[7]).toStdString() == "DI");
    REQUIRE(aa_codec.decode(code_list[8]).toStdString() == "WW");  // false
    REQUIRE(aa_codec.decode(code_list[9]).toStdString() == "WDA"); // false
    // REQUIRE(aa_codec.decode(code_list[10]).toStdString() == "DIII");
  }


  // QCoreApplication a(argc, argv);
  SECTION("..:: test trace filters ::..", "[filterpeakdelta15046a]")
  {
    MassSpectrum spectrum_simple = readMgf(
      QString(CMAKE_SOURCE_DIR).append("/tests/data/peaklist_15046.mgf"));
    pappso::FilterResampleKeepGreater(160).filter(spectrum_simple);
    pappso::FilterChargeDeconvolution(
      pappso::PrecisionFactory::getDaltonInstance(0.02))
      .filter(spectrum_simple);
    //.applyCutOff(150).takeNmostIntense(100).applyDynamicRange(100);
    pappso::FilterGreatestY(100).filter(spectrum_simple);
    WARN(spectrum_simple.toString().toStdString());


    pappso::FilterPeakDelta filter_peak_delta;
    filter_peak_delta.filter(spectrum_simple);

    REQUIRE(spectrum_simple.size() == 4950);
    pappso::FilterGreatestY(60).filter(spectrum_simple);
    REQUIRE(spectrum_simple.size() == 60);

    AaCode aa_code;
    aa_code.addAaModification('C', AaModification::getInstance("MOD:00397"));
    pappso::AaStringCodeMassMatching aaMatching(
      aa_code, 5, pappso::PrecisionFactory::getDaltonInstance(0.01));

    std::vector<double> mass_list = spectrum_simple.xValues();
    std::vector<uint32_t> code_list =
      aaMatching.getAaCodeFromMassList(mass_list);

    code_list = aaMatching.filterCodeList(code_list);

    REQUIRE(code_list.size() == 24);
    // AIADGSLLDLLR
    AaStringCodec aa_codec(aa_code);
    REQUIRE(aa_codec.decode(code_list[0]).toStdString() == "G");
    REQUIRE(aa_codec.decode(code_list[1]).toStdString() == "A");
    REQUIRE(aa_codec.decode(code_list[2]).toStdString() == "S");
    REQUIRE(aa_codec.decode(code_list[3]).toStdString() == "I");
    REQUIRE(aa_codec.decode(code_list[4]).toStdString() == "D");
    REQUIRE_FALSE(aa_codec.decode(code_list[5]).toStdString() == "W"); // false
    REQUIRE(aa_codec.decode(code_list[5]).toStdString() == "SG");
    REQUIRE(aa_codec.decode(code_list[6]).toStdString() == "DG");
    REQUIRE(aa_codec.decode(code_list[15]).toStdString() == "DII");
    REQUIRE(aa_codec.decode(code_list[16]).toStdString() == "DSAG");
    REQUIRE(aa_codec.decode(code_list[17]).toStdString() == "IISG");
    REQUIRE(aa_codec.decode(code_list[18]).toStdString() == "DISG");
    REQUIRE(aa_codec.decode(code_list[19]).toStdString() == "DIIS");
    REQUIRE(aa_codec.decode(code_list[20]).toStdString() == "DIII");
    REQUIRE(aa_codec.decode(code_list[21]).toStdString() == "DISAG");
    REQUIRE(aa_codec.decode(code_list[22]).toStdString() == "DIISG");
    REQUIRE(aa_codec.decode(code_list[23]).toStdString() == "DIIII");

    //


    ProteinSp protsp =
      Protein(
        "GRMZM2G083841_P01",
        "MASTKAPGPGEKHHSIDAQLRQLVPGKVSEDDKLIEYDALLVDRFLNILQDLHGPSLREFVQECYEVSAD"
        "YEGKGDTTKLGELGAKLTGLAPADAILVASSILHMLNLANLAEEVQIAHRRRNSKLKKGGFADEGSATTE"
        "SDIEETLKRLVSEVGKSPEEVFEALKNQTVDLVFTAHPTQSARRSLLQKNARIRNCLTQLNAKDITDDDK"
        "QELDEALQREIQAAFRTDEIRRAQPTPQDEMRYGMSYIHETVWKGVPKFLRRVDTALKNIGINERLPYNV"
        "SLIRFSSWMGGDRDGNPRVTPEVTRDVCLLARMMAANLYIDQIEELMFELSMWRCNDELRVRAEELHSSS"
        "GSKVTKYYIEFWKQIPPNEPYRVILGHVRDKLYNTRERARHLLASGVSEISAESSFTSIEEFLEPLELCY"
        "KSLCDCGDKAIADGSLLDLLRQVFTFGLSLVKLDIRQESERHTDVIDAITTHLGIGSYREWPEDKRQEWL"
        "LSELRGKRPLLPPDLPQTDEIADVIGAFHVLAELPPDSFGPYIISMATAPSDVLAVELLQRECGVRQPLP"
        "VVPLFERLADLQSAPASVERLFSVDWYMDRIKGKQQVMVGYSDSGKDAGRLSAAWQLYRAQEEMAQVAKR"
        "YGVKLTLFHGRGGTVGRGGGPTHLAILSQPPDTINGSIRVTVQGEVIEFCFGEEHLCFQTLQRFTAATLE"
        "HGMHPPVSPKPEWRKLMDEMAVVATEEYRSVVVKEARFVEYFRSATPETEYGRMNIGSRPAKRRPGGGIT"
        "TLRAIPWIFSWTQTRFHLPVWLGVGAAFKFAIDKDVRNFQVLKEMYNEWPFFRVTLDLLEMVFAKGDPGI"
        "AGLYDELLVAEELKPFGKQLRDKYVETQQLLLQIAGHKDILEGDPFLKQGLVLRNPYITTLNVFQAYTLK"
        "RIRDPNFKVTPQPPLSKEFADENKPAGLVKLNPASEYPPGLEDTLILTMKGIAAGMQNTG")
        .makeProteinSp();

    AaStringCodec codec(aa_code);


    ProteinIntegerCode protein_code(protsp, codec, 5);

    auto match = protein_code.match(code_list);

    for(auto pairi : match)
      {
        qDebug() << "pos=" << pairi.first << " size=" << pairi.second;
      }
  }

  // QCoreApplication a(argc, argv);
  SECTION("..:: test trace filters ::..", "[filterpeakdelta15968]")
  {
    // EITLGFVDLLR
    MassSpectrum spectrum_simple =
      readMgf(QString(CMAKE_SOURCE_DIR).append("/tests/data/scan_15968.mgf"));
    pappso::FilterResampleKeepGreater(160).filter(spectrum_simple);
    pappso::FilterChargeDeconvolution(
      pappso::PrecisionFactory::getDaltonInstance(0.02))
      .filter(spectrum_simple);
    //.applyCutOff(150).takeNmostIntense(100).applyDynamicRange(100);
    pappso::FilterGreatestY(100).filter(spectrum_simple);
    WARN(spectrum_simple.toString().toStdString());


    pappso::FilterPeakDelta filter_peak_delta;
    filter_peak_delta.filter(spectrum_simple);

    REQUIRE(spectrum_simple.size() == 4950);
    pappso::FilterGreatestY(80).filter(spectrum_simple);
    REQUIRE(spectrum_simple.size() == 80);

    AaCode aa_code;
    aa_code.addAaModification('C', AaModification::getInstance("MOD:00397"));
    pappso::AaStringCodeMassMatching aaMatching(
      aa_code, 4, pappso::PrecisionFactory::getDaltonInstance(0.03));

    std::vector<double> mass_list = spectrum_simple.xValues();
    std::vector<uint32_t> code_list =
      aaMatching.getAaCodeFromMassList(mass_list);
    REQUIRE(code_list.size() == 128);

    code_list = aaMatching.filterCodeList(code_list);
    REQUIRE(code_list.size() == 1);
    // EITLGFVDLLR
    AaStringCodec aa_codec(aa_code);
    REQUIRE(aa_codec.decode(code_list[0]).toStdString() == "H");


    pappso::FilterGreatestY(50).filter(spectrum_simple);
    REQUIRE(spectrum_simple.size() == 50);
    mass_list = spectrum_simple.xValues();
    code_list = aaMatching.getAaCodeFromMassList(mass_list);
    code_list = aaMatching.filterCodeList(code_list);
    REQUIRE(code_list.size() == 1);

    // EITLGFVDLLR

    REQUIRE(aa_codec.decode(code_list[0]).toStdString() == "H");
  }
}

TEST_CASE("trace filters test suite.", "[filters]")
{
  // Set the debugging message formatting pattern.
  qSetMessagePattern(QString("%{file}@%{line}, %{function}(): %{message}"));

  // QCoreApplication a(argc, argv);
  SECTION("..:: test trace filters ::..", "[filters]")
  {
    //   OboPsiMod test;

    MassSpectrum spectrum_simple =
      readMgf(QString(CMAKE_SOURCE_DIR)
                .append("/tests/data/peaklist_15046_simple_xt.mgf"));
    //.applyCutOff(150).takeNmostIntense(100).applyDynamicRange(100);
    std::cout << std::endl << "..:: FilterGreatestY ::.." << std::endl;

    qDebug();
    MassSpectrum most_intense(spectrum_simple);
    most_intense.filter(FilterGreatestY(10));

    REQUIRE(most_intense.size() == 10);

    qDebug();
    most_intense = spectrum_simple;
    most_intense.massSpectrumFilter(
      pappso::MassSpectrumFilterGreatestItensities(10));

    REQUIRE(most_intense.size() == 10);
    qDebug();
    most_intense =
      MassSpectrum(spectrum_simple)
        .massSpectrumFilter(pappso::MassSpectrumFilterGreatestItensities(1000));

    REQUIRE(most_intense.size() == 100);
    qDebug();
    most_intense = MassSpectrum(spectrum_simple).filter(FilterGreatestY(100));

    REQUIRE(most_intense.size() == 100);
    qDebug();
    // 3.018830
    std::cout << std::endl << "..:: resample filters ::.." << std::endl;
    Trace deca_suite({DataPoint(0, 1),
                      DataPoint(1, 2),
                      DataPoint(2, 3),
                      DataPoint(3, 4),
                      DataPoint(4, 3),
                      DataPoint(5, 2),
                      DataPoint(6, 1),
                      DataPoint(7, 1),
                      DataPoint(8, 2),
                      DataPoint(9, 3),
                      DataPoint(10, 4),
                      DataPoint(11, 5),
                      DataPoint(12, 6)});
    Trace deca_keep({DataPoint(9, 3), DataPoint(10, 4), DataPoint(11, 5)});

    Trace deca_remove({DataPoint(0, 1),
                       DataPoint(1, 2),
                       DataPoint(2, 3),
                       DataPoint(3, 4),
                       DataPoint(4, 3),
                       DataPoint(5, 2),
                       DataPoint(6, 1),
                       DataPoint(7, 1),
                       DataPoint(8, 2),
                       DataPoint(12, 6)});
    Trace deca_remove2({DataPoint(0, 1),
                        DataPoint(1, 2),
                        DataPoint(2, 3),
                        DataPoint(3, 4),
                        DataPoint(4, 3),
                        DataPoint(5, 2),
                        DataPoint(6, 1),
                        DataPoint(7, 1),
                        DataPoint(8, 2)});
    Trace deca_smaller({DataPoint(0, 1), DataPoint(1, 2), DataPoint(2, 3)});

    Trace deca_greater({DataPoint(10, 4), DataPoint(11, 5), DataPoint(12, 6)});
    Trace deca(deca_suite);
    deca.filter(FilterResampleRemoveXRange(9, 11.5));
    REQUIRE(deca == deca_remove);
    deca = deca_suite;
    deca.filter(FilterResampleRemoveXRange(9, 12));
    REQUIRE(deca == deca_remove2);

    deca = deca_suite;
    deca.filter(FilterResampleKeepXRange(9, 11));
    REQUIRE(deca == deca_keep);


    deca = deca_suite;
    deca.filter(FilterResampleKeepSmaller(3));
    REQUIRE(deca == deca_smaller);

    deca = deca_suite;
    deca.filter(FilterResampleKeepGreater(9));
    REQUIRE(deca == deca_greater);
    std::cout << std::endl << "..:: morpho filters ::.." << std::endl;

    deca = deca_suite;
    deca.filter(FilterMorphoMedian(1));
    Trace deca_median({DataPoint(0, 2),
                       DataPoint(1, 2),
                       DataPoint(2, 3),
                       DataPoint(3, 3),
                       DataPoint(4, 3),
                       DataPoint(5, 2),
                       DataPoint(6, 1),
                       DataPoint(7, 1),
                       DataPoint(8, 2),
                       DataPoint(9, 3),
                       DataPoint(10, 4),
                       DataPoint(11, 5),
                       DataPoint(12, 6)});
    REQUIRE(deca.size() == deca_median.size());
    std::cout << "deca.size() == deca_median.size() " << deca.size()
              << std::endl;
    REQUIRE(deca == deca_median);

    std::cout << std::endl << "..:: FilterTandemDeisotope ::.." << std::endl;
    qDebug();
    MassSpectrum remove_tandem_deisotope =
      MassSpectrum(spectrum_simple)
        .massSpectrumFilter(FilterTandemDeisotope(1.5, 200));

    REQUIRE(remove_tandem_deisotope.size() == 97);
    qDebug();


    QualifiedMassSpectrum spectrum_scan_15968 = readQualifiedMassSpectrumMgf(
      QString(CMAKE_SOURCE_DIR).append("/tests/data/scan_15968.mgf"));
    REQUIRE(spectrum_scan_15968.getPrecursorMz() ==
            Catch::Approx(638.3693473259));
    REQUIRE(spectrum_scan_15968.getPrecursorMass() ==
            Catch::Approx(1274.724141718));
    qDebug();
    QualifiedMassSpectrum spectrum_removed_tandem_deisotope =
      QualifiedMassSpectrum(spectrum_scan_15968).cloneMassSpectrumSPtr();
    qDebug();
    spectrum_removed_tandem_deisotope.getMassSpectrumSPtr()
      .get()
      ->massSpectrumFilter(FilterTandemDeisotope());
    qDebug();
    QFile file("scan_15968_tandem_deisotope.mgf");
    if(file.open(QIODevice::WriteOnly | QIODevice::Text))
      {
        MgfOutput mgf_output(&file);
        mgf_output.write(spectrum_removed_tandem_deisotope);
        mgf_output.close();
        file.close();
      }
    qDebug();

    Peptide pep15968("EITLGFVDLLR");
    qDebug();
    std::list<Enums::PeptideIon> ion_list;
    ion_list.push_back(Enums::PeptideIon::y);
    ion_list.push_back(Enums::PeptideIon::b);
    qDebug();

    std::cout << std::endl << "..:: FilterRemoveC13 ::.." << std::endl;
    qDebug();
    MassSpectrum remove_c13 =
      MassSpectrum(spectrum_simple)
        .filter(FilterRemoveC13(PrecisionFactory::getDaltonInstance(0.02)));

    REQUIRE(remove_c13.size() == 93);
    qDebug();

    QualifiedMassSpectrum spectrum_removed_c13 =
      QualifiedMassSpectrum(spectrum_scan_15968).cloneMassSpectrumSPtr();

    spectrum_removed_c13.getMassSpectrumSPtr().get()->filter(
      FilterRemoveC13(PrecisionFactory::getDaltonInstance(0.02)));

    QFile file2("scan_15968_remove_c13.mgf");
    if(file2.open(QIODevice::WriteOnly | QIODevice::Text))
      {
        MgfOutput mgf_output(&file2);
        mgf_output.write(spectrum_removed_c13);
        mgf_output.close();
        file2.close();
      }


    XtandemHyperscore hyperscore15968(
      *spectrum_scan_15968.getMassSpectrumSPtr().get(),
      pep15968.makePeptideSp(),
      2,
      PrecisionFactory::getDaltonInstance(0.02),
      ion_list,
      true);
    std::cout << "peptide " << pep15968.getSequence().toStdString()
              << " hyperscore15968=" << hyperscore15968.getHyperscore()
              << std::endl;
    std::cout << "peptide " << pep15968.getSequence().toStdString()
              << " mz1=" << pep15968.getMz(1) << " mz2=" << pep15968.getMz(2)
              << std::endl;

    REQUIRE(hyperscore15968.getHyperscore() ==
            Catch::Approx(50.9568806832).margin(0.1));

    XtandemHyperscore hyperscore15968_remove_c13(
      *spectrum_removed_c13.getMassSpectrumSPtr().get(),
      pep15968.makePeptideSp(),
      2,
      PrecisionFactory::getDaltonInstance(0.02),
      ion_list,
      true);
    std::cout << "peptide " << pep15968.getSequence().toStdString()
              << " hyperscore15968_remove_c13="
              << hyperscore15968_remove_c13.getHyperscore() << std::endl;
    std::cout << "peptide " << pep15968.getSequence().toStdString()
              << " mz1=" << pep15968.getMz(1) << " mz2=" << pep15968.getMz(2)
              << std::endl;


    std::cout << std::endl
              << "..:: FilterMassSpectrumComplementIonEnhancer ::.."
              << std::endl;
    qDebug();

    QualifiedMassSpectrum spectrum_complement_ion =
      QualifiedMassSpectrum(spectrum_scan_15968).cloneMassSpectrumSPtr();

    spectrum_complement_ion.getMassSpectrumSPtr().get()->filter(
      FilterComplementIonEnhancer(spectrum_complement_ion,
                                  PrecisionFactory::getDaltonInstance(0.02)));

    QFile file3("scan_15968_complement_ion_enhancer.mgf");
    if(file3.open(QIODevice::WriteOnly | QIODevice::Text))
      {
        MgfOutput mgf_output(&file3);
        mgf_output.write(spectrum_complement_ion);
        mgf_output.close();
        file3.close();
      }

    XtandemHyperscore hyperscore15968_complement_ion_enhancer(
      *spectrum_complement_ion.getMassSpectrumSPtr().get(),
      pep15968.makePeptideSp(),
      2,
      PrecisionFactory::getDaltonInstance(0.02),
      ion_list,
      true);
    std::cout << "peptide " << pep15968.getSequence().toStdString()
              << " hyperscore15968_complement_ion_enhancer="
              << hyperscore15968_complement_ion_enhancer.getHyperscore()
              << std::endl;
    std::cout << "peptide " << pep15968.getSequence().toStdString()
              << " mz1=" << pep15968.getMz(1) << " mz2=" << pep15968.getMz(2)
              << std::endl;


    std::cout << std::endl << "..:: FilterGreatestYperWindow ::.." << std::endl;
    FilterGreatestYperWindow filter_per_window(40, 2);
    qDebug();
    QualifiedMassSpectrum spectrum_greatest_y_per_window =
      QualifiedMassSpectrum(spectrum_scan_15968).cloneMassSpectrumSPtr();
    qDebug();
    filter_per_window.filter(
      *spectrum_greatest_y_per_window.getMassSpectrumSPtr().get());
    qDebug();
    QFile file4("scan_15968_greatest_y_per_window.mgf");
    if(file4.open(QIODevice::WriteOnly | QIODevice::Text))
      {
        MgfOutput mgf_output(&file4);
        mgf_output.write(spectrum_greatest_y_per_window);
        mgf_output.close();
        file4.close();
      }
    qDebug();


    std::cout << std::endl
              << "..:: FilterGreatestYperWindow  on scan_PXD001468.mgf ::.."
              << std::endl;
    QualifiedMassSpectrum spectrum_scan_PXD001468 =
      readQualifiedMassSpectrumMgf(
        QString(CMAKE_SOURCE_DIR).append("/tests/data/scan_PXD001468.mgf"));
    qDebug();
    QualifiedMassSpectrum spectrum_greatest_y_per_window_PXD001468 =
      QualifiedMassSpectrum(spectrum_scan_PXD001468).cloneMassSpectrumSPtr();
    qDebug();
    spectrum_greatest_y_per_window_PXD001468.getMassSpectrumSPtr()
      .get()
      ->filter(FilterGreatestYperWindow(40, 2));
    qDebug();
    QFile file5("scan_PXD001468_greatest_y_per_window.mgf");
    if(file5.open(QIODevice::WriteOnly | QIODevice::Text))
      {
        MgfOutput mgf_output(&file5);
        mgf_output.write(spectrum_greatest_y_per_window_PXD001468);
        mgf_output.close();
        file5.close();
      }
    qDebug();


    std::shared_ptr<pappso::FilterSuite> filter_suite =
      std::make_shared<pappso::FilterSuite>();
    filter_suite->push_back(std::make_shared<FilterGreatestYperWindow>(40, 2));
    filter_suite->push_back(std::make_shared<FilterRemoveC13>(
      PrecisionFactory::getDaltonInstance(0.02)));

    /*
      XtandemHyperscore hyperscore15968_greatest_y_per_window(
        *spectrum_greatest_y_per_window.getMassSpectrumSPtr().get(),
        pep15968.makePeptideSp(),
        2,
        PrecisionFactory::getDaltonInstance(0.02),
        ion_list,
        true);
      std::cout << "peptide " << pep15968.getSequence().toStdString()
           << " hyperscore15968_greatest_y_per_window="
           << hyperscore15968_greatest_y_per_window.getHyperscore() <<
      std::endl; std::cout << "peptide " << pep15968.getSequence().toStdString()
           << " mz1=" << pep15968.getMz(1) << " mz2=" << pep15968.getMz(2) <<
      endl;

    */

    std::cout << std::endl << "..:: FilterSuiteString ::.." << std::endl;

    std::shared_ptr<pappso::FilterSuiteString> filter_suite_str =
      std::make_shared<pappso::FilterSuiteString>(
        "chargeDeconvolution|0.02dalton");


    std::cout << std::endl
              << "filter_suite_str = "
              << filter_suite_str.get()->toString().toStdString() << std::endl;

    std::cout << std::endl
              << "..:: FilterChargeDeconvolution from FilterSuiteString ::.."
              << std::endl;

    std::shared_ptr<pappso::FilterChargeDeconvolution>
      filter_charge_deconvolution =
        std::make_shared<pappso::FilterChargeDeconvolution>(
          filter_suite_str.get()->toString());

    Trace loaded_trace({// First peak z = 2
                        DataPoint(10, 20),
                        DataPoint(10.5, 18),
                        DataPoint(11, 16),
                        // Not a real peak
                        DataPoint(15, 50),
                        DataPoint(16, 51),
                        // Second peak z = 1
                        DataPoint(24, 200),
                        DataPoint(25, 20),
                        // Third peak z = 2
                        DataPoint(31, 20),
                        DataPoint(31.5, 15)});
    Trace theorical_trace({// Not modified peaks
                           DataPoint(15, 50),
                           DataPoint(16, 51),
                           // First peak merged
                           DataPoint(20 - MHPLUS, 54),
                           // Second
                           DataPoint(24, 220),
                           // Third
                           DataPoint(62 - MHPLUS, 35)});
    std::cout << "Transform Trace data" << std::endl;
    filter_charge_deconvolution->filter(loaded_trace);


    if(loaded_trace != theorical_trace)
      {
        std::cerr << "transformed Trace are different from theorical trace :"
                  << std::endl;

        if(loaded_trace.xValues().size() == theorical_trace.xValues().size())
          {
            std::cerr << "transformed trace: \t";
            for(std::size_t i = 0; i < loaded_trace.xValues().size(); i++)
              {
                std::cerr << "(" << loaded_trace.xValues()[i] << ", "
                          << loaded_trace.yValues()[i] << ") ";
              }
            std::cerr << std::endl;
            std::cerr << "theorical trace : \t";
            for(std::size_t i = 0; i < theorical_trace.xValues().size(); i++)
              {
                std::cerr << "(" << theorical_trace.xValues()[i] << ", "
                          << theorical_trace.yValues()[i] << ") ";
              }
            std::cerr << std::endl;
          }
        else
          {
            std::cerr << "Number of peaks is different : "
                      << loaded_trace.xValues().size() << " vs "
                      << theorical_trace.xValues().size();
          }
      }

    REQUIRE(loaded_trace == theorical_trace);
  }
  SECTION("..:: test FilterMorphoAntiSpike ::..", "[filters]")
  {
    // MESSAGE("..:: FilterMorphoAntiSpike filters ::..");
    Trace anti_spike_trace({DataPoint(0, 0),
                            DataPoint(1, 1),
                            DataPoint(2, 3),
                            DataPoint(3, 0),
                            DataPoint(4, 0),
                            DataPoint(5, 0),
                            DataPoint(6, 0),
                            DataPoint(7, 0),
                            DataPoint(8, 0),
                            DataPoint(9, 3),
                            DataPoint(10, 0),
                            DataPoint(11, 0),
                            DataPoint(12, 0)});
    FilterMorphoAntiSpike filter_anti_spike(2);
    filter_anti_spike.filter(anti_spike_trace);

    REQUIRE(anti_spike_trace[9].y == 0);
    REQUIRE(anti_spike_trace[2].y == 3);
    // std::cout << std::endl << anti_spike_trace.toString().toStdString() <<
    // std::endl;

    INFO("filter_anti_spike_too_big");
    FilterMorphoAntiSpike filter_anti_spike_too_big(7);
    filter_anti_spike_too_big.filter(anti_spike_trace);
  }

  SECTION("..:: check empty trace ::..", "[filters]")
  {
    // MESSAGE("..:: FilterMorphoAntiSpike filters ::..");
    Trace empty_trace;
    FilterMorphoMean m_smooth(3);

    REQUIRE_NOTHROW(m_smooth.filter(empty_trace));


    FilterMorphoMinMax m_minMax(5);
    REQUIRE_NOTHROW(m_minMax.filter(empty_trace));

    FilterMorphoMaxMin m_maxMin(5);
    REQUIRE_NOTHROW(m_maxMin.filter(empty_trace));

    Trace xic;
    xic.push_back(DataPoint(1434.7806210000, 85.9931205504));
    xic.push_back(DataPoint(1586.4034760000, 162.9869610431));
    xic.push_back(DataPoint(1643.5517360000, 84.9932005440));
    xic.push_back(DataPoint(1645.8830080000, 98.9920806335));

    FilterMorphoMaxMin m_maxMin2(2);
    REQUIRE_NOTHROW(m_maxMin2.filter(xic));
  }

  SECTION("..:: check FilterQuantileBasedRemoveY ::..", "[filters]")
  {

    QualifiedMassSpectrum qspectrum = readQualifiedMassSpectrumMgf(
      QString(CMAKE_SOURCE_DIR).append("/tests/data/xic/PXD014777_xic.tsv"));
    REQUIRE(qspectrum.size() == 12028);
    Trace xic_PXD0014777 = *(qspectrum.getMassSpectrumSPtr().get());


    REQUIRE(Catch::Approx(62.43).epsilon(0.01) ==
            quantileYTrace(xic_PXD0014777.begin(), xic_PXD0014777.end(), 0.1));
    REQUIRE(Catch::Approx(250.75).epsilon(0.01) ==
            quantileYTrace(xic_PXD0014777.begin(), xic_PXD0014777.end(), 0.5));
    REQUIRE(Catch::Approx(136.96).epsilon(0.01) ==
            quantileYTrace(xic_PXD0014777.begin(), xic_PXD0014777.end(), 0.3));


    FilterQuantileBasedRemoveY quantile(0.1);
    xic_PXD0014777 = quantile.filter(xic_PXD0014777);


    QFile fnew("quantile_trace.tsv");
    fnew.open(QFile::WriteOnly | QFile::Text);
    QTextStream out(&fnew);
    out << xic_PXD0014777.toString();
  }
}
