#ifndef __LIBAGF_SVM_MULTI_H__DEFINED__
#define __LIBAGF_SVM_MULTI_H__DEFINED__

#include "binaryclassifier.h"

namespace libagf {
  //forward declaration to resolve large circular dependency:
  //template <class real, class cls_t>
  //class borders_classifier;
  
  template <typename real>
  class svm_helper;

  template <typename real, typename cls_t>
  svm_helper<real> * unite_support_vectors(binaryclassifier<real, cls_t> **, cls_ta);

  template <class real>
  int solve_cond_prob_1v1(real **r, int ncls, real *p);

  //multi-class classifier specialized for "one vs. one":
  template <class real, class cls_t>
  class onevone:public classifier_obj<real, cls_t> {
    protected:
      int voteflag;		//voting or solve for probabilities
      cls_t *label;
      virtual real ** classify_raw(real *x)=0;
    public:
      onevone();
      virtual ~onevone();
      virtual cls_t classify(real *x, real *p, real *praw=NULL);
      virtual cls_t class_list(cls_t *cls);
  };

  //we can unify this at some later time with "svm2class" binary classifier (*done*)
  template <class real, class cls_t>
  class svm_multi:public onevone<real, cls_t> {
    friend svm_helper<real> * unite_support_vectors<real, cls_t>(binaryclassifier<real, cls_t> **, cls_ta);
    protected:
      real **sv;		//support vectors
      real **coef;		//coefficients
      nel_ta nsv_total;		//total number of support vectors
      nel_ta *nsv;		//number of support vectors
      nel_ta *start;
      real *rho;		//constant terms

      //kernel function:
      real (* kernel) (real *, real *, dim_ta, void *);
      //parameters for kernel function:
      real *param;
      //kernel function with derivatives:
      real (* kernel_deriv) (real *, real *, dim_ta, void *, real *);

      //for calculating probabilities:
      real *probA;
      real *probB;

      virtual real ** classify_raw(real *x);
    public:
      svm_multi();
      svm_multi(char *file, int vflag=0);
      svm_multi(svm_multi<real, cls_t> *other);
      virtual ~svm_multi();

      virtual int ltran_model(real **mat, real *b, dim_ta d1, dim_ta d2);
      //virtual void print(FILE *fs, char *fbase=NULL, int depth=0);
      //virtual int commands(multi_train_param &param, cls_t **clist, char *fbase);

      //difference in conditional probabilities comparing pair of labels,
      //R_{ij}(x) = (P(j | x) - P(i | x))/(P(i | x) + P(j | x))
      real R(real *x, 				//test point
		cls_t i, 			//first label
		cls_t j, 			//second label
		real *praw=NULL);		//just stores result
      //returns derivative in drdx:
      real R_deriv(real *x, cls_t i, cls_t j, real *drdx);

      virtual int load(FILE *fs);
      virtual int save(FILE *fs);

      void subsample(real frac, 		//fraction of data to keep
		      int cconst); 		//keep relative class numbers
      						//constant
  };

  template <class real, class cls_t>
  class borders1v1:public onevone<real, cls_t> {
    protected:
      //binary classifiers:
      borders_classifier<real, cls_t> **classifier;
      int *pol;					//"polarities"
      virtual real ** classify_raw(real *x);
    public:
      borders1v1();
      borders1v1(char *file, int vflag=0);
      borders1v1(svm_multi<real, cls_t> *other, 
		      real **x,			//training samples
		      cls_t *cls,		//class data
		      dim_ta nvar,		//number of variables
		      nel_ta ntrain,		//number of training samples
		      nel_ta nsamp,		//number of border samples
		      real tol,			//tolerance of border samples
		      int tflag=0);		//copy transform?
      virtual ~borders1v1();

      virtual int ltran_model(real **mat, real *b, dim_ta d1, dim_ta d2);
      //virtual void print(FILE *fs, char *fbase=NULL, int depth=0);
      //virtual int commands(multi_train_param &param, cls_t **clist, char *fbase);

      virtual int load(FILE *fs);
      virtual int save(FILE *fs);
  };
}

#endif

