#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <zmq.hpp>
#include "async-worker.h"
#include <vector>
#include <math.h>

typedef std::vector<double> Numbers ;
typedef Numbers::iterator Iterator ;

struct Crunch : public async::worker_t
{
	/*
		Constructor takes a range of consecutive numbers to process,
		and a pointer to where you want the result.
		"Work()" will populate the internal 'privateSum',
		"Result()" will add privateSum to the double provided via resultPtr.
	*/
	Crunch(Iterator startRange, Iterator endRange, double* resultPtr)
		: worker_t()
		, start(startRange)
		, end(endRange)
		, result(resultPtr)
		, privateSum(0.0)
		{}

	/*
		The actual work
	*/
	virtual void Work()
	{
		for ( auto it = start ; it != end ; ++it )
		{
			privateSum += abs(abs(*it) + sin((unsigned long long)(*it)) * abs(cos(*it))) + ((float)*it * (unsigned short)*it) ;
		}
	}

	/*
		Add the result of our range to the result pointer.
	*/
	virtual void Result()
	{
		*(result) += privateSum ;
	}

	Iterator start, end ;
	double* result ;
	double privateSum ;
} ;




int main(int argc, const char* const argv[])
{
	/*
		Create a large array of numbers
	*/

	Numbers numbers(50 * 1000 * 1000) ;

	for ( size_t i = 0 ; i < numbers.size() ; ++i )
	{
		numbers[i] = (double)i ;
	}

	/*
		Determine 'batch size' from the command line
	*/

	int batchSize = atoi(argv[1]) ;

	/*
		Storage for the result of crunching the numbers
	*/
	double result = 0.0 ;

	if ( batchSize == 0 )
	{
		/*
			Serial version
		*/
		Crunch serial(numbers.begin(), numbers.end(), &result) ;
		serial.Work() ;
		serial.Result() ;
	}
	else
	{
		/*
			Parallel version
		*/
		auto start = numbers.begin() ;
		do
		{
			/*
				Use iterators to create ranges of consecutive numbers
				to be processed.
			*/
			auto end = std::min( start + batchSize, numbers.end() ) ;
			auto workload = new Crunch(start, end, &result) ;

			async::Queue(workload) ;		// Dispatch.

			start = end ;
		}
		while ( start != numbers.end() ) ;

		/*
			Collect the results
		*/
		async::GetResults() ;
	}

	printf("total is %g\n", result) ;

	return 0 ;
}

