// --------------------------------------------------------------------------
//                   OpenMS -- Open-Source Mass Spectrometry               
// --------------------------------------------------------------------------
// Copyright The OpenMS Team -- Eberhard Karls University Tuebingen,
// ETH Zurich, and Freie Universitaet Berlin 2002-2020.
// 
// This software is released under a three-clause BSD license:
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of any author or any participating institution 
//    may be used to endorse or promote products derived from this software 
//    without specific prior written permission.
// For a full list of authors, refer to the file AUTHORS. 
// --------------------------------------------------------------------------
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL ANY OF THE AUTHORS OR THE CONTRIBUTING 
// INSTITUTIONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// --------------------------------------------------------------------------
// $Maintainer: Hannes Roest $
// $Authors: Hannes Roest $
// --------------------------------------------------------------------------

///////////////////////////
#include <OpenMS/ANALYSIS/OPENSWATH/MasstraceCorrelator.h>
///////////////////////////

#include <OpenMS/test_config.h>
#include <OpenMS/CONCEPT/ClassTest.h>
#include <OpenMS/OPENSWATHALGO/ALGO/MRMScoring.h>
#include <OpenMS/FORMAT/ConsensusXMLFile.h>

using namespace OpenMS;
using namespace std;

class MasstraceCorrelator_facade : MasstraceCorrelator
{
  public:

    void matchMassTraces_(const MasstracePointsType& hull_points1, const MasstracePointsType& hull_points2,
        std::vector<double>& vec1, std::vector<double>& vec2, double mindiff,
        double padEnds = true)
    {
      MasstraceCorrelator::matchMassTraces_(hull_points1, hull_points2, vec1, vec2, mindiff, padEnds);
    }

};
START_TEST(CorrelateMasstraces, "$Id$")

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////

START_SECTION((virtual void matchMassTraces_()))
{

  MasstraceCorrelator_facade mtcorr;
  static const double arr1[] = {0,1,3,5,2,0};
  static const double arr2[] = {1,3,5,2,0,0};
  static const double arr3[] = {0,1,2,3,4,5};
  static const double arr4[] = {0,1,2,4,5,6};
  std::vector<double> data1 (arr1, arr1 + sizeof(arr1) / sizeof(arr1[0]) );
  std::vector<double> data2 (arr2, arr2 + sizeof(arr2) / sizeof(arr2[0]) );
  std::vector<double> rt1 (arr3, arr3 + sizeof(arr3) / sizeof(arr3[0]) );
  std::vector<double> rt2 (arr4, arr4 + sizeof(arr4) / sizeof(arr4[0]) );

  std::vector<double> vec1;
  std::vector<double> vec2;

  std::vector<std::pair<double, double> > data1_2d;
  std::vector<std::pair<double, double> > data2_2d;

  data1_2d.clear();
  data2_2d.clear();
  for(Size i = 0; i < data1.size(); i++)
  {
    data1_2d.push_back(std::make_pair(rt1[i], data1[i]) );
    data2_2d.push_back(std::make_pair(rt2[i], data2[i]) );
  }

  vec1.clear(); vec2.clear();
  mtcorr.matchMassTraces_(data1_2d, data2_2d, vec1, vec2, 0.1);

  TEST_EQUAL(vec1.size(), 7);
  TEST_EQUAL(vec2.size(), 7);

  TEST_EQUAL(vec1[0], 0);
  TEST_EQUAL(vec1[1], 1);
  TEST_EQUAL(vec1[2], 3);
  TEST_EQUAL(vec1[3], 5);
  TEST_EQUAL(vec1[4], 2);
  TEST_EQUAL(vec1[5], 0);
  TEST_EQUAL(vec1[6], 0);

  TEST_EQUAL(vec2[0], 1);
  TEST_EQUAL(vec2[1], 3);
  TEST_EQUAL(vec2[2], 5);
  TEST_EQUAL(vec2[3], 0);
  TEST_EQUAL(vec2[4], 2);
  TEST_EQUAL(vec2[5], 0);
  TEST_EQUAL(vec2[6], 0);

  vec1.clear(); vec2.clear();
  mtcorr.matchMassTraces_(data2_2d, data1_2d, vec2, vec1, 0.1);

  TEST_EQUAL(vec1.size(), 7);
  TEST_EQUAL(vec2.size(), 7);

  TEST_EQUAL(vec1[0], 0);
  TEST_EQUAL(vec1[1], 1);
  TEST_EQUAL(vec1[2], 3);
  TEST_EQUAL(vec1[3], 5);
  TEST_EQUAL(vec1[4], 2);
  TEST_EQUAL(vec1[5], 0);
  TEST_EQUAL(vec1[6], 0);

  TEST_EQUAL(vec2[0], 1);
  TEST_EQUAL(vec2[1], 3);
  TEST_EQUAL(vec2[2], 5);
  TEST_EQUAL(vec2[3], 0);
  TEST_EQUAL(vec2[4], 2);
  TEST_EQUAL(vec2[5], 0);
  TEST_EQUAL(vec2[6], 0);

  vec1.clear(); vec2.clear();
  mtcorr.matchMassTraces_(data1_2d, data2_2d, vec1, vec2, 1.5);

  TEST_EQUAL(vec1.size(), 6);
  TEST_EQUAL(vec2.size(), 6);

  TEST_EQUAL(vec1[0], 0);
  TEST_EQUAL(vec1[1], 1);
  TEST_EQUAL(vec1[2], 3);
  TEST_EQUAL(vec1[3], 5);
  TEST_EQUAL(vec1[4], 2);
  TEST_EQUAL(vec1[5], 0);

  TEST_EQUAL(vec2[0], 1);
  TEST_EQUAL(vec2[1], 3);
  TEST_EQUAL(vec2[2], 5);
  TEST_EQUAL(vec2[3], 2);
  TEST_EQUAL(vec2[4], 0);
  TEST_EQUAL(vec2[5], 0);

}
END_SECTION

START_SECTION((virtual void match_elution_arrays_no_padding()))
{

  MasstraceCorrelator_facade mtcorr;
  static const double arr1[] = {0,1,3,5,2,0};
  static const double arr2[] = {1,3,5,2,0,0};
  static const double arr3[] = {0,1,2,3,4,5};
  static const double arr4[] = {-1,1,2,4,5,6};
  std::vector<double> data1 (arr1, arr1 + sizeof(arr1) / sizeof(arr1[0]) );
  std::vector<double> data2 (arr2, arr2 + sizeof(arr2) / sizeof(arr2[0]) );
  std::vector<double> rt1 (arr3, arr3 + sizeof(arr3) / sizeof(arr3[0]) );
  std::vector<double> rt2 (arr4, arr4 + sizeof(arr4) / sizeof(arr4[0]) );

  std::vector<double> vec1;
  std::vector<double> vec2;

  std::vector<std::pair<double, double> > data1_2d;
  std::vector<std::pair<double, double> > data2_2d;

  data1_2d.clear();
  data2_2d.clear();
  for(Size i = 0; i < data1.size(); i++)
  {
    data1_2d.push_back(std::make_pair(rt1[i], data1[i]) );
    data2_2d.push_back(std::make_pair(rt2[i], data2[i]) );
  }

  vec1.clear(); vec2.clear();
  TEST_EQUAL(vec1.size(), 0);
  TEST_EQUAL(vec2.size(), 0);
  // if we do not pad the ends, this means that we do not add zeros to the first vector that is shorter in RT
  bool pad_ends = false;
  mtcorr.matchMassTraces_(data1_2d, data2_2d, vec1, vec2, 0.1, pad_ends);

  TEST_EQUAL(vec1.size(), 5);
  TEST_EQUAL(vec2.size(), 5);

  TEST_EQUAL(vec1[0], 1);
  TEST_EQUAL(vec1[1], 3);
  TEST_EQUAL(vec1[2], 5);
  TEST_EQUAL(vec1[3], 2);
  TEST_EQUAL(vec1[4], 0);

  TEST_EQUAL(vec2[0], 3);
  TEST_EQUAL(vec2[1], 5);
  TEST_EQUAL(vec2[2], 0);
  TEST_EQUAL(vec2[3], 2);
  TEST_EQUAL(vec2[4], 0);

  vec1.clear(); vec2.clear();
  TEST_EQUAL(vec1.size(), 0);
  TEST_EQUAL(vec2.size(), 0);
  // if we do pad the ends, this means that we do add zeros to the first vector that is shorter in RT
  mtcorr.matchMassTraces_(data1_2d, data2_2d, vec1, vec2, 0.1, true);

  TEST_EQUAL(vec1.size(), 8);
  TEST_EQUAL(vec2.size(), 8);

  TEST_EQUAL(vec1[0], 0); // -1 
  TEST_EQUAL(vec1[1], 0); // 0  
  TEST_EQUAL(vec1[2], 1); // 1  
  TEST_EQUAL(vec1[3], 3); // 2  
  TEST_EQUAL(vec1[4], 5); // 3  
  TEST_EQUAL(vec1[5], 2); // 4  
  TEST_EQUAL(vec1[6], 0); // 5  
  TEST_EQUAL(vec1[7], 0); // 6  

  TEST_EQUAL(vec2[0], 1); // -1
  TEST_EQUAL(vec2[1], 0); // 0
  TEST_EQUAL(vec2[2], 3); // 1
  TEST_EQUAL(vec2[3], 5); // 2
  TEST_EQUAL(vec2[4], 0); // 3
  TEST_EQUAL(vec2[5], 2); // 4
  TEST_EQUAL(vec2[6], 0); // 5
  TEST_EQUAL(vec2[7], 0); // 6

}
END_SECTION

START_SECTION((virtual void scoreHullpoints()))
{

  OpenMS::MasstraceCorrelator mtcorr;
  static const double arr1[] = {0,1,3,5,2,0};
  static const double arr2[] = {1,3,5,2,0,0};
  static const double arr3[] = {0,1,2,3,4,5};
  static const double arr4[] = {0,1,2,4,5,6};
  std::vector<double> data1 (arr1, arr1 + sizeof(arr1) / sizeof(arr1[0]) );
  std::vector<double> data2 (arr2, arr2 + sizeof(arr2) / sizeof(arr2[0]) );
  std::vector<double> rt1 (arr3, arr3 + sizeof(arr3) / sizeof(arr3[0]) );
  std::vector<double> rt2 (arr4, arr4 + sizeof(arr4) / sizeof(arr4[0]) );

  std::vector<std::pair<double, double> > data1_2d;
  std::vector<std::pair<double, double> > data2_2d;

  OpenSwath::Scoring::standardize_data(data1);
  OpenSwath::Scoring::standardize_data(data2);

  for(Size i = 0; i < data1.size(); i++)
  {
    data1_2d.push_back( std::make_pair(rt1[i], data1[i]) );
    data2_2d.push_back( std::make_pair(rt1[i], data2[i]) );
  }

  OpenSwath::Scoring::XCorrArrayType result = OpenSwath::Scoring::calculateCrossCorrelation(data1, data2, 2, 1);
  for(OpenSwath::Scoring::XCorrArrayType::iterator it = result.begin(); it != result.end(); ++it)
  {
    it->second = it->second / 6.0;
  }

  TEST_EQUAL (result.data[0].first, -2);
  TEST_EQUAL (result.data[1].first, -1);
  TEST_EQUAL (result.data[2].first, 0);
  TEST_EQUAL (result.data[3].first, 1);
  TEST_EQUAL (result.data[4].first, 2);

  TEST_REAL_SIMILAR (result.data[4].second, -0.7374631);   // .find( 2)->
  TEST_REAL_SIMILAR (result.data[3].second, -0.567846);    // .find( 1)->
  TEST_REAL_SIMILAR (result.data[2].second,  0.4159292);   // .find( 0)->
  TEST_REAL_SIMILAR (result.data[1].second,  0.8215339);   // .find(-1)->
  TEST_REAL_SIMILAR (result.data[0].second,  0.15634218);  // .find(-2)->

  double min_pearson_score = -1.1; int maxlag = data1_2d.size();
  int lag; double lag_intensity; double pearson_score;
  mtcorr.scoreHullpoints(data1_2d, data2_2d, lag, lag_intensity, pearson_score, min_pearson_score, maxlag);
  TEST_EQUAL(lag, -1);
  TEST_REAL_SIMILAR(lag_intensity, 0.821534);
  TEST_REAL_SIMILAR(pearson_score, 0.41593);

  // now we use different RT data for the 2nd data array
  data1_2d.clear();
  data2_2d.clear();
  for(Size i = 0; i < data1.size(); i++)
  {
    data1_2d.push_back( std::make_pair(rt1[i], data1[i]) );
    data2_2d.push_back( std::make_pair(rt2[i], data2[i]) );
  }

  // if we allow for an RT difference of more than 1, we should get the same result as above
  mtcorr.scoreHullpoints(data1_2d, data2_2d, lag, lag_intensity, pearson_score, min_pearson_score, maxlag, 1.5);
  TEST_EQUAL(lag, -1);
  TEST_REAL_SIMILAR(lag_intensity, 0.821534);
  TEST_REAL_SIMILAR(pearson_score, 0.41593);

  // if the allowed difference in RT is less than 1, the algorithm will substitute zeros
  mtcorr.scoreHullpoints(data1_2d, data2_2d, lag, lag_intensity, pearson_score, min_pearson_score, maxlag, 0.1);
  TEST_EQUAL(lag, -1);
  TEST_REAL_SIMILAR(lag_intensity, 0.625368);
  TEST_REAL_SIMILAR(pearson_score, 0.405604);

}
END_SECTION

START_SECTION((virtual void createPseudoSpectra()))
{

  ConsensusMap masstraces;
  ConsensusXMLFile().load(OPENMS_GET_TEST_DATA_PATH("Masstraces_Testdata.consensusXML"), masstraces);

  MSExperiment pseudo_spectra;
  masstraces.sortByIntensity(true);
  OpenMS::MasstraceCorrelator mtcorr;
  mtcorr.createPseudoSpectra(masstraces, pseudo_spectra, 0, 0.7, 1, 3);

  TEST_EQUAL(pseudo_spectra.size(), 3);
  TEST_EQUAL(pseudo_spectra[1].size(), 1);
  TEST_REAL_SIMILAR(pseudo_spectra[1].getRT(), 4203.0);
  TEST_REAL_SIMILAR(pseudo_spectra[1][0].getMZ(), 668.5);

  pseudo_spectra.clear(true);
  mtcorr.createPseudoSpectra(masstraces, pseudo_spectra, 1, 0.7, 1, 3);
  TEST_EQUAL(pseudo_spectra.size(), 2);

  TEST_EQUAL(pseudo_spectra[0].size(), 2);
  TEST_EQUAL(pseudo_spectra[1].size(), 2);

  TEST_REAL_SIMILAR(pseudo_spectra[0].getRT(), 5201.0);
  TEST_REAL_SIMILAR(pseudo_spectra[0][0].getMZ(), 568.5);

  TEST_REAL_SIMILAR(pseudo_spectra[1].getRT(), 5203.0);
  TEST_REAL_SIMILAR(pseudo_spectra[1][0].getMZ(), 768.5);

}
END_SECTION

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
END_TEST



