#include <iostream>
#include <vector>
#include <string>
#include <time.h>
#include <stdlib.h>
///包含这个工具文件以使用分组排列组合算法
#include "nmcalc.h"
using namespace NM_CALC;	//使用命名空间
using namespace std;

void demo_cnm()
{
	printf("\n%s\n",__FUNCTION__);
	using std::vector;
	tag_NM_State state;
	const int n = 7, m = 3, group = 10;
	vector<vector<unsigned short> > result;
	int ret = cnm(n,m,result,&state,group);
	while (ret>0)
	{
		printf("\ngroup contains %d results:\n",ret);
		for (int i=0;i<ret;++i)
		{
			printf("\n\t");
			for (int j=0;j<m;++j)
				printf("%d ",result[i][j]);
		}
		ret = cnm(n,m,result,&state,group);
	}
	printf("\nFinished\n");
}

void demo_pnm()
{
	printf("\n%s\n",__FUNCTION__);
	using std::vector;
	tag_NM_State state;
	const int n = 4, m = 3, group = 10;
	vector<vector<unsigned short> > result;
	int ret = pnm(n,m,result,&state,group);
	while (ret>0)
	{
		printf("\ngroup contains %d results:\n",ret);
		for (int i=0;i<ret;++i)
		{
			printf("\n\t");
			for (int j=0;j<m;++j)
				printf("%d ",result[i][j]);
		}
		ret = pnm(n,m,result,&state,group);
	}
	printf("\nFinished\n");
}

void demo_fnm()
{
	printf("\n%s\n",__FUNCTION__);
	using std::vector;
	tag_NM_State state;
	const unsigned long long n[3] = {2,3,4}, m = 3, group = 10;
	vector<vector<unsigned long long> > result;
	int ret = fnm(n,m,result,&state,group);
	while (ret>0)
	{
		printf("\ngroup contains %d results:\n",ret);
		for (int i=0;i<ret;++i)
		{
			printf("\n\t");
			for (unsigned long long j=0;j<m;++j)
				printf("%lld ",result[i][j]);
		}
		ret = fnm(n,m,result,&state,group);
	}
	printf("\nFinished\n");
}

void demo_serials()
{
	printf("\n%s\n",__FUNCTION__);
	typedef nmserial<string> STMS ;
	STMS stm;
	/// 设置状态机每次返回的规模
	stm.set_batch_deal(65536);		//每次返回65536个排列组合
	stm.set_group_deal(4096);		//每组内，以该参数为单位记忆缓存提高速度

	///0.压入三组数据
	vector<string> layer1;
	layer1.push_back("Alice ");
	layer1.push_back("Bob   ");
	layer1.push_back("Cray  ");
	layer1.push_back("Dave  ");
	layer1.push_back("Elic  ");
	layer1.push_back("Frank ");
	stm.addLayer(layer1,3,STMS::func_CNM);

	vector<string> layer2;
	layer2.push_back("red   ");
	layer2.push_back("blue  ");
	layer2.push_back("white ");
	layer2.push_back("black ");
	layer2.push_back("gray  ");
	stm.addLayer(layer2,2,STMS::func_PNM);

	vector<string> layer3;
	layer3.push_back("beer   ");
	layer3.push_back("cake   ");
	layer3.push_back("hamberg");
	layer3.push_back("noodles");
	stm.addLayer(layer3,3,STMS::func_PNM);


	///1.准备一个矩阵用来存放结果窗口
	vector<vector<string> > vec_results;

	///2.调用开始方法，产生首组结果，返回总共的规模。
	unsigned long long totalAmounts = stm.serial_nm_begin(vec_results);
	printf ("Total = %lld\n" ,totalAmounts);
	int currGpsz = vec_results.size();
	///3.不断获取一组排列组合并处理
	unsigned long long currDeal = 0;
	//最终结果
	while (currGpsz)
	{
		//此处可以并行化 #pragma omp parallel for
		for (int gi = 0;gi<currGpsz;++gi)
		{
			if (gi % 100)
				continue;
			const vector<string> &vec_result = vec_results[gi];
			//开始干活,这里只是打印出顺序
			for (auto v:vec_result)
				cout<<v;
			cout<<endl;
		}
		currDeal += currGpsz;
		///3.1 获取下一组数据。注意，vec_results 已经分配好内存，不会重复分配
		currGpsz = stm.serial_nm_next(vec_results);
	}//end while (currGpsz)

	printf ("\n Finished.");
}

void bench_serials();

//演示
int main()
{
	demo_pnm();
	demo_cnm();
	demo_fnm();
	demo_serials();
	bench_serials();
	return 0;
}



typedef nmserial<unsigned char> STMC;
void prepare_task(STMC * task);
void serial_deal(STMC * task);

void bench_serials()
{
	printf("\n%s\n",__FUNCTION__);
	srand(time(0));
	///测试10次
	for (int test = 0; test < 10; ++ test)
	{
		printf ("Test %d:\n",test);
		/// 0. 一次联合排列组合由一个状态机完成，状态机可以批量的返回结果，速度非常快。
		STMC state_machine;
		/// 设置状态机每次返回的规模
		state_machine.set_batch_deal(1024*1024);	//每次返回1M个排列组合
		state_machine.set_group_deal(65536);		//每组内，以该参数为单位记忆缓存提高速度

		/// 1. 随机产生排列需求，实际过程中应使用真实数据填写，
		/// 元素类型也不一定是string。这里使用string是为了打印美观。
		prepare_task(&state_machine);

		/// 2. 开始处理，不断获取批量数据
		serial_deal(&state_machine);

		printf ("\n");
	}
	printf ("\n Finished.");
}




void serial_deal(STMC * state)
{
	///1.准备一个矩阵用来存放结果窗口
	std::vector<std::vector<unsigned char> > vec_results;
	///2.调用开始方法，产生首组结果，返回总共的规模。
	unsigned long long totalAmounts = state->serial_nm_begin(vec_results);

	printf (" = %lld\n" ,totalAmounts);

	///3.不断获取一组排列组合并处理
	int currGpsz = vec_results.size();
	unsigned long long currDeal = 0;
	//最终结果
	while (currGpsz)
	{
		//此处可以并行化
//#pragma omp parallel for
		for (int gi = 0;gi<currGpsz;++gi)
		{
			const vector<unsigned char> &vec_result = vec_results[gi];
			//开始干活,这里只是打印出顺序
			for (int j = 0; j < state->total_out(); ++j)
				printf("%d ",(unsigned int)vec_result[j]);
			break;
		}
		currDeal += currGpsz;
		printf (",%6.2lf%%\n",double(100.0*currDeal / totalAmounts));

		///3.1 获取下一组数据。注意，vec_results 已经分配好内存，不会重复分配
		currGpsz = state->serial_nm_next(vec_results);
		fflush(stdout);
	}//end while (currGpsz)


}


void prepare_task(STMC * state)
{
	//分 serial_count 组
	const int serial_count = rand()%3+3;
	printf ("serial_count = %d\n",serial_count);
	//插入若干字符，准备数据
	for (int i=0;i<serial_count;++i)
	{
		vector<unsigned char> layer;

		//为本组追加插入随机个数
		int n = rand() % 3 + 3;
		int m = n - rand()%2 - 1;
		//插入各组元素
		for (int j = 0; j < n; j++)
			layer.push_back(i*10+j);
		//随机决定本组是 PNM 还是 CNM
		if (rand()%2)
		{
			//通过向量 state.group_type 指定各组的策略是PNM还是CNM
			state->addLayer(layer,m,STMC::func_CNM);
			printf ("C(%d,%d) ",n,m);
		}
		else
		{
			//通过向量 state.group_type 指定各组的策略是PNM还是CNM
			state->addLayer(layer,m,STMC::func_PNM);
			printf ("P(%d,%d) ",n,m);
		}
	}
}
