/* cellauto.h  -bds 10/02
 * Ane dimensional cellular automata project. 
 * Draft sequential code */

#include <vector>
#include <stdlib.h>
#include <mpi.h>
using namespace std;

#include "apply_rule.h"
// apply_rule.h defines these  functions:
// apply_rule(), display_cells(), areEqual(), and binrule().
// apply_rule(rule, left, center, right) gives next value for center.
// according to rule.  You shouldn't need to modify any of that.


/* cellular_automaton_steps applies the rule to modify each position
 * in [b..e), assuming wrap around so that b is a neighbor of e-1.
 * Requires e-b is at least 3, and 0 <= rule < 256.
 * The process is repeated M times.
 *
 * this is the serial version.  
 * Make a parallel version.  Add a MPI_Communicator to the argument 
 * list of your parallel version.  
 */
template<class Ptr> 
void cellular_automaton_steps(int rule, Ptr b, Ptr e, int M)
{   typedef iterator_traits<Ptr>::value_type Cell;
    for (int i = 1; i < M; ++i)
    {
	Cell left = e[-1];
	Cell first = *b;
	for (Ptr i = b; i < e-1; ++i)
	{   left = apply_rule(rule, left, *i, i[1]);
	    swap(left, *i);
	}
	e[-1] = apply_rule(rule, left, e[-1], first);
    }
}

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

    // N is size of cell ring
    int N = 1680; // divisible by all p in [1..16], except 9, 11, 13.
    vector<int> C; 
    
    // number of generations. 
    int M = 1000000;

    // rule number and initial cells state
    int rule = 30;
    unsigned long long initial = 1;

    int p, r;
    MPI_Init( &argc, &argv );
    MPI_Comm_size(MPI_COMM_WORLD, &p);
    MPI_Comm_rank(MPI_COMM_WORLD, &r);

    if (r == 0)
    {
	if (argc > 1) rule = atoi(argv[1]);
	if (argc > 2) initial = static_cast<unsigned long long>(atol(argv[2]));
	cout << "million generations, rin size 1680, running: " << argv[0]; 
	cout << " " << rule << " " << initial << endl;

	// set the initial state for the first 64 positions.
	C.resize(N);
	unsigned long long L;
	for (int i = 0; i < 64; ++i) C[i] = (initial & ((L=1) << i))?1:0;
	for (int i = 0; i < 64; ++i) cout << C[i]; cout << endl;
    }

    // main part
    if (p == 1) cellular_automaton_steps(rule, C.begin(), C.end(), M);
    else 
    {
        // parallel version
        // End with final state in process 0's vector named C.
    }

    MPI_Finalize();

    // run a few generations for display
    int display_length = 50;
    int display_width = 80; // show this many per generation.
    char ON = '+', OFF = ' ';

    for (int i = 0; i < display_length; ++i)
    {
        vector<int>::iterator b = C.begin() + (N - display_width)/2;
	display_cells(C.begin(), C.begin() + display_width, ON, OFF);
	cellular_automaton_steps(rule, C.begin(), C.end(), 1);
    }

}

