// file: $isip_ifc/class/algo/CorrelationDimension/cdim_02.cc
// version: $Id: cdim_02.cc 10608 2006-08-20 23:09:34Z ss754 $
//

// isip include files
//
#include "CorrelationDimension.h"
#include <CorrelationIntegral.h>
#include <Rps.h>
#include <Generator.h>

// method: diagnose
//
// arguments:
//  Integral::DEBUG level: (input) debug level for diagnostics
//
// return: a boolean value indicating status
//
boolean CorrelationDimension::diagnose(Integral::DEBUG level_a) {

  //---------------------------------------------------------------------------
  //
  // 0. preliminaries
  //
  //---------------------------------------------------------------------------

  // output the class name
  //
  if (level_a > Integral::NONE) {
    SysString output(L"diagnosing class ");
    output.concat(CLASS_NAME);
    output.concat(L": ");
    Console::put(output);
    Console::increaseIndention();
  }

  float max_percent = (float)1.5;
  
  //--------------------------------------------------------------------------
  //
  // 1. required public methods
  //     class constructors
  //
  //--------------------------------------------------------------------------

  // set indentation
  //  
  if (level_a > Integral::NONE) {
    Console::put(L"testing required public methods: class constructors...\n");
    Console::increaseIndention();
  }
  
  CorrelationDimension cor0;
  cor0.setAlgorithm(NORMAL);
  cor0.setImplementation(BACKWARD);
  cor0.setScaling(LINSCALE);
  CorrelationDimension cor1(cor0);
  
  if (!cor1.eq(cor0)) {
    return Error::handle(name(), L"copy constructor/eq", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test large allocation construction and deletion
  //
  if (level_a == Integral::ALL) {
    
    Console::put(L"\ntesting large chunk memory allocation and deletion:\n");
    
    // set the memory to a strange block size so we can hopefully catch any
    // frame overrun errors
    //
    
    CorrelationDimension::setGrowSize((long)500);
    
    CorrelationDimension* pcor = new CorrelationDimension();

    for (long j = 1; j <= 100; j++) {
      CorrelationDimension** pcors = new CorrelationDimension*[j * 100];
      
      // create the objects
      //
    for (long i = 0; i < j * 100; i++) {
      pcors[i] = new CorrelationDimension();
    }
    
    // delete objects
    //
    for (long i = (j * 100) - 1; i >= 0; i--) {
      delete pcors[i];
    }
    
    delete [] pcors;
    } 
    
    delete pcor;
  }

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
 
  
  //---------------------------------------------------------------------------
  //
  // 2. required public methods
  //     i/o methods 
  //
  //---------------------------------------------------------------------------

  // set indentation
  //  
  if (level_a > Integral::NONE) {
    Console::put(L"testing required public methods: io methods...\n");
    Console::increaseIndention();
  }

  // declare a reference object
  //
  cor0.setAlgorithm(NORMAL);
  cor0.setImplementation(BACKWARD);
  cor0.setScaling(LINSCALE);
 
  // we need binary and text sof files
  //
  String tmp_filename0;
  Integral::makeTemp(tmp_filename0);
  String tmp_filename1;
  Integral::makeTemp(tmp_filename1);

  // open files in write mode
  //
  Sof tmp_file0;
  tmp_file0.open(tmp_filename0, File::WRITE_ONLY, File::TEXT);
  Sof tmp_file1;
  tmp_file1.open(tmp_filename1, File::WRITE_ONLY, File::BINARY);

  cor0.write(tmp_file0, (long)0);
  cor0.write(tmp_file1, (long)0);
  
  // close the files
  //
  tmp_file0.close();
  tmp_file1.close();

  // open the files in read mode
  //
  tmp_file0.open(tmp_filename0);
  tmp_file1.open(tmp_filename1);

  // read the object back
  //
  cor1.read(tmp_file0, (long)0);
  cor1.init();

  if (!cor0.eq(cor1)) {
    return Error::handle(name(), L"i/o", Error::TEST,
			 __FILE__, __LINE__);
  }
    
  cor1.read(tmp_file1, (long)0);
  cor1.init();

  if (!cor0.eq(cor1)) {
    return Error::handle(name(), L"i/o", Error::TEST,
			 __FILE__, __LINE__);
  }
    
  // close and delete the temporary files
  //
  tmp_file0.close();
  tmp_file1.close();
  File::remove(tmp_filename0);
  File::remove(tmp_filename1);

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  //---------------------------------------------------------------------------
  //
  // 3. class-specific public methods
  //     set and get methods
  //
  //---------------------------------------------------------------------------

  // set indentation
  //  
  if (level_a > Integral::NONE) {
    Console::put(L"testing class-specific public methods: set and get methods...\n");
    Console::increaseIndention();
  }

  // set the parameters manually
  //
  cor0.setAlgorithm(NORMAL);
  cor0.setImplementation(BACKWARD);
  cor0.setScaling(LINSCALE);
  cor0.setEpsilonMin(4);
  cor0.setEpsilonMax(200);
  cor0.setEpsilonRes(40);
  cor0.setDelta((float)1);
  cor0.setTheilerCorrection(150);

  // check that the values were set
  //
  if (cor0.algorithm_d != NORMAL) {
    return Error::handle(name(), L"setAlgorithm", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (cor0.implementation_d != BACKWARD) {
    return Error::handle(name(), L"setImplementation", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (cor0.scaling_d != LINSCALE) {
    return Error::handle(name(), L"setScaling", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (cor0.epsilon_min_d != (Float)4) {
    return Error::handle(name(), L"setEpsilonMin", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (cor0.epsilon_max_d != (Float)200) {
    return Error::handle(name(), L"setEpsilonMax", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (cor0.epsilon_res_d != (Long)40) {
    return Error::handle(name(), L"setEpsilonRes", Error::TEST,
			 __FILE__, __LINE__);
  }  
  else if (cor0.delta_d != (Float)1) {
    return Error::handle(name(), L"setDelta", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (cor0.theiler_correction_d != (Long)150) {
    return Error::handle(name(), L"setTheilerCorrection", Error::TEST,
			 __FILE__, __LINE__);
  }

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  //---------------------------------------------------------------------------
  //
  // 4. class-specific public methods
  //     computational methods
  //
  //---------------------------------------------------------------------------

  // set indentation
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing class-specific public methods: computational methods...\n");
    Console::increaseIndention();
  }

  // generate the required time series for testing the implementations
  //
  // we use Lorenz time series
  //
  VectorFloat time_series, dummy;
  Generator gen1;
  gen1.set(Generator::LORENTZ);
  gen1.setSampleFrequency((float)100);
  gen1.setGenSampleFrequency((float)100);
  gen1.setSignalDuration(10);
  gen1.setFrameDuration(10);
  gen1.compute(time_series, dummy);

  // generate Rps for the input time series
  //
  Long D = 15, svd_window_size = 16;
   
  // create input to the actual class. that is the Rps
  // Rps of the time series is the input for the time delay estimation
  //
  Rps rps_block;
  MatrixFloat rps_x;
  rps_block.set(Rps::SVD_EMBEDDING, Rps::EMBEDDING, D, svd_window_size);
  rps_block.compute(rps_x, time_series);

  // declare the parameters
  //
  float epsilon_min = 1, epsilon_max = 40, delta = 0.5;
  long epsilon_res = 5, theiler_correction = 50;

  // test the backward differentiation
  //
  CorrelationDimension corr_dim1;
  VectorFloat corr_dim_res1;
  corr_dim1.setImplementation(CorrelationDimension::BACKWARD);
  corr_dim1.setScaling(CorrelationDimension::LINSCALE);  
  corr_dim1.setEpsilonMin(epsilon_min);
  corr_dim1.setEpsilonMax(epsilon_max);
  corr_dim1.setEpsilonRes(epsilon_res);
  corr_dim1.setTheilerCorrection(theiler_correction);
  corr_dim1.setDelta(delta);
  corr_dim1.compute(corr_dim_res1, rps_x);
   
  // the values computed from the program
  //
  VectorFloat expected1 = (L"3.89202, 1.91427, 1.42318, 1.03247, 0.785251");
   
  if (!corr_dim_res1.almostEqual(expected1, max_percent)) {
    expected1.debug(L"expected");
    corr_dim_res1.debug(L"computed");
    return  Error::handle(name(),
  			  L"compute (Backward)",
  			  ERR, __FILE__, __LINE__);
  }

  // test the backward differentiation for logscale
  //
  CorrelationDimension corr_dim2;
  VectorFloat corr_dim_res2;
  corr_dim2.setImplementation(CorrelationDimension::BACKWARD);
  corr_dim2.setScaling(CorrelationDimension::LOGSCALE);  
  corr_dim2.setEpsilonMin(epsilon_min);
  corr_dim2.setEpsilonMax(epsilon_max);
  corr_dim2.setEpsilonRes(epsilon_res);
  corr_dim2.setTheilerCorrection(theiler_correction);
  corr_dim2.setDelta(delta);
  corr_dim2.compute(corr_dim_res2, rps_x);
   
  // the values computed from the program
  //
  VectorFloat expected2 = (L"3.89202, 2.49883, 2.37526, 2.17846, 1.99784, 1.7887, 1.3758, 0.928242");

  if (!corr_dim_res2.almostEqual(expected2, max_percent)) {
    expected2.debug(L"expected");
    corr_dim_res2.debug(L"computed");
    return  Error::handle(name(),
  			  L"compute (Backward)",
  			  ERR, __FILE__, __LINE__);
  }  


  // test for Central differentiation
  //
  CorrelationDimension corr_dim3;
  VectorFloat corr_dim_res3;
  corr_dim3.setImplementation(CorrelationDimension::CENTRAL);
  corr_dim3.setScaling(CorrelationDimension::LINSCALE);
  corr_dim3.setEpsilonMin(epsilon_min);
  corr_dim3.setEpsilonMax(epsilon_max);
  corr_dim3.setEpsilonRes(epsilon_res);
  corr_dim3.setTheilerCorrection(theiler_correction);
  corr_dim3.setDelta(delta);
  corr_dim3.compute(corr_dim_res3, rps_x);
  VectorFloat expected3 = (L"3.52372, 1.88872, 1.38971, 1.02214, 0.774269");
 
  if (!corr_dim_res3.almostEqual(expected3, max_percent)) {
    expected3.debug(L"expected");
    corr_dim_res3.debug(L"computed");
    return  Error::handle(name(),
  			  L"compute (Central)",
  			  ERR, __FILE__, __LINE__);
  }
  
  CorrelationDimension corr_dim4;
  VectorFloat corr_dim_res4;
  corr_dim4.setImplementation(CorrelationDimension::CENTRAL);
  corr_dim4.setScaling(CorrelationDimension::LOGSCALE);
  corr_dim4.setEpsilonMin(epsilon_min);
  corr_dim4.setEpsilonMax(epsilon_max);
  corr_dim4.setEpsilonRes(epsilon_res);
  corr_dim4.setTheilerCorrection(theiler_correction);
  corr_dim4.setDelta(delta);
  corr_dim4.compute(corr_dim_res4, rps_x);
  VectorFloat expected4 = (L"3.52372, 2.47672, 2.24068, 2.09793, 1.98137, 1.76464, 1.40366, 0.923831");

  if (!corr_dim_res4.almostEqual(expected4, max_percent)) {
    expected4.debug(L"expected");
    return  Error::handle(name(),
  			  L"compute (Central)",
  			  ERR, __FILE__, __LINE__);
  }
  
  // test for Smooth differentiation
  //
  CorrelationDimension corr_dim5;
  VectorFloat corr_dim_res5;
  corr_dim5.setImplementation(CorrelationDimension::SMOOTH);
  corr_dim5.setScaling(CorrelationDimension::LINSCALE);
  corr_dim5.setEpsilonMin(epsilon_min);
  corr_dim5.setEpsilonMax(epsilon_max);
  corr_dim5.setEpsilonRes(epsilon_res);
  corr_dim5.setTheilerCorrection(theiler_correction);
  corr_dim5.setDelta(delta);
  corr_dim5.compute(corr_dim_res5, rps_x);
  VectorFloat expected5 = (L"3.2919, 1.78525, 1.34109, 1.02677, 0.839752");

  if (!corr_dim_res5.almostEqual(expected5, max_percent)) {
    expected5.debug(L"expected");
    corr_dim_res5.debug(L"computed");
    return  Error::handle(name(),
  			  L"compute (Smooth)",
  			  ERR, __FILE__, __LINE__);
  }
  
  CorrelationDimension corr_dim6;
  VectorFloat corr_dim_res6;
  corr_dim6.setImplementation(CorrelationDimension::SMOOTH);
  corr_dim6.setScaling(CorrelationDimension::LOGSCALE);
  corr_dim6.setEpsilonMin(epsilon_min);
  corr_dim6.setEpsilonMax(epsilon_max);
  corr_dim6.setEpsilonRes(epsilon_res);
  corr_dim6.setTheilerCorrection(theiler_correction);
  corr_dim6.setDelta(delta);
  corr_dim6.compute(corr_dim_res6, rps_x);
  VectorFloat expected6 = (L"3.2919, 2.92211, 2.26095, 2.09263, 1.94995, 1.71903, 1.35823, 0.962608");

  if (!corr_dim_res6.almostEqual(expected6, max_percent)) {
    expected6.debug(L"expected");
    corr_dim_res6.debug(L"computed");
    return  Error::handle(name(),
  			  L"compute (Smooth)",
  			  ERR, __FILE__, __LINE__);
  }
  
  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }

  //---------------------------------------------------------------------------
  //
  // 5. print completion message
  //
  //---------------------------------------------------------------------------

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }

  // possibly print completion message
  //  
  if (level_a > Integral::NONE) {
    String output(L"diagnostics completed successfully for class ");
    output.concat(name());
    output.concat(L"\n");
    Console::put(output);
  }

  // exit gracefully
  //
  return true;
}
