/*
  Copyright(C) 2007-2012 National Institute of Information and Communications Technology
*/

/*
  svmtools
  Multiclass training program
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/times.h>
#include "exception.h"
#include "svm_common.h"
#include "svm_smo.h"
#include "svm_mc.h"


#define DFLT_COST 1.0
#define DFLT_KERNEL_TYPE 1
#define DFLT_DEGREE 2.0f
#define DFLT_GAMMA 1.0f
#define DFLT_COEF 1.0f
#define DFLT_CACHE_SIZE 40
#define DFLT_MC_METHOD SVM_MC_METHOD_OVR


int main(int argc, char **argv) {
  int i;
  char *exmfile, *mdlfile;
  FILE *fp;
  SVM_EXM *exm;
  SVM_TPRM tprm;
  struct tms tms_start, tms_finish;
  float utime;
  int method;
  int class;

  times(&tms_start);

  /* パラメータ作成とオプションの処理 */

  tprm.c = DFLT_COST;
  tprm.kprm.ktype = DFLT_KERNEL_TYPE;
  tprm.kprm.degree = DFLT_DEGREE;
  tprm.kprm.gamma = DFLT_GAMMA;
  tprm.kprm.coef = DFLT_COEF;
  tprm.alpha_eps = 1e-12;
  tprm.kkt_eps = 1.0e-9;
  tprm.grad_del = 0.001;
  tprm.kkt_del = 0.01;
  tprm.shrink_loop = 100;
  tprm.expand_loop = 10;
  tprm.kc_size = DFLT_CACHE_SIZE;
  method = DFLT_MC_METHOD;

  for (i = 1; i < argc; i += 2) {
    if (strcmp(argv[i], "-c") == 0) {
      tprm.c = (float)atof(argv[i + 1]);
      exception(tprm.c < 0.0, "C must be positive");
    } else if (strcmp(argv[i], "-t") == 0) {
      tprm.kprm.ktype = atoi(argv[i + 1]);
      exception(tprm.kprm.ktype < 0 || 3 < tprm.kprm.ktype, "bad kernel type");
    } else if (strcmp(argv[i], "-d") == 0) {
      tprm.kprm.degree = (float)atof(argv[i + 1]);
    } else if (strcmp(argv[i], "-g") == 0) {
      tprm.kprm.gamma = (float)atof(argv[i + 1]);
    } else if (strcmp(argv[i], "-r") == 0) {
      tprm.kprm.coef = (float)atof(argv[i + 1]);
    } else if (strcmp(argv[i], "-m") == 0) {
      tprm.kc_size = atoi(argv[i + 1]);
    } else if (strcmp(argv[i], "-u") == 0) {
      method = atoi(argv[i + 1]);
      exception(method != 0 && method != 1 && method != 2, "wrong multiclass classification method");
    } else {
      break;
    }
  }

  if (argc - i != 2) {
    fprintf(stderr, "usage: %s [<options>...] <example file> <model file>\n", argv[0]);
    fprintf(stderr, "<options>:\n");
    fprintf(stderr, "\t-c <cost> : cost C of constraints violation [%g]\n", DFLT_COST);
    fprintf(stderr, "\t-t <kernel_type> : kernel type [%d]\n", DFLT_KERNEL_TYPE);
    fprintf(stderr, "\t   0 - linear <x,y>\n");
    fprintf(stderr, "\t   1 - polynomial (gamma <x, y> + coef)^d\n");
    fprintf(stderr, "\t   2 - rbf exp(-gamma ||x - y||^2)\n");
    fprintf(stderr, "\t   3 - sigmoid tanh(gamma <x, y> + coef)\n");
    fprintf(stderr, "\t-d <degree> : degree [%g]\n", DFLT_DEGREE);
    fprintf(stderr, "\t-g <gamma> : gamma [%g]\n", DFLT_COEF);
    fprintf(stderr, "\t-r <coef> : coef [%g]\n", DFLT_COEF);
    fprintf(stderr, "\t-m <cache_size> : cache size (MB) [%d]\n", (int)DFLT_CACHE_SIZE);
    fprintf(stderr, "\t-u <multiclass_method> : multiclass classification method [%d]\n", DFLT_MC_METHOD);
    fprintf(stderr, "\t   0 - one versus rest\n");
    fprintf(stderr, "\t   1 - pairwise\n");
    fprintf(stderr, "\t   2 - sequential binary tree\n");

    return 1;
  }
  exmfile = argv[i];
  mdlfile = argv[i + 1];

  /* 訓練データの読み込み */
  fprintf(stderr, "Reading the example file... ");
  fp = fopen(exmfile, "rt");
  exception(fp == NULL, "cannot open the example file '%s'", exmfile);
  exm = svm_readexm(fp);
  exception(exm == NULL, "cannot read the example file '%s'", exmfile);
  fclose(fp);
  exception(exm->num == 0, "example file contains no data");
  fprintf(stderr, "done (%d examples).\n", exm->num);

  /* クラスの数を出力 */

  class = -1;
  for (i = 0; i < exm->num; i++) if (class < exm->label[i]) class = exm->label[i];
  class++;

  fprintf(stderr, "#class: %d\n", class);

  /* 学習とモデル書き込み */
  fprintf(stderr, "Making the model file... ");
  fp = fopen(mdlfile, "wt");
  exception(fp == NULL, "cannot open the model file '%s'", mdlfile);
  exception(svm_mc_makemdl(method, class, &tprm, exm, fp), "cannot write the model file '%s'", mdlfile);
  fclose(fp);
  fprintf(stderr, "done.\n");

  times(&tms_finish);

  /* 情報表示 */

  utime = (tms_finish.tms_utime - tms_start.tms_utime) / (double)sysconf(_SC_CLK_TCK);
  printf("CPU time: %.1f\n", utime);

  return 0;
}
