/*
FRACTAL - A program growing fractals to benchmark parallelization and drawing
libraries.

Copyright 2009-2021, Javier Burguete Tolosa.

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.

THIS SOFTWARE IS PROVIDED BY Javier Burguete Tolosa ``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 Javier Burguete Tolosa 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.
*/

/**
 * \file simulator.c
 * \brief Source file to define the windows data and functions.
 * \author Javier Burguete Tolosa.
 * \copyright Copyright 2009-2021, Javier Burguete Tolosa.
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <gsl/gsl_rng.h>
#include <glib.h>
#include <libintl.h>
#include <png.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <gtk/gtk.h>
#include <GL/glew.h>
#if HAVE_FREEGLUT
#include <GL/freeglut.h>
#elif HAVE_SDL
#include <SDL.h>
#elif HAVE_GLFW
#include <GLFW/glfw3.h>
extern GLFWwindow *window;
#endif

#include "config.h"
#include "fractal.h"
#include "image.h"
#include "text.h"
#include "graphic.h"
#include "draw.h"
#include "simulator.h"

#if HAVE_SDL
SDL_Event exit_event[1];
#endif

static float phid = -45.;       ///< Horizontal perspective angle (in degrees).
static float thetad = 80.;      ///< Vertical perspective angle (in degrees).

/**
 * Function the set the perspective of a point.
 */
static void
perspective (int x,             ///< Point x-coordinate.
             int y,             ///< Point y-coordinate.
             int z,             ///< Point z-coordinate.
             float *X,          ///< Perspective X-coordinate.
             float *Y)          ///< Perspective Y-coordinate.
{
  float cp, sp, st, ct;
  sincosf (graphic->phi, &sp, &cp);
  sincosf (graphic->theta, &st, &ct);
  *X = x * cp - y * sp;
  *Y = z * st + (y * cp + x * sp) * ct;
}

/**
 * Function to set the view perspective.
 */
static void
set_perspective ()
{
  float k1, k2;
  phid = gtk_range_get_value (GTK_RANGE (dialog_simulator->hscale));
  thetad = gtk_range_get_value (GTK_RANGE (dialog_simulator->vscale));
  graphic->phi = phid * M_PI / 180.;
  graphic->theta = thetad * M_PI / 180.;
  if (fractal_3D)
    {
      perspective (0, 0, 0, &k1, &k2);
      graphic->xmin = k1;
      perspective (length, width, 0, &k1, &k2);
      graphic->xmax = k1;
      perspective (length, 0, 0, &k1, &k2);
      graphic->ymin = k2;
      perspective (0, width, height, &k1, &k2);
      graphic->ymax = k2;
    }
  draw ();
}

/**
 * Function to show an error message
 */
void
show_error (const char *message)        ///< Error message string.
{
  GtkMessageDialog *dlg;
  dlg = (GtkMessageDialog *)
    gtk_message_dialog_new (dialog_simulator->window,
                            GTK_DIALOG_DESTROY_WITH_PARENT,
                            GTK_MESSAGE_ERROR,
                            GTK_BUTTONS_CLOSE, "%s", message);
#if !GTK4
  gtk_widget_show_all (GTK_WIDGET (dlg));
  g_signal_connect_swapped (dlg, "response", G_CALLBACK (gtk_widget_destroy),
                            GTK_WIDGET (dlg));
#else
  gtk_widget_show (GTK_WIDGET (dlg));
  g_signal_connect_swapped (dlg, "response", G_CALLBACK (gtk_window_destroy),
                            GTK_WINDOW (dlg));
#endif
}

/**
 * Function to update the enabled fractal characteristics.
 */
static void
dialog_options_update ()
{
  int i;
  DialogOptions *dlg = dialog_options;
  i = gtk_check_button_get_active (dlg->button_3D);
  gtk_widget_set_sensitive (GTK_WIDGET (dlg->label_length), i);
  gtk_widget_set_sensitive (GTK_WIDGET (dlg->entry_length), i);
  for (i = 0; i < N_RANDOM_SEED_TYPES; ++i)
    if (gtk_check_button_get_active (dlg->array_seeds[i]))
      break;
  i = (i == RANDOM_SEED_TYPE_FIXED) ? 1 : 0;
  gtk_widget_set_sensitive (GTK_WIDGET (dlg->label_seed), i);
  gtk_widget_set_sensitive (GTK_WIDGET (dlg->entry_seed), i);
}

/**
 * Function to close a dialog to set the fractal options.
 */
static void
dialog_options_close (DialogOptions * dlg,      ///< DialogOptions struct.
                      int response_id)  ///< Response identifier.
{
  unsigned int i;
  if (response_id == GTK_RESPONSE_OK)
    {
      fractal_diagonal = gtk_check_button_get_active (dlg->button_diagonal);
      fractal_3D = gtk_check_button_get_active (dlg->button_3D);
      for (i = 0; i < N_FRACTAL_TYPES; ++i)
        if (gtk_check_button_get_active (dlg->array_fractals[i]))
          fractal_type = i;
      length = gtk_spin_button_get_value_as_int (dlg->entry_length);
      width = gtk_spin_button_get_value_as_int (dlg->entry_width);
      height = gtk_spin_button_get_value_as_int (dlg->entry_height);
#if HAVE_GTKGLAREA
      gtk_widget_set_size_request (GTK_WIDGET (dialog_simulator->gl_area),
                                   width, height);
#endif
      random_seed = gtk_spin_button_get_value_as_int (dlg->entry_seed);
      nthreads = gtk_spin_button_get_value_as_int (dlg->entry_nthreads);
      animating = gtk_check_button_get_active (dlg->button_animate);
      for (i = 0; i < N_RANDOM_TYPES; ++i)
        if (gtk_check_button_get_active (dlg->array_algorithms[i]))
          random_algorithm = i;
      for (i = 0; i < N_RANDOM_SEED_TYPES; ++i)
        if (gtk_check_button_get_active (dlg->array_seeds[i]))
          random_seed_type = i;
      medium_start ();
      set_perspective ();
      breaking = 1;
    }
  else if (response_id == GTK_RESPONSE_CANCEL);
  else
    return;
#if !GTK4
  gtk_widget_destroy (GTK_WIDGET (dlg->dialog));
#else
  gtk_window_destroy (GTK_WINDOW (dlg->dialog));
#endif
}

/**
 * Function to create a dialog to set the fractal options.
 */
static void
dialog_options_create ()
{
  unsigned int i;
  const char *array_fractals[N_FRACTAL_TYPES] =
    { _("_Tree"), _("_Forest"), _("_Neuron") };
  const char *array_algorithms[N_RANDOM_TYPES] = {
    "_mt19937",
    "_ranlxs0",
    "r_anlxs1",
    "ra_nlxs2",
    "ran_lxd1",
    "ranlx_d2",
    "ranlu_x",
    "ranlux_389",
    "_cmrg",
    "mr_g",
    "_taus2",
    "g_fsr4"
  };
  const char *array_seeds[N_RANDOM_SEED_TYPES] =
    { _("_Default"), _("_Clock based"), _("_Fixed") };
  DialogOptions *dlg = dialog_options;
#if !GTK4
  GtkContainer *content;
#else
  GtkBox *content;
#endif

  dlg->button_diagonal = (GtkCheckButton *) gtk_check_button_new_with_mnemonic
    (_("_Diagonal movement"));
  dlg->button_3D =
    (GtkCheckButton *) gtk_check_button_new_with_mnemonic ("3_D");
  g_signal_connect (dlg->button_3D, "clicked", dialog_options_update, NULL);
  dlg->label_length = (GtkLabel *) gtk_label_new (_("Length"));
  dlg->label_width = (GtkLabel *) gtk_label_new (_("Width"));
  dlg->label_height = (GtkLabel *) gtk_label_new (_("Height"));
  dlg->label_seed = (GtkLabel *) gtk_label_new (_("Random seed"));
  dlg->label_nthreads = (GtkLabel *) gtk_label_new (_("Threads number"));
  dlg->entry_length =
    (GtkSpinButton *) gtk_spin_button_new_with_range (32., 2400., 1.);
  dlg->entry_width =
    (GtkSpinButton *) gtk_spin_button_new_with_range (32., 2400., 1.);
  dlg->entry_height =
    (GtkSpinButton *) gtk_spin_button_new_with_range (20., 2400., 1.);
  dlg->entry_seed =
    (GtkSpinButton *) gtk_spin_button_new_with_range (0., 4294967295., 1.);
  dlg->entry_nthreads =
    (GtkSpinButton *) gtk_spin_button_new_with_range (0., 64., 1.);

  dlg->grid_fractal = (GtkGrid *) gtk_grid_new ();
  dlg->array_fractals[0] = NULL;
  for (i = 0; i < N_FRACTAL_TYPES; ++i)
    {
#if !GTK4
      dlg->array_fractals[i] =
        (GtkRadioButton *) gtk_radio_button_new_with_mnemonic_from_widget
        (dlg->array_fractals[0], array_fractals[i]);
#else
      dlg->array_fractals[i] = (GtkCheckButton *)
        gtk_check_button_new_with_mnemonic (array_fractals[i]);
      gtk_check_button_set_group (dlg->array_fractals[i],
                                  dlg->array_fractals[0]);
#endif
      gtk_grid_attach (dlg->grid_fractal, GTK_WIDGET (dlg->array_fractals[i]),
                       0, i, 1, 1);
    }
  gtk_check_button_set_active (dlg->array_fractals[fractal_type], 1);
  dlg->frame_fractal = (GtkFrame *) gtk_frame_new (_("Fractal type"));
  gtk_frame_set_child (dlg->frame_fractal, GTK_WIDGET (dlg->grid_fractal));

  dlg->button_animate = (GtkCheckButton *) gtk_check_button_new_with_mnemonic
    (_("_Animate"));
  gtk_check_button_set_active (dlg->button_animate, animating);

  dlg->grid_algorithm = (GtkGrid *) gtk_grid_new ();
  dlg->array_algorithms[0] = NULL;
  for (i = 0; i < N_RANDOM_TYPES; ++i)
    {
#if !GTK4
      dlg->array_algorithms[i] =
        (GtkRadioButton *) gtk_radio_button_new_with_mnemonic_from_widget
        (dlg->array_algorithms[0], array_algorithms[i]);
#else
      dlg->array_algorithms[i] = (GtkCheckButton *)
        gtk_check_button_new_with_mnemonic (array_algorithms[i]);
      gtk_check_button_set_group (dlg->array_algorithms[i],
                                  dlg->array_algorithms[0]);
#endif
      gtk_grid_attach (dlg->grid_algorithm,
                       GTK_WIDGET (dlg->array_algorithms[i]), 0, i, 1, 1);
    }
  gtk_check_button_set_active (dlg->array_algorithms[random_algorithm], 1);
  dlg->frame_algorithm = (GtkFrame *) gtk_frame_new (_("Random algorithm"));
  gtk_frame_set_child (dlg->frame_algorithm, GTK_WIDGET (dlg->grid_algorithm));

  dlg->grid_seed = (GtkGrid *) gtk_grid_new ();
  dlg->array_seeds[0] = NULL;
  for (i = 0; i < N_RANDOM_SEED_TYPES; ++i)
    {
#if !GTK4
      dlg->array_seeds[i] =
        (GtkRadioButton *) gtk_radio_button_new_with_mnemonic_from_widget
        (dlg->array_seeds[0], array_seeds[i]);
#else
      dlg->array_seeds[i] = (GtkCheckButton *)
        gtk_check_button_new_with_mnemonic (array_seeds[i]);
      gtk_check_button_set_group (dlg->array_seeds[i], dlg->array_seeds[0]);
#endif
      gtk_grid_attach (dlg->grid_seed, GTK_WIDGET (dlg->array_seeds[i]),
                       0, i, 1, 1);
      g_signal_connect (dlg->array_seeds[i], "clicked", dialog_options_update,
                        NULL);
    }
  gtk_check_button_set_active (dlg->array_seeds[random_seed_type], 1);
  dlg->frame_seed = (GtkFrame *) gtk_frame_new (_("Random seed type"));
  gtk_frame_set_child (dlg->frame_seed, GTK_WIDGET (dlg->grid_seed));

  dlg->grid = (GtkGrid *) gtk_grid_new ();
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->button_diagonal), 0, 0, 2, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->button_3D), 0, 1, 2, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->label_length), 0, 2, 1, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->entry_length), 1, 2, 1, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->label_width), 0, 3, 1, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->entry_width), 1, 3, 1, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->label_height), 0, 4, 1, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->entry_height), 1, 4, 1, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->frame_fractal), 0, 5, 2, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->button_animate), 0, 6, 2, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->frame_algorithm), 2, 0, 1, 8);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->frame_seed), 0, 7, 2, 2);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->label_seed), 0, 9, 1, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->entry_seed), 1, 9, 1, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->label_nthreads), 0, 10, 1, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->entry_nthreads), 1, 10, 1, 1);

  dlg->logo = (GtkImage *) gtk_image_new_from_file ("logo2.png");

  dlg->bar = (GtkHeaderBar *) gtk_header_bar_new ();
#if !GTK4
  gtk_header_bar_set_title (dlg->bar, _("Options"));
  gtk_header_bar_set_subtitle (dlg->bar, _("Set the fractal options"));
  gtk_header_bar_set_show_close_button (dlg->bar, 1);
#endif
  gtk_header_bar_pack_start (dlg->bar, GTK_WIDGET (dlg->logo));

  dlg->dialog
    = (GtkDialog *) gtk_dialog_new_with_buttons (NULL,
                                                 dialog_simulator->window,
                                                 GTK_DIALOG_MODAL |
                                                 GTK_DIALOG_DESTROY_WITH_PARENT,
                                                 _("_OK"),
                                                 GTK_RESPONSE_OK,
                                                 _("_Cancel"),
                                                 GTK_RESPONSE_CANCEL, NULL);
  gtk_window_set_titlebar (GTK_WINDOW (dlg->dialog), GTK_WIDGET (dlg->bar));
#if !GTK4
  content = (GtkContainer *) gtk_dialog_get_content_area (dlg->dialog);
  gtk_container_add (content, GTK_WIDGET (dlg->grid));
  gtk_widget_show_all (GTK_WIDGET (dlg->dialog));
#else
  gtk_window_set_title (GTK_WINDOW (dlg->dialog), _("Options"));
  content = (GtkBox *) gtk_dialog_get_content_area (dlg->dialog);
  gtk_box_append (content, GTK_WIDGET (dlg->grid));
  gtk_widget_show (GTK_WIDGET (dlg->dialog));
#endif

  gtk_check_button_set_active (dlg->button_diagonal, fractal_diagonal);
  gtk_check_button_set_active (dlg->button_3D, fractal_3D);
  gtk_spin_button_set_value (dlg->entry_length, length);
  gtk_spin_button_set_value (dlg->entry_width, width);
  gtk_spin_button_set_value (dlg->entry_height, height);
  gtk_spin_button_set_value (dlg->entry_seed, random_seed);
  gtk_spin_button_set_value (dlg->entry_nthreads, nthreads);
  dialog_options_update ();

  g_signal_connect_swapped (dlg->dialog, "response",
                            G_CALLBACK (dialog_options_close), dlg);
}

/**
 * Function to show a help dialog.
 */
static void
dialog_simulator_help ()
{
  gchar *authors[] = {
    "Javier Burguete Tolosa (jburguete@eead.csic.es)",
    NULL
  };
  gtk_show_about_dialog (dialog_simulator->window,
                         "program_name", "Fractal",
                         "comments",
                         _("A program using growing fractals to benchmark "
                           "parallelization and drawing libraries"),
                         "authors", authors,
                         "translator-credits",
                         _("Javier Burguete Tolosa (jburguete@eead.csic.es)"),
                         "version", "3.4.15",
                         "copyright",
                         "Copyright 2009-2021 Javier Burguete Tolosa",
                         "license-type", GTK_LICENSE_BSD,
                         "logo", dialog_simulator->logo,
                         "website-label", _("Website"),
                         "website", "https://github.com/jburguete/fractal",
                         NULL);
}

/**
 * Function to update the enabled window properties.
 */
void
dialog_simulator_update ()
{
  gtk_widget_set_sensitive
    (GTK_WIDGET (dialog_simulator->button_start), !simulating);
  gtk_widget_set_sensitive
    (GTK_WIDGET (dialog_simulator->button_stop), simulating);
  set_perspective ();
}

/**
 * Function to show the fractal progress.
 */
void
dialog_simulator_progress ()
{
  GMainContext *context;
  register unsigned int k;
  register float x;
  if (fractal_3D)
    {
      switch (fractal_type)
        {
        case 0:
        case 1:
          x = max_d / (float) (height - 1);
          break;
        default:
          k = length;
          if (width < k)
            k = width;
          if (height < k)
            k = height;
          k = k / 2;
          if (k)
            --k;
          x = fmin (1., max_d / (float) k);
        }
    }
  else
    {
      switch (fractal_type)
        {
        case 0:
        case 1:
          x = max_d / (float) (height - 1);
          break;
        default:
          k = width;
          if (height < k)
            k = height;
          k = k / 2 - 1;
          x = fmin (1., max_d / (float) k);
        }
    }
  gtk_progress_bar_set_fraction (dialog_simulator->progress, x);
  gtk_spin_button_set_value
    (dialog_simulator->entry_time, difftime (time (NULL), t0));
  context = g_main_context_default ();
  while (g_main_context_pending (context))
    g_main_context_iteration (context, 0);
}

/**
 * Function to close the dialog to save the graphical view.
 */
static void
dialog_simulator_graphic_close (GtkFileChooserDialog * dlg,
                                ///< GtkFileChooserDialog struct.
                                int response_id)       ///< Response identifier.
{
  char *filename;
  if (response_id == GTK_RESPONSE_ACCEPT)
    filename = gtk_file_chooser_get_current_name (GTK_FILE_CHOOSER (dlg));
  else
    filename = NULL;
#if !GTK4
  gtk_widget_destroy (GTK_WIDGET (dlg));
#else
  gtk_window_destroy (GTK_WINDOW (dlg));
#endif
  if (filename)
    {
      graphic_save (filename);
      g_free (filename);
    }
}

/**
 * Function to save the graphical view on a PNG file.
 */
static void
dialog_simulator_graphic_save ()
{
  GtkFileChooserDialog *dlg;
  dlg = (GtkFileChooserDialog *)
    gtk_file_chooser_dialog_new (_("Save graphical"),
                                 dialog_simulator->window,
                                 GTK_FILE_CHOOSER_ACTION_SAVE,
                                 _("_Cancel"), GTK_RESPONSE_CANCEL,
                                 _("_Open"), GTK_RESPONSE_ACCEPT, NULL);
#if !GTK4
  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dlg), 1);
  gtk_widget_show_all (GTK_WIDGET (dlg));
#else
  gtk_widget_show (GTK_WIDGET (dlg));
#endif
  g_signal_connect (dlg, "response",
                    G_CALLBACK (dialog_simulator_graphic_close), NULL);
}

#if HAVE_GTKGLAREA

static void
dialog_simulator_draw_init (GtkGLArea * gl_area)
{
#if DEBUG
  printf ("dialog_simulator_draw_init: start\n");
#endif
  gtk_gl_area_make_current (gl_area);
  graphic_init (graphic, "logo.png");
#if DEBUG
  printf ("dialog_simulator_draw_init: end\n");
#endif
}

#elif HAVE_GLFW

/**
 * Function to close the GLFW window.
 */
static void
window_close ()
{
  glfwSetWindowShouldClose (window, GL_TRUE);
}

#endif

/**
 * Function to create the main window.
 */
void
dialog_simulator_create ()
{
  static char *tip_exit, *tip_options, *tip_start, *tip_stop, *tip_save,
    *tip_help;
  DialogSimulator *dlg;

#if DEBUG
  printf ("dialog_simulator_create: start\n");
#endif

  dlg = dialog_simulator;
#if HAVE_GTKGLAREA
  dlg->loop = g_main_loop_new (NULL, 0);
#elif HAVE_SDL
  exit_event->type = SDL_QUIT;
#endif

  tip_options = _("Fractal options");
  tip_start = _("Start fractal growing");
  tip_stop = _("Stop fractal growing");
  tip_save = _("Save graphical");
  tip_help = _("Help");
  tip_exit = _("Exit");

#if !GTK4
  dlg->logo = gtk_image_get_pixbuf
    (GTK_IMAGE (gtk_image_new_from_file ("logo.png")));
  dlg->logo_min = gtk_image_get_pixbuf
    (GTK_IMAGE (gtk_image_new_from_file ("logo2.png")));
#else
  dlg->logo = gtk_image_get_paintable
    (GTK_IMAGE (gtk_image_new_from_file ("logo.png")));
  dlg->logo_min = gtk_image_get_paintable
    (GTK_IMAGE (gtk_image_new_from_file ("logo2.png")));
#endif

  dlg->box = (GtkBox *) gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);

  dlg->button_options = (GtkButton *)
#if !GTK4
    gtk_button_new_from_icon_name ("preferences-system", GTK_ICON_SIZE_BUTTON);
#else
    gtk_button_new_from_icon_name ("preferences-system");
#endif
  gtk_widget_set_tooltip_text (GTK_WIDGET (dlg->button_options), tip_options);
  gtk_box_append (dlg->box, GTK_WIDGET (dlg->button_options));
  g_signal_connect_swapped
    (dlg->button_options, "clicked", dialog_options_create, dlg->logo_min);

  dlg->button_start = (GtkButton *)
#if !GTK4
    gtk_button_new_from_icon_name ("system-run", GTK_ICON_SIZE_BUTTON);
#else
    gtk_button_new_from_icon_name ("system-run");
#endif
  gtk_widget_set_tooltip_text (GTK_WIDGET (dlg->button_start), tip_start);
  gtk_box_append (dlg->box, GTK_WIDGET (dlg->button_start));
  g_signal_connect (dlg->button_start, "clicked", fractal, NULL);

  dlg->button_stop = (GtkButton *)
#if !GTK4
    gtk_button_new_from_icon_name ("process-stop", GTK_ICON_SIZE_BUTTON);
#else
    gtk_button_new_from_icon_name ("process-stop");
#endif
  gtk_widget_set_tooltip_text (GTK_WIDGET (dlg->button_stop), tip_stop);
  gtk_box_append (dlg->box, GTK_WIDGET (dlg->button_stop));
  g_signal_connect (dlg->button_stop, "clicked", fractal_stop, NULL);

  dlg->button_save = (GtkButton *)
#if !GTK4
    gtk_button_new_from_icon_name ("document-save", GTK_ICON_SIZE_BUTTON);
#else
    gtk_button_new_from_icon_name ("document-save");
#endif
  gtk_widget_set_tooltip_text (GTK_WIDGET (dlg->button_save), tip_save);
  gtk_box_append (dlg->box, GTK_WIDGET (dlg->button_save));
  g_signal_connect (dlg->button_save, "clicked", dialog_simulator_graphic_save,
                    NULL);

  dlg->button_help = (GtkButton *)
#if !GTK4
    gtk_button_new_from_icon_name ("help-about", GTK_ICON_SIZE_BUTTON);
#else
    gtk_button_new_from_icon_name ("help-about");
#endif
  gtk_widget_set_tooltip_text (GTK_WIDGET (dlg->button_help), tip_help);
  gtk_box_append (dlg->box, GTK_WIDGET (dlg->button_help));
  g_signal_connect (dlg->button_help, "clicked", dialog_simulator_help, NULL);

  dlg->button_exit = (GtkButton *)
#if !GTK4
    gtk_button_new_from_icon_name ("application-exit", GTK_ICON_SIZE_BUTTON);
#else
    gtk_button_new_from_icon_name ("application-exit");
#endif
  gtk_widget_set_tooltip_text (GTK_WIDGET (dlg->button_exit), tip_exit);
  gtk_box_append (dlg->box, GTK_WIDGET (dlg->button_exit));
#if HAVE_GTKGLAREA
  g_signal_connect_swapped (dlg->button_exit, "clicked",
                            (GCallback) g_main_loop_quit, dlg->loop);
#elif HAVE_FREEGLUT
  g_signal_connect (dlg->button_exit, "clicked", glutLeaveMainLoop, NULL);
#elif HAVE_SDL
  g_signal_connect_swapped (dlg->button_exit, "clicked",
                            (void (*)) SDL_PushEvent, exit_event);
#elif HAVE_GLFW
  g_signal_connect (dlg->button_exit, "clicked", (void (*)) window_close, NULL);
#endif

  dlg->label_time = (GtkLabel *) gtk_label_new (_("Calculating time"));
  dlg->entry_time =
    (GtkSpinButton *) gtk_spin_button_new_with_range (0., 1.e6, 0.1);
  gtk_widget_set_sensitive (GTK_WIDGET (dlg->entry_time), 0);

  dlg->progress = (GtkProgressBar *) gtk_progress_bar_new ();
  gtk_progress_bar_set_text (dlg->progress, _("Progress"));

  dlg->hscale = (GtkScale *) gtk_scale_new_with_range
    (GTK_ORIENTATION_HORIZONTAL, -90., 0., 1.);
  dlg->vscale = (GtkScale *) gtk_scale_new_with_range
    (GTK_ORIENTATION_HORIZONTAL, 0., 90., 1.);
  gtk_scale_set_digits (dlg->hscale, 0);
  gtk_scale_set_digits (dlg->vscale, 0);
  gtk_range_set_value (GTK_RANGE (dlg->hscale), phid);
  gtk_range_set_value (GTK_RANGE (dlg->vscale), thetad);
  g_signal_connect (dlg->hscale, "value-changed", set_perspective, NULL);
  g_signal_connect (dlg->vscale, "value-changed", set_perspective, NULL);

  dlg->label_horizontal = (GtkLabel *) gtk_label_new
    (_("Horizontal perspective angle (º)"));
  dlg->label_vertical = (GtkLabel *) gtk_label_new
    (_("Vertical perspective angle (º)"));

  dlg->grid = (GtkGrid *) gtk_grid_new ();
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->box), 0, 0, 3, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->progress), 0, 1, 1, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->label_time), 1, 1, 1, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->entry_time), 2, 1, 1, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->label_horizontal), 0, 2, 1, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->hscale), 1, 2, 2, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->label_vertical), 0, 3, 1, 1);
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->vscale), 1, 3, 2, 1);

#if HAVE_GTKGLAREA
  dlg->gl_area = (GtkGLArea *) gtk_gl_area_new ();
  gtk_widget_set_size_request (GTK_WIDGET (dlg->gl_area),
                               window_width, window_height);
  g_signal_connect (dlg->gl_area, "realize",
                    (GCallback) dialog_simulator_draw_init, NULL);
  g_signal_connect (dlg->gl_area, "render", draw, NULL);
  g_signal_connect (dlg->gl_area, "resize", (GCallback) resize, NULL);
#if WINDOW_GLAREA
#if !GTK4
  dlg->window_gl = (GtkWindow *) gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_icon (dlg->window_gl, dlg->logo_min);
  gtk_container_add (GTK_CONTAINER (dlg->window_gl), GTK_WIDGET (dlg->gl_area));
  g_signal_connect_swapped (dlg->window_gl, "delete_event",
                            (GCallback) g_main_loop_quit, dlg->loop);
  gtk_widget_show_all (GTK_WIDGET (dlg->window_gl));
#else
  dlg->window_gl = (GtkWindow *) gtk_window_new ();
  gtk_window_set_child (dlg->window_gl, GTK_WIDGET (dlg->gl_area));
  g_signal_connect_swapped (dlg->window_gl, "close-request",
                            (GCallback) g_main_loop_quit, dlg->loop);
  gtk_widget_show (GTK_WIDGET (dlg->window_gl));
#endif
#else
  gtk_grid_attach (dlg->grid, GTK_WIDGET (dlg->gl_area), 0, 4, 3, 1);
#endif
#elif HAVE_SDL
  SDL_SetWindowMinimumSize (window, window_width, window_height);
#elif HAVE_GLFW
  glfwSetWindowSizeLimits (window, window_width, window_height, GLFW_DONT_CARE,
                           GLFW_DONT_CARE);
#endif

#if !GTK4
  dlg->window = (GtkWindow *) gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (dlg->window, _("Fractal growing"));
  gtk_window_set_icon (dlg->window, dlg->logo_min);
  gtk_container_add (GTK_CONTAINER (dlg->window), GTK_WIDGET (dlg->grid));
#if HAVE_GTKGLAREA
  g_signal_connect_swapped (dlg->window, "delete_event",
                            (GCallback) g_main_loop_quit, dlg->loop);
#elif HAVE_FREEGLUT
  g_signal_connect (dlg->window, "delete_event", glutLeaveMainLoop, NULL);
#elif HAVE_SDL
  g_signal_connect_swapped (dlg->window, "delete_event",
                            (void (*)) SDL_PushEvent, exit_event);
#elif HAVE_GLFW
  g_signal_connect (dlg->window, "delete_event", (void (*)) window_close, NULL);
#endif
  gtk_widget_show_all (GTK_WIDGET (dlg->window));
#else
  dlg->window = (GtkWindow *) gtk_window_new ();
  gtk_window_set_title (dlg->window, _("Fractal growing"));
  gtk_window_set_child (dlg->window, GTK_WIDGET (dlg->grid));
#if HAVE_GTKGLAREA
  g_signal_connect_swapped (dlg->window, "close-request",
                            (GCallback) g_main_loop_quit, dlg->loop);
#elif HAVE_FREEGLUT
  g_signal_connect (dlg->window, "close-request", glutLeaveMainLoop, NULL);
#elif HAVE_SDL
  g_signal_connect_swapped (dlg->window, "close-request",
                            (void (*)) SDL_PushEvent, exit_event);
#elif HAVE_GLFW
  g_signal_connect (dlg->window, "close-request", (void (*)) window_close,
                    NULL);
#endif
  gtk_widget_show (GTK_WIDGET (dlg->window));
#endif

#if DEBUG
  printf ("dialog_simulator_create: end\n");
#endif
}


#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdbool.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_eigen.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <functions.h>
#define FOR(i,n) for(i=0;i<n;i++)
typedef double DD, LD;
typedef long LL;

void quicksort_mds(double* x, long first, long last)
{
	long pivot, j, i;
	double temp;
	if (first<last)
	{
		pivot = first;
		i = first;
		j = last;
		while (i<j)
		{
			while (x[i]<=x[pivot] && i<last)
				i++;
			while (x[j]>x[pivot])
				j--;
			if (i<j)
			{
				temp = x[i];
				x[i] = x[j];
				x[j] = temp;
			}
		}
		temp = x[pivot];
		x[pivot] = x[j];
		x[j] = temp;
		quicksort(x, first, j - 1);
		quicksort(x, j + 1, last);

	}
}

void find_eigen(double **mat, int n, double*** res_vec, double** res_val)
{
	int i, j;
	double *data = (double*)calloc(((long)(n*n)), sizeof(double));
	double *eig_values = (double*)calloc(n, sizeof(double));
	double** eig_vec = (double**)calloc(n, sizeof(double*));
	for (i = 0; i<n; i++)eig_vec[i] = (double*)calloc(n, sizeof(double));
	for (i = 0; i<n; i++)
	{
		for (j = 0; j<n; j++)
		{
			data[i*n + j] = mat[i][j];
		}
	}
	gsl_matrix_view m= gsl_matrix_view_array(data, n, n);
	gsl_vector *eval = gsl_vector_alloc(n);
	gsl_matrix *evec = gsl_matrix_alloc(n, n);
	gsl_eigen_symmv_workspace * w =gsl_eigen_symmv_alloc(n);
	gsl_eigen_symmv(&m.matrix, eval, evec, w);
	gsl_eigen_symmv_free(w);
	gsl_eigen_symmv_sort(eval, evec,GSL_EIGEN_SORT_VAL_ASC);
	for (i = 0; i<n; i++)
	{
		eig_values[i] = gsl_vector_get(eval, i);
		for (j = 0; j<n; j++)
		{
			eig_vec[i][j] = gsl_matrix_get(evec, i, j);
		}
	}
	*res_val = eig_values;
	*res_vec = eig_vec;
	gsl_vector_free(eval);
	gsl_matrix_free(evec);
	free(data);
}

DD** Multiply_Matrices_mds(DD **Mat1, DD **Mat2, LL M, LL Q, LL P)
{
	LL i, c, d, k;
	DD Sum = 0;
	DD** Temp = (DD**)calloc(M, sizeof(DD*));
	FOR(i, M)Temp[i] = (DD*)calloc(Q, sizeof(DD));
	FOR(c, M)
	{
		FOR(d, Q)
		{
			FOR(k, P)
			{
				Sum += (Mat1[c][k] * Mat2[k][d]);
			}
			Temp[c][d] = Sum;
			Sum = 0;
		}
	}
	return Temp;
}

double ** H_mat(long n)
{
	long i, j;
	DD** k = (DD**)calloc(n, sizeof(DD*));
	FOR(i, n)
	{
		k[i] = (DD*)calloc(n, sizeof(DD));
	}
	FOR(i, n){
		FOR(j, n){ k[i][j] = (i == j) ? (1.00 - 1.00 / n) : (-1.00 / n);
		}
    }
	return k;
}
typedef struct{
	MAT Y;
	DD* eigen;
	LL number;
} Output_mds;

Output_mds MDS(DD** D,long rows,long columns, DD delta)
{
	//      ************** function to calculate Classical Multidimensional Scaling *************
	long  i, j, n;
	DD    **H, **new_D, **K, **new_k;
	new_D = (DD**)calloc(rows, sizeof(DD*));
	n = max(rows, columns);
	FOR(i, rows)
	{
		new_D[i] = (DD*)calloc(columns, sizeof(DD));
	}
	if (delta >= 0)
	{
		H = H_mat(n);
		new_k = (DD**)calloc(n, sizeof(DD*));
		FOR(i, rows)
		{
			new_k[i] = (DD*)calloc(n, sizeof(DD));
		}
		FOR(i, rows)
		{
			FOR(j, columns)
				new_D[i][j] = -0.5*D[i][j];
		}
		K = Multiply_Matrices_mds(Multiply_Matrices_mds(H, new_D, n, columns, n), H, columns, n, n);
        FOR(i,rows)free(new_D[i]);free(new_D);
        FOR(i,n)free(H[i]);free(H);
		DD* idx = (DD*)calloc(n, sizeof(DD));
		DD  ** temp, *val;
		FOR(i, n)
		{
			idx[i] = n - i;
			FOR(j, rows)
				new_k[i][j] = (K[i][j] + K[j][i]) / 2;
		}
		find_eigen(new_k, n, &temp, &val);
		find_eigen(new_k, n, &temp, &val);
		j = n - 1;
		for (i = 0; i < n / 2; i++)
		{
			DD temporary = val[i];
			val[i] = val[j];
			val[j] = temporary;
			j--;
		}
		LL* keep;
		DD max_val = val[0];
		LL magn_keep = 0;
		FOR(i, n){
			if (val[i] > max_val)max_val = val[i];
		}
		max_val = max_val*exp(-10);
		FOR(i, n){
			if (val[i] > max_val)magn_keep++;
		}
		keep = (LL*)calloc(magn_keep, sizeof(LL));
		magn_keep = 0;
		FOR(i, n){
			if (val[i] > max_val){
				keep[magn_keep++] = i;
			}
		}
		DD** V = (DD**)calloc(n, sizeof(DD*));
		DD** mult_V = (DD**)calloc(magn_keep, sizeof(DD*));
		FOR(i, n)V[i] = (DD*)calloc(magn_keep, sizeof(DD));
		FOR(i, magn_keep)
		{
			mult_V[i] = (DD*)calloc(magn_keep, sizeof(DD));
			FOR(j, n)
			{
				if (j < magn_keep)
				{
					if (i != j)mult_V[i][j] = 0;
					else mult_V[i][j] = sqrt(val[keep[i]]);
				}
				V[j][i] = temp[j][(int)idx[keep[i]]-1];
			}
		}
		V = Multiply_Matrices_mds(V, mult_V, n, magn_keep, magn_keep);
		MAT return_MAT;
		return_MAT.matrix = V;
		return_MAT.rows = n;
		return_MAT.columns = magn_keep;
		Output_mds t;
		t.Y = return_MAT; t.eigen = val; t.number=n;
		return t;
	}
	Output_mds t;
    return t;
}


#ifndef EXOBJ_H
#define EXOBJ_H

#include <unistd.h>
#include <iostream>
#include <fstream>

#include <float.h>
#include <math.h>
#include <complex>
#include <vector>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_multimin.h>
//#include <string>

#include <SQuIDS/SQuIDS.h>
#include <nuSQuIDS/nuSQuIDS.h>

namespace nusquids{

// NeutrinoDISCrossSectionsFromTablesExtended
// Is basically a copy of the default NeutrinoDISCrossSectionsFromTables but instead of
// returning error when the energy is lower that the low energy value in the tables it returns zero.
// This is effectively true for the range of energies given in the nuSQuIDS default tables(Emin=1e2GeV)
// This allows to use a wider range of the energy to compute atmospheric oscillations as it's shown in the
// main.cpp example.
  class NeutrinoDISCrossSectionsFromTablesExtended : public NeutrinoDISCrossSectionsFromTables {
  public :
    NeutrinoDISCrossSectionsFromTablesExtended():NeutrinoDISCrossSectionsFromTables(){}
    double TotalCrossSection(double Enu, NeutrinoFlavor flavor, NeutrinoType neutype, Current current) const override;
    double SingleDifferentialCrossSection(double E1, double E2, NeutrinoFlavor flavor, NeutrinoType neutype, Current current) const override;
  };
}

#endif


#include<iostream>
#include<string.h>
#include<vector>
#include<set>
#include<algorithm>
#include<fstream>
#include<cstdlib>
#include<time.h>
#include<iterator>
#include<sys/time.h>
#include<stdio.h>
#include<numeric>
#include <stdexcept>
#include<math.h>
#include<limits>
#include<random>
#include<ctime>
#include<map>
#include<omp.h>
#include <gsl/gsl_rng.h>
#include<pthread.h>
#include<chrono>

extern int GenNum;
extern std::vector<int> ArgPop;
//double PopulationFitnessTable = new double[numberofSNPs*diploidSize];
extern int popSize;
extern double RecombRate;
extern double MutationRate;
extern int m;
extern int epiflag;
extern int ChromLen;


/*
 * Copyright (c) 1997-1999 Massachusetts Institute of Technology
 *
 * 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 2 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/* This file was automatically generated --- DO NOT EDIT */
/* Generated on Sun Nov  7 20:45:10 EST 1999 */

#include <fftw-int.h>
#include <fftw.h>

/* Generated by: ./genfft -magic-alignment-check -magic-twiddle-load-all -magic-variables 4 -magic-loopi -hc2hc-backward 4 */

/*
 * This function contains 34 FP additions, 18 FP multiplications,
 * (or, 28 additions, 12 multiplications, 6 fused multiply/add),
 * 15 stack variables, and 32 memory accesses
 */
static const fftw_real K1_414213562 = FFTW_KONST(+1.414213562373095048801688724209698078569671875);
static const fftw_real K2_000000000 = FFTW_KONST(+2.000000000000000000000000000000000000000000000);

/*
 * Generator Id's :
 * $Id: exprdag.ml,v 1.41 1999/05/26 15:44:14 fftw Exp $
 * $Id: fft.ml,v 1.43 1999/05/17 19:44:18 fftw Exp $
 * $Id: to_c.ml,v 1.25 1999/10/26 21:41:32 stevenj Exp $
 */

void fftw_hc2hc_backward_4(fftw_real *A, const fftw_complex *W, int iostride, int m, int dist)
{
     int i;
     fftw_real *X;
     fftw_real *Y;
     X = A;
     Y = A + (4 * iostride);
     {
	  fftw_real tmp39;
	  fftw_real tmp42;
	  fftw_real tmp37;
	  fftw_real tmp40;
	  ASSERT_ALIGNED_DOUBLE;
	  {
	       fftw_real tmp38;
	       fftw_real tmp41;
	       fftw_real tmp35;
	       fftw_real tmp36;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp38 = X[iostride];
	       tmp39 = K2_000000000 * tmp38;
	       tmp41 = Y[-iostride];
	       tmp42 = K2_000000000 * tmp41;
	       tmp35 = X[0];
	       tmp36 = X[2 * iostride];
	       tmp37 = tmp35 + tmp36;
	       tmp40 = tmp35 - tmp36;
	  }
	  X[2 * iostride] = tmp37 - tmp39;
	  X[0] = tmp37 + tmp39;
	  X[3 * iostride] = tmp40 + tmp42;
	  X[iostride] = tmp40 - tmp42;
     }
     X = X + dist;
     Y = Y - dist;
     for (i = 2; i < m; i = i + 2, X = X + dist, Y = Y - dist, W = W + 3) {
	  fftw_real tmp9;
	  fftw_real tmp28;
	  fftw_real tmp18;
	  fftw_real tmp25;
	  fftw_real tmp12;
	  fftw_real tmp24;
	  fftw_real tmp21;
	  fftw_real tmp29;
	  ASSERT_ALIGNED_DOUBLE;
	  {
	       fftw_real tmp7;
	       fftw_real tmp8;
	       fftw_real tmp16;
	       fftw_real tmp17;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp7 = X[0];
	       tmp8 = Y[-2 * iostride];
	       tmp9 = tmp7 + tmp8;
	       tmp28 = tmp7 - tmp8;
	       tmp16 = Y[0];
	       tmp17 = X[2 * iostride];
	       tmp18 = tmp16 - tmp17;
	       tmp25 = tmp16 + tmp17;
	  }
	  {
	       fftw_real tmp10;
	       fftw_real tmp11;
	       fftw_real tmp19;
	       fftw_real tmp20;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp10 = X[iostride];
	       tmp11 = Y[-3 * iostride];
	       tmp12 = tmp10 + tmp11;
	       tmp24 = tmp10 - tmp11;
	       tmp19 = Y[-iostride];
	       tmp20 = X[3 * iostride];
	       tmp21 = tmp19 - tmp20;
	       tmp29 = tmp19 + tmp20;
	  }
	  X[0] = tmp9 + tmp12;
	  {
	       fftw_real tmp14;
	       fftw_real tmp22;
	       fftw_real tmp13;
	       fftw_real tmp15;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp14 = tmp9 - tmp12;
	       tmp22 = tmp18 - tmp21;
	       tmp13 = c_re(W[1]);
	       tmp15 = c_im(W[1]);
	       X[2 * iostride] = (tmp13 * tmp14) + (tmp15 * tmp22);
	       Y[-iostride] = (tmp13 * tmp22) - (tmp15 * tmp14);
	  }
	  Y[-3 * iostride] = tmp18 + tmp21;
	  {
	       fftw_real tmp26;
	       fftw_real tmp30;
	       fftw_real tmp23;
	       fftw_real tmp27;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp26 = tmp24 + tmp25;
	       tmp30 = tmp28 - tmp29;
	       tmp23 = c_re(W[0]);
	       tmp27 = c_im(W[0]);
	       Y[-2 * iostride] = (tmp23 * tmp26) - (tmp27 * tmp30);
	       X[iostride] = (tmp27 * tmp26) + (tmp23 * tmp30);
	  }
	  {
	       fftw_real tmp32;
	       fftw_real tmp34;
	       fftw_real tmp31;
	       fftw_real tmp33;
	       ASSERT_ALIGNED_DOUBLE;
	       tmp32 = tmp25 - tmp24;
	       tmp34 = tmp28 + tmp29;
	       tmp31 = c_re(W[2]);
	       tmp33 = c_im(W[2]);
	       Y[0] = (tmp31 * tmp32) - (tmp33 * tmp34);
	       X[3 * iostride] = (tmp33 * tmp32) + (tmp31 * tmp34);
	  }
     }
     if (i == m) {
	  fftw_real tmp1;
	  fftw_real tmp2;
	  fftw_real tmp3;
	  fftw_real tmp4;
	  fftw_real tmp5;
	  fftw_real tmp6;
	  ASSERT_ALIGNED_DOUBLE;
	  tmp1 = X[0];
	  tmp2 = X[iostride];
	  tmp3 = tmp1 - tmp2;
	  tmp4 = Y[0];
	  tmp5 = Y[-iostride];
	  tmp6 = tmp4 + tmp5;
	  X[0] = K2_000000000 * (tmp1 + tmp2);
	  X[2 * iostride] = -(K2_000000000 * (tmp4 - tmp5));
	  X[iostride] = K1_414213562 * (tmp3 - tmp6);
	  X[3 * iostride] = -(K1_414213562 * (tmp3 + tmp6));
     }
}

static const int twiddle_order[] =
{1, 2, 3};
fftw_codelet_desc fftw_hc2hc_backward_4_desc =
{
     "fftw_hc2hc_backward_4",
     (void (*)()) fftw_hc2hc_backward_4,
     4,
     FFTW_BACKWARD,
     FFTW_HC2HC,
     102,
     3,
     twiddle_order,
};


#ifndef QDM_MSPLINE_H
#define QDM_MSPLINE_H 1

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

void
qdm_mspline_vector(gsl_vector *result, const double tau, const size_t spline_df, const gsl_vector *knots);

void
qdm_mspline_matrix(gsl_matrix *result, const size_t spline_df, const gsl_vector *x, const gsl_vector *knots);

#endif /* QDM_MSPLINE_H */


// =============================================================================
// == IMG.h
// == --------------------------------------------------------------------------
// == An image class to be used with temporal superpixels.
// ==
// == All work using this code should cite:
// == J. Chang, D. Wei, and J. W. Fisher III. A Video Representation Using
// ==    Temporal Superpixels. CVPR 2013.
// == --------------------------------------------------------------------------
// == Written by Jason Chang and Donglai Wei 06-20-2013
// =============================================================================

#ifndef _IMG_H_INCLUDED_
#define _IMG_H_INCLUDED_
#include <iostream>
#include <fstream>
#include "utils.h"
#include "SP.h"
#include "NormalD.h"
#include "topology.h"
#include "array.h"
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_blas.h>

#include "helperMEX.h"

using namespace std;

class IMG
{
private:
   int debug;
   int xdim;
   int ydim;
   int N;

   arr(double) data;
   arr(int) label;
   int K;
   //NIW new_pos;
   NormalD new_pos;
   NormalD new_app;
   arr(SP*) SP_arr;
   SP* SP_new; // a pointer to an exemplar "new" SP
   arr(bool) SP_old; // indicates the SPs that are from a prev frame
   arr(bool) SP_changed; // indicates which SPs changed in the previous iteration

   arr(linkedListNode<int>*) border_ptr;
   arr(linkedListNode<int>*) pixel_ptr;

   arr(double) T4Table;

   double log_alpha; // size parameter
   double log_beta;
   double area;
   double area_var;
   double log_area_var;
   unsigned long max_UID;

   double lambda; // flow parameters
   double lambda_sigma;

   arr(int) prev_label;
   arr(double) prev_app_mean;
   arr(double) prev_pos_mean;
   int prev_K;
   arr(double) prev_covariance;
   arr(double) prev_precision;
   linkedList< std::pair<int, int> > prev_neighbors;
   linkedList< double > prev_neighbors_scale;
   bool alive_dead_changed;

   double dummy_log_prob;
   arr(bool) boundary_mask;

   // flow stuff
   gsl_vector *vgsl;
   gsl_vector *ugsl;
   arr(double) Sxy;
   arr(double) Syy;
   arr(double) SxySyy;
   arr(double) obs_u;
   arr(double) obs_v;
   arr(double) temp_Syy;
   arr(double) temp_d;
   arr(bool) temp_still_alive;
   int num_alive;
   int num_dead;

   arr(int) all2alive;
   arr(int) alive2all;
   arr(int) all2dead;
   arr(int) dead2all;

public:
   IMG();
   virtual ~IMG();

   void ReadIMG();
   void mxReadIMG(const mxArray *mstruct);
   void mxWriteIMG(mxArray* plhs[], const mxArray* oldstruct);

   // --------------------------------------------------------------------------
   // -- move_local
   // --   finds the optimal local joint move of labels and parameters. Chooses
   // -- a super pixel at random and loops through and updates its borders.
   // --------------------------------------------------------------------------
   bool move_local_IMG();

   bool move_switch_IMG();

   // --------------------------------------------------------------------------
   // -- move_merge
   // --   finds the optimal merge between all pairs of super pixels
   // --------------------------------------------------------------------------
   void move_merge_IMG();
   void move_split_IMG();
   void move_IMG();

   void Flow_QP2();
   void find_SxySyy(arr(int) all2alive, arr(int) alive2all, arr(int) all2dead, arr(int) dead2all);
private:
   //
   // -- 1. I/O
   void U_initialize();
   void U_relabel_SP(bool final=false);

   double U_calc_energy();
   double U_calc_model_order(int N, bool is_old);

   // -- 3. border bookkeeping
   bool U_check_border_pix(int index);
   bool U_check_border_pix(int index, int cur_label);
   void U_find_border_SP(int k,arr(bool) neighbors,linkedList<int> &neighborsLL);
   // --------------------------------------------------------------------------
   // -- U_update_border_changed
   // --   Updates all border linked lists and pointers for the neighbors of a
   // -- changed pixel at index.
   // --
   // --   parameters:
   // --     - index : the index of the recently changed pixel
   // --------------------------------------------------------------------------
   void U_update_border_changed(int index);
   // --------------------------------------------------------------------------
   // -- U_update_border_changed_pixel
   // --   Updates all border linked lists and pointers at index
   // --
   // --   parameters:
   // --     - index : the index of the recently changed pixel
   // --------------------------------------------------------------------------
   void U_update_border_changed_pixel(int index);

   // --------------------------------------------------------------------------
   // -- U_update_neighbor_list
   // --   Updates super pixel neighbors used in merge
   // --
   // --   parameters:
   // --     - neighbors : a boolean array indicating which labels are added
   // --     - neighborsLL : a linked list of the neighbor indices
   // --     - index : the index of the point to consider adding
   // --------------------------------------------------------------------------
   void U_update_neighbor_list(arr(bool) neighbors, linkedList<int> &neighborsLL, int index);

   void U_fix_neighbors_self(int k);
   void U_fix_neighbors_neighbors(int k);
   void U_fix_neighbors_neighbors(int k, int kignore);
   void U_print_neighbors(int k);

   // --------------------------------------------------------------------------
   // -- U_update_neighbors_rem
   // --   Updates all neighbor lists when a pixel is "removed". A subsequent
   // -- call to update_neighbors_add should be completed right after this one.
   // -- The neighboring label should be changed before calling this function.
   // --
   // --   parameters:
   // --     - old_label : the label of the pixel before it was changed
   // --     - index : the index bordering the removed pixel
   // --------------------------------------------------------------------------
   void U_update_neighbors_rem(int old_label, int index);

   // --------------------------------------------------------------------------
   // -- U_update_neighbors_add
   // --   Updates all neighbor lists when a pixel is "added". A previous
   // -- call to update_neighbors_rem should be completed right before this one.
   // -- The neighboring label should be changed before calling this function.
   // --
   // --   parameters:
   // --     - index : the index bordering the removed pixel
   // --------------------------------------------------------------------------
   void U_update_neighbors_add(int index);

   // --------------------------------------------------------------------------
   // -- U_update_neighbors_merge
   // --   Updates the neighbor lists for merging two super pixels.
   // -- All labels and SP_arr things should be updated *before* calling this.
   // --
   // --   parameters:
   // --     - index : the index bordering the removed pixel
   // --------------------------------------------------------------------------
   void U_update_neighbors_merge(int new_label, int old_label);

   // --------------------------------------------------------------------------
   // -- U_update_neighbors_split
   // --   Updates the neighbor lists for merging two super pixels.
   // -- All labels and SP_arr things should be updated *before* calling this.
   // --
   // --   parameters:
   // --     - index : the index bordering the removed pixel
   // --------------------------------------------------------------------------
   void U_update_neighbors_split(int label1, int label2);

   // -- 4. Modularized Move
   // -- 4.0 Change of Energy

   // --------------------------------------------------------------------------
   // -- link_cost
   // --   calculates the energy of linking SP_arr[ko] (old) to SP_arr[kn] (new)
   // --
   // --   parameters:
   // --     - ko : the old k
   // --     - kn : the new k
   // --------------------------------------------------------------------------
   double link_cost(int ko, int kn);

   double move_local_calc_delta_MM(int index, int new_k, double& max_prob, int& max_k);

   // --------------------------------------------------------------------------
   // -- move_local_calc_neigbor
   // --   calculates the probability of assigning the pixel at index to the
   // -- cluster of nindex (neighbor index). Updates the max_prob and max_k if
   // -- the new_app value is greater than the old one
   // --
   // --   parameters:
   // --     - index : the new point to add
   // --     - new_k : the neighbor to add this point to
   // --     - max_prob : the maximum probability of all neighbors
   // --     - max_k : the super pixel index of the maximum probability
   // --------------------------------------------------------------------------
   double move_local_calc_delta(int index, int new_k, bool add, double& max_prob, int& max_k);

   double move_switch_calc_delta(SP* &oldSP, SP* &newSP);

   // --------------------------------------------------------------------------
   // -- move_merge_calc_delta
   // --   calculates the probability of assigning the pixel at index to the
   // -- cluster of nindex (neighbor index).
   // --
   // --   parameters:
   // --     - index : the new point to add
   // --     - nindex : the neighbor to add this point to
   // --------------------------------------------------------------------------
   double move_merge_calc_delta(int k, int merge_k);

   // --------------------------------------------------------------------------
   // -- move_split_calc_delta
   // --   calculates the change in energy for
   // -- (k1 U new_k1) && (k2 U new_k2) - (k1 U new_k1 U new_k) && (k2)
   // --
   // --   parameters:
   // --     - SP1 : the SP that originates the splitting
   // --     - SP2 : the SP to split to
   // --     - new_SP1 : temporary SP that contains pixels that will go in k1
   // --     - new_SP2 : temporary SP that contains pixels that will go in k2
   // --     - SP1_old : indicates if SP1 is an old SP
   // --     - SP2_old : indicates if SP2 is an old SP
   // --------------------------------------------------------------------------
   double move_split_calc_delta(SP* SP1, SP* SP2, SP* new_SP1, SP* new_SP2, bool SP1_old, bool SP2_old);

   // -- 4.1 move local
   // need to be done
   int move_local_SP_region(int k,linkedList<int> &check_labels);
   int move_local_SP(int k);
   // need to be done
   int move_local_pix(int index);
   //-- 4.2 move merge
   void move_merge_SP_propose(int k,arr(bool) neighbors,double &max_E,int &max_k);
   void move_merge_SP_propose_region(int k,arr(bool) neighbors, linkedList<int> &check_labels,double &max_E,int &max_k);
   void move_merge_SP(int k,arr(bool) neighbors);

   // -- 4.3 move split
   void move_split_SP(int k);
   bool U_Kmeans_plusplus(int k, int *bbox, int num_SP, int numiter, int index2=-1);
   void U_connect_newSP(int *bbox, int num_SP);
   double U_dist(int index1 , double* center);
   double U_dist(int index1 , int index2);

   void move_split_SP_propose(int index, int num_SP, int option, double &max_E,int &ksplit,int* new_ks);

};

#endif


/* multifit_nlin/gsl_multifit_nlin.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 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_MULTIFIT_NLIN_H__
#define __GSL_MULTIFIT_NLIN_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_permutation.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

int gsl_multifit_gradient (const gsl_matrix * J, const gsl_vector * f,
                           gsl_vector * g);

int gsl_multifit_covar (const gsl_matrix * J, const double epsrel, gsl_matrix * covar);
int gsl_multifit_covar_QRPT (gsl_matrix * r, gsl_permutation * perm,
                             const double epsrel, gsl_matrix * covar);

/* Definition of vector-valued functions with parameters based on gsl_vector */

struct gsl_multifit_function_struct
{
  int (* f) (const gsl_vector * x, void * params, gsl_vector * f);
  size_t n;   /* number of functions */
  size_t p;   /* number of independent variables */
  void * params;
};

typedef struct gsl_multifit_function_struct gsl_multifit_function ;

#define GSL_MULTIFIT_FN_EVAL(F,x,y) (*((F)->f))(x,(F)->params,(y))

typedef struct
  {
    const char *name;
    size_t size;
    int (*alloc) (void *state, size_t n, size_t p);
    int (*set) (void *state, gsl_multifit_function * function, gsl_vector * x, gsl_vector * f, gsl_vector * dx);
    int (*iterate) (void *state, gsl_multifit_function * function, gsl_vector * x, gsl_vector * f, gsl_vector * dx);
    void (*free) (void *state);
  }
gsl_multifit_fsolver_type;

typedef struct
  {
    const gsl_multifit_fsolver_type * type;
    gsl_multifit_function * function ;
    gsl_vector * x ;
    gsl_vector * f ;
    gsl_vector * dx ;
    void *state;
  }
gsl_multifit_fsolver;

gsl_multifit_fsolver *
gsl_multifit_fsolver_alloc (const gsl_multifit_fsolver_type * T,
                            size_t n, size_t p);

void gsl_multifit_fsolver_free (gsl_multifit_fsolver * s);

int gsl_multifit_fsolver_set (gsl_multifit_fsolver * s,
                              gsl_multifit_function * f,
                              const gsl_vector * x);

int gsl_multifit_fsolver_iterate (gsl_multifit_fsolver * s);

int gsl_multifit_fsolver_driver (gsl_multifit_fsolver * s,
                                 const size_t maxiter,
                                 const double epsabs, const double epsrel);

const char * gsl_multifit_fsolver_name (const gsl_multifit_fsolver * s);
gsl_vector * gsl_multifit_fsolver_position (const gsl_multifit_fsolver * s);

/* Definition of vector-valued functions and gradient with parameters
   based on gsl_vector */

struct gsl_multifit_function_fdf_struct
{
  int (* f) (const gsl_vector * x, void * params, gsl_vector * f);
  int (* df) (const gsl_vector * x, void * params, gsl_matrix * df);
  int (* fdf) (const gsl_vector * x, void * params, gsl_vector * f, gsl_matrix *df);
  size_t n;       /* number of functions */
  size_t p;       /* number of independent variables */
  void * params;  /* user parameters */
  size_t nevalf;  /* number of function evaluations */
  size_t nevaldf; /* number of Jacobian evaluations */
};

typedef struct gsl_multifit_function_fdf_struct gsl_multifit_function_fdf ;

typedef struct
  {
    const char *name;
    size_t size;
    int (*alloc) (void *state, size_t n, size_t p);
    int (*set) (void *state, const gsl_vector * wts,
                gsl_multifit_function_fdf * fdf, gsl_vector * x,
                gsl_vector * f, gsl_vector * dx);
    int (*iterate) (void *state, const gsl_vector * wts,
                    gsl_multifit_function_fdf * fdf, gsl_vector * x,
                    gsl_vector * f, gsl_vector * dx);
    int (*gradient) (void *state, gsl_vector * g);
    int (*jac) (void *state, gsl_matrix * J);
    void (*free) (void *state);
  }
gsl_multifit_fdfsolver_type;

typedef struct
  {
    const gsl_multifit_fdfsolver_type * type;
    gsl_multifit_function_fdf * fdf ;
    gsl_vector * x;        /* parameter values x */
    gsl_vector * f;        /* residual vector f(x) */
    gsl_vector * dx;       /* step dx */
    gsl_vector * g;        /* gradient J^T f */
    gsl_vector * sqrt_wts; /* sqrt(wts) */
    size_t niter;          /* number of iterations performed */
    void *state;
  }
gsl_multifit_fdfsolver;

gsl_multifit_fdfsolver *
gsl_multifit_fdfsolver_alloc (const gsl_multifit_fdfsolver_type * T,
                              size_t n, size_t p);

int
gsl_multifit_fdfsolver_set (gsl_multifit_fdfsolver * s,
                            gsl_multifit_function_fdf * fdf,
                            const gsl_vector * x);
int gsl_multifit_fdfsolver_wset (gsl_multifit_fdfsolver * s,
                                 gsl_multifit_function_fdf * f,
                                 const gsl_vector * x,
                                 const gsl_vector * wts);

int
gsl_multifit_fdfsolver_iterate (gsl_multifit_fdfsolver * s);

int gsl_multifit_fdfsolver_driver (gsl_multifit_fdfsolver * s,
                                   const size_t maxiter,
                                   const double xtol,
                                   const double gtol,
                                   const double ftol,
                                   int *info);

int gsl_multifit_fdfsolver_jac (gsl_multifit_fdfsolver * s,
                                gsl_matrix * J);

void
gsl_multifit_fdfsolver_free (gsl_multifit_fdfsolver * s);

const char * gsl_multifit_fdfsolver_name (const gsl_multifit_fdfsolver * s);
gsl_vector * gsl_multifit_fdfsolver_position (const gsl_multifit_fdfsolver * s);
gsl_vector * gsl_multifit_fdfsolver_residual (const gsl_multifit_fdfsolver * s);
size_t gsl_multifit_fdfsolver_niter (const gsl_multifit_fdfsolver * s);
int gsl_multifit_eval_wf(gsl_multifit_function_fdf *fdf,
                         const gsl_vector *x, const gsl_vector *wts,
                         gsl_vector *y);
int gsl_multifit_eval_wdf(gsl_multifit_function_fdf *fdf,
                          const gsl_vector *x, const gsl_vector *wts,
                          gsl_matrix *dy);

int gsl_multifit_fdfsolver_test (const gsl_multifit_fdfsolver * s,
                                 const double xtol,
                                 const double gtol,
                                 const double ftol, int *info);
int gsl_multifit_test_delta (const gsl_vector * dx, const gsl_vector * x,
                             double epsabs, double epsrel);

int gsl_multifit_test_gradient (const gsl_vector * g, double epsabs);

int gsl_multifit_fdfsolver_dif_df(const gsl_vector *x,
                                  const gsl_vector *wts,
                                  gsl_multifit_function_fdf *fdf,
                                  const gsl_vector *f, gsl_matrix *J);
int gsl_multifit_fdfsolver_dif_fdf(const gsl_vector *x, gsl_multifit_function_fdf *fdf,
                                   gsl_vector *f, gsl_matrix *J);

typedef struct
{
  size_t n;                         /* number of (original) residuals */
  size_t p;                         /* number of model parameters */
  double lambda;                    /* damping parameter */
  const gsl_vector *L_diag;         /* diagonal damping matrix or NULL */
  const gsl_matrix *L;              /* general damping matrix or NULL */
  gsl_vector *f;                    /* function values for finite diff J */
  gsl_vector *wts;                  /* weight vector for augmented system */
  gsl_multifit_fdfsolver * s;
  gsl_multifit_function_fdf *fdf;   /* user defined fdf */
  gsl_multifit_function_fdf fdftik; /* Tikhonov modified fdf */
} gsl_multifit_fdfridge;

gsl_multifit_fdfridge *
gsl_multifit_fdfridge_alloc (const gsl_multifit_fdfsolver_type * T,
                             const size_t n, const size_t p);
void gsl_multifit_fdfridge_free(gsl_multifit_fdfridge *work);
const char *gsl_multifit_fdfridge_name(const gsl_multifit_fdfridge * w);
gsl_vector *gsl_multifit_fdfridge_position (const gsl_multifit_fdfridge * w);
gsl_vector *gsl_multifit_fdfridge_residual (const gsl_multifit_fdfridge * w);
size_t gsl_multifit_fdfridge_niter (const gsl_multifit_fdfridge * w);
int gsl_multifit_fdfridge_set (gsl_multifit_fdfridge * w,
                               gsl_multifit_function_fdf * f,
                               const gsl_vector * x,
                               const double lambda);
int gsl_multifit_fdfridge_wset (gsl_multifit_fdfridge * w,
                                gsl_multifit_function_fdf * f,
                                const gsl_vector * x,
                                const double lambda,
                                const gsl_vector * wts);
int gsl_multifit_fdfridge_set2 (gsl_multifit_fdfridge * w,
                                gsl_multifit_function_fdf * f,
                                const gsl_vector * x,
                                const gsl_vector * lambda);
int gsl_multifit_fdfridge_wset2 (gsl_multifit_fdfridge * w,
                                 gsl_multifit_function_fdf * f,
                                 const gsl_vector * x,
                                 const gsl_vector * lambda,
                                 const gsl_vector * wts);
int gsl_multifit_fdfridge_set3 (gsl_multifit_fdfridge * w,
                                gsl_multifit_function_fdf * f,
                                const gsl_vector * x,
                                const gsl_matrix * L);
int gsl_multifit_fdfridge_wset3 (gsl_multifit_fdfridge * w,
                                 gsl_multifit_function_fdf * f,
                                 const gsl_vector * x,
                                 const gsl_matrix * L,
                                 const gsl_vector * wts);
int gsl_multifit_fdfridge_iterate (gsl_multifit_fdfridge * w);
int gsl_multifit_fdfridge_driver (gsl_multifit_fdfridge * w,
                                  const size_t maxiter,
                                  const double xtol,
                                  const double gtol,
                                  const double ftol,
                                  int *info);

/* extern const gsl_multifit_fsolver_type * gsl_multifit_fsolver_gradient; */

GSL_VAR const gsl_multifit_fdfsolver_type * gsl_multifit_fdfsolver_lmsder;
GSL_VAR const gsl_multifit_fdfsolver_type * gsl_multifit_fdfsolver_lmder;
GSL_VAR const gsl_multifit_fdfsolver_type * gsl_multifit_fdfsolver_lmniel;

__END_DECLS

#endif /* __GSL_MULTIFIT_NLIN_H__ */


/* histogram/test_trap.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <gsl/gsl_histogram.h>
#include <gsl/gsl_test.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_ieee_utils.h>

#define N 397

void my_error_handler (const char *reason, const char *file,
		       int line, int err);
int status = 0;

int
main (void)
{
  gsl_histogram *h;
  double result, lower, upper;
  size_t i;

  gsl_set_error_handler (&my_error_handler);

  gsl_ieee_env_setup ();

  status = 0;
  h = gsl_histogram_calloc (0);
  gsl_test (!status, "gsl_histogram_calloc traps zero-length histogram");
  gsl_test (h != 0,
	    "gsl_histogram_calloc returns NULL for zero-length histogram");

  status = 0;
  h = gsl_histogram_calloc_uniform (0, 0.0, 1.0);
  gsl_test (!status,
	    "gsl_histogram_calloc_uniform traps zero-length histogram");
  gsl_test (h != 0,
     "gsl_histogram_calloc_uniform returns NULL for zero-length histogram");

  status = 0;
  h = gsl_histogram_calloc_uniform (10, 1.0, 1.0);
  gsl_test (!status,
	    "gsl_histogram_calloc_uniform traps equal endpoints");
  gsl_test (h != 0,
	    "gsl_histogram_calloc_uniform returns NULL for equal endpoints");

  status = 0;
  h = gsl_histogram_calloc_uniform (10, 2.0, 1.0);
  gsl_test (!status,
	    "gsl_histogram_calloc_uniform traps invalid range");
  gsl_test (h != 0,
	    "gsl_histogram_calloc_uniform returns NULL for invalid range");

  h = gsl_histogram_calloc_uniform (N, 0.0, 1.0);

  status = gsl_histogram_accumulate (h, 1.0, 10.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram_accumulate traps x at xmax");

  status = gsl_histogram_accumulate (h, 2.0, 100.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram_accumulate traps x above xmax");

  status = gsl_histogram_accumulate (h, -1.0, 1000.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram_accumulate traps x below xmin");

  status = gsl_histogram_increment (h, 1.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram_increment traps x at xmax");

  status = gsl_histogram_increment (h, 2.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram_increment traps x above xmax");

  status = gsl_histogram_increment (h, -1.0);
  gsl_test (status != GSL_EDOM, "gsl_histogram_increment traps x below xmin");

  result = gsl_histogram_get (h, N);
  gsl_test (result != 0, "gsl_histogram_get traps index at n");

  result = gsl_histogram_get (h, N + 1);
  gsl_test (result != 0, "gsl_histogram_get traps index above n");

  status = gsl_histogram_get_range (h, N, &lower, &upper);
  gsl_test (status != GSL_EDOM,
	    "gsl_histogram_get_range traps index at n");

  status = gsl_histogram_get_range (h, N + 1, &lower, &upper);
  gsl_test (status != GSL_EDOM,
	    "gsl_histogram_get_range traps index above n");

  status = 0;
  gsl_histogram_find (h, -0.01, &i);
  gsl_test (status != GSL_EDOM, "gsl_histogram_find traps x below xmin");

  status = 0;
  gsl_histogram_find (h, 1.0, &i);
  gsl_test (status != GSL_EDOM, "gsl_histogram_find traps x at xmax");

  status = 0;
  gsl_histogram_find (h, 1.1, &i);
  gsl_test (status != GSL_EDOM, "gsl_histogram_find traps x above xmax");

  gsl_histogram_free (h);

  exit (gsl_test_summary ());
}

void
my_error_handler (const char *reason, const char *file, int line, int err)
{
  if (0)
    printf ("(caught [%s:%d: %s (%d)])\n", file, line, reason, err);
  status = 1;
}


// Test program for comparison with brighten_float_image.

#include <assert.h>
#include <stdlib.h>

#include <glib.h>
#include <gsl/gsl_blas.h>

#include <asf_meta.h>

int
main (int argc, char **argv)
{
  assert (argc == 3);

  // Image to be brightened.
  GString *input_image_basename = g_string_new (argv[1]);

  // Basename to use for brightened version of image.
  GString *output_image_basename = g_string_new (argv[2]);

  // Form names for the input image data and metadata.
  GString *input_metadata_file = g_string_new (input_image_basename->str);
  g_string_append (input_metadata_file, ".meta");
  GString *input_data_file = g_string_new (input_image_basename->str);
  g_string_append (input_data_file, ".img");

  // Form names for the output image data and metadata.
  GString *output_metadata_file = g_string_new (output_image_basename->str);
  g_string_append (output_metadata_file, ".meta");
  GString *output_data_file = g_string_new (output_image_basename->str);
  g_string_append (output_data_file, ".img");

  // Read the metadata for the input image.
  meta_parameters *imd = meta_read (input_image_basename->str);

  // Shorthand for the dimension of the input image.
  size_t ixs = imd->general->sample_count;
  size_t iys = imd->general->line_count;

  // Set up gsl matrix for the input image.
  gsl_matrix_float *id = gsl_matrix_float_alloc (iys, ixs);

  // Set up gsl matrix for the output image.
  gsl_matrix_float *od = gsl_matrix_float_alloc (iys, ixs);

  // Do the brightening.
  size_t ii;
  // For each line.
  for ( ii = 0 ; ii < iys ; ii++ ) {
    size_t jj;
    // For each pixel of line.
    for ( jj = 0 ; jj < ixs ; jj++ ) {
      // Brighten pixel by factor of two and store in output image.
      float pixel_value = gsl_matrix_float_get (id, ii, jj);
      gsl_matrix_float_set (od, ii, jj, pixel_value * 2);
    }
  }

  // Store the output image data.
  FILE *od_stream = fopen (output_data_file->str, "wb");
  g_assert (od_stream != NULL);
  int return_code = gsl_matrix_float_fwrite (od_stream, od);
  g_assert (return_code == GSL_SUCCESS);
  return_code = fclose (od_stream);
  g_assert (return_code == 0);

  // Copy the input metadata to the output metadata.
  GString *system_command = g_string_new ("cp ");
  g_string_append_printf (system_command, "%s %s", input_metadata_file->str,
			  output_metadata_file->str);
  return_code = system (system_command->str);
  g_assert (return_code == 0);

  exit (EXIT_SUCCESS);
}


/* Copyright (c) 2014, Giuseppe Argentieri <giuseppe.argentieri@ts.infn.it>

 * 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.
 *
 * 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.
 *
 */
/*
 *
 *
 *       Filename:  mat_file.c
 *
 *    Description:  Write and read the generator matrices on files
 *
 *        Version:  1.0
 *        Created:  03/05/2014 13:52:15
 *       Revision:  none
 *        License:  BSD
 *
 *         Author:  Giuseppe Argentieri (ga), giuseppe.argentieri@ts.infn.it
 *   Organization:  Università degli Studi di Trieste
 *
 *
 */

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_errno.h>
#include <string.h>
#include "funcs.h"

/*
 *      FUNCTION
 *         Name:  mat_write
 *  Description:  Save the generator matrix in a file.
 *
 */
int mat_write ( gsl_matrix* mat, char* name )
{
	FILE* f = fopen ( name, "w" ) ;
	if ( f == NULL )
		printf("Error: %s.\nFailed to open %s.\n", strerror(errno),
				name) ;

	int status = gsl_matrix_fprintf ( f, mat, "%.9f" ) ;

	fclose (f) ;

	return status;
}		/* -----  end of function mat_write  ----- */

/*
 *      FUNCTION
 *         Name:  mat_read
 *  Description:  Read the Redfield matrix from a file
 *
 */
int mat_read ( gsl_matrix* mat, char* name )
{
	FILE* f = fopen ( name, "r" ) ;
	if ( f == NULL )
		printf( "Error: %s.\nFailed to open %s.\n", strerror(errno),
				name ) ;

	int status = gsl_matrix_fscanf ( f, mat ) ;

	fclose (f) ;

	return status;
}		/* -----  end of function mat_read  ----- */


/**
 *
 * @file testing_cher2k.c
 *
 *  PLASMA testing routines
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Mathieu Faverge
 * @date 2010-11-15
 * @generated c Tue Jan  7 11:45:19 2014
 *
 **/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <plasma.h>
#include <cblas.h>
#include <lapacke.h>
#include <core_blas.h>
#include "testing_cmain.h"

static int check_solution(PLASMA_enum uplo, PLASMA_enum trans, int N, int K,
                          PLASMA_Complex32_t alpha, PLASMA_Complex32_t *A, int LDA,
                          PLASMA_Complex32_t *B, int LDB,
                          float beta,  PLASMA_Complex32_t *Cref, PLASMA_Complex32_t *Cplasma, int LDC);

int testing_cher2k(int argc, char **argv)
{
    /* Check for number of arguments*/
    if ( argc != 7 ){
        USAGE("HER2K", "alpha beta M N LDA LDB LDC",
              "   - alpha : alpha coefficient\n"
              "   - beta : beta coefficient\n"
              "   - N : number of columns and rows of matrix C and number of row of matrix A and B\n"
              "   - K : number of columns of matrix A and B\n"
              "   - LDA : leading dimension of matrix A\n"
              "   - LDB : leading dimension of matrix B\n"
              "   - LDC : leading dimension of matrix C\n");
        return -1;
    }

    PLASMA_Complex32_t alpha = (PLASMA_Complex32_t) atol(argv[0]);
    float beta  = (float) atol(argv[1]);
    int N     = atoi(argv[2]);
    int K     = atoi(argv[3]);
    int LDA   = atoi(argv[4]);
    int LDB   = atoi(argv[5]);
    int LDC   = atoi(argv[6]);
    int NKmax = max(N, K);

    float eps;
    int info_solution;
    int u, t;
    size_t LDAxK = LDA*NKmax;
    size_t LDBxK = LDB*NKmax;
    size_t LDCxN = LDC*N;

    PLASMA_Complex32_t *A      = (PLASMA_Complex32_t *)malloc(LDAxK*sizeof(PLASMA_Complex32_t));
    PLASMA_Complex32_t *B      = (PLASMA_Complex32_t *)malloc(LDBxK*sizeof(PLASMA_Complex32_t));
    PLASMA_Complex32_t *C      = (PLASMA_Complex32_t *)malloc(LDCxN*sizeof(PLASMA_Complex32_t));
    PLASMA_Complex32_t *Cinit  = (PLASMA_Complex32_t *)malloc(LDCxN*sizeof(PLASMA_Complex32_t));
    PLASMA_Complex32_t *Cfinal = (PLASMA_Complex32_t *)malloc(LDCxN*sizeof(PLASMA_Complex32_t));

    /* Check if unable to allocate memory */
    if ( (!A) || (!B) || (!Cinit) || (!Cfinal) ){
        printf("Out of Memory \n ");
        return -2;
    }

    eps = LAPACKE_slamch_work('e');

    printf("\n");
    printf("------ TESTS FOR PLASMA CHER2K ROUTINE -------  \n");
    printf("            Size of the Matrix C %d by %d\n", N, K);
    printf("\n");
    printf(" The matrix A is randomly generated for each test.\n");
    printf("============\n");
    printf(" The relative machine precision (eps) is to be %e \n",eps);
    printf(" Computational tests pass if scaled residuals are less than 10.\n");

    /*----------------------------------------------------------
    *  TESTING CHER2K
    */

    /* Initialize A,B */
    LAPACKE_clarnv_work(IONE, ISEED, LDAxK, A);
    LAPACKE_clarnv_work(IONE, ISEED, LDBxK, B);

    /* Initialize C */
    PLASMA_cplghe( (float)0., N, C, LDC, 51 );

    for (u=0; u<2; u++) {
        for (t=0; t<3; t++) {
            if (trans[t] == PlasmaTrans) continue;

            memcpy(Cinit,  C, LDCxN*sizeof(PLASMA_Complex32_t));
            memcpy(Cfinal, C, LDCxN*sizeof(PLASMA_Complex32_t));

            /* PLASMA CHER2K */
            PLASMA_cher2k(uplo[u], trans[t], N, K, alpha, A, LDA, B, LDB, beta, Cfinal, LDC);

            /* Check the solution */
            info_solution = check_solution(uplo[u], trans[t], N, K,
                                           alpha, A, LDA, B, LDB, beta, Cinit, Cfinal, LDC);

            if (info_solution == 0) {
                printf("***************************************************\n");
                printf(" ---- TESTING CHER2K (%5s, %s) ........... PASSED !\n", uplostr[u], transstr[t]);
                printf("***************************************************\n");
            }
            else {
                printf("************************************************\n");
                printf(" - TESTING CHER2K (%5s, %s) ... FAILED !\n", uplostr[u], transstr[t]);
                printf("************************************************\n");
            }
        }
    }

    free(A); free(B); free(C);
    free(Cinit); free(Cfinal);

    return 0;
}

/*--------------------------------------------------------------
 * Check the solution
 */

static int check_solution(PLASMA_enum uplo, PLASMA_enum trans, int N, int K,
                          PLASMA_Complex32_t alpha, PLASMA_Complex32_t *A, int LDA,
                          PLASMA_Complex32_t *B, int LDB,
                          float beta, PLASMA_Complex32_t *Cref, PLASMA_Complex32_t *Cplasma, int LDC)
{
    int info_solution;
    float Anorm, Bnorm, Cinitnorm, Cplasmanorm, Clapacknorm, Rnorm, result;
    float eps;
    PLASMA_Complex32_t beta_const;

    float *work = (float *)malloc(max(N, K)* sizeof(float));

    beta_const  = -1.0;
    Anorm       = LAPACKE_clange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm),
                                      (trans == PlasmaNoTrans) ? N : K,
                                      (trans == PlasmaNoTrans) ? K : N, A, LDA, work);
    Bnorm       = LAPACKE_clange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm),
                                      (trans == PlasmaNoTrans) ? N : K,
                                      (trans == PlasmaNoTrans) ? K : N, B, LDB, work);
    Cinitnorm   = LAPACKE_clange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), N, N, Cref,    LDC, work);
    Cplasmanorm = LAPACKE_clange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), N, N, Cplasma, LDC, work);

    cblas_cher2k(CblasColMajor, (CBLAS_UPLO)uplo, (CBLAS_TRANSPOSE)trans,
                 N, K, CBLAS_SADDR(alpha), A, LDA, B, LDB, (beta), Cref, LDC);

    Clapacknorm = LAPACKE_clange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), N, N, Cref, LDC, work);

    cblas_caxpy(LDC*N, CBLAS_SADDR(beta_const), Cplasma, 1, Cref, 1);

    Rnorm = LAPACKE_clange_work(LAPACK_COL_MAJOR, lapack_const(PlasmaInfNorm), N, N, Cref, LDC, work);

    eps = LAPACKE_slamch_work('e');

    printf("Rnorm %e, Anorm %e, Cinitnorm %e, Cplasmanorm %e, Clapacknorm %e\n",
           Rnorm, Anorm, Cinitnorm, Cplasmanorm, Clapacknorm);

    result = Rnorm / ((Anorm + Bnorm + Cinitnorm) * N * eps);
    printf("============\n");
    printf("Checking the norm of the difference against reference CHER2K \n");
    printf("-- ||Cplasma - Clapack||_oo/((||A||_oo+||C||_oo).N.eps) = %e \n", result);

    if (  isnan(Rnorm) || isinf(Rnorm) || isnan(result) || isinf(result) || (result > 10.0) ) {
         printf("-- The solution is suspicious ! \n");
         info_solution = 1;
    }
    else {
         printf("-- The solution is CORRECT ! \n");
         info_solution= 0 ;
    }

    free(work);

    return info_solution;
}


/**
 * \author Sylvain Marsat, University of Maryland - NASA GSFC
 *
 * \brief C header for the geometric coefficients entering the response for LIGO-VIRGO detectors.
 *
 *
 */

#ifndef _LLVGEOMETRY_H
#define _LLVGEOMETRY_H

#define _XOPEN_SOURCE 500

#ifdef __GNUC__
#define UNUSED __attribute__ ((unused))
#else
#define UNUSED
#endif

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <complex.h>
#include <time.h>
#include <unistd.h>
#include <getopt.h>
#include <stdbool.h>
#include <string.h>

#include <gsl/gsl_errno.h>
#include <gsl/gsl_bspline.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_min.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_complex.h>

#include "constants.h"

/********************************************************/
/**************** Type definitions **********************/

/* Enumerator for the detector selector and network selector */
typedef enum {LHO, LLO, VIRGO} Detectortag;
typedef enum {L, H, V, LH, LV, HV, LHV} Networktag;

/*************************************************************/
/**************** Geometrical constants **********************/

/**
 * \name LIGO Hanford Observatory 4km Interferometric Detector constants
 * The following constants describe the location and geometry of the
 * LIGO Hanford Observatory 4km Interferometric Detector.
 */
#define LAL_LHO_4K_DETECTOR_NAME               	"LHO_4k"	/**< LHO_4k detector name string */
#define LAL_LHO_4K_DETECTOR_PREFIX             	"H1"	/**< LHO_4k detector prefix string */
#define LAL_LHO_4K_DETECTOR_LONGITUDE_RAD      	-2.08405676917	/**< LHO_4k vertex longitude (rad) */
#define LAL_LHO_4K_DETECTOR_LATITUDE_RAD       	0.81079526383	/**< LHO_4k vertex latitude (rad) */
#define LAL_LHO_4K_DETECTOR_ELEVATION_SI       	142.554	/**< LHO_4k vertex elevation (m) */
#define LAL_LHO_4K_DETECTOR_ARM_X_AZIMUTH_RAD  	5.65487724844	/**< LHO_4k x arm azimuth (rad) */
#define LAL_LHO_4K_DETECTOR_ARM_Y_AZIMUTH_RAD  	4.08408092164	/**< LHO_4k y arm azimuth (rad) */
#define LAL_LHO_4K_DETECTOR_ARM_X_ALTITUDE_RAD 	-0.00061950000	/**< LHO_4k x arm altitude (rad) */
#define LAL_LHO_4K_DETECTOR_ARM_Y_ALTITUDE_RAD 	0.00001250000	/**< LHO_4k y arm altitude (rad) */
#define LAL_LHO_4K_DETECTOR_ARM_X_MIDPOINT_SI  	1997.54200000000	/**< LHO_4k x arm midpoint (m) */
#define LAL_LHO_4K_DETECTOR_ARM_Y_MIDPOINT_SI  	1997.52200000000	/**< LHO_4k y arm midpoint (m) */
#define LAL_LHO_4K_VERTEX_LOCATION_X_SI        	-2.16141492636e+06	/**< LHO_4k x-component of vertex location in Earth-centered frame (m) */
#define LAL_LHO_4K_VERTEX_LOCATION_Y_SI        	-3.83469517889e+06	/**< LHO_4k y-component of vertex location in Earth-centered frame (m) */
#define LAL_LHO_4K_VERTEX_LOCATION_Z_SI        	4.60035022664e+06	/**< LHO_4k z-component of vertex location in Earth-centered frame (m) */
#define LAL_LHO_4K_ARM_X_DIRECTION_X           	-0.22389266154	/**< LHO_4k x-component of unit vector pointing along x arm in Earth-centered frame */
#define LAL_LHO_4K_ARM_X_DIRECTION_Y           	0.79983062746	/**< LHO_4k y-component of unit vector pointing along x arm in Earth-centered frame */
#define LAL_LHO_4K_ARM_X_DIRECTION_Z           	0.55690487831	/**< LHO_4k z-component of unit vector pointing along x arm in Earth-centered frame */
#define LAL_LHO_4K_ARM_Y_DIRECTION_X           	-0.91397818574	/**< LHO_4k x-component of unit vector pointing along y arm in Earth-centered frame */
#define LAL_LHO_4K_ARM_Y_DIRECTION_Y           	0.02609403989	/**< LHO_4k y-component of unit vector pointing along y arm in Earth-centered frame */
#define LAL_LHO_4K_ARM_Y_DIRECTION_Z           	-0.40492342125	/**< LHO_4k z-component of unit vector pointing along y arm in Earth-centered frame */

/**
 * \name LIGO Livingston Observatory 4km Interferometric Detector constants
 * The following constants describe the location and geometry of the
 * LIGO Livingston Observatory 4km Interferometric Detector.
 */
#define LAL_LLO_4K_DETECTOR_NAME               	"LLO_4k"	/**< LLO_4k detector name string */
#define LAL_LLO_4K_DETECTOR_PREFIX             	"L1"	/**< LLO_4k detector prefix string */
#define LAL_LLO_4K_DETECTOR_LONGITUDE_RAD      	-1.58430937078	/**< LLO_4k vertex longitude (rad) */
#define LAL_LLO_4K_DETECTOR_LATITUDE_RAD       	0.53342313506	/**< LLO_4k vertex latitude (rad) */
#define LAL_LLO_4K_DETECTOR_ELEVATION_SI       	-6.574	/**< LLO_4k vertex elevation (m) */
#define LAL_LLO_4K_DETECTOR_ARM_X_AZIMUTH_RAD  	4.40317772346	/**< LLO_4k x arm azimuth (rad) */
#define LAL_LLO_4K_DETECTOR_ARM_Y_AZIMUTH_RAD  	2.83238139666	/**< LLO_4k y arm azimuth (rad) */
#define LAL_LLO_4K_DETECTOR_ARM_X_ALTITUDE_RAD 	-0.00031210000	/**< LLO_4k x arm altitude (rad) */
#define LAL_LLO_4K_DETECTOR_ARM_Y_ALTITUDE_RAD 	-0.00061070000	/**< LLO_4k y arm altitude (rad) */
#define LAL_LLO_4K_DETECTOR_ARM_X_MIDPOINT_SI  	1997.57500000000	/**< LLO_4k x arm midpoint (m) */
#define LAL_LLO_4K_DETECTOR_ARM_Y_MIDPOINT_SI  	1997.57500000000	/**< LLO_4k y arm midpoint (m) */
#define LAL_LLO_4K_VERTEX_LOCATION_X_SI        	-7.42760447238e+04	/**< LLO_4k x-component of vertex location in Earth-centered frame (m) */
#define LAL_LLO_4K_VERTEX_LOCATION_Y_SI        	-5.49628371971e+06	/**< LLO_4k y-component of vertex location in Earth-centered frame (m) */
#define LAL_LLO_4K_VERTEX_LOCATION_Z_SI        	3.22425701744e+06	/**< LLO_4k z-component of vertex location in Earth-centered frame (m) */
#define LAL_LLO_4K_ARM_X_DIRECTION_X           	-0.95457412153	/**< LLO_4k x-component of unit vector pointing along x arm in Earth-centered frame */
#define LAL_LLO_4K_ARM_X_DIRECTION_Y           	-0.14158077340	/**< LLO_4k y-component of unit vector pointing along x arm in Earth-centered frame */
#define LAL_LLO_4K_ARM_X_DIRECTION_Z           	-0.26218911324	/**< LLO_4k z-component of unit vector pointing along x arm in Earth-centered frame */
#define LAL_LLO_4K_ARM_Y_DIRECTION_X           	0.29774156894	/**< LLO_4k x-component of unit vector pointing along y arm in Earth-centered frame */
#define LAL_LLO_4K_ARM_Y_DIRECTION_Y           	-0.48791033647	/**< LLO_4k y-component of unit vector pointing along y arm in Earth-centered frame */
#define LAL_LLO_4K_ARM_Y_DIRECTION_Z           	-0.82054461286	/**< LLO_4k z-component of unit vector pointing along y arm in Earth-centered frame */

/**
 * \name VIRGO 3km Interferometric Detector constants
 * The following constants describe the location and geometry of the
 * VIRGO 3km Interferometric Detector.
 */
#define LAL_VIRGO_DETECTOR_NAME               	"VIRGO"	/**< VIRGO detector name string */
#define LAL_VIRGO_DETECTOR_PREFIX             	"V1"	/**< VIRGO detector prefix string */
#define LAL_VIRGO_DETECTOR_LONGITUDE_RAD      	0.18333805213	/**< VIRGO vertex longitude (rad) */
#define LAL_VIRGO_DETECTOR_LATITUDE_RAD       	0.76151183984	/**< VIRGO vertex latitude (rad) */
#define LAL_VIRGO_DETECTOR_ELEVATION_SI       	51.884	/**< VIRGO vertex elevation (m) */
#define LAL_VIRGO_DETECTOR_ARM_X_AZIMUTH_RAD  	0.33916285222	/**< VIRGO x arm azimuth (rad) */
#define LAL_VIRGO_DETECTOR_ARM_Y_AZIMUTH_RAD  	5.05155183261	/**< VIRGO y arm azimuth (rad) */
#define LAL_VIRGO_DETECTOR_ARM_X_ALTITUDE_RAD 	0.00000000000	/**< VIRGO x arm altitude (rad) */
#define LAL_VIRGO_DETECTOR_ARM_Y_ALTITUDE_RAD 	0.00000000000	/**< VIRGO y arm altitude (rad) */
#define LAL_VIRGO_DETECTOR_ARM_X_MIDPOINT_SI  	1500.00000000000	/**< VIRGO x arm midpoint (m) */
#define LAL_VIRGO_DETECTOR_ARM_Y_MIDPOINT_SI  	1500.00000000000	/**< VIRGO y arm midpoint (m) */
#define LAL_VIRGO_VERTEX_LOCATION_X_SI        	4.54637409900e+06	/**< VIRGO x-component of vertex location in Earth-centered frame (m) */
#define LAL_VIRGO_VERTEX_LOCATION_Y_SI        	8.42989697626e+05	/**< VIRGO y-component of vertex location in Earth-centered frame (m) */
#define LAL_VIRGO_VERTEX_LOCATION_Z_SI        	4.37857696241e+06	/**< VIRGO z-component of vertex location in Earth-centered frame (m) */
#define LAL_VIRGO_ARM_X_DIRECTION_X           	-0.70045821479	/**< VIRGO x-component of unit vector pointing along x arm in Earth-centered frame */
#define LAL_VIRGO_ARM_X_DIRECTION_Y           	0.20848948619	/**< VIRGO y-component of unit vector pointing along x arm in Earth-centered frame */
#define LAL_VIRGO_ARM_X_DIRECTION_Z           	0.68256166277	/**< VIRGO z-component of unit vector pointing along x arm in Earth-centered frame */
#define LAL_VIRGO_ARM_Y_DIRECTION_X           	-0.05379255368	/**< VIRGO x-component of unit vector pointing along y arm in Earth-centered frame */
#define LAL_VIRGO_ARM_Y_DIRECTION_Y           	-0.96908180549	/**< VIRGO y-component of unit vector pointing along y arm in Earth-centered frame */
#define LAL_VIRGO_ARM_Y_DIRECTION_Z           	0.24080451708	/**< VIRGO z-component of unit vector pointing along y arm in Earth-centered frame */

#if 0
{ /* so that editors will match succeeding brace */
#elif defined(__cplusplus)
}
#endif

#endif /* _LLVGEOMETRY_H */


#pragma once

#include <cassert>
#include <stdint.h>

#include <gsl/gsl-lite.hpp> // gsl::span
#include "HypCommands.h" // RECORD_LENGTH

// Utility constexpr function converting a enum class to its underlying type N/U
#include <type_traits>
template <typename E,
          typename = std::enable_if_t<std::is_enum<E>::value>>
constexpr auto ut(E e) noexcept
{
    return static_cast<std::underlying_type_t<E>>(e);
}

class SamplePoint {
public:
    explicit SamplePoint(const gsl::span<uint8_t>& data);

    enum ValueIndex {
        eSeconds = 0,
        eVolts   ,
        eAmps    ,
        emAh_Out , // recalculated
        emAh_In  , // -"- (is rarely present)
        eRPM     , // []
        eAltitude, //  RDU
        eTemp1   , // []
        eTemp2   , // []
        eTemp3   , // []
        eThrottle, // [RDU]
        eTempAmb , //  RDU

        ePowerIn,
        ePowerOut, // [PRM]
        eEfficiency,//[PRM]
        eThrust,   // [RPM]
        eNUM_VALUES
    };

    inline double& operator [](size_t idx) {
        if (idx < eNUM_VALUES) {
            return m_values[idx];
        } else {
            assert(!"Invalid index");
            static double ERROR = 0.0;
            return ERROR;
        }
    }

    inline const double& operator [](size_t idx) const {
        if (idx < eNUM_VALUES) {
            return m_values[idx];
        } else {
            assert(!"Invalid index");
            static const double ERROR = 0.0;
            return ERROR;
        }
    }

//static:
    static const char* SeriesName(size_t idx) {
        if (idx < eNUM_VALUES) { return sSeriesNames[idx]; }
        else                   { return "ERROR"; }
    }

    static const std::array<size_t, eNUM_VALUES> sSeriesPrecision;

private:
    std::array<double, eNUM_VALUES> m_values;
//static
    static const std::array<const char*, eNUM_VALUES> sSeriesNames;
};


/**
 *
 * @file core_dlatro.c
 *
 *  PLASMA core_blas kernel
 *  PLASMA is a software package provided by Univ. of Tennessee,
 *  Univ. of California Berkeley and Univ. of Colorado Denver
 *
 * @version 2.6.0
 * @author Azzam Haidar
 * @date 2010-11-15
 * @generated d Tue Jan  7 11:44:49 2014
 *
 **/
#include <lapacke.h>
#include "common.h"

/***************************************************************************//**
 *
 * @ingroup CORE_double
 *
 *  CORE_dlatro transposes a m-by-n matrix out of place.
 *
 *******************************************************************************
 *
 * @param[in] uplo
 *          Specifies whether the matrix A is upper triangular or lower
 *          triangular:
 *          = PlasmaUpper: the upper triangle of A and the lower triangle of B
 *          are referenced.
 *          = PlasmaLower: the lower triangle of A and the upper triangle of B
 *          are referenced.
 *          = PlasmaUpperLower: All A and B are referenced.
 *
 * @param[in] trans
 *          Specifies whether the matrix A is transposed, not transposed or
 *          ugate transposed:
 *          = PlasmaNoTrans:   B is a copy of A (equivalent to dlacpy);
 *          = PlasmaTrans:     B is the transpose of A;
 *          = PlasmaTrans: B is the ugate transpose of A.
 *
 * @param[in] M
 *         Number of rows of the matrix A and number of columns of the matrix B, if trans == Pasma[Conj]Trans.
 *         Number of rows of the matrix A and the matrix B, if trans == PasmaNoTrans.
 *
 * @param[in] N
 *         Number of columns of the matrix A and number of rows of the matrix B, if trans == Pasma[Conj]Trans.
 *         Number of columns of the matrix A and of the matrix B, if trans == PlasmaNoTrans.
 *
 * @param[in] A
 *         Matrix of size LDA-by-N, if trans == Pasma[Conj]Trans.
 *         Matrix of size LDA-by-M, if trans == PasmaNoTrans.
 *
 * @param[in] LDA
 *         The leading dimension of the array A.
 *         LDA >= max(1,M), if trans == Pasma[Conj]Trans.
 *         LDA >= max(1,N), if trans == PasmaNoTrans.
 *
 * @param[out] B
 *         Matrix of size LDB-by-M, if trans == Pasma[Conj]Trans.
 *         Matrix of size LDB-by-N, if trans == PasmaNoTrans.
 *
 * @param[in] LDB
 *         The leading dimension of the array B.
 *         LDB >= max(1,N), if trans == Pasma[Conj]Trans.
 *         LDB >= max(1,M), if trans == PasmaNoTrans.
 *
 *
 *******************************************************************************
 *
 * @return
 *         \retval PLASMA_SUCCESS successful exit
 *         \retval <0 if INFO = -k, the k-th argument had an illegal value
 *
 ******************************************************************************/
#if defined(PLASMA_HAVE_WEAK)
#pragma weak CORE_dlatro = PCORE_dlatro
#define CORE_dlatro PCORE_dlatro
#endif
int CORE_dlatro(PLASMA_enum uplo, PLASMA_enum trans,
                int M, int N,
                const double *A, int LDA,
                      double *B, int LDB)
{
    int i, j;

    /* Check input arguments */
    if ((uplo != PlasmaUpper) && (uplo != PlasmaLower) && (uplo != PlasmaUpperLower) ) {
        coreblas_error(1, "Illegal value of uplo");
        return -1;
    }
    if ((trans != PlasmaTrans) && (trans != PlasmaNoTrans) && (trans != PlasmaTrans) ) {
        coreblas_error(2, "Illegal value of trans");
        return -2;
    }
    if (M < 0) {
        coreblas_error(3, "Illegal value of M");
        return -3;
    }
    if (N < 0) {
        coreblas_error(4, "Illegal value of N");
        return -4;
    }
    if ( (LDA < max(1,M)) && (M > 0) ) {
        coreblas_error(6, "Illegal value of LDA");
        return -6;
    }
    if ( (LDB < max(1,N)) && (N > 0) ) {
        coreblas_error(8, "Illegal value of LDB");
        return -8;
    }

    if (trans == PlasmaNoTrans) {
        CORE_dlacpy(uplo, M, N, A, LDA, B, LDB);
    }
    else {
        if (trans == PlasmaTrans) {
            if(uplo == PlasmaUpper) {
                for(j=0; j<N; j++)
                    for(i=0; i<min(j+1,M); i++)
                        B[j+i*LDB] = (A[i+j*LDA]);
            }
            else if(uplo == PlasmaLower) {
                for(j=0;j<N;j++)
                    for(i=j;i<M;i++)
                        B[j+i*LDB] = (A[i+j*LDA]);
            }
            else {
                for(j=0;j<N;j++)
                    for(i=0;i<M;i++)
                        B[j+i*LDB] = (A[i+j*LDA]);
            }
        }
        else {
            if(uplo==PlasmaUpper) {
                for(j=0;j<N;j++)
                    for(i=0;i<min(j+1,M);i++)
                        B[j+i*LDB] = A[i+j*LDA];
            }
            else if(uplo==PlasmaLower) {
                for(j=0;j<N;j++)
                    for(i=j;i<M;i++)
                        B[j+i*LDB] = A[i+j*LDA];
            }
            else {
                for(j=0;j<N;j++)
                    for(i=0;i<M;i++)
                        B[j+i*LDB] = A[i+j*LDA];
            }
        }
    }

    return PLASMA_SUCCESS;
}