// file: $isip/class/algo/Reflection/refl_02.cc
// version: $Id: refl_02.cc 7696 2002-02-06 03:17:04Z zheng $
//

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

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

  // test memory manager methods
  //
  Reflection refl;

  Reflection::setGrowSize((long)500);

  Reflection* reflp = new Reflection();

  for (long j = 1; j <= 100; j++) {

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

  delete reflp;
  
  // test i/o methods
  //
  Reflection refl0;
  Reflection refl1;
  Reflection refl2;
  refl0.set(COVARIANCE, CHOLESKY, 20, -30);
  
  // 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);

  refl0.write(tmp_file0, (long)0);
  refl0.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 (!refl1.read(tmp_file0, (long)0) ||
      (refl1.getOrder() != refl0.getOrder()) ||
      (refl1.getDynRange() != refl0.getDynRange()) ||
      (refl1.getAlgorithm() != refl0.getAlgorithm())) {
    return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
  }

  if (!refl2.read(tmp_file0, (long)0) ||
      (refl2.getOrder() != refl0.getOrder()) ||
      (refl2.getDynRange() != refl0.getDynRange()) ||
      (refl2.getAlgorithm() != refl0.getAlgorithm())) {
    return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
  }

  // close and  delete the temporary files
  //
  tmp_file0.close();
  tmp_file1.close();
  File::remove(tmp_filename0);
  File::remove(tmp_filename1);
  
  // test the conditional i/o methods
  //
  Reflection refl_0;
  refl_0.setOrder(20);

  Sof tmp_file2;
  String tmp_filename2;
  Integral::makeTemp(tmp_filename2);

  tmp_file2.open(tmp_filename2, File::WRITE_ONLY);

  refl_0.write(tmp_file2, (long)1);

  // close the file
  //
  tmp_file2.close();

  // open the file in read mode
  //
  tmp_file2.open(tmp_filename2);

  refl_0.read(tmp_file2, (long)5); 

  if (refl_0.getOrder() != 20) {
    return Error::handle(name(), L"readData", Error::TEST, __FILE__, __LINE__);
  }
  
  refl_0.read(tmp_file2, (long)1);
  
  // close and delete the temporary files
  //
  tmp_file2.close();
  
  File::remove(tmp_filename2);

  if (level_a > Integral::BRIEF) {
    Console::close();
    File::remove(tmp_filename0);
    Console::put(L"closed and removed temp console");
  }

  // 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 and get the order
  //
  Reflection reflc_1;
  reflc_1.setOrder((long)4);
  if (reflc_1.getOrder() != 4) {
    return Error::handle(name(), L"set/get order", Error::TEST,
                         __FILE__, __LINE__);
  }

  // set and get the algorithm
  //
  reflc_1.setAlgorithm(AUTOCORRELATION); 
  if (reflc_1.getAlgorithm() != AUTOCORRELATION) {
    return Error::handle(name(), L"set/get algorithm", Error::TEST,
                         __FILE__, __LINE__);
  }
  
  // set and get the dynamic range
  //
  reflc_1.setDynRange((float)4.5);
  if (reflc_1.getDynRange() != 4.5) {
    return Error::handle(name(), L"set/get DynRange", Error::TEST,
                         __FILE__, __LINE__);
  }
  
  // set and get the set
  //
  long ord;
  ALGORITHM alg;
  IMPLEMENTATION imp;
  float dyn;
  reflc_1.set(AUTOCORRELATION, DURBIN, 4, (float)4.5);
  reflc_1.get(alg, imp, ord, dyn);
  if ((ord != 4) || (alg != AUTOCORRELATION) || (imp != DURBIN) ||
      (dyn != 4.5)) {
    return Error::handle(name(), L"set/get method", 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();
  }
    
  // setup variables for test
  //
  Reflection reflc;
  VectorFloat input;
  VectorFloat refl_coef;
  VectorFloat pred_coef;

  {
    // declare the number of samples
    //
    int N = 24;

    // declare the order
    //
    long M = 4;
    
    // use the following data as input:
    // 
    //  x(n) = 0 when n = 0, 1, 2, 3; 
    //  x(n) = 1*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.setLength(N);

    float z = 1;
    for (long i = 4; i < 20; i++) {
      input(i) = 2 * z - z * z;
      z = 0.99 * z;
    }    

    // expected prediction coeffience and reflection coefficients for the
    // autocorrelation, covariance and burg analysis
    //
    VectorFloat res_pred_coef_autoc;
    VectorFloat res_pred_coef_cov;
    VectorFloat res_pred_coef_burg;

    VectorFloat res_refl_coef_autoc;
    VectorFloat res_refl_coef_cov;
    VectorFloat res_refl_coef_burg;
    VectorFloat zeros;    

    res_pred_coef_autoc.assign(L"1.000000, -0.9666891, 0.00009353, 0.00009286241, 0.03525937");
    res_pred_coef_cov.assign(L"1.000000, -0.9666891, 0.0000935849, 0.000092812, 0.03525938");
    res_pred_coef_burg.assign(L"1.0000000, -0.9666889, 0.00009363125, 0.00009251215, 0.03525940"); 

    zeros.assign(L"0, 0, 0, 0");    
    res_refl_coef_autoc.assign(L"-0.9378492, 0.03325092, 0.03422026, 0.03525937");
    res_refl_coef_cov.assign(L"-0.9378492, 0.03325093, 0.03422021, 0.03525938");
    res_refl_coef_burg.assign(L"-0.9378492, 0.03325069, 0.03421993, 0.03525940");

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // case: Algorithm = AUTOCORRELATION, Implementation = DURBIN
    //       Input = non-zero CORRELATION vector
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    
    // compute the autocorrelation coefficients with order M
    //
    Correlation autoCorr(Correlation::AUTO, Correlation::FACTORED,
			 Correlation::NONE, M);
    VectorFloat auto_coeff;
    autoCorr.compute(auto_coeff, input);

    // since the order for computing the autocorrelation coefficient is M
    // we sould get M + 1 autocorrelation coefficient
    //
    if (auto_coeff.length() != M + 1) {
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    }
    
    // compute the reflection coefficients using the autocorrelation
    // coefficients via the DURBAL algorithm with order M
    //
    reflc.set(AUTOCORRELATION, DURBIN, M, -200);
    reflc.compute(refl_coef, auto_coeff);    
    
    if (!refl_coef.almostEqual(res_refl_coef_autoc)) {
      refl_coef.debug(L"refl_coef");
      res_refl_coef_autoc.debug(L"res_refl_coef");
      return Error::handle(name(), L"compute", ERR, __FILE__,
			   __LINE__);
    }

    // since the order for computing the reflection coefficients is M
    // we sould get M reflection coefficients
    //
    if (refl_coef.length() != M) {
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    }

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // case: Algorithm = AUTOCORRELATION, Implementation = DURBIN
    //       Input = zero CORRELATION vector
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        
    // test the algorithm for a vector of zeros
    //
    auto_coeff.assign(0.0);
    reflc.compute(refl_coef, auto_coeff);

    if (!refl_coef.almostEqual(zeros)) {
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    }
    
    refl_coef.clear();

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // case: Algorithm = COVARIANCE, Implementation = CHOLESKY
    //       Input = non-zero COVARIANCE matrix
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    
    // compute the covariance coefficients of order M
    //
    Covariance covar(Covariance::DEF_ALGORITHM, Covariance::DEF_IMPLEMENTATION,
		     Covariance::DEF_NORMALIZATION, M);
    MatrixFloat covar_coeff;
    covar.compute(covar_coeff, input);

    // compute the reflection coefficients using the covariance
    // coefficients via the CHOLESKY algorithm with order M
    //
    reflc.set(COVARIANCE, CHOLESKY, M, -200);
    reflc.compute(refl_coef, covar_coeff, AlgorithmData::COVARIANCE);
    
    if (!refl_coef.almostEqual(res_refl_coef_cov)) {
      res_pred_coef_cov.debug(L"res_pred_coef");
      refl_coef.debug(L"refl_coef");
      res_refl_coef_cov.debug(L"res_refl_coef");
      return Error::handle(name(), L"compute", ERR, __FILE__, __LINE__);
      }

    // since the order for computing the reflection coefficients is M
    // we sould get M reflection coefficients
    //
    if (refl_coef.length() != M) {
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    }    

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // case: Algorithm = COVARIANCE, Implementation = CHOLESKY
    //       Input = zero COVARIANCE matrix
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

    // test the algorithm for a vector of zeros
    //
    covar_coeff.assign(0);
    reflc.compute(refl_coef, covar_coeff, AlgorithmData::COVARIANCE);    

    if (!refl_coef.almostEqual(zeros)) {
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    }

    refl_coef.clear();

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // case: Algorithm = LATTICE, Implementation = BURG
    //       Input = non-zero SIGNAL vector
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

    // compute the reflection coefficients using the BURG algorithm via
    // a lattice with order M
    //
    reflc.set(LATTICE, BURG, M, -200);
    reflc.compute(refl_coef, input, AlgorithmData::SIGNAL);
  
    if (!refl_coef.almostEqual(res_refl_coef_burg)) {
      refl_coef.debug(L"pred_coef");
      res_refl_coef_burg.debug(L"res_refl_coef");
      return Error::handle(name(), L"compute", ERR, __FILE__, __LINE__);    
    }

    // since the order for computing the reflection coefficients is M
    // we sould get M reflection coefficients
    //
    if (refl_coef.length() != M) {
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    }

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // case: Algorithm = LATTICE, Implementation = BURG
    //       Input = zero SIGNAL vector
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

    // test the algorithm for a vector of zeros
    //
    input.assign(0.0);
    reflc.compute(refl_coef, input, AlgorithmData::SIGNAL);    

    if (!refl_coef.almostEqual(zeros)) {
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    }
    
    refl_coef.clear();
    
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // case: Algorithm = PREDICTION, Implementation = STEP_DOWN
    //       Input = non-zero PREDICTION vector
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    
    // compute the reflection coefficients from the predictor coefficients
    //
    pred_coef.assign(L"1.000000, -0.9666891, 0.00009353, 0.00009286241, 0.03525937");
    reflc.setAlgorithm(PREDICTION);
    reflc.setImplementation(STEP_UP);
    reflc.compute(refl_coef, pred_coef, AlgorithmData::PREDICTION);

    VectorFloat res_refl_coef;
    res_refl_coef.assign(L"-0.9378492, 0.03325092, 0.03422026, 0.03525937");
    
    if (!refl_coef.almostEqual(res_refl_coef, .3)) {
      refl_coef.debug(L"refl_coef from predictor");
      return Error::handle(name(), L"computeFromRefelction", ERR, __FILE__,
			   __LINE__);
    }

    // since we have used M + 1  predictor coefficients to compute the
    // reflection coefficients we should get M reflection coefficients
    //
    if (refl_coef.length() != M) {
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    }

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // case: Algorithm = PREDICTION, Implementation = STEP_DOWN
    //       Input = zero PREDICTION vector
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

    // test the algorithm for a vector of zeros
    //
    pred_coef.assign(0.0);
    reflc.compute(refl_coef, pred_coef, AlgorithmData::PREDICTION);

    if (!refl_coef.almostEqual(zeros)) {
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    }
    
    refl_coef.clear();
    
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // case: Algorithm = LOG_AREA_RATIO, Implementation = KELLY_LOCHBAUM
    //       Input = non-zero LOG_AREA_RATIO vector
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    
    // compute the reflection coefficients from the log area ratio coefficients
    //
    VectorFloat log_arr_coef(L"3.43977, -0.0665264, -0.0684673, -0.070548");

    reflc.setAlgorithm(LOG_AREA_RATIO);
    reflc.setImplementation(KELLY_LOCHBAUM);
    reflc.compute(refl_coef, log_arr_coef, AlgorithmData::LOG_AREA_RATIO);

    if (!refl_coef.almostEqual(refl_coef, .3)) {
      refl_coef.debug(L"refl_coef from log_arr");
      return Error::handle(name(), L"computeFromLogArr", ERR,
			   __FILE__, __LINE__);
    }

    // since we have used M log area ratio coefficients to compute the
    // reflection coefficients we should get M reflection coefficients
    //
    if (refl_coef.length() != M) {
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    }

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // case: Algorithm = LOG_AREA_RATIO, Implementation = KELLY_LOCHBAUM
    //       Input = zero LOG_AREA_RATIO vector
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    
    // test the algorithm for a vector of zeros
    //
    log_arr_coef.assign(0.0);
    reflc.compute(refl_coef, log_arr_coef, AlgorithmData::LOG_AREA_RATIO);    

    if (!refl_coef.almostEqual(zeros)) {
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    }

    refl_coef.clear();
  }

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

  // --------------------------------------------------------------------
  //
  // 5. 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 = LATTICE, Implementation = BURG
  //       Input = non-zero and zero SIGNAL at channel 0 and 1
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

  // local variables
  //
  Vector< CircularBuffer<AlgorithmData> > in;
  Vector<AlgorithmData> out;
  AlgorithmData data;
  
  float z = 1;
  for (long i = 4; i < 20; i++) {
    input(i) = 2 * z - z * z;
    z = 0.99 * z;
  }    

  // number of channels
  //
  long N = 2;
  
  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::SIGNAL);
  }
  in(0)(0).getVectorFloat().assign(input);
  input.assign((float)0);
  in(1)(0).getVectorFloat().assign(input);
    
  // compute the reflection coefficients using the BURG algorithm via
  // a lattice with order M
  //
  long M = 4;
  reflc.set(LATTICE, BURG, M, -200);
  reflc.apply(out, in);

  VectorFloat res_refl_coef_burg(L"-0.9378492, 0.03325069, 0.03421993, 0.03525940");
  VectorFloat zeros(L"0, 0, 0, 0");
  
  if (!out(0).getVectorFloat().almostEqual(res_refl_coef_burg)) {
    out(0).getVectorFloat().debug(L"refl(0)");
    res_refl_coef_burg.debug(L"res_refl_coef");
    return Error::handle(name(), L"diagnose", ERR, __FILE__, __LINE__);    
  }

  if (!out(1).getVectorFloat().almostEqual(zeros)) {
    out(1).getVectorFloat().debug(L"refl(1)");
    zeros.debug(L"zeros_refl_coef");
    return Error::handle(name(), L"diagnose", ERR, __FILE__, __LINE__);    
  }
  
  Error::reset();
  Error::set(Error::WARNING);

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

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