// file: $isip/class/stat/UniformModel/uni_02.cc
// version: $Id: uni_02.cc 8296 2002-07-05 21:16:28Z parihar $
//

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

// method: diagnose
//
// arguments:
//  Integral::DEBUG level: (input) debug level for diagnostics
//
// return: a boolean value indicating status
//
// this is the diagnose method
//
boolean UniformModel::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 to test with
  //
  Float input_score_0 = 0;
  Float input_score_1 = 0.037037037;
  VectorFloat input_data_0(L"-1.1, -2.2, -3.3");
  VectorFloat input_data_1(L"0.9, 1.9, -0.1");
  VectorFloat min_data(L"-2, -1, -3");
  VectorFloat max_data(L"1, 2, 0");
  float min_val = -4.4;
  float max_val = 4.4;

  // test the constructor
  //
  UniformModel model0(min_data, max_data, NONE);

  // test the copy constructor and the required eq method
  //
  UniformModel model1(model0);

  if (!model1.eq(model0)) {
    model0.debug(L"expected Uniform");
    model1.debug(L"actual Uniform");
    return Error::handle(name(), L"copy constructor", Error::TEST,
                         __FILE__, __LINE__);
  }

  // test the setDebug method
  //
  setDebug(debug_level_d);

  // test the clear method
  //
  model1.clear();
  if (model1.eq(model0)) {
    model1.debug(L"model1 not clear");
    return Error::handle(name(), L"clear", Error::TEST, __FILE__, __LINE__);
  }

  // test the assign method
  //
  model1.assign(model0);
  if (!model1.eq(model0)) {
    model0.debug(L"expected Uniform");
    model1.debug(L"actual Uniform");
    return Error::handle(name(), L"assign", Error::TEST,
			 __FILE__, __LINE__);
  }

  // check the operator= method which, in turn, checks the assign method
  //
  model1 = model0;
  if (!model1.eq(model0)) {
    model0.debug(L"expected Uniform");
    model1.debug(L"actual Uniform");
    return Error::handle(name(), L"operator=", Error::TEST,
                         __FILE__, __LINE__);
  }

  // test the memory management methods
  //
  model0.setGrowSize(5);
  UniformModel* uni_ptr0 = new UniformModel;
  UniformModel* uni_ptr1 = new UniformModel[10];
  delete uni_ptr0;
  delete [] uni_ptr1;
  
  // test the i/o methods
  //
  UniformModel model2(min_data, max_data, NONE);
  UniformModel model3(min_val, max_val, PRECOMPUTE);
  UniformModel model4;
  UniformModel model5;

  // 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 the models
  //
  model2.write(tmp_file0, (long)0);
  model3.write(tmp_file0, (long)1);
  model2.write(tmp_file1, (long)0);
  model3.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 values back in
  //
  model4.read(tmp_file0, (long)0);
  if (!model4.eq(model2)) {
    model2.debug(L"expected Uniform");
    model4.debug(L"read model");
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }

  model5.read(tmp_file0, (long)1);
  if (!model5.eq(model3)) {
    model3.debug(L"expected Uniform");
    model5.debug(L"read model");
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }

  model4.read(tmp_file1, (long)0);
  if (!model4.eq(model2)) {
    model2.debug(L"expected Uniform");
    model4.debug(L"read model");
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }

  model5.read(tmp_file1, (long)1);
  if (!model5.eq(model3)) {
    model3.debug(L"expected Uniform");
    model5.debug(L"read model");
    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 assign method
  //
  model1.clear();
  model1.assign((StatisticalModelBase&)model0);
  if (!model1.eq(model0)) {
    model0.debug(L"expected Uniform");
    model1.debug(L"actual Uniform");
    return Error::handle(name(), L"assign", Error::TEST, __FILE__, __LINE__);
  }

  // test the virtual eq method
  //
  if (!model1.eq((StatisticalModelBase&)model0)) {
    model0.debug(L"expected Uniform");
    model1.debug(L"actual Uniform");
    return Error::handle(name(), L"eq", Error::TEST, __FILE__, __LINE__);
  }

  // test setMin and getMin methods
  //
  VectorFloat tmp_vec;
  model1.clear();
  model1.setMin(min_data);
  model1.getMin(tmp_vec);
  if (!tmp_vec.eq(min_data)) {
    min_data.debug(L"expected min");
    tmp_vec.debug(L"actual min");    
    return Error::handle(name(), L"setMin/getMin", Error::TEST,
			 __FILE__, __LINE__);
  }

  model1.setMin(min_val);
  model1.getMin(tmp_vec);
  if ((tmp_vec.length() != 1) || (tmp_vec(0) != min_val)) {
    ((Float)min_val).debug(L"expected min");
    tmp_vec.debug(L"actual min");
    return Error::handle(name(), L"setMin/getMin", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  // test setMax and getMax methods
  //
  model1.clear();
  model1.setMax(max_data);
  model1.getMax(tmp_vec);
  if (!tmp_vec.eq(max_data)) {
    max_data.debug(L"expected max");
    tmp_vec.debug(L"actual max");    
    return Error::handle(name(), L"setMax/getMax", Error::TEST,
			 __FILE__, __LINE__);
  }

  model1.setMax(max_val);
  model1.getMax(tmp_vec);
  if ((tmp_vec.length() != 1) || (tmp_vec(0) != max_val)) {
    ((Float)max_val).debug(L"expected max");
    tmp_vec.debug(L"actual max");
    return Error::handle(name(), L"setMax/getMax", Error::TEST,
			 __FILE__, __LINE__);
  }
  
  // test additional constructors
  //
  model1.clear();
  UniformModel model6(min_data, max_data, NONE);
  model1.setMin(min_data);
  model1.setMax(max_data);
  model1.setMode(NONE);
  if (!model6.eq(model1)) {
    model1.debug(L"expected Uniform");
    model6.debug(L"actual Uniform");
    return Error::handle(name(), L"constructor", Error::TEST,
			 __FILE__, __LINE__);
  }

  UniformModel model7(min_val, max_val, PRECOMPUTE);
  model1.setMin(min_val);
  model1.setMax(max_val);
  model1.setMode(PRECOMPUTE);
  if (!model7.eq(model1)) {
    model1.debug(L"expected Uniform");
    model7.debug(L"actual Uniform");
    return Error::handle(name(), L"constructor", 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();
  }

  // test the getLikelihood methods
  //
  UniformModel model8(min_data, max_data);
  if (model8.getLikelihood(input_data_0) != input_score_0) {
    Float output_score(input_score_0);
    Float likelihood(model8.getLikelihood(input_data_0));
    output_score.debug(L"expected value");
    likelihood.debug(L"actual value");
    return Error::handle(name(), L"getLikelihood", Error::TEST, __FILE__,
			 __LINE__);
  }

  if (model8.getLikelihood(input_data_1) != input_score_1) {
    Float output_score(input_score_1);
    Float likelihood(model8.getLikelihood(input_data_1));
    output_score.debug(L"expected value");
    likelihood.debug(L"actual value");
    return Error::handle(name(), L"getLikelihood", Error::TEST, __FILE__,
			 __LINE__);
  }

  // test the getLogLikelihood methods
  //
  if (!Integral::almostEqual((double)model8.getLogLikelihood(input_data_1),
			     Integral::log(input_score_1))) {
    Float output_score(Integral::log(input_score_1));
    Float likelihood(model8.getLogLikelihood(input_data_1));
    output_score.debug(L"expected value");
    likelihood.debug(L"actual value");
    return Error::handle(name(), L"getLogLikelihood", Error::TEST, __FILE__,
			 __LINE__);
  }

  // test the getMean and getCovariance methods for one dimensional
  // distribution
  //
  VectorFloat input_data_max(L"2");
  VectorFloat input_data_min(L"-2");
  VectorFloat exp_mean(L"0");
  MatrixFloat exp_cov;
  exp_cov.assign(1, 1, L"1.333333");
  VectorFloat mean;
  MatrixFloat cov;
  UniformModel model9(input_data_min, input_data_max);

  model9.getMean(mean);
  if (!mean.almostEqual(exp_mean)) {
    exp_mean.debug(L"expected mean");
    mean.debug(L"actual mean");
    return Error::handle(name(), L"getMean", Error::TEST, __FILE__,
			 __LINE__);
  }

  model9.getCovariance(cov);
  if (!cov.almostEqual(exp_cov)) {
    exp_cov.debug(L"expected covariance");
    cov.debug(L"actual covariance");
    return Error::handle(name(), L"getCovariance", Error::TEST, __FILE__,
			 __LINE__);
  }

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