// file: $isip/class/algo/Prediction/pred_02.cc
// version: $Id: pred_02.cc 7846 2002-03-05 20:15:10Z zheng $
//

// isip include files
//
#include <Console.h>
#include "Prediction.h"
#include "LogAreaRatio.h"

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

  //  don't exit
  //
  Error::reset();
  Error::set(Error::NONE);
   
  //--------------------------------------------------------------------------
  //
  // 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
  //
  Prediction lp;

  Prediction::setGrowSize((long)500);

  Prediction* lpp = new Prediction();
  
  for (long j = 1; j <= 100; j++) {

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

  delete lpp;
  
  // test i/o methods
  //
  Prediction lp0;
  Prediction lp1;
  Prediction lp2;
  lp0.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);

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

  if (!lp2.read(tmp_file1, (long)0) ||
      (lp2.getOrder() != lp0.getOrder()) ||
      (lp2.getDynRange() != lp0.getDynRange()) ||
      (lp2.getAlgorithm() != lp0.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
  //
  Prediction lp_0;
  lp_0.setOrder(20);

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

  tmp_file2.open(tmp_filename2, File::WRITE_ONLY);
  lp_0.write(tmp_file2, (long)5);

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

  // open the file in read mode
  //
  tmp_file2.open(tmp_filename2);
  lp_0.read(tmp_file2, (long)5); 

  if (lp_0.getOrder() != (long)20) {
    lp_0.debug(L"lp_0");
    return Error::handle(name(), L"readData", Error::TEST, __FILE__, __LINE__);
  }
  
  lp_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: get and set methods...\n");
    Console::increaseIndention();
  }

  // set and get the order
  //
  Prediction lpc_1;
  lpc_1.setOrder((long)4);
  if (lpc_1.getOrder() != 4) {
    return Error::handle(name(), L"set/get order", Error::TEST,
                         __FILE__, __LINE__);
  }

  // set and get the algorithm
  //
  lpc_1.setAlgorithm(AUTOCORRELATION); 
  if (lpc_1.getAlgorithm() != AUTOCORRELATION) {
    return Error::handle(name(), L"set/get algorithm", Error::TEST,
                         __FILE__, __LINE__);
  }
  
  // set and get the dynamic range
  //
  lpc_1.setDynRange((double)4.5);
  if (lpc_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;
  lpc_1.set(AUTOCORRELATION, DURBIN, 4, 4.5);
  lpc_1.get(alg, imp, ord, dyn);
  if ((ord != 4) || (alg != AUTOCORRELATION) || (dyn != 4.5)) {
    return Error::handle(name(), L"set/get set 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 vairables for test
  //
  Prediction lpc;
  VectorFloat input;
  VectorFloat pred_coef;
  VectorFloat result_01(L"1, 0, 0, 0, 0");  

  // test LATTICE algorithm
  //
  {
    // 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(24);

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

    // expected prediction coefficients for the autocorrelation,
    // covariance and burg analysis
    //
    VectorFloat res_pred_coef_autoc;
    VectorFloat res_pred_coef_burg;
    res_pred_coef_autoc.assign(L"1.000000, -0.9666891, 0.00009353, 0.00009286241, 0.03525937");
    res_pred_coef_burg.assign(L"1.0000000, -0.9666889, 0.00009363125, 0.00009251215, 0.03525940"); 
    
    // case: algorithm = LATTICE, implementation = BURG,
    //       input = non-zero SIGNAL vector
    //
    lpc.set(LATTICE, BURG, 4, -200);
    lpc.compute(pred_coef, input, AlgorithmData::SIGNAL);
    
    if (!pred_coef.almostEqual(res_pred_coef_burg)) {
      pred_coef.debug(L"pred_coef");
      return Error::handle(name(), L"computeCor", ERR, __FILE__, __LINE__);    
    }

    // case: algorithm = LATTICE, implementation = BURG,
    //       input = zero SIGNAL vector
    //
    for (long i = 0; i < input.length(); i++) {
      input(i) = 0;
    }
    lpc.compute(pred_coef, input, AlgorithmData::SIGNAL);

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

  // test COVARIANCE algorithm
  //
  {
    // 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(24);

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

    Covariance cov_01;
    MatrixFloat cov_matrix;
    float err_energy;
    VectorFloat res_pred_coef_cov;
    res_pred_coef_cov.assign(L"1.000000, -0.9666891, 0.0000935849, 0.000092812, 0.03525938");
    
    // case: algorithm = COVARIANCE, implementation = CHOLESKY,
    //       input = non-zero COVARIANCE matrix
    //
    cov_01.setOrder((long)4);
    cov_01.compute(cov_matrix, input);    

    lpc.set(COVARIANCE, CHOLESKY, 4, -200);
    lpc.compute(pred_coef, err_energy, cov_matrix, AlgorithmData::COVARIANCE);
    
    if (!pred_coef.almostEqual(res_pred_coef_cov)) {
      pred_coef.debug(L"pred_coef");
      return Error::handle(name(), L"computeCor", ERR, __FILE__, __LINE__);    
    }

    // case: algorithm = COVARIANCE, implementation = CHOLESKY,
    //       input = non-zero COVARIANCE matrix
    //
    cov_matrix.assign(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");
    lpc.compute(pred_coef, err_energy, cov_matrix, AlgorithmData::COVARIANCE);
    if (!pred_coef.almostEqual(result_01)) {
      pred_coef.debug(L"pred_coef");
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    } 

    // case: algorithm = COVARIANCE, implementation = CHOLESKY,
    //       input = singular COVARIANCE matrix
    //
    lpc.set(COVARIANCE, CHOLESKY, 2, -10);
    cov_matrix.assign(4, 4, L"0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 4, 3");
    
    lpc.compute(pred_coef, err_energy, cov_matrix, AlgorithmData::COVARIANCE);
    VectorFloat result_02(L"1, 0, 0");
    if (!pred_coef.almostEqual(result_02)) {
      pred_coef.debug(L"pred_coef");
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    }

    lpc.set(COVARIANCE, CHOLESKY, 12, -10);
    cov_matrix.assign(13, 13, 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, 0, 0, 0, 5.27765e-05, 0, 0, 0, -4.53232e-05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5.27765e-05, 0, 0, 0, -4.53232e-05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5.27765e-05, 0, 0, 0, -4.53232e-05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5.27765e-05, 0, 0, 0, -4.53232e-05, 0, 0, 0, 0, 0, -4.53232e-05, 0, 0, 0, 9.1699e-05, 0, 0, 0, -4.53232e-05, 0, 0, 0, 0, 0, -4.53232e-05, 0, 0, 0, 9.1699e-05, 0, 0, 0, -4.53232e-05, 0, 0, 0, 0, 0, -4.53232e-05, 0, 0, 0, 9.1699e-05, 0, 0, 0, -4.53232e-05, 0, 0, 0, 0, 0, -4.53232e-05, 0, 0, 0, 9.1699e-05, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4.53232e-05, 0, 0, 0, 9.1699e-05, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4.53232e-05, 0, 0, 0, 9.1699e-05, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4.53232e-05, 0, 0, 0, 9.1699e-05");

    lpc.compute(pred_coef, err_energy, cov_matrix, AlgorithmData::COVARIANCE);
    VectorFloat result_03(L"1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0");
    if (!pred_coef.almostEqual(result_03)) {
      pred_coef.debug(L"pred_coef");
      return Error::handle(name(), L"diagnose", Error::TEST,
			   __FILE__, __LINE__);
    }
  }

  //  test AUTOCORRELATION algorithm
  //    exchange from correlation coefficients
  //
  {
    // case: algorithm = AUTOCORRELATION, implementation = DURBIN,
    //       input = non-zero CORRELATION vector
    //
    float err_energy;
    lpc.set(AUTOCORRELATION, DURBIN, 4, -200);

    input.assign(L"0.657422, 0.616563, 0.57561, 0.534578, 0.493479");
    lpc.compute(pred_coef, err_energy, input, AlgorithmData::CORRELATION);

    VectorFloat res_pred_coef;
    res_pred_coef.assign(L"1.000000, -0.9666891, 0.00009353, 0.00009286241, 0.03525937");
    
    Double err_enr(err_energy);
    if (level_a > Integral::BRIEF) {
      err_enr.debug(L"error energy");
    }
    
    if (!pred_coef.almostEqual(res_pred_coef, .3)) {
      pred_coef.debug(L"pred_coef from autocorrelation");
      res_pred_coef.debug(L"expected pred_coef from autocorrealtion");
      return Error::handle(name(), L"computeFromAuto", ERR, __FILE__,
			   __LINE__);
    }

    // case: algorithm = AUTOCORRELATION, implementation = DURBIN,
    //       input = zero CORRELATION vector
    //
    input.assign(L"0, 0, 0, 0, 0");
    lpc.compute(pred_coef, err_energy, input, AlgorithmData::CORRELATION);    
    if (!pred_coef.almostEqual(result_01)) {
      pred_coef.debug(L"pred_coef");
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    } 
  }

  // test reflection algorithm:
  //  exchange from predictor and relection coefficients
  //
  {
    // case: algorithm = REFLECTION, implementation = STEPDOWN,
    //       input = non-zero REFLECTION vector
    //    
    VectorFloat refl_coef;
    lpc.set(REFLECTION, STEP_DOWN, 4, -200);
    
    refl_coef.assign(L"-0.9378492, 0.03325092, 0.03422026, 0.03525937");
    lpc.compute(pred_coef, refl_coef, AlgorithmData::REFLECTION);
    
    VectorFloat res_pred_coef;
    res_pred_coef.assign(L"1.000000, -0.9666891, 0.00009353, 0.00009286241, 0.03525937");

    if (!pred_coef.almostEqual(res_pred_coef)) {
      pred_coef.debug(L"pred_coef from reflection");
      return Error::handle(name(), L"computeFromRefelction", ERR, __FILE__, __LINE__);
    }

    // case: algorithm = REFLECTION, implementation = STEPDOWN,
    //       input = zero REFLECTION vector
    //    
    refl_coef.assign(L"0, 0, 0, 0");
    lpc.compute(pred_coef, refl_coef, AlgorithmData::REFLECTION);    
    if (!pred_coef.almostEqual(result_01)) {
      pred_coef.debug(L"pred_coef");
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    } 

    pred_coef.clear();
    refl_coef.clear();
  }

  //  test LOG_AREA_RATIO algorithm
  //
  {
    // case: algorithm = AUTOCORRELATION, implementation = DURBIN,
    //       input = non-zero LOG_AREA_RATIO vector
    //
    VectorFloat lar_input(L"3.43977, -0.0665264, -0.0684673, -0.070548");
    
    lpc.set(LOG_AREA_RATIO, KELLY_LOCHBAUM, 4, -200);
    lpc.compute(pred_coef, lar_input, AlgorithmData::LOG_AREA_RATIO);

    VectorFloat res_pred_coef;
    res_pred_coef.assign(L"1.000000, -0.9666891, 0.00009353, 0.00009286241, 0.03525937");
    
    if (!pred_coef.almostEqual(res_pred_coef, .3)) {
      pred_coef.debug(L"pred_coef from autocorrelation");
      res_pred_coef.debug(L"expected pred_coef from autocorrealtion");
      return Error::handle(name(), L"computeFromAuto", ERR, __FILE__,
			   __LINE__);
    }

    // case: algorithm = LOG_AREA_RATIO, implementation = KELLY_LOCHBAUM,
    //       input = zero LOG_AREA_RATIO vector
    //    
    lar_input.assign(L"0, 0, 0, 0");
    lpc.compute(pred_coef, lar_input, AlgorithmData::LOG_AREA_RATIO);    
    if (!pred_coef.almostEqual(result_01)) {
      pred_coef.debug(L"pred_coef");
      return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
    } 
  }

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

  Vector< CircularBuffer<AlgorithmData> > in;
  Vector<AlgorithmData> out;
  AlgorithmData data;
  
  // number of channels
  //
  long N = 2;
  
  in.setLength(N);
  out.setLength(N);

  float z = 1;
  input.setLength(24);
  for (long i = 4; i < 20; i++) {
    input(i) = 2 * z - z * z;
    z = 0.99 * z;
  }
  
  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);
  
  // case: algorithm = LATTICE, implementation = BURG,
  //       input = non-zero and zero SIGNAL at channel 0 and 1
  //
  lpc.set(LATTICE, BURG, 4, -200);
  lpc.apply(out, in);

  VectorFloat res_pred_coef_burg(L"1.0000000, -0.9666889, 0.00009363125, 0.00009251215, 0.03525940"); 
  
  if (!out(0).getVectorFloat().almostEqual(res_pred_coef_burg) ||
      !out(1).getVectorFloat().almostEqual(result_01)) {
    out(0).getVectorFloat().debug(L"result1");
    out(1).getVectorFloat().debug(L"result2");
    res_pred_coef_burg.debug(L"pred_coef");
    result_01.debug(L"result_zeros");
    return Error::handle(name(), L"computeCor", ERR, __FILE__, __LINE__);    
  }
  
  // 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;
}
