/*
 * hier_cluster.cpp
 *
 *  Created on: Nov 22, 2014
 *      Author: qimao
 */

#include "hier_cluster.h"

std::string TreeNode::print_tree(int level)
{
	std::string tree_string("");
	for(int i =0;i<level;++i) tree_string += "\t";
	tree_string += this->name;
	tree_string +=":";
	tree_string += std::to_string( this->cluster.size() );
	tree_string += ", diameter=";
	tree_string += std::to_string(this->diameter);
	tree_string += "\n";

	for(unsigned int i = 0;i<this->children.size();++i)
		tree_string += children[i].print_tree(level+1);

	return tree_string;
}

void TreeNode::print_tree(std::ostream &out)
{
	std::string path="";
	std::vector<std::string> paths;
	std::vector<std::unordered_set<int> >  clusters;

	get_struct_clusters(path,paths,clusters);

	for(unsigned int i=0;i<paths.size();++i)
	{
		out<<"cluster"<<i<<paths[i]<<std::endl;
		std::unordered_set<int>::iterator it = clusters[i].begin();
		for(;it != clusters[i].end();++it)
			out<<*it<<" ";
		out<<std::endl;
	}
}


void TreeNode::get_struct_clusters(std::string path, std::vector<std::string> &paths, std::vector<std::unordered_set<int> > & clusters)
{
	if( this->children.size() == 0 )
	{
		path += ";";
		path += this->name;
		path += ":";
		path += std::to_string(this->diameter);
		paths.push_back(path);
		clusters.push_back(this->cluster);
		return;
	}

	for(unsigned int i = 0;i<this->children.size(); ++i)
	{
		std::string tmp_path(path);
		tmp_path += ";";
		tmp_path += this->name;
		tmp_path += ":";
		tmp_path += std::to_string(this->diameter);
		children[i].get_struct_clusters( tmp_path, paths, clusters);
	}
}

void TreeNode::get_clusters(std::vector<std::unordered_set<int> > & clusters)
{
	if( this->children.size() == 0 )
	{
		clusters.push_back(this->cluster);
		return;
	}

	for(unsigned int i = 0;i<this->children.size(); ++i)
	{
		children[i].get_clusters(clusters);
	}
}



void DivisiveHierCluster::cluster(SeqSparseMatrix &smat)
{
	int n = smat.get_n_rows();
	uvec indices(n);
	for(int i=0;i<n;i++) indices(i) = i;
	for(uword i=0;i<indices.n_rows;i++) root.cluster.insert(indices(i));

	depth_first_cluster(root, smat, indices);
}

void DivisiveHierCluster::depth_first_cluster(TreeNode &root, SeqSparseMatrix &smat, uvec & indices)
{
	if(root.cluster.size() < (unsigned int)params.get_size_cluster()) return;

	base_cluster.active_cluster(smat,indices);
	std::vector<std::unordered_set<uword> > clusters = base_cluster.get_clusters();
	root.diameter = base_cluster.get_diameter();

	if(root.diameter <  params.get_diameter() ) return;

	for(unsigned int i = 0;i<clusters.size();++i)
	{
		std::unordered_set<uword> child_set = clusters[i];
		unsigned int child_n = child_set.size();
		if(child_n == 0 || child_n == indices.n_rows) // cannot be divided even further
		{
			std::cout<<"cannot be divided"<<std::endl;
			continue;
		}

		std::string name("node");
		name += std::to_string(i);
		TreeNode child(name);
		uvec child_indices(child_n);
		std::unordered_set<uword>::iterator it = child_set.begin();
		for(int i = 0; it != child_set.end(); ++it, ++i)
		{
			child.cluster.insert(*it);
			child_indices(i) = *it;
		}
		depth_first_cluster(child, smat, child_indices);

		root.children.push_back(child);
	}
}
