/***************************************************************************
 *   Copyright (C) 2007 by Honorius Galmeanu                               *
 *   honorius_galmeanu@yahoo.com                                           *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 *                                                                         *
 *   Copyright: See COPYING file that comes with this distribution         *
 *                                                                         *
 ***************************************************************************/

#ifndef __IDSVM_H__
#define __IDSVM_H__


class x_svm
{
	struct x_svm_problem 	probl;	// input patterns
	struct x_svm_problem 	test;	// test patterns
	struct x_svm_param	param;	// parameters
	
	x_svm_kernel * 		pkern;	// kernel to be created after parsing parameters
	double			w0;	// free coeff

	x_line<x_elem>		s_vect;	// unbounded support vectors (S)
	x_line<x_elem>		r_vect;	// rest vectors (R = O U E)
					// bounded support vectors (E)
					// correctly classified vectors - others (O)
	x_matrix<double> 	p;	// inverse of Q
	x_matrix<double>	q;	// Q

	x_vector<int> 		perm;

	double * precalc;		// precalculated y_i*y_j*kern(i, j)
	double ** getq;			// accelerated access

	// pointer to function get_q
	double (x_svm::*get_q) (int i, int j) const;

public:
	x_svm (void) : pkern (NULL), w0 (0.5), precalc (NULL), getq (NULL), get_q (&x_svm::get_q_classic) { }
	~x_svm (void) {
		if (pkern) { delete pkern; pkern = NULL; }
		if (precalc) { delete precalc; precalc = NULL; }
		if (getq) { delete getq; getq = NULL; }
	}

	void precalculate (void);

	void usage (void);
	int  parse_args (int argc, char * argv[]);

	int  read_problem (void);
	int  read_test (void);
	void print_problem (void);
	void print_test (void);

	int create_kernel (void);

	void print_patterns (x_vector<int>& perm);

	void create_pq (/* support vector */ int s);
	void expand_pq (/* support vector */ int k);
	void contract_pq (/* support vector */ int k);

	void train (void);
	void evaluate (void);

	double calc_g (x_svm_node * x);

	void print_state (/* unlearned vector */ x_elem * uv = NULL);
	void print_cross (void);
	void save_state (x_vector<int>& perm, char * file);
	void restore_state (x_vector<int>& perm, char * file);

	// support functions
	void what_if (int k, double delta_l, x_line<double>& beta);
	void check_pq_consistency (void);
	int check_duplicate (int k, bool inSupport = true);

	// core methods
	int pattern_learn (	/* [in] cycle number */ int incr,
				/* [in] (yet) unlearned pattern */ int k,
				/* [in] eta_k */ x_line<double>& eta_k,
				/* [in] beta_k */ x_line<double>& beta_k,
				/* [in] gamma_k */ x_line<double>& gamma_k );

	void pattern_unlearn (	/* [in] SV / RV */ bool isSupport,
				/* [in] pattern to be unlearned */ int k,
				/* [in] eta_k */ x_line<double>& eta_k,
				/* [in] beta_k */ x_line<double>& beta_k,
				/* [in] gamma_k */ x_line<double>& gamma_k );

	void pattern_unlearn (	/* [in] pattern to be unlearned in set */ int k,
				/* [in] eta_k */ x_line<double>& eta_k,
				/* [in] beta_k */ x_line<double>& beta_k,
				/* [in] gamma_k */ x_line<double>& gamma_k );

	void decrease_c (int incr);

	void rollback (	/* [in] learned pattern */ int k,
			/* [in] delta_lc */ double delta_l, 
			/* [in] eta_k */ x_line<double>& eta_k,
			/* [in] beta_k */ x_line<double>& beta_k,
			/* [in] gamma_k */ x_line<double>& gamma_k );

	// eta, beta, gamma calculations 
	void calculate_eta (	/* [in] new vector k */ int k, 
				/* [out] result eta_k */ x_line<double>& eta );
	void calculate_beta (	/* [in] eta_k */ x_line<double>& eta, 
				/* [out] result beta_k */ x_line<double>& beta );
	void calculate_gamma (	/* [in] new vector k */ int k,
				/* [in] beta_k */ x_line<double>& beta,
				/* [out] gamma_k */ x_line<double>& gamma );

	// eta, beta, gamma calculations for adjusting C 
	void calculate_etaC (	/* [out] result eta */ x_line<double>& eta );
	void calculate_gammaC (	/* [in] beta */ x_line<double>& beta,
				/* [out] gamma_c */ x_line<double>& gamma );

	// determine minimum deltas
	void limit_first_unbounded (	/* [in] */ bool incremental,
					/* [in] beta_k */ x_line<double>& beta_k,
					/* [out] results */ int& sm, int& sp, double& delta_lm, double& delta_lp );
	void limit_second_rest (	/* [in] */ bool incremental,
					/* [in] unlearned vector */ x_elem& uv,
					/* [in] gamma_k */ x_line<double>& gamma_k, 
					/* [out] results */ int& ro, int& re, double& delta_lo, double& delta_le,
					/* [inout] duplicate idxs */ x_line<int>& dup_o, x_line<int>& dup_e );
	void limit_third_extra (	/* [in] new vector k */ int k,
					/* [in] unlearned vector */ x_elem& uv,
			 		/* [in] gamma_k */ x_line<double>& gamma_k,
					/* [out] results */ bool& enabled, double& delta_lc );

	void bound_first_unbounded (	/* [in] beta_c */ x_line<double>& beta_c,
					/* [out] results */ int& sm, int& sp, double& delta_lm, double& delta_lp );
	void bound_second_rest (	/* [in] gamma_c */ x_line<double>& gamma_c,
					/* [out] results */ int& ro, int& re, double& delta_lo, double& delta_le,
					/* [inout] duplicate idxs */ x_line<int>& dup_o, x_line<int>& dup_e );

	// find min(incremental) or max(decremental) from the above cases
	void find_min_delta (	/* in */ bool enable[], double delta[], int len,
				/* out */ int& min_idx );
	void find_max_delta (	/* in */ bool enable[], double delta[], int len,
				/* out */ int& max_idx );

	// update w0 and lambdas for margin support vectors
	void update_margins ( /* in */ double dlc, x_line<double>& beta_k );

	// calculate gradient
	double calc_h (int i, /* yet unlearned vector */ x_elem * uv = NULL);

	// kernel multiplied with outputs
	inline double get_q_classic (int i, int j) const { return probl.y [i] * probl.y [j] * pkern->K (i, j); }
	inline double get_q_precalc (int i, int j) const { return i <= j ? getq[i][j - i] : getq[j][i - j]; }
};


#endif // __IDSVM_H__
