// Copyright 2003 Regents of the University of California

// Astropulse is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2, or (at your option) any later
// version.

// Astropulse is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
// more details.

// You should have received a copy of the GNU General Public License along
// with Astropulse; see the file COPYING.  If not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

/* ap_fold.C */
/* function for folding */

#ifndef _WIN32
#include "ap_config.h"
#include <unistd.h>
#else
#include <windows.h>
#define BOINC_APP_GRAPHICS 1
#endif

#include <cassert>
#include <cstdio>
#include <cstdlib>

#include "boinc_api.h"
#include "util.h"

#include "astropulse.h"
#include "lcgamm.h"
#include "ap_debug.h"
#include <cmath>

#define ITMAX 1000
#define EPS 1e-6
#define FP_EPS 1.0e-35


namespace Astropulse {

void set_key_nfb(std::vector<int> &key_nfb, float min_freq, int n_bins);

/* General plan: We are given an array of $fold_buf_size power bins.  Each bin contains one float, and
 * corresponds to a sum of powers from
 * $samples_per_bin complex time samples of dechirped data.  In the old fast folding 
 * algorithm (henceforth "ffa"), $samples_per_bin was fixed at the number 32.  We now allow it to vary.
 * Hence, $fold_buf_size * $samples_per_bin is a fixed value.
 *
 * We search for repeating signals of various periods T_i.  The period of a signal is defined as
 * the number of power bins over which it repeats.  To do a proper search, we must gradually
 * increment the period in such a way that it looks at every possible period.  This means that we
 * must set T_{i+1} = (1 + 1/$fold_buf_size) T_i every step, so that if two series of pulses,
 * with periods T_{i+1} and T_i, start at the same bin initially, they will end at adjacent bins.
 * The largest period T_0 is set to $fold_buf_size / $freq_0, where $freq_0 = 137, corresponding to
 * a period of 0.1 seconds.  We step by (1 + 1/$fold_buf_size) until we reach T_N = 2T_0,
 * where N is on the order of $fold_buf_size. 
 * Then, for each period T_i, we do two things:
 *  1) We use the Fast Folding Algorithm to test periods T_i / 2, T_i / 4, ...
 *  2) We co-add adjacent bins in groups of 2, 4, ...
 * Notes that the most important co-adds (2) are the ones that are around 10% to 30% of the
 * overall period, since this is a typical pulsar duty cycle.
 *
 * Optionally, instead of summing the entire fold buffer, we can sum over one "sub-buffer" at a
 * time, each of which contains $sub_buf_size power bins.  The fold buffer is divided into
 * $fold_buf_size / $sub_buf_size mutually exclusive sub-buffers.  In this case, we increment the period
 * by a factor of (1 + 1/$sub_buf_size) each time, and repeat the procedure for each sub-buffer
 * individually.
 */

/* Science::ffa
 *
 * arguments:
 * data: array of power bins.  Its length should be state.fold_buf_size, which is
 *       (data bytes) * (samples per byte) / (fold level)
 *    = (8 * 1024 * 1024) * (4) / (state.fold_level_<size>)
 *       where <size> is "large" or "small"
 * num_sub_buffers: we divide the buffer into a number of sub buffers, as discussed above
 * write_thresh_x: if this is set to true, we should be reading a noise file and using it to
 * set the thresholds, which we save in a file.
 */

int Science::ffa(float *data, int num_sub_buffers = 1, bool is_long = true, bool write_thresh_x = false, bool debug_msg = false, int pulse_limit_single = 0, int pulse_limit_rep = 0, double ffa_thresh_mult = 1.0) {

  state_t& state = client.ap_shmem->ap_gdata.state;

  /* n_bins = the size of the fold buffer, see above */
  int dm_chunk, n_bins, fold_level;
  if(is_long) {
    dm_chunk = state.dm_chunk_large;
    n_bins = state.fold_buf_size_long; 
    fold_level = state.fold_level_large;
  }
  else {
    dm_chunk = state.dm_chunk_small;
    n_bins = state.fold_buf_size_short;
    fold_level = state.fold_level_small;
  }
 
  /* The top level folding can reach one element beyond the actual data.
   * The allocations for the buffers have been increased so we can 
   * zero the n_bins element.
   */
  data[n_bins]=0.0f;
  state.num_sub_buffers = num_sub_buffers;
  int sub_buffer_size = n_bins / state.num_sub_buffers;
#if 0
  int size_of_temp_short = sizeof(float) * state.fold_buf_size_short
				/ state.num_sub_buffers;
  int size_of_temp_long = sizeof(float) * state.fold_buf_size_long
				/ state.num_sub_buffers;
#else
  /* Revised calc moved later so it can be done only when needed. */
  int size_of_temp_short;
  int size_of_temp_long;
#endif

  timer bin_timer;
  timer build_table_timer;
  timer ffa_checkpoint_timer;

  /* For debugging: replaces ap_client_main input with
   * data that has "ideal" lcgf behavior
  int idum = -1;
  for(int i = 0; i < n_bins; i++) {
     data[i] = random_combined_power_bin(dm_chunk, idum);
  } */

  /* float freq: the number of periods in a repeating pattern, over the length of the fold buffer.
   *   (Each fold buffer element representing 2^(fold_level) = 32 samples
   * int n_per: = (int)freq
   */
  // using increased precision for period, subfreq, and maxfreq
  double period;
  double subfreq;
  /* Max freq for folding - period of two samples */
  double maxfreq=(double) sub_buffer_size/2.0;
  int per_int, per_int_overlap;
  int j,k,l,m;
  static float *temp_long = NULL;
  static float *temp_short = NULL;
  static float *temp_coadd_long = NULL;
  static float *temp_coadd_short = NULL;
  float *temp;
  float *temp_coadd;
  int temp_coadd_working_length;
  int max_coadd;
  int n_per;
  int n_client_bins_pre_coadd, n_client_bins;
  ap_signal s;
#if 0
  /* I think this is actually waaay too big for the temp array; it really only needs
   * to be as long as the longest period, which this number divided by min_freq
          */
  int size_of_temp;
  if(is_long) size_of_temp = size_of_temp_long;
  else size_of_temp = size_of_temp_short;
#else
  /* Size is identical to largest used value of per_int in level 1 */
  int size_of_temp = sizeof(float) * (int) ceil((double)sub_buffer_size/state.min_freq);

  /* These are used for one-time allocations, should not depend on passed num_sub_buffers
   * parameter, but also need be no larger than the maximum possible per_int in level 1.
   */
  if (temp_short == NULL || temp_coadd_short == NULL)
    size_of_temp_short = sizeof(float) * (int) ceil((double)(state.fold_buf_size_short)
				 / state.min_freq);
  if (temp_long == NULL || temp_coadd_long == NULL)
    size_of_temp_long = sizeof(float) * (int) ceil((double)(state.fold_buf_size_long)
				 / state.min_freq);
#endif
  int last_log2 = 0;

  // using increased precision for dp
  // dp gives the relative period difference per iteration
  double dp;
  dp  = (double)state.num_sub_buffers/(double)n_bins;

  /* variables that depend on is_long */

  int *threshold_table;
  build_table_timer.start();
  if(!write_thresh_x) {
    client.status = "Building ffa threshold table";
    build_threshold_table(threshold_table, debug_msg, is_long, ffa_thresh_mult);  // Allocates memory for threshold_table
  } else if(debug_msg) {
    printf("In ap_fold.cpp: not building threshold table, writing ffa_thresh_mult file instead.\n");
    fprintf(stderr, "In ap_fold.cpp: not building threshold table, writing ffa_thresh_mult file instead.\n");
    fflush(stdout);
  }
  build_table_timer.stop();

  if(state.code_segment == build_threshold_table_short)
    state.code_segment = main_ffa_short;
  else if(state.code_segment == build_threshold_table_long)
    state.code_segment = main_ffa_long;
  else {
    fprintf(stderr, "Error: state.code_segment is inconsistent\n");
    exit(0);  // Error;
  }

  /* lcgf_file will contain a list of key_nfb's and their corresponding thresholds */
  FILE *thresh_x_file;
  static std::vector<int> key_nfb(0);
  static std::vector<float> max_thresh_x(0);
  static std::vector<int> n_bins_ntc(0);
  std::ostringstream thresh_x_filename;
  int n_ffa_bins;
  // The following vectors will be of the same length:
  static std::vector<int>::iterator iter_key_nfb;  // key_nfb vector
  static std::vector<float>::iterator iter_max_thresh_x; // max_thresh_x vector
  static std::vector<int>::iterator iter_nbins; // num of bins vector

  if(write_thresh_x) {
    thresh_x_filename << "max_thresh_x_for_dm_chunk_";
    thresh_x_filename << dm_chunk;
    thresh_x_file = boinc_fopen((thresh_x_filename.str()).c_str(), "a");
  }

  // Initialize key_nfb, max_thresh_x, n_bins_ntc, only if this is our first run through ffa
  static bool first_run = true;
  if(write_thresh_x && first_run) {
    set_key_nfb(key_nfb, state.min_freq, n_bins);
    max_thresh_x.resize(key_nfb.size(), 0);
    n_bins_ntc.resize(key_nfb.size(), 0);
    first_run = false;
  }

  // FILE* writefile = fopen("christest.dat", "a+");

  if (temp_long == NULL) {
    temp_long = (float *)malloc(size_of_temp_long);
    // Should we call an error here if malloc fails?
  }
  if (temp_coadd_long == NULL) {
    temp_coadd_long = (float *)malloc(size_of_temp_long);
  }
  if (temp_short == NULL) {
    temp_short = (float *)malloc(size_of_temp_short);
  }
  if (temp_coadd_short == NULL) {
    temp_coadd_short = (float *)malloc(size_of_temp_short);
  }
  
  if(is_long) { temp = temp_long; temp_coadd = temp_coadd_long; }
  else { temp = temp_short; temp_coadd = temp_coadd_short; }

  int sb_count = 0;
  long freq_count = 0;
  long subfreq_count = 0;
  long coadd_count = 0;
  long bin_count = 0;
  long pulse_count = 0;

  /* Level 0: Loop over all sub-buffers */
  for(; state.sub_buffer < state.num_sub_buffers; state.sub_buffer++) {
    sb_count++;
    client.status= "Computing fast fold.";
    if(state.freq == 0) state.freq = (double)state.min_freq;
    /* Level 1: Loop over all periods, from min to max */
    for(; state.freq < 2*(double)state.min_freq; state.freq *= (1 + dp)) {
      state.compute_fraction_done(-1);
      client.checkpoint();
      /* Check if app client is in standalone mode (as opposed to being
       * run by the boinc client.  If so, force checkpoint if over 300 seconds.
       */
      if(boinc_is_standalone()) {
        if(ffa_checkpoint_timer.get_total_seconds() > 300) {
          ffa_checkpoint_timer.reset();
	      client.do_checkpoint();
        }
      }
      
      client.status = "Fast folding algorithm";

      /* boinc API boinc_fraction_done tells BOINC how much
       * of the current workunit has been completed.  This is
       * used to inform the core client GUI of the % complete.
       */
      boinc_fraction_done(state.frac_done);
      boinc_ops_cumulative(state.frac_done*FLOPS_PER_DM*(state.dm_hi-state.dm_low)*log((double)state.fft_len)/log(32768.0),0);

      freq_count++;
      /* Report to stdout if we have reached a new "milestone" frequency. */
      if(int_log2(freq_count) > last_log2) {
        if(debug_msg) {
          printf("In ap_fold.cpp: freq = %f\n", state.freq);
          fprintf(stderr, "In ap_fold.cpp: freq = %f\n", state.freq);
          fflush(stdout);
        }
        last_log2 = int_log2(freq_count);
      }
      /* Set the period we will consider */
      period = (double)sub_buffer_size/state.freq;
      /* $per_int is the number of bins we will need in the temp array; one for
       * each bin in a period, and rounded up so we can catch the last fraction of
       * a bin.
       */
      per_int = (int)ceil(period);
      /* $n_per is the number of discrete blocks of length $period in one sub-buffer.
       * We will build the @temp array by looping through all $n_per blocks,
       * and adding each of them to @temp.
       */
      n_per = (int)state.freq;
      /* Number of bins of original @power array that we are adding together to
       * make each bin of @temp. 
       */
      n_client_bins_pre_coadd = dm_chunk*n_per;
      /* Set all elements of the temp buffer to zero */
      memset(temp, 0, size_of_temp);
      /* Fill the temp buffer.  How this works: define
       * A float block: a span of consecutive bins with a possibly non-integer length equal
       *   to $period.  It can start and end at fractions of bins.  I visualize the bins
       *   as having a left edge and a right edge, and a length of 1 unit.  E.g. bin 0
       *   has a left edge at 0, and a right edge at 1.
       * An int block: an discrete set of bins of length $per_int (which is $period rounded up)
       *   in the fold buffer.  By discrete, I mean that it starts at the left edge of its 
       *   leftmost bin, and ends at the right edge of its rightmost bin.
       * Then this code effectively divides the subbuffer into nonoverlapping, consecutive 
       * float blocks and (at the same time) possibly overlapping int blocks.
       * Any final (rightmost) bins that are too short to form a block of length $period
       * are ignored when dividing into float blocks.
       * The code then adds the int blocks together elementwise to make the @temp array.  
       * Each int block
       * corresponds to one float block, and it starts in the bin such that the left edge of
       * the int block is as close as possible to the left edge of the float block.
       *
       */
#if 0
      for (j=0; j<per_int; j++) {
        for (k=0; k<n_per; k++) {
          l = (int)(period*(double)k + 0.5 + (double)j) + state.sub_buffer * sub_buffer_size;
          /* Note: there's a danger that l == n_bins and we'll go over the edge of the
           * fold buffer.  This would happen if 
           * a) period == an integer + frac, for some frac < 0.5
           * b) period almost exactly divide n_bins, such that period * n_per > n_bins - 0.5 + frac
           * By (a), we have per_int = period + 1 - frac, so when j and k are maximum,
           * l = (int)(x), where
           *   x = period * (n_per - 1) + (per_int - 1) + 0.5
           *   x = (period * n_per - frac + 0.5) 
           *   x > n_bins
           * l = (int)(x) >= n_bins
           * l can't actually by > n_bins, so it must equal n_bins if this happens.  Hence
           * the "if" clause in the following statement:
           */
          if(l != n_bins) temp[j] += data[l];
        }
      }
#else
      /* First, calculate offsets in a tight loop so the compiler may be able
       * to arrange the truncation to ints efficiently.
       */
      int* offsets=new int[n_per];
      for (k=0; k<n_per; k++) offsets[k]=(int)(period*(float)k + 0.5) + state.sub_buffer * sub_buffer_size;
      /* Then, fold the data with cache-friendly loop nesting order.
       */
      for (k=0; k<n_per; k++) {
        register int ll=offsets[k];
        for (j=0; j<per_int; j++) {
          l = ll + j;
         /* Note: The n_bins element of data[] is allocated and zeroed so
          * no avoidance conditional is needed.
          */
          temp[j] += data[l];
        }//j
      }//k
	  delete[] offsets;
#endif

      /* Level 2: Loop over all subfrequencies freq, freq*2, freq*4 ... */
      for(subfreq = state.freq; subfreq < maxfreq; subfreq *= 2) {
        subfreq_count++;
        /* Note that per_int is not adjusted here, but at the end of the for loop. */
        period =(double)sub_buffer_size/subfreq;
        /* We can coadd up to 2^max_coadd bins, which must be at most half the period. */
        max_coadd = int_log2(per_int) - 1;
        /* Create a second temporary buffer to hold the co-added bins.  */
        memcpy(temp_coadd, temp, sizeof(float) * per_int);
        /* The size of the coadd buffer starts out equal to a period, then shrinks by factors of 2. */
        temp_coadd_working_length = per_int;
 	/* n_client_bins_pre_coadd is the number of client bins summed at coadd 0, and will double
 	 *   each time we cycle through the level 2 loop.  (Iterate through doubled frequencies)
 	 * n_client_bins is the number of client bins summed at a particular coadd, and will double
 	 *   each time we cycle through the level 3 loop, in addition to the level 2 loop.
 	 * The doubling happens at the end of the loop.
         */
        n_client_bins = n_client_bins_pre_coadd;
        /* Level 3: Loop over coadds */
        for(int coadd = 0; coadd <= max_coadd; coadd++ ) {
          coadd_count++;
          if(write_thresh_x) {
            n_ffa_bins = n_client_bins / dm_chunk;
            /* Find the first key_nfb that is larger than n_ffa_bins, and
             * prepare iter_max_thresh_x to point to the corresponding max_thresh_x
             */
            for(iter_key_nfb = key_nfb.begin(), iter_max_thresh_x = max_thresh_x.begin(), iter_nbins = n_bins_ntc.begin();
                iter_key_nfb != key_nfb.end(); iter_key_nfb++, iter_max_thresh_x++, iter_nbins++) {
              assert(iter_max_thresh_x != max_thresh_x.end() && iter_nbins != n_bins_ntc.end());
              if(*iter_key_nfb > n_ffa_bins)
                break;
            }
            int q = *iter_key_nfb;  // Pause point for gdb
            /* Count the number of bins per key_nfb */
            *iter_nbins += temp_coadd_working_length;
          }
          /* Level 4: Loop over bins.  We'll do the coadd at the end of the loop. */
          bin_timer.start();
          double cur_cpu_time;
          boinc_wu_cpu_time(cur_cpu_time);
          client.ap_shmem->ap_gdata.cpu_time = cur_cpu_time;
          for(m = 0; m < temp_coadd_working_length; m++) {
            bin_count++;
            /* If a bin goes over threshhold, record it.
             * We determine if a bin is over threshhold by deciding whether its
             * probability of existence (in random noise) is < 1/(number of bins we examine per workunit)
             * This ensures that on average we will detect 1 false positive.
             * There are N freq's, where (1 + 1/sub_buffer_size)^N = 2.
             * That is, e^(N/sub_buffer_size) = 2, so N = sub_buffer_size * ln 2.
             * Each freq has a period equal to sub_buffer_size / freq bins, for a
             * total of about sub_buffer_size^2 / state.min_freq terms.
             * And sub_buffer_size = num_samples / (dm_chunk * state.num_sub_buffers), so that makes
             * num_samples^2 / (dm_chunk^2 * state.num_sub_buffers^2 * state.min_freq)
             * Second, for each freq, there are a number of subfreqs, each of which has
             * half as many bins as the previous.  This doubles the number of bins.
             * Third, for each subfreqs, there are a number of coadds, each of which
             * has half as many bins as the previous.  This doubles the number of
             * bins again.
             * Fourth, the code runs state.num_sub_buffers times, so we multiply by that.
             * Finally, there are 15000 / dm_chunk dm chunks, and we run
             * the ffa once per dm chunk.
             * Therefore, the number of bins we examine per workunit is
             * (num_samples^2 * 4 * 15000) / (state.num_sub_buffers * state.min_freq * dm_chunk^3).
             * Currently, num_samples = 32 * 10^6, state.num_sub_buffers = 1, state.min_freq = 137,
             * and dm_chunk = 128.
             * That's 2 * 10^11 bins per workunit, which is e^26.18
             * This suggests that the lcgf should have a threshold of -26.  But evidently this isn't enough ... ?
             * Note: for dm_chunk_small, change sub_buffer_size to fold_buf_bytes_short * 4 / dm_chunk
             * (fold_buf_bytes_short^2 * 16 * 4 * 15000) / (state.num_sub_buffers * state.min_freq
             *                * dm_chunk^3)    ... I get e^(-25.5)
             *
             * First, total power in these bins must exceed the number of client bins.
             */
            if(temp_coadd[m] > n_client_bins + 1) {
              /* If we're writing the thresholds file, then
               * check if we have a new max thresh_x for this key_nfb. 
               */
              if (write_thresh_x) {
                float cur_thresh_x = (temp_coadd[m] - n_client_bins) / sqrt(2 * (double) n_client_bins);
                // lcgf((double) n_client_bins, temp_coadd[m]);
                if(cur_thresh_x > *iter_max_thresh_x)
                  *iter_max_thresh_x = cur_thresh_x;
              } else if (temp_coadd[m] >= threshold_table[n_client_bins / dm_chunk]) {
                if(pulse_limit_rep && state.result_count_rep >= pulse_limit_rep) // We've already found enough repeating pulses.  (Must be that we haven't found enough single pulses.)  Don't record this pulse.
		    continue;
                // Temporary split between if statements?
                pulse_count++;

                time_series_b_t time_series;
                time_series.build(temp_coadd, m, temp_coadd_working_length, (double) n_client_bins);

                /* I don't understand why state.dm_now / dm_chunk is here. */
                state.pulse_jd = state.jd0 + (state.data_chunk_now + state.dm_now/dm_chunk)
                                        /(state.datasize)*(state.jd1-state.jd0);
                s.time=state.pulse_jd;
                // s.peak_bin = state.data_chunk_now*4 + m;
                s.peak_bin = m * dm_chunk * (1 << coadd);
                s.peak_power = temp_coadd[m];
                s.scale = fold_level;
                if(is_long)
                  s.dm = state.dm_sign*(state.dm_chunk_large_now);
		else
                  s.dm = state.dm_sign*(state.dm_chunk_large_now+state.dm_chunk_small_now);
                s.period = period;
		s.ffa_scale = coadd;
                // Machinery for writing s.num_std_devs.  fudge_factor is about 1.
                float thresh_x_intercept;
                float thresh_x_slope;
                float fudge_factor;
                if(is_long) {
                     thresh_x_intercept = 4.898856 * ffa_thresh_mult;
                     thresh_x_slope = -0.00000565047 * ffa_thresh_mult;
                } else {
                     thresh_x_intercept = 4.829341 * ffa_thresh_mult;
                     thresh_x_slope = -0.0000536972 * ffa_thresh_mult;
                }
                fudge_factor = 1 - thresh_x_slope * (n_client_bins / dm_chunk) / thresh_x_intercept;
		s.num_std_devs = (s.peak_power - n_client_bins)/(fudge_factor * sqrt((double)n_client_bins));
                s.time_series=time_series.data;
		if(add_signal(client.signal_vector, s)) state.result_count_rep++;
                // client.outfile.output_result(s);
		
                if(pulse_limit_single && state.result_count_single >= pulse_limit_single && pulse_limit_rep && state.result_count_rep >= pulse_limit_rep) {
                  // Quit early
                  printf("Found %d single pulses and %d repeating pulses, exiting.\n", state.result_count_single, state.result_count_rep);
                  fprintf(stderr, "Found %d single pulses and %d repeating pulses, exiting.\n", state.result_count_single, state.result_count_rep);
                  fflush(stdout);
		  client.outfile.write_main(client.signal_vector);
                  client.outfile.finish_results();
                  client.finish();
                  main_timer.stop();
                  boinc_finish(0);
                  exit(0);
                }
              }
            } // End if (bin goes over threshhold)
          } // End loop over bins
          bin_timer.stop();
          /* Coadd adjacent bins, and throw away the odd bin at the end if any. */
          Science::coadd_in_place(temp_coadd, per_int);
          /* Now that our coadd has increased, there are less bins in our temp_coadd array ... */
          temp_coadd_working_length >>= 1;
          /* ... and there are more samples represented by each bin */
          n_client_bins <<= 1;
        } // End loop over coadds

        /* Cut the temp array's size in half, adding the second half to the first half
         *
         * Note per_int and per_int_overlap differ iff the old per_int is odd.
         *
         * Odd block size issues:
         * There's some question about what to do when the array has an odd size.
         * I believe the most sensible thing to do is to round up, and allow the
         * two halves to overlap by one bin.  This is in keeping with our convention that
         * $per_int is $period rounded up.  If $per_int is odd, then $period is slightly 
         * greater than an even integer, so $period / 2 is also slightly greater than
         * an even integer.  Therefore the new $per_int should be odd as well.
         * However, no system is entirely satisfactory.  Under certain conditions, it is
         * possible for float blocks to differ from their respective int blocks by as
         * much as 1.5 bins.  This may interfere with detection of very narrow pulses.
         * (To see this effect, try starting with block of size 9, whose float block is
         * of length 9 - epsilon and starts at 0.5 - epsilon after the int block starts.
         * Cut the int block in halves twice and look at the last of the four subblocks.)
         */
        per_int_overlap = per_int >> 1;
        per_int = (per_int + 1)  >> 1;
        for (j=0; j<per_int; j++) {
          temp[j] = temp[j] + temp[j+per_int_overlap];
        }
        n_client_bins_pre_coadd = n_client_bins_pre_coadd << 1;
      } // End loop over subfrequencies
    } // End loop over periods
    state.freq = 0;  // Codes for min freq
  } // End loop over all subbuffers
  state.sub_buffer = 0;

  if(!write_thresh_x) {  // WARNING: if we're writing thresh_x, this is never allocated to begin with.
    if(threshold_table) {delete [] threshold_table; threshold_table = NULL;}
  }

  if(write_thresh_x) {
    for(iter_key_nfb = key_nfb.begin(), iter_max_thresh_x = max_thresh_x.begin(), iter_nbins = n_bins_ntc.begin();
        iter_key_nfb != key_nfb.end(); iter_key_nfb++, iter_max_thresh_x++, iter_nbins++) {
      assert(iter_max_thresh_x != max_thresh_x.end() && iter_nbins != n_bins_ntc.end());
      fprintf(thresh_x_file, "%d %f %d\n", *iter_key_nfb, *iter_max_thresh_x, *iter_nbins);
    }
    fclose(thresh_x_file);
  }

  if(debug_msg) {
    printf("Time spent in building threshold table: %d seconds, %d ticks\n", build_table_timer.get_seconds(), build_table_timer.get_ticks());
    fprintf(stderr, "Time spent in building threshold table: %d seconds, %d ticks\n", build_table_timer.get_seconds(), build_table_timer.get_ticks());
    printf("Time spent in bins: %d seconds, %d ticks\n", bin_timer.get_seconds(), bin_timer.get_ticks());
    fprintf(stderr, "Time spent in bins: %d seconds, %d ticks\n", bin_timer.get_seconds(), bin_timer.get_ticks());
    printf("sb_count: %d\nfreq_count: %ld\nsubfreq_count: %ld\ncoadd_count: %ld\nbin_count: %ld\npulse_count: %ld\n", sb_count, freq_count, subfreq_count, coadd_count, bin_count, pulse_count);
    fprintf(stderr, "sb_count: %d\nfreq_count: %ld\nsubfreq_count: %ld\ncoadd_count: %ld\nbin_count: %ld\npulse_count: %ld\n", sb_count, freq_count, subfreq_count, coadd_count, bin_count, pulse_count);
    fflush(stdout);
  }
  return(0);
}

/* build_threshold_table
 *
 * Create an array, and store it in @table.
 * The index of the array represents an nfb ("number of ffa bins"), and the value represents
 * the threshold for that nfb.  If we add $nfb ffa bins together, and the total power has
 * power > table[nfb], we have detected a pulse.
 */
void Science::build_threshold_table(int*& table, bool debug_msg, bool is_long, double ffa_thresh_mult) {
  double thresh_x_intercept;
  double thresh_x_slope;
  double thresh_x;

  // Numbers come from lcgf_test.cpp, using e^-25.49 for short and e^-26.18
  // for long fold buffers
  // Note this (below) doesn't mean the x intercept, it means the y intercept for the
  // function thresh_x(nfb).  Bad notation.
  if(is_long) {
    // rewrite this one
    // artificially high one: thresh_x_intercept = 10;
    thresh_x_intercept = 4.898856 * ffa_thresh_mult;
    // old: thresh_x_slope = -0.0000131539;
    thresh_x_slope = -0.00000565047 * ffa_thresh_mult;
  } else {
    // artificially high one: thresh_x_intercept = 10;
    thresh_x_intercept = 4.829341 * ffa_thresh_mult;
    thresh_x_slope = -0.0000536972 * ffa_thresh_mult;
  }

  state_t& state = client.ap_shmem->ap_gdata.state;
  int min_nfb = state.min_freq; // We add at least min_freq ffa bins ...
  int max_nfb;
  if(is_long)
    max_nfb = state.fold_buf_size_long; // And at most all of the bins.
  else
    max_nfb = state.fold_buf_size_short; 
  float threshold;
  if (debug_msg) {
    printf("In ap_fold.cpp: at Science::build_threshold_table()\n");
    fprintf(stderr, "In ap_fold.cpp: at Science::build_threshold_table()\n");
    fflush(stdout);
  }
  table = new int[max_nfb+1];

  for(state.nfb = min_nfb; state.nfb <= max_nfb; state.nfb++) {
    int ncb;
    if(is_long) {ncb = state.nfb * state.dm_chunk_large;}  // Are we in long or short ffa?
           else {ncb = state.nfb * state.dm_chunk_small;}
    state.compute_fraction_done(-2);
    thresh_x = thresh_x_intercept + thresh_x_slope * state.nfb;
    threshold = ncb + thresh_x * sqrt(2.0 * (double) ncb);
    table[state.nfb] = threshold;
  }
}

/* Search for the smallest power_sum between min and max inclusive
 * such that lcgf(ncb, power_sum) <= threshold.  If no value satisfies
 * the inequality, return -1, a signal that we should search higher.
 *
 * Since we are no longer using lcgf, this function is no longer in use
 */
int Science::lcgf_binary_search(int min, int max, int ncb, float thresh_lcgf) {
  if (min == max) { // base case
    if (lcgf(ncb, min) > thresh_lcgf) return -1; // Search higher!
    else return min; // Found it!
  }
  int cut = (min + max) / 2;
  if (lcgf(ncb, cut) <= thresh_lcgf) return lcgf_binary_search(min, cut, ncb, thresh_lcgf);
  else return lcgf_binary_search(cut + 1, max, ncb, thresh_lcgf);
}

/* set_key_nfb
 *
 * Arguments:
 * key_nfb: output array  (nfb = number of ffa bins)
 * min_freq: minimum frequency considered by the ffa 
 * n_bins: number of ffa bins processed by the ffa (each ffa bin corresponds to dm_chunk of the client's bins)
 *
 * Algorithm:
 * The purpose of this function is to set an array of key_nfb's.  An nfb is a number of ffa bins
 * that we add together to make one power sum.  Between any two consecutive key_nfbs should be roughly
 * the same number of power sums.  (That way, when we find the min lcgfs for each key_nfb, they will be
 * roughly the same.)
 * For instance, if the array of key_nfbs goes: 137, 139, 142 ...  then we would expect there to be
 * the same number of power sums between 137 to 139 as between 139 to 142.
 * Hence, to perform this algorithm we must be able to count how many power sums will be processed 
 * for each ncb.  (Number of client bins.)
 *
 * The first point is that the only allowed n_ffa_bins are of the form base_freq * 2^n, where
 * min_freq <= base_freq < 2 * min_freq.  However, we treat n_ffa_bins as a continuous variable.
 * For each n_ffa_bins, we estimate the number of power sums between it and n_ffa_bins + 1.
 *
 * This is the product of:
 *
 *  1) Assume we're at coadd 0, so n_ffa_bins = subfreq.  Then find # of subfreqs in the interval:
 *  subfreqs = n_bins / n_ffa_bins  
 *    Each new subfreq is obtained from the previous one by multiplication
 *    with 1 + 1 / n_bins, which we can do about n_bins / subfreq times before
 *    we change n_per, i.e. int(subfreq), thereby changing n_ffa_bins.
 *
 *  2) Include all coadds.
 *  coadds = log_2(n_ffa_bins / min_freq) + 1
 *   (For n_ffa_bins = base_freq*2^n, we must count coadd 0 through n.  For instance,
 *    for n = 2, the given nfb will come from coadds of 3 subfreqs:)
 *    * * * * * * * *
 *    **  **  **  **
 *    ****    ****
 *
 *  3) Number of translations allowed  (phases of the wave)
 *  trans = n_bins / n_ffa_bins
 *   (The number of right and left translations of n_ffa_bins bins that fit into n_bins.
 *    In the example below, the number is 2:)
 *    **  **  **  **
 *      **  **  **  **
 *
 * After we've counted up a certain number of bins, the value of which is stored in
 * key_increment_bins, we store a key_nfb, and start counting bins from 0 again.
 */
void set_key_nfb(std::vector<int> &key_nfb, float min_freq, int n_bins) {
  int total_bins = 0;
  int bins_per_nfb_subfreq = 0;
  key_nfb.clear();

  /* key_increment_bins: once we've counted this number of lcgfs, move on to the next key_nfb */
  int key_increment_bins = (int)(((double)n_bins * (double)n_bins / min_freq) / 240);
  int test_val;

  for (int nfb_subfreq = (int)min_freq; nfb_subfreq < n_bins; nfb_subfreq++) {
    bins_per_nfb_subfreq = ((double)n_bins * n_bins) / ((double)nfb_subfreq * nfb_subfreq) * (int_log2((int)(nfb_subfreq/min_freq)) + 1);
    total_bins += bins_per_nfb_subfreq;
    if (total_bins >= key_increment_bins) {
      total_bins = 0;
      key_nfb.push_back(nfb_subfreq);
      for (std::vector<int>::iterator iter = key_nfb.begin(); iter != key_nfb.end(); iter++)
        test_val = *iter;
    }
  }
  key_nfb.push_back(n_bins-1);  // One last key_nfb for the ones that don't have a place yet
}
}


