// file: $isip/class/stat/SupportVectorModel/svm_02.cc
// version: $Id: svm_02.cc 8930 2002-12-21 20:55:48Z parihar $
//

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

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

  // do a quick test of constructors, destructors and memory management
  // methods
  //
  SupportVectorModel::setGrowSize(5);
  SupportVectorModel* svm0 = new SupportVectorModel();
  SupportVectorModel* svm1 = new SupportVectorModel(*svm0);
  SupportVectorModel* svm2 = new SupportVectorModel[10];

  // clear the pointers
  //
  delete svm0;
  delete svm1;
  delete [] svm2;

  // set up simple SVM
  //
  svm0 = new SupportVectorModel;

  // create data to test SVM model
  //
  VectorFloat vector1(L"0, 1");
  VectorFloat vector2(L"2, 3");
  VectorDouble alphas(L"0.7, 0.3");
  VectorFloat kernel_constants(L"0.1");
  float sigmoid_gain = 11, sigmoid_slope = 12;
  float sigmoid_xoffset = 13, sigmoid_yoffset = 14;
  Kernel::ALGORITHM kernel_algorithm = Kernel::RBF;
  float bias = 0.1;

  svm0->setNumSupportVectors(2);
  svm0->getSupportVector(0) = vector1;
  svm0->getSupportVector(1) = vector2;
  svm0->getAlpha(0) = alphas(0);
  svm0->getAlpha(1) = alphas(1);
  svm0->getBias() = bias;

  svm0->getKernel(0).setAlgorithm(kernel_algorithm);
  svm0->getKernel(0).setConstants(kernel_constants);

  svm0->getSigmoid().set(sigmoid_gain, sigmoid_slope,
			 sigmoid_xoffset, sigmoid_yoffset);

  // test write method
  //
  String tmp_filename;
  Integral::makeTemp(tmp_filename);

  Sof output_sof;
  output_sof.open(tmp_filename, File::WRITE_ONLY);
  svm0->write(output_sof, 0);
  output_sof.close();

  // test read method
  //
  svm1 = new SupportVectorModel;
  
  Sof input_sof;
  input_sof.open(tmp_filename);
  svm1->read(input_sof, 0);
  input_sof.close();

  // verify whether reading was correct
  //
  if (!svm1->eq(*svm0)) {
    return Error::handle(name(),
			 L"diagnose - error in read, write or eqaual method",
			 Error::TEST, __FILE__, __LINE__);
  }
  delete svm1;
  
  // test assign method
  //
  svm1 = new SupportVectorModel;
  svm1->assign(*svm0);
  if (!svm1->eq(*svm0)) {
    return Error::handle(name(), L"diagnose - error in assign",
			 Error::TEST, __FILE__, __LINE__);
  }
  
  // test operator= method
  //
  svm2 = new SupportVectorModel;
  *svm2 = *svm0;
  if (!svm2->eq(*svm0)) {
    return Error::handle(name(), L"diagnose - error in assign method",
			 Error::TEST, __FILE__, __LINE__);
  }
  delete svm1;
  delete svm2;

  // test copy constructor
  //
  svm1 = new SupportVectorModel(*svm0);
  if (!svm1->eq(*svm0)) {
    return Error::handle(name(), L"diagnose - error in copy constructor",
			 Error::TEST, __FILE__, __LINE__);
  }

  // test clear method
  //
  svm1->clear();
  if (svm1->eq(*svm0)) {
    return Error::handle(name(), L"diagnose - error in clear method",
			 Error::TEST, __FILE__, __LINE__);
  }
  delete svm1;

  // test general statistical model setType method
  //
  StatisticalModel sm;
  sm.setType(StatisticalModel::SUPPORT_VECTOR_MODEL);
  sm = *svm0;
  if (!sm.eq(*svm0)) {
    return Error::handle(name(), L"diagnose - stat model setType error",
			 Error::TEST, __FILE__, __LINE__);
  }

  // test the i/o methods
  //
  SupportVectorModel svm3;
  SupportVectorModel svm4;
  SupportVectorModel svm5;
  SupportVectorModel svm6;

  svm3.setNumSupportVectors(2);
  svm3.getSupportVector(0) = vector1;
  svm3.getSupportVector(1) = vector2;
  svm3.getAlpha(0) = alphas(0);
  svm3.getAlpha(1) = alphas(1);
  svm3.getBias() = bias;
  svm3.getKernel(0).setAlgorithm(kernel_algorithm);
  svm3.getKernel(0).setConstants(kernel_constants);
  
  // 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);

  svm3.write(tmp_file0, (long)0);
  svm4.write(tmp_file0, (long)1);
  svm3.write(tmp_file1, (long)0);
  svm4.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 model written in PRE_COMPUTE mode back in
  //
  svm5.read(tmp_file0, (long)0);
  svm6.read(tmp_file0, (long)0);

  if (!svm5.eq(svm3) || !svm6.eq(svm3)) {
    svm3.debug(L"expected SVM");
    svm5.debug(L"svm5 actual");
    svm6.debug(L"svm6 actual");
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }

  // read the model written in NONE mode back in
  //
  svm5.clear();
  svm6.clear();
  svm5.read(tmp_file0, (long)1);
  svm6.read(tmp_file0, (long)1);

  if (!svm5.eq(svm4) || !svm6.eq(svm4)) {
    svm4.debug(L"expected SVM");
    svm5.debug(L"svm5 actual");
    svm6.debug(L"svm6 actual");
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }

  // now read the binary files for NONE mode
  //
  svm5.clear();
  svm6.clear();
  svm5.read(tmp_file1, (long)0);
  svm6.read(tmp_file1, (long)0);

  if (!svm5.eq(svm3) || !svm6.eq(svm3)) {
    svm3.debug(L"expected SVM");
    svm5.debug(L"svm5 actual");
    svm6.debug(L"svm6 actual");
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }

  // and read the PRE_COMPUTE model from the binary file
  //
  svm5.clear();
  svm6.clear();
  svm5.read(tmp_file1, (long)1);
  svm6.read(tmp_file1, (long)1);

  if (!svm5.eq(svm4) || !svm6.eq(svm4)) {
    svm4.debug(L"expected SVM");
    svm5.debug(L"svm5 actual");
    svm6.debug(L"svm6 actual");
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }

  // close and  delete the temporary files
  //
  tmp_file0.close();
  tmp_file1.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 eq and assign methods
  //
  SupportVectorModel svm7;
  svm7.assign((StatisticalModelBase&)svm3);
  if (!svm7.eq((StatisticalModelBase&)svm3)) {
    svm3.debug(L"expected SVM");
    svm7.debug(L"actual SVM");
    return Error::handle(name(), L"assign/eq", Error::TEST, __FILE__,
			 __LINE__);
  }

  // test the setMode method
  //
  svm7.setMode(NONE);
  svm3.setMode(PRECOMPUTE);
  if ((svm7.getMode() != NONE) || (svm3.getMode() != PRECOMPUTE)) {
    return Error::handle(name(), L"setMode", Error::TEST, __FILE__,
			 __LINE__);
  }

  // test getSupportVector method
  //
  VectorFloat tmp_vec;
  svm7.setNumSupportVectors(2);
  svm7.getSupportVector(0) = vector1;
  svm7.getSupportVector(1) = vector2;
  tmp_vec = svm7.getSupportVector(0);
  if (!tmp_vec.eq(vector1)) {
    vector1.debug(L"expected support vector");
    tmp_vec.debug(L"actual support vector");
    return Error::handle(name(), L"getSupportVector", Error::TEST, __FILE__,
			 __LINE__);
  }
  tmp_vec = svm7.getSupportVector(1);
  if (!tmp_vec.eq(vector2)) {
    vector2.debug(L"expected support vector");
    tmp_vec.debug(L"actual support vector");
    return Error::handle(name(), L"getSupportVector", Error::TEST, __FILE__,
			 __LINE__);
  }

  // test getAlpha method
  //
  svm7.getAlpha(0) = alphas(0);
  svm7.getAlpha(1) = alphas(1);
  Double alpha = svm7.getAlpha(0);
  if (!alpha.eq(alphas(0))) {
    alphas(0).debug(L"expected alpha");
    alpha.debug(L"actual alpha");
    return Error::handle(name(), L"getAlpha", Error::TEST, __FILE__,
			 __LINE__);
  }
  alpha = svm7.getAlpha(1);
  if (!alpha.eq(alphas(1))) {
    alphas(1).debug(L"expected alpha");
    alpha.debug(L"actual alpha");
    return Error::handle(name(), L"getAlpha", Error::TEST, __FILE__,
			 __LINE__);
  }

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

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

  VectorFloat test_vector(L"0.2, 0.3");
  Float dist = svm0->getDistance(test_vector);
  if (!dist.almostEqual(0.668533)) {
    dist.debug(L"");
    return Error::handle(name(), L"diagnose - getDistance error",
			 Error::TEST, __FILE__, __LINE__);
  }

  Float log_likelihood = svm0->getLogLikelihood(test_vector);
  if (!log_likelihood.almostEqual(2.63906)) {
    log_likelihood.debug(L"");
    return Error::handle(name(), L"diagnose - getLogLikelihood",
			 Error::TEST, __FILE__, __LINE__);    
  }

  Float likelihood = svm0->getLikelihood(test_vector);
  if (!likelihood.almostEqual(14)) {
    return Error::handle(name(), L"diagnose - getLikelihood",
			 Error::TEST, __FILE__, __LINE__);    
  }

  // clear the pointer
  //
  delete svm0;
  
  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }

  //---------------------------------------------------------------------------
  //
  // 4. 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;
}
