// file: $isip_ifc/class/algo/MutualInformation/mi_02.cc
// version: $Id: mi_02.cc 10519 2006-03-20 22:04:28Z pannuri $
//

// isip include files
//
#include "MutualInformation.h"
#include "Generator.h"
#include <Console.h>

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

  // set indentation
  //  
  if (level_a > Integral::NONE) {
    Console::put(L"testing required public methods: class constructors...\n");
    Console::increaseIndention();
  }
  
  MutualInformation mi0;
  mi0.setAlgorithm(AUTO);
  mi0.setImplementation(HISTOGRAM);
  mi0.setNormalization(NONE);
  MutualInformation mi1(mi0);
   
  if (!mi1.eq(mi0)) {
    return Error::handle(name(), L"copy constructor/eq", 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
    //
    
    MutualInformation::setGrowSize((long)500);    
    MutualInformation* pmi = new MutualInformation();

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

  // 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: io methods...\n");
    Console::increaseIndention();
  }

  // declare a reference object
  //
  mi0.setAlgorithm(AUTO);
  mi0.setImplementation(HISTOGRAM);
  mi0.setNormalization(NONE);

  // 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);
  mi0.write(tmp_file0, (long)0);
  mi0.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 object back
  //
  mi1.read(tmp_file0, (long)0);
  mi1.init();

  if (!mi0.eq(mi1)) {
    return Error::handle(name(), L"i/o", Error::TEST,
			 __FILE__, __LINE__);
  }
     
  //  mi1.read(tmp_file1, (long)0);
  //   mi1.init();

  //    if (!mi0.eq(mi1)) {
  //       return Error::handle(name(), L"i/o", 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();
  }
  
  //---------------------------------------------------------------------------
  //
  // 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 the parameters manually
  //
  mi0.setAlgorithm(AUTO);
  mi0.setImplementation(HISTOGRAM);
  mi0.setNormalization(NONE);
  mi0.setOrder(27);
  mi0.setNumBins(20);

  // check that the values were set
  //
  if (mi0.algorithm_d != AUTO) {
    return Error::handle(name(), L"setAlgorithm", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (mi0.implementation_d != HISTOGRAM) {
    return Error::handle(name(), L"setImplementation", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (mi0.normalization_d != NONE) {
    return Error::handle(name(), L"setNormalization", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (mi0.order_d != (Long)27) {
    return Error::handle(name(), L"setOrder", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (mi0.num_bins_d != (Long)20) {
    return Error::handle(name(), L"setNumBins", 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();
  }

  // establish some signals
  //
  VectorFloat input(L"0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15");
  VectorFloat output;
  VectorFloat zeros(L"0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0");
  VectorFloat ones(L"1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1");
  VectorFloat ans;

  // eastablishing some signals using the generator class
  //

  // first generating two sine signals with different frequencies
  //
  Generator gen_sine_1, gen_sine_2;
  VectorFloat sine_generation_1, sine_generation_2, dummy;
  gen_sine_1.set(Generator::SINE);
  gen_sine_2.set(Generator::SINE);
  gen_sine_1.setSampleFrequency((float)8000);
  gen_sine_2.setSampleFrequency((float)8000);
  gen_sine_1.setGenSampleFrequency((float)8000);
  gen_sine_2.setGenSampleFrequency((float)8000);
  gen_sine_1.setSine((float)50, (float)1, (float)0, (float)0);
  gen_sine_2.setSine((float)300, (float)1, (float)0, (float)0);
  gen_sine_1.setSignalDuration(1);
  gen_sine_2.setFrameDuration(1);
  gen_sine_1.compute(sine_generation_1, dummy);
  gen_sine_2.compute(sine_generation_2, dummy);

  // algorithm: AUTO
  //

  // testing with some random time series
  //
  mi0.setAlgorithm(AUTO);
  mi0.setImplementation(HISTOGRAM);
  mi0.setNormalization(NONE);
  mi0.setOrder(4);
  mi0.setNumBins(5);  
  mi0.compute(ans, input);

  VectorFloat expected1 = (L"1.60944, 1.11071, 1.30133, 0.693147");

  if (!ans.almostEqual(expected1)) {
    ans.debug(L"wrong answer");
    expected1.debug(L"correct answer");
    return  Error::handle(name(),
  			  L"compute (AUTO) from sampled data",
  			  ERR, __FILE__, __LINE__);
  }

  // testing with some sine with frequency 50
  //
  mi0.setAlgorithm(AUTO);
  mi0.setImplementation(HISTOGRAM);
  mi0.setNormalization(NONE);
  mi0.setOrder(4);
  mi0.setNumBins(5);  
  mi0.compute(ans, sine_generation_1);
 
  VectorFloat expected2 = (L"1.48081, 1.11462, 0.903323, 0.768677");

  if (!ans.almostEqual(expected2)) {
    ans.debug(L"wrong answer");
    expected2.debug(L"correct answer");
    return  Error::handle(name(),
  			  L"compute (AUTO) from sampled data with sine (50)",
  			  ERR, __FILE__, __LINE__);
  }
  
  // testing with sine with frequency 300
  //
  mi0.setAlgorithm(AUTO);
  mi0.setImplementation(HISTOGRAM);
  mi0.setNormalization(NONE);
  mi0.setOrder(4);
  mi0.setNumBins(5);  
  mi0.compute(ans, sine_generation_2);

  VectorFloat expected3 = (L"1.54578, 0.875128, 0.853796, 0.543551");

  if (!ans.almostEqual(expected3)) {
    ans.debug(L"wrong answer");
    expected3.debug(L"correct answer");
    return  Error::handle(name(),
  			  L"compute (AUTO) from sampled data with sine (300)",
  			  ERR, __FILE__, __LINE__);
  }

  // testing cross mutual information
  //
  mi0.setAlgorithm(CROSS);
  mi0.setImplementation(HISTOGRAM);
  mi0.setNormalization(NONE);
  mi0.setOrder(4);
  mi0.setNumBins(5);  
  mi0.compute(ans, sine_generation_1, sine_generation_2);

  VectorFloat expected4 = (L"0.336374, 0.351493, 0.407856, 0.496065");
 
  if (!ans.almostEqual(expected4)) {
    ans.debug(L"wrong answer");
    expected4.debug(L"correct answer");
    return  Error::handle(name(),
  			  L"compute (CROSS) from sampled data with sine (300)",
  			  ERR, __FILE__, __LINE__);
  }
    
  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }

  //---------------------------------------------------------------------------
  //
  // 5. print completion message
  //
  //---------------------------------------------------------------------------

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

  // possibly print completion message
  //  
  if (level_a > Integral::NONE) {
    String output(L"diagnostics completed successfully for class ");
    output.concat(name());
    output.concat(L"\n");
    Console::put(output);
  }

  // exit gracefully
  //
  return true;
}
