// 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_client_main.C */
/* Main program for AstroPulse client */
#include "ap_config.h"
#ifdef _WIN32
#include "boinc_win.h"
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef _WIN32
#include <windows.h>
#define BOINC_APP_GRAPHICS 1
#endif

#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include "boinc_api.h"
#include "util.h"
#include "str_util.h"

#include "astropulse.h"
#include "diagnostics.h"

#include "mtrand.h"

#ifdef BOINC_APP_GRAPHICS
// #include "ap_graphics.h"   Not part of the main program any more
#include "ap_gfx_main.h"
#include "graphics2.h"
#endif

#include <time.h>

long starttime = time(0);
int lastcputicks = clock();
float cputime = 0;
int maxcputicksinterval = 0;

// #define DEBUGGING_0 1 // If set to 1, print program status regularly (i.e. run in a verbose manner)

#ifdef _WIN32
#include <windows.h>
#include "stackwalker_win.h"

extern int main(int argc, char** argv);

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR Args, int WinMode) {
  LPSTR command_line;
  char* argv[100];
  int argc;

  //InitAllocCheck();

  command_line = GetCommandLine();
  argc = parse_command_line(command_line,argv);

  int ret = main(argc, argv);

  //DeInitAllocCheck();

  return ret;
}
#endif

/* Default thresholds */
static float thresh_def[] = {
                              (float)21.50,
                              (float)24.748650,
                              (float)30.015163,
                              (float)38.773587,
                              (float)53.662645,
                              (float)79.565603,
                              (float)125.71365,
                              (float)209.85380,
                              (float)366.44671,
                              (float)662.93874,
                              (float)1232.2705,
                              0, 0, 0, 0
                            };

#ifdef HAVE_GL_LIB
#ifdef UNIX

#include <GL/glut.h>

void displayCB(void)  /* function called whenever redisplay needed */
{
  glClear(GL_COLOR_BUFFER_BIT);  /* clear the display */
  glColor3f(1.0, 1.0, 1.0);  /* set current color to white */
  glBegin(GL_POLYGON);   /* draw filled triangle */
  glVertex2i(200,125);   /* specify each vertex of triangle */
  glVertex2i(100,375);
  glVertex2i(300,375);
  glEnd();    /* OpenGL draws the filled triangle */
  glFlush();    /* Complete any pending operations */
}

void keyCB(unsigned char key, int x, int y) /* called on key press */
{
  if ( key == 'q' ) exit(0);
}

#endif
#endif

/*
enum OnOff {on, off};
 
class timer {
  private:
  clock_t ticks;
  clock_t last_clock;
  clock_t start_clock;
 
  long seconds;
  long last_time;
  long start_seconds;
 
  OnOff state;
 
  public:
  timer() {
    ticks = 0;
    seconds = 0;
    start_clock = clock();
    start_seconds = time(NULL);
    state = off;
  }
  void start() {
    last_clock = clock();
    last_time = time(NULL);
    assert(state == off);
    state = on;
  }
  void stop() {
    clock_t ticks_this_time = clock() - last_clock;
    ticks += ticks_this_time;
    long seconds_this_time = time(NULL) - last_time;
    seconds += seconds_this_time;
    assert(state == on);
    state = off;
  }
  void reset() {
    ticks = 0;
    seconds = 0;
    start_clock = clock();
    start_seconds = time(NULL);
    state = off;
  }
  clock_t get_ticks() {
    return ticks;
  }
  clock_t get_total_ticks() {
    return clock() - start_clock;
  }
  long get_seconds() {
    return seconds;
  }
  long get_total_seconds() {
    return time(NULL) - start_seconds;
  }
};
 
void print_time() {
  long seconds = time(NULL) - starttime;
  long minutes = seconds / 60;
  int hours = minutes / 60;
  int days = hours / 24;
  printf("Time since start: %d days, %d hours, %ld minutes, %ld seconds\n",
   days, hours % 24, minutes % 60, seconds % 60);
  fprintf(stderr, "Time since start: %d days, %d hours, %ld minutes, %ld seconds\n",
   days, hours % 24, minutes % 60, seconds % 60);
}
*/


// This was a function used temporarily for debugging.
// It looks at a short time series, and determines whether
// the power stays above a certain threshhold for a certain length
// of time in consecutive bins.
// Not sure why I wanted to know this.
#if 0
int detect_hit(float *arr, float& total) {

  int stage = 0;
  total = 0;
  int count = 0;
  for (int i = 0; i < 10; i++) {
    if ( arr[i] < 1 && stage == 0) {
      return 0;  // not a hit; goes below 1 too soon.
    }
    if ( arr[i] > 5 && stage == 0) { // first time above 5
      stage = 1;
      count++;
    }
    if ( arr[i] > 5 && stage == 1) { // Above 5, but not first time
      count++;
    }
    if ( arr[i] < 5 && stage == 1) { // Going below 5
      if (count < 5) return 0; // goes below 5 too soon
      else stage = 2; // goes below 5 at the right time
    }
    total += arr[i];
  }

  if (count < 5) return 0;
  return 1;
}
#endif

/* updatecputime
 *
 * Purpose: determine the runtime of the program.
 * Sets the global variable cputime to equal the number of seconds the program has been running so far.
 * Sets the global variable maxcputicksinterval to the maximum interval in clock ticks so far between
 * calls to this function.
 */
void updatecputime() {
  int cputicksinterval = abs(clock() - lastcputicks);
  lastcputicks = clock();
  maxcputicksinterval = (maxcputicksinterval > cputicksinterval)?maxcputicksinterval:cputicksinterval;
  cputime += ((float)cputicksinterval / (float)CLOCKS_PER_SEC);
}

/* worker
 *
 * Function is not called unless graphics are turned on.
 */
void worker() {
  Astropulse::client.init();
  Astropulse::client.science.mainloop();
  Astropulse::client.finish();
  boinc_finish(0);
}

// Command-line arguments must be declared globally, because worker() takes no arguments
bool skipffa_long = false;
bool skipffa_short = false;
bool debug_msg; // set to true if debugging
bool debug_loop_msg; // set to true if debugging
bool write_lcgf_short = false;
bool write_lcgf_long = false;
bool remove_radar = false;
bool print_best = false;
bool pulsegraphs = false;
int single_dm_chunk_large = -1;
int single_dm_chunk_small = -1;
int data_byte_limit = -1;
int single_dm = -1;
int full_dm = -1;
int pulse_limit_single = 30;
int pulse_limit_rep = 30;
int fold_dm = 0;
int fold_dm_chunk_small = 0;
double ffa_thresh_mult = 1.0;

APP_INIT_DATA app_init_data;

/* Main Program */
int main(int argc, char *argv[]) {

  Astropulse::main_timer.start();

  //char* foo = (char*) (void*) 0xbadf00d;
  //*foo = 'x';

  //fprintf(stderr, "START TIME: %ld\n", time(NULL));

  int i, retval;
  bool standalone = false;

  /* Initialize Diagnostics
   *
   * The diagnostics flags are defined in boinc/lib/diagnostics.h
   * There are many flags, and we are setting some of them.  E.g.
   * BOINC_DIAG_REDIRECTSTDERR redirects stderr to stderr.txt
   */
  unsigned long dwDiagnosticsFlags = 0;

  dwDiagnosticsFlags =
    BOINC_DIAG_DUMPCALLSTACKENABLED |
    BOINC_DIAG_HEAPCHECKENABLED |
    BOINC_DIAG_TRACETOSTDERR |
    BOINC_DIAG_REDIRECTSTDERR;

  /* boinc_init_diagnostics is a function in the boinc API.
   */
  retval = boinc_init_diagnostics(dwDiagnosticsFlags);
  if (retval) {
    fprintf(stderr, "boinc_init_diagnostics failed: %d\n", retval);
    exit(retval);
  }

  boinc_parse_init_data_file();
  boinc_get_init_data(app_init_data);
  // We've moved the state variable into the graphics shmem segment,
  // so we always need to initialize graphics.
  ap_graphics_init(app_init_data);  


  /* Possible arguments:
   * -standalone
   * -reset: reset ap_state.dat and pulse.out
   * -thresh0 %f: Set the power threshhold value for zero co-adds
   * -skipffa_long, -skipffa_short: Don't run the fast folding algorithm
   * -print_best: Print out the best pulses on a regular basis
   * -debug_msg: Print out debugging messages
   * -debug_loop_msg: Print out debugging messages for all statements in the main loop
   */
  for (i=1; i<argc; i++) {
    if (!strcmp(argv[i], "-standalone")) {
      standalone = true;
      fprintf(stderr, "## debug standalone mode\n");
    }
    if (!strcmp(argv[i], "-reset")) {
      fprintf(stderr, "## resetting ap_state.dat and pulse.out\n");
      unlink("ap_state.dat");
      unlink("pulse.out");
      continue;
    }
    if (standalone && !strcmp(argv[i], "-thresh0") && ++i < argc) {
      fprintf(stderr, "## thresh[0] = %f\n",
              (Astropulse::client.ap_shmem->ap_gdata.state.thresh[0] =(float)atoi(argv[i])) );
      continue;
    }
    if (!strcmp(argv[i], "-skipffa_long")) {
      skipffa_long = true;
      fprintf(stderr, "Skipping the long ffa\n");
    }
    if (!strcmp(argv[i], "-skipffa_short")) {
      skipffa_short = true;
      fprintf(stderr, "Skipping the short ffa\n");
    }
    if (!strcmp(argv[i], "-remove_radar")) {
      remove_radar = true;
      fprintf(stderr, "Removing radar\n");
    }
    if (!strcmp(argv[i], "-debug_msg")) {
      debug_msg = true;
      fprintf(stderr, "Debugging messages on\n");
    }
    if (!strcmp(argv[i], "-debug_loop_msg")) {
      debug_loop_msg = true;
      fprintf(stderr, "Debugging loop messages on\n");
    }
    if (!strcmp(argv[i], "-write_lcgf_long")) {
      write_lcgf_long = true;
    }
    if (!strcmp(argv[i], "-write_lcgf_short")) {
      write_lcgf_short = true;
      if(write_lcgf_long) {
        fprintf(stderr, "Error: can't write both lcgf_short and lcgf_long.\n");
        exit(1);
      }
    }
    if (!strcmp(argv[i], "-print_best")) {
      print_best = true;
    }
    /* We can print data files that will let us graph
     * number of pulses vs. dm, or # vs. power
     */
    if (!strcmp(argv[i], "-pulsegraphs")) {
      pulsegraphs = true;
      fprintf(stderr, "Printing pulsegraphs.\n");
    }
    /* -dm_chunk (the command line argument) should be set to
     * dm_low + multiple of dm_chunk (the values defined in
     * the header of the workunit.)
     */
    if (!strncmp(argv[i], "-dm_chunk_large", 15)) {
      single_dm_chunk_large = atoi(argv[i] + 15);
      fprintf(stderr, "Running on a single large dm chunk: %d\n", single_dm_chunk_large);
      /* -dm_chunk_small (the command line argument) should be set to a value
       * divisible by dm_chunk_small, and between 0 and dm_chunk_large 
       * (defined in the header of the workunit)
       */
    } else if (!strncmp(argv[i], "-dm_chunk_small", 15)) {
      single_dm_chunk_small = atoi(argv[i] + 15);
      fold_dm_chunk_small = single_dm_chunk_small;
      fprintf(stderr, "Running on a single small dm chunk: %d\n", single_dm_chunk_small);
      /* -dm (the command line argument) should be set to a value between 0 and
       * dm_chunk_small (defined in the header of the workunit)
       */
    } else if (!strncmp(argv[i], "-dm", 3)) {
      single_dm = atoi(argv[i] + 3);
      fold_dm = single_dm;
      fprintf(stderr, "Running on a single dm: %d\n", single_dm);
    } else if(!strncmp(argv[i], "-full_dm", 8)) {
      full_dm = atoi(argv[i] + 8);
    } else if (!strncmp(argv[i], "-pulse_limit_single", 19)) {
      // To get no pulse limit, set -pulse_limit_single0
      pulse_limit_single = atoi(argv[i] + 19);
      fprintf(stderr, "Running on pulse limit single %d\n", pulse_limit_single);
    } else if (!strncmp(argv[i], "-pulse_limit_rep", 16)) {
      // To get no pulse limit, set -pulse_limit_rep0
      pulse_limit_rep = atoi(argv[i] + 16);
      fprintf(stderr, "Running on pulse limit rep %d\n", pulse_limit_rep);
    }
    /* -data_byte_limit should be set to a value between 0 and
     * datasize (which is 1/4 of nsamples, defined in the header of
     * the workunit.)  Actually the upper limit is datasize - (something small)
     * The smallest meaningful amount of data bytes is 4096, which is
     * (fft_len / 4) / 2, see below for discussion.
     */
    if (!strncmp(argv[i], "-data_byte_limit", 16)) {
      data_byte_limit = atoi(argv[i] + 16);
      fprintf(stderr, "Running on data chunks starting up to: %d\n", data_byte_limit);
    }
	if (!strncmp(argv[i], "-ffa_thresh_mult", 16)) {
	  ffa_thresh_mult = atof(argv[i] + 16);
	  fprintf(stderr, "Multiplying ffa threshold by: %f\n", ffa_thresh_mult);
	}
  }

// #ifndef BOINC_APP_GRAPHICS
  /* The function below is defined in boinc/api/boinc_api.C */
  retval = boinc_init();
  if (retval) {
    fprintf(stderr, "boinc_init failed: %d (try -standalone for testing)\n", retval);
    exit(retval);
  }
// #else
  //app_graphics_init();
  // ap_graphics.data_struct_init();
  /* Boinc API: initialize boinc with graphics.  worker() is a function
  * that performs the role of main(), whereas main() should run
  * the graphics.  It's not clear to me that this is in fact what happens:
  * it looks to me like both main and worker are playing the same role here.
  */
  // retval = boinc_init_graphics(worker);
  // if (retval) {
  //   fprintf(stderr, "boinc_init_graphics failed: %d\n", retval);
  //  exit(retval);
  // }
// #endif

  /* The 3 functions below are all defined in this file */
  if (debug_msg) {
    printf("In ap_client_main.cpp: at Astropulse::client.init()\n");
    fprintf(stderr, "In ap_client_main.cpp: at Astropulse::client.init()\n");
  }
  // checking for blank statefile
  // printf("Just before client.init()\n");
  // fprintf(stderr, "Just before client.init()\n");
  Astropulse::client.init();
  if (debug_msg) {
    printf("In ap_client_main.cpp: at Astropulse::client.science.mainloop()\n");
    fprintf(stderr, "In ap_client_main.cpp: at Astropulse::client.science.mainloop()\n");
    fflush(stdout);
  }
  Astropulse::client.science.mainloop();
  if (debug_msg) {
    printf("In ap_client_main.cpp: at Astropulse::client.finish()\n");
    fprintf(stderr, "In ap_client_main.cpp: at Astropulse::client.finish()\n");
    fflush(stdout);
  }
  Astropulse::client.finish();

  //fprintf(stderr, "END TIME: %ld\n", time(NULL));

  Astropulse::main_timer.stop();

  /* The function below is defined in boinc/api/boinc_api.C
   * The argument 0 means we've successfully finished the result.
   */
  boinc_finish(0);
  return 0;
}

double interpolate_dec(double jd0, double jd1, double pulse_jd, double dec0, double dec1);
double interpolate_ra(double jd0, double jd1, double pulse_jd, double ra0, double ra1);

void segfault_atexit() {
  assert(0);
}

/* variables and functions defined here may be called via
 * Astropulse::<variable> or Astropulse::<function>
 */
namespace Astropulse {
/* The class below is defined in astropulse.h
 * Each Client contains an Outfile, Wufile, Foldfile, Statefile, and Science.
 */
Client client;
timer main_timer;

/* calculate_fold_level
 *
 * Argument: int dm_chunk 
 * Return value: The highest power of 2 that is less than or equal to dm_chunk.
 * So calculate_fold_level(32) == 5 and calculate_fold_level(63) == 5.
 */
inline int calculate_fold_level(int dm_chunk) {
  /* Figure out sizes */
  int level = 0;
  for (;; ++level) {
    int temp = dm_chunk >> level;
    if (temp == 0) {
      fprintf(stderr, "Error calculating fold_level.\n");
      state_t &state=client.ap_shmem->ap_gdata.state;
      state.print();
      exit(PARSE_ERR);
    }
    if (temp == 1) {
      return level;
    }
  }
}

int pow_int(int a, int b) {
  int retval = 1;
  for(int i = 0; i < b; i++) {
    retval *= a; 
  }
  return retval;
}

/* Calculates the highest power of two that is a factor
 * of the input.  For instance,
 * input 4 => output 2
 * input 8 => output 3
 * input 12 => output 2
 * UNUSED FUNCTION
inline int calculate_power_of_two_factor(int dm) {
  int retval = 0;
  assert(dm != 0);
  while(dm % 2 == 0) {
    retval++;
    dm /= 2;
  }
  return retval;  
}
*/

state_t::state_t() {
  /* Default parameters for a state_t.
   * This constructor function is mostly useless.
   * In our program, the only state_t is the global variable client.state.
   * In Client::init, some of the members of client.state are reset by
   * Wufile::parse_header_to_state, which alters the
   * global variable by reading
   * from in.dat.  Other members are set in the body of Client::init
   * 
   * Variables that do not appear to get reset include:
   * best[].peak_power, best[].period
   * data_chunk_now, frac_done, and result_count
   */
  datasize  = 8*1024*1024;
  dm_low    = 1;                                      // ~ 0 pc/cm^3
  dm_hi     = 15000;                                  // ~ 100 pc/cm^3
  // NOTE: I think the correct conversion is
  // dm = DM * 18.11,
  // so dm_hi = 15000 corresponds to DM = 828 pc / cm^2
  dm_chunk_large  = 128;                           // DM resolution for folding
  dm_chunk_small =  8;
  fft_len   = 32768;                 // Needs to be at least twice DM_HI
  max_coadd = 10;
  for (int l = 0; l < max_coadd; ++l) {
    thresh[l] = thresh_def[l];

    // NOTE: none of these initial values except peak_power
    // should ever be used
    best[l].peak_power = -INFINITY;
    best[l].period     = -1.0;
    best[l].ffa_scale   = 0;
    best[l].num_std_devs = -100.0;
  }

  /* Starting place within computation */
  data_chunk_now = 0;
  dm_chunk_large_now   = dm_low;
  dm_chunk_small_now = 0;
  dm_now         = 0;
  dm_sign        = 1;
  sub_buffer    = 0;
  freq           = 0;
  min_freq		= 137;
  fold_buf_loc_long_pos = 0;
  fold_buf_loc_long_neg = 0;
  fold_buf_loc_short_pos   = 0;
  fold_buf_loc_short_neg   = 0;
  frac_done      = 0.0;
  result_count_single   = 0;
  result_count_rep   = 0;
}

/* Client::init
 * 
 * This function initializes the Client by performing a large number of
 * unrelated actions.  See below for descriptions of these actions.
 */
void Client::init() {
  /* Read the statefile, to see if we have been working on this computation before.
   * Initialize the wufile (in.dat)
   * Initialize the outfile (pulse.out)
   */
  state_t &state=ap_shmem->ap_gdata.state;
  
  if (debug_msg) {
    printf("In ap_client_main.cpp, in Client::init(), at statefile.read()\n");
    fprintf(stderr, "In ap_client_main.cpp, in Client::init(), at statefile.read()\n");
  }
  statefile.Read();
  if (debug_msg) {
    printf("In ap_client_main.cpp, in Client::init(), at wufile.init()\n");
    fprintf(stderr, "In ap_client_main.cpp, in Client::init(), at wufile.init()\n");
  }
  wufile.init();
  // We have to parse the header from in.dat
  // to client.wuheader each time, because
  // it is not part of the state, hence it does not get saved.
  wufile.parse_header_for_writeout();
  if (!statefile.resumed) {
    /* if no state file exists, then we have to start from scratch.
     * Parse the data header from client.wuheader into client.state, 
     * then set the starting dm chunk
     */
    wufile.parse_header_to_state();
    state.mtr.srandom(-1);
    state.mtr_new.srandom(-1);
    state.numSamplesBlanked = 0;
    state.numSamplesBlankedNew = 0;
    state.lastDataChunkBlanked = 0;
    state.lastDataChunkBlankedNew = 0;
    // state.idum = -1; // for tracking pseudo-random number generator
    // state.idum_new = -1;
    state.dm_chunk_large_now = state.dm_low;
  } else {  // state file exists, so there might be a result file
    // FILE *randsampfile;
    // randsampfile = fopen("randsamp.txt", "a");
    // fprintf(randsampfile, "In Client::init(), recovering state file.\n");
    // fclose(randsampfile);
    if (debug_msg) {
      printf("In ap_client_main.cpp, in Client::init(), reading signal_vector\n");
      fprintf(stderr, "In ap_client_main.cpp, in Client::init(), reading signal_vector\n");
    }
    read_signal_vector(signal_vector);
  }

  // if (debug_msg) printf("in ap_client_main.cpp, in Client::init(), at outfile.init()\n");
  // outfile.init();  Not initing this here anymore

  /* Figure out the fold level, which is the exponent in the power of 2 that
   * describes the number of consecutive samples to sum, for the purpose of
   * the folding algorithm.  (Which detects repeating pulses.)  The folding
   * algorithm works on groups of samples, not individual samples, because
   * there are too many samples to do otherwise.
   * The fold level is the same as the exponent in the power of 2 that
   * describes the dm_chunk size.  Why?
   */
  state.fold_level_large = calculate_fold_level(state.dm_chunk_large);
  state.fold_level_small = calculate_fold_level(state.dm_chunk_small);
 
  /* If the user specified a full_dm on the command line, deduce
   * the various dm components from it.
   */ 
  if(full_dm != -1) {    
    single_dm_chunk_large = full_dm - full_dm % state.dm_chunk_large;
    single_dm_chunk_small = full_dm % state.dm_chunk_large - full_dm % state.dm_chunk_small;
    single_dm = full_dm % state.dm_chunk_small;
  }


  /* Figure out the fold_buf_size_long, which is the number of complex samples in the
   * entire wufile, divided by the fold level's power of 2.  This is enough to
   * describe the entire workunit, because the folding algorithm combines samples 
   * into groups of 2^state.fold_level.
   * fold_buf_size_short is based on fold_buf_bytes_short, instead of on
   * state.datasize, because it would take too long to run the full time series
   * on a smaller dm chunk.
   */
  state.fold_buf_size_long = (state.datasize*4)>>state.fold_level_large;
  state.fold_buf_size_short = (state.fold_buf_bytes_short*4)>>state.fold_level_small;

  /* In Client::init.  Initialize variables, fft plans, etc. */

  if (debug_msg) {
    printf("In ap_client_main.cpp, in Client::init(), at science.init()\n");
    fprintf(stderr, "In ap_client_main.cpp, in Client::init(), at science.init()\n");
  }
  science.init();

  /* Power array needs to hold half of the fft_len time bins that will result from the
   * dechirping process.  I'm not sure why only half.  ?????
   */
  power.resize(state.fft_len/2);

  /* See above for the definition of state.fold_buf_size
   * and allow for folding which can access one element past actual data
   */
  fold_buf_long_pos.resize(state.fold_buf_size_long + 4);
  fold_buf_short_pos.resize(state.fold_buf_size_short + 4);

  /* Negative dms */
  fold_buf_long_neg.resize(state.fold_buf_size_long + 4);
  fold_buf_short_neg.resize(state.fold_buf_size_short + 4);

  /* In Client::init.  Read/write files */

  if (debug_msg) {
    printf("In ap_client_main.cpp, in Client::init(), at foldfile.read()\n");
    fprintf(stderr, "In ap_client_main.cpp, in Client::init(), at foldfile.read()\n");
  }
  if (foldfile.Read()) {
    /* Couldn't read fold file => reset to beginning of dm chunk */
    state.fold_buf_loc_long_pos = 0;
    state.fold_buf_loc_long_neg = 0;
    state.fold_buf_loc_short_pos = 0;
    state.fold_buf_loc_short_neg = 0;
    state.dm_now       = 0;
    state.dm_sign      = 1;
  }

  if (debug_msg) {
    printf("In ap_client_main.cpp, in Client::init(), at wufile.read_raw_data()\n");
    fprintf(stderr, "In ap_client_main.cpp, in Client::init(), at wufile.read_raw_data()\n");
  }
  wufile.read_raw_data();
  if (debug_msg) {
    printf("In ap_client_main.cpp, in Client::init(), at wufile.finish()\n");
    fprintf(stderr, "In ap_client_main.cpp, in Client::init(), at wufile.finish()\n");
  }
  wufile.finish();
  if (debug_msg) {
    printf("In ap_client_main.cpp, in Client::init(), at statefile.write()\n");
    fprintf(stderr, "In ap_client_main.cpp, in Client::init(), at statefile.write()\n");
  }
  statefile.write();
}

/* time_series_b_t::build
 *
 * Arguments:
 * array: The power array, containing a power level for each of fft_len/2 time samples
 *        If we are dealing with the FFA, we instead have a power level for each folded & coadded sample.
 * index: m, the index of a power bin which is above threshhold.
 * range: fft_len/2, or smaller (fft_len/(2 >> ell)) if there is a coadd ell > 0.
 * n_bins: the number of bins that we add up to get each element in the power array.
 *   Since the average noise level is 1 per bin, n_bins serves as a baseline or typical value
 *   for the total power.
 *
 * This function builds a time series of length at most MAXLENGTH, which is defined in astropulse.h.
 * A time series consists of a series of power values from the array, but generally 
 * not the whole array.  The power values are normalized so that array[index] has 
 * value 255, and all power values can be stored as unsigned chars.
 * 
 * struct time_series_b_t is defined in astropulse.h
 */
void time_series_b_t::build(float array[], int index, int range, float n_bins) {
  int temp_data_val;
  /* Set peak to equal the power that was above threshhold */
  float peak = array[index];
  /* Remove all elements from the data vector, a member of time_series_b_t */
  data.clear();
  if (range >= MAXLENGTH) {
    /* Create a time series of length MAXLENGTH, centered at index.
     * In some cases, this may be impossible, i.e. if the index is very
     * close to the beginning or end of the array.  In this case, we
     * do the best we can.
     */
    data.resize(MAXLENGTH);
    index -= MAXLENGTH/2;
    if (index < 0) {
      index = 0;
    } else if (index + MAXLENGTH > range) {
      index = range - MAXLENGTH;
    }
    for (int j = 0; j < MAXLENGTH; ++index, ++j) {
      /* Normalize so the peak has height 255, and n_bins has height 63. */
      /* The value of index increments each time, so array[index] == peak only when
       * index is set to its original value. */
      temp_data_val = (int) (63 + 192 * (array[index] - n_bins)/(peak - n_bins));
      if (temp_data_val < 0) temp_data_val = 0;
      if (temp_data_val > 255) temp_data_val = 255;
      data[j] = (unsigned char) temp_data_val;
    }
    length = MAXLENGTH;
  } else {
    /* Create a time series out of all the data */
    data.resize(range);
    for (index = 0; index < range; ++index) {
      /* normalize so that the peak has height 255. */
      temp_data_val = (int) (63 + 192 * (array[index] - n_bins)/(peak - n_bins));
      if (temp_data_val < 0) temp_data_val = 0;
      if (temp_data_val > 255) temp_data_val = 255;
      data[index] = (unsigned char) temp_data_val;
    }
    length = range;
  }
}

/* time_series_f_t::build
 * 
 * This function serves the same purpose as time_series_b_t::build, but 
 * it creates an array of floats instead of an array of chars.  Therefore, 
 * the power values do not need to be normalized.
 */
void time_series_f_t::build(float array[], int index, int range) {
  /* Remove all elements from the data vector.  No need to store a float called
   * <peak> in this function, because the <array> already consists of floats.
   */
  for(int i = 0; i < MAXLENGTH; i++) {data[i] = 0;}  // clear data
  if (range >= MAXLENGTH) {
    /* Create a time series out of part of the data */
    index -= MAXLENGTH/2;
    if (index < 0) {
      index = 0;
    } else if (index + MAXLENGTH > range) {
      index = range - MAXLENGTH;
    }
    for(int i = 0; i < MAXLENGTH; i++) { data[i] = array[index + i]; }
    length = MAXLENGTH;
  } else {
    /* Create a time series out of all the data */
    for(int i = 0; i < range; i++) { data[i] = array[index + i]; }
    length = range;
  }
}

/* I need to rewrite this completely. 
 * Currently it doesn't know about the small and large
 * dm chunks.
 */
void state_t::compute_fraction_done(double ell) {
    int dm_chunk_log = int_log2(dm_chunk_large);

    if(code_segment == main_ffa_long) {
        frac_done = log(freq / min_freq) / log((double)2);
        frac_done += sub_buffer;
        frac_done /= num_sub_buffers;
	frac_done *= frac_in_main_ffa[dm_chunk_log] / 2;
        if(dm_sign == 1) 
                frac_done += (frac_in_main_ffa[dm_chunk_log] + frac_in_btt[dm_chunk_log])/ 2;
        frac_done += (1 - frac_in_main_ffa[dm_chunk_log] - frac_in_btt[dm_chunk_log]/2);
        frac_done += (dm_chunk_large_now - dm_low) / dm_chunk_large;
        frac_done /= ((dm_hi - dm_low + dm_chunk_large - 1)/dm_chunk_large);
    } else if(code_segment == main_ffa_short ||
	      code_segment == build_threshold_table_short) {
	frac_done = log(freq / min_freq) / log((double)2);
        frac_done += sub_buffer;
        frac_done /= num_sub_buffers;
	frac_done *= frac_in_main_ffa_short / 2;
        if(dm_sign == 1) frac_done += frac_in_main_ffa_short / 2;
	frac_done += (1 - frac_in_main_ffa_short);
	frac_done += (dm_chunk_small_now / dm_chunk_small);
        frac_done /= (dm_chunk_large / dm_chunk_small); // % of a large dm chunk
        frac_done *= (1 - frac_in_btt[dm_chunk_log] - frac_in_main_ffa[dm_chunk_log]);  // we haven't run btt or ffa yet, so our current % is too large.
        frac_done += (dm_chunk_large_now-dm_low)/dm_chunk_large;
        frac_done /= ((dm_hi - dm_low + dm_chunk_large - 1)/dm_chunk_large);
    } else if(code_segment == build_threshold_table_long) {
	frac_done = (float)nfb / (float)fold_buf_size_long;
	frac_done *= frac_in_btt[dm_chunk_log] / 2;
        if(dm_sign == 1)
                frac_done += (frac_in_main_ffa[dm_chunk_log] + frac_in_btt[dm_chunk_log]) / 2;
	frac_done += (1 - frac_in_btt[dm_chunk_log] - frac_in_main_ffa[dm_chunk_log]); // we have already run the single pulse finder
	frac_done += (dm_chunk_large_now - dm_low) / dm_chunk_large;
	frac_done /= ((dm_hi - dm_low + dm_chunk_large -1)/dm_chunk_large);
    } else {   // In single pulse
        frac_done = ell / (double)max_coadd;  // % coadds complete
        frac_done += (1-dm_sign) / 2;
        frac_done /= 2;   // % of the 2 dm signs
        frac_done += dm_now;
        frac_done /= dm_chunk_small;  // % of a small dm chunk
        frac_done += data_chunk_now/((fft_len/2)/4);
        frac_done /= (datasize/((fft_len/2)/4))-1; // % of data
	frac_done *= (1 - frac_in_main_ffa_short);
	frac_done += (dm_chunk_small_now / dm_chunk_small);
        frac_done /= (dm_chunk_large / dm_chunk_small); // % of a large dm chunk
        frac_done *= (1 - frac_in_btt[dm_chunk_log] - frac_in_main_ffa[dm_chunk_log]);  // we haven't run btt or ffa yet, so our current % is too large.
        frac_done += (dm_chunk_large_now-dm_low)/dm_chunk_large;
        frac_done /= ((dm_hi - dm_low + dm_chunk_large - 1)/dm_chunk_large);
    }
}


void Science::mainloop() {
  // Uncomment the following line (atexit) if the program is exiting
  // without saying why.  It will segfault when the program exits.
  // atexit(segfault_atexit);

  // FILE* hit_file;
  // hit_file = fopen("hit_file.txt", "w");
  // FILE* power_file = fopen("power_file.txt", "w");  // temp for debugging
  float total; // temp for debugging
  int last_hit; // temp for debugging
  int count = 0;  // temp for debugging
  state_t& state = client.ap_shmem->ap_gdata.state;
  time_series_f_t temp_time_series; // Holds time series before it gets
				    // stored in shmem
  if(remove_radar) state.remove_radar = 1;  // override, turn remove_radar on

  /* It's not clear why the line below is necessary.  It's basically setting a pointer
   * called power, to equal the address of the first element in a std::vector<float>
   * called client.power.  Why can't we just use the vector instead of the pointer?
   */
  float*   power = &(client.power[0]);
  if (debug_msg) {
    printf("state.dm_low: %d\n", state.dm_low);
    fprintf(stderr, "state.dm_low: %d\n", state.dm_low);
  }

  /* Stuff for making pulse graphs */
  long int count_pulses_per_fold_power[MAX_COADD][POWER_MAX] = {{0}};
  int count_pulses_per_dm[DM_MAX] = {0};
  FILE *pulses_per_fold_power_file;
  FILE *pulses_per_dm_file;

  FILE *best_file;
  if (print_best) {
    best_file = boinc_fopen("best_file.txt", "w");
  }

  timer checkpoint_timer;
  timer ffa_timer;

  state.code_segment = single_pulse;
  // state.print();

  int should_print_DC_strengths = 0;

  long maxSamplesBlanked = 0;

  // This line tests a randomized workunit, to see how strong the DC component gets in any N-sample FFT
  if(should_print_DC_strengths) print_DC_strengths();

  // These lines randomize parts of a workunit
  std::vector<long> indices;
  int num_indices;
  if(state.remove_radar) {
    num_indices = get_indices_to_randomize(indices, 0);
  // Now that we know which segments to randomize, we generate
  // an average noise envelope out of the RFI-free segments.
  // The results are stored in members of the Science class.
    generate_envelope(indices, num_indices);
    generate_pre_envelope();
  }

  /* mainloop level 1:
   * This loop iterates over dm chunks.  Each dm_chunk consists of, say, 32 dms.
   * The number 32 (or whatever it is) comes from state.dm_chunk, which
   * is defined in in.dat
   */
  for (;state.dm_chunk_large_now < state.dm_hi;
       state.dm_chunk_large_now += state.dm_chunk_large) {
    
    client.checkpoint();
    debug_loop(0, 0, debug_loop_msg);

    printf("In ap_client_main.cpp: in mainloop(): at dm_chunk_large %d\n", state.dm_chunk_large_now);
    fprintf(stderr, "In ap_client_main.cpp: in mainloop(): at dm_chunk_large %d\n", state.dm_chunk_large_now);

    if (single_dm_chunk_large != -1) {
      if (state.dm_chunk_large_now != single_dm_chunk_large) continue;
    }
    debug_loop(0, 1, debug_loop_msg);

    debug_loop(0, 2, debug_loop_msg);

    /* Print best pulses so far. [Was this intended to be temporary?  What purposes does it serve?] */
    if (print_best) {
      for (int scale = 0; scale < state.max_coadd; scale++)
        fprintf(best_file, "scale %d, peak_power %f\n", scale, state.best[scale].peak_power);
      fprintf(best_file, "\n");
      fflush(best_file);
    }
    debug_loop(0, 3, debug_loop_msg);

    /* Create an array of chirps, containing state.dm_chunk different chirps,
    * with dms ranging from state.dm_chunk_large_now to 
    * state.dm_chunk_large_now + state.dm_chunk - 1
    */
    build_chirp_table(state.dm_chunk_large_now);
    debug_loop(0, 4, debug_loop_msg);

    /* mainloop level 1.a 
     * This loop iterates over smaller dm_chunks.
     */ 
    for (;state.dm_chunk_small_now < state.dm_chunk_large;
          state.dm_chunk_small_now += state.dm_chunk_small) {
      debug_loop(1, 0, debug_loop_msg);

      if (single_dm_chunk_small != -1) {
        if(state.dm_chunk_small_now != single_dm_chunk_small) continue;
      }
      debug_loop(1, 1, debug_loop_msg);
 
      /* Start over at fractionBlanked = 0. */ 
      state.numSamplesBlanked = 0;
      state.numSamplesBlankedNew = 0;
      state.lastDataChunkBlanked = 0;
      state.lastDataChunkBlankedNew = 0;
 
    /* THIS SHOULD MOVE BACK UP ONE LEVEL! */ 
    if(pulsegraphs) {
      pulses_per_fold_power_file = boinc_fopen("pulses_per_fold_power_file.txt", "w");
      pulses_per_dm_file = boinc_fopen("pulses_per_dm_file.txt", "w"); 

      /* file pointer to beginning of the file.  (Not necessary anymore, we open the file just above.)
      fseek(pulses_per_fold_power_file, 0, SEEK_SET);
      fseek(pulses_per_dm_file, 0, SEEK_SET); */
      for(int fold_level = 0; fold_level < MAX_COADD; fold_level++)
        for(int power_level = 0; power_level < POWER_MAX; power_level++) 
          fprintf(pulses_per_fold_power_file, "%d %d %ld\n", fold_level, power_level, count_pulses_per_fold_power[fold_level][power_level]);
      
      for(int dm = 0; dm < DM_MAX; dm++)
        fprintf(pulses_per_dm_file, "%d %d\n", dm, count_pulses_per_dm[dm]);

      fflush(pulses_per_fold_power_file);  fclose(pulses_per_fold_power_file);
      fflush(pulses_per_dm_file);	   fclose(pulses_per_dm_file);
    }

      /* mainloop level 2
              * This loop iterates over all data chunks.  Each data chunk consists of fft_len
              * complex samples, or fft_len / 4 bytes.  The number state.data_chunk_now follows the
       * pattern: 0, 4096, 4096 * 2, 4096 * 3, ...
       *
       * However, there is an overlap of fft_len / 2 complex samples, or fft_len / 8
       * bytes.  That is, each time we iterate to the next data chunk, we only move
       * ahead by fft_len / 8 bytes of data.
       *
       * This means that the loop runs state.datasize / (state.fft_len / 8)
       * times per data chunk.
       *
       * I suspect that the criterion to continue ought to be 
       * state._nowdata_chunk_now <= (state.datasize - state.fft_len/4),
       * but that the two criteria are equivalent in this case.
       */
      for (; state.data_chunk_now<(state.datasize-state.fft_len/2/4); state.data_chunk_now+=(state.fft_len/2)/4) {
        
        client.checkpoint();

        debug_loop(2, 0, debug_loop_msg);

        /*  Should we just skip the data?
        bool good_data_chunk = true;

        if(state.remove_radar) {
          for(int n=0; n<num_indices; n++) {
            if( indices[n] < data_chunk_now + 100000 && indices[n] > data_chunk_now - 100000) {
              good_data_chunk = false;
              break;
            }
          }
        }

        if(good_data_chunk == false) continue;
	*/

        if (data_byte_limit != -1) { 
          if (state.data_chunk_now >= data_byte_limit) {
            // state.mtr = state.mtr_new;  // I don't think this is actually necessary here.
            continue; 
	  }
          // Only consider the first few data chunks, up to the one
          // that starts with data_byte_limit bytes.
        }
        debug_loop(2, 1, debug_loop_msg);

        /* Print debugging information about the current dm_chunk and data_chunk
         * We do this on data chunks 4096 * {0, 1, 2, 4, 8, ...} for the first dm of each dm_chunk
         */
        if (debug_msg) {
          int i = state.data_chunk_now / ((state.fft_len/2)/4);
          if (i==0) i=1; // Handle the edge case that i==0
          while (i % 2 == 0 && i > 1) {
            i /= 2;
          }
          if (i == 1) {
            printf("In ap_client_main.cpp, Science::mainloop\n");
            fprintf(stderr, "In ap_client_main.cpp, Science::mainloop\n");
            printf("state.dm_chunk_large_now: %d, state.dm_chunk_small_now: %d, state.data_chunk_now: %d\n", state.dm_chunk_large_now,
		   state.dm_chunk_small_now, state.data_chunk_now);
            fprintf(stderr, "state.dm_chunk_large_now: %d, state.dm_chunk_small_now: %d, state.data_chunk_now: %d\n", 
                   state.dm_chunk_large_now, state.dm_chunk_small_now, state.data_chunk_now);
            main_timer.print_total_time();
            fflush(stdout);
          }
        }
        debug_loop(2, 2, debug_loop_msg);

        debug_loop(2, 3, debug_loop_msg);

        /* convert_bits_to_float calls splitter_bits_to_float,
        * which is defined in sbtf.cpp.  This function converts each bit into
         * a float, resulting in a large array of floats.  The number of complex
        * samples converted is equal to the length of one FFT. 
        * If one of the "random indices" is within 100,000 bytes of our data_chunk_now, we randomize all of the data.
        */
        convert_bits_to_float();

        if(state.remove_radar) { 
	  state.mtr_new = state.mtr; // mtr_new will contain the new mtr _after_ this function runs.
	  state.numSamplesBlankedNew = state.numSamplesBlanked;
	  state.lastDataChunkBlankedNew = state.lastDataChunkBlanked;
	  randomize_indices(indices, num_indices, state.data_chunk_now, state.fft_len, state); 
	  maxSamplesBlanked = (maxSamplesBlanked > state.numSamplesBlanked)?maxSamplesBlanked:state.numSamplesBlanked;
	}

        debug_loop(2, 4, debug_loop_msg);

        debug_loop(2, 5, debug_loop_msg);

        /* FFT the time-domain data, stored in the float array client.science.data,
        * into frequency-domain data 
        */
        compute_forward_fft();
        debug_loop(2, 6, debug_loop_msg);

        /* Filter the frequency-domain data to get rid of low frequency "noise".
         * This line should be removed if at a later time we figure out how to get rid of the noise.
         */
        high_pass_filter();
        debug_loop(2, 7, debug_loop_msg);
  
        /* Graphics stuff */
  #ifdef BOINC_APP_GRAPHICS
        double max = 0.0;
	if(!nographics()) {
          rarray.init_data(state.fft_len/2, state.dm_chunk_small); // const
        }
  #endif
  
        /* mainloop level 3
          * This loop iterates over dms within a dm chunk.
         */
        for (; state.dm_now<state.dm_chunk_small; state.dm_now++) {
	  client.checkpoint();
          debug_loop(3, 0, debug_loop_msg);

          if (single_dm != -1) {
            if (state.dm_now != single_dm) continue;
          }
          debug_loop(3, 1, debug_loop_msg);

          /* Checks if app client is in standalone mode, for testing
           * purposes.  (As opposed to being run by the boinc
           * client.)  If so, force checkpoint if over 60 seconds.
           */
          if (boinc_is_standalone()) {
            if(checkpoint_timer.get_total_seconds() > 60) {
              checkpoint_timer.reset();
              client.do_checkpoint();
            }
          }
          debug_loop(3, 2, debug_loop_msg);

          /* mainloop level 4
           * This loop iterates over the two dm signs, positive and negative
           */
          for (; state.dm_sign>=-1; state.dm_sign-=2) {
            client.checkpoint();
            debug_loop(4, 0, debug_loop_msg);

            debug_loop(4, 1, debug_loop_msg);
  
            /* Dechirp the frequency domain data via multiplication by a dechirping
             * function.  Then perform the inverse fourier transform
             */
            dechirp(state.dm_chunk_small_now + state.dm_now, power, state.dm_sign);
            debug_loop(4, 2, debug_loop_msg);
  
            /* Graphics stuff */
  #ifdef BOINC_APP_GRAPHICS
            if (!nographics() && state.dm_sign > 0) { // add positive dispersions to graph
              rarray.add_source_row(power);
	      memcpy(&client.ap_shmem->rarray_data, &rarray, sizeof(REDUCED_ARRAY_DATA));
            }
  #endif
  
            /* measuring the time that has passed since the program started running */
            updatecputime();
            debug_loop(4, 3, debug_loop_msg);
  
            /* mainloop level 5 * This loop iterates over coadd levels.
             */
	    for (int l=0; l<state.max_coadd; l++) {
              debug_loop(5, 0, debug_loop_msg);
  
              /* Uses ell, along with some members of client.state
               * Stores the result in state.frac_done.
               * The sole purpose of this computation is to print the fraction
               * done on the screen.
               */
              state.compute_fraction_done(l);
              debug_loop(5, 1, debug_loop_msg);

              /* 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((float)state.fft_len)/log(32768.0),0);
              debug_loop(5, 2, debug_loop_msg);
  
              /* This is the size of the power array.
               * It starts at state.fft_len / 2, for ell = 0.  As ell gets larger, 
               * we perform coadds in place, and this reduces the effective 
               * size of the power array.
               * Note that from the very beginning, we ignore the second
               * half of the power bins.  Due to the overlap in ffts,
               * this second half is not necessary.
               */
              int dechirped_range_data_length = state.fft_len >> (l+1);
              debug_loop(5, 3, debug_loop_msg);

              /* mainloop level 6
              * This loop iterates over all of the power bins, searching for
              * a bin that exceeds the threshhold power.
              */
              last_hit = -10;
	      double cur_cpu_time;
	      boinc_wu_cpu_time(cur_cpu_time);
	      client.ap_shmem->ap_gdata.cpu_time = cur_cpu_time;
              for (int m=0; m<dechirped_range_data_length; m++) {
  
                // fprintf(power_file, "m = %d, power[m] = %f, state.data_chunk_now = %d\n", m, power[m], state.data_chunk_now);  // temp for debugging
                if (state.dm_sign == 1 && l == 0 && m < dechirped_range_data_length - 10) {
                  /* This was for debugging.  Not sure for what exactly.
                  if(m > last_hit + 10 && detect_hit(&power[m], total)) {
                    count++;
                    fprintf(hit_file, "m = %d, sample = %d, total = %f, num = %d\n", m, m + state.data_chunk_now * 4, total, count);
                    last_hit = m;
                  } */
                }
                /* Graphics stuff */
  #if BOINC_APP_GRAPHICS
                if (power[m] > max && !nographics()) {
                  temp_time_series.build(power, m, dechirped_range_data_length);
		  memcpy(&client.ap_shmem->time_series_shmem, &temp_time_series, sizeof(time_series_f_t));
                  max = power[m];
                }
  #endif

		if(pulsegraphs) {
		/* For plotting pulse graph of # hits vs. power */
		  int cur_power = (int)power[m];
		  if(cur_power >= POWER_MAX || l >= MAX_COADD) {
		    fprintf(stderr, "Error: power %d, coadd %d is overflow\n", cur_power, l);
		    exit(0);
                  }
		  count_pulses_per_fold_power[l][cur_power]++;

		/* For plotting a graph of # hits vs. dm */
		  if (l == 0 && state.dm_sign == 1 && power[m] > 10.0) {
		    int cur_dm = state.dm_chunk_large_now + state.dm_chunk_small_now + state.dm_now;
		    if(cur_dm >= DM_MAX) {
		      fprintf(stderr, "Error: dm %d is overflow\n", cur_dm);
		      exit(0);
		    }
                    count_pulses_per_dm[cur_dm]++;
		  }
		}
                if (power[m] > state.thresh[l]) {
                
		/* for debugging: check that the client randomizes these bits
		   if(state.data_chunk_now == 1359872 - 4096 && l == 3) {
		      for(int i = 16470; i < 16500; i++)
		        printf("data[%d][0] = %f, data[%d][1] = %f\n",
		           i, data[i][0], i, data[i][1]);
		   }
                 */
 
                /* If power exceeds threshhold, record a pulse.
                 * This involves first creating an ap_signal, which is defined in
                 * astropulse/server/db/ap_schema.h.  Then, we write the signal
                 * to pulse.out using Outfile::output_result.
                 */
		    
                  if(pulse_limit_single && state.result_count_single >= pulse_limit_single)  // We've already found enough single pulses.  (Must be that we haven't found enough repeating pulses.)  Don't record this pulse.
                      continue;
                  ap_signal s;
                  time_series_b_t time_series;
                  time_series.build(power, m, dechirped_range_data_length, pow_int(2, l));
                  state.pulse_jd = state.jd0 + ((float)state.data_chunk_now)/((float)state.datasize)*(state.jd1-state.jd0);
  
                  s.dm= state.dm_sign*(state.dm_chunk_large_now+state.dm_chunk_small_now + state.dm_now); //dm
                  s.scale=l;                   // scale
                  s.peak_power=power[m];       // peak_power
                  s.fft_num=state.data_chunk_now*4;
                  s.peak_bin=state.data_chunk_now * 4 + (m << l); // index
                  s.time=state.pulse_jd;       // time
                  s.period=0.0;                // period
		  s.ffa_scale=0;		// ffa bin size
		  s.num_std_devs=-100.0;		// single pulse has no num_std_devs
                  // Interpolate betw. ra0 & ra1 using state.pulse_jd
                  s.ra = interpolate_ra(state.jd0, state.jd1, state.pulse_jd, state.ra0, state.ra1);
                  // Interpolate betw. dec0 and dec1
                  s.decl = interpolate_dec(state.jd0, state.jd1, state.pulse_jd, state.dec0, state.dec1);
                  s.time_series_len=time_series.length;
                  s.time_series=time_series.data; // WARNING why can we use an equal sign here?
                  s.time_series.encoding=_x_hex;
  		  if(add_signal(client.signal_vector, s)) state.result_count_single++;
                  // 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
                    fprintf(stderr, "Found %d single pulses and %d repeating pulses, exiting.\n", state.result_count_single, state.result_count_rep);
		    printf("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);
                  }
                }
  
                /* We record the best peak at each coadd level.  That is, there is
                 * one "best" value for each coadd level, for the entire run of
                 * the client.
                 */
                if (power[m] > state.best[l].peak_power) {
                  time_series_b_t time_series;
                  time_series.build(power, m, dechirped_range_data_length, pow_int(2, l));
                  state.pulse_jd = state.jd0 + (state.data_chunk_now)/(state.datasize)*(state.jd1-state.jd0);
                  state.best[l].fft_num=state.data_chunk_now*4;
                  state.best[l].peak_bin = state.data_chunk_now * 4 + (m << l);
                  state.best[l].peak_power = power[m];
                  state.best[l].scale = l;
                  state.best[l].dm = state.dm_sign*(state.dm_chunk_large_now+state.dm_chunk_small_now+state.dm_now);
                  state.best[l].period = 0.0;
		  state.best[l].ffa_scale = 0;
		  state.best[l].num_std_devs = -100.0;
		  state.best[l].ra = interpolate_ra(state.jd0, state.jd1, state.pulse_jd, state.ra0, state.ra1);
                  state.best[l].decl = interpolate_dec(state.jd0, state.jd1, state.pulse_jd, state.dec0, state.dec1);
                  state.best[l].time = state.pulse_jd;
		  state.best[l].time_series_len = time_series.length;
		  assert(time_series.length <= MAXLENGTH_OF_TS);  // Don't want an overflow
		  // This memcpy works because state_signal.time_series is an array, and so is
		  // time_series.data.  (ap_signal.time_series would be an sqlblob.)
		  memcpy(state.best[l].time_series, &(time_series.data[0]), time_series.length);
                }
              } /* End mainloop level 6, bin number (m) */
              debug_loop(5, 10, debug_loop_msg);
  
	      /* When l is at two specific values (called state.fold_level_large and
               * state.fold_level small), we are ready to contribute data to the fast
               * folding algorithm.  
               * 
               * Large scale version: This happens only once per (dm_chunk_large, data_chunk) pair.
               * So we only hit one out of every 2^(state.fold_level_large)
               * dms in this way (henceforth 2^(s.fll)).  This is why we do it at a fold_level
               * such that 2^(s.fll) is equal to state.dm_chunk_large: it's not meaningful to look
               * at data at a better time resolution than that provided by the dm resolution.
               *
               * We also look at a finer resolution (below), namely 2^(state.fold_level_small).  
               * In this case, we contribute data more often, and there is more of it.  To compensate for this,
               * we must cut off the power array at some number of bytes, state.fold_buf_bytes_short.
               */
              // Temporary fix
              if ((l==state.fold_level_large) && (state.dm_now == fold_dm) && (state.dm_chunk_small_now == fold_dm_chunk_small)) {
                /* Save for folding */

 		// positive dms
		if(state.dm_sign == 1) {
                  memcpy(&client.fold_buf_long_pos[state.fold_buf_loc_long_pos], power,
                       sizeof(float)*(state.fft_len>>(l+1)));
                  state.fold_buf_loc_long_pos += (state.fft_len>>(l+1));
		  // On the last data chunk, do one extra memcpy.
		  // This is because the number of iterations in the data chunk loop is one less than enough
		  // to make the long fold buffer fill up, due to the overlap between data chunks.
		  // Note that datasize is measured in bytes, and increments by fft_len/8.
		  if(state.data_chunk_now == (state.datasize - state.fft_len/4)) {
                    memcpy(&client.fold_buf_long_pos[state.fold_buf_loc_long_pos], power,
                         sizeof(float)*(state.fft_len>>(l+1)));
                    state.fold_buf_loc_long_pos += (state.fft_len>>(l+1));
		  }

		// identical code, but for negative dms
		} else if(state.dm_sign == -1) { 
		  memcpy(&client.fold_buf_long_neg[state.fold_buf_loc_long_neg], power,
		  	sizeof(float)*(state.fft_len>>(l+1)));
                  state.fold_buf_loc_long_neg += (state.fft_len>>(l+1));
                  if(state.data_chunk_now == (state.datasize - state.fft_len/4)) {
		    memcpy(&client.fold_buf_long_neg[state.fold_buf_loc_long_neg], power,
		      	sizeof(float)*(state.fft_len>>(l+1)));
		    state.fold_buf_loc_long_neg += (state.fft_len>>(l+1));
		  }
                } else { 
		  fprintf(stderr, "Error in ap_client_main.cpp: bad dm_sign value");
		  exit(-1);
		}
              }
	      debug_loop(5, 11, debug_loop_msg);

	      /* More frequently, we copy the power array into the short fold buffer.
	       * Note that the condition  state.data_chunk_now < state.fold_buf_bytes_short,
	       * even though each data chunk extends beyond the start of the next one.
 	       */
   	      if ((l==state.fold_level_small) && (state.dm_now == fold_dm) 
					&& (state.data_chunk_now < state.fold_buf_bytes_short)) {
  	            /* Save for folding in short time series, for finding short period repeating pulses. */
		if(state.dm_sign == 1) {
                  memcpy(&client.fold_buf_short_pos[state.fold_buf_loc_short_pos], power,
                       sizeof(float)*(state.fft_len>>(l+1)));
                  state.fold_buf_loc_short_pos += (state.fft_len>>(l+1));
		} else if(state.dm_sign == -1) {
		  memcpy(&client.fold_buf_short_neg[state.fold_buf_loc_short_neg], power,
			sizeof(float)*(state.fft_len>>(l+1)));
		  state.fold_buf_loc_short_neg += (state.fft_len>>(l+1));
		}
              }
	      debug_loop(5, 12, debug_loop_msg);
  
              /* coadd_in_place is defined in ap_science.cpp
               * Its purpose is to halve the size of the power array by adding
               * array elements in consecutive pairs.
               * The second argument is the initial array size.  Note that
               * 
               */
              coadd_in_place(power, state.fft_len>>(l+1));
	      debug_loop(5, 13, debug_loop_msg);

            } /* End mainloop level 5, coadd level (l) */
	    debug_loop(4, 10, debug_loop_msg);

            // client.checkpoint(); Can only checkpoint at beginning of for loop
	    debug_loop(4, 11, debug_loop_msg);

          } /* End mainloop level 4, dm sign (s) */
	  debug_loop(3, 10, debug_loop_msg);

          state.dm_sign = 1;
	  debug_loop(3, 11, debug_loop_msg);

        } /* End mainloop level 3, inner dm (k) */
	debug_loop(2, 10, debug_loop_msg);

        state.dm_now = 0;
	debug_loop(2, 11, debug_loop_msg);
      
        // Now that we've finished that data_chunk, 
        // we change over the mtr.  
        // * If we crash after a later checkpoint takes effect,
        //   state.dm_now will equal 0 and our
        //   next run of randomize_indices will (correctly) happen
        //   with the newmtr 
        // * If we crash after this, but before any checkpoint takes effect,
        //   then the state.dm_now = 0 won't have taken effect,
        //   so we'll (correctly) start again with the old mtr.
        if(state.remove_radar) {
          state.mtr = state.mtr_new;
	  state.numSamplesBlanked = state.numSamplesBlankedNew;
	  state.lastDataChunkBlanked = state.lastDataChunkBlankedNew;
	}
      } /* End mainloop level 2, data (j) */

      debug_loop(1, 10, debug_loop_msg);

      if (state.fold_buf_loc_short_pos != state.fold_buf_size_short) {
	  fprintf(stderr, "Short positive fold buffer didn't fill up (lol=%d, size=%d).\n",
		  state.fold_buf_loc_short_pos, state.fold_buf_size_short);
      }
      if (state.fold_buf_loc_short_neg != state.fold_buf_size_short) {
	  fprintf(stderr, "Short negative fold buffer didn't fill up (lol=%d, size=%d).\n",
		  state.fold_buf_loc_short_neg, state.fold_buf_size_short);
      }

      ffa_timer.start();
      debug_loop(1, 11, debug_loop_msg);

      /* Arguments are: (pointer to beginning of array, ?, are we on large dm
       * chunk, ?, ?, max # of pulses before we exit the ffa
       */
      if(!skipffa_short) {
    state.dm_sign = -1;
	state.code_segment = build_threshold_table_short;
        ffa(&client.fold_buf_short_neg[0], 1, false, write_lcgf_short, debug_msg, pulse_limit_single, pulse_limit_rep, ffa_thresh_mult);
    state.dm_sign = 1;
	state.code_segment = build_threshold_table_short;
        ffa(&client.fold_buf_short_pos[0], 1, false, write_lcgf_short, debug_msg, pulse_limit_single, pulse_limit_rep, ffa_thresh_mult);
	state.code_segment = single_pulse;
      }
      debug_loop(1, 12, debug_loop_msg);

      ffa_timer.stop();
      debug_loop(1, 13, debug_loop_msg);
      
      state.data_chunk_now = 0; // I moved this line down in case we crash during ffa
      
      debug_loop(1, 14, debug_loop_msg);

      state.fold_buf_loc_short_pos = 0;
      state.fold_buf_loc_short_neg = 0;
      debug_loop(1, 15, debug_loop_msg);


    } /* End mainloop level 1.a, dm_chunk_small */
    debug_loop(0, 10, debug_loop_msg);

    /* When we've finished the data loop, we will have hopefully
     * filled up the fold buffer, by successively copying the power array
     * into it.  We are then ready to reset the fold buffer to the beginning
     * of the buffer, and reset the data chunk to the first data chunk.
     */
    if (state.fold_buf_loc_long_pos != state.fold_buf_size_long) {
      fprintf(stderr, "Long pos fold buffer didn't fill up (lol=%d, size=%d).\n",
           state.fold_buf_loc_long_pos, state.fold_buf_size_long);
    }
    if (state.fold_buf_loc_long_neg != state.fold_buf_size_long) {
      fprintf(stderr, "Long neg fold buffer didn't fill up (lol=%d, size=%d).\n",
           state.fold_buf_loc_long_neg, state.fold_buf_size_long);
    }
    debug_loop(0, 11, debug_loop_msg);
   
    /* do fold - results output within ffa */
    // fclose(hit_file);  // temp for debugging
    ffa_timer.start();
    debug_loop(0, 12, debug_loop_msg);

    /* Arguments are: (pointer to beginning of array, ?, are we on large dm
     * chunk, ?, ?, max # of pulses before we exit the ffa
     */
    if(!skipffa_long) {
      state.code_segment = build_threshold_table_long;
      state.dm_sign = -1;
      ffa(&client.fold_buf_long_neg[0], 1, true, write_lcgf_long, debug_msg, pulse_limit_single, pulse_limit_rep, ffa_thresh_mult);
      state.dm_sign = 1;
      // state.code_segment = single_pulse;
      state.code_segment = build_threshold_table_long;
      ffa(&client.fold_buf_long_pos[0], 1, true, write_lcgf_long, debug_msg, pulse_limit_single, pulse_limit_rep, ffa_thresh_mult);
      state.code_segment = single_pulse;
    }
    debug_loop(0, 13, debug_loop_msg);

    ffa_timer.stop();
    debug_loop(0, 14, debug_loop_msg);

    state.dm_chunk_small_now = 0;  // I moved this line down in case we crash during ffa
    debug_loop(0, 15, debug_loop_msg);
    
    state.fold_buf_loc_long_pos = 0; 
    state.fold_buf_loc_long_neg = 0; 
    debug_loop(0, 16, debug_loop_msg);


  } /* End mainloop level 1, DM chunk (i) */

  /* output the top result no matter how many we have >= threshold, so
   * that if none >= threshold we can still validate redundant results
   * against each other.
   */
  /* Was used for debugging before the ap_timer class was invented
  FILE* progress_file = fopen("progress_file.txt", "a");
  fprintf(progress_file, "Total time: %d\n", time(0) - starttime);
  fprintf(progress_file, "Total cpu time: %f\n", cputime);
  fprintf(progress_file, "Max cpu ticks interval: %d\n", maxcputicksinterval);
  fclose(progress_file);
  */

  /* Write the best pulses, and various other xml tags. */
  client.outfile.write_main(client.signal_vector);
  client.outfile.finish_results();
  if (debug_msg) {
    printf("FFA timer reads: %ld seconds, %ld ticks\n", ffa_timer.get_seconds(), ffa_timer.get_ticks());
    fprintf(stderr, "FFA timer reads: %ld seconds, %ld ticks\n", ffa_timer.get_seconds(), ffa_timer.get_ticks());
  }
} /* End mainloop */

/* Client::finish
 *
 * Calls all finish functions.
 */
void Client::finish() {
  // Clean up stuff and exit
  // unloadGl();
  science.finish();
  // outfile.finish();
  statefile.finish();
  foldfile.finish();
}

/* debug_loop
 * int level: the level of the loop hierarchy that we are currently in.
 * bool debug_loop_msg: true if we should print out these messages
 *
 * debug_loop
 */
void debug_loop(int level, int number, bool debug_loop_msg) {
  if(debug_loop_msg) {

    int max = 2;
    const int num_levels = 10;
    const int after_inner_loop = 10;
    static int loopnum[num_levels] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    bool print_msg = true;

    // If we've just finished an inner loop,
    // reset all levels deeper than this one
    if(number == after_inner_loop) {
      for(int i = level + 1; i < 10; i++) {
        loopnum[i] = 0;
      }
    }
    
    // If we're at the beginning of the loop,
    // increment current level's loop count
    if(number == 0) {
      loopnum[level]++;
    }

    // If all loop counts are less than or equal to max, then print the message
    print_msg = true;
    for(int i = 0; i < num_levels; i++) {
      if(loopnum[i] > max) {print_msg = false;}
    }
    
    if(print_msg) {
      for(int i = 0; i < level; i++)
        fprintf(stderr, "* "); 
      fprintf(stderr, "In ap_client_main.cpp, Science::mainloop, loop level %d, number %d\n", level, number);
    }
  }
}
/*
void state_t::print() {
  fprintf(stderr, "\n*****************state file ********************\n");
  fprintf(stderr, "char recvname[64]: %s\n", recvname);
  fprintf(stderr, "char wuname[64]: %s\n", wuname);
  fprintf(stderr, "int datasize: %d\n", datasize);
  fprintf(stderr, "int dm_low: %d\n", dm_low);
  fprintf(stderr, "int dm_hi: %d\n", dm_hi);
  fprintf(stderr, "int dm_chunk_large: %d\n", dm_chunk_large);
  fprintf(stderr, "int dm_chunk_small: %d\n", dm_chunk_small);
  fprintf(stderr, "int fft_len: %d\n", fft_len);
  fprintf(stderr, "float high_pass: %f\n", high_pass);
  fprintf(stderr, "float ffa_threshold: %f\n", ffa_threshold);
  fprintf(stderr, "int max_coadd: %d\n", max_coadd);
  fprintf(stderr, "int fold_buf_size_short: %d\n", fold_buf_size_short);
  fprintf(stderr, "int fold_buf_size_long: %d\n", fold_buf_size_long);
  fprintf(stderr, "int fold_buf_bytes_short: %d\n", fold_buf_bytes_short);
  fprintf(stderr, "int fold_level_large: %d\n", fold_level_large);
  fprintf(stderr, "int fold_level_small: %d\n", fold_level_small);
  for(int i = 0; i < MAX_COADD; i++) 
    fprintf(stderr, "double thresh[%d] = %f\n", i, thresh[i]);
  fprintf(stderr, "double ra0: %f\n", ra0); 
  fprintf(stderr, "double ra1: %f\n", ra1); 
  fprintf(stderr, "double dec0: %f\n", dec0); 
  fprintf(stderr, "double dec1: %f\n", dec1); 
  fprintf(stderr, "double jd0: %f\n", jd0); 
  fprintf(stderr, "double jd1: %f\n", jd1); 
  fprintf(stderr, "int data_chunk_now: %d\n", data_chunk_now); 
  fprintf(stderr, "int dm_chunk_large_now: %d\n", dm_chunk_large_now); 
  fprintf(stderr, "int dm_chunk_small_now: %d\n", dm_chunk_small_now); 
  fprintf(stderr, "int dm_now: %d\n", dm_now); 
  fprintf(stderr, "int dm_sign: %d\n", dm_sign); 
  fprintf(stderr, "int num_sub_buffers: %d\n", num_sub_buffers); 
  fprintf(stderr, "int sub_buffer: %d\n", sub_buffer); 
  fprintf(stderr, "float freq: %f\n", freq);
  fprintf(stderr, "float min_freq: %f\n", min_freq);
  fprintf(stderr, "int nfb: %d\n", nfb); 
  fprintf(stderr, "int fold_buf_loc_long_pos: %d\n", fold_buf_loc_long_pos); 
  fprintf(stderr, "int fold_buf_loc_long_neg: %d\n", fold_buf_loc_long_neg); 
  fprintf(stderr, "int fold_buf_loc_short_pos: %d\n", fold_buf_loc_short_pos); 
  fprintf(stderr, "int fold_buf_loc_short_neg: %d\n", fold_buf_loc_short_neg); 
  fprintf(stderr, "double frac_done: %f\n", frac_done); 
  fprintf(stderr, "double pulse_jd: %f\n", pulse_jd); 
  fprintf(stderr, "int result_count_single: %d\n", result_count_single); 
  fprintf(stderr, "int result_count_rep: %d\n", result_count_rep); 
  fprintf(stderr, "enum code_segment: %d\n", (int) code_segment);
  for(int j = 0; j < MAX_COADD; j++) {
    
  } 
  fprintf(stderr, "\n********************************************\n");
}
*/

} // namespace Astropulse

double interpolate_ra(double jd0, double jd1, double pulse_jd, double ra0, double ra1) {
  assert(jd1 > jd0);
  double time_fraction = (pulse_jd - jd0) / (jd1 - jd0);
  double retval;
  if (fabs(ra0 - ra1) < 12) {
    return ra0 + (ra1 - ra0) * time_fraction;
  } else {  // We've crossed RA 0 -- RA 24.
    if (ra0 > ra1) // crossed from high RA to low RA
      retval = (ra0 - 24) +
               (ra1 - (ra0 - 24)) * time_fraction;
    else // crossed from low RA to high RA
      retval = ra0 +
               ((ra1 - 24) - ra0) * time_fraction;
    if (retval > 0) return retval;
    else return retval + 24;
  }
}

double interpolate_dec(double jd0, double jd1, double pulse_jd, double dec0, double dec1) {
  assert(jd1 > jd0);
  double time_fraction = (pulse_jd - jd0) / (jd1 - jd0);
  return (dec0 + (dec1 - dec0) * time_fraction);
}

