/* Sound display/edit/etc
 *
 * originally intended as a re-implementation of my much-missed dpysnd -- the Foonly/SAIL/E/Mus10/Grnlib sound editor from ca 1983.
 */

#include "snd.h"

snd_state *ss = NULL;

static bool ignore_mus_error(int type, char *msg)
{
  XEN result = XEN_FALSE;

  if (XEN_HOOKED(ss->mus_error_hook))
    result = run_or_hook(ss->mus_error_hook,
			 XEN_LIST_2(C_TO_XEN_INT(type),
				    C_TO_XEN_STRING(msg)),
			 S_mus_error_hook);
  return(XEN_NOT_FALSE_P(result));
}

#if HAVE_SETJMP_H
  void top_level_catch(int ignore);
#endif

void mus_error_to_snd(int type, char *msg)
{
  if (!ss)
    {
      fprintf(stderr, "%s", msg);
      return;
    }

  if (!(ignore_mus_error(type, msg)))
    {
#if HAVE_EXTENSION_LANGUAGE
      if (msg == NULL)
	XEN_ERROR(XEN_ERROR_TYPE("mus-error"),
		  XEN_LIST_1(C_TO_XEN_STRING((char *)mus_error_type_to_string(type))));
      else XEN_ERROR(XEN_ERROR_TYPE("mus-error"),
		     XEN_LIST_1(C_TO_XEN_STRING(msg)));
#endif
      snd_error("%s: %s", mus_error_type_to_string(type), msg);
#if HAVE_SETJMP_H
      ss->jump_ok = true;
      top_level_catch(1); /* sigh -- try to keep going */
#endif
    }
}

static void mus_print_to_snd(char *msg)
{
  if (!ss)
    {
      fprintf(stderr, "%s", msg);
      return;
    }
  if (!(ignore_mus_error(MUS_NO_ERROR, msg)))
    if (msg)
      {
	int i, len;
	listener_append(";");
	len = strlen(msg);

	for (i = 1; i < len - 1; i++)
	  if ((msg[i] == '\n') && (msg[i + 1] == ' '))
	    msg[i + 1] = ';';

	if (msg[0] == '\n')
	  listener_append((char *)(msg + 1));
	else listener_append(msg);

	if (msg[strlen(msg) - 1] != '\n')
	  listener_append("\n");
      }
}

static void initialize_load_path(void)
{
  /* look for SND_PATH env var, add dirs to %load-path or load_path */
  char *path;
  path = getenv("SND_PATH");
  if (path)
    {
      /* colon-separated list of directory names, pushed on load-path in reverse order (hopefully = search order) */
      int i, len, dirs = 1, curdir = 0, start = 0;
      char **dirnames;

      len = strlen(path);
      for (i = 0; i < len; i++)
	if (path[i] == ':')
	  dirs++;

      dirnames = (char **)calloc(dirs, sizeof(char *));
      for (i = 0; i < len; i++)
	{
	  if ((path[i] == ':') ||
	      (i == len - 1))
	    {
	      if (i > start)
		{
		  int j, lim;
		  char *tmp;

		  if (i == (len - 1))
		    lim = i + 1;
		  else lim = i;

		  tmp = (char *)calloc(lim - start + 1, sizeof(char));
		  for (j = start; j < lim; j++)
		    tmp[j - start] = path[j];

		  dirnames[curdir++] = mus_expand_filename(tmp);
		  start = i + 1;
		  free(tmp);
		}
	    }
	}

      for (i = curdir - 1; i >= 0; i--)
	{
	  XEN_ADD_TO_LOAD_PATH(dirnames[i]);
	  free(dirnames[i]);
	}
      free(dirnames);
    }
}

void snd_set_global_defaults(bool need_cleanup)
{
  if (need_cleanup)
    {
      if (ss->HTML_Program) {free(ss->HTML_Program); ss->HTML_Program = NULL;}
      if (ss->HTML_Dir) {free(ss->HTML_Dir); ss->HTML_Dir = NULL;}
      if (ss->Temp_Dir) {free(ss->Temp_Dir); ss->Temp_Dir = NULL;}
      if (ss->Save_Dir) {free(ss->Save_Dir); ss->Save_Dir = NULL;}
      if (ss->Ladspa_Dir) {free(ss->Ladspa_Dir); ss->Ladspa_Dir = NULL;}
      if (ss->Save_State_File) {free(ss->Save_State_File); ss->Save_State_File = NULL;}
      if (ss->Eps_File) {free(ss->Eps_File); ss->Eps_File = NULL;}
      if (ss->Listener_Prompt) {free(ss->Listener_Prompt); ss->Listener_Prompt = NULL;}
      if (ss->Open_File_Dialog_Directory) {free(ss->Open_File_Dialog_Directory); ss->Open_File_Dialog_Directory = NULL;}

      /* not sure about the next two... */
      if ((cursor_style(ss) == CURSOR_PROC) && (XEN_PROCEDURE_P(ss->cursor_proc)))
	snd_unprotect_at(ss->cursor_proc_loc);
      if ((zoom_focus_style(ss) == ZOOM_FOCUS_PROC) && (XEN_PROCEDURE_P(ss->zoom_focus_proc)))
	snd_unprotect_at(ss->zoom_focus_proc_loc);
    }

  ss->Transform_Size =              DEFAULT_TRANSFORM_SIZE;
  ss->Fft_Window =                  DEFAULT_FFT_WINDOW;
  ss->Fft_Window_Alpha =            DEFAULT_FFT_WINDOW_ALPHA;
  ss->Fft_Window_Beta =             DEFAULT_FFT_WINDOW_BETA;
  ss->Transform_Graph_Type =        DEFAULT_TRANSFORM_GRAPH_TYPE;
  ss->Sinc_Width =                  DEFAULT_SINC_WIDTH;
  ss->Zero_Pad =                    DEFAULT_ZERO_PAD;
  ss->Wavelet_Type =                DEFAULT_WAVELET_TYPE;
  ss->Transform_Type =              DEFAULT_TRANSFORM_TYPE;
  ss->Transform_Normalization =     DEFAULT_TRANSFORM_NORMALIZATION;
  ss->Show_Transform_Peaks =        DEFAULT_SHOW_TRANSFORM_PEAKS;
  ss->Show_Sonogram_Cursor =        DEFAULT_SHOW_SONOGRAM_CURSOR;
  ss->Fft_Log_Magnitude =           DEFAULT_FFT_LOG_MAGNITUDE;
  ss->Fft_Log_Frequency =           DEFAULT_FFT_LOG_FREQUENCY;
  ss->Fft_With_Phases =             DEFAULT_FFT_WITH_PHASES;
  ss->Max_Transform_Peaks =         DEFAULT_MAX_TRANSFORM_PEAKS;
  ss->Log_Freq_Start =              DEFAULT_LOG_FREQ_START;
  ss->Min_dB =                      DEFAULT_MIN_DB;
  ss->lin_dB =                      pow(10.0, DEFAULT_MIN_DB * 0.05);
  ss->Show_Selection_Transform =    DEFAULT_SHOW_SELECTION_TRANSFORM;
  ss->Default_Output_Chans =        DEFAULT_OUTPUT_CHANS;
  ss->Default_Output_Srate =        DEFAULT_OUTPUT_SRATE;
  ss->Default_Output_Header_Type =  DEFAULT_OUTPUT_HEADER_TYPE;
  ss->Default_Output_Data_Format =  DEFAULT_OUTPUT_DATA_FORMAT;
  ss->Audio_Input_Device =          DEFAULT_AUDIO_INPUT_DEVICE;
  ss->Audio_Output_Device =         DEFAULT_AUDIO_OUTPUT_DEVICE;
  ss->Dac_Size =                    DEFAULT_DAC_SIZE;
  ss->Dac_Combines_Channels =       DEFAULT_DAC_COMBINES_CHANNELS;
  ss->Auto_Resize =                 DEFAULT_AUTO_RESIZE;
  ss->Auto_Update =                 DEFAULT_AUTO_UPDATE;
  ss->Auto_Update_Interval =        DEFAULT_AUTO_UPDATE_INTERVAL;
  ss->Ask_Before_Overwrite =        DEFAULT_ASK_BEFORE_OVERWRITE;
  ss->With_Toolbar =                DEFAULT_WITH_TOOLBAR;
  ss->With_Tooltips =               DEFAULT_WITH_TOOLTIPS;
  ss->Remember_Sound_State =        DEFAULT_REMEMBER_SOUND_STATE;
  ss->Ask_About_Unsaved_Edits =     DEFAULT_ASK_ABOUT_UNSAVED_EDITS;
  ss->Save_As_Dialog_Src =          DEFAULT_SAVE_AS_DIALOG_SRC;
  ss->Save_As_Dialog_Auto_Comment = DEFAULT_SAVE_AS_DIALOG_AUTO_COMMENT;
  ss->Show_Full_Duration =          DEFAULT_SHOW_FULL_DURATION;
  ss->Show_Full_Range =             DEFAULT_SHOW_FULL_RANGE;
  ss->Initial_Beg =                 DEFAULT_INITIAL_BEG;
  ss->Initial_Dur =                 DEFAULT_INITIAL_DUR;
  ss->With_Background_Processes =   DEFAULT_WITH_BACKGROUND_PROCESSES;
  ss->With_File_Monitor =           DEFAULT_WITH_FILE_MONITOR;
  ss->Selection_Creates_Region =    DEFAULT_SELECTION_CREATES_REGION;
  ss->Channel_Style =               DEFAULT_CHANNEL_STYLE;
  ss->Sound_Style =                 DEFAULT_SOUND_STYLE;
  ss->Graphs_Horizontal =           DEFAULT_GRAPHS_HORIZONTAL;
  ss->Graph_Style =                 DEFAULT_GRAPH_STYLE;
  ss->Region_Graph_Style =          DEFAULT_GRAPH_STYLE;
  ss->Time_Graph_Type =             DEFAULT_TIME_GRAPH_TYPE;
  ss->X_Axis_Style =                DEFAULT_X_AXIS_STYLE;
  ss->Beats_Per_Minute =            DEFAULT_BEATS_PER_MINUTE;
  ss->Beats_Per_Measure =           DEFAULT_BEATS_PER_MEASURE;
  ss->With_Relative_Panes =         DEFAULT_WITH_RELATIVE_PANES;
  ss->With_GL =                     DEFAULT_WITH_GL;
  ss->Dot_Size =                    DEFAULT_DOT_SIZE;
  ss->Grid_Density =                DEFAULT_GRID_DENSITY;
  ss->Zoom_Focus_Style =            DEFAULT_ZOOM_FOCUS_STYLE;
  ss->zoom_focus_proc =             XEN_UNDEFINED;
  ss->zoom_focus_proc_loc =         NOT_A_GC_LOC;
  ss->Max_Regions =                 DEFAULT_MAX_REGIONS;
  ss->Show_Y_Zero =                 DEFAULT_SHOW_Y_ZERO;
  ss->Show_Grid =                   DEFAULT_SHOW_GRID;
  ss->Show_Axes =                   DEFAULT_SHOW_AXES;
  ss->Show_Indices =                DEFAULT_SHOW_INDICES;
  ss->Show_Backtrace =              DEFAULT_SHOW_BACKTRACE;
  ss->With_Inset_Graph =            DEFAULT_WITH_INSET_GRAPH;
  ss->With_Interrupts =             DEFAULT_WITH_INTERRUPTS;
  ss->With_Menu_Icons =             DEFAULT_WITH_MENU_ICONS;
  ss->With_Smpte_Label =            DEFAULT_WITH_SMPTE_LABEL;
  ss->With_Pointer_Focus =          DEFAULT_WITH_POINTER_FOCUS;
  ss->Play_Arrow_Size =             DEFAULT_PLAY_ARROW_SIZE;
  ss->Sync_Style =                  DEFAULT_SYNC_STYLE;
  ss->Listener_Prompt =             mus_strdup(DEFAULT_LISTENER_PROMPT);
  ss->listener_prompt_length =      mus_strlen(ss->Listener_Prompt);
  ss->Minibuffer_History_Length =   DEFAULT_MINIBUFFER_HISTORY_LENGTH;
  ss->Clipping =                    DEFAULT_CLIPPING;
  ss->Optimization =                DEFAULT_OPTIMIZATION;
  ss->Print_Length =                DEFAULT_PRINT_LENGTH;
  ss->View_Files_Sort =             DEFAULT_VIEW_FILES_SORT;
  ss->Just_Sounds =                 DEFAULT_JUST_SOUNDS;
  ss->Open_File_Dialog_Directory =  NULL;
  ss->HTML_Dir =                    mus_strdup(DEFAULT_HTML_DIR);
  ss->HTML_Program =                mus_strdup(DEFAULT_HTML_PROGRAM);
  ss->Cursor_Size =                 DEFAULT_CURSOR_SIZE;
  ss->Cursor_Style =                DEFAULT_CURSOR_STYLE;
  ss->Tracking_Cursor_Style =       DEFAULT_TRACKING_CURSOR_STYLE;
  ss->With_Tracking_Cursor =        DEFAULT_WITH_TRACKING_CURSOR;
  ss->cursor_proc =                 XEN_UNDEFINED;
  ss->cursor_proc_loc =             NOT_A_GC_LOC;
  ss->Verbose_Cursor =              DEFAULT_VERBOSE_CURSOR;
  ss->Cursor_Update_Interval =      DEFAULT_CURSOR_UPDATE_INTERVAL;
  ss->Cursor_Location_Offset =      DEFAULT_CURSOR_LOCATION_OFFSET;
  ss->Show_Mix_Waveforms =          DEFAULT_SHOW_MIX_WAVEFORMS;
  ss->Mix_Waveform_Height =         DEFAULT_MIX_WAVEFORM_HEIGHT;
  ss->Mix_Tag_Width =               DEFAULT_MIX_TAG_WIDTH;
  ss->Mix_Tag_Height =              DEFAULT_MIX_TAG_HEIGHT;
  ss->With_Mix_Tags =               DEFAULT_WITH_MIX_TAGS;
  ss->Mark_Tag_Width =              DEFAULT_MARK_TAG_WIDTH;
  ss->Mark_Tag_Height =             DEFAULT_MARK_TAG_HEIGHT;
  ss->Show_Marks =                  DEFAULT_SHOW_MARKS;
  ss->Color_Map =                   DEFAULT_COLOR_MAP;
  ss->Color_Map_Size =              DEFAULT_COLOR_MAP_SIZE;
  ss->Color_Cutoff =                DEFAULT_COLOR_CUTOFF;
  ss->Color_Scale =                 DEFAULT_COLOR_SCALE;
  ss->Color_Inverted =              DEFAULT_COLOR_INVERTED;
  ss->Color_Map =                   DEFAULT_COLOR_MAP;
  ss->Wavo_Hop =                    DEFAULT_WAVO_HOP;
  ss->Wavo_Trace =                  DEFAULT_WAVO_TRACE;
  ss->Spectro_Hop =                 DEFAULT_SPECTRO_HOP;
  ss->Spectro_X_Scale =             DEFAULT_SPECTRO_X_SCALE;
  ss->Spectro_Y_Scale =             DEFAULT_SPECTRO_Y_SCALE;
  ss->Spectro_Z_Scale =             DEFAULT_SPECTRO_Z_SCALE;
  ss->Spectro_Z_Angle =             DEFAULT_SPECTRO_Z_ANGLE;
  ss->Spectro_X_Angle =             DEFAULT_SPECTRO_X_ANGLE;
  ss->Spectro_Y_Angle =             DEFAULT_SPECTRO_Y_ANGLE;
  ss->Spectrum_End =                DEFAULT_SPECTRUM_END;
  ss->Spectrum_Start =              DEFAULT_SPECTRUM_START;
  ss->Enved_Base =                  DEFAULT_ENVED_BASE;
  ss->Enved_Power =                 DEFAULT_ENVED_POWER;
  ss->Enved_Wave_p =                DEFAULT_ENVED_WAVE_P;
  ss->Enved_Style =                 DEFAULT_ENVED_STYLE;
  ss->Enved_Target =                DEFAULT_ENVED_TARGET;
  ss->Enved_Filter_Order =          DEFAULT_ENVED_FILTER_ORDER;
  ss->Eps_Bottom_Margin =           DEFAULT_EPS_BOTTOM_MARGIN;
  ss->Eps_Left_Margin =             DEFAULT_EPS_LEFT_MARGIN;
  ss->Eps_Size =                    DEFAULT_EPS_SIZE;
  ss->Expand_Control_Min =          DEFAULT_EXPAND_CONTROL_MIN;
  ss->Expand_Control_Max =          DEFAULT_EXPAND_CONTROL_MAX;
  ss->Amp_Control_Min =             DEFAULT_AMP_CONTROL_MIN;
  ss->Amp_Control_Max =             DEFAULT_AMP_CONTROL_MAX;
  ss->Speed_Control_Min =           DEFAULT_SPEED_CONTROL_MIN;
  ss->Speed_Control_Max =           DEFAULT_SPEED_CONTROL_MAX;
  ss->Contrast_Control_Min =        DEFAULT_CONTRAST_CONTROL_MIN;
  ss->Contrast_Control_Max =        DEFAULT_CONTRAST_CONTROL_MAX;
  ss->Contrast_Control_Amp =        DEFAULT_CONTRAST_CONTROL_AMP;
  ss->Expand_Control_Length =       DEFAULT_EXPAND_CONTROL_LENGTH;
  ss->Expand_Control_Ramp =         DEFAULT_EXPAND_CONTROL_RAMP;
  ss->Expand_Control_Hop =          DEFAULT_EXPAND_CONTROL_HOP;
  ss->Expand_Control_Jitter =       DEFAULT_EXPAND_CONTROL_JITTER;
  ss->Reverb_Control_Feedback =     DEFAULT_REVERB_CONTROL_FEEDBACK;
  ss->Reverb_Control_Lowpass =      DEFAULT_REVERB_CONTROL_LOWPASS;
  ss->Reverb_Control_Scale_Min =    DEFAULT_REVERB_CONTROL_SCALE_MIN;
  ss->Reverb_Control_Scale_Max =    DEFAULT_REVERB_CONTROL_SCALE_MAX;
  ss->Reverb_Control_Decay =        DEFAULT_REVERB_CONTROL_DECAY;
  ss->Speed_Control_Tones =         DEFAULT_SPEED_CONTROL_TONES;
  ss->Speed_Control_Style =         DEFAULT_SPEED_CONTROL_STYLE;
  ss->Reverb_Control_Length_Min =   DEFAULT_REVERB_CONTROL_LENGTH_MIN;
  ss->Reverb_Control_Length_Max =   DEFAULT_REVERB_CONTROL_LENGTH_MAX;
  ss->Filter_Control_Order =        DEFAULT_FILTER_CONTROL_ORDER;
  ss->Filter_Control_In_Db =        DEFAULT_FILTER_CONTROL_IN_DB;
  ss->Filter_Control_In_Hz =        DEFAULT_FILTER_CONTROL_IN_HZ;
  ss->Show_Controls =               DEFAULT_SHOW_CONTROLS;

  if (MUS_DEFAULT_TEMP_DIR != (char *)NULL)
    ss->Temp_Dir = mus_strdup(MUS_DEFAULT_TEMP_DIR);
  else ss->Temp_Dir = NULL;

  if (MUS_DEFAULT_SAVE_DIR != (char *)NULL)
    ss->Save_Dir = mus_strdup(MUS_DEFAULT_SAVE_DIR);
  else ss->Save_Dir = NULL;

  if (DEFAULT_LADSPA_DIR != (char *)NULL)
    ss->Ladspa_Dir = mus_strdup(DEFAULT_LADSPA_DIR);
  else ss->Ladspa_Dir = NULL;

  if (DEFAULT_SAVE_STATE_FILE != (char *)NULL)
    ss->Save_State_File = mus_strdup(DEFAULT_SAVE_STATE_FILE);
  else ss->Save_State_File = NULL;

  if (DEFAULT_PEAK_ENV_DIR != (char *)NULL)
    ss->Peak_Env_Dir = mus_strdup(DEFAULT_PEAK_ENV_DIR);
  else ss->Peak_Env_Dir = NULL;

  if (DEFAULT_EPS_FILE != (char *)NULL)
    ss->Eps_File = mus_strdup(DEFAULT_EPS_FILE);
  else ss->Eps_File = NULL;
}

#if HAVE_SETJMP_H && HAVE_SCHEME
static void jump_to_top_level(void)
{
  top_level_catch(1);
}
#endif

#if HAVE_GSL
#include <gsl/gsl_ieee_utils.h>
#include <gsl/gsl_errno.h>

/* default gsl error handler apparently aborts main program! */

static void snd_gsl_error(const char *reason, const char *file, int line, int gsl_errno)
{
  XEN_ERROR(XEN_ERROR_TYPE("gsl-error"),
	    XEN_LIST_6(C_TO_XEN_STRING("GSL: ~A, ~A in ~A line ~A, gsl err: ~A"),
		       C_TO_XEN_STRING(gsl_strerror(gsl_errno)),
		       C_TO_XEN_STRING(reason),
		       C_TO_XEN_STRING(file),
		       C_TO_XEN_INT(line),
		       C_TO_XEN_INT(gsl_errno)));
}
#endif

#if SND_AS_WIDGET
  snd_state *snd_main(int argc, char **argv)
#else
  int main(int argc, char **argv)
#endif
{
  int i;

#if HAVE_GSL
  /* if HAVE_GSL and the environment variable GSL_IEEE_MODE exists, use it */
  /* GSL_IEEE_MODE=double-precision,mask-underflow,mask-denormalized */
  if (getenv("GSL_IEEE_MODE") != NULL)
    gsl_ieee_env_setup();
  gsl_set_error_handler(snd_gsl_error);
#endif

  ss = (snd_state *)calloc(1, sizeof(snd_state)); /* not calloc! */
  ss->fam_ok = false;
  ss->startup_errors = NULL;

#if HAVE_GTK_3
  g_type_init();
#endif

  mus_sound_initialize(); /* has to precede version check (mus_audio_moniker needs to be setup in Alsa/Oss) */
  xen_initialize();

#if HAVE_SCHEME && HAVE_SETJMP_H
  s7_set_error_exiter(s7, jump_to_top_level);
#endif

  for (i = 1; i < argc; i++)
    {
      if (strcmp(argv[i], "--version") == 0)
	{
	  fprintf(stdout, "%s", version_info());
	  snd_exit(0);
	}
      else
	{
	  if (strcmp(argv[i], "--help") == 0)
	    {
	      fprintf(stdout, "%s", "Snd is a sound editor; see http://ccrma.stanford.edu/software/snd/.\n");
	      fprintf(stdout, "%s", version_info());
	      snd_exit(0);
	    }
	}
    }

  initialize_format_lists();
  snd_set_global_defaults(false);

#if MUS_DEBUGGING
  ss->Trap_Segfault = false;
#else
  ss->Trap_Segfault = DEFAULT_TRAP_SEGFAULT;
#endif
  ss->jump_ok = false;
  allocate_regions(max_regions(ss));
  ss->init_window_x = DEFAULT_INIT_WINDOW_X;
  ss->init_window_y = DEFAULT_INIT_WINDOW_Y;
  ss->init_window_width = DEFAULT_INIT_WINDOW_WIDTH;
  ss->init_window_height = DEFAULT_INIT_WINDOW_HEIGHT;
  ss->click_time = 100;
  init_sound_file_extensions();

  ss->max_sounds = 4;                 /* expands to accommodate any number of files */
  ss->sound_sync_max = 0;
  ss->stopped_explicitly = false;     /* C-g sets this flag so that we can interrupt various loops */
  ss->checking_explicitly = false;
  ss->selection_play_stop = false;
  ss->reloading_updated_file = 0;
  ss->selected_sound = NO_SELECTION;
  ss->sounds = (snd_info **)calloc(ss->max_sounds, sizeof(snd_info *));
  ss->print_choice = PRINT_SND;
  ss->graph_hook_active = false;
  ss->lisp_graph_hook_active = false;
  ss->exiting = false;
  ss->deferred_regions = 0;
  ss->fam_connection = NULL;
  ss->snd_error_data = NULL;
  ss->snd_error_handler = NULL;
  ss->snd_warning_data = NULL;
  ss->snd_warning_handler = NULL;
  ss->xen_error_data = NULL;
  ss->xen_error_handler = NULL;
  ss->update_sound_channel_style = NOT_A_CHANNEL_STYLE;

#if HAVE_GL && WITH_GL2PS
  ss->gl_printing = false;
#endif
  g_xen_initialize();
  ss->search_proc = XEN_UNDEFINED;
  ss->search_expr = NULL;
  ss->search_tree = NULL;
  mus_error_set_handler(mus_error_to_snd);
  mus_print_set_handler(mus_print_to_snd);

  initialize_load_path(); /* merge SND_PATH entries into the load-path */

#ifdef SND_AS_WIDGET
  return(ss);
#else
  snd_doit(argc, argv);
  return(0);
#endif
}

void g_init_base(void)
{
  #define H_mus_error_hook S_mus_error_hook " (error-type error-message):  called upon mus_error. \
If it returns " PROC_TRUE ", Snd ignores the error (it assumes you've handled it via the hook)."

  ss->mus_error_hook = XEN_DEFINE_HOOK(S_mus_error_hook, 2, H_mus_error_hook);       /* arg = error-type error-message */
}


/* Author: G. Jungman
 */
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <gsl/gsl_errno.h>

/* Compile all the inline functions */

#define COMPILE_INLINE_STATIC
#include "build.h"
#include <gsl/gsl_qrng.h>


/* block/gsl_block_int.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 Gerard Jungman, Brian Gough
 *
 * This program 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 3 of the License, or (at
 * your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_BLOCK_INT_H__
#define __GSL_BLOCK_INT_H__

#include <stdlib.h>
#include <gsl/gsl_errno.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

struct gsl_block_int_struct
{
  size_t size;
  int *data;
};

typedef struct gsl_block_int_struct gsl_block_int;

gsl_block_int *gsl_block_int_alloc (const size_t n);
gsl_block_int *gsl_block_int_calloc (const size_t n);
void gsl_block_int_free (gsl_block_int * b);

int gsl_block_int_fread (FILE * stream, gsl_block_int * b);
int gsl_block_int_fwrite (FILE * stream, const gsl_block_int * b);
int gsl_block_int_fscanf (FILE * stream, gsl_block_int * b);
int gsl_block_int_fprintf (FILE * stream, const gsl_block_int * b, const char *format);

int gsl_block_int_raw_fread (FILE * stream, int * b, const size_t n, const size_t stride);
int gsl_block_int_raw_fwrite (FILE * stream, const int * b, const size_t n, const size_t stride);
int gsl_block_int_raw_fscanf (FILE * stream, int * b, const size_t n, const size_t stride);
int gsl_block_int_raw_fprintf (FILE * stream, const int * b, const size_t n, const size_t stride, const char *format);

size_t gsl_block_int_size (const gsl_block_int * b);
int * gsl_block_int_data (const gsl_block_int * b);

__END_DECLS

#endif /* __GSL_BLOCK_INT_H__ */


/*
   Copyright [2019,2021] [IBM Corporation]
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
       http://www.apache.org/licenses/LICENSE-2.0
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#ifndef __CCPM_CRASH_CONSISTENT_ALLOCATOR_H__
#define __CCPM_CRASH_CONSISTENT_ALLOCATOR_H__

#include <ccpm/interfaces.h>
#include <common/exceptions.h>
#include <gsl/pointers>
#include <iosfwd>
#include <memory>
#include <string>
#include <vector>

namespace ccpm
{

	struct area_top;

	struct cca
		: public IHeap_expandable
	{
		using byte_span = common::byte_span;
		using persist_type = gsl::not_null<persister *>;

		/*
		 * An objection arose to requiring multiple arguments for the cca constructor.
		 * This struct allows construction from a single argument in all cases.
		 * cca.h may not be the best place for this, as cca does not itsenf use the struct.
		 */
		struct ctor_args
		{
			persist_type persister;
			region_span regions;
			bool force_init;
			ownership_callback_type callee_owns;
		};

	private:
		using top_vec_t = std::vector<std::unique_ptr<area_top>>;
		top_vec_t _top;
		top_vec_t::difference_type _last_top_allocate;
		top_vec_t::difference_type _last_top_free;
		persist_type _persist;

		void init(
			region_span regions
			, ownership_callback_type resolver
			, bool force_init
		);
	public:
		explicit cca(persist_type persist, region_span regions, ownership_callback_type resolver);

		explicit cca(persist_type persist, region_span regions);

		explicit cca(persist_type persist);

		~cca();

		cca(const cca &) = delete;
		const cca& operator=(const cca &) = delete;

		bool reconstitute(
			region_span regions
			, ownership_callback_type resolver
			, bool force_init
		) override;

		status_t allocate(
			void * & ptr_
			, std::size_t bytes_
			, std::size_t alignment_
		) override;

    void * allocate(std::size_t bytes_, std::size_t alignment_ = 1) {
      void * ptr = nullptr;
      if(allocate(ptr, bytes_, alignment_) != 0)
        throw General_exception("ccpm::cca::allocate failed");
      return ptr;
    }

    void * allocate_root(std::size_t bytes_, std::size_t alignment_ = 8) {
      void * ptr = nullptr;
      if(allocate(ptr, bytes_, alignment_) != 0)
        throw General_exception("ccpm::cca::allocate failed");
      set_root(common::make_byte_span(ptr, bytes_));
      return ptr;
    }

		status_t free(
			void * & ptr_
			, std::size_t bytes_
		) override;

		void add_regions(
			region_span
		) override;

		bool includes(
			const void *addr
		) const override;

		status_t remaining(
			std::size_t & out_size_
		) const override;

		region_vector_t get_regions() const override;

    void set_root(
      byte_span
    );

    byte_span get_root() const;

		void print(std::ostream &, const std::string &title = "cca") const;
	};
}

#endif


/*
 * VGMTrans (c) 2002-2019
 * Licensed under the zlib license,
 * refer to the included LICENSE.txt file
 */
#pragma once

#include <cstdint>
#include <vector>
#include <map>
#include <string>
#include <cassert>
#include <climits>
#include <gsl-lite.hpp>

class RawFile;

constexpr auto PSF_TAG_SIG = "[TAG]";
constexpr auto PSF_TAG_SIG_LEN = 5;
constexpr auto PSF_STRIP_BUF_SIZE = 4096;

class PSFFile2 {
   public:
    PSFFile2(const RawFile &file);
    ~PSFFile2() = default;

    uint8_t version() const noexcept { return m_version; }
    const std::map<std::string, std::string> &tags() const noexcept { return m_tags; }
    const std::vector<char> &exe() const noexcept { return m_exe_data; }
    const std::vector<char> &reservedSection() const noexcept { return m_reserved_data; }

    template <typename T>
    T getExe(size_t ind) const {
        assert(ind + sizeof(T) < m_exe_data.size());

        T value = 0;
        for (size_t i = 0; i < sizeof(T); i++) {
            value |= (m_exe_data[ind + i] << (i * CHAR_BIT));
        }

        return value;
    }

    template <typename T>
    T getRes(size_t ind) const {
        assert(ind + sizeof(T) < m_reserved_data.size());

        T value = 0;
        for (size_t i = 0; i < sizeof(T); i++) {
            value |= (m_reserved_data[ind + i] << (i * CHAR_BIT));
        }

        return value;
    }

   private:
    uint8_t m_version;
    uint32_t m_exe_CRC;
    std::vector<char> m_exe_data;
    std::vector<char> m_reserved_data;
    std::map<std::string, std::string> m_tags;

    void parseTags(gsl::span<const char> data);
};


/*
Copyright (c) 2015, Patrick Weltevrede
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gsl/gsl_sort_float.h>
#include <gsl/gsl_statistics_float.h>
#include <gsl/gsl_integration.h>
#include "psrsalsa.h"
int filterPApoints(datafile_definition *datafile, verbose_definition verbose)
{
  int dPa_polnr;
  long i, j, nrpoints;
  float *olddata;
  if(datafile->poltype != POLTYPE_ILVPAdPA && datafile->poltype != POLTYPE_PAdPA && datafile->poltype != POLTYPE_ILVPAdPATEldEl) {
    printerror(verbose.debug, "ERROR filterPApoints: Data doesn't appear to have poltype ILVPAdPA, PAdPA or ILVPAdPATEldEl.");
    return 0;
  }
  if(datafile->poltype == POLTYPE_ILVPAdPA && datafile->NrPols != 5) {
    printerror(verbose.debug, "ERROR filterPApoints: 5 polarization channels were expected, but there are only %ld.", datafile->NrPols);
    return 0;
  }else if(datafile->poltype == POLTYPE_ILVPAdPATEldEl && datafile->NrPols != 8) {
    printerror(verbose.debug, "ERROR filterPApoints: 8 polarization channels were expected, but there are only %ld.", datafile->NrPols);
    return 0;
  }else if(datafile->poltype == POLTYPE_PAdPA && datafile->NrPols != 2) {
    printerror(verbose.debug, "ERROR filterPApoints: 2 polarization channels were expected, but there are only %ld.", datafile->NrPols);
    return 0;
  }
  if(datafile->NrSubints > 1 || datafile->NrFreqChan > 1) {
    printerror(verbose.debug, "ERROR filterPApoints: Can only do this opperation if there is one subint and one frequency channel.");
    return 0;
  }
  if(datafile->tsampMode != TSAMPMODE_LONGITUDELIST) {
    printerror(verbose.debug, "ERROR filterPApoints: Expected pulse longitudes to be defined.");
    return 0;
  }
  if(datafile->poltype == POLTYPE_ILVPAdPA || datafile->poltype == POLTYPE_ILVPAdPATEldEl) {
    dPa_polnr = 4;
  }else if(datafile->poltype == POLTYPE_PAdPA) {
    dPa_polnr = 1;
  }
  nrpoints = 0;
  for(i = 0; i < datafile->NrBins; i++) {
    if(datafile->data[i+dPa_polnr*datafile->NrBins] > 0) {
      nrpoints++;
    }
  }
  if(verbose.verbose)
    printf("Keeping %ld significant PA points\n", nrpoints);
  olddata = datafile->data;
  datafile->data = (float *)malloc(nrpoints*datafile->NrPols*sizeof(float));
  if(datafile->data == NULL) {
    printerror(verbose.debug, "ERROR filterPApoints: Memory allocation error.");
    return 0;
  }
  j = 0;
  for(i = 0; i < datafile->NrBins; i++) {
    if(olddata[i+dPa_polnr*datafile->NrBins] > 0) {
      if(datafile->poltype == POLTYPE_ILVPAdPA) {
 datafile->data[j+0*nrpoints] = olddata[i+0*datafile->NrBins];
 datafile->data[j+1*nrpoints] = olddata[i+1*datafile->NrBins];
 datafile->data[j+2*nrpoints] = olddata[i+2*datafile->NrBins];
 datafile->data[j+3*nrpoints] = olddata[i+3*datafile->NrBins];
 datafile->data[j+4*nrpoints] = olddata[i+4*datafile->NrBins];
      }else if(datafile->poltype == POLTYPE_ILVPAdPATEldEl) {
 datafile->data[j+0*nrpoints] = olddata[i+0*datafile->NrBins];
 datafile->data[j+1*nrpoints] = olddata[i+1*datafile->NrBins];
 datafile->data[j+2*nrpoints] = olddata[i+2*datafile->NrBins];
 datafile->data[j+3*nrpoints] = olddata[i+3*datafile->NrBins];
 datafile->data[j+4*nrpoints] = olddata[i+4*datafile->NrBins];
 datafile->data[j+5*nrpoints] = olddata[i+5*datafile->NrBins];
 datafile->data[j+6*nrpoints] = olddata[i+6*datafile->NrBins];
 datafile->data[j+7*nrpoints] = olddata[i+7*datafile->NrBins];
      }else {
 datafile->data[j+0*nrpoints] = olddata[i+0*datafile->NrBins];
 datafile->data[j+1*nrpoints] = olddata[i+1*datafile->NrBins];
      }
      datafile->tsamp_list[j] = datafile->tsamp_list[i];
      j++;
    }
  }
  free(olddata);
  datafile->NrBins = nrpoints;
  return datafile->NrBins;
}
int make_paswing_fromIQUV(datafile_definition *datafile, int extended, pulselongitude_regions_definition onpulse, int normalize, int correctLbias, float correctQV, float correctV, int nolongitudes, float loffset, float paoffset, datafile_definition *rms_file, float rebin_factor, verbose_definition verbose)
{
  int indent, rms_file_specified;
  long i, j, NrOffpulseBins, pulsenr, freqnr, output_nr_pols;
  float ymax, baseline_intensity, RMSQ, RMSU, *Loffpulse, *Poffpulse, medianL, medianP, *newdata, *newdata_rms;
  rms_file_specified = 1;
  if(rms_file == NULL) {
    rms_file = datafile;
    rms_file_specified = 0;
  }
  if(verbose.verbose) {
    for(indent = 0; indent < verbose.indent; indent++) printf(" ");
    printf("Constructing PA and degree of linear polarization");
    if(extended)
      printf(", total polarization and ellipticity");
    if(rms_file_specified)
      printf(" (using a seperate file to determine the off-pulse rms)");
    printf("\n");
    for(indent = 0; indent < verbose.indent; indent++) printf(" ");
    printf("  Reference frequency for PA is ");
    if(datafile->isDeFarad) {
      if((datafile->freq_ref > -1.1 && datafile->freq_ref < -0.9) || (datafile->freq_ref > 0.99e10 && datafile->freq_ref < 1.01e10))
 printf("infinity\n");
      else if(datafile->freq_ref < 0)
 printf("unknown\n");
      else
 printf("%f MHz\n", datafile->freq_ref);
    }else {
      if(datafile->NrFreqChan == 1)
 printf("%lf MHz\n", get_centre_frequency(*datafile, verbose));
      else
 printf("observing frequencies of individual frequency channels\n");
    }
    for(indent = 0; indent < verbose.indent; indent++) printf(" ");
    printf("  ");
    switch(correctLbias) {
    case -1: printf("No L de-bias applied"); break;
    case 0: printf("De-bias L using median noise subtraction"); break;
    case 1: printf("De-bias L using Wardle & Kronberg correction"); break;
    default: printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Undefined L de-bias method specified."); return 0;
    }
    if(correctQV != 1 || correctV != 1)
      printf(", Q correction factor %f, V correction factor %f", 1.0/correctQV, 1.0/(correctQV*correctV));
    if(normalize)
      printf(", output is normalised");
    if(loffset != 0)
      printf(", pulse longitude shifted by %f deg\n", loffset);
    if(paoffset != 0)
      printf(", PA shifted by %f deg\n", paoffset);
    printf("\n");
    if(extended) {
      printwarning(verbose.debug, "WARNING make_paswing_fromIQUV: Total polarization is computed, and the median off-pulse value is subtracted (probably not a very good idea).");
    }
  }
  if(datafile->NrPols != 4 || rms_file->NrPols != 4) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Expected 4 input polarizations.");
    return 0;
  }
  if(datafile->poltype != POLTYPE_STOKES) {
    if(datafile->poltype == POLTYPE_UNKNOWN) {
      printwarning(verbose.debug, "WARNING make_paswing_fromIQUV: Polarization state unknown, it is assumed the data are Stokes parameters.");
    }else {
      printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Convert data into Stokes parameters first.");
      return 0;
    }
  }
  if(rms_file_specified) {
    if(rms_file->poltype != POLTYPE_STOKES) {
      if(rms_file->poltype == POLTYPE_UNKNOWN) {
 printwarning(verbose.debug, "WARNING make_paswing_fromIQUV: Polarization state of the data to be used to determine the off-pulse rms is unknown, it is assumed the data are Stokes parameters.");
      }else {
 printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Convert data to be used to determine the off-pulse rms into Stokes parameters first.");
 return 0;
      }
    }
  }
  if(datafile->tsampMode != TSAMPMODE_FIXEDTSAMP) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Expects input to have a regular sampling.");
    return 0;
  }
  if(correctQV == 0) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR make_paswing_fromIQUV: correctQV is set to zero, you probably want this to be 1.");
    return 0;
  }
  if(correctV == 0) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR make_paswing_fromIQUV: correctV is set to zero, you probably want this to be 1.");
    return 0;
  }
  if(datafile->isDebase == 0) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Please remove baseline first, i.e. use pmod -debase.");
    return 0;
  }else if(datafile->isDebase != 1) {
    fflush(stdout);
    printwarning(verbose.debug, "WARNING make_paswing_fromIQUV: Unknown baseline state. It is assumed the baseline has already removed from the data.");
  }
  if(rms_file_specified) {
    if(rms_file->isDebase == 0) {
      fflush(stdout);
      printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Please remove baseline first, i.e. use pmod -debase.");
      return 0;
    }else if(rms_file->isDebase != 1) {
      fflush(stdout);
      printwarning(verbose.debug, "WARNING make_paswing_fromIQUV: Unknown baseline state. It is assumed the baseline has already removed from the data.");
    }
  }
  if(rms_file_specified) {
    if(datafile->NrSubints != rms_file->NrSubints) {
      printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Number of subintegrations is different in the data to be used to determine the off-pulse rms.");
 return 0;
    }
    if(datafile->NrFreqChan != rms_file->NrFreqChan) {
      printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Number of frequency channels is different in the data to be used to determine the off-pulse rms (%ld != %ld).", rms_file->NrFreqChan, datafile->NrFreqChan);
 return 0;
    }
    if(correctLbias == 0) {
      printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Subtracting the median of L is not supported when a separate file is used for the offpulse statistics.");
      return 0;
    }
    if(extended) {
      printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Subtracting the median of P is not supported when a separate file is used for the offpulse statistics.");
      return 0;
    }
  }
  if(datafile->offpulse_rms != NULL) {
    free(datafile->offpulse_rms);
  }
  Loffpulse = (float *)malloc((rms_file->NrBins)*sizeof(float));
  Poffpulse = (float *)malloc((rms_file->NrBins)*sizeof(float));
  if(extended) {
    output_nr_pols = 8;
  }else {
    output_nr_pols = 5;
  }
  newdata = (float *)malloc(datafile->NrBins*datafile->NrSubints*datafile->NrFreqChan*output_nr_pols*sizeof(float));
  if(rms_file_specified) {
    newdata_rms = (float *)malloc(rms_file->NrBins*rms_file->NrSubints*rms_file->NrFreqChan*output_nr_pols*sizeof(float));
  }else {
    newdata_rms = newdata;
  }
  datafile->offpulse_rms = (float *)malloc(datafile->NrSubints*datafile->NrFreqChan*output_nr_pols*sizeof(float));
  if(Loffpulse == NULL || Poffpulse == NULL || newdata == NULL || datafile->offpulse_rms == NULL || newdata_rms == NULL) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Memory allocation error.");
    return 0;
  }
  if(nolongitudes == 0) {
    datafile->tsamp_list = (double *)malloc(datafile->NrBins*sizeof(double));
    if(datafile->tsamp_list == NULL) {
      fflush(stdout);
      printerror(verbose.debug, "ERROR make_paswing_fromIQUV: Memory allocation error.");
      return 0;
    }
    for(j = 0; j < (datafile->NrBins); j++) {
      datafile->tsamp_list[j] = get_pulse_longitude(*datafile, 0, j, verbose);
      datafile->tsamp_list[j] += loffset;
    }
  }
  if(normalize && (datafile->NrSubints > 1 || datafile->NrFreqChan > 1)) {
    fflush(stdout);
    printwarning(verbose.debug, "WARNING make_paswing_fromIQUV: Normalization will cause all subintegrations/frequency channels to be normalised individually. This may not be desired.");
  }
  long sindex_I, sindex_Q, sindex_U, sindex_V, sindex_I_rms, sindex_Q_rms, sindex_U_rms, sindex_V_rms, newindex_I, newindex_L, newindex_V, newindex_Pa, newindex_dPa, newindex_T, newindex_Ell, newindex_dEll, newindex_L_rms, newindex_T_rms;
  for(pulsenr = 0; pulsenr < datafile->NrSubints; pulsenr++) {
    for(freqnr = 0; freqnr < datafile->NrFreqChan; freqnr++) {
      sindex_I = datafile->NrBins*(0+datafile->NrPols*(freqnr+pulsenr*datafile->NrFreqChan));
      sindex_Q = datafile->NrBins*(1+datafile->NrPols*(freqnr+pulsenr*datafile->NrFreqChan));
      sindex_U = datafile->NrBins*(2+datafile->NrPols*(freqnr+pulsenr*datafile->NrFreqChan));
      sindex_V = datafile->NrBins*(3+datafile->NrPols*(freqnr+pulsenr*datafile->NrFreqChan));
      newindex_I = datafile->NrBins*(0+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
      newindex_L = datafile->NrBins*(1+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
      newindex_V = datafile->NrBins*(2+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
      newindex_Pa = datafile->NrBins*(3+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
      newindex_dPa = datafile->NrBins*(4+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
      if(extended) {
 newindex_T = datafile->NrBins*(5+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
 newindex_Ell = datafile->NrBins*(6+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
 newindex_dEll = datafile->NrBins*(7+output_nr_pols*(freqnr+datafile->NrFreqChan*pulsenr));
      }
      if(rms_file_specified) {
 sindex_I_rms = rms_file->NrBins*(0+rms_file->NrPols*(freqnr+pulsenr*rms_file->NrFreqChan));
 sindex_Q_rms = rms_file->NrBins*(1+rms_file->NrPols*(freqnr+pulsenr*rms_file->NrFreqChan));
 sindex_U_rms = rms_file->NrBins*(2+rms_file->NrPols*(freqnr+pulsenr*rms_file->NrFreqChan));
 sindex_V_rms = rms_file->NrBins*(3+rms_file->NrPols*(freqnr+pulsenr*rms_file->NrFreqChan));
 newindex_L_rms = rms_file->NrBins*(1+output_nr_pols*(freqnr+rms_file->NrFreqChan*pulsenr));
 if(extended) {
   newindex_T_rms = rms_file->NrBins*(5+output_nr_pols*(freqnr+rms_file->NrFreqChan*pulsenr));
 }
      }else {
 sindex_I_rms = sindex_I;
 sindex_Q_rms = sindex_Q;
 sindex_U_rms = sindex_U;
 sindex_V_rms = sindex_V;
 newindex_L_rms = newindex_L;
 if(extended) {
   newindex_T_rms = newindex_T;
 }
      }
      if(normalize == 0) {
 ymax = 1;
      }else {
 ymax = datafile->data[sindex_I];
 for(j = 1; j < (datafile->NrBins); j++) {
   if(datafile->data[sindex_I+j] > ymax)
     ymax = datafile->data[sindex_I+j];
 }
      }
      if(ymax == 0)
 ymax = 1;
      for(j = 0; j < (datafile->NrBins); j++) {
 datafile->data[sindex_I + j] /= ymax;
 datafile->data[sindex_Q + j] /= correctQV*ymax;
 datafile->data[sindex_U + j] /= ymax;
 datafile->data[sindex_V + j] /= correctV*correctQV*ymax;
 newdata[j+newindex_L] = sqrt((datafile->data[sindex_Q+j])*(datafile->data[sindex_Q+j])+(datafile->data[sindex_U+j])*(datafile->data[sindex_U+j]));
 if(extended) {
   newdata[j+newindex_T] = sqrt(newdata[j+newindex_L]*newdata[j+newindex_L]+datafile->data[sindex_V+j]*datafile->data[sindex_V+j]);
 }
      }
      if(rms_file_specified) {
 for(j = 0; j < (rms_file->NrBins); j++) {
   rms_file->data[sindex_I_rms + j] /= ymax;
   rms_file->data[sindex_Q_rms + j] /= correctQV*ymax;
   rms_file->data[sindex_U_rms + j] /= ymax;
   rms_file->data[sindex_V_rms + j] /= correctV*correctQV*ymax;
   newdata_rms[j+newindex_L_rms] = sqrt((rms_file->data[sindex_Q_rms+j])*(rms_file->data[sindex_Q_rms+j])+(rms_file->data[sindex_U_rms+j])*(rms_file->data[sindex_U_rms+j]));
   if(extended) {
     newdata_rms[j+newindex_T_rms] = sqrt(newdata[j+newindex_L_rms]*newdata[j+newindex_L_rms]+rms_file->data[sindex_V_rms+j]*rms_file->data[sindex_V_rms+j]);
   }
 }
      }
      datafile->offpulse_rms[0+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = 0;
      datafile->offpulse_rms[1+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = 0;
      datafile->offpulse_rms[2+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = 0;
      datafile->offpulse_rms[3+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = -1;
      datafile->offpulse_rms[4+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = -1;
      if(extended) {
 datafile->offpulse_rms[5+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = 0;
 datafile->offpulse_rms[6+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = -1;
 datafile->offpulse_rms[7+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = -1;
      }
      baseline_intensity = 0;
      RMSQ = 0;
      RMSU = 0;
      NrOffpulseBins = 0;
      for(i = 0; i < (rms_file->NrBins); i++) {
 Loffpulse[i] = 0;
 if(checkRegions(i, &onpulse, 0, verbose) == 0) {
   NrOffpulseBins++;
   baseline_intensity += rms_file->data[sindex_I_rms+i];
   RMSQ += (rms_file->data[sindex_Q_rms+i])*(rms_file->data[sindex_Q_rms+i]);
   RMSU += (rms_file->data[sindex_U_rms+i])*(rms_file->data[sindex_U_rms+i]);
   datafile->offpulse_rms[0+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] += (rms_file->data[sindex_I_rms+i])*(rms_file->data[sindex_I_rms+i]);
   datafile->offpulse_rms[1+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] += (newdata_rms[i+newindex_L_rms])*(newdata_rms[i+newindex_L_rms]);
   datafile->offpulse_rms[2+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] += (rms_file->data[sindex_V_rms+i])*(rms_file->data[sindex_V_rms+i]);
   if(extended) {
     datafile->offpulse_rms[5+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] += (newdata_rms[i+newindex_L_rms])*(newdata_rms[i+newindex_L_rms]) + (rms_file->data[sindex_V_rms+i])*(rms_file->data[sindex_V_rms+i]);
   }
   Loffpulse[NrOffpulseBins-1] = newdata_rms[i+newindex_L_rms];
   if(extended)
     Poffpulse[NrOffpulseBins-1] = newdata_rms[i+newindex_T_rms];
 }
      }
      baseline_intensity /= (float)NrOffpulseBins;
      RMSQ = sqrt(RMSQ/(float)NrOffpulseBins);
      RMSU = sqrt(RMSU/(float)NrOffpulseBins);
      datafile->offpulse_rms[0+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = sqrt(datafile->offpulse_rms[0+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]/(float)NrOffpulseBins);
      datafile->offpulse_rms[1+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = sqrt(datafile->offpulse_rms[1+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]/(float)NrOffpulseBins);
      datafile->offpulse_rms[2+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = sqrt(datafile->offpulse_rms[2+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]/(float)NrOffpulseBins);
      if(extended) {
 datafile->offpulse_rms[5+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] = sqrt(datafile->offpulse_rms[5+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]/(float)NrOffpulseBins);
      }
      if(rms_file_specified) {
 float scale = 1.0/sqrt(rebin_factor);
 RMSQ *= scale;
 RMSU *= scale;
 datafile->offpulse_rms[0+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] *= scale;
 datafile->offpulse_rms[1+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] *= scale;
 datafile->offpulse_rms[2+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] *= scale;
 if(extended) {
   datafile->offpulse_rms[5+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)] *= scale;
 }
      }
      if(verbose.verbose) {
 if((freqnr == 0 && pulsenr == 0) || verbose.debug) {
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "  PA conversion output for subint %ld frequency channel %ld:\n", pulsenr, freqnr);
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "    Average baseline Stokes I: %f\n", baseline_intensity);
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "    RMS I:                  %f\n", datafile->offpulse_rms[0+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]);
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "    RMS Q:                  %f\n", RMSQ);
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "    RMS U:                  %f\n", RMSU);
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "    RMS V:                  %f\n", datafile->offpulse_rms[2+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]);
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "    RMS L (before de-bias): %f\n", datafile->offpulse_rms[1+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]);
   if(extended) {
     for(indent = 0; indent < verbose.indent; indent++) printf(" ");
     fprintf(stdout, "    RMS sqrt(Q^2+U^2+V^2):  %f\n", datafile->offpulse_rms[5+output_nr_pols*(freqnr + datafile->NrFreqChan*pulsenr)]);
   }
 }
      }
      gsl_sort_float (Loffpulse, 1, NrOffpulseBins);
      medianL = gsl_stats_float_median_from_sorted_data(Loffpulse, 1, NrOffpulseBins);
      fflush(stdout);
      if((verbose.verbose && freqnr == 0 && pulsenr == 0) || verbose.debug) {
 for(indent = 0; indent < verbose.indent; indent++) printf(" ");
 fprintf(stdout, "    Median L: %f\n", medianL);
      }
      if(extended) {
 gsl_sort_float (Poffpulse, 1, NrOffpulseBins);
 medianP = gsl_stats_float_median_from_sorted_data(Poffpulse, 1, NrOffpulseBins);
 fflush(stdout);
 if((verbose.verbose && freqnr == 0 && pulsenr == 0) || verbose.debug) {
   for(indent = 0; indent < verbose.indent; indent++) printf(" ");
   fprintf(stdout, "    Median sqrt(Q^2+U^2+V^2): %f\n", medianP);
 }
      }
      for(j = 0; j < (datafile->NrBins); j++) {
 if(correctLbias == 1) {
   float junk = (0.5*(RMSQ+RMSU)/newdata[j+newindex_L]);
   if(junk < 1)
     newdata[j+newindex_L] *= sqrt(1.0-junk*junk);
   else
     newdata[j+newindex_L] = 0.0;
 }else if(correctLbias == 0) {
   newdata[j+newindex_L] -= medianL;
 }
 if(extended)
   newdata[j+newindex_T] -= medianP;
      }
      for(i = 0; i < (datafile->NrBins); i++) {
 newdata[i+newindex_Pa] = 90.0*atan2(datafile->data[sindex_U+i],datafile->data[sindex_Q+i])/M_PI;
 if(paoffset) {
   newdata[i+newindex_Pa] += paoffset;
   newdata[i+newindex_Pa] = derotate_180_small_double(newdata[i+newindex_Pa]);
 }
 if(datafile->data[i+sindex_Q] != 0 || datafile->data[i+sindex_U] != 0) {
   newdata[i+newindex_dPa] = sqrt((datafile->data[sindex_Q+i]*RMSU)*(datafile->data[sindex_Q+i]*RMSU) + (datafile->data[sindex_U+i]*RMSQ)*(datafile->data[sindex_U+i]*RMSQ));
   newdata[i+newindex_dPa] /= 2.0*(datafile->data[i+sindex_Q]*datafile->data[i+sindex_Q] + datafile->data[i+sindex_U]*datafile->data[i+sindex_U]);
   newdata[i+newindex_dPa] *= 180.0/M_PI;
 }else {
   newdata[i+newindex_dPa] = 0;
 }
 if(extended) {
   newdata[i+newindex_Ell] = 0;
     newdata[i+newindex_dEll] = 0;
 }
      }
      for(j = 0; j < (datafile->NrBins); j++) {
 newdata[j+newindex_I] = datafile->data[j+sindex_I];
 newdata[j+newindex_V] = datafile->data[j+sindex_V];
      }
    }
  }
  free(datafile->data);
  datafile->data = newdata;
  if(rms_file_specified) {
    free(newdata_rms);
  }
  if(nolongitudes == 0) {
    datafile->tsampMode = TSAMPMODE_LONGITUDELIST;
  }
  datafile->NrPols = output_nr_pols;
  if(extended) {
    datafile->poltype = POLTYPE_ILVPAdPATEldEl;
  }else {
    datafile->poltype = POLTYPE_ILVPAdPA;
  }
  free(Loffpulse);
  free(Poffpulse);
  return 1;
}
int writePPOLHeader(datafile_definition datafile, int argc, char **argv, verbose_definition verbose)
{
  char *txt;
  txt = malloc(10000);
  if(txt == NULL) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR writePPOLHeader: Memory allocation error.");
    return 0;
  }
  constructCommandLineString(txt, 10000, argc, argv, verbose);
  fprintf(datafile.fptr_hdr, "#ppol file: %s\n", txt);
  free(txt);
  return 1;
}
int readPPOLHeader(datafile_definition *datafile, int extended, verbose_definition verbose)
{
  float dummy_float;
  int ret, maxlinelength, nrwords;
  char *txt, *ret_ptr, *word_ptr;
  maxlinelength = 2000;
  txt = malloc(maxlinelength);
  if(txt == NULL) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readPPOLHeader: Memory allocation error.");
    return 0;
  }
  datafile->isFolded = 1;
  datafile->foldMode = FOLDMODE_FIXEDPERIOD;
  datafile->fixedPeriod = 0;
  datafile->tsampMode = TSAMPMODE_LONGITUDELIST;
  datafile->fixedtsamp = 0;
  datafile->tsubMode = TSUBMODE_FIXEDTSUB;
  if(datafile->tsub_list != NULL)
    free(datafile->tsub_list);
  datafile->tsub_list = (double *)malloc(sizeof(double));
  if(datafile->tsub_list == NULL) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readPPOLHeader: Memory allocation error");
    return 0;
  }
  datafile->tsub_list[0] = 0;
  datafile->NrSubints = 1;
  datafile->NrFreqChan = 1;
  datafile->datastart = 0;
  rewind(datafile->fptr);
  ret = fread(txt, 1, 3, datafile->fptr);
  txt[3] = 0;
  if(ret != 3) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readPPOLHeader: cannot read from file.");
    free(txt);
    return 0;
  }
  if(strcmp(txt, "#pp") != 0
) {
    fflush(stdout);
    printwarning(verbose.debug, "WARNING readPPOLHeader: File does not appear to be in PPOL or PPOLSHORT format. I will try to load file, but this will probably fail. Did you run ppol first?");
  }
  skipallhashedlines(datafile);
  datafile->NrBins = 0;
  dummy_float = 0;
  do {
    ret_ptr = fgets(txt, maxlinelength, datafile->fptr);
    if(ret_ptr != NULL) {
      if(txt[0] != '#') {
 if(extended) {
   word_ptr = pickWordFromString(txt, 2, &nrwords, 1, ' ', verbose);
   if(nrwords != 10 && nrwords != 14) {
     fflush(stdout);
     printerror(verbose.debug, "ERROR readPPOLHeader: Line should have 10 or 14 words, got %d", nrwords);
     if(nrwords == 3)
       printerror(verbose.debug, "                             Maybe file is in format %s?", returnFileFormat_str(PPOL_SHORT_format));
     printerror(verbose.debug, "                             Line: '%s'.", txt);
     free(txt);
     return 0;
   }
   if(nrwords == 10) {
     datafile->poltype = POLTYPE_ILVPAdPA;
     datafile->NrPols = 5;
   }else {
     datafile->poltype = POLTYPE_ILVPAdPATEldEl;
     datafile->NrPols = 8;
   }
 }else {
   word_ptr = pickWordFromString(txt, 1, &nrwords, 1, ' ', verbose);
   if(nrwords != 3) {
     fflush(stdout);
     printerror(verbose.debug, "ERROR readPPOLHeader: Line should have 3 words, got %d", nrwords);
     if(nrwords == 10)
       printerror(verbose.debug, "                             Maybe file is in format %s?", returnFileFormat_str(PPOL_format));
     printerror(verbose.debug, "                             Line: '%s'.", txt);
     free(txt);
     return 0;
   }
 }
 ret = sscanf(word_ptr, "%f", &dummy_float);
 if(ret != 1) {
   fflush(stdout);
   printerror(verbose.debug, "ERROR readPPOLHeader: Cannot interpret as a float: '%s'.", txt);
   free(txt);
   return 0;
 }
 if(dummy_float >= 360) {
   fflush(stdout);
   printwarning(verbose.debug, "WARNING: IGNORING POINTS AT PULSE LONGITUDES > 360 deg.");
 }else {
   (datafile->NrBins)++;
 }
      }
    }
  }while(ret_ptr != NULL && dummy_float < 360);
  if(extended == 0) {
    datafile->poltype = POLTYPE_PAdPA;
    datafile->NrPols = 2;
  }
  fflush(stdout);
  if(verbose.verbose) fprintf(stdout, "Going to load %ld points from %s\n", datafile->NrBins, datafile->filename);
  if(datafile->NrBins == 0) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readPPOLHeader: No data in %s", datafile->filename);
    free(txt);
    return 0;
  }
  fseek(datafile->fptr, datafile->datastart, SEEK_SET);
  free(txt);
  if(datafile->offpulse_rms != NULL) {
    free(datafile->offpulse_rms);
    datafile->offpulse_rms = NULL;
  }
  if(extended) {
    datafile->offpulse_rms = (float *)malloc(datafile->NrSubints*datafile->NrFreqChan*datafile->NrPols*sizeof(float));
    if(datafile->offpulse_rms == NULL) {
      fflush(stdout);
      printerror(verbose.debug, "ERROR readPPOLHeader: Memory allocation error");
      return 0;
    }
  }
  datafile->tsamp_list = (double *)malloc(datafile->NrBins*sizeof(double));
  if(datafile->tsamp_list == NULL) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readPPOLHeader: Memory allocation error");
    return 0;
  }
  return 1;
}
int readPPOLfile(datafile_definition *datafile, float *data, int extended, float add_longitude_shift, verbose_definition verbose)
{
  int maxlinelength;
  long i, k, dummy_long;
  char *txt, *ret_ptr;
  if(datafile->NrBins == 0) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readPPOLfile: No data in %s", datafile->filename);
    return 0;
  }
  maxlinelength = 2000;
  txt = malloc(maxlinelength);
  if(txt == NULL) {
    fflush(stdout);
    printerror(verbose.debug, "ERROR readPPOLfile: Memory allocation error.");
    return 0;
  }
  fseek(datafile->fptr, datafile->datastart, SEEK_SET);
  k = 0;
  if(extended) {
    datafile->offpulse_rms[3] = -1;
    datafile->offpulse_rms[4] = -1;
    if(datafile->NrPols == 8) {
      datafile->offpulse_rms[6] = -1;
      datafile->offpulse_rms[7] = -1;
    }
  }
  for(i = 0; i < datafile->NrBins; i++) {
    ret_ptr = fgets(txt, maxlinelength, datafile->fptr);
    if(ret_ptr == NULL) {
      fflush(stdout);
      printerror(verbose.debug, "ERROR readPPOLfile: Cannot read next line, should not happen after successfully reading in header");
      free(txt);
      return 0;
    }
    if(txt[0] != '#') {
      if(extended == 0) {
 sscanf(txt, "%lf %f %f", &(datafile->tsamp_list[k]), &(data[k]), &(data[k+datafile->NrBins]));
      }else {
 if(datafile->NrPols == 8) {
   sscanf(txt, "%ld %lf %f %f %f %f %f %f %f %f %f %f %f %f", &dummy_long, &(datafile->tsamp_list[k]), &(data[k]), &(datafile->offpulse_rms[0]), &(data[k+datafile->NrBins]), &(datafile->offpulse_rms[1]), &(data[k+2*datafile->NrBins]), &(datafile->offpulse_rms[2]), &(data[k+3*datafile->NrBins]), &(data[k+4*datafile->NrBins]), &(data[k+5*datafile->NrBins]), &(datafile->offpulse_rms[5]), &(data[k+6*datafile->NrBins]), &(data[k+7*datafile->NrBins]));
 }else {
   sscanf(txt, "%ld %lf %f %f %f %f %f %f %f %f", &dummy_long, &(datafile->tsamp_list[k]), &(data[k]), &(datafile->offpulse_rms[0]), &(data[k+datafile->NrBins]), &(datafile->offpulse_rms[1]), &(data[k+2*datafile->NrBins]), &(datafile->offpulse_rms[2]), &(data[k+3*datafile->NrBins]), &(data[k+4*datafile->NrBins]));
 }
      }
      datafile->tsamp_list[k] += add_longitude_shift;
      if(datafile->tsamp_list[k] >= 0 && datafile->tsamp_list[k] < 360) {
   k++;
      }else {
 fflush(stdout);
 printwarning(verbose.debug, "WARNING readPPOLfile: IGNORING POINTS AT PULSE LONGITUDES > 360 deg.");
      }
    }
  }
  if(k != datafile->NrBins) {
    fflush(stdout);
    printerror(verbose.debug, "WARNING readPPOLfile: The nr of bins read in is different as determined from header. Something is wrong.");
    return 0;
  }
  fflush(stdout);
  if(verbose.verbose) fprintf(stdout, "readPPOLfile: Accepted %ld points\n", datafile->NrBins);
     free(txt);
  return 1;
}
int writePPOLfile(datafile_definition datafile, float *data, int extended, int onlysignificantPA, int twoprofiles, float PAoffset, verbose_definition verbose)
{
  long j;
  if(datafile.poltype != POLTYPE_ILVPAdPA && datafile.poltype != POLTYPE_PAdPA && datafile.poltype != POLTYPE_ILVPAdPATEldEl) {
    printerror(verbose.debug, "ERROR writePPOLfile: Data doesn't appear to have poltype ILVPAdPA, PAdPA or ILVPAdPATEldEl (it is %d).", datafile.poltype);
    return 0;
  }
  if(datafile.poltype == POLTYPE_ILVPAdPA && datafile.NrPols != 5) {
    printerror(verbose.debug, "ERROR writePPOLfile: 5 polarization channels were expected, but there are %ld.", datafile.NrPols);
    return 0;
  }else if(datafile.poltype == POLTYPE_PAdPA && datafile.NrPols != 2) {
    printerror(verbose.debug, "ERROR writePPOLfile: 2 polarization channels were expected, but there are %ld.", datafile.NrPols);
    return 0;
  }else if(datafile.poltype == POLTYPE_ILVPAdPATEldEl && datafile.NrPols != 8) {
    printerror(verbose.debug, "ERROR writePPOLfile: 8 polarization channels were expected, but there are %ld.", datafile.NrPols);
    return 0;
  }
  if(datafile.NrSubints > 1 || datafile.NrFreqChan > 1) {
    printerror(verbose.debug, "ERROR writePPOLfile: Can only do this opperation if there is one subint and one frequency channel.");
    return 0;
  }
  if(datafile.tsampMode != TSAMPMODE_LONGITUDELIST) {
    printerror(verbose.debug, "ERROR writePPOLfile: Expected pulse longitudes to be defined.");
    return 0;
  }
  int pa_offset, dpa_offset;
  if(datafile.poltype == POLTYPE_ILVPAdPA || datafile.poltype == POLTYPE_ILVPAdPATEldEl) {
    pa_offset = 3;
    dpa_offset = 4;
  }else if(datafile.poltype == POLTYPE_PAdPA) {
    pa_offset = 0;
    dpa_offset = 1;
  }
  for(j = 0; j < datafile.NrBins; j++) {
    if(data[j+dpa_offset*datafile.NrBins] > 0 || onlysignificantPA == 0) {
      if(extended) {
 fprintf(datafile.fptr, "%ld %e %e %e %e %e %e %e %e %e", j, datafile.tsamp_list[j], data[j], datafile.offpulse_rms[0], data[j+datafile.NrBins], datafile.offpulse_rms[1], data[j+2*datafile.NrBins], datafile.offpulse_rms[2], data[j+pa_offset*datafile.NrBins]+PAoffset, data[j+dpa_offset*datafile.NrBins]);
 if(datafile.poltype == POLTYPE_ILVPAdPATEldEl)
   fprintf(datafile.fptr, " %e %e %e %e", data[j+5*datafile.NrBins], datafile.offpulse_rms[5], data[j+6*datafile.NrBins], data[j+7*datafile.NrBins]);
 fprintf(datafile.fptr, "\n");
      }else {
 fprintf(datafile.fptr, "%e %e %e\n", datafile.tsamp_list[j], data[j+pa_offset*datafile.NrBins]+PAoffset, data[j+dpa_offset*datafile.NrBins]);
      }
    }
  }
  if(twoprofiles) {
    for(j = 0; j < datafile.NrBins; j++) {
      if(data[j+dpa_offset*datafile.NrBins] > 0 || onlysignificantPA == 0) {
 if(extended) {
   fprintf(datafile.fptr, "%ld %e %e %e %e %e %e %e %e %e", j, datafile.tsamp_list[j]+360, data[j], datafile.offpulse_rms[0], data[j+datafile.NrBins], datafile.offpulse_rms[1], data[j+2*datafile.NrBins], datafile.offpulse_rms[2], data[j+pa_offset*datafile.NrBins]+PAoffset, data[j+dpa_offset*datafile.NrBins]);
   if(datafile.poltype == POLTYPE_ILVPAdPATEldEl)
     fprintf(datafile.fptr, " %e %e %e %e", data[j+5*datafile.NrBins], datafile.offpulse_rms[5], data[j+6*datafile.NrBins], data[j+7*datafile.NrBins]);
   fprintf(datafile.fptr, "\n");
 }else {
   fprintf(datafile.fptr, "%e %e %e\n", datafile.tsamp_list[j]+360, data[j]+PAoffset, data[j+dpa_offset*datafile.NrBins]);
 }
      }
    }
  }
  return 1;
}


/*
 *  Copyright (C) 2021 FISCO BCOS.
 *  SPDX-License-Identifier: Apache-2.0
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 * @file LogEntry.h
 * @author: yujiechen
 * @date: 2021-03-18
 */
#pragma once
#include "../interfaces/crypto/CryptoSuite.h"
#include "../libutilities/Common.h"
#include "../libutilities/FixedBytes.h"
#include <gsl/span>

namespace bcos
{
namespace protocol
{
class LogEntry
{
public:
    using Ptr = std::shared_ptr<LogEntry>;
    LogEntry() = default;
    LogEntry(bytes const& _address, h256s _topics, bytes _data)
      : m_address(_address), m_topics(std::move(_topics)), m_data(std::move(_data))
    {}

    ~LogEntry() {}

    std::string_view address() const
    {
        return std::string_view((char*)m_address.data(), m_address.size());
    }
    gsl::span<const h256> topics() const { return gsl::span(m_topics.data(), m_topics.size()); }
    bytesConstRef data() const { return ref(m_data); }
    // Define the scale decode method, which cannot be modified at will
    template <class Stream, typename = std::enable_if_t<Stream::is_decoder_stream>>
    friend Stream& operator>>(Stream& _stream, LogEntry& _logEntry)
    {
        return _stream >> _logEntry.m_address >> _logEntry.m_topics >> _logEntry.m_data;
    }

    // Define the scale encode method, which cannot be modified at will
    template <class Stream, typename = std::enable_if_t<Stream::is_encoder_stream>>
    friend Stream& operator<<(Stream& _stream, LogEntry const& _logEntry)
    {
        return _stream << _logEntry.m_address << _logEntry.m_topics << _logEntry.m_data;
    }

private:
    bcos::bytes m_address;
    bcos::h256s m_topics;
    bytes m_data;
};

using LogEntries = std::vector<LogEntry>;
using LogEntriesPtr = std::shared_ptr<std::vector<LogEntry>>;
}  // namespace protocol
}  // namespace bcos


/*
   Copyright [2019-2021] [IBM Corporation]
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
       http://www.apache.org/licenses/LICENSE-2.0
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#ifndef __CCPM_INTERFACES_H__
#define __CCPM_INTERFACES_H__

#include <common/byte_span.h>
#include <common/types.h>
#include <gsl/pointers>
#include <cstddef>
#include <functional>
#include <vector>

namespace ccpm
{
	struct persister
	{
		using byte_span = common::byte_span;
		virtual void persist(byte_span span) = 0;
	protected:
		~persister() {}
	};

/*
 * Ownership callback can resolve the ambiguity about area
 *        ownership which occurs during phase (b) of allocate() and free()
 *        Intended to be used by IHeap::reconstitute when a crash during
 *        an IHeap::allocate or IHeap::free call has left area ownership
 *        in doubt.
 *
 * In order to support ownership resolution, it is expected that a caller to
 * allocate or free will
 *   (1) store and persist the initial value of ptr
 *   (2) call allocate or free
 *
 * It is expected that IHeap::allocate() will
 *   (1) determine an appropriate new value for ptr
 *   (2) persist a note indicating that the allocation status of the new value
 *       of ptr is indeterminate.
 *   (3) store and persist the new value of ptr
 *   (4) persist an invalidation of the note written in step 2.
 *
 * It is expected that IHeap::free() will
 *   (1) persist a note indicating that the allocation status of the value
 *       of ptr is indeterminate.
 *   (2) store and persist nullptr as the new value of ptr
 *   (3) return the area located by the original value of ptr to free status
 *   (4) persist an invalidation of the note written in step 1.
 */

using ownership_callback_type = std::function<bool(const void * ptr)>;
using ownership_callback_t = ownership_callback_type;

inline bool accept_all(const void *) { return true; }
/*
 * Allocators can expand to more than one coarse-grained region of
 * memory.
 */
struct region_vector_t : public std::vector<common::byte_span>
{
	using base = std::vector<common::byte_span>;
  explicit region_vector_t(void * ptr_, std::size_t size_)
    : region_vector_t(common::make_byte_span(static_cast<common::byte *>(ptr_), size_))
  {}
  explicit region_vector_t(const value_type &v) {
    push_back(v);
  }
  region_vector_t() {
  }
	const base &cbase() const { return *this; }
};

using region_span = gsl::span<common::byte_span>;

enum class Type_id : int64_t
  {
    None        = 0,
    Fixed_array = 0xF0,
  };

/**
 * Heap allocator (variable sized allocations)
 */
class IHeap
{
public:
  virtual ~IHeap() {}

  /* Reconstitute/initialize slab from existing memory
   *
   * @param regions Pointer/length regions of contiguous memory
   * @param resolver Access to an object which can resolve the ambiguity
   *        over area ownership which occurs during a phase of allocate()
   *        and free() calls. If the callee of the ownership_callback_t
   *        function might own the area located by the arument, the callee
   *        must return true. If the callee does not own the area, it should
   *        return false.
   * @param force_init If true, force re-setting to empty
   *
   *
   * @return : True if memory was reset to empty
   **/
  virtual bool reconstitute(const region_span regions,
                            ownership_callback_t resolver = [] (const void *) -> bool { return true; },
                            const bool force_init = false) = 0;

  /* Allocate memory
   *
   * @param ptr in/out: nullptr -> pointer to newly allocated memory
   *                    On a successful allocation the allocator will
   *                    first write and then persist ptr. Allocation
   *                    has three phases:
   *                    (a) ptr is not yet written; the allocator unambiguously
   *                        owns the free "area" the address of which it will
   *                        later write into ptr.
   *                    (b) allocator has written ptr, but has not yet persisted
   *                        ptr. Caller must be able to tell the allocator (see
   *                        reconstitute) whether caller has accepted ownership
   *                        of the allocated area. Acceptance is implicit.
   *                        Visibility of a non-null value in ptr, which happens
   *                        upon write in the non-crash case and upon persist in
   *                        the crash+reconstitute case, consititutes acceptance.
   *                    (c) allocator has persisted the ptr. Caller will know upon
   *                        successful return (normal case) or discovery of a
   *                        non-null value (crash+reconstitute case) that it owns
   *                        the area.
   *
   *                    The pointer shall be altered iff the function returns S_OK.
   *
   * @param size Size of memory to allocate in bytes
   * @param alignment Alignment for memory
   *
   * @return : S_OK, E_INVAL, E_BAD_PARAM, E_EMPTY
   **/
  virtual status_t allocate(void * & ptr,
                            std::size_t bytes,
                            std::size_t alignment) = 0;

  /* Free previously allocated memory.
   * @param ptr in/out: Pointer to memory to free, which must have been previously
   *                    allocated with the same size and alignment.
   *                    On a successful free the allocator will first nullify ptr
   *                    and then persist ptr. Free has has three phases, similar
   *                    to allocation:
   *                    (a) ptr is not yet written; the caller unambiguously
   *                        owns the "area".
   *                    (b) allocator has nullified ptr, but has not yet persisted
   *                        ptr. Caller must be able to tell the allocator (see
   *                        reconstitute) whether caller has relinquished ownership
   *                        of the allocated area. Relinquishment is implicit.
   *                        Visibility of a null value in ptr, which happens upon
   *                        write in the non-crash case and upon persist in the
   *                        crash+reconstitute case, consititutes relinquishment.
   *                    (c) allocator has persisted the (null-valued) ptr. Caller
   *                        will know upon successful return (normal case) or
   *                        discovery of null value (crash+reconstitute case) that
   *                        the allocator now owns the area.
   *
   *                    The pointer shall be altered iff the function returns S_OK.
   *
   * @param size Size of memory to free in bytes
   *
   * @return : S_OK, E_INVAL
   **/
  virtual status_t free(void * & ptr,
                        std::size_t bytes = 0) = 0;

  /* Return remaining space in bytes
   * @param out_size Size remaining in bytes
   *
   * @return S_OK or E_NOT_IMPL
   **/
  virtual status_t remaining(std::size_t& out_size) const = 0;

  /* Return a vector of all regions
   *
   * @return vector containing the initial regions and all subsequently added
   * regions, if the implementation keeps track of regions. Otherwise, an
   * empty region_vector_t.
   **/
  virtual region_vector_t get_regions() const = 0;
};

/**
 * Heap allocator (variable sized allocations)
 */
class IHeap_expandable : public IHeap
{
public:
  /* Add an additional regions to the heap
   * @param regions Pointer/length regions of contiguous memory
   **/
  virtual void add_regions(const region_span regions) = 0;

  /** Test whether an address is in any heap region
   * @param addr the address to test
   *
   * @erturn true iff the address is in some heap region
   */
  virtual bool includes(const void *ptr) const = 0;
};

class ILog
{
public:
  virtual ~ILog() = default;

  /*
   * Record old value of a region to the log.
   */
  virtual void add(void *begin, std::size_t size) = 0;
  /*
   * Record an allocation to the log.
   */
  virtual void allocated(void *&p, std::size_t size) = 0;
  /*
   * Record a free to the log.
   */
  virtual void freed(void *&p, std::size_t size) = 0;

  /*
   * commit all previous add/allocated/freed commands
   */
  virtual void commit() = 0;
  /*
   * Restore all data areas added after initialization (or the most recent clear)
   * to the values present at their last add command.
   */
  virtual void rollback() = 0;

};
}
#endif //  __CCPM_INTERFACES_H__


#ifndef SPC_FITSCARDS_H
#define SPC_FITSCARDS_H

#include <stdlib.h>

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>

#include "aXe_grism.h"
#include "fitsio.h"
#include "disp_conf.h"
#include "aper_conf.h"

typedef struct
{
  int n;          /* Number of cards in the **cards array */
  char **cards;   /* A pointer to an array of strings     */
} FITScards;

extern FITScards *
allocate_FITScards(int n);

extern void
free_FITScards(FITScards *cards);

extern FITScards *
get_WCS_FITScards(const double rval1, const double delta1, const double rval2);

extern FITScards *
beam_to_FITScards(object *ob, int beamnum);

extern FITScards *
stpmin_to_FITScards(d_point stp_min);

extern FITScards *
drzinfo_to_FITScards(object *ob, int beamnum, d_point outref,
		     aperture_conf *conf, gsl_matrix *drizzcoeffs,
		     int trlength, double relx, double rely,
		     double objwidth, d_point refwave_pos,
		     float sky_cps, double drizzle_width,
		     double cdcorr, double spcorr);

extern FITScards *
nicbck_info_to_FITScards(const double skypix_frac, const double scale_factor,
			 const double offset);

extern FITScards *
dispstruct_to_FITScards(dispstruct *disp);

extern void
update_contam_model(fitsfile *fptr, char model_name[]);

extern int
check_quantitative_contamination(fitsfile *fptr);

extern void
transport_cont_modname(char grism_file_path[], char PET_file_path[]);

#endif