
#include <iostream>
#include <ctime>
#include <random>

#include <catch.hpp>

#include <libmass/Trace.hpp>
#include <globals/globals.hpp>

#include <QDebug>
#include <QFile>

using std::cout;

#define TEST_DATA_DIR "/home/rusconi/devel/msxpertsuite/tests/data"

// qDebug() << __FILE__ << __LINE__ << __FUNCTION__
//<< "Should compile libmass tests.";

namespace msXpSlibmass
{

TEST_CASE("traces can have a title ", "[Trace]")
{
  QString title("trace title");
  Trace trace(title);

  REQUIRE(trace.title() == "trace title");
}


TEST_CASE("traces have decimal places", "[Trace]")
{
  Trace trace;
  int decimalPlaces = 5;

  trace.setDecimalPlaces(decimalPlaces);
  REQUIRE(trace.decimalPlaces() == decimalPlaces);

  decimalPlaces = 10;

  trace.rdecimalPlaces() = decimalPlaces;
  REQUIRE(trace.decimalPlaces() == decimalPlaces);
}


TEST_CASE("traces can be initialized with double lists", "[Trace]")
{
  Trace Trace;

  REQUIRE(Trace.size() == 0);

  SECTION("The trace is initialized with key and val double lists")
  {
    QList<double> keyList = {1.00, 2.100, 3.200, 4.300, 5.400};
    QList<double> valList = {1300, 2600, 5200, 10400, 20800};
    Q_ASSERT(keyList.size() == valList.size());
    int itemCount = keyList.size();

    Trace.initialize(keyList, valList);

    REQUIRE(Trace.size() == itemCount);

    int failedKeyComparisons = 0;
    int failedIComparisons   = 0;

    for(int iter = 0; iter < itemCount; ++iter)
      {
        DataPoint *p_dp = Trace.at(iter);

        if(p_dp->key() != keyList.at(iter))
          ++failedKeyComparisons;

        if(p_dp->val() != valList.at(iter))
          ++failedIComparisons;
      }

    REQUIRE(failedKeyComparisons == 0);
    REQUIRE(failedIComparisons == 0);
  }


  SECTION(
    "The trace is initialized with key and val double lists "
    "and start-end range values")
  {
    QList<double> keyList = {
      725.639, 987.4569, 1235.1234, 1236.1234, 2598.456, 2599.457, 3569.12458};
    QList<double> valList = {1250, 2500, 5000, 10000, 20000, 40000, 80000};
    Q_ASSERT(keyList.size() == valList.size());
    int itemCount = keyList.size();

    double start = 1235.12341;
    double end   = 2599.456;

    Trace.initialize(keyList, valList, start, end);

    REQUIRE(Trace.size() == 2);

    REQUIRE(Trace[0]->key() == 1236.1234);
    REQUIRE(Trace[0]->val() == 10000);

    REQUIRE(Trace[1]->key() == 2598.456);
    REQUIRE(Trace[1]->val() == 20000);
  }
}


TEST_CASE(
  "traces can be initialized with double vectors "
  "and start-end range values",
  "[Trace]")
{
  Trace Trace;

  REQUIRE(Trace.size() == 0);

  SECTION(
    "The trace is initialized with key and val double "
    "vectors and no start-end range values")
  {
    QVector<double> keyVector = {1.00, 2.100, 3.200, 4.300, 5.400};
    QVector<double> valVector = {1300, 2600, 5200, 10400, 20800};
    Q_ASSERT(keyVector.size() == valVector.size());
    int itemCount = keyVector.size();

    Trace.initialize(keyVector, valVector);

    REQUIRE(Trace.size() == itemCount);

    int failedKeyComparisons = 0;
    int failedIComparisons   = 0;

    for(int iter = 0; iter < itemCount; ++iter)
      {
        DataPoint *p_dp = Trace.at(iter);

        if(p_dp->key() != keyVector.at(iter))
          ++failedKeyComparisons;

        if(p_dp->val() != valVector.at(iter))
          ++failedIComparisons;
      }

    REQUIRE(failedKeyComparisons == 0);
    REQUIRE(failedIComparisons == 0);
  }


  SECTION(
    "The trace is initialized with key and val double "
    "vectors and start-end range values")
  {
    QVector<double> keyVector = {
      725.639, 987.4569, 1235.1234, 1236.1234, 2598.456, 2599.457, 3569.12458};
    QVector<double> valVector = {1250, 2500, 5000, 10000, 20000, 40000, 80000};
    Q_ASSERT(keyVector.size() == valVector.size());
    int itemCount = keyVector.size();

    double start = 1235.12341;
    double end   = 2599.456;

    Trace.initialize(keyVector, valVector, start, end);

    REQUIRE(Trace.size() == 2);

    REQUIRE(Trace[0]->key() == 1236.1234);
    REQUIRE(Trace[0]->val() == 10000);

    REQUIRE(Trace[1]->key() == 2598.456);
    REQUIRE(Trace[1]->val() == 20000);
  }
}


TEST_CASE("traces can be initialized with simple XY-formatted text.", "[Trace]")
{
  Trace Trace;

  REQUIRE(Trace.size() == 0);

  QStringList xyFormatStringList;
  xyFormatStringList << "199.928558349609 1840.123456700000"
                     << "199.934738159180 2471.456780000000"
                     << "199.940917968750 3497.000000056985"
                     << "199.947082519531 5236.000000000000"
                     << "199.953262329102 8631.000000000000"
                     << "199.959442138672 31505.000000000000"
                     << "199.965606689453 75716.000000000000"
                     << "199.971786499023 79882.000000000000"
                     << "199.977951049805 47849.000000000000"
                     << "199.984130859375 33539.000000000000"
                     << "199.990295410156 29096.000000000000"
                     << "199.996475219727 29104.000000000000"
                     << "200.002655029297 26650.000000000000"
                     << "200.008819580078 24243.000000000000"
                     << "200.014999389648 25983.000000000000"
                     << "200.021179199219 28867.000000000000"
                     << "200.027359008789 23874.000000000000"
                     << "200.033523559570 19043.000000000000"
                     << "200.039703369141 16450.000000000000"
                     << "200.045883178711 17226.000000000000"
                     << "200.052062988281 30648.000000000000"
                     << "200.058242797852 44396.000000000000"
                     << "200.064407348633 34241.000000000000"
                     << "200.070587158203 23241.000000000000"
                     << "200.076766967773 17745.000000000000"
                     << "200.082946777344 16258.000000000000"
                     << "200.089126586914 30343.000000000000";

  int xyFormatStringListSize = xyFormatStringList.size();

  QString xyFormatString = xyFormatStringList.join("\n");

  Trace.initialize(xyFormatString);

  REQUIRE(xyFormatStringListSize == Trace.size());

  int failedKeyComparisons = 0;
  int failedIComparisons   = 0;

  for(int iter = 0; iter < xyFormatStringListSize; ++iter)
    {
      DataPoint *p_curDp = Trace.at(iter);
      DataPoint *p_dp    = new DataPoint(xyFormatStringList.at(iter));

      if(p_curDp->key() != p_dp->key())
        ++failedKeyComparisons;

      if(p_curDp->val() != p_dp->val())
        ++failedIComparisons;
    }

  REQUIRE(failedKeyComparisons == 0);
  REQUIRE(failedIComparisons == 0);
}


TEST_CASE("All the data points of a trace can be deleted.", "[Trace]")
{
  Trace Trace;
  REQUIRE(Trace.size() == 0);

  QVector<double> keyVector = {
    725.639, 987.4569, 1235.1234, 1236.1234, 2598.456, 2599.457, 3569.12458};
  QVector<double> valVector = {1250, 2500, 5000, 10000, 20000, 40000, 80000};
  Q_ASSERT(keyVector.size() == valVector.size());
  int itemCount = keyVector.size();

  Trace.initialize(keyVector, valVector);
  REQUIRE(Trace.size() == itemCount);

  Trace.deleteDataPoints();
  REQUIRE(Trace.size() == 0);
}


TEST_CASE("traces can be reset to an empty state.", "[Trace]")
{
  Trace Trace;
  REQUIRE(Trace.size() == 0);

  QVector<double> keyVector = {
    725.639, 987.4569, 1235.1234, 1236.1234, 2598.456, 2599.457, 3569.12458};
  QVector<double> valVector = {1250, 2500, 5000, 10000, 20000, 40000, 80000};
  Q_ASSERT(keyVector.size() == valVector.size());
  int itemCount = keyVector.size();

  Trace.initialize(keyVector, valVector);
  REQUIRE(Trace.size() == itemCount);

  Trace.reset();
  REQUIRE(Trace.size() == 0);
}


TEST_CASE("ValSum calculations.", "[Trace]")
{
  QList<double> keyList = {
    694.5166264003, 694.5333824250, 694.5501386518, 694.5668950807,
    694.5836517118, 694.6004085450, 694.6171655803, 694.6339228177,
    694.6506802573, 694.6674378989, 694.6841957428, 694.7009537887,
    694.7177120368, 694.7344704869, 694.7512291393, 694.7679879937,
    694.7847470503, 694.8015063090, 694.8182657698, 694.8350254327};

  QList<double> valList = {
    133.0000000000, 195.0000000000, 225.0000000000, 220.0000000000,
    286.0000000000, 332.0000000000, 282.0000000000, 164.0000000000,
    140.0000000000, 156.0000000000, 153.0000000000, 89.0000000000,
    104.0000000000, 143.0000000000, 196.0000000000, 295.0000000000,
    396.0000000000, 399.0000000000, 369.0000000000, 345.0000000000};

  if(keyList.size() != valList.size())
    qFatal(
      "Fatal error at %s@%d -- %s. "
      "The key and val lists have not the same size."
      "Program aborted.",
      __FILE__,
      __LINE__,
      __FUNCTION__);

  int itemCount = keyList.size();

  Trace Trace;
  Trace.initialize(keyList, valList);

  // Check that initialization is correct.
  REQUIRE(Trace.size() == itemCount);

  SECTION("Compute the ValSum with no key range.", "[Trace]")
  {
    // We computed the ValSum in SpeedCrunch: (4622.00000000).
    REQUIRE(Trace.valSum() == 4622.00000000);
  }

  SECTION("Compute the ValSum with key range.", "[Trace]")
  {
    // Want to leave out the first four and last four key values of the
    // trace. SpeedCrunch value: 2340.00000000
    double keyStart = 694.5668950808; // slightly more than fourth from top
    double keyEnd   = 694.7847470502; // slightly less fourth from bottom

    REQUIRE(Trace.valSum(keyStart, keyEnd) == 2340.00000000);
  }
}


TEST_CASE("trace combination tests.", "[Trace]")
{
  QList<double> keyList = {
    694.5166264003, 694.5333824250, 694.5501386518, 694.5668950807,
    694.5836517118, 694.6004085450, 694.6171655803, 694.6339228177,
    694.6506802573, 694.6674378989, 694.6841957428, 694.7009537887,
    694.7177120368, 694.7344704869, 694.7512291393, 694.7679879937,
    694.7847470503, 694.8015063090, 694.8182657698, 694.8350254327};

  QList<double> valList = {
    133.0000000000, 195.0000000000, 225.0000000000, 220.0000000000,
    286.0000000000, 332.0000000000, 282.0000000000, 164.0000000000,
    140.0000000000, 156.0000000000, 153.0000000000, 89.0000000000,
    104.0000000000, 143.0000000000, 196.0000000000, 295.0000000000,
    396.0000000000, 399.0000000000, 369.0000000000, 345.0000000000};

  if(keyList.size() != valList.size())
    qFatal(
      "Fatal error at %s@%d -- %s. "
      "The key and val lists have not the same size."
      "Program aborted.",
      __FILE__,
      __LINE__,
      __FUNCTION__);

  int itemCount = keyList.size();

  Trace trace;
  trace.initialize(keyList, valList);

  // Create an identical copy to later check the differences.
  Trace otherTrace;
  otherTrace.initialize(keyList, valList);

  // Check that initialization is correct.
  REQUIRE(trace.size() == itemCount);

  SECTION(
    "Combine a data point into the trace when "
    "key is already in the trace.",
    "[Trace]")
  {

    // In this test, we should not create a new DataPoint in the trace, but
    // increment the val value of one of the DataPoint items.
    std::default_random_engine generator;
    std::uniform_int_distribution<int> distribution(10, itemCount - 10);
    int keyIndex = distribution(
      generator); // generates number in the range 1..6srand(time(NULL));
    // cout << "Random index:" << keyIndex << "\n";
    int prevVal = trace.at(keyIndex)->val();
    // cout << std::setprecision(15) << "m/z value for index:" <<
    // trace.at(keyIndex)->key() << "\n"; cout << std::setprecision(15) <<
    // "Intensity for index:" << prevVal << "\n";

    double newKeyValue = trace.at(keyIndex)->key();

    DataPoint dataPoint(newKeyValue, 1234);

    trace.combine(dataPoint);

    // The only differing DataPoint item should be at index keyIndex. Let's
    // check that.

    QMap<int, double> changedIdxVsKeyMap;
    QMap<int, double> changedIdxVsValMap;

    for(int iter = 0; iter < itemCount; ++iter)
      {
        double key = trace.at(iter)->key();
        double val = trace.at(iter)->val();

        double otherKey = otherTrace.at(iter)->key();
        double otherVal = otherTrace.at(iter)->val();

        if(key != otherKey)
          changedIdxVsKeyMap.insert(iter, key);

        if(val != otherVal)
          changedIdxVsValMap.insert(iter, val);
      }

    REQUIRE(changedIdxVsKeyMap.size() == 0);
    REQUIRE(changedIdxVsValMap.size() == 1);

    REQUIRE(changedIdxVsValMap.first() /* value in the map */ ==
            prevVal + 1234);
    REQUIRE(changedIdxVsValMap.key(changedIdxVsValMap.first()) == keyIndex);

    // At this point we have tested that we have updated the val value of a
    // DataPoint located at the intended position.
  }

  SECTION(
    "Combine a data point into the trace when "
    "key is not already in the trace.",
    "[Trace]")
  {
    // In this test, we know we should create a new DataPoint item inside the
    // trace one index after the targeted one.

    std::default_random_engine generator;
    std::uniform_int_distribution<int> distribution(10, itemCount - 10);
    int keyIndex = distribution(
      generator); // generates number in the range 1..6srand(time(NULL));
    // cout << "Random index:" << keyIndex << "\n";
    double origPrevKey = trace.at(keyIndex)->key();
    double origNextKey = trace.at(keyIndex + 1)->key();

    double origPrevVal = trace.at(keyIndex)->val();
    double origNextVal = trace.at(keyIndex + 1)->val();

    // cout << std::setprecision(15) << "orig prev: (" << origPrevKey << "," <<
    // origPrevVal  << ")\n"; cout << std::setprecision(15) << "orig next: (" <<
    // origNextKey << "," << origNextVal  << ")\n";

    // We want to insert the new DataPoint between origPrevKey and origNextKey.
    double newKeyValue =
      origPrevKey + (origNextKey - origPrevKey) - 0.0000000001;

    DataPoint dataPoint(newKeyValue, 1234);

    trace.combine(dataPoint);

    // Now that trace should be longer by one item.

    REQUIRE(trace.size() == itemCount + 1);

    // The only differing DataPoint item should be at index (keyIndex + 1).
    // Let's check that.

    int failedComparisons = 0;

    for(int iter = 0; iter <= keyIndex; ++iter)
      {
        if(trace.at(iter)->key() != otherTrace.at(iter)->key())
          {
            // cout << std::setprecision(5) << "failed key comparison at iter:"
            // << iter << "\n";

            ++failedComparisons;
          }

        // Also compare the intensity, since they should not have changed
        // neither.

        if(trace.at(iter)->val() != otherTrace.at(iter)->val())
          {
            // cout << std::setprecision(5) << "failed val comparison at iter:"
            // << iter << "\n";

            ++failedComparisons;
          }
      }

    REQUIRE(failedComparisons == 0);

    // The inserted DataPoint should be at (keyIndex + 1)
    REQUIRE(trace.at(keyIndex + 1)->key() == newKeyValue);

    // And now all the remaining items should be identical, but dephased.

    failedComparisons = 0;

    for(int iter = keyIndex + 1; iter < itemCount; ++iter)
      {
        if(otherTrace.at(iter)->key() != trace.at(iter + 1)->key())
          ++failedComparisons;

        if(otherTrace.at(iter)->val() != trace.at(iter + 1)->val())
          ++failedComparisons;
      }

    REQUIRE(failedComparisons == 0);

    // At this point we have tested the we inserted a new DataPoint at the
    // intended position.
  }

  SECTION(
    "Combine a data point into the trace when "
    "key is not already in the trace and new one should be on top.",
    "[Trace]")
  {

    // In this test, we should create a new DataPoint in the trace, that
    // should become the first DataPoint of the trace after insertion
    // at index 0.

    int keyIndex        = 0;
    double origFirstKey = trace.at(keyIndex)->key();
    double origFirstI   = trace.at(keyIndex)->val();

    // cout << std::setprecision(15) << "m/z value for index:" << origFirstKey
    // << "\n"; cout << std::setprecision(15) << "Intensity for index:" <<
    // origFirstI << "\n";


    // Prepare a new key value that is very slightly inferior to the key value
    // of the first DataPoint of the trace;
    double newKeyValue = trace.at(keyIndex)->key() - 0.0000000001;
    DataPoint dataPoint(newKeyValue, 1234);

    trace.combine(dataPoint);

    // The only differing DataPoint item should be at index keyIndex. Let's
    // check that.

    REQUIRE(trace.at(0)->key() == newKeyValue);
    REQUIRE(trace.at(0)->val() == 1234);

    // Now, all the other items, should be identical. Let's check that. Skip
    // first data point of trace, because we know it's the new one.
    // Watch the dephasing in the for loop.

    int failedComparisons = 0;

    for(int iter = 1; iter <= itemCount; ++iter)
      {
        double key = trace.at(iter)->key();
        double val = trace.at(iter)->val();

        double otherKey = otherTrace.at(iter - 1)->key();
        double otherVal = otherTrace.at(iter - 1)->val();

        if(key != otherKey || val != otherVal)
          ++failedComparisons;
      }

    REQUIRE(failedComparisons == 0);

    // At this point we have tested that we have inserted at index 0 a new
    // DataPoint instance.
  }


  SECTION(
    "Combine a data point into the trace when "
    "key is not already in the trace and new one should be on bottom.",
    "[Trace]")
  {

    // In this test, we should create a new DataPoint in the trace, that
    // should become the last DataPoint of the trace after insertion
    // at index (itemCount - 1).

    int keyIndex       = itemCount - 1;
    double origLastKey = trace.at(keyIndex)->key();
    double origLastI   = trace.at(keyIndex)->val();

    // cout << std::setprecision(15) << "m/z value for index:" << origLastKey <<
    // "\n"; cout << std::setprecision(15) << "Intensity for index:" << origLastI
    // << "\n";

    // Prepare a new key value that is very slightly greater to the key value
    // of the last DataPoint of the trace;
    double newKeyValue = trace.at(keyIndex)->key() + 0.0000000001;
    DataPoint dataPoint(newKeyValue, 1234);

    trace.combine(dataPoint);

    // We should have added a new DataPoint instance, the trace should have
    // grown by 1 unit. Thus, the last index is now itemCount.

    int newItemCount = trace.size();
    REQUIRE(newItemCount == itemCount + 1);

    REQUIRE(trace.at(newItemCount - 1)->key() == newKeyValue);
    REQUIRE(trace.at(newItemCount - 1)->val() == 1234);

    // Now, all the other items, should be identical. Let's check that. Skip
    // last data point of Trace, because we know it's the new one.

    int failedComparisons = 0;

    for(int iter = 0; iter < newItemCount - 1 /* skip last item */; ++iter)
      {
        double key = trace.at(iter)->key();
        double val = trace.at(iter)->val();

        double otherKey = otherTrace.at(iter)->key();
        double otherVal = otherTrace.at(iter)->val();

        if(key != otherKey || val != otherVal)
          ++failedComparisons;
      }

    REQUIRE(failedComparisons == 0);

    // At this point we have tested that we have inserted a new data point as
    // the very last item in the trace.
  }
}


TEST_CASE("Combination of trace list into a mass trace", "[Trace]")
{
  // Preparation of various mobility-experiment data that are not binnable
  // traces because the key intervals are not equal all along the key
  // vector. First twenty data points from files
  // tests/data/xy/protein-mobility-waters-synapt-hdms.keyml-spec-index-[0--4].xy
  // and
  // protein-mobility-waters-synapt-hdms.keyml-combination-spec-idx-0-to-4.xy.

  QStringList xyFormatStringList;
  xyFormatStringList << "2207.9724474677 0.0000000000"
                     << "2207.9932149877 3.0000000000"
                     << "2208.0137381840 32.0000000000"
                     << "2208.0342613806 0.0000000000";

  int xyFormatStringListSize = xyFormatStringList.size();
  QString xyFormatString     = xyFormatStringList.join("\n");
  Trace *massSpecIdx0        = new Trace;
  int count                  = massSpecIdx0->initialize(xyFormatString);

  // Since the initialization is actually a combination of each DataPoint
  // objects represented by each line, the 0-val DataPoint instances are not
  // combined.

  REQUIRE(count == 2);

  // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
  //<< "Count is:" << count;

  REQUIRE(massSpecIdx0->size() == 2);

  xyFormatStringList.clear();
  xyFormatStringList << "1861.6054557607 0.0000000000"
                     << "1861.6243881557 4.0000000000"
                     << "1861.6431984064 16.0000000000"
                     << "1861.6620086572 0.0000000000";

  xyFormatStringListSize = xyFormatStringList.size();
  xyFormatString         = xyFormatStringList.join("\n");
  Trace *massSpecIdx1    = new Trace;
  massSpecIdx1->initialize(xyFormatString);
  REQUIRE(massSpecIdx1->size() == 2);

  xyFormatStringList.clear();
  xyFormatStringList << "2173.2127519882 0.0000000000"
                     << "2173.2330305731 52.0000000000"
                     << "2173.2533091582 3.0000000000"
                     << "2173.2738320637 0.0000000000";

  xyFormatStringListSize = xyFormatStringList.size();
  xyFormatString         = xyFormatStringList.join("\n");
  Trace *massSpecIdx2    = new Trace;
  massSpecIdx2->initialize(xyFormatString);
  REQUIRE(massSpecIdx2->size() == 2);

  xyFormatStringList.clear();
  xyFormatStringList << "1879.3452969428 0.0000000000"
                     << "1879.3642294765 27.0000000000"
                     << "1879.3831620103 0.0000000000";

  xyFormatStringListSize = xyFormatStringList.size();
  xyFormatString         = xyFormatStringList.join("\n");
  Trace *massSpecIdx3    = new Trace;
  massSpecIdx3->initialize(xyFormatString);
  REQUIRE(massSpecIdx3->size() == 1);

  xyFormatStringList.clear();
  xyFormatStringList << "1848.9427702443 0.0000000000"
                     << "1848.9615803966 21.0000000000"
                     << "1848.9803905489 17.0000000000"
                     << "1848.9990785576 0.0000000000"
                     << "2007.9375013022 0.0000000000"
                     << "2007.9570456000 34.0000000000"
                     << "2007.9765898979 3.0000000000"
                     << "2007.9962563479 0.0000000000";

  xyFormatStringListSize = xyFormatStringList.size();
  xyFormatString         = xyFormatStringList.join("\n");
  Trace *massSpecIdx4    = new Trace;
  massSpecIdx4->initialize(xyFormatString);
  REQUIRE(massSpecIdx4->size() == 4);

  xyFormatStringList.clear();


  SECTION("Combination with no key range", "[Trace]")
  {
    // We now have five traces. We can now construct the trace
    // list.

    QList<Trace *> traces;
    traces << massSpecIdx0 << massSpecIdx1 << massSpecIdx2 << massSpecIdx3
           << massSpecIdx4;

    // The combined trace as computed apart:
    xyFormatStringList.clear();
    xyFormatStringList << "1848.9427702443 0.0000000000"
                       << "1848.9615803966 21.0000000000"
                       << "1848.9803905489 17.0000000000"
                       << "1848.9990785576 0.0000000000"
                       << "1861.6054557607 0.0000000000"
                       << "1861.6243881557 4.0000000000"
                       << "1861.6431984064 16.0000000000"
                       << "1861.6620086572 0.0000000000"
                       << "1879.3452969428 0.0000000000"
                       << "1879.3642294765 27.0000000000"
                       << "1879.3831620103 0.0000000000"
                       << "2007.9375013022 0.0000000000"
                       << "2007.9570456000 34.0000000000"
                       << "2007.9765898979 3.0000000000"
                       << "2007.9962563479 0.0000000000"
                       << "2173.2127519882 0.0000000000"
                       << "2173.2330305731 52.0000000000"
                       << "2173.2533091582 3.0000000000"
                       << "2173.2738320637 0.0000000000"
                       << "2207.9724474677 0.0000000000"
                       << "2207.9932149877 3.0000000000"
                       << "2208.0137381840 32.0000000000"
                       << "2208.0342613806 0.0000000000";

    xyFormatStringListSize = xyFormatStringList.size();
    xyFormatString         = xyFormatStringList.join("\n");
    Trace theorCombinedTrace;
    theorCombinedTrace.initialize(xyFormatString);
    REQUIRE(theorCombinedTrace.size() == 11);

    xyFormatStringList.clear();

    // Finally actually test the combination function.

    Trace combinedTrace;
    combinedTrace.combine(traces);

    // qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
    //<< "combinedTrace as text:" << combinedTrace.asText();

    // Check that both the theor and the actual combined spectra have the same
    // number of data point instances.

    REQUIRE(theorCombinedTrace.size() == combinedTrace.size());

    // Now test that the obained mass combined trace is identical to the
    // one computed apart.

    int failedComparisons = 0;

    for(int iter = 0; iter < combinedTrace.size(); ++iter)
      {
        double key = combinedTrace.at(iter)->key();
        double val = combinedTrace.at(iter)->val();

        double theorKey = theorCombinedTrace.at(iter)->key();
        double theorVal = theorCombinedTrace.at(iter)->val();

        // cout << std::setprecision(15)
        //<< "(" << key << "," << val << ") vs (" << theorKey << "," << theorVal
        //<< ")\n";

        // It is important to use the almostEqual() because otherwise we get
        // spurious messages.
        if(!msXpS::almostEqual(key, theorKey, 15))
          {
            cout << std::setprecision(15) << "failed comparison:"
                 << "test (" << key << " vs theor " << theorKey << "\n";

            ++failedComparisons;
          }

        if(!msXpS::almostEqual(val, theorVal, 15))
          {
            cout << std::setprecision(15) << "failed comparison:"
                 << "test (" << val << " vs theor " << theorVal << "\n";

            ++failedComparisons;
          }
      }

    REQUIRE(failedComparisons == 0);
  }


  SECTION("Combine with key range", "[Trace]")
  {
    // We now have five traces. We can now construct the trace
    // list.

    QList<Trace *> traces;
    traces << massSpecIdx0 << massSpecIdx1 << massSpecIdx2 << massSpecIdx3
           << massSpecIdx4;

    // The un-key-ranged combination above provides the trace below
    //
    // 1848.9615803966 21.0000000000
    // 1848.9803905489 17.0000000000
    // 1861.6243881557 4.0000000000
    // 1861.6431984064 16.0000000000
    // 1879.3642294765 27.0000000000
    // 2007.9570456000 34.0000000000
    // 2007.9765898979 3.0000000000
    // 2173.2330305731 52.0000000000
    // 2173.2533091582 3.0000000000
    // 2207.9932149877 3.0000000000
    // 2208.0137381840 32.0000000000

    // Skip 3 spectra on the top (massSpecIdx0).
    // A data point is in the range if key >= keyStart.
    double keyStart = 1861.63;

    // Skip 3 spectra on the bottom
    // A data point is in the range if key <= keyEnd.
    double keyEnd = 2173.24;

    // cout << std::setprecision(15) << "Combining spectra with key range: ["
    //<< keyStart << "," << keyEnd << "]\n";

    // The combined trace as computed apart:

    xyFormatStringList.clear();
    xyFormatStringList << "1861.6431984064 16.0000000000"
                       << "1879.3642294765 27.0000000000"
                       << "2007.9570456000 34.0000000000"
                       << "2007.9765898979 3.0000000000"
                       << "2173.2330305731 52.0000000000";

    xyFormatStringListSize = xyFormatStringList.size();
    xyFormatString         = xyFormatStringList.join("\n");
    Trace theorCombinedTrace;
    theorCombinedTrace.initialize(xyFormatString);
    REQUIRE(xyFormatStringListSize == theorCombinedTrace.size());

    xyFormatStringList.clear();

    // Finally actually test the combination function.

    Trace combinedTrace;

    combinedTrace.combine(traces, keyStart, keyEnd);

    // Check that both the theor and the actual combined spectra have the same
    // number of data point instances.

    REQUIRE(theorCombinedTrace.size() == combinedTrace.size());

    // Now test that the obained mass combined trace is identical to the
    // one computed apart.

    int failedComparisons = 0;

    for(int iter = 0; iter < combinedTrace.size(); ++iter)
      {
        double key = combinedTrace.at(iter)->key();
        double val = combinedTrace.at(iter)->val();

        double theorKey = theorCombinedTrace.at(iter)->key();
        double theorVal = theorCombinedTrace.at(iter)->val();

        // cout << std::setprecision(15)
        //<< "(" << key << "," << val << ") vs (" << theorKey << "," << theorVal
        //<< ")\n";

        // It is important to use the almostEqual() because otherwise we get
        // spurious messages.
        if(!msXpS::almostEqual(key, theorKey, 15))
          {
            cout << std::setprecision(15) << "failed comparison:"
                 << "test (" << key << " vs theor " << theorKey << "\n";

            ++failedComparisons;
          }

        if(!msXpS::almostEqual(val, theorVal, 15))
          {
            cout << std::setprecision(15) << "failed comparison:"
                 << "test (" << val << " vs theor " << theorVal << "\n";

            ++failedComparisons;
          }
      }

    REQUIRE(failedComparisons == 0);
  }

  SECTION("Combination of keyList/valList", "[Trace]")
  {
    // We now have five traces. We can now construct the trace
    // list. Only use the first four, as the fifth will be combined in the form
    // of keyList and valList, which allows use to recycle the combined trace
    // form an earlier test.

    QList<Trace *> traces;
    traces << massSpecIdx0 << massSpecIdx1 << massSpecIdx2 << massSpecIdx3;

    // Last trace to combine (massSpecIdx4) but in keyList/valList format:

    QList<double> keyList;
    keyList << 1848.9427702443 << 1848.9615803966 << 1848.9803905489
            << 1848.9990785576 << 2007.9375013022 << 2007.9570456000
            << 2007.9765898979 << 2007.9962563479;

    QList<double> valList;
    valList << 0.0 << 21.0 << 17.0 << 0.0 << 0.0 << 34.0 << 3.0 << 0.0;

    // The combined trace as computed apart:

    xyFormatStringList.clear();
    xyFormatStringList << "1848.9615803966 21.0000000000"
                       << "1848.9803905489 17.0000000000"
                       << "1861.6243881557 4.0000000000"
                       << "1861.6431984064 16.0000000000"
                       << "1879.3642294765 27.0000000000"
                       << "2007.9570456000 34.0000000000"
                       << "2007.9765898979 3.0000000000"
                       << "2173.2330305731 52.0000000000"
                       << "2173.2533091582 3.0000000000"
                       << "2207.9932149877 3.0000000000"
                       << "2208.0137381840 32.0000000000";

    xyFormatStringListSize = xyFormatStringList.size();
    xyFormatString         = xyFormatStringList.join("\n");
    Trace theorCombinedTrace;
    theorCombinedTrace.initialize(xyFormatString);
    REQUIRE(xyFormatStringListSize == theorCombinedTrace.size());

    xyFormatStringList.clear();

    // Finally actually test the combination function.

    Trace combinedTrace;
    combinedTrace.combine(traces);
    combinedTrace.combine(keyList, valList);

    // Check that both the theor and the actual combined spectra have the same
    // number of data point instances.

    REQUIRE(theorCombinedTrace.size() == combinedTrace.size());

    // Now test that the obained mass combined trace is identical to the
    // one computed apart.

    int failedComparisons = 0;

    for(int iter = 0; iter < combinedTrace.size(); ++iter)
      {
        double key = combinedTrace.at(iter)->key();
        double val = combinedTrace.at(iter)->val();

        double theorKey = theorCombinedTrace.at(iter)->key();
        double theorVal = theorCombinedTrace.at(iter)->val();

        // cout << std::setprecision(15)
        //<< "(" << key << "," << val << ") vs (" << theorKey << "," << theorVal
        //<< ")\n";

        // It is important to use the almostEqual() because otherwise we get
        // spurious messages.
        if(!msXpS::almostEqual(key, theorKey, 15))
          {
            cout << std::setprecision(15) << "failed comparison:"
                 << "test (" << key << " vs theor " << theorKey << "\n";

            ++failedComparisons;
          }

        if(!msXpS::almostEqual(val, theorVal, 15))
          {
            cout << std::setprecision(15) << "failed comparison:"
                 << "test (" << val << " vs theor " << theorVal << "\n";

            ++failedComparisons;
          }
      }

    REQUIRE(failedComparisons == 0);
  }

  SECTION("Combination (not-binned case) of keyList/valList with key range.",
          "[Trace]")
  {
    // We now have five traces. We can now construct the trace
    // list. Only use the first four, as the fifth will be combined in the form
    // of keyList and valList, which allows use to recycle the combined trace
    // form an earlier test.

    QList<Trace *> traces;
    traces << massSpecIdx0 << massSpecIdx1 << massSpecIdx2 << massSpecIdx3;

    // Last trace to combine (massSpecIdx4) but in keyList/valList format:

    QList<double> keyList;
    keyList << 1848.9427702443 << 1848.9615803966 << 1848.9803905489
            << 1848.9990785576 << 2007.9375013022 << 2007.9570456000
            << 2007.9765898979 << 2007.9962563479;

    QList<double> valList;
    valList << 0.0 << 21.0 << 17.0 << 0.0 << 0.0 << 34.0 << 3.0 << 0.0;

    // The combined trace as computed apart:

    xyFormatStringList.clear();
    xyFormatStringList << "1861.6243881557 4.0000000000"
                       << "1861.6431984064 16.0000000000"
                       << "1861.6620086572 0.0000000000"
                       << "1879.3452969428 0.0000000000"
                       << "1879.3642294765 27.0000000000"
                       << "1879.3831620103 0.0000000000"
                       << "2007.9375013022 0.0000000000"
                       << "2007.9570456000 34.0000000000"
                       << "2007.9765898979 3.0000000000"
                       << "2007.9962563479 0.0000000000"
                       << "2173.2127519882 0.0000000000"
                       << "2173.2330305731 52.0000000000"
                       << "2173.2533091582 3.0000000000";

    xyFormatStringListSize = xyFormatStringList.size();
    xyFormatString         = xyFormatStringList.join("\n");
    Trace theorCombinedTrace;
    theorCombinedTrace.initialize(xyFormatString);
    REQUIRE(theorCombinedTrace.size() == 7);

    xyFormatStringList.clear();

    // Skip 5 spectra on the top (massSpecIdx0).
    // A data point is in the range if key >= keyStart.
    double keyStart = 1861.61;

    // Skip 5 spectra on the bottom
    // A data point is in the range if key <= keyEnd.
    double keyEnd = 2173.26;

    // Finally actually test the combination function.

    Trace combinedTrace;
    combinedTrace.combine(traces, keyStart, keyEnd);
    combinedTrace.combine(keyList, valList, keyStart, keyEnd);

    // Check that both the theor and the actual combined spectra have the same
    // number of data point instances.

    REQUIRE(theorCombinedTrace.size() == combinedTrace.size());

    // Now test that the obained mass combined trace is identical to the
    // one computed apart.

    int failedComparisons = 0;

    for(int iter = 0; iter < combinedTrace.size(); ++iter)
      {
        double key = combinedTrace.at(iter)->key();
        double val = combinedTrace.at(iter)->val();

        double theorKey = theorCombinedTrace.at(iter)->key();
        double theorVal = theorCombinedTrace.at(iter)->val();

        // cout << std::setprecision(15)
        //<< "(" << key << "," << val << ") vs (" << theorKey << "," << theorVal
        //<< ")\n";

        // It is important to use the almostEqual() because otherwise we get
        // spurious messages.
        if(!msXpS::almostEqual(key, theorKey, 15))
          {
            cout << std::setprecision(15) << "failed comparison:"
                 << "test (" << key << " vs theor " << theorKey << "\n";

            ++failedComparisons;
          }

        if(!msXpS::almostEqual(val, theorVal, 15))
          {
            cout << std::setprecision(15) << "failed comparison:"
                 << "test (" << val << " vs theor " << theorVal << "\n";

            ++failedComparisons;
          }
      }

    REQUIRE(failedComparisons == 0);
  }

  delete massSpecIdx0;
  delete massSpecIdx1;
  delete massSpecIdx2;
  delete massSpecIdx3;
  delete massSpecIdx4;

  // At this point we have tested the un-binned combination into an empty mass
  // trace of a trace list containing five traces.
}


TEST_CASE("Various utility functions about traces.", "[Trace]")
{
  QStringList xyFormatStringList;
  xyFormatStringList << "694.5165997465 1500.0000000000"
                     << "694.5333557712 1332.0000000000"
                     << "694.5501119980 1410.0000000000"
                     << "694.5668684269 1363.0000000000"
                     << "694.5836250580 1424.0000000000"
                     << "694.6003818912 1503.0000000000"
                     << "694.6171389265 1341.0000000000"
                     << "694.6338961639 1089.0000000000"
                     << "694.6506536035 983.0000000000"
                     << "694.6674112451 999.0000000000"
                     << "694.6841690890 1043.0000000000"
                     << "694.7009271349 1015.0000000000"
                     << "694.7176853830 1057.0000000000"
                     << "694.7344438331 1140.0000000000"
                     << "694.7512024855 1284.0000000000"
                     << "694.7679613399 1479.0000000000"
                     << "694.7847203965 1661.0000000000"
                     << "694.8014796552 1703.0000000000"
                     << "694.8182391160 1541.0000000000"
                     << "694.8349987789 879.0000000000";

  REQUIRE(xyFormatStringList.size() == 20);
  QString xyFormatString = xyFormatStringList.join("\n");
  Trace trace;
  trace.initialize(xyFormatString);
  REQUIRE(xyFormatStringList.size() == trace.size());

  SECTION("Tell if a trace contains a given m/z value.", "[Trace]")
  {
    int index  = 10;
    double key = trace.at(index)->key();

    // 20170926 add one more decimal to 0.0000000000001 and it fails.
    REQUIRE(trace.contains(key + 0.0000000000001) < 0);
    REQUIRE(trace.contains(key) >= 0);
    REQUIRE(trace.contains(key) == 10);
  }

  SECTION("Provide the tracesl m/z data as a list of double values.", "[Trace]")
  {
    QList<double> keyList = trace.keyList();

    int failedComparisons = 0;

    for(int iter = 0; iter < trace.size(); ++iter)
      {
        double iterKey = trace.at(iter)->key();

        double listKey = keyList.at(iter);

        if(iterKey != listKey)
          ++failedComparisons;
      }

    REQUIRE(failedComparisons == 0);
  }

  SECTION("Provide the tracesl val data as a list of double values.", "[Trace]")
  {
    QList<double> valList = trace.valList();

    int failedComparisons = 0;

    for(int iter = 0; iter < trace.size(); ++iter)
      {
        double iterI = trace.at(iter)->val();

        double listI = valList.at(iter);

        if(iterI != listI)
          ++failedComparisons;
      }

    REQUIRE(failedComparisons == 0);
  }

  SECTION("Provide the tracesl m/z and val data as two lists of double values.",
          "[Trace]")
  {
    QList<double> keyList;
    QList<double> valList;

    trace.toLists(&keyList, &valList);

    int failedComparisons = 0;

    for(int iter = 0; iter < trace.size(); ++iter)
      {
        double iterKey = trace.at(iter)->key();
        double listKey = keyList.at(iter);

        double iterI = trace.at(iter)->val();
        double listI = valList.at(iter);

        if(iterKey != listKey)
          ++failedComparisons;
        if(iterI != listI)
          ++failedComparisons;
      }

    REQUIRE(failedComparisons == 0);
  }

  SECTION(
    "Provide the tracesl m/z and val data as two vectors of double values.",
    "[Trace]")
  {
    QList<double> keyList;
    QList<double> valList;

    // We have tested that this works correctly.
    trace.toLists(&keyList, &valList);

    QVector<double> keyVector;
    QVector<double> valVector;

    trace.toVectors(&keyVector, &valVector);

    int failedComparisons = 0;

    for(int iter = 0; iter < trace.size(); ++iter)
      {
        double iterKey   = trace.at(iter)->key();
        double vectorKey = keyVector.at(iter);

        double iterI   = trace.at(iter)->val();
        double vectorI = valVector.at(iter);

        if(iterKey != vectorKey)
          ++failedComparisons;
        if(iterI != vectorI)
          ++failedComparisons;
      }

    REQUIRE(failedComparisons == 0);
  }

  SECTION(
    "Provide the tracesl m/z and val data as a <m/z,i> map of double values.",
    "[Trace]")
  {
    QList<double> keyList;
    QList<double> valList;

    // We have tested that this works correctly.
    trace.toLists(&keyList, &valList);

    QMap<double, double> map;
    trace.toMap(&map);

    int failedComparisons = 0;

    QList<double> mapKeyList = map.keys();

    REQUIRE(mapKeyList.size() == keyList.size());

    for(int iter = 0; iter < trace.size(); ++iter)
      {
        double iterKey = trace.at(iter)->key();
        double mapKey  = mapKeyList.at(iter);

        double iterI = trace.at(iter)->val();
        double mapI  = map.value(mapKey);

        if(iterKey != mapKey)
          ++failedComparisons;
        if(iterI != mapI)
          ++failedComparisons;
      }

    REQUIRE(failedComparisons == 0);
  }

  SECTION(
    "Create a textual representation of a region of a mass "
    "trace (without key range).",
    "[Trace]")
  {
    QString text = trace.asText(-1, -1);
    REQUIRE(!text.isEmpty());

    Trace reconstructedTrace;
    reconstructedTrace.initialize(text);

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

    // Now perform a deep comparison of the spectra.
    int failedComparisons = 0;

    for(int iter = 0; iter < trace.size(); ++iter)
      {
        double iterKey        = trace.at(iter)->key();
        double reconstructKey = reconstructedTrace.at(iter)->key();

        double iterI        = trace.at(iter)->val();
        double reconstructI = reconstructedTrace.at(iter)->val();

        if(iterKey != reconstructKey)
          ++failedComparisons;
        if(iterI != reconstructI)
          ++failedComparisons;
      }

    REQUIRE(failedComparisons == 0);
  }

  SECTION(
    "Create a textual representation of a region of a mass "
    "trace (with key range).",
    "[Trace]")
  {

    // Now do the same with a key range. We know that Trace contains this:
    //
    //<< "694.5165997465 1500.0000000000"
    //<< "694.5333557712 1332.0000000000"
    //<< "694.5501119980 1410.0000000000"
    //<< "694.5668684269 1363.0000000000"
    //<< "694.5836250580 1424.0000000000"
    //<< "694.6003818912 1503.0000000000"
    //<< "694.6171389265 1341.0000000000"
    //<< "694.6338961639 1089.0000000000"
    //<< "694.6506536035 983.0000000000"
    //<< "694.6674112451 999.0000000000"
    //<< "694.6841690890 1043.0000000000"
    //<< "694.7009271349 1015.0000000000"
    //<< "694.7176853830 1057.0000000000"
    //<< "694.7344438331 1140.0000000000"
    //<< "694.7512024855 1284.0000000000"
    //<< "694.7679613399 1479.0000000000"
    //<< "694.7847203965 1661.0000000000"
    //<< "694.8014796552 1703.0000000000"
    //<< "694.8182391160 1541.0000000000"
    //<< "694.8349987789 879.0000000000" ;

    // Skip the 5 top-most and bottom-most peaks.
    QString text = trace.asText(5, 14);
    REQUIRE(!text.isEmpty());

    Trace reconstructedTrace;
    reconstructedTrace.initialize(text);

    // The reconstructed trace has 10 peaks less than the original
    // one.
    REQUIRE(trace.size() == reconstructedTrace.size() + 10);

    // Now perform a deep comparison of the spectra. Note the dephasing during
    // the iteration in both spectra.
    int failedComparisons = 0;

    for(int iter = 0; iter < reconstructedTrace.size(); ++iter)
      {
        double iterKey        = trace.at(iter + 5)->key();
        double reconstructKey = reconstructedTrace.at(iter)->key();

        double iterI        = trace.at(iter + 5)->val();
        double reconstructI = reconstructedTrace.at(iter)->val();

        if(iterKey != reconstructKey)
          ++failedComparisons;
        if(iterI != reconstructI)
          ++failedComparisons;
      }

    REQUIRE(failedComparisons == 0);
  }
}
// End of
// TEST_CASE("Various utility functions about traces.", "[Trace]")


TEST_CASE("Create trace from base64-encoded data in byte arrays", "[Trace]")
{
  // Prepare the byte arrays from reading data from specific test files.

  QFile key64File(
    QString(
      "%1/base64/protein-mobility-waters-synapt-hdms.db-index-47-mzlist.base64")
      .arg(TEST_DATA_DIR));
  REQUIRE(key64File.open(QFile::ReadOnly));

  QTextStream key64Stream(&key64File);
  QByteArray key64ByteArray    = key64Stream.readAll().toLatin1();
  QByteArray keyDec64ByteArray = QByteArray::fromBase64(key64ByteArray);

  QFile i64File(
    QString(
      "%1/base64/protein-mobility-waters-synapt-hdms.db-index-47-ilist.base64")
      .arg(TEST_DATA_DIR));
  REQUIRE(i64File.open(QFile::ReadOnly));
  QTextStream i64Stream(&i64File);
  QByteArray i64ByteArray      = i64Stream.readAll().toLatin1();
  QByteArray valDec64ByteArray = QByteArray::fromBase64(i64ByteArray);

  // We know that the files correspond to zlib-compressed data, so the size of
  // the encoded byte arrays can be different (and must in this specific
  // case).
  REQUIRE(keyDec64ByteArray.size() != valDec64ByteArray.size());

  // As per Qt documentation if decompressing zlib-compressed data from
  // outside of Qt, then we have to prepend 4 bytes to the arrays before
  // decompression.
  QByteArray header("aaaa");
  keyDec64ByteArray.prepend(header);
  valDec64ByteArray.prepend(header);

  QByteArray finalKeyArray = qUncompress(keyDec64ByteArray);
  QByteArray finalIArray   = qUncompress(valDec64ByteArray);

  // Now , yes, after decompression, the number of key items needs to be
  // identical to the number of val items.
  REQUIRE(finalKeyArray.size() == finalIArray.size());

  Trace trace;
  trace.initialize(&key64ByteArray,
                   &i64ByteArray,
                   msXpS::DataCompression::DATA_COMPRESSION_ZLIB);

  // This one contains both key and val lists in the xy format.
  QFile xyFile(
    QString("%1/xy/protein-mobility-waters-synapt-hdms.db-spectrum-index-47.xy")
      .arg(TEST_DATA_DIR));
  REQUIRE(xyFile.open(QFile::ReadOnly));
  QTextStream xyStream(&xyFile);
  QString xyText = xyStream.readAll();

  Trace testtrace;
  testtrace.initialize(xyText);

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

  int failedComparisons = 0;

  for(int iter = 0; iter < testtrace.size(); ++iter)
    {
      double key     = trace.at(iter)->key();
      double testKey = testtrace.at(iter)->key();

      double val     = trace.at(iter)->val();
      double testVal = testtrace.at(iter)->val();

      if(abs(key - testKey) > 0.0000000001)
        // if(!msXpS::almostEqual(key, testKey, 10))
        {
          cout << std::setprecision(10) << key << " vs " << testKey
               << "\n"
                  "\t\t difference:"
               << key - testKey << "\n";

          ++failedComparisons;
        }

      // if(!msXpS::almostEqual(val, testval, 10))
      //++failedComparisons;
    }

  REQUIRE(failedComparisons == 0);
}
// End of
// TEST_CASE("Create trace from base64-encoded data in byte arrays.", "[Trace]")


TEST_CASE(
  "Create trace from base64-encoded data in "
  "byte arrays (with key range).",
  "[Trace]")
{
  // Prepare the byte arrays from reading data from specific test files.

  QFile key64File(QString("%1/base64/"
                          "protein-mobility-waters-synapt-hdms.db-index-47-mz-"
                          "2000-2400-mzlist.base64")
                    .arg(TEST_DATA_DIR));
  REQUIRE(key64File.open(QFile::ReadOnly));
  QTextStream key64Stream(&key64File);
  QByteArray key64ByteArray    = key64Stream.readAll().toLatin1();
  QByteArray keyDec64ByteArray = QByteArray::fromBase64(key64ByteArray);

  QFile i64File(QString("%1/base64/"
                        "protein-mobility-waters-synapt-hdms.db-index-47-mz-"
                        "2000-2400-ilist.base64")
                  .arg(TEST_DATA_DIR));
  REQUIRE(i64File.open(QFile::ReadOnly));
  QTextStream i64Stream(&i64File);
  QByteArray i64ByteArray      = i64Stream.readAll().toLatin1();
  QByteArray valDec64ByteArray = QByteArray::fromBase64(i64ByteArray);

  // We know that the files correspond to zlib-compressed data, so the size of
  // the encoded byte arrays can be different (and must in this specific
  // case).
  REQUIRE(keyDec64ByteArray.size() != valDec64ByteArray.size());

  // As per Qt documentation is decompressing zlib-compressed data from
  // outside of Qt, then we have to prepend 4 bytes to the arrays before
  // decompression.
  QByteArray header("aaaa");
  keyDec64ByteArray.prepend(header);
  valDec64ByteArray.prepend(header);

  QByteArray finalKeyArray = qUncompress(keyDec64ByteArray);
  QByteArray finalIArray   = qUncompress(valDec64ByteArray);

  // Now , yes, after decompression, the number of key items needs to be
  // identical to the number of val items.
  REQUIRE(finalKeyArray.size() == finalIArray.size());

  Trace trace;
  trace.initialize(&key64ByteArray,
                   &i64ByteArray,
                   msXpS::DataCompression::DATA_COMPRESSION_ZLIB,
                   2000,
                   2400);

  // This one contains both key and val lists in the xy format.
  QFile xyFile(QString("%1/xy/"
                       "protein-mobility-waters-synapt-hdms.db-spectrum-index-"
                       "47-mz-2000-2400.xy")
                 .arg(TEST_DATA_DIR));
  REQUIRE(xyFile.open(QFile::ReadOnly));
  QTextStream xyStream(&xyFile);
  QString xyText = xyStream.readAll();

  Trace testtrace;
  testtrace.initialize(xyText);

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

  int failedComparisons = 0;

  for(int iter = 0; iter < testtrace.size(); ++iter)
    {
      double key     = trace.at(iter)->key();
      double testKey = testtrace.at(iter)->key();

      double val     = trace.at(iter)->val();
      double testVal = testtrace.at(iter)->val();

      if(abs(key - testKey) > 0.0000000001)
        // if(!msXpS::almostEqual(key, testKey, 10))
        {
          cout << std::setprecision(10) << key << " vs " << testKey
               << "\n"
                  "\t\t difference:"
               << key - testKey << "\n";

          ++failedComparisons;
        }

      // if(!msXpS::almostEqual(val, testval, 10))
      //++failedComparisons;
    }

  REQUIRE(failedComparisons == 0);
}


// TEST_CASE("", "[Trace]")
//{

//}

} // namespace msXpSlibmass
