#include <iostream>
#include <fstream>

#include <lbfgs.h>
#include "Boost.h"


using namespace std;
using namespace arma;
extern my_param para;
/*euclidean */
lbfgsfloatval_t evaluate(
	void *instance,
	const lbfgsfloatval_t *x,
	lbfgsfloatval_t *g,
	const int n,
	const lbfgsfloatval_t step)
{	
	INSTANCE_T	*pInstances = (INSTANCE_T*)instance;
	const mat &Feature = *pInstances->pFeature;
	const mat &Partial_Target = *pInstances->pPartial_Target;
	vec	&D = *pInstances->pW;
	mat	w;
	size_t	n_ins = Feature.n_rows;
	size_t	n_ft = Feature.n_cols;
	size_t	n_lbl = Partial_Target.n_cols;	
	double	lamda=para.lamda/(n_ft*n_lbl);

	mat h(n_ins, n_lbl);	
	*const_cast<double**>(&w.mem) = const_cast<double*>(x);
	*const_cast<uword*>(&w.n_rows) = n_ft;
	*const_cast<uword*>(&w.n_cols) = n_lbl;
	*const_cast<uword*>(&w.n_elem) = n_ft*n_lbl;
	//logfile << "w：" << w;
	lbfgsfloatval_t f = .0;
	//计算h
	h =exp(Feature*w);
	//归一化
	for (size_t ins = 0; ins < n_ins; ins++)
		h.row(ins) = h.row(ins) / sum(h.row(ins));
	/******************************************************************************/
	/*         f=-sum_i{w_i*sum_k[q(i,k)*logh(i,k)]}+Regularity						  */
	/*			where	Regularity = |W|^2										  */			
	/******************************************************************************/
	//for (size_t ins = 0; ins < n_ins; ins++)
	//{
	//	double	tmp = 0;
	//	for (size_t k = 0; k < n_lbl; k++)
	//		if (Partial_Target(ins,k)>0)
	//			tmp += Partial_Target(ins, k)*log(h(ins,k));
	//	f -= tmp*D(ins);
	//}
	/******************************************************************************/
	/*         f=-sum_i{w_i*logsum_k[q(i,k)h(i,k)]}+Regularity						  */
	/*			where	Regularity = |W|^2										  */			
	/******************************************************************************/
	for (size_t ins = 0; ins < n_ins; ins++)
	{
		double	tmp = 0;
		for (size_t k = 0; k < n_lbl; k++)
			if (Partial_Target(ins, k)>0)
				tmp += Partial_Target(ins, k)*h(ins, k);
		f -= log(tmp)*D(ins);
	}
	f += lamda*sum(sum(pow(w, 2)));

	//gradant
	mat	Grad;//将梯度向量g使用aram 矩阵的形式表示, 便于操作
	*const_cast<uword*>(&Grad.n_rows) = n_ft; *const_cast<uword*>(&Grad.n_cols) = n_lbl; *const_cast<uword*>(&Grad.n_elem)=n_ft*n_lbl;
	*const_cast<double**>(&Grad.mem)  = g;

	memset(g,0,n_ft*n_lbl*sizeof(*g));
	/*for (size_t t = 0; t < n_lbl; t++){
		for (size_t ins = 0; ins < n_ins; ins++)
			Grad.col(t) += D(ins)*(h(ins, t) - Partial_Target(ins, t))*Feature.row(ins).t();
	}		*/
	mat	h_hat = Partial_Target;
	for (size_t r = 0; r < n_ins; r++){
		for (size_t c = 0; c < n_lbl; c++)
			h_hat(r, c) = Partial_Target(r, c)*h(r, c);
		h_hat.row(r) = h_hat.row(r) / sum(h_hat.row(r));
	}
	for (size_t t = 0; t < n_lbl; t++){
		for (size_t ins = 0; ins < n_ins; ins++)
			Grad.col(t) += D(ins)*(h(ins, t) - h_hat(ins, t))*Feature.row(ins).t();
	}
	Grad += 2 * lamda*w;

	//logfile << "Grad：" << Grad;
	*const_cast<double**>(&Grad.mem)  = NULL;
	*const_cast<double**>(&w.mem) = NULL;
	return f;
}
int progress(
	void *instance,
	const lbfgsfloatval_t *x,
	const lbfgsfloatval_t *g,
	const lbfgsfloatval_t fx,
	const lbfgsfloatval_t xnorm,
	const lbfgsfloatval_t gnorm,
	const lbfgsfloatval_t step,
	int n,
	int k,
	int ls
	)
{
	INSTANCE_T	*pInstances = (INSTANCE_T*)instance;
	const mat &Feature = *pInstances->pFeature;
	const mat &Label = *pInstances->pTarget;
	//not support train set labels
	if(Label.n_cols <= 0)return 0;
	size_t	n_ft = Feature.n_cols,
		n_lbl=Label.n_cols,
		n_ins=Feature.n_rows;
	mat	w;
	*const_cast<double**>(&w.mem) = const_cast<double*>(x); 
	*const_cast<uword*>(&w.n_rows) = n_ft;
	*const_cast<uword*>(&w.n_cols) = n_lbl;
	*const_cast<uword*>(&w.n_elem) = n_ft*n_lbl;
	mat		h = exp(Feature*w);
	uword	count = 0;
	for (size_t ins = 0; ins < n_ins; ins++)
	{
		uword	idx = -1;
		h.row(ins).max(idx);
		if (Label(ins,idx))//correct
			count = count + 1;		
	}
	printf("  Iteration %d:\n", k);
	printf("    fx = %f\n", fx);
	printf("    xnorm = %lf, gnorm = %lf, step = %lf\n", xnorm, gnorm, step);
	printf("    disambiguation rate: %f\n",double(count)/n_ins);
	*const_cast<double**>(&w.mem) = NULL;
	return 0;
}
