/** How to compile and run this driver
 *
 * Serial version:
 *
 * g++ -g3 -DNDEBUG  -Werror -Wall -I../src  \
 *  -lm  -lpthread   range.cpp   -o range \
 * -lblas -llapack
 *
 * And run it with:
 *
 * ./range
 *
 * Parallel version:
 *
 * mpicxx -DUSE_MPI -g3 -DNDEBUG  -Werror -Wall -I../src \
 *  -lm  -lpthread   range.cpp   -o range \
 * -lblas -llapack
 *
 * And run it with:
 *
 * your batch system script
 *
 */
#define USE_PTHREADS_OR_NOT_NG
#include "Concurrency.h"
#include "Parallelizer.h"
#include <iostream>
#include <unistd.h>

class MyLoop {

	typedef PsimagLite::Concurrency ConcurrencyType;

public:

	MyLoop(SizeType nthreads, SizeType total)
	    : sum_(ConcurrencyType::storageSize(nthreads)),
	      v_(total,0)
	{}

	SizeType tasks() const { return v_.size(); }

	void doTask(SizeType taskNumber, SizeType threadNum)
	{
		sleep(1);

		SizeType ind = ConcurrencyType::storageIndex(threadNum);
		sum_[ind] += taskNumber;
		v_[taskNumber] = taskNumber * taskNumber;
	}

	void sync()
	{
		if (ConcurrencyType::hasPthreads()) {
			SizeType tmp = PsimagLite::sum(sum_);
			sum_[0] = tmp;
		}

		if (ConcurrencyType::hasMpi()) {
			SizeType tmp = sum_[0];
			PsimagLite::MPI::allReduce(tmp);
			sum_[0] = tmp;

			PsimagLite::MPI::allReduce(v_);
		}
	}

	SizeType sum() const
	{
		assert(sum_.size() > 0);
		return sum_[0];
	}

	const PsimagLite::Vector<SizeType>::Type& v() const
	{
		return v_;
	}

private:

	PsimagLite::Vector<SizeType>::Type sum_;
	PsimagLite::Vector<SizeType>::Type v_;
};

int main(int argc,char *argv[])
{

	typedef PsimagLite::Concurrency ConcurrencyType;

	SizeType nthreads = 1;
	if (argc == 3) nthreads = atoi(argv[2]);

	ConcurrencyType concurrency(&argc,&argv,nthreads);

	typedef MyLoop HelperType;
	typedef PsimagLite::Parallelizer<HelperType> ParallelizerType;
	ParallelizerType threadObject(ConcurrencyType::codeSectionParams);

	if (argc < 2) return 1;

	SizeType total = atoi(argv[1]);

	HelperType helper(nthreads,total);

	threadObject.loopCreate(helper);

	helper.sync();

	SizeType sum = helper.sum();

	if (ConcurrencyType::root()) {
		std::cout<<"Using "<<threadObject.name()<<" mode= "<<ConcurrencyType::mode;
		std::cout<<" with "<<nthreads;
		std::cout<<" threads or mpi procs.\n";
		std::cout<<"sum="<<sum<<"\n";
		std::cout<<helper.v();
	}

}
