/*
 * flatclusteralg.h
 *
 *  Created on: Nov 19, 2014
 *      Author: qimao
 */

#ifndef FLAT_CLUSTER_H_
#define FLAT_CLUSTER_H_

#include <armadillo>
#include <vector>

#include "params.h"
#include "sparse_matrix.h"

using namespace arma;

/*
 * abstract flat cluster algorithm
 */
class FlatCluster{

protected:
	Params &params;
	uvec clusters; // cluster assignment clusters(i) = c_i

public:
	FlatCluster(Params & params_): params(params_){}
	virtual ~FlatCluster(){}
	virtual void cluster(fmat & W) = 0;
	virtual void report_result()=0;
	virtual uvec get_clusters(){return clusters;}
};

class KMeans: public FlatCluster
{
private:
	int k; // the number of clusters required
	int maxiter; // the maximum number of iterations
	float relative_mse; // relative mse for termination
	int nrepeats; // repeat times
	fmat centroids; // cluster centroids

protected:

	float get_distance(frowvec a, frowvec b){
	    frowvec temp = a - b;
	    return norm(temp, 2);
	}

	float cluster_rand_init(fmat & W, fmat &centroids_, uvec &clusters_);

public:
	KMeans(Params & params_):FlatCluster(params_)
	{
		k = params.get_numofclusters();
		maxiter = params.get_maxiter();
		relative_mse = params.get_relative_mse();
		nrepeats = params.get_nsample();
	}
	virtual ~KMeans(){}
	void cluster(fmat & W);
	void report_result()
	{
		std::cout<<"centroids:\n"<<centroids<<std::endl;
		std::cout<<"assignment:\n"<<clusters<<std::endl;
	}
};

class SpectralCluster: public FlatCluster
{
private:
	int k;

public:
	SpectralCluster(Params & params_):FlatCluster(params_){ k = params.get_numofclusters(); }

	virtual ~SpectralCluster(){}

	void cluster(fmat & W);

	void report_result(){
		std::cout<<"assignent:\n"<<clusters<<std::endl;
	}
};

/*
 * landmark set is randomly sampled
 */
class  ActiveFlatCluster
{
protected:
	Params &params;
	std::vector< std::unordered_set<uword> > clusters;
	int k;
	FlatCluster & base_alg;
	float approx_diameter_dist;

public:
	ActiveFlatCluster(Params & params_, FlatCluster &base)
		:params(params_), base_alg(base)
	{
		k = params.get_numofclusters();
		approx_diameter_dist = 0.0;
	}

	virtual ~ActiveFlatCluster(){}

	/*
	 * Note: W is a full matrix, while sample_list is just a subset of indices
	 */
	void cluster(SeqSparseMatrix &smat)
	{
		int nsample = smat.get_n_rows();
		uvec indices;
		for(int i =0;i<nsample;++i) indices(i) = i;
		active_cluster(smat,indices);
	}

	virtual void landmark_selection(SeqSparseMatrix &smat, uvec & indices, uvec & sub_indices);

	virtual void active_cluster(SeqSparseMatrix &smat, uvec & indices);

	std::vector< std::unordered_set<uword> > get_clusters(){ return clusters;}
	float get_diameter(){return this->approx_diameter_dist;}
};


class ActiveFlatCluster_HalfN : public ActiveFlatCluster
{
public:
	ActiveFlatCluster_HalfN(Params & params_, FlatCluster &base): ActiveFlatCluster(params_,base){}
	virtual ~ActiveFlatCluster_HalfN(){}

	void landmark_selection(SeqSparseMatrix &smat, uvec & indices, uvec & sub_indices);

};


#endif /* FLAT_CLUSTER_H_ */
