// file: $isip/class/algo/Covariance/cov_02.cc
// version: $Id: cov_02.cc 8021 2002-03-31 00:18:04Z gao $
//

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

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

  Covariance cov_1(DEF_ALGORITHM, DEF_IMPLEMENTATION, DEF_NORMALIZATION, 2);
  Covariance cov_2(cov_1);
  
  if (!cov_2.eq(cov_1)) {
    return Error::handle(name(), L"copy constructor/eq", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  // testing large chunk of memory allocation
  //
  Covariance::setGrowSize((long)500);

  for (long j = 1; j <= 10; j++) {
    Covariance** dyn_cov = new Covariance*[j * 100];
    
    // create the objects
    //
    for (long i = 0; i < j * 100; i++) {
      dyn_cov[i] = new Covariance();
    }
    
    // delete objects
    //
    for (long i = (j * 100) - 1; i >= 0; i--) {
      delete dyn_cov[i];
    }
      
    delete [] dyn_cov;
  } 
  
  // test the i/o methods
  //
  Covariance cov_3(DEF_ALGORITHM, DEF_IMPLEMENTATION, DEF_NORMALIZATION, 3);
  Covariance cov_4;
  Covariance cov_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);

  cov_3.write(tmp_file0, (long)0);
  cov_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 (!cov_4.read(tmp_file0, (long)0) || !cov_4.eq(cov_3)) {
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }
  
  if (!cov_5.read(tmp_file1, (long)0) || !cov_5.eq(cov_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 window size
  //
  Covariance cov_6;
  cov_6.setOrder((long)4);
  if (cov_6.getOrder() != 4) {
    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();
  }
  
  // setup variables for test
  //
  VectorFloat input;
  VectorFloat zeros(L"0,0,0,0,0");
  VectorFloat ones(L"1,1,1,1,1");
  VectorFloat general(L"0, 2, 1, 2, 0, -1, -2");
  MatrixFloat result_01(5, 5, L"0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0");
  MatrixFloat result_02(5, 5, L"0.2,0.2, 0.2, 0.2,0.2, 0.2,0.2,0.2,0.2,0.2, 0.2,0.2,0.2,0.2,0.2, 0.2,0.2,0.2,0.2,0.2, 0.2,0.2,0.2,0.2,0.2");
  MatrixFloat result_03(3, 3, L"10, 6, 2, 6,10, 4, 2, 4, 9");

  // normalize expected result
  //
  result_03.div(7);

  // test factored implementation
  //
  
  // use the following data as input:
  // 
  //  x(n) = 0 when n = 0, 1, 2, 3; 
  //  x(n) = 2*pow(0.99, n-4) - pow(0.99, 2(n-4)),  when 4 <= n < 20;
  //  x(n) = 0 when n = 20, 21, 22, 23 
  //
  input.clear();
  input.setLength(24);
  
  double z = 1;
  for (long i = 4; i < 20; i++) {
    input(i) = 2 * z - z * z;
    z = 0.99 * z;
  }

  // expected covariance coefficients
  //
  MatrixFloat exp_coeffs;
  MatrixFloat out_coeffs;

  exp_coeffs.assign(5, 5, L"0.657422, 0.616563, 0.57561, 0.534578, 0.493479, 0.616563, 0.657422, 0.616563, 0.57561, 0.534578, 0.57561, 0.616563, 0.657422, 0.616563, 0.57561, 0.534578, 0.57561, 0.616563, 0.657422, 0.616563, 0.493479, 0.534578, 0.57561, 0.616563, 0.657422");

  // case: Algorithm = NORMAL, Implementation = FACTORED,
  //       Input = non-zero SIGNAL vector
  //
  cov_6.compute(out_coeffs, input);

  if (!out_coeffs.almostEqual(exp_coeffs)) {
    exp_coeffs.debug(L"expected");
    out_coeffs.debug(L"factored");
    return  Error::handle(name(), L"apply compute from factored", ERR,
			    __FILE__, __LINE__);
  }
  
  // case: Algorithm = NORMAL, Implementation = FACTORED,
  //       Input = zero SIGNAL vector
  //
  cov_6.compute(out_coeffs, zeros);

  if (!out_coeffs.almostEqual(result_01)) {
      return  Error::handle(name(), L"apply compute from factored", ERR,
			    __FILE__, __LINE__);
  }

  // case: Algorithm = NORMAL, Implementation = FACTORED,
  //       Input = SIGNAL vector of ones
  //
  cov_6.compute(out_coeffs, ones);

  if (!out_coeffs.almostEqual(result_02)) {
      return  Error::handle(name(), L"apply compute from factored", ERR,
			    __FILE__, __LINE__);
  }

  // case: Algorithm = NORMAL, Implementation = FACTORED,
  //       Input = non-zero SIGNAL vector
  //
  cov_6.setOrder(2);
  cov_6.compute(out_coeffs, general);

  if (!out_coeffs.almostEqual(result_03)) {
      return  Error::handle(name(), L"apply compute from factored", ERR,
			    __FILE__, __LINE__);
  }

  // case: Algorithm = NORMAL, Implementation = UNFACTORED,
  //       Input = non-zero SIGNAL vector
  //
  cov_6.setImplementation(UNFACTORED);
  cov_6.setOrder(4);
  cov_6.compute(out_coeffs, input);

  if (!out_coeffs.almostEqual(exp_coeffs)) {
      exp_coeffs.debug(L"expected");
      out_coeffs.debug(L"unfactored");
      return  Error::handle(name(), L"apply compute from unfactored", ERR,
			    __FILE__, __LINE__);
  }

  // case: Algorithm = NORMAL, Implementation = UNFACTORED,
  //       Input = zero SIGNAL vector
  //
  cov_6.compute(out_coeffs, zeros);

  if (!out_coeffs.almostEqual(result_01)) {
      return  Error::handle(name(), L"apply compute from unfactored", ERR,
			    __FILE__, __LINE__);
  }

  // case: Algorithm = NORMAL, Implementation = UNFACTORED,
  //       Input = SIGNAL vector of ones
  //
  cov_6.compute(out_coeffs, ones);

  if (!out_coeffs.almostEqual(result_02)) {
      return  Error::handle(name(), L"apply compute from unfactored", ERR,
			    __FILE__, __LINE__);
  }

  // case: Algorithm = NORMAL, Implementation = --,
  //       Input = GENERIC
  //

  // calculate the covariance for the following function
  // test calculate the covariance from file
  //
  Vector< CircularBuffer<AlgorithmData> > in;
  Vector<AlgorithmData> out;
  AlgorithmData data;
  
  in.setLength(1);
  out.setLength(1);
  in(0).append(data);
  in(0)(0).makeVectorFloat();
  in(0)(0).setCoefType(AlgorithmData::GENERIC);
  
  // set the order
  //
  cov_6.setOrder(4);

  // compute the output
  //  the first frame input
  //
  in(0)(0).getVectorFloat().assign(L"1, 0");
  cov_6.apply(out, in);
  
  // the other frame input
  //
  in(0)(0).getVectorFloat().assign(L"0, 1");
  cov_6.apply(out, in);
 
  // the last frame input
  //
  in(0)(0).getVectorFloat().assign(L"1, 1");
  cov_6.setSignalDuration(0.03);
  cov_6.apply(out, in);
  
  exp_coeffs.clear();
  exp_coeffs.assign(2, 2, L"0.222222, -0.111111, -0.111111, 0.222222");
  
  if (!out(0).getMatrixFloat().almostEqual(exp_coeffs)) {
    out(0).getMatrixFloat().debug(L"out_coeffs");
    exp_coeffs.debug(L"exp_coeffs");
    return  Error::handle(name(), L"apply compute from file", ERR,
			  __FILE__, __LINE__);
  }
  
  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }

  // --------------------------------------------------------------------
  //
  // 3. class-specific public methods
  //     apply methods
  //
  // --------------------------------------------------------------------

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

  // case: Algorithm = NORMAL, Implementation = --,
  //       Input = GENERIC
  //

  // number of channels
  //
  long N = 2;

  cov_6.clear();
  
  in.setLength(N);
  out.setLength(N);

  for (long i = 0; i < N; i++) {
    in(i).append(data);
    in(i)(0).makeVectorFloat();
    in(i)(0).setCoefType(AlgorithmData::GENERIC);
    in(i)(0).getVectorFloat().assign(L"1, 0");
  }
  
  // set the order
  //
  cov_6.setOrder(4);

  // compute the output
  //  the first frame input
  //
  cov_6.apply(out, in);

  exp_coeffs.assign(2, 2, L"1, 0, 0, 0");
  if (!out(0).getMatrixFloat().almostEqual(exp_coeffs) ||
      !out(1).getMatrixFloat().almostEqual(exp_coeffs)) {
    out(0).getMatrixFloat().debug(L"out_coeffs");
    exp_coeffs.debug(L"exp_coeffs");
    return  Error::handle(name(), L"apply compute from file", ERR,
			  __FILE__, __LINE__);
  }
  
  // case: Algorithm = NORMAL, Implementation = FACTORED,
  //       Input = non-zero SIGNAL vector
  //
  for (long i = 0; i < N; i++) {
    in(i).append(data);
    in(i)(0).makeVectorFloat();
    in(i)(0).setCoefType(AlgorithmData::SIGNAL);
    in(i)(0).getVectorFloat().assign(input);
  }

  cov_6.apply(out, in);
  
  exp_coeffs.assign(5, 5, L"0.657422, 0.616563, 0.57561, 0.534578, 0.493479, 0.616563, 0.657422, 0.616563, 0.57561, 0.534578, 0.57561, 0.616563, 0.657422, 0.616563, 0.57561, 0.534578, 0.57561, 0.616563, 0.657422, 0.616563, 0.493479, 0.534578, 0.57561, 0.616563, 0.657422");

  if (!out(0).getMatrixFloat().almostEqual(exp_coeffs) ||
      !out(1).getMatrixFloat().almostEqual(exp_coeffs)) {
    out(0).getMatrixFloat().debug(L"out_coeffs");
    exp_coeffs.debug(L"exp_coeffs");
    return  Error::handle(name(), L"apply compute from factored", 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;
}
