// file: $isip_ifc/class/algo/CorrelationIntegral/corrint_02.cc
// version: $Id: corrint_02.cc 10670 2009-03-21 15:38:59Z tm334 $ 
//

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

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

boolean CorrelationIntegral::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();
  }

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

  CorrelationIntegral corr_int_1(DEF_ALGORITHM, DEF_IMPLEMENTATION,
				 DEF_SCALING);
  CorrelationIntegral corr_int_2(corr_int_1);
  
  if (!corr_int_2.eq(corr_int_1)) {
    return Error::handle(name(), L"copy constructor/eq", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  // testing large chunk of memory allocation
  //
  CorrelationIntegral::setGrowSize((long)500);

  for (long j = 1; j <= 10; j++) {
    CorrelationIntegral** dyn_corr_int = new CorrelationIntegral*[j * 100];
    
    // create the objects
    //
    for (long i = 0; i < j * 100; i++) {
      dyn_corr_int[i] = new CorrelationIntegral();
    }
    
    // delete objects
    //
    for (long i = (j * 100) - 1; i >= 0; i--) {
      delete dyn_corr_int[i];
    }
      
    delete [] dyn_corr_int;
  } 
  
  // test the i/o methods
  //
  CorrelationIntegral corr_int_3(DEF_ALGORITHM, DEF_IMPLEMENTATION,
				 DEF_SCALING);
  CorrelationIntegral corr_int_4;
  CorrelationIntegral corr_int_5;

  // 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);

  corr_int_3.write(tmp_file0, (long)0);
  corr_int_3.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 value back
  //
  if (!corr_int_4.read(tmp_file0, (long)0) || !corr_int_4.eq(corr_int_3)) {
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }
  
  if (!corr_int_5.read(tmp_file1, (long)0) || !corr_int_5.eq(corr_int_3)) {
    corr_int_3.debug(L"3");
    corr_int_5.debug(L"5");
    return Error::handle(name(), L"read", 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();
  }
  
  // --------------------------------------------------------------------
  //
  // 2. 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 and get the Epsilon Minimum
  //
  CorrelationIntegral corr_int_6;
  corr_int_6.setEpsilonMin((float)25);
  if (corr_int_6.getEpsilonMin() != (float)25) {
    return Error::handle(name(), L"set /get Epsilon Minimum", Error::TEST,
			 __FILE__, __LINE__);
  }

  // set and get the Epsilon Maximum
  //
  CorrelationIntegral corr_int_7;
  corr_int_7.setEpsilonMax((float)25);
  if (corr_int_7.getEpsilonMax() != (float)25) {
    return Error::handle(name(), L"set /get Epsilon Maximum", Error::TEST,
			 __FILE__, __LINE__);
  }

  // set and get the Epsilon Resolution
  //
  CorrelationIntegral corr_int_8;
  corr_int_8.setEpsilonRes((long)25);
  if (corr_int_8.getEpsilonRes() != (long)25) {
    return Error::handle(name(), L"set /get Epsilon Resolution", Error::TEST,
			 __FILE__, __LINE__);
  }

  // set and get the Theiler's Correction
  //
  CorrelationIntegral corr_int_9;
  corr_int_9.setTheilerCorrection((long)25);
  if (corr_int_9.getTheilerCorrection() != (long)25) {
    return Error::handle(name(), L"set /get Epsilon Minimum", Error::TEST,
			 __FILE__, __LINE__);
  }

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

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

  // Case: Scaling = Default (LINSCALE)
  //
  VectorFloat out_corr_int;
  float epsilon_min = 1, epsilon_max = 10;
  long epsilon_res = 10, theiler_correction = 50;

  // Setting the parameters of the CorrelationIntegral block
  //
  corr_int_9.set(CorrelationIntegral::NORMAL,
		 CorrelationIntegral::REGULAR,
		 CorrelationIntegral::LINSCALE,
		 CorrelationIntegral::LINEAR_EPSILON_SPACING);

  corr_int_9.setEpsilon(epsilon_min, epsilon_max, epsilon_res);
  corr_int_9.setTheilerCorrection(theiler_correction);
		 
  Rps rps_block;
  MatrixFloat rps_x;
  VectorFloat x_scalar_series, dummy;
  long D = 10;
  long len = 30, svd_window_size = 15;

  Generator gen1;
  gen1.set(Generator::LORENTZ);
  gen1.setSampleFrequency((float)100);
  gen1.setGenSampleFrequency((float)100);
  gen1.setSignalDuration(len);
  gen1.setFrameDuration(len);
  gen1.setLorentz((float)16, (float)40, (float)4, (float)0.5,
		  (float)0.5 , (float)0.5, (long)1);
  gen1.compute(x_scalar_series, dummy);

  //Normalizing the attractor to a unit hypercube
  //
  float mean;
  mean = x_scalar_series.mean();
  x_scalar_series.sub(x_scalar_series, mean);

  // Reconstructing the system attractor using SVD based embedding
  //
  rps_block.set(Rps::SVD_EMBEDDING, Rps::EMBEDDING, D, svd_window_size);
  rps_block.compute(rps_x, x_scalar_series);

  // CorrelationIntegral
  //
  corr_int_9.compute(out_corr_int, rps_x);

  // Defining the Output obtained on a standalone program, known to produce
  // correct results of Correlation Dimension estimates
  //
  VectorFloat exp_output(L"0.000478812, 0.00166551, 0.00391428, 0.00717638, 0.0114256, 0.0166431, 0.0226697, 0.0294768, 0.0372461, 0.0456519");

  // float max_percent = (float)7.0;
  float max_percent = (float)10.0;
  
  if (!out_corr_int.almostEqual(exp_output, max_percent)) {
    exp_output.debug(L"expected");
    out_corr_int.debug(L"computed");
    return  Error::handle(name(), L"apply compute for correlation sum, scaling is linear scale", ERR,
			  __FILE__, __LINE__);
  }
  
  // Case: Scaling = LOGSCALE
  //
  VectorFloat out_corr_int_2;

  // Setting the parameters of the CorrelationIntegral block
  //
  corr_int_9.set(CorrelationIntegral::NORMAL,
		 CorrelationIntegral::REGULAR,
		 CorrelationIntegral::LOGSCALE,
		 CorrelationIntegral::LINEAR_EPSILON_SPACING);
  corr_int_9.setEpsilon(epsilon_min, epsilon_max, epsilon_res);
  corr_int_9.setTheilerCorrection(theiler_correction);

  // CorrelationIntegral
  //
  corr_int_9.compute(out_corr_int_2, rps_x);

  // Defining the Output obtained on a standalone program, known to produce
  // correct results of Correlation Dimension estimates
  //
  VectorFloat exp_output_2(L"-7.6442, -6.39762, -5.54312, -4.93696, -4.4719, -4.09576, -3.78673, -3.52415, -3.29021, -3.08671");

  if (!out_corr_int_2.almostEqual(exp_output_2, max_percent)) {
    exp_output_2.debug(L"expected");
    out_corr_int_2.debug(L"computed");
    return  Error::handle(name(), L"apply compute for correlation sum, scaling is logarithmic scale", ERR,
			  __FILE__, __LINE__);
  }

  
  // Case: Scaling = LOG2SCALE
  //
  VectorFloat out_corr_int_3;

  // Setting the parameters of the CorrelationIntegral block
  //
  corr_int_9.set(CorrelationIntegral::NORMAL,
		 CorrelationIntegral::REGULAR,
		 CorrelationIntegral::LOG2SCALE,
		 CorrelationIntegral::LINEAR_EPSILON_SPACING);
  corr_int_9.setEpsilon(epsilon_min, epsilon_max, epsilon_res);
  corr_int_9.setTheilerCorrection(theiler_correction);
  
  // CorrelationIntegral
  //
  corr_int_9.compute(out_corr_int_3, rps_x);

  // Defining the Output obtained on a standalone program, known to produce
  // correct results of Correlation Dimension estimates
  //
  VectorFloat exp_output_3(L"-11.0283, -9.22982, -7.99704, -7.12253, -6.45159, -5.90893, -5.46309, -5.08428, -4.74677, -4.45318");

  if (!out_corr_int_3.almostEqual(exp_output_3, max_percent)) {
    exp_output_3.debug(L"expected");
    out_corr_int_3.debug(L"computed");
    return  Error::handle(name(), L"apply compute for correlation sum, scaling is log2scale", ERR,
			  __FILE__, __LINE__);
  }

  // Case: Scaling = LOG10SCALE
  //
  VectorFloat out_corr_int_4;

  // Setting the parameters of the CorrelationIntegral block
  //
  corr_int_9.set(CorrelationIntegral::NORMAL,
		 CorrelationIntegral::REGULAR,
		 CorrelationIntegral::LOG10SCALE,
		 CorrelationIntegral::LINEAR_EPSILON_SPACING);
  
  corr_int_9.setEpsilon(epsilon_min, epsilon_max, epsilon_res);
  corr_int_9.setTheilerCorrection(theiler_correction);

  // CorrelationIntegral
  //
  corr_int_9.compute(out_corr_int_4, rps_x);

  // Defining the Output obtained on a standalone program, known to produce
  // correct results of Correlation Dimension estimates
  //
  VectorFloat exp_output_4(L"-3.31983, -2.77845, -2.40735, -2.14409, -1.94212, -1.77877, -1.64456, -1.53052, -1.42892, -1.34054");

  if (!out_corr_int_4.almostEqual(exp_output_4, max_percent)) {
    exp_output_4.debug(L"expected");
    out_corr_int_4.debug(L"computed");
    return  Error::handle(name(), L"apply compute for correlation sum, scaling is log10scale", ERR,
			  __FILE__, __LINE__);
  }
  
  // Case: Spacing_Mode = USER_SPECIFIED_EPSILON_SPACING, Scaling = LOG10SCALE
  //
  VectorFloat out_corr_int_5;
  
  // Setting the parameters of the CorrelationIntegral block
  //
  corr_int_9.set(CorrelationIntegral::NORMAL,
		 CorrelationIntegral::REGULAR,
		 CorrelationIntegral::LOG10SCALE,
		 CorrelationIntegral::USER_SPECIFIED_EPSILON_SPACING);

  VectorFloat eps(L"1,2,3,4,5,6,7,8,9,10");
  
  corr_int_9.setEpsilon(eps);
  corr_int_9.setTheilerCorrection(theiler_correction);

  // CorrelationIntegral
  //
  corr_int_9.compute(out_corr_int_5, rps_x);

  // Defining the Output obtained on a standalone program, known to produce
  // correct results of Correlation Dimension estimates
  //
  VectorFloat exp_output_5(L"-3.31983, -2.77845, -2.40735, -2.14409, -1.94212, -1.77877, -1.64456, -1.53052, -1.42892, -1.34054");

   if (!out_corr_int_5.almostEqual(exp_output_5, max_percent)) {
    exp_output_5.debug(L"expected");
    out_corr_int_5.debug(L"computed");
    return  Error::handle(name(), L"apply compute for correlation sum, scaling is log10scale, spacing is user_specified_epsilon_spacing", ERR,
			    __FILE__, __LINE__);
   }

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

  //---------------------------------------------------------------------------
  //
  // 4. print completion message
  //
  //---------------------------------------------------------------------------

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  if (level_a > Integral::NONE) {
    SysString output(L"diagnostics passed for class ");
    output.concat(name());
    output.concat(L"\n");
    Console::put(output);
  }

  // exit gracefully
  //
  return true;

}
