
#include <catch2/catch_test_macros.hpp>


#include <QDebug>

#include <pappsomspp/core/utils.h>
#include <pappsomspp/core/trace/datapoint.h>
#include <pappsomspp/core/trace/trace.h>
#include <pappsomspp/core/trace/maptrace.h>

using namespace pappso;


TEST_CASE("Constructing Trace objects.", "[Trace][constructors]")
{
  // Construct two vectors of double values.
  std::vector<double> x_vector{1234.56789, 1233.56789, 1232.56789};
  std::vector<double> y_vector{7876.54321, 8876.54321, 9876.54321};

  // Construct a vector of pairs
  std::vector<std::pair<pappso_double, pappso_double>> pair_vector;

  // Create the points in decreasing mz value, on purpose.
  std::pair<double, double> pair1(1234.56789, 7876.54321);
  pair_vector.push_back(pair1);

  std::pair<double, double> pair2(1233.56789, 8876.54321);
  pair_vector.push_back(pair2);

  std::pair<double, double> pair3(1232.56789, 9876.54321);
  pair_vector.push_back(pair3);

  // Construct a vector of DataPoint_s
  std::vector<DataPoint> data_point_vector;

  DataPoint datapoint1(1234.56789, 7876.54321);
  data_point_vector.push_back(datapoint1);

  DataPoint datapoint2(1233.56789, 8876.54321);
  data_point_vector.push_back(datapoint2);

  DataPoint datapoint3(1232.56789, 9876.54321);
  data_point_vector.push_back(datapoint3);

  // Construct a MapTrace object
  MapTrace map_trace1(pair_vector);

  SECTION("Construct Trace from two vectors of double_s", "[Trace]")
  {
    Trace trace1(x_vector, y_vector);

    // Now check that all the pairs are in:

    REQUIRE(trace1.size() == 3);

    // Now check that the pairs have been sorted.

    REQUIRE(trace1.front().x == 1232.56789);
    REQUIRE(trace1[1].x == 1233.56789);
    REQUIRE(trace1.back().x == 1234.56789);
  }

  SECTION("Construct Trace from vector of double pairs", "[Trace]")
  {
    Trace trace1(pair_vector);

    // Now check that all the pairs are in:

    REQUIRE(trace1.size() == 3);

    // Now check that the pairs have been sorted.

    REQUIRE(trace1.front().x == 1232.56789);
    REQUIRE(trace1[1].x == 1233.56789);
    REQUIRE(trace1.back().x == 1234.56789);
  }

  SECTION("Construct Trace from vector of DataPoint_s", "[Trace]")
  {
    Trace trace2(data_point_vector);

    // Now check that all the pairs are in:

    REQUIRE(trace2.size() == 3);

    // Now check that the pairs have been sorted.

    REQUIRE(trace2.front().x == 1232.56789);
    REQUIRE(trace2[1].x == 1233.56789);
    REQUIRE(trace2.back().x == 1234.56789);
  }

  SECTION("Construct Trace from vector of a MapTrace", "[Trace]")
  {
    Trace trace3(map_trace1);

    // Now check that the pairs are sorted with no explicit sorting
    // as map traces are sorted automatically.

    REQUIRE(trace3.front().x == 1232.56789);
    REQUIRE(trace3[1].x == 1233.56789);
    REQUIRE(trace3.back().x == 1234.56789);
  }

  SECTION("Construct Trace from other Trace", "[Trace]")
  {
    Trace trace1(pair_vector);
    Trace trace2(trace1);

    REQUIRE(trace2.size() == 3);

    // Now check that the pairs have been sorted.

    REQUIRE(trace2.front().x == 1232.56789);
    REQUIRE(trace2[1].x == 1233.56789);
    REQUIRE(trace2.back().x == 1234.56789);
  }
}

TEST_CASE("Initializing Trace objects.", "[Trace][initializers]")
{
  // Create a vector of doubles (x)
  std::vector<double> x_vector{1234.56789, 1233.56789, 1232.56789};

  // Create a vector of doubles (y)
  std::vector<double> y_vector{7876.54321, 8876.54321, 9876.54321};

  // Create a map
  std::map<pappso_double, pappso_double> map_of_doubles{
    {1234.56789, 7876.54321},
    {1233.56789, 8876.54321},
    {1232.56789, 9876.54321}};

  SECTION("Initialize Trace with two vectors of double_s", "[Trace]")
  {
    Trace trace1;

    std::size_t trace_size = trace1.initialize(x_vector, y_vector);
    REQUIRE(trace_size == x_vector.size());
    REQUIRE(trace1.size() == trace_size);

    // Now check that the pairs have been sorted.

    REQUIRE(trace1.front().x == 1232.56789);
    REQUIRE(trace1[1].x == 1233.56789);
    REQUIRE(trace1.back().x == 1234.56789);
  }

  SECTION("Initialize Trace with a map<double,double>", "[Trace]")
  {
    Trace trace1;

    std::size_t trace_size = trace1.initialize(map_of_doubles);
    REQUIRE(trace_size == map_of_doubles.size());
    REQUIRE(trace1.size() == trace_size);

    // Now check that the pairs have been sorted.

    REQUIRE(trace1.front().x == 1232.56789);
    REQUIRE(trace1[1].x == 1233.56789);
    REQUIRE(trace1.back().x == 1234.56789);
  }

  SECTION("Initialize Trace with another Trace", "[Trace]")
  {
    Trace trace1;
    trace1.initialize(map_of_doubles);

    Trace trace2;
    trace2.initialize(trace1);

    REQUIRE(trace1.size() == trace2.size());

    REQUIRE(trace2.front().x == 1232.56789);
    REQUIRE(trace2[1].x == 1233.56789);
    REQUIRE(trace2.back().x == 1234.56789);
  }

  SECTION("Initialize Trace with operator =", "[Trace]")
  {
    Trace trace1;
    trace1.initialize(map_of_doubles);

    Trace trace2 = trace1;

    REQUIRE(trace1.size() == trace2.size());

    REQUIRE(trace2.front().x == 1232.56789);
    REQUIRE(trace2[1].x == 1233.56789);
    REQUIRE(trace2.back().x == 1234.56789);
  }
}

TEST_CASE("Basic functionality of Trace", "[Trace]")
{
  // Create a vector of doubles (x)
  std::vector<double> x_vector{1234.56789, 1233.56789, 1232.56789};

  // Create a vector of doubles (y)
  std::vector<double> y_vector{7876.54321, 8876.54321, 9876.54321};

  Trace trace(x_vector, y_vector);

  SECTION("Get the x values as a vector of double_s", "[Trace]")
  {
    std::vector<double> vector_of_doubles = trace.xValues();

    REQUIRE(vector_of_doubles.size() == trace.size());
    REQUIRE(vector_of_doubles[0] == 1232.56789);
    REQUIRE(vector_of_doubles[1] == 1233.56789);
    REQUIRE(vector_of_doubles[2] == 1234.56789);
  }

  SECTION("Get the y values as a vector of double_s", "[Trace]")
  {
    std::vector<double> vector_of_doubles = trace.yValues();

    REQUIRE(vector_of_doubles.size() == trace.size());
    REQUIRE(vector_of_doubles[0] == 9876.54321);
    REQUIRE(vector_of_doubles[1] == 8876.54321);
    REQUIRE(vector_of_doubles[2] == 7876.54321);
  }

  SECTION("Get the Trace as a map of doubles.", "[Trace]")
  {
    std::map<pappso_double, pappso_double> map = trace.toMap();

    REQUIRE(map.size() == trace.size());

    REQUIRE(map[1232.56789] == 9876.54321);
    REQUIRE(map[1233.56789] == 8876.54321);
    REQUIRE(map[1234.56789] == 7876.54321);
  }

  SECTION("Check if contains x value using a tolerance.", "[Trace]")
  {
    PrecisionPtr precision_p =
      pappso::PrecisionFactory::getDaltonInstance(0.0000005);

    DataPoint datapoint = trace.containsX(1233.5678, precision_p);
    REQUIRE(datapoint.isValid() == false);
    // The returned data point is not valid, then x=-1.
    REQUIRE(datapoint.x == -1);

    precision_p = pappso::PrecisionFactory::getDaltonInstance(0.000085);
    datapoint   = trace.containsX(1233.5678, precision_p);
    REQUIRE(datapoint.isValid() == false);
    // The returned data point is not valid, then x=-1.
    REQUIRE(datapoint.x == -1);

#if !defined(__i386__)

    // This test unbelievable does not pass on i386...
    precision_p = pappso::PrecisionFactory::getDaltonInstance(0.00009);
    datapoint   = trace.containsX(1233.5678, precision_p);
    REQUIRE(datapoint.isValid() == true);
    REQUIRE(datapoint.x == 1233.56789);

#endif
  }

  SECTION("Get DataPoint having min y value of all.", "[Trace]")
  {
    DataPoint datapoint = trace.Trace::minYDataPoint();

    REQUIRE(datapoint.isValid() == true);
    REQUIRE(datapoint.y == 7876.54321);
    REQUIRE(datapoint.x == 1234.56789);
  }

  SECTION("Get DataPoint having max y value of all.", "[Trace]")
  {
    DataPoint datapoint = trace.Trace::maxYDataPoint();

    REQUIRE(datapoint.isValid() == true);
    REQUIRE(datapoint.y == 9876.54321);
    REQUIRE(datapoint.x == 1232.56789);
  }

  SECTION("Get the min y value of all.", "[Trace]")
  {
    double y = trace.minY();
    REQUIRE(y == 7876.54321);
  }

  SECTION("Get the max y value of all.", "[Trace]")
  {
    double y = trace.maxY();
    REQUIRE(y == 9876.54321);
  }

  SECTION("Get the sum of all the y values.", "[Trace]")
  {
    double tic = trace.sumY();
    REQUIRE(tic == (7876.54321 + 8876.54321 + 9876.54321));
  }

  SECTION("Get the sum of all the y values in a given x range.", "[Trace]")
  {
    double tic = trace.sumY(1232, 1234);
    REQUIRE(tic == (9876.54321 + 8876.54321));
  }

  SECTION("Craft a string representation of the Trace.", "[Trace]")
  {
    QString result_string = trace.toString();

    QString test_string;

    test_string += QString("%1 %2\n")
                     .arg(trace[0].x, 0, 'f', 15)
                     .arg(trace[0].y, 0, 'f', 15);
    test_string += QString("%1 %2\n")
                     .arg(trace[1].x, 0, 'f', 15)
                     .arg(trace[1].y, 0, 'f', 15);
    test_string += QString("%1 %2\n")
                     .arg(trace[2].x, 0, 'f', 15)
                     .arg(trace[2].y, 0, 'f', 15);

    REQUIRE(result_string.toStdString() == test_string.toStdString());
  }
}

TEST_CASE("Filters and extractors for Trace", "[Trace]")
{
  // Create a Trace that looks like something real.

  std::vector<DataPoint> data_point_vector;

  // clang-format off
  data_point_vector.push_back(DataPoint("610.02000 12963.5715942383"));
  data_point_vector.push_back(DataPoint("610.07000 15639.6568298340"));
  data_point_vector.push_back(DataPoint("610.12000 55999.7628784180"));
  data_point_vector.push_back(DataPoint("610.17000 9335990.3578681946"));
  data_point_vector.push_back(DataPoint("610.2200000 635674266.1611375809"));
  data_point_vector.push_back(DataPoint("610.27000 54459.4762458801"));
  data_point_vector.push_back(DataPoint("610.32000 205580.6580276489"));
  data_point_vector.push_back(DataPoint("610.37000 84627.3196716309"));
  data_point_vector.push_back(DataPoint("610.42000 51061.7636718750"));
  data_point_vector.push_back(DataPoint("610.47000 20260.0218505859"));
  data_point_vector.push_back(DataPoint("610.52000 29848.3424072266"));
  data_point_vector.push_back(DataPoint("610.57000 27266.8031616211"));
  data_point_vector.push_back(DataPoint("610.62000 36922.3937377930"));
  data_point_vector.push_back(DataPoint("610.67000 44082.4265441895"));
  data_point_vector.push_back(DataPoint("610.72000 32580.1677703857"));
  data_point_vector.push_back(DataPoint("610.77000 39453.8380126953"));
  data_point_vector.push_back(DataPoint("610.82000 63698.7124328613"));
  data_point_vector.push_back(DataPoint("610.87000 34406.7755126953"));
  data_point_vector.push_back(DataPoint("610.92000 33385.7486267090"));
  data_point_vector.push_back(DataPoint("610.97000 22105.9357604980"));
  data_point_vector.push_back(DataPoint("611.02000 34175.9539184570"));
  data_point_vector.push_back(DataPoint("611.07000 18893.1743774414"));
  data_point_vector.push_back(DataPoint("611.12000 33655.8040771484"));
  data_point_vector.push_back(DataPoint("611.17000 1985432.9582707286"));
  data_point_vector.push_back(DataPoint("611.220000 350845489.9638078809"));
  data_point_vector.push_back(DataPoint("611.27000 201093.0304565430"));
  data_point_vector.push_back(DataPoint("611.32000 130582.0210266113"));
  data_point_vector.push_back(DataPoint("611.37000 191842.3922958374"));
  data_point_vector.push_back(DataPoint("611.42000 154248.7982788086"));
  data_point_vector.push_back(DataPoint("611.47000 31618.7268676758"));
  data_point_vector.push_back(DataPoint("611.52000 31222.7547607422"));
  data_point_vector.push_back(DataPoint("611.57000 44491.2401733398"));
  data_point_vector.push_back(DataPoint("611.62000 17576.1995697021"));
  data_point_vector.push_back(DataPoint("611.67000 29514.0727844238"));
  data_point_vector.push_back(DataPoint("611.72000 60104.3011474609"));
  data_point_vector.push_back(DataPoint("611.77000 54284.4918212891"));
  data_point_vector.push_back(DataPoint("611.82000 73222.5983886719"));
  data_point_vector.push_back(DataPoint("611.87000 34145.2793884277"));
  data_point_vector.push_back(DataPoint("611.92000 43328.2853698730"));
  data_point_vector.push_back(DataPoint("611.97000 34486.0655212402"));
  data_point_vector.push_back(DataPoint("612.02000 33462.5801696777"));
  data_point_vector.push_back(DataPoint("612.07000 45740.2189331055"));
  data_point_vector.push_back(DataPoint("612.12000 16315.6034545898"));
  data_point_vector.push_back(DataPoint("612.17000 3137434.1922011375"));
  data_point_vector.push_back(DataPoint("612.220000 244825577.0747365952"));
  data_point_vector.push_back(DataPoint("612.27000 107560.2426452637"));
  data_point_vector.push_back(DataPoint("612.32000 114637.8828430176"));
  data_point_vector.push_back(DataPoint("612.37000 87648.2245483398"));
  data_point_vector.push_back(DataPoint("612.42000 75788.5508422852"));
  data_point_vector.push_back(DataPoint("612.47000 33858.1067199707"));
  data_point_vector.push_back(DataPoint("612.52000 26404.7212524414"));
  data_point_vector.push_back(DataPoint("612.57000 45710.7806396484"));
  data_point_vector.push_back(DataPoint("612.62000 31294.2814941406"));
  data_point_vector.push_back(DataPoint("612.67000 29457.2803955078"));
  data_point_vector.push_back(DataPoint("612.72000 23048.1495971680"));
  data_point_vector.push_back(DataPoint("612.77000 15255.7875061035"));
  data_point_vector.push_back(DataPoint("612.82000 48801.3896789551"));
  data_point_vector.push_back(DataPoint("612.87000 36464.7495727539"));
  data_point_vector.push_back(DataPoint("612.92000 51507.2986145020"));
  data_point_vector.push_back(DataPoint("612.97000 45925.2813415527"));
  data_point_vector.push_back(DataPoint("613.02000 40396.0013732910"));
  data_point_vector.push_back(DataPoint("613.07000 40331.5417785645"));
  data_point_vector.push_back(DataPoint("613.12000 47593.1001892090"));
  data_point_vector.push_back(DataPoint("613.17000 1537602.4452308416"));
  data_point_vector.push_back(DataPoint("613.22000 95458729.2017828822"));
  data_point_vector.push_back(DataPoint("613.27000 117836.8993530273"));
  data_point_vector.push_back(DataPoint("613.32000 172388.2191467285"));
  data_point_vector.push_back(DataPoint("613.37000 117651.7643432617"));
  data_point_vector.push_back(DataPoint("613.42000 134527.9219970703"));
  data_point_vector.push_back(DataPoint("613.47000 21348.3722229004"));
  data_point_vector.push_back(DataPoint("613.52000 480.5255737305"));
  data_point_vector.push_back(DataPoint("613.57000 71578.8419189453"));
  data_point_vector.push_back(DataPoint("613.62000 47088.4295806885"));
  data_point_vector.push_back(DataPoint("613.67000 11516.1672668457"));
  data_point_vector.push_back(DataPoint("613.72000 42134.2611389160"));
  data_point_vector.push_back(DataPoint("613.77000 43366.3011627197"));
  data_point_vector.push_back(DataPoint("613.82000 76272.4001235962"));
  data_point_vector.push_back(DataPoint("613.87000 98113.1940002441"));
  data_point_vector.push_back(DataPoint("613.92000 29165.7173461914"));
  data_point_vector.push_back(DataPoint("613.97000 55852.7840881348"));
  data_point_vector.push_back(DataPoint("614.02000 29957.4020996094"));
  data_point_vector.push_back(DataPoint("614.07000 46380.1217651367"));
  data_point_vector.push_back(DataPoint("614.12000 22395.2603759766"));
  data_point_vector.push_back(DataPoint("614.17000 1585080.8988181949"));
  data_point_vector.push_back(DataPoint("614.22000 35239172.4581222534"));
  data_point_vector.push_back(DataPoint("614.27000 185695.5917358398"));
  data_point_vector.push_back(DataPoint("614.32000 231630.3202667236"));
  data_point_vector.push_back(DataPoint("614.37000 165712.7152709961"));
  data_point_vector.push_back(DataPoint("614.42000 115771.4071350098"));
  data_point_vector.push_back(DataPoint("614.47000 58982.7239074707"));
  data_point_vector.push_back(DataPoint("614.52000 2547873.7498645782"));
  data_point_vector.push_back(DataPoint("614.57000 18437.3129577637"));
  data_point_vector.push_back(DataPoint("614.62000 22860.9023132324"));
  data_point_vector.push_back(DataPoint("614.67000 21072.2604064941"));
  data_point_vector.push_back(DataPoint("614.72000 35379.6509933472"));
  data_point_vector.push_back(DataPoint("614.77000 59054.2345886230"));
  data_point_vector.push_back(DataPoint("614.82000 75995.3719787598"));
  data_point_vector.push_back(DataPoint("614.87000 106325.3341369629"));
  data_point_vector.push_back(DataPoint("614.92000 43670.2942276001"));
  data_point_vector.push_back(DataPoint("614.97000 39008.6439514160"));
  // clang-format on

  Trace trace(data_point_vector);

  REQUIRE(100 == trace.size());

  SECTION("Test findFirstEqualOrGreaterX", "[Trace][filters]")
  {
    // First the non const version of the iterator

    std::vector<DataPoint>::iterator iterator =
      findFirstEqualOrGreaterX(trace.begin(), trace.end(), 614.47000);
    REQUIRE((*iterator).y == 58982.7239074707);

    iterator = findFirstEqualOrGreaterX(trace.begin(), trace.end(), 613.025);
    REQUIRE((*iterator).y == 40331.5417785645);

    iterator = findFirstEqualOrGreaterX(trace.begin(), trace.end(), 614.97);
    REQUIRE((*iterator).y == 39008.6439514160);

    iterator = findFirstEqualOrGreaterX(trace.begin(), trace.end(), 614.977);
    REQUIRE(iterator == trace.end());

    iterator = findFirstEqualOrGreaterX(trace.begin(), trace.end(), 610.02000);
    REQUIRE((*iterator).y == 12963.5715942383);

    iterator = findFirstEqualOrGreaterX(trace.begin(), trace.end(), 610.019900);
    REQUIRE((*iterator).y == 12963.5715942383);

    // Now the const version of the iterator

    std::vector<DataPoint>::const_iterator const_iterator =
      findFirstEqualOrGreaterX(trace.begin(), trace.end(), 614.47000);
    REQUIRE((*const_iterator).y == 58982.7239074707);

    const_iterator =
      findFirstEqualOrGreaterX(trace.begin(), trace.end(), 613.025);
    REQUIRE((*const_iterator).y == 40331.5417785645);

    const_iterator =
      findFirstEqualOrGreaterX(trace.begin(), trace.end(), 614.97);
    REQUIRE((*const_iterator).y == 39008.6439514160);

    const_iterator =
      findFirstEqualOrGreaterX(trace.begin(), trace.end(), 614.977);
    REQUIRE(const_iterator == trace.end());

    const_iterator =
      findFirstEqualOrGreaterX(trace.begin(), trace.end(), 610.02000);
    REQUIRE((*const_iterator).y == 12963.5715942383);

    const_iterator =
      findFirstEqualOrGreaterX(trace.begin(), trace.end(), 610.019900);
    REQUIRE((*const_iterator).y == 12963.5715942383);
  }

  SECTION("Test findFirstGreaterX", "[Trace][filters]")
  {
    // First the non const version of the iterator

    std::vector<DataPoint>::iterator iterator =
      findFirstGreaterX(trace.begin(), trace.end(), 614.47000);
    REQUIRE((*iterator).y == 2547873.7498645782);

    iterator = findFirstGreaterX(trace.begin(), trace.end(), 614.92);
    REQUIRE((*iterator).y == 39008.6439514160);

    iterator = findFirstGreaterX(trace.begin(), trace.end(), 614.97);
    REQUIRE(iterator == trace.end());

    // Now the const version of the iterator

    std::vector<DataPoint>::const_iterator const_iterator =
      findFirstGreaterX(trace.begin(), trace.end(), 614.47000);
    REQUIRE((*const_iterator).y == 2547873.7498645782);

    const_iterator = findFirstGreaterX(trace.begin(), trace.end(), 614.92);
    REQUIRE((*const_iterator).y == 39008.6439514160);

    const_iterator = findFirstGreaterX(trace.begin(), trace.end(), 614.97);
    REQUIRE(const_iterator == trace.end());
  }

  SECTION("Test findDifferentYvalue", "[Trace][filters]")
  {
    // Create a Trace that looks like something real with specific y values.

    std::vector<DataPoint> data_point_vector;

    // clang-format off
    data_point_vector.push_back(DataPoint("610.02000 55999.00000"));
    data_point_vector.push_back(DataPoint("610.07000 55999.00000"));
    data_point_vector.push_back(DataPoint("610.12000 55999.00000"));
    data_point_vector.push_back(DataPoint("610.17000 55999.00000"));
    data_point_vector.push_back(DataPoint("610.2200000 635674266.1611375809"));
    data_point_vector.push_back(DataPoint("610.27000 54459.4762458801"));
    data_point_vector.push_back(DataPoint("610.32000 205580.6580276489"));
    data_point_vector.push_back(DataPoint("610.37000 84627.3196716309"));
    data_point_vector.push_back(DataPoint("610.42000 51061.7636718750"));
    data_point_vector.push_back(DataPoint("610.47000 20260.0218505859"));
    data_point_vector.push_back(DataPoint("610.52000 29848.3424072266"));
    data_point_vector.push_back(DataPoint("610.57000 27266.8031616211"));
    // clang-format on

    Trace trace(data_point_vector);

    std::vector<DataPoint>::iterator iterator =
      findDifferentYvalue(trace.begin(), trace.end(), 55999.00000);

    REQUIRE((*iterator).y == 635674266.1611375809);
  }

  SECTION("Test minYDataPoint", "[Trace][filters]")
  {
    std::vector<DataPoint>::iterator iterator =
      minYDataPoint(trace.begin(), trace.end());

    REQUIRE((*iterator).x == 613.52000);
    REQUIRE((*iterator).y == 480.5255737305);

    std::vector<DataPoint>::const_iterator const_iterator =
      minYDataPoint(trace.begin(), trace.end());

    REQUIRE((*const_iterator).x == 613.52000);
    REQUIRE((*const_iterator).y == 480.5255737305);
  }

  SECTION("Test maxYDataPoint", "[Trace][filters]")
  {
    std::vector<DataPoint>::iterator iterator =
      maxYDataPoint(trace.begin(), trace.end());

    REQUIRE((*iterator).x == 610.2200000);
    REQUIRE((*iterator).y == 635674266.1611375809);

    std::vector<DataPoint>::const_iterator const_iterator =
      maxYDataPoint(trace.begin(), trace.end());

    REQUIRE((*const_iterator).x == 610.2200000);
    REQUIRE((*const_iterator).y == 635674266.1611375809);
  }

  SECTION("Test moveLowerYRigthDataPoint", "[Trace][filters]")
  {
    // As long as next datapoint has y value less or equal to prev,
    // move along down the container. That is, continue moving if
    // direction is downhill to the end of the container (its back).

    std::vector<DataPoint> data_point_vector;

    // clang-format off
    data_point_vector.push_back(DataPoint("610.02000 12963.5715942383"));
    data_point_vector.push_back(DataPoint("610.02000 12962.5715942383"));
    data_point_vector.push_back(DataPoint("610.02000 12961.5715942383"));
    data_point_vector.push_back(DataPoint("610.02000 12961.5715942383"));
    data_point_vector.push_back(DataPoint("610.02000 12960.5715942383"));
    data_point_vector.push_back(DataPoint("610.07000 15639.6568298340"));
    data_point_vector.push_back(DataPoint("610.12000 55999.7628784180"));
    data_point_vector.push_back(DataPoint("610.17000 9335990.3578681946"));
    // clang-format on

    Trace trace(data_point_vector);

    std::vector<DataPoint>::const_iterator const_iterator =
      moveLowerYRigthDataPoint(trace, trace.begin());

    REQUIRE((*const_iterator).y == 12960.5715942383);
  }


  SECTION("Test moveLowerYLeftDataPoint", "[Trace][filters]")
  {
    // As long as prev datapoint has y value less or equal to next,
    // move along up the container. That is, continue moving if
    // direction is downhill to the beginning of the container (its front).

    std::vector<DataPoint> data_point_vector;

    // clang-format off
    data_point_vector.push_back(DataPoint("610.02000 12960.5715942383"));
    data_point_vector.push_back(DataPoint("610.02000 12961.5715942383"));
    data_point_vector.push_back(DataPoint("610.07000 17000.6568298340"));
    data_point_vector.push_back(DataPoint("610.07000 15639.6568298340"));
    data_point_vector.push_back(DataPoint("610.11000 15640.6568298340"));
    data_point_vector.push_back(DataPoint("610.12000 15641.6568298340"));
    data_point_vector.push_back(DataPoint("610.09000 15642.7000000000"));
    // clang-format on

    Trace trace(data_point_vector);

    std::vector<DataPoint>::const_iterator const_iterator =
      // I (FR) find the test ill-conceived because if the user passes
      // trace.end() as the start iterator, then it does not work.
      moveLowerYLeftDataPoint(trace, std::prev(trace.end()));

    REQUIRE(const_iterator->x == 610.11000);
    REQUIRE(const_iterator->y == 15640.6568298340);
  }
}
