#include "ap_debug.h"
#include <math.h>

namespace Astropulse {

/* This function is used to generate a random value for a combined power bin
 * in the fold algorithm.  It will be used to test why the lcgf's in the
 * fold algorithm were not the values I expected.  Instead of taking
 * the power bin from the client, the power bin will be randomly generated
 * by this function to be a sum of squares of state.dm_chunk * 2
 * squares of normals with sigma = 1.
 */
float random_combined_power_bin(int num_bins, int &idum) {
  float total = 0;
  float cur_normal;
  for (int i = 0; i < num_bins * 2; i++) {
    cur_normal = gasdev(idum);
    total += cur_normal * cur_normal / 2;
  }
  return total;
}

double random_discrete_sum(int num_bins, int &idum) {
  double total = 0;
  const double pi = 3.1415926;

  for (int i = 0; i < num_bins; i++) {
    total += cos(2 * pi * ran1(idum));
  }
  return total;
}

/* From numerical recipes, outputs a normally distributed random
 * number with mean 0 and sigma = 1, using ran1(idum) as the source
 * of uniform deviates.
 */
double gasdev(int &idum) {
  static int iset=0;
  static double gset;
  double fac, rsq, v1, v2;

  if (idum < 0) iset=0;
  if (iset == 0) {
    do {
      v1 = 2.0 * ran1(idum)-1.0;
      v2 = 2.0 * ran1(idum)-1.0;
      rsq=v1*v1+v2*v2; // See if they are in the unit circle.
    } while (rsq >= 1.0 || rsq == 0.0); // and if they are not, try again.
    fac=sqrt(-2.0*log(rsq)/rsq);
    /* Now make the Box-Muller transformation to get two normal
     * deviates.  Return one and save the other for next time.
     */
    gset = v1*fac;
    iset = 1;  // Set flag
    return v2*fac;
  } else {
    /* We have an extra deviate handy, so unset the flag
     * and return it.
     */
    iset=0;
    return gset;
  }
}

/* From Numerical Recipes.
 * "Minimal" random number generator of Park and Miller with Bays-Durham
 * shuffle and added safeguards.  Returns a uniform random deviate between
 * 0.0 and 1.0 (exclusive of the endpoint values).  Call with idum a negative
 * integer to initialize; thereafter, do not alter idum between successive
 * deviates in a sequence.  RNMX should approximate the larges floating
 * value that is less than 1.
 */
double ran1(int &idum) {
  const int IA=16807, IM=2147483647,IQ=127773,IR=2836,NTAB=32;
  const int NDIV=(1+(IM-1)/NTAB);
  const double EPS=3.0e-16,AM=1.0/IM,RNMX=(1.0-EPS);
  static int iy=0;
  static int iv[NTAB];
  int j,k;
  double temp;

  if (idum <= 0 || !iy) { // Initialize
    if (-idum < 1) idum = 1; // Be sure to prevent idum = 0
    else idum = -idum;
    for (j = NTAB+7; j>=0;j--) { // Load the shuffle table (after 8 warm-ups)
      k=idum/IQ;
      idum=IA*(idum-k*IQ)-IR*k;
      if (idum < 0) idum += IM;
      if (j < NTAB) iv[j] = idum;
    }
    iy=iv[0];
  }
  k=idum/IQ; // Start here when not initializing
  idum=IA*(idum-k*IQ)-IR*k; // Compute idum=(IA*idum) % IM without
  if (idum < 0) idum += IM; // overflows by Schrage's method
  j=iy/NDIV; // Will be in the range 0..NTAB-1
  iy=iv[j]; // Output previously stored value and refill the
  iv[j] = idum; // shuffle table.
  if ((temp=AM*iy) > RNMX) return RNMX;  // Because users don't expect endpoint values.
  else return temp;
}

}

