// file: $isip/class/stat/SupportVectorModel/svm_03.cc
// version: $Id: svm_03.cc 8764 2002-10-26 19:45:56Z jelinek $
//

// isip include files
//
#include "SupportVectorModel.h"

// method: read
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//  const String& name: (input) sof object instance name
//
// return: a boolean value indicating status
//
// this method has the object read itself from an Sof file
//
boolean SupportVectorModel::read(Sof& sof_a, long tag_a,
				 const String& name_a) {

  // read the instance of the object from the Sof file
  //
  if (!sof_a.find(name_a, tag_a)) {
    return false;
  }
  
  // read the actual data from the sof file
  //
  if (!readData(sof_a)) {
    return false;
  }
  
  // exit gracefully
  //
  return true;  
}

// method: readData
//
// arguments:
//  Sof& sof: (input) sof file object
//  const String& pname: (input) parameter name
//  long size: (input) size in bytes of object (or full_size)
//  boolean param: (input) is the parameter name in the file?
//  boolean nested: (input) are we nested?
//
// return: a boolean value indicating status
//
// this method has the object read itself from an Sof file. it assumes
// that the Sof file is already positioned correctly.
//
boolean SupportVectorModel::readData(Sof& sof_a, const String& pname_a,
				long size_a, boolean param_a,
				boolean nested_a) {

  Integral::DEBUG debug_level = Integral::NONE;

  SofParser parser;

  parser.setDebug(debug_level);
    
  // are we nested?
  //
  if (nested_a) {
    parser.setNest();
  }
  
  // load the parser
  //
  if (!parser.load(sof_a, size_a)) {
    return Error::handle(name(), L"readData", Error::READ,
			 __FILE__, __LINE__, Error::WARNING);
  }
  
  // get the algorithm
  //
  if (parser.isPresent(sof_a, PARAM_ALGORITHM)) {
    if (!ALGO_MAP.readElementData((long&)algorithm_d, sof_a, PARAM_ALGORITHM,
				  parser.getEntry(sof_a, PARAM_ALGORITHM))) {

      // return a warning message
      //
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
  }
  else {
    algorithm_d = DEF_ALGORITHM;
  }
  setAlgorithm(algorithm_d);
  
  // get the implementation
  //
  if (parser.isPresent(sof_a, PARAM_IMPLEMENTATION)) {
    if (!IMPL_MAP.readElementData((long&)implementation_d, sof_a,
				  PARAM_IMPLEMENTATION,
				  parser.getEntry(sof_a,
						  PARAM_IMPLEMENTATION))) {

      // return a warning message
      //
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
    if (implementation_d != SIGMOID) {
      return Error::handle(name(), L"readData", Error::NOT_IMPLEM,
			   __FILE__, __LINE__);
    }
  }
  else {
    implementation_d = DEF_IMPLEMENTATION;
  }
  
  // get the support vectors
  //
  if (parser.isPresent(sof_a, PARAM_SUPPORT_VECTORS)) {
    if (!support_vectors_d.readData(sof_a, PARAM_SUPPORT_VECTORS,
				    parser.getEntry(sof_a,
						    PARAM_SUPPORT_VECTORS),
				    false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
  }
  else {
    return Error::handle(name(), L"readData - sv not pres", Error::ARG,
			 __FILE__, __LINE__);
  }

  // get the kernels
  //
  if (!kernels_d.readData(sof_a, PARAM_KERNELS,
			  parser.getEntry(sof_a, PARAM_KERNELS),
			  false, false)) {
    return Error::handle(name(), L"readData", Error::READ,
			 __FILE__, __LINE__, Error::WARNING);
  }

  // get the alphas
  //
  if (!alphas_d.readData(sof_a, PARAM_ALPHAS,
			   parser.getEntry(sof_a, PARAM_ALPHAS),
			   false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			 __FILE__, __LINE__, Error::WARNING);
  }

  // get the bias
  //
  if (!bias_d.readData(sof_a, PARAM_BIAS,
		       parser.getEntry(sof_a, PARAM_BIAS),
		       false, false)) {
    return Error::handle(name(), L"readData", Error::READ,
			 __FILE__, __LINE__, Error::WARNING);
  }

  if (implementation_d == SIGMOID) {
    if (!sigmoid_d.readData(sof_a, PARAM_SIGMOID,
			   parser.getEntry(sof_a, PARAM_SIGMOID),
			   true, true)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
  }

  // call initialization method and exit gracefully
  //
  return init();
}

// method: loadSvmLight
//
// arguments:
//  const String& input_filename: (input) input file name
//
// return: a boolean value indicating status
//
// this method has the object read itself from an SVM file
//
boolean SupportVectorModel::loadSvmLight(const String& input_filename_a) {

  // open the model file for reading
  //
  File fp;
  if (!fp.open(input_filename_a)) {
    Console::put(input_filename_a);
    return Error::handle(name(), L"loadSvmLight - can not open model file",
			 Error::ARG, __FILE__, __LINE__);
  }
  
  // read SVM-light header
  //
  String line;
  
  fp.get(line);
  if (!line.eq(L"SVM-light Version V3.01")) {
    String message(L"loadSvmLight - wrong header, should be:\n");
    message.concat(L"SVM-light Version V3.01");
    return Error::handle(name(), message, Error::ARG, __FILE__, __LINE__);
  }

  // kernel type
  //
  fp.get(line); line.trim();
  
  String sub;
  long pos = 0;
  line.tokenize(sub, pos);
  
  long val;
  sub.get(val);
  
  if (val != 2) {
    line.debug(L"line");
    sub.debug(L"sub");
    return Error::handle(name(), L"loadSvmLight - kernel type should be 2",
			 Error::ARG, __FILE__, __LINE__);
  }
  long type = 2;

  // kernel parameter -d
  //
  fp.get(line); line.trim();
  pos = 0;
  line.tokenize(sub, pos);
  sub.get(val);
  
  if (val != 3) {
    line.debug(L"line");
    sub.debug(L"sub");
    return Error::handle(name(), L"loadSvmLight - order should be 2",
			 Error::ARG, __FILE__, __LINE__);
  }

  // kernel parameter -g (gamma - useful)
  //
  fp.get(line); line.trim();
  pos = 0;
  line.tokenize(sub, pos);
  double val2 = 0.1234;
  sub.get(val2);
  
  if (val2 != 0.5) {
    line.debug(L"line");
    sub.debug(L"sub");
    return Error::handle(name(), L"loadSvmLight - kern param -d should be 0.5",
			 Error::ARG, __FILE__, __LINE__);
  }
  double gamma = val2;

  // kernel parameter -s (sigma for the polynomial - not used)
  //
  fp.get(line); line.trim();
  pos = 0;
  line.tokenize(sub, pos);
  sub.get(val);
  
  if (val != 1) {
    line.debug(L"line");
    sub.debug(L"sub");
    return Error::handle(name(), L"loadSvmLight - kern param -r should be 1",
			 Error::ARG, __FILE__, __LINE__);
  }

  // kernel parameter -r (don't know what is it)
  //
  fp.get(line); line.trim();
  pos = 0;
  line.tokenize(sub, pos);
  sub.get(val);
  
  if (val != 1) {
    line.debug(L"line");
    sub.debug(L"sub");
    return Error::handle(name(), L"loadSvmLight - kern param -r should be 1",
			 Error::ARG, __FILE__, __LINE__);
  }

  // kernel parameter -u
  //
  fp.get(line); line.trim();
  pos = 0;
  line.tokenize(sub, pos);
  
  if (!sub.eq(L"empty")) {
    line.debug(L"line");
    sub.debug(L"sub");
    return Error::handle(name(),
			 L"loadSvmLight - kern param -u should be empty",
			 Error::ARG, __FILE__, __LINE__);
  }

  // number of sv
  //
  long num_sv_p1 = -1;
  fp.get(line); line.trim();
  pos = 0;
  line.tokenize(sub, pos);
  sub.get(num_sv_p1);
  
  if (val < 1) {
    line.debug(L"line");
    sub.debug(L"sub");
    return Error::handle(name(), L"loadSvmLight - num of SV should be > 0",
			 Error::ARG, __FILE__, __LINE__);
  }

  // threshold
  //
  fp.get(line); line.trim();
  pos = 0;
  line.tokenize(sub, pos);
  double threshold = -1234567.89;
  sub.get(threshold);
  
  if (val2 == -1234567.89) {
    line.debug(L"line");
    sub.debug(L"sub");
    return Error::handle(name(), L"loadSvmLight - threshold not read",
			 Error::ARG, __FILE__, __LINE__);
  }

  // set what you read
  //
  setNumSupportVectors(num_sv_p1 - 1);
  
  setAlgorithm(SINGLE_KERNEL);
  setImplementation(SIGMOID);
  if (type == 2) {
    getKernel(0).setAlgorithm(Kernel::RBF);
  }
  VectorFloat kernel_constants(1, gamma);
  getKernel(0).setConstants(kernel_constants);

  getBias() = threshold;

  // get the support vectors and their alphas
  long sv_index = 0;
  while (fp.get(line)) {

    line.trim();

    pos = line.firstStr(DEF_COMMENT_TAG); // find a comment
    
    if (pos == 0) {
      line.debug(L"line");
      return Error::handle(name(), L"loadSvmLight - unexpected comment",
			   Error::ARG, __FILE__, __LINE__);
    }

    // get alpha
    //
    pos = 0;
    line.tokenize(sub, pos);

    val2 = 1000;
    sub.get(val2);
    if (val2 == 1000) {
      return Error::handle(name(), L"loadSvmLight - alpha not read",
			   Error::ARG, __FILE__, __LINE__);
    }
    alphas_d(sv_index) = val2;

    // get other svs
    //
    long index = 0;
    long index_ver = 0;
    support_vectors_d(sv_index).setLength(118);
    
    while (line.tokenize(sub, pos)) {
      
      // get sv elemnt index
      //
      String sub2;
      long pos2 = 0;
      sub.tokenize(sub2, pos2, L":");
      sub2.get(index_ver);
      if ((index_ver - 1) != index) {
	return Error::handle(name(), L"loadSvmLight - index verif failed",
			     Error::ARG, __FILE__, __LINE__);
      }
      
      // get sv element value
      //
      sub.tokenize(sub2, pos2, L":");
      sub2.get(val2);
      support_vectors_d(sv_index)(index) = val2;
      index++;
    }
    if (index != 118) {
      return Error::handle(name(), L"loadSvmLight - not 118",
			   Error::ARG, __FILE__, __LINE__);
    }
    sv_index++;
  }
  if (sv_index != (num_sv_p1 - 1)) {
    String out(L"loadSvmLight - bad num of sv");
    return Error::handle(name(), out,
			 Error::ARG, __FILE__, __LINE__);
  }

  // close the file descriptor
  //
  fp.close();

  // exit gracefully
  //
  return true;
  
}


