/*********************************************************
** Copyright (c) 2005
** University of Washington
** Licensed under the terms set forth by University of
** Washington. If you did not sign such a license, you
** are using this software/code illegally and you do not
** have permission to use, modify, or redistribute
** this or any files in this software package.
**
** File: DataReaderTest.h
**
**********************************************************/
#ifndef __DATAREADERTEST__
#define __DATAREADERTEST__

#include "DataIO.h"
#include "Cluster.h"
#include "VectorTestClasses.h"

#include <iostream>
#include <sstream>
#include <fstream>
#include "VectorDataFunctions.h"

using namespace std;


class CDataReaderTest : public CUnitTestClass
{
 public:
  CDataReaderTest() : CUnitTestClass("DataReaderTest")
  {
  }

 protected:
  void RunTests()
  {
    TestDataReaderDouble();
    TestDataReaderVector();
    TestReadFile();
    TestCluster();
  }

 private:
  static string sData; 
  static string sClusteredData; 
      
  void TestDataReaderDouble()
  {
    BeginTest("TestDataReaderDouble");
    bool success = true;

    try
    {
      static int declaredRowCount = 7;
      static int declaredRowLength = 8;

      int* ppDeclaredData[declaredRowCount]; // seven rows of intacter arrays
      int line0[] = { 0, 0, 0, 0, 1, 1, 0, 0 };
      int line1[] = { 0, 0, 1, 1, 0, 0, 0, 0 };
      int line2[] = { 0, 0, 0, 0, 0, 1, 1, 0 };
      int line3[] = { 0, 1, 1, 0, 0, 0, 0, 0 };
      int line4[] = { 0, 0, 0, 1, 1, 0, 0, 0 };
      int line5[] = { 1, 1, 0, 0, 0, 0, 0, 0 };
      int line6[] = { 0, 0, 0, 0, 0, 0, 1, 1 };

      ppDeclaredData[0] = line0;
      ppDeclaredData[1] = line1;
      ppDeclaredData[2] = line2;
      ppDeclaredData[3] = line3;
      ppDeclaredData[4] = line4;
      ppDeclaredData[5] = line5;
      ppDeclaredData[6] = line6;

      // read in sData
      std::stringstream sstr(stringstream::in | stringstream::out);
      sstr << sData;

      CDelimitedMatrixReader reader;
      int rowCount;
      int rowLength;
      double** ppData;

      reader.Read(sstr, &rowCount, &rowLength, &ppData);
						
      // compare result to expected ppDeclaredData
      if(declaredRowCount != rowCount)
	{
	  cout << "declared row count = " << declaredRowCount 
	    << ", actual row count = " << rowCount << "\n";
	  __throw_message("row count does not match declared");
	}

      if(declaredRowLength != rowLength)
	{
	  cout << "declared row length = " << declaredRowLength
	    << ", actual row length = " << rowLength << "\n";
	  __throw_message("row length does not match declared");
	}

      for(int r = 0; r < rowCount; r++)
	{
	  for(int i = 0; i < rowLength; i++)
	    {
	      if (ppData[r][i] != ppDeclaredData[r][i])
		{
		  cout << "declared data ["  << r << ", " << i << "] = " 
		       << ppDeclaredData[r][i]  << "\n";
		  cout << "actual data ["  << r << ", " << i << "] = " 
		       << ppData[r][i]  << "\n";
		  __throw_message("declared and actual data don't match");
		}
	    }
	}
    }
    catch(CTestException *pEx)
    {
      success = false;
      cout << pEx->Message() << "\n";
      delete pEx;
    }
    catch(...)
    {
      success = false;
      cout << "something's wrong\n";
    }
    
    EndTest(success);
  }

  void TestDataReaderVector()
  {
    BeginTest("TestDataReaderDouble");
    bool success = true;

    int rowCount;
    int rowLength;
    TDoubleVector** ppData = NULL;

    try
    {
      static int declaredRowCount = 7;
      static int declaredRowLength = 8;

      int* ppDeclaredData[declaredRowCount]; // seven rows of intacter arrays
      int line0[] = { 0, 0, 0, 0, 1, 1, 0, 0 };
      int line1[] = { 0, 0, 1, 1, 0, 0, 0, 0 };
      int line2[] = { 0, 0, 0, 0, 0, 1, 1, 0 };
      int line3[] = { 0, 1, 1, 0, 0, 0, 0, 0 };
      int line4[] = { 0, 0, 0, 1, 1, 0, 0, 0 };
      int line5[] = { 1, 1, 0, 0, 0, 0, 0, 0 };
      int line6[] = { 0, 0, 0, 0, 0, 0, 1, 1 };

      ppDeclaredData[0] = line0;
      ppDeclaredData[1] = line1;
      ppDeclaredData[2] = line2;
      ppDeclaredData[3] = line3;
      ppDeclaredData[4] = line4;
      ppDeclaredData[5] = line5;
      ppDeclaredData[6] = line6;

      // read in sData
      std::stringstream sstr(stringstream::in | stringstream::out);
      sstr << sData;

      CDelimitedMatrixReader reader;

      reader.Read(sstr, &rowCount, &rowLength, &ppData);
						
      // compare result to expected ppDeclaredData
      if(declaredRowCount != rowCount)
	{
	  cout << "declared row count = " << declaredRowCount 
	    << ", actual row count = " << rowCount << "\n";
	  __throw_message("row count does not match declared");
	}

      if(declaredRowLength != rowLength)
	{
	  cout << "declared row length = " << declaredRowLength
	    << ", actual row length = " << rowLength << "\n";
	  __throw_message("row length does not match declared");
	}

      for(int r = 0; r < rowCount; r++)
	{
	  vector<double>::iterator iIter = ppData[r]->begin();
	  for(int i = 0; iIter != ppData[r]->end();iIter++, i++)
	    {
	      if (*iIter != ppDeclaredData[r][i])
		{
		  cout << "declared data ["  << r << ", " << i << "] = " 
		       << ppDeclaredData[r][i]  << "\n";
		  cout << "actual data ["  << r << ", " << i << "] = " 
		       << *iIter  << "\n";
		  __throw_message("declared and actual data don't match");
		}
	    }
	}
    }
    catch(CTestException *pEx)
    {
      success = false;
      cout << pEx->Message() << "\n";
      delete pEx;
    }
    catch(...)
    {
      success = false;
      cout << "something's wrong\n";
    }
    
    if (NULL != ppData)
      {
	for(int i = 0; i < rowCount; i++)
	  {
	    delete ppData[i];
	  }
	delete ppData;
      }
    EndTest(success);
  }

  void TestReadFile()
  {
    BeginTest("TestReadFile");
    bool success = true;

    int rowCount;
    int rowLength;
    TDoubleVector** ppData = NULL;

    try
    {
      static int declaredRowCount = 2;
      static int declaredRowLength = 112;

      // read in sData (TODO: make pathname dependent on current working directory)
      std::fstream fstr("./iotest.txt", fstream::in);

      CDelimitedMatrixReader reader;

      reader.Read(fstr, &rowCount, &rowLength, &ppData);
						
      // compare result to expected ppDeclaredData
      if(declaredRowCount != rowCount)
	{
	  cout << "declared row count = " << declaredRowCount 
	    << ", actual row count = " << rowCount << "\n";
	  __throw_message("row count does not match declared");
	}

      if(declaredRowLength != rowLength)
	{
	  cout << "declared row length = " << declaredRowLength
	    << ", actual row length = " << rowLength << "\n";
	  __throw_message("row length does not match declared");
	}

      for(int r = 0; r < rowCount; r++)
	{
	  TDoubleVector* v = ppData[r];
	  if(v->size() != declaredRowLength)
	    __throw_message("a row length does not match declared");
	}
    }
    catch(CTestException *pEx)
    {
      success = false;
      cout << pEx->Message() << "\n";
      delete pEx;
    }
    catch(...)
    {
      success = false;
      cout << "something's wrong\n";
    }
    
    if (NULL != ppData)
      {
	for(int i = 0; i < rowCount; i++)
	  {
	    delete ppData[i];
	  }
	delete ppData;
      }
    EndTest(success);
  }

  void TestCluster()
  {
    BeginTest("TestCluster");
    bool success = true;

    TDoubleVector** pDataVectors = NULL;
    int rowCount, rowLength;

    try
    {
      CAddVectors combine;
      CDelimitedMatrixReader reader;
      CClusterNode* pClusterRoot;

      // read in sData
      stringstream sstr(sData);
      if (!sstr.good())
	__throw_message("stringstream is no longer good");
      reader.Read(sstr, &rowCount, &rowLength, &pDataVectors);
      if (!sstr.good())
	sstr.clear();

      if (rowCount != 7)
	__throw_message("did not get expected row count");
      if (rowLength != 8)
	__throw_message("did not get expected row length");

      CCorrelation distance(rowCount);
      CBasicCluster clusterer(&distance, &combine);
      
      // cluster it, using ?? distance function
      pClusterRoot = clusterer.Cluster((void**)pDataVectors, rowCount);
      COptimalOrdering ordering;
      ordering.OrderLeaves(pClusterRoot, clusterer.GetDistanceMatrix());

      // write it out
      sstr.str("");
      if (!sstr.good())
	__throw_message("stringstream is no longer good");

      CVectorToStream display(&sstr);
      pClusterRoot->WalkLeaves(&display);
      string sResult = sstr.str();

      // compare it with an expected matrix
      if (sResult.compare(sClusteredData) != 0)
	{
	  cout << "\nexpected:\n" << sClusteredData;
	  cout << "\nactual:\n" << sResult;
	  __throw_message("declared clustered data does not match");
	}

    }
    catch(CTestException *pEx)
    {
      success = false;
      cout << pEx->Message() << "\n";
      delete pEx;
    }
    catch(...)
    {
      success = false;
      cout << "something's wrong\n";
    }
    
    if (NULL != pDataVectors)
      {
	for(int i = 0; i < rowCount; i++)
	  {
	    delete pDataVectors[i];
	  }
      }

    EndTest(success);
  }


};

string CDataReaderTest::sData = 
  /*  "7	8\n"*/							/*centroid*/
  "0	0	0	0	1	1	0	0\n"	//4.5
  "0	0	1	1	0	0	0	0\n"	//2.5
  "0	0	0	0	0	1	1	0\n"	//5.5
  "0	1	1	0	0	0	0	0\n"	//1.5
  "0	0	0	1	1	0	0	0\n"	//3.5
  "1	1	0	0	0	0	0	0\n"	//0.5
  "0	0	0	0	0	0	1	1\n";	//6.5



string CDataReaderTest::sClusteredData = 
  "1	1	0	0	0	0	0	0	\n"
  "0	1	1	0	0	0	0	0	\n"
  "0	0	1	1	0	0	0	0	\n"
  "0	0	0	1	1	0	0	0	\n"
  "0	0	0	0	1	1	0	0	\n"
  "0	0	0	0	0	1	1	0	\n"
  "0	0	0	0	0	0	1	1	\n";

#endif
