#include <cstdlib>
#include <iomanip>
#include <iostream>

#include "chain-random-walk.h"

using namespace crw;

namespace {
   // set each element forward or backward by 1, at random
   void stochastic_unit_step(long size, float config[])
   {
      for(long i = 0; i < size; i++)
         if(std::rand() % 2 == 0) config[i]++;
         else config[i]--;
   }
   
   void shift_centre_to_origin(long size, float config[])
   {
       double sum = 0.0;
       for(long i = 0; i < size; i++) sum += config[i];
       for(long i = 0; i < size; i++) config[i] -= sum/size;
   }
   
   const int el_out_max = 9;  // show at most the first nine elements
}

float crw::elongation(long size, float config[])
{
   float min = config[0];
   float max = config[0];
   
   for(long i = 1; i < size; i++)
      if(config[i] > max) max = config[i];
      else if(config[i] < min) min = config[i];

   return max - min;
}

float* crw::step(long size, float previous[])
{
   // allocate the next configuration
   float* config = new float[size]();
    
   // first, let the chain contract: each element is attracted by its neighbours
   for(long i = 0; i < size; i++)
      config[i] = 0.5*previous[i] + 0.25*previous[(i-1) % size]
                                  + 0.25*previous[(i+1) % size];

   stochastic_unit_step(size, config);  // actual random walk step
   shift_centre_to_origin(size, config);  // shift such that the average remains zero

   return config;
}

void crw::output(long no, long size, float present[], float extreme[], double avg_el)
{
   /* output statistics over the run so far 
    */
   std::cout << std::setw(12) << no << "\t" << std::setprecision(3) << std::fixed
             << std::setw(6) << elongation(size, present) << " "
             << std::setprecision(7) << std::setw(10) << avg_el << " " << std::setprecision(5) 
             << std::setw(9) << elongation(size, extreme) << std::setprecision(3) << "\t\t";
   
   /* display the present configuration, showing up to <el_out_max> element coordinates */
   for(long i = 0; (i < size) && (i < el_out_max); i++)
       std::cout << std::setw(7) << present[i] << " ";
   if(size > el_out_max) std::cout << "...";
   std::cout << "\t\t";
    
   /* display the most extreme configuration so far,
    * showing up to <el_out_max> element coordinates
    */
   for(long i = 0; (i < size) && (i < el_out_max); i++)
       std::cout << std::setw(7) << extreme[i] << " ";
   if(size > el_out_max) std::cout << "...";
   std::cout << "\n";
}
