// file: $isip/class/algo/FilterBank/fb_02.cc
// version: $Id: fb_02.cc 8318 2002-07-10 00:53:25Z picone $
//

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

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

  //----------------------------------------------------------------------
  //
  // 0. preliminaries
  //
  //----------------------------------------------------------------------

  // output the class name
  //
  if (level_a > Integral::NONE) {
    String 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 destructor/constructor(s) and memory management methods
  //
  FilterBank mel_FilterBanker;
  FilterBank bark_FilterBanker;

  FilterBank::setGrowSize((long)500);

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

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  //--------------------------------------------------------------------------
  //
  // 2. required public methods
  //     i/o methods 
  //
  //--------------------------------------------------------------------------

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

  // test the i/o methods 
  // 
  FilterBank fb0o, fb0w;
  FilterBank fb1o, fb1w;
  FilterBank fb2o, fb2w;

  fb0o.set(FREQUENCY, TRIANGULAR, FULL, BARK, (long)30, (float)8000);
  fb0w.set(FREQUENCY, TRIANGULAR, FULL, MEL, (float)2.1, (float)8000);

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

  fb0o.write(tmp_file0, (long)0);
  fb0o.write(tmp_file1, (long)0);
  fb0w.write(tmp_file0, (long)1);
  fb0w.write(tmp_file1, (long)1);

  // 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 first value back
  //
  if (!fb1o.read(tmp_file0, (long)0) || (!fb1o.eq(fb0o))) {
    fb1o.debug(L"fb1o");
    fb0o.debug(L"fb0o");
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }
  
  if (!fb2o.read(tmp_file1, (long)0) || (!fb2o.eq(fb0o))) {
    fb2o.debug(L"fb2o");
    fb0o.debug(L"fb0o");
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }

  // read the second value back
  //
  if (!fb1w.read(tmp_file0, (long)1) || (!fb1w.eq(fb0w))) {
    fb1w.debug(L"fb1w");
    fb0w.debug(L"fb0w");
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }
  
  if (!fb2w.read(tmp_file1, (long)1) || (!fb2w.eq(fb0w))) {
    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
  //
  FilterBank fb_0;
  fb_0.setOrder(20);
  
  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"algorithm = FREQUENCY;\n");
  tmp_file2.puts(line);

  tmp_file2.put(CLASS_NAME, 10, -1);
  
  line.assign(L"algorithm = TIME;\n");
  tmp_file2.puts(line);

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

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

  fb_0.read(tmp_file2, (long)5); 
  
  fb_0.clear();
  fb_0.read(tmp_file2, (long)10);
  
  if (fb_0.getAlgorithm() != TIME) {
    return Error::handle(name(), L"readData", Error::TEST, __FILE__, __LINE__);
  }
    
  // close and delete the temporary files
  //
  tmp_file2.close();
  
  File::remove(tmp_filename2);
  
  // 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();
  }

  // test the set methods
  //
  FilterBank fb1;
  
  fb1.setAlgorithm(FREQUENCY);
  fb1.setImplementation(TRIANGULAR);
  fb1.setInputMode(FULL);
  fb1.setScale(MEL);
  fb1.setFrequencySampling(BANDWIDTH);
  fb1.setBandwidth((float)85.0);
			     
  if (fb1.algorithm_d != FREQUENCY) {
    return Error::handle(name(), L"setAlgorithm", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  if (fb1.implementation_d != TRIANGULAR) {
    return Error::handle(name(), L"setImplementation", Error::TEST,
			 __FILE__, __LINE__);
  }

    if (fb1.input_mode_d != FULL) {
    return Error::handle(name(), L"setInputMode", Error::TEST,
			 __FILE__, __LINE__);
  }

  if (fb1.scale_d != MEL) {
    return Error::handle(name(), L" setScale", Error::TEST,
			 __FILE__, __LINE__);
  }

  if (fb1.fsmp_d != BANDWIDTH) {
    return Error::handle(name(), L"setFrequencySampling", Error::TEST,
			 __FILE__, __LINE__);
  }

  if (fb1.bandwidth_d != (float)85.0) {
    return Error::handle(name(), L"setBandwidth", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  fb1.setFrequencySampling(ORDER);
  fb1.setOrder((long)12.0);
  
  if (fb1.fsmp_d != ORDER) {
    return Error::handle(name(), L" setFrequencySampling", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  if (fb1.order_d != (long)12.0) {
    return Error::handle(name(), L"setBandwidth", Error::TEST,
			 __FILE__, __LINE__);
  }

  fb1.set(FREQUENCY, RAISED_COSINE, FULL, BARK, (long)11, (float)8000);
    
  if (fb1.algorithm_d != FREQUENCY) {
    return Error::handle(name(), L"setAlgorithm", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  if (fb1.implementation_d != RAISED_COSINE) {
    return Error::handle(name(), L"setImplementation", Error::TEST,
			 __FILE__, __LINE__);
  }

  if (fb1.input_mode_d != FULL) {
    return Error::handle(name(), L"setInputMode", Error::TEST,
			 __FILE__, __LINE__);
  }

  if (fb1.scale_d != BARK) {
    return Error::handle(name(), L" setScale", Error::TEST,
			 __FILE__, __LINE__);
  }

  if (fb1.fsmp_d != ORDER) {
    return Error::handle(name(), L"setFrequencySampling", Error::TEST,
			 __FILE__, __LINE__);
  }

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

  fb1.set(FREQUENCY, UNIFORM, SYMMETRIC, BARK, (float)90.0, (float)8000);
    
  if (fb1.algorithm_d != FREQUENCY) {
    return Error::handle(name(), L"setAlgorithm", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  if (fb1.implementation_d != UNIFORM) {
    return Error::handle(name(), L"setImplementation", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  if (fb1.input_mode_d != SYMMETRIC) {
    return Error::handle(name(), L"setInputMode", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  if (fb1.scale_d != BARK) {
    return Error::handle(name(), L" setScale", Error::TEST,
			 __FILE__, __LINE__);
  }

  if (fb1.fsmp_d != BANDWIDTH) {
    return Error::handle(name(), L"setFrequencySampling", Error::TEST,
			 __FILE__, __LINE__);
  }

  if (fb1.bandwidth_d != (float)90.0) {
    return Error::handle(name(), L"setBandwidth", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test the get methods
  //
  ALGORITHM algo = FREQUENCY;
  IMPLEMENTATION impl = UNIFORM;
  SCALE scale = BARK;
  FREQUENCY_SAMPLING fsmp = BANDWIDTH;
  float bandwidth = (float)90.0;
  
  ALGORITHM algo1 = fb1.getAlgorithm();
  
  if (algo1 != algo) {
    return Error::handle(name(), L"getAlgorithm", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  IMPLEMENTATION impl1 = fb1.getImplementation();
  
  if (impl1 != impl) {
    return Error::handle(name(), L"getImplementation", Error::TEST,
			 __FILE__, __LINE__);
  }

  SCALE scale1 = fb1.getScale();

  if (scale1 != scale) {
    return Error::handle(name(), L"getScale", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  FREQUENCY_SAMPLING fsmp1 = fb1.getFrequencySampling();

  if (fsmp1 != fsmp) {
    return Error::handle(name(), L"getFrequencyScaling", Error::TEST,
			 __FILE__, __LINE__);
  }

  float bandwidth1 = fb1.getBandwidth();
  if (bandwidth1 != bandwidth) {
    return Error::handle(name(), L"getBandwidth", 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();
  }
  
  // declare the test variables
  //   
  FilterBank fb2;
  
  VectorFloat in;
  VectorFloat out;

  fb2.set(FilterBank::FREQUENCY, FilterBank::TRIANGULAR,
	  FilterBank::FULL, MEL, (long)24, (float)8000.0);
  fb2.setFrequencySampling(FilterBank::ORDER);
 
  // input vector, magnitude of the fourier transform spectrum
  //
  in.assign(L"58.682139, 27.352428, 40.681365, 119.681669, 392.449627, 432.773435, 345.984134, 175.399777, 97.606844, 84.056130, 17.666764, 183.297954, 353.490908, 299.106977, 225.944408, 217.028463, 147.725524, 79.667382, 66.890813, 159.987856, 277.815117, 348.145957, 319.758575, 195.799156, 110.001231, 204.067351, 277.309939, 297.949248, 332.091696, 331.723196, 255.286538, 273.359881, 272.285779, 311.599805, 345.027893, 212.905936, 161.199889, 135.805134, 304.131899, 476.053649, 429.051692, 220.269884, 191.161898, 252.903533, 399.897320, 564.444916, 614.706096, 527.111672, 216.314477, 143.531824, 224.984319, 177.870041, 119.225657, 119.125281, 191.705437, 331.646061, 331.707291, 258.307997, 239.035180, 188.409972, 208.742715, 226.295477, 124.783300, 102.649914, 309.236580, 608.241764, 716.499312, 558.650286, 292.596118, 122.329684, 119.593925, 289.007079, 458.072794, 502.682422, 258.833866, 82.387426, 234.900435, 264.945319, 186.505473, 77.865519, 255.331651, 269.183680, 227.671808, 299.592818, 298.635834, 304.989029, 321.191356, 274.161865, 237.735515, 265.636801, 207.432039, 17.495657, 195.379949, 209.062702, 155.231238, 69.315452, 72.466060, 170.337070, 218.257217, 296.282090, 305.743929, 265.765756, 337.868700, 404.684734, 324.115077, 156.992589, 60.598958, 172.276998, 194.153034, 180.096359, 228.302885, 343.293707, 392.712837, 443.115785, 264.347343, 41.262893, 26.965390, 153.590370, 291.268403, 322.579844, 156.570626, 261.273282, 449.424058, 368.535495, 200.616840, 90.091156, 162.905182, 274.145543");

  // expected result, the amplitude values in the filter banks  
  //
  VectorFloat ans;
  ans.assign(L"93.245049, 567.165213, 722.184125, 287.052976, 320.802828, 764.999928, 445.762165, 532.648583, 889.815068, 865.428979, 1168.719488, 1138.940633, 1317.224901, 1749.744130, 1799.689392, 1192.632330, 1418.028593, 2547.179765, 2052.749578, 1842.888912, 1893.310976, 1887.059615, 2375.503550, 2523.254046");

  // FBA expects a full spectrum as input, but only uses the first
  // half of it. double the length to "fake" the second half"
  //
  in.setLength(in.length() * 2);
  fb2.compute(out, in, AlgorithmData::GENERIC);
  if (!out.almostEqual(ans, (long)19)) {
    in.debug(L"input:");
    out.debug(L"wrong result:");
    ans.debug(L"expected result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  if (level_a > Integral::BRIEF) {
    fb2.cen_freq_d.debug(L"mel central frequencies for 8kHz:");
  }
  
  VectorFloat cen_8k(fb2.cen_freq_d);

  // test whether the mel width set to 85.84258
  // match the result when the filter bank order is set to 24
  //
  fb2.setFrequencySampling(FilterBank::BANDWIDTH);
  fb2.setBandwidth(85.84258);
  fb2.compute(out, in, AlgorithmData::GENERIC);
  if (!out.almostEqual(ans, (long)19)) {
    in.debug(L"input:");
    out.debug(L"wrong result:");
    ans.debug(L"expected result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }

  // test for 16kHz with the same width
  // store the results and compare with 8kHz
  //
  fb2.setSampleFrequency(16000);
  fb2.setFrequencySampling(FilterBank::BANDWIDTH);
  fb2.compute(out, in, AlgorithmData::GENERIC);
  
  if (level_a > Integral::BRIEF) {
    fb2.cen_freq_d.debug(L"mel central frequencies for 16kHz:");
  }
  
  // store the 16kHz data, retain only 24 bins
  //
  VectorFloat cen_16k(fb2.cen_freq_d);
  cen_16k.setLength(26);

  // compare the results for 8kHz and 16kHz
  //
  if (!cen_16k.almostEqual(cen_8k)) {
    cen_8k.debug(L"8 kHz central frequencies:");
    return Error::handle(name(), L"mismatch between 8kHz and 16kHz",
			 Error::TEST, __FILE__, __LINE__);
  }
  
  // test the FREQUENCY algorithm for zero input vector
  //
  VectorFloat zeros(L"0, 0, 0, 0, 0, 0, 0, 0, 0, 0");
  fb2.setOrder(3);
  fb2.compute(out, zeros, AlgorithmData::GENERIC);
  ans.assign(L"0, 0, 0");

  if (!out.almostEqual(ans)) {
    zeros.debug(L"input:");
    out.debug(L"wrong result:");
    ans.debug(L"expected result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // test the FREQUENCY algorithm for constant input vector
  //
  VectorFloat ones(L"1, 1, 1, 1, 1");
  ones.setLength(ones.length() * 2);
  fb2.setOrder(3);
  fb2.compute(out, ones, AlgorithmData::GENERIC);
  ans.assign(L"0.11177, 1.1618, 1.77692");
  
  if (!out.almostEqual(ans)) {
    ones.debug(L"input:");
    out.debug(L"wrong result:");
    ans.debug(L"expected result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // change scale to linear
  //
  fb2.setScale(LINEAR);
  
  // test the linear scale for zero input vector
  //
  // expected result, the amplitude values in the filter bank
  //
  ans.assign(L"0, 0, 0");

  // compute the filter bank coefficients
  //
  fb2.compute(out, zeros, AlgorithmData::GENERIC);
  if (!out.almostEqual(ans)) {
    zeros.debug(L"input:");
    out.debug(L"wrong result:");
    ans.debug(L"expected result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }

  // test the linear scale for constant input vector
  //
  fb2.setOrder(4);
  
  // expected result, the amplitude values in the filter bank
  //
  ans.assign(L"1, 1, 1, 1");

  // compute the filter bank coefficients
  //
  fb2.compute(out, ones, AlgorithmData::GENERIC);
  if (!out.almostEqual(ans)) {
    ones.debug(L"input:");
    out.debug(L"wrong result:");
    ans.debug(L"expected result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }

  // test the BARK scale
  //
  fb2.set(FilterBank::FREQUENCY, FilterBank::TRIANGULAR,
	  FilterBank::FULL, BARK,
	  (long)24, (float)16000.0);
  fb2.setFrequencySampling(FilterBank::ORDER);
  
  // input vector, magnitude of the fourier transform spectrum
  //
  in.assign(L"58.682139, 27.352428, 40.681365, 119.681669, 392.449627, 432.773435, 345.984134, 175.399777, 97.606844, 84.056130, 17.666764, 183.297954, 353.490908, 299.106977, 225.944408, 217.028463, 147.725524, 79.667382, 66.890813, 159.987856, 277.815117, 348.145957, 319.758575, 195.799156, 110.001231, 204.067351, 277.309939, 297.949248, 332.091696, 331.723196, 255.286538, 273.359881, 272.285779, 311.599805, 345.027893, 212.905936, 161.199889, 135.805134, 304.131899, 476.053649, 429.051692, 220.269884, 191.161898, 252.903533, 399.897320, 564.444916, 614.706096, 527.111672, 216.314477, 143.531824, 224.984319, 177.870041, 119.225657, 119.125281, 191.705437, 331.646061, 331.707291, 258.307997, 239.035180, 188.409972, 208.742715, 226.295477, 124.783300, 102.649914, 309.236580, 608.241764, 716.499312, 558.650286, 292.596118, 122.329684, 119.593925, 289.007079, 458.072794, 502.682422, 258.833866, 82.387426, 234.900435, 264.945319, 186.505473, 77.865519, 255.331651, 269.183680, 227.671808, 299.592818, 298.635834, 304.989029, 321.191356, 274.161865, 237.735515, 265.636801, 207.432039, 17.495657, 195.379949, 209.062702, 155.231238, 69.315452, 72.466060, 170.337070, 218.257217, 296.282090, 305.743929, 265.765756, 337.868700, 404.684734, 324.115077, 156.992589, 60.598958, 172.276998, 194.153034, 180.096359, 228.302885, 343.293707, 392.712837, 443.115785, 264.347343, 41.262893, 26.965390, 153.590370, 291.268403, 322.579844, 156.570626, 261.273282, 449.424058, 368.535495, 200.616840, 90.091156, 162.905182, 274.145543");

  // expected result, the amplitude values in the filter banks  
  //
  ans.assign(L"42.2956, 168.098, 548.709, 519.76, 290.34, 126.417, 158.263, 558.984, 474.401, 315.25, 283.504, 766.251, 604.564, 959.185, 1152.48, 1188.05, 1721.5, 2464.58, 1682.24, 2588.35, 3255.67, 2934.38, 2842.28, 3935.95");

  // FBA expects a full spectrum as input, but only uses the first
  // half of it. double the length to "fake" the second half"
  //
  in.setLength(in.length() * 2);
  fb2.compute(out, in, AlgorithmData::GENERIC);
  if (!out.almostEqual(ans, (long)19)) {
    in.debug(L"input:");
    out.debug(L"wrong result:");
    ans.debug(L"expected result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }

  // test the RAISED_COSINE implementation
  //
  fb2.set(FilterBank::FREQUENCY, FilterBank::RAISED_COSINE,
	  FilterBank::FULL, MEL,
	  (long)24, (float)8000);
  fb2.setFrequencySampling(FilterBank::ORDER);
  
  // input vector, magnitude of the fourier transform spectrum
  //
  in.assign(L"58.682139, 27.352428, 40.681365, 119.681669, 392.449627, 432.773435, 345.984134, 175.399777, 97.606844, 84.056130, 17.666764, 183.297954, 353.490908, 299.106977, 225.944408, 217.028463, 147.725524, 79.667382, 66.890813, 159.987856, 277.815117, 348.145957, 319.758575, 195.799156, 110.001231, 204.067351, 277.309939, 297.949248, 332.091696, 331.723196, 255.286538, 273.359881, 272.285779, 311.599805, 345.027893, 212.905936, 161.199889, 135.805134, 304.131899, 476.053649, 429.051692, 220.269884, 191.161898, 252.903533, 399.897320, 564.444916, 614.706096, 527.111672, 216.314477, 143.531824, 224.984319, 177.870041, 119.225657, 119.125281, 191.705437, 331.646061, 331.707291, 258.307997, 239.035180, 188.409972, 208.742715, 226.295477, 124.783300, 102.649914, 309.236580, 608.241764, 716.499312, 558.650286, 292.596118, 122.329684, 119.593925, 289.007079, 458.072794, 502.682422, 258.833866, 82.387426, 234.900435, 264.945319, 186.505473, 77.865519, 255.331651, 269.183680, 227.671808, 299.592818, 298.635834, 304.989029, 321.191356, 274.161865, 237.735515, 265.636801, 207.432039, 17.495657, 195.379949, 209.062702, 155.231238, 69.315452, 72.466060, 170.337070, 218.257217, 296.282090, 305.743929, 265.765756, 337.868700, 404.684734, 324.115077, 156.992589, 60.598958, 172.276998, 194.153034, 180.096359, 228.302885, 343.293707, 392.712837, 443.115785, 264.347343, 41.262893, 26.965390, 153.590370, 291.268403, 322.579844, 156.570626, 261.273282, 449.424058, 368.535495, 200.616840, 90.091156, 162.905182, 274.145543");

  // expected result, the amplitude values in the filter banks  
  //
  ans.assign(L"169.642, 913.332, 1272.85, 694.356, 681.822, 1190.28, 978.15, 1233.35, 1410.31, 1556.33, 2096.33, 1796.04, 2319.23, 3640.08, 3047.91, 2311.44, 2767.39, 3745.11, 4082.67, 3719.39, 3251.61, 3418.82, 4299.72, 4630.3");

  // FBA expects a full spectrum as input, but only uses the first
  // half of it. double the length to "fake" the second half"
  //
  in.setLength(in.length() * 2);
  fb2.compute(out, in, AlgorithmData::GENERIC);
  if (!out.almostEqual(ans, (long)19)) {
    in.debug(L"input:");
    out.debug(L"wrong result:");
    ans.debug(L"expected result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }

  // declare the test variables for the TIME algorithm
  //
  in.assign(L"1, 2, 3, 4, 5, 6, 7,8 ,9");
  Vector<VectorFloat> out1;
  VectorFloat ans1(L"1, 0.5, 3.7, 0.25, 9.35, -3.525, 21.575, -19.0275, 54.1175");
  VectorFloat ans2(L"2, 3.0, 3.3, 5.65, 5.52, 6.215, 9.233, 7.7905, 9.7412 ");

  // set the parameters of FilterBank
  //
  fb2.setAlgorithm(FilterBank::TIME);
  fb2.setImplementation(FilterBank::CCDE);
  fb2.setFiltersParamFile(L"$ISIP_DEVEL/class/algo/FilterBank/filters.sof");
  
  fb2.compute(out1, in);
  
  if ((out1.length() != 2) || (!out1(0).almostEqual(ans1))
      || (!out1(1).almostEqual(ans2))) {
    in.debug(L"input:");
    out1.debug(L"wrong result:");
    ans1.debug(L"expected result:");
    ans2.debug(L"expected result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }
  
  // test the FILTER algorithm for zero input vector
  //
  FilterBank fb3;
  
  fb3.setAlgorithm(FilterBank::TIME);
  fb3.setImplementation(FilterBank::CCDE);
  fb3.setFiltersParamFile(L"$ISIP_DEVEL/class/algo/FilterBank/filters.sof");
  in.assign(L"0, 0, 0");
  ans1.assign(L"0, 0, 0");
  ans2.assign(L"0, 0, 0");

  fb3.compute(out1, in);
  
  if ((out1.length() != 2) || (!out1(0).almostEqual(ans1))
      || (!out1(1).almostEqual(ans2))) {
    in.debug(L"input:");
    out1.debug(L"wrong result:");
    ans1.debug(L"expected result:");
    ans2.debug(L"expected result:");
    return Error::handle(name(), L"compute", Error::TEST, __FILE__, __LINE__);
  }

  // test the FILTER algorithm for constant input vector
  //
  FilterBank fb4;
  
  fb4.setAlgorithm(FilterBank::TIME);
  fb4.setImplementation(FilterBank::CCDE);
  fb4.setFiltersParamFile(L"$ISIP_DEVEL/class/algo/FilterBank/filters.sof");
  in.assign(L"1, 1, 1");
  ans1.assign(L"1, -0.5, 3.2");
  ans2.assign(L"2, 1, 0.3");  

  fb4.compute(out1, in);
  
  if ((out1.length() != 2) || (!out1(0).almostEqual(ans1))
      || (!out1(1).almostEqual(ans2))) {
    in.debug(L"input:");
    out1.debug(L"wrong result:");
    ans1.debug(L"expected result:");
    ans2.debug(L"expected 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) {
    String output(L"diagnostics passed for class ");
    output.concat(name());
    output.concat(L"\n");
    Console::put(output);
  }

  // exit gracefully
  //
  return true;
}
