// file: $isip/class/algo/Cepstrum/cep_02.cc
// version: $Id: cep_02.cc 9410 2004-02-26 18:17:43Z parihar $
//

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

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

  // declare Cepstrum objects for read/write methods
  //
  Cepstrum ceps2;
  Cepstrum ceps3;
  Cepstrum ceps4;

  // set the number of coefficients and lifer coefficients
  //
  ceps2.setOrder(12);

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

  // write to text and binary files
  //
  ceps2.write(tmp_file0, (long)0);
  ceps2.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
  //
  ceps3.read(tmp_file0, (long)0);
  if (!ceps3.eq(ceps2)) {
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }
  ceps4.read(tmp_file0, (long)0);
  if (!ceps4.eq(ceps2)) {
    ceps4.debug(L"ceps4");
    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);

  // test the conditional i/o methods 
  //
  Cepstrum cep_0;
  
  Sof tmp_file2;
  String tmp_filename2;
  Integral::makeTemp(tmp_filename2);

  tmp_file2.open(tmp_filename2, File::WRITE_ONLY);
  
  tmp_file2.put(CLASS_NAME, 5, -1);
  String line;

  line.assign(L"order = 24;\n");
  tmp_file2.puts(line);

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

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

  // read the values back
  //
  cep_0.read(tmp_file2, (long)5);
  
  // close and  delete the temporary files
  //
  tmp_file2.close();
  File::remove(tmp_filename2);

  // check constructors and memory management methods
  //
  Cepstrum ceps0;
  Cepstrum ceps1(ceps0);

  if (!ceps1.eq(ceps0)) {
    return Error::handle(name(), L"copy constructor", Error::TEST,
                         __FILE__, __LINE__);
  }

  // test large allocation construction and deletion
  //
  if (level_a == Integral::ALL) {
    
    Console::put(L"\ntesting large chunk memory allocation and deletion:\n");
    
    // set the memory to a strange block size so we can hopefully catch any
    // frame overrun errors
    //
    
    Cepstrum::setGrowSize((long)500);
    
    Cepstrum* pft = new Cepstrum();
    
    for (long j = 1; j <= 100; j++) {
      Cepstrum** pfts = new Cepstrum*[j * 100];
      
      // create the objects
      //
      for (long i = 0; i < j * 100; i++) {
	pfts[i] = new Cepstrum();
      }
    
      // delete objects
      //
      for (long i = (j * 100) - 1; i >= 0; i--) {
	delete pfts[i];
      }
      delete [] pfts;
    } 
    delete pft;
  }

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  //---------------------------------------------------------------------------
  //
  // 2. class-specific public methods:
  //     set methods     
  //
  //---------------------------------------------------------------------------

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

  // test set number of coefficients methods
  //
  ceps2.setOrder(12);

  if (ceps2.order_d != (long)12) {
    return Error::handle(name(), L"setOrder", Error::TEST,
                         __FILE__, __LINE__);
  }

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  //---------------------------------------------------------------------------
  //
  // 3. class-specific public methods:
  //     get methods     
  //
  //---------------------------------------------------------------------------

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

  if (ceps2.getOrder() != 12) {
    return Error::handle(name(), L"getOrder", Error::TEST,
                         __FILE__, __LINE__);
  }

  {
    Cepstrum cep1;
    Cepstrum cep2;

    if (!cep1.eq(cep2)) {
      return Error::handle(name(), L"diagnose", 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();
  }

  // local variables
  //
  Cepstrum ceps;
  VectorFloat realinput;
  VectorFloat realoutput;
  VectorComplexFloat complexinput;
  VectorComplexFloat complexoutput;
  VectorFloat realans;
  VectorComplexFloat complexans;
  
  // test the first row in the table where algorithm is IDCT
  // 
  ceps.setAlgorithm(IDCT);

  // test using TYPE_I
  //
  ceps.setImplementation(TYPE_I);

  // the input data is real magnitude of FFT of one cycle of a 1000 Hz
  // cosine sampled at 16 KHz
  //
  realinput.assign(L"1.0000, 8.7079, 0.3839, 0.1354, 0.0676, 0.0383, 0.0222, 0.0117, 0.0037");

  // get all the cepstral coeffs
  //
  ceps.setOrder(8);
  
  // expected result is IDCT of the log of the above signal
  //
  realans.assign(L" 0.765171, 0.999744, 0.765171, 0.414108, 6.62604e-17, -0.414108, -0.765171, -0.999744, -0.765171");

  // compute the result
  //
  ceps.compute(realoutput, realinput);

  if (!realoutput.almostEqual(realans, 20)) {
    realans.debug(L"expected result:");
    realoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // the input data is complex FFT of one cycle of a 1000 Hz cosine
  // sampled at 16 KHz
  //
  complexinput.assign(L"1.0000, 8.5596 + 1.6001j, -0.3580 - 0.1387j, -0.1151 - 0.0713j, -0.0500 - 0.0456j, -0.0231 - 0.0306j, -0.0099 - 0.0199j, -0.0032 - 0.0113j, -0.0003 - 0.0037j");
  
  // expected result is tha same as before, compute the result
  //
  ceps.compute(realoutput, complexinput);
  
  if (!realoutput.almostEqual(realans, 20)) {
    realans.debug(L"expected result:");
    realoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // test with a frame that has some zero values in it
  //
  realinput.assign(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");
  
  // get all the cepstral coeffs
  //
  ceps.setOrder(8);
  
  // expected result is IDCT of the log of the above signal
  //
  realans.assign(L"0, 0, 0, 0, 0, 0, 0, 0, 0");

  // compute the result
  //
  ceps.compute(realoutput, realinput);
  
  if (!realoutput.almostEqual(realans, 20)) {
    realans.debug(L"expected result:");
    realoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // test using TYPE_II
  //
  ceps.setImplementation(TYPE_II);

  // the input data is real magnitude of FFT of one cycle of a 1000 Hz
  // cosine sampled at 16 KHz
  //
  realinput.assign(L"1.0000, 8.7079, 0.3839, 0.1354, 0.0676, 0.0383, 0.0222, 0.0117, 0.0037");
  
  // expected result is IDCT of the log of the above signal
  //
  realans.assign(L"1.00473, 0.883543, 0.65579, 0.348939, 6.24709e-17, -0.348939, -0.65579, -0.883543, -1.00473");
  
  // compute the result
  //
  ceps.compute(realoutput, realinput);

  if (!realoutput.almostEqual(realans, 20)) {
    realans.debug(L"expected result:");
    realoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // the input data is complex FFT of one cycle of a 1000 Hz cosine
  // sampled at 16 KHz
  //
  complexinput.assign(L"1.0000, 8.5596 + 1.6001j, -0.3580 - 0.1387j, -0.1151 - 0.0713j, -0.0500 - 0.0456j, -0.0231 - 0.0306j, -0.0099 - 0.0199j, -0.0032 - 0.0113j, -0.0003 - 0.0037j");
  
  // expected result is tha same as before, compute the result
  //
  ceps.compute(realoutput, complexinput);
  
  if (!realoutput.almostEqual(realans, 20)) {
    realans.debug(L"expected result:");
    realoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // test with a frame that has some zero values in it
  //
  realinput.assign(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");
  
  // get all the cepstral coeffs
  //
  ceps.setOrder(8);
  
  // expected result is IDCT of the log of the above signal
  //
  realans.assign(L"0, 0, 0, 0, 0, 0, 0, 0, 0");

  // compute the result
  //
  ceps.compute(realoutput, realinput);
  
  if (!realoutput.almostEqual(realans, 20)) {
    realans.debug(L"expected result:");
    realoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // test using TYPE_III
  //
  ceps.setImplementation(TYPE_III);

  // the input data is real magnitude of FFT of one cycle of a 1000 Hz
  // cosine sampled at 16 KHz
  //
  realinput.assign(L"1.0000, 8.7079, 0.3839, 0.1354, 0.0676, 0.0383, 0.0222, 0.0117, 0.0037");
  
  // expected result is IDCT of the log of the above signal
  //
  realans.assign(L"0.72141, 0.883543, 0.510114, 6.24709e-17, -0.510114, -0.883543, -1.02023, -0.883543, -0.510114");
  
  // compute the result
  //
  ceps.compute(realoutput, realinput);
  
  if (!realoutput.almostEqual(realans, 20)) {
    realans.debug(L"expected result:");
    realoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // the input data is complex FFT of one cycle of a 1000 Hz cosine
  // sampled at 16 KHz
  //
  complexinput.assign(L"1.0000, 8.5596 + 1.6001j, -0.3580 - 0.1387j, -0.1151 - 0.0713j, -0.0500 - 0.0456j, -0.0231 - 0.0306j, -0.0099 - 0.0199j, -0.0032 - 0.0113j, -0.0003 - 0.0037j");
  
  // expected result is tha same as before, compute the result
  //
  ceps.compute(realoutput, complexinput);
  
  if (!realoutput.almostEqual(realans, 20)) {
    realans.debug(L"expected result:");
    realoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }

  // test with a frame that has some zero values in it
  //
  realinput.assign(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");
  
  // get all the cepstral coeffs
  //
  ceps.setOrder(8);
  
  // expected result is IDCT of the log of the above signal
  //
  realans.assign(L"0, 0, 0, 0, 0, 0, 0, 0, 0");

  // compute the result
  //
  ceps.compute(realoutput, realinput);
  
  if (!realoutput.almostEqual(realans, 20)) {
    realans.debug(L"expected result:");
    realoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // test using TYPE_IV
  //
  ceps.setImplementation(TYPE_IV);

  // the input data is real magnitude of FFT of one cycle of a 1000 Hz
  // cosine sampled at 16 KHz
  //
  realinput.assign(L"1.0000, 8.7079, 0.3839, 0.1354, 0.0676, 0.0383, 0.0222, 0.0117, 0.0037");
  
  // expected result is IDCT of the log of the above signal
  //
  realans.assign(L"0.985465, 0.72141, 0.264054, -0.264054, -0.72141, -0.985465, -0.985465, -0.72141, -0.264054");

  // compute the result
  //
  ceps.compute(realoutput, realinput);

  if (!realoutput.almostEqual(realans, 20)) {
    realans.debug(L"expected result:");
    realoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // the input data is complex FFT of one cycle of a 1000 Hz cosine
  // sampled at 16 KHz
  //
  complexinput.assign(L"1.0000, 8.5596 + 1.6001j, -0.3580 - 0.1387j, -0.1151 - 0.0713j, -0.0500 - 0.0456j, -0.0231 - 0.0306j, -0.0099 - 0.0199j, -0.0032 - 0.0113j, -0.0003 - 0.0037j");
  
  // expected result is tha same as before, compute the result
  //
  ceps.compute(realoutput, complexinput);
  
  if (!realoutput.almostEqual(realans, 20)) {
    realans.debug(L"expected result:");
    realoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // test with a frame that has some zero values in it
  //
  realinput.assign(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");
  
  // get all the cepstral coeffs
  //
  ceps.setOrder(8);
  
  // expected result is IDCT of the log of the above signal
  //
  realans.assign(L"0, 0, 0, 0, 0, 0, 0, 0, 0");
  
  // compute the result
  //
  ceps.compute(realoutput, realinput);
  
  if (!realoutput.almostEqual(realans, 20)) {
    realans.debug(L"expected result:");
    realoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // test the second algorithm that is IDFT
  //  
  ceps.setAlgorithm(IDFT);
  
  // test using CONVENTIONAL
  //
  ceps.setImplementation(CONVENTIONAL);
  
  // the input data is real magnitude FFT of one cycle of a 1000 Hz
  // cos + a 2000 Hz sin sampled at 16 KHz
  //
  realinput.assign(L"1.0000, 8.7270, 8.0161, 1.4306, 0.7777, 0.5766, 0.4838, 0.4369, 0.4166");
  
  // expected result is complex IDFT of the log magnitude spectra
  //
  complexans.assign(L"0.511774+0j, 0.204663+0.416944j, -0.19542+0.281699j, -0.196205+0.00817621j, -0.0689259-0.0318726j, -0.0689259+0.0318726j, -0.196205-0.00817621j, -0.19542-0.281699j, 0.204663-0.416944j");
  
  // compute the result
  //
  ceps.compute(complexoutput, realinput);
  
  if (!complexoutput.almostEqual(complexans, 20)) {
    complexans.debug(L"expected result:");
    complexoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // the input data is complex FFT of one cycle of a 1000 Hz cos + a
  // 2000 Hz sin sampled at 16 KHz
  //
  complexinput.assign(L"1.0000, 8.6656 + 1.0334j, 2.5344 - 7.6049j, -0.8648 + 1.1396j, -0.5720 + 0.5270j, -0.4822 + 0.3162j, -0.4425 + 0.1955j, -0.4233 + 0.1082j,  -0.4152 + 0.0348j");

  // the input data is complex as before, expected result is complex
  // one cycle of a 1000 Hz cos + a 2000 Hz sine sampled at 16 KHz
  //
  complexans.assign(L"0.1527 + 1.6359j, 0.9561 + 0.1609j, 0.2648 + 0.3548j, -0.2375 + 0.0479j, -0.1764 - 0.3916j, 0.1302 - 0.3504j, -0.0379 - 0.0352j, -0.5724 - 0.2806j, -0.4796 - 1.1417j");
  
  ceps.compute(complexoutput, complexinput);
  
  if (!complexoutput.almostEqual(complexans, 20)) {
    complexans.debug(L"expected result:");
    complexoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // test using TRIGONOMETRIC
  //
  ceps.setImplementation(CONVENTIONAL);
  
  // the input data is real magnitude FFT of one cycle of a 1000 Hz
  // cos + a 2000 Hz sin sampled at 16 KHz
  //
  realinput.assign(L"1.0000, 8.7270, 8.0161, 1.4306, 0.7777, 0.5766, 0.4838, 0.4369, 0.4166");
  
  // expected result is complex IDFT of the log magnitude spectra
  //
  complexans.assign(L"0.511774+0j, 0.204663+0.416944j, -0.19542+0.281699j, -0.196205+0.00817621j, -0.0689259-0.0318726j, -0.0689259+0.0318726j, -0.196205-0.00817621j, -0.19542-0.281699j, 0.204663-0.416944j");
  
  // compute the result
  //
  ceps.compute(complexoutput, realinput);
  
  if (!complexoutput.almostEqual(complexans, 20)) {
    complexans.debug(L"expected result:");
    complexoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // the input data is complex FFT of one cycle of a 1000 Hz cos + a
  // 2000 Hz sin sampled at 16 KHz
  //
  complexinput.assign(L"1.0000, 8.6656 + 1.0334j, 2.5344 - 7.6049j, -0.8648 + 1.1396j, -0.5720 + 0.5270j, -0.4822 + 0.3162j, -0.4425 + 0.1955j, -0.4233 + 0.1082j,  -0.4152 + 0.0348j");
  
  // the input data is complex as before, expected result is complex
  // one cycle of a 1000 Hz sine
  //
  complexans.assign(L"0.1527 + 1.6359j, 0.9561 + 0.1609j, 0.2648 + 0.3548j, -0.2375 + 0.0479j, -0.1764 - 0.3916j, 0.1302 - 0.3504j, -0.0379 - 0.0352j, -0.5724 - 0.2806j, -0.4796 - 1.1417j");
  
  ceps.compute(complexoutput, complexinput);
  
  if (!complexoutput.almostEqual(complexans, 20)) {
    complexans.debug(L"expected result:");
    complexoutput.debug(L"wrong result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
 
  //---------------------------------------------------------------------------
  //
  // 5. 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;
}
