// file: $isip/class/stat/StatisticalModel/sm_02.cc
// version: $Id: sm_02.cc 10640 2007-01-27 02:36:04Z tm334 $
//

// isip include files
//
#include "StatisticalModel.h"
#include <Console.h>
#include <GaussianModel.h>
#include <MixtureModel.h>
#include <SupportVectorModel.h>

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

  // create data for testing of Gaussian model
  //
  VectorFloat test_mean(L"1, 2, 3");
  MatrixFloat test_covar(3, 3, L"1, 2, 3", Integral::DIAGONAL);

  // create data for testing of support vector model
  //
  Vector<VectorFloat> support_vectors(3);
  support_vectors(0) = (L"1, 2, 3");
  support_vectors(1) = (L"4, 5, 6");
  support_vectors(2) = (L"7, 8, 9");
  VectorFloat alphas(L"0.4, 0.1, 0.5");
  VectorFloat kernel_constants(L"0.3");
  Kernel::ALGORITHM kernel_algorithm = Kernel::RBF;
  
  // test constructors
  //
  StatisticalModel stat0(GAUSSIAN_MODEL);
  GaussianModel gaus0;
  gaus0.setMean(test_mean);
  gaus0.setCovariance(test_covar);

  StatisticalModel stat00(SUPPORT_VECTOR_MODEL);
  SupportVectorModel svm0;
  svm0.setNumSupportVectors(3);
  for (long i = 0; i < 3; i++) {
    svm0.getSupportVector(i) = support_vectors(i);
    svm0.getAlpha(i) = alphas(i);
  }
  svm0.getKernel(0).setAlgorithm(kernel_algorithm);
  svm0.getKernel(0).setConstants(kernel_constants);
  
  // test the interface assign method
  //
  stat0.assign(gaus0);
  stat00.assign(svm0);

  // test the interface eq method
  //
  if (!stat0.eq(gaus0)) {
    return Error::handle(name(), L"assign/eq gauss", Error::TEST, __FILE__,
			 __LINE__);
  }
  if (!stat00.eq(svm0)) {
    return Error::handle(name(), L"assign/eq svm", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // test the copy constructor and the required eq method
  //
  StatisticalModel stat1(stat0);
  if (!stat1.eq(gaus0) || !stat1.eq(stat0)) {
    return Error::handle(name(), L"copy constructor gauss",
			 Error::TEST, __FILE__, __LINE__);
  }
  StatisticalModel stat11(stat00);
  if (!stat11.eq(svm0) || !stat11.eq(stat00)) {
    return Error::handle(name(), L"copy constructor",
			 Error::TEST, __FILE__, __LINE__);
  }
  
  // test the setDebug method
  //
  setDebug(debug_level_d);

  // test the clear method
  //
  stat1.clear();
  if (stat1.virtual_model_d != (StatisticalModelBase*)&NO_STAT_MODEL) {
    return Error::handle(name(), L"clear - gauss", Error::TEST, __FILE__,
			 __LINE__);
  }
  stat11.clear();
  if (stat11.virtual_model_d != (StatisticalModelBase*)&NO_STAT_MODEL) {
    return Error::handle(name(), L"clear - svm", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // test the operator= method
  //
  stat1 = stat0;
  if (!stat1.eq(gaus0) || !stat1.eq(stat0)) {
    return Error::handle(name(), L"operator= gauss", Error::TEST, __FILE__,
			 __LINE__);
  }
  stat11 = stat00;
  if (!stat11.eq(svm0) || !stat11.eq(stat00)) {
    return Error::handle(name(), L"operator= svm", Error::TEST, __FILE__,
			 __LINE__);
  }
  
  // test the memory management methods
  //
  StatisticalModel::setGrowSize(5);
  StatisticalModel* stat_ptr0 = new StatisticalModel[10];
  StatisticalModel* stat_ptr1 = new StatisticalModel(GAUSSIAN_MODEL);
  StatisticalModel* stat_ptr2 = new StatisticalModel(SUPPORT_VECTOR_MODEL);

  // ISO C++ forbids initialization in array new
  //
  //StatisticalModel* stat_ptr3 = new StatisticalModel[10](GAUSSIAN_MODEL);
  //StatisticalModel* stat_ptr4 = new StatisticalModel[10](SUPPORT_VECTOR_MODEL);

  delete [] stat_ptr0;
  delete stat_ptr1;
  delete stat_ptr2;

  // ISO C++ forbids initialization in array new
  //
  //delete [] stat_ptr3;
  //delete [] stat_ptr4;

  // test the i/o methods
  //
  StatisticalModel write_gauss(GAUSSIAN_MODEL);
  write_gauss.assign(gaus0);
  StatisticalModel write_svm(GAUSSIAN_MODEL); // it works anyway
  write_svm.assign(svm0);

  // create two models to read with
  //
  StatisticalModel read_text;
  StatisticalModel read_bin;
  StatisticalModel read_text_svm;
  StatisticalModel read_bin_svm;
  
  // we need binary and text sof files
  //
  String tmp_filename0;
  Integral::makeTemp(tmp_filename0);
  String tmp_filename1;
  Integral::makeTemp(tmp_filename1);
  String tmp_filename2;
  Integral::makeTemp(tmp_filename2);
  String tmp_filename3;
  Integral::makeTemp(tmp_filename3);

  // 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);
  Sof tmp_file2;
  tmp_file2.open(tmp_filename2, File::WRITE_ONLY, File::TEXT);
  Sof tmp_file3;
  tmp_file3.open(tmp_filename3, File::WRITE_ONLY, File::BINARY);

  write_gauss.write(tmp_file0, 0);
  write_gauss.write(tmp_file1, 0);
  write_svm.write(tmp_file2, 0);
  write_svm.write(tmp_file3, 0);
  
  // close the files
  //
  tmp_file0.close();
  tmp_file1.close();
  tmp_file2.close();
  tmp_file3.close();

  // open the files in read mode
  //
  tmp_file0.open(tmp_filename0);
  tmp_file1.open(tmp_filename1);
  tmp_file2.open(tmp_filename2);
  tmp_file3.open(tmp_filename3);

  // read the value back
  //
  read_text.read(tmp_file0, 0);
  if (!read_text.eq(write_gauss)) {
    read_text.debug(L"read_text");
    write_gauss.debug(L"write_gauss");
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }

  read_bin.read(tmp_file1, 0);
  if (!read_bin.eq(write_gauss)) {
    read_bin.debug(L"read_bin");
    write_gauss.debug(L"write_gauss");
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }

  read_text_svm.read(tmp_file2, 0);
  if (!read_text_svm.eq(write_svm)) {
    read_text.debug(L"read_text_svm");
    write_svm.debug(L"write_svm");
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }

  read_bin_svm.read(tmp_file3, 0);
  if (!read_bin_svm.eq(write_svm)) {
    read_bin_svm.debug(L"read_bin_svm");
    write_svm.debug(L"write_svm");
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }

  // close the temporary files
  //
  tmp_file0.close();
  tmp_file1.close();
  tmp_file2.close();
  tmp_file3.close();
  
  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  //---------------------------------------------------------------------------
  //
  // 2. class-specific public methods:
  //     extensions to required methods     
  //
  //---------------------------------------------------------------------------

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

  // test the virtual constructor
  //
  StatisticalModel stat_virt(gaus0);
  if (!stat_virt.eq(gaus0)) {
    return Error::handle(name(), L"constructor - gauss", Error::TEST, __FILE__,
			 __LINE__);
  }
  StatisticalModel stat_virt0(svm0);
  if (!stat_virt0.eq(svm0)) {
    return Error::handle(name(), L"constructor - svm", Error::TEST, __FILE__,
			 __LINE__);
  }

  // the virtual assign and eq methods have already been tested
  //

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

  // the setType method is tested in all of the constructors and assign methods
  //

  // test the getType method
  //
  StatisticalModel stat_type(GAUSSIAN_MODEL);
  if (stat_type.getType() != GAUSSIAN_MODEL) {
    return Error::handle(name(), L"setType/getType - gauss",
			 Error::TEST, __FILE__, __LINE__);
  }
  StatisticalModel stat_type0(SUPPORT_VECTOR_MODEL);
  if (stat_type0.getType() != SUPPORT_VECTOR_MODEL) {
    return Error::handle(name(), L"setType/getType - svm",
			 Error::TEST, __FILE__, __LINE__);
  }

  // test the setMode and getMode methods
  //
  StatisticalModel stat_mode(gaus0);
  stat_mode.setMode(NONE);
  if (stat_mode.getMode() != NONE) {
    return Error::handle(name(), L"setMode/getMode",
			 Error::TEST, __FILE__, __LINE__);
  }
  
  stat_mode.setMode(PRECOMPUTE);
  if (stat_mode.getMode() != PRECOMPUTE) {
    return Error::handle(name(), L"setMode/getMode",
			 Error::TEST, __FILE__, __LINE__);
  }
  
  StatisticalModel stat_mode0(svm0);
  stat_mode0.setMode(NONE);
  if (stat_mode0.getMode() != NONE) {
    return Error::handle(name(), L"setMode/getMode",
			 Error::TEST, __FILE__, __LINE__);
  }
  
  stat_mode0.setMode(PRECOMPUTE);
  if (stat_mode0.getMode() != PRECOMPUTE) {
    return Error::handle(name(), L"setMode/getMode",
			 Error::TEST, __FILE__, __LINE__);
  }

  // create test data for the likelihood methods
  //
  VectorFloat mean(L"-3.639462, 1.653617, 1.787559, 4.262538, 1.369591, 2.418731, 0.2277095, 1.344989, 0.7442688, 0.8819447, 0.5029327, 0.6623194, -7.385917, -0.01989461, 0.03300723, 3.038317e-04, 7.731465e-02, 2.036626e-02, 1.392890e-02, 1.471747e-02, 2.386167e-02, -1.337681e-02, 1.569910e-02, -7.796203e-05, 9.159852e-03, -3.887357e-02, 1.891592e-02, 3.389170e-03, 6.476890e-03, -9.673509e-03, -3.964308e-03, -1.162331e-02, -5.141579e-03, -8.838848e-03, -3.854857e-03, -2.433208e-05, -3.092395e-03, -5.406926e-04, 1.159509e-02 ");
  MatrixFloat covar(39, 39, L"1.433564e+01, 1.975537e+01, 1.671448e+01, 2.977089e+01, 2.330977e+01, 2.712510e+01, 2.764725e+01, 3.032939e+01, 3.006948e+01, 2.652304e+01, 2.420893e+01, 2.028848e+01, 5.845720e+00, 2.440532e-01, 4.785362e-01, 7.022072e-01, 1.055732e+00, 1.272656e+00, 1.545274e+00, 1.733616e+00, 1.836289e+00, 1.909358e+00, 1.861139e+00, 1.752789e+00, 1.551828e+00, 2.726359e-02, 4.366880e-02, 8.581409e-02, 1.317306e-01, 1.942055e-01, 2.418336e-01, 2.950188e-01, 3.333847e-01, 3.536546e-01, 3.684548e-01, 3.604752e-01, 3.396829e-01, 3.016612e-01, 4.171541e-03", Integral::DIAGONAL);
  VectorFloat input(L"-1.05023813258004e+00,-7.77674896710878e+00,1.43041098327926e+01,-6.21318461516028e+00,-8.64903872637757e+00,1.02604097385083e+01,-5.57177352692909e+00,5.09477660171566e+00,-4.09609275292737e+00,5.96100219381628e+00,3.76151348252777e-01,-2.97277346115335e+00,-2.30355781300591e+00,7.66914363607964e-01,1.27304216160438e-01,-3.20960700374856e-01,-1.93367994699623e+00,-4.25686341458929e-01,4.12861672859121e-01,-3.16990107434180e-01,1.81895735084098e+00,1.97179456310064e+00,-5.23509954876174e-01,-2.63924777075424e-01,3.76853360820597e-03,6.07853048112972e-04,9.75499698349679e-02,5.80349863491104e-01,-4.96041065023962e-01,-5.45823432246360e-02,-1.61513044481162e-01,2.12215102626400e-01,2.76088189007932e-01,4.25378696913581e-01,-3.04615852820205e-02,-2.56536949288540e-02,2.44496596888947e-01,6.41653297530483e-01,-6.79771984752767e-02");
  Float result = -69.848372;
  
  // test the likelihood methods
  //
  GaussianModel model0;
  model0.setMean(mean);
  model0.setCovariance(covar);

  StatisticalModel stat_like(model0);
  Float score = stat_like.getLogLikelihood(input);
  if (!score.almostEqual(result)) {
    result.debug(L"expected result");
    score.debug(L"actual result");
    model0.debug(L"model");
    return Error::handle(name(), L"getLogLikelihood",
			 Error::TEST, __FILE__, __LINE__);
  }

  // evaluate the likelihood of the model
  //
  score = model0.getLikelihood(input);

  if (!score.almostEqual(Integral::exp(result))) {
    Float exp_results = Integral::exp(result);
    exp_results.debug(L"expected result");
    score.debug(L"actual result");
    model0.debug(L"model");
    return Error::handle(name(), L"getLikelihood",
			 Error::TEST, __FILE__, __LINE__);
  }

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

  //---------------------------------------------------------------------------
  //
  // 4. class-specific public methods:
  //     makeMixtureModel method
  //
  //---------------------------------------------------------------------------

  // define models for testing
  //
  MixtureModel mixture_test;
  GaussianModel model_00;
  
  // create test data for testing the method
  //
  VectorFloat mean_00(L"-1.661615e+00, -5.825935e-01, 3.062509e+00, 3.518107e+00, 3.818454e+00, 2.027107e+00, -1.621985e-01, -1.050469e+00, 1.790901e+00, 1.895867e+00, 8.390875e-01, 1.148266e+00, -8.506311e+00, -5.370536e-01, -6.530181e-01, -7.040118e-01, -4.309049e-01, -5.868642e-01, -9.918470e-02, 9.738632e-03, 9.953918e-02, 1.136576e-01, 7.269651e-02, 3.915468e-01, 2.288062e-01, -4.447905e-01, 2.466336e-01, 2.429315e-01, 1.189543e-01, -4.975573e-03, -4.464930e-02, -1.035047e-01, -8.204004e-02, -1.486870e-02, -9.913510e-02, -6.961887e-02, -5.752704e-02, -3.685988e-02, 1.080603e-01");
  MatrixFloat covar_00(39, 39, L"1.695376e+01, 2.242232e+01, 2.675792e+01, 2.894077e+01, 3.766036e+01, 3.452074e+01, 3.076440e+01, 3.718127e+01, 3.030838e+01, 2.703625e+01, 2.583121e+01, 2.229824e+01, 3.308335e+00, 9.803090e-01, 1.296801e+00, 1.754328e+00, 1.844795e+00, 2.356385e+00, 2.393251e+00, 2.718197e+00, 3.126061e+00, 2.671936e+00, 2.522524e+00, 2.542614e+00, 2.097981e+00, 1.908329e-01, 1.685192e-01, 1.930260e-01, 2.387826e-01, 2.817971e-01, 3.201537e-01, 3.745581e-01, 3.976555e-01, 4.419698e-01, 3.864717e-01, 3.817808e-01, 3.532687e-01, 2.944204e-01, 2.367065e-02", Integral::DIAGONAL);

  // set the model parameters
  //
  model_00.setMean(mean_00);
  model_00.setCovariance(covar_00);
  model_00.init();

  mixture_test.add(model_00);
  mixture_test.add(model_00);
  
  StatisticalModel mixture_00;
  StatisticalModel mixture_01;
  StatisticalModel gauss_00(model_00);
  StatisticalModel mixture_02(mixture_test);
  
  // create a mixture model out of the gaussian
  //
  StatisticalModel::addModelToMixture(gauss_00, mixture_00);
  StatisticalModel::addModelToMixture(mixture_00, mixture_01);

  // open files in write mode
  //
  String tmp_filename_00(L"addModelToMixture.sof");
  Sof tmp_file_00;
  tmp_file_00.open(tmp_filename_00, File::WRITE_ONLY, File::TEXT);

  // write the models
  //
  gauss_00.write(tmp_file_00, 0);
  mixture_00.write(tmp_file_00, 1);
  mixture_01.write(tmp_file_00, 2);

  // close the files
  //
  tmp_file_00.close();

  // open files in write mode
  //
  tmp_filename_00.assign(L"splitMixtureModel.sof");
  tmp_file_00.open(tmp_filename_00, File::WRITE_ONLY, File::TEXT);
  
  // test splitting the mixture
  //
  
  mixture_02.splitMixtureModel(4);

  // write the models
  //
  mixture_02.write(tmp_file_00, 0);
  
  // close the files
  //
  tmp_file_00.close();
  
  //---------------------------------------------------------------------------
  //
  // 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;
}
