// file: $isip_ifc/class/algo/CorrelationEntropy/correntp_02.cc
// version: $Id: correntp_02.cc
//

// isip include files
//
#include "CorrelationEntropy.h"
#include <Console.h>

// method: diagnose
//
// arguments:
//  Integral::DEBUG level: (input) debug level for diagnostics
//
// return: a boolean value indicating status
//
boolean CorrelationEntropy::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();
  }

  CorrelationEntropy correntpy_1(DEF_ALGORITHM, DEF_IMPLEMENTATION,
				 DEF_EMBEDDING, (float)1, (long)1, (long)16,
				 (long)150, (long)13, (float)1);
  CorrelationEntropy correntpy_2(correntpy_1);

  float max_percent = Integral::DEF_PERCENTAGE;
  
  if (!correntpy_2.eq(correntpy_1)) {
    return Error::handle(name(), L"copy constructor/eq", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  // testing large chunk of memory allocation
  //
  CorrelationEntropy::setGrowSize((long)500);

  for (long j = 1; j <= 10; j++) {
    CorrelationEntropy** dyn_correntpy = new CorrelationEntropy*[j * 100];
    
    // create the objects
    //
    for (long i = 0; i < j * 100; i++) {
      dyn_correntpy[i] = new CorrelationEntropy();
    }
    
    // delete objects
    //
    for (long i = (j * 100) - 1; i >= 0; i--) {
      delete dyn_correntpy[i];
    }
      
    delete [] dyn_correntpy;
  } 
  
  // test the i/o methods
  //
  CorrelationEntropy correntpy_3(DEF_ALGORITHM, DEF_IMPLEMENTATION,
				 DEF_EMBEDDING, (float)1, (long)1, (long)16,
				 (long)150, (long)13, (float)1);
  CorrelationEntropy correntpy_4;
  CorrelationEntropy correntpy_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);

  correntpy_3.write(tmp_file0, (long)0);
  correntpy_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 (!correntpy_4.read(tmp_file0, (long)0) || !correntpy_4.eq(correntpy_3)) {
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }
  
  if (!correntpy_5.read(tmp_file1, (long)0) || !correntpy_5.eq(correntpy_3)) {
    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 method
  //
  CorrelationEntropy correntpy_6;
  correntpy_6.set(CorrelationEntropy::NORMAL, CorrelationEntropy::CORRELATION_INTEGRAL, CorrelationEntropy::TIME_DELAY, (float)1, (long)1, (long)16, (long)150, (long)13, (float)1);
  if ((correntpy_6.getEpsilon() != 1) || (correntpy_6.getEmbedDimMin() != 1)
      || (correntpy_6.getEmbedDimMax() != 16) ||
      (correntpy_6.getTheilerCorrection() != 150) ||
      (correntpy_6.getSVDWindowSize() != 13) ||
      (correntpy_6.getDelay() != 1)) {
    return Error::handle(name(), L"set /get Order", 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();
  }

  // Creating a correlation entropy object
  //
  CorrelationEntropy correntpy_7;
  correntpy_7.setSampleFrequency(1);
  float epsilon = 0.5;
  correntpy_7.set(CorrelationEntropy::NORMAL, CorrelationEntropy::CORRELATION_INTEGRAL, CorrelationEntropy::TIME_DELAY, epsilon, (long)2, (long)7, (long)150, (long)13, (float)1); 
  
  // Generating a time series from a well known Chaotic Map - The Henon map
  //
  VectorFloat output, xn, x_scalar_series, exp_entropy;
  long len = 5000;
  xn.setLength(len);
  x_scalar_series.setLength(len);
  float a = 1.4, b = 0.3;
  xn(0) = 0.5; // Initial Condition
  x_scalar_series(0) = xn(0);
  for (long i = 0 ; i < len - 1 ; i++) {
    xn(i + 1) = a - xn(i) * xn(i) + b * x_scalar_series(i);
    x_scalar_series(i + 1) = xn(i); 
  }

  // case: Algorithm = NORMAL, Implementation = CORRELATION_INTEGRAL,
  // Embedding = Time Delay
  //
  correntpy_7.compute(output, x_scalar_series);

  // The following numbers corresponding the Kolmogorov estimate of the Henon
  // map that have been matched with published results of the same
  //
  exp_entropy.assign(L"0.521823, 0.500607, 0.433724, 0.399544, 0.388435, 0.357481");

  if (!output.almostEqual(exp_entropy, 2.1)) {
    exp_entropy.debug(L"expected");
    output.debug(L"output");
    return  Error::handle(name(), L"apply compute from correlation_integral",
			  ERR, __FILE__, __LINE__);
  }
  
  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }

  //---------------------------------------------------------------------------
  //
  // 3. 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;
}
