// file: $isip_ifc/class/algo/Lyapunov/lyap_02.cc
// version: $Id: lyap_02.cc 10485 2006-03-15 17:22:34Z srinivas $
//

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

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

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

  Lyapunov lyap_1(DEF_ALGORITHM, DEF_IMPLEMENTATION, (long)25, (long)5);
  Lyapunov lyap_2(lyap_1);
  
  if (!lyap_2.eq(lyap_1)) {
    return Error::handle(name(), L"copy constructor/eq", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  // testing large chunk of memory allocation
  //
  Lyapunov::setGrowSize((long)500);

  for (long j = 1; j <= 10; j++) {
    Lyapunov** dyn_lyap = new Lyapunov*[j * 100];
    
    // create the objects
    //
    for (long i = 0; i < j * 100; i++) {
      dyn_lyap[i] = new Lyapunov();
    }
    
    // delete objects
    //
    for (long i = (j * 100) - 1; i >= 0; i--) {
      delete dyn_lyap[i];
    }
      
    delete [] dyn_lyap;
  } 
  
  // test the i/o methods
  //
  Lyapunov lyap_3(DEF_ALGORITHM, DEF_IMPLEMENTATION, (long)15, (long)5);
  Lyapunov lyap_4;
  Lyapunov lyap_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);

  lyap_3.write(tmp_file0, (long)0);
  lyap_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 (!lyap_4.read(tmp_file0, (long)0) || !lyap_4.eq(lyap_3)) {
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }
  
  if (!lyap_5.read(tmp_file1, (long)0) || !lyap_5.eq(lyap_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 the number of neighbors
  //
  Lyapunov lyap_6;
  lyap_6.setNumNeighbors((long)25);
  if (lyap_6.getNumNeighbors() != (long)25) {
    return Error::handle(name(), L"set /get embedding dimension", 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: Algorithm = LINEAR_TANGEMT_MAP, Implementation = TREPPEN_ITERATION,
  //
  
  //       Input = exponentially decaying matrix (actually, a vector)
  //
  VectorFloat out_lyap;
  float samp_freq = 1.0;
  
  long N1 = 90, D1 = 1;
  MatrixFloat test_exp(N1, D1 + 1);
  for (long i = 0; i < N1; i++) {
    for (long j = 0; j < D1; j++) {
      test_exp.setValue(i, j, (float)(10.00 * Integral::exp(-(1.1 + j) * i)));
    }
    test_exp.setValue(i, D1, 1.0);
  }
  test_exp.setValue(N1 - 1, D1, 0.0);
  VectorFloat result_lyap_exp(L"-1.1");
  lyap_6.set(Lyapunov::DEF_ALGORITHM, Lyapunov::DEF_IMPLEMENTATION,
	     (long)3, (long)4, (long)D1, (long)20);
  lyap_6.setSampleFrequency(samp_freq);
  lyap_6.compute(out_lyap, test_exp);
  
  if (!out_lyap.almostEqual(result_lyap_exp)) {
    return  Error::handle(name(), L"compute for exp wave", ERR,
    			  __FILE__, __LINE__);
  }
    
  //       Input = sine (with optimum time-delayed versions) matrix 
  //
  long N2 = 200, D2 = 3;
  MatrixFloat test_sin_rps;
  VectorFloat tmp_sin(N2);
  samp_freq = 1.0 / 0.06;
  float freq = 1.0;
  for (long i = 0; i < N2; i++) {
    tmp_sin(i) = (float)(1.00 *Integral::sin(2* M_PI * freq * i / samp_freq));
  }
  long svd_win_size = 15;
  Rps rps_block(Rps::SVD_EMBEDDING, Rps::DEF_IMPLEMENTATION, D2, svd_win_size);
  rps_block.compute(test_sin_rps, tmp_sin);
  
  VectorFloat result_lyap_sin(L"0.00, 0.00, -1.85");
  lyap_6.set(Lyapunov::DEF_ALGORITHM, Lyapunov::DEF_IMPLEMENTATION,
	     (long)14, (long)8, (long)D2, (long)20, (float)0.01);
  lyap_6.setSampleFrequency(samp_freq);
  lyap_6.compute(out_lyap, test_sin_rps);

  if (!((float)Integral::abs(out_lyap(0) - result_lyap_sin(0)) <= (float)0.01) ||
      !((float)Integral::abs(out_lyap(1) - result_lyap_sin(1)) <= (float)0.01) ||
      (out_lyap(2) > (float)(-0.5))) {
    return  Error::handle(name(), L"compute for sine wave", 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;
}
