/**
 * File: scaleback_utils.c
 * Subroutine for the WFC3 background scaling
 *
 */
#include <gsl/gsl_vector.h>
#include <gsl/gsl_multifit_nlin.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_sort.h>
#include "aXe_grism.h"
#include "aXe_utils.h"
#include "spc_FITScards.h"
#include "spce_sect.h"
#include "spce_fitting.h"
#include "spce_is_in.h"
#include "spc_back.h"
#include "spce_pathlength.h"
#include "trfit_utils.h"
#include "nicback_utils.h"
#include "scaleback_utils.h"

/**
 * Function: fit_to_FITScards
 * Fills the results of the fitting into a set of fits cards.
 *
 * Parameters:
 * @param bck_vals   - vector with the fit results
 * @param npixels    - dimension of all images
 *
 * Returns:
 * @return cards - the list of fits header cards
 */
FITScards *fit_to_FITScards(const gsl_vector* bck_vals, const px_point npixels)
{
    char templt[FLEN_CARD];
    int i=0,keytype, f_status=0;
    int npix_tot;

    FITScards *cards;

    // allocate the cards
    cards = allocate_FITScards(7);

    // compute the number of pixels
    npix_tot = npixels.x * npixels.y;

    i=0;
    sprintf(templt,"SCALVAL = %e / computed scale value", gsl_vector_get(bck_vals, 0));
    fits_parse_template (templt, cards->cards[i++], &keytype, &f_status);
    sprintf(templt,"SCALERR = %e / error for scale value", gsl_vector_get(bck_vals, 1));
    fits_parse_template (templt, cards->cards[i++], &keytype, &f_status);
    sprintf(templt,"NPIXINI = %e / initial fill value", gsl_vector_get(bck_vals, 2));
    fits_parse_template (templt, cards->cards[i++], &keytype, &f_status);
    sprintf(templt,"NPIXFIN = %e / final fill value", gsl_vector_get(bck_vals, 3));
    fits_parse_template (templt, cards->cards[i++], &keytype, &f_status);
    sprintf(templt,"FRACINI = %e / initial fill value", gsl_vector_get(bck_vals, 2) / (float)npix_tot);
    fits_parse_template (templt, cards->cards[i++], &keytype, &f_status);
    sprintf(templt,"FRACFIN = %e / final fill value", gsl_vector_get(bck_vals, 3) / (float)npix_tot);
    fits_parse_template (templt, cards->cards[i++], &keytype, &f_status);
    sprintf(templt,"NITER = %d / number of iterations", (int)gsl_vector_get(bck_vals, 4));
    fits_parse_template (templt, cards->cards[i++], &keytype, &f_status);

    // return the filled cards
    return cards;
}

/**
 * Function: make_scale_back
 * Loads the various image data inputs and performs a fit using a kappa-sigma
 * clipping iteration. Then a scaled version of one of the input images
 * is produced and written to disk. Optionally, an ASCII list with the
 * pixel values before the fitting is produced.
 *
 * Parameters:
 * @param grism_image - full pathname to the grism image
 * @param grism_mask  - full pathname to the mask image
 * @param conf_file   - full pathname to the configuration file
 * @param scale_image - full pathname to the scaling (=master sky) image
 * @param bck_image   - full pathname to the scaled (=background) image
 * @param plist_name  - full pathname to the pixel list
 * @param scale_to_master - integer/boolean to specify what should be scaled as output
 * @param make_plis   - integer/boolean to request a pixel list as output
 *
 * Returns:
 * @return -
 */
void
make_scale_back(char grism_image[], const char grism_mask[], char conf_file[],
		const char scale_image[], char bck_image[], const char plist_name[],
		const int scale_to_master, const int make_plis)
{
  px_point npixels;

  gsl_matrix *gr_img;
  gsl_matrix *gr_dqval;
  gsl_matrix *gr_mask;
  gsl_matrix *sc_img;
  gsl_matrix *bck_img;

  gsl_vector *bck_vals;

  fitbck_data *fbck_data;
  //int f_status=0;

  FITScards *cards;

  aperture_conf  *conf;

  char  ID[MAXCHAR];

  // fix the extension name
  sprintf (ID, "BCK");

  // read the configuration file
  // determine all extensions
  conf = get_aperture_descriptor(conf_file);
  get_extension_numbers(grism_image, conf, conf->optkey1, conf->optval1);

  // load the scale image
  fprintf(stdout,"Loading DATA from: %s...", scale_image);
  sc_img = FITSimage_to_gsl(scale_image, 1, 1);
  fprintf(stdout,". Done.\n");

  // load the grism image
  fprintf(stdout,"Loading DATA from: %s...", grism_image);
  gr_img = FITSimage_to_gsl(grism_image, conf->science_numext, 1);
  fprintf(stdout,". Done.\n");

  if (conf->dq_numext < 0)
    {
    // allocate the space for the dq-image
    // set all values to 0.0
    gr_dqval = gsl_matrix_alloc(gr_img->size1, gr_img->size2);
    gsl_matrix_set_all (gr_dqval, 0.0);
    }
  else
    {
    // load the scale dq values
    fprintf(stdout,"Loading DQ from: %s...", grism_image);
    gr_dqval = FITSimage_to_gsl(grism_image, conf->dq_numext, 1);
    fprintf(stdout,". Done.\n");
    }

  // load the grism mask
  fprintf(stdout,"Loading DATA from: %s...", grism_mask);
  gr_mask = FITSimage_to_gsl(grism_mask, 2, 1);
  fprintf(stdout,". Done.\n");

  // get the number of pixels
  npixels.x = sc_img->size1;
  npixels.y = sc_img->size2;

  // report the number of pixels
  //fprintf(stdout,"Loading DATA from: %i pix\n", npixels.x * npixels.y);

  // allocate memory for the fit data
  fbck_data = alloc_fitbck_data(npixels.x * npixels.y);

  // fill the data into the structure
  if (scale_to_master)
    fill_cont_data(gr_img, gr_dqval, gr_mask, sc_img, fbck_data, conf->dqmask);
  else
    fill_mask_data(gr_img, gr_dqval, gr_mask, sc_img, fbck_data, conf->dqmask);

  // make a pixel list if desired
  if (make_plis)
    print_plis(fbck_data, plist_name);

  // make the fit
  bck_vals = make_ksig_scalefit(fbck_data);

  // report the result of the fit onto the screen
  fprintf(stdout, "\nScale result image %s : c0 = %f +- %f", grism_image, gsl_vector_get(bck_vals, 0), gsl_vector_get(bck_vals, 1));
  fprintf(stdout, "\nInitial fill factor: %.1f%%, final: %.1f%%", 100.0 * gsl_vector_get(bck_vals, 2) / ((float)npixels.x * (float)npixels.y), 100.0 *gsl_vector_get(bck_vals, 3) / ((float)npixels.x * (float)npixels.y));
  fprintf(stdout, "\nNumber of iterations: % 2i\n\n", (int)gsl_vector_get(bck_vals, 4));

  if (scale_to_master)
    bck_img = compute_scale_grism(gr_img, gr_dqval, gr_mask, conf->dqmask, bck_vals);
  else
    bck_img = compute_scale_master(sc_img, bck_vals);

  // write the background image to a file
  fprintf(stdout,"Writing data to: %s...", bck_image);
  gsl_to_FITSimage (bck_img, bck_image, 1, ID);
  cards = fit_to_FITScards(bck_vals, npixels);
  put_FITS_cards(bck_image, 1, cards);
  fprintf(stdout,". Done.\n");

  // release memory
  free_fitbck_data(fbck_data);
  free_FITScards(cards);
  gsl_matrix_free(sc_img);
  gsl_matrix_free(gr_img);
  gsl_matrix_free(gr_dqval);
  gsl_matrix_free(gr_mask);
  gsl_matrix_free(bck_img);
  gsl_vector_free(bck_vals);
  free_aperture_conf(conf);
}

/**
 * Function: print_plis
 * Prints the content of a pixel list structure
 * to an ASCII file.
 *
 * Parameters:
 * @param fbck_data  - the list of pixel values
 * @param plist_name - full pathname to the pixel list
 *
 * Returns:
 * @return -
 */
void
print_plis(const fitbck_data *fbck_data, const char plist_name[])
{
    int index=0;
    char Buffer[10240];
    FILE *fout;

    // open the pixel list
    fout = fopen(plist_name, "w");

    // go over all data values
    for (index=0; index < fbck_data->n_data; index++)
      {
      // put the values into the buffer
      sprintf (Buffer, "%i %i %e %e\n",fbck_data->x_pos[index], fbck_data->y_pos[index],
          fbck_data->x_values[index], fbck_data->y_values[index]);

      // push the buffer to the  file
      fputs (Buffer, fout);
      }

    // close the pixel file
    fclose(fout);
}

/**
 * Function: compute_scale_grism
 * Computes a scaled version of the input grism images, using the
 * scale given as input. Pixels masked in other images are given
 * a fixed value.
 *
 * Parameters:
 * @param gr_img  - the list of pixel values
 * @param gr_dqval - full pathname to the pixel list
 * @param gr_mask - full pathname to the pixel list
 * @param bck_vals - full pathname to the pixel list
 *
 * Returns:
 * @return bck_img - the scaled grism image
 */
gsl_matrix *
compute_scale_grism(gsl_matrix *gr_img, gsl_matrix *gr_dqval, gsl_matrix *gr_mask,
		int dqmask, gsl_vector *bck_vals)
{
    gsl_matrix *bck_img;

    int ii, jj;
    double scale=0.0;

    // get the scale
    scale = gsl_vector_get(bck_vals, 0);

    // allocate the space for the background image
    bck_img = gsl_matrix_alloc(gr_img->size1, gr_img->size2);

    // go over each row
    for (ii=0; ii < (int)gr_img->size1; ii++)
      // go over each column
      for (jj=0; jj < (int)gr_img->size2; jj++)
        // compute and set the value in the background image
        if (gsl_matrix_get(gr_mask, ii, jj) > 0.0 ||
            (int)gsl_matrix_get(gr_dqval, ii, jj) & dqmask)
          gsl_matrix_set(bck_img, ii, jj, MASK_VALUE);
        else
          // using the fit values only
          gsl_matrix_set(bck_img, ii, jj, gsl_matrix_get(gr_img, ii, jj) * scale);

    // return the background image
    return bck_img;
}

/**
 * Function: compute_scale_master
 * Computes a scaled version of the scaling (=master sky) image,
 * using the scale given as input.
 *
 * Parameters:
 * @param sc_img  - the list of pixel valuessc_img
 * @param bck_vals - full pathname to the pixel list
 *
 * Returns:
 * @return bck_img - the scaled grism image
 */
gsl_matrix *
compute_scale_master(const gsl_matrix *sc_img, const gsl_vector *bck_vals)
{
    gsl_matrix *bck_img;

    int ii, jj;
    double scale=0.0;

    // get the scale
    scale = gsl_vector_get(bck_vals, 0);

    // allocate the space for the background image
    bck_img = gsl_matrix_alloc(sc_img->size1, sc_img->size2);

    // go over each row
    for (ii=0; ii < (int)sc_img->size1; ii++)
      // go over each column
      for (jj=0; jj < (int)sc_img->size2; jj++)
        // scale the master sky
        gsl_matrix_set(bck_img, ii, jj, gsl_matrix_get(sc_img, ii, jj) * scale);

    // return the background image
    return bck_img;
}

/**
 * Function: fill_mask_data
 * Fills the pixel value structure with the data for the x/y-positions,
 * the grism image and the scaling image values and a weigth. dq-flagged
 * pixels and masked pixel are neglected. The mask is supposed to be a background
 * mask where pixel with a value < 900000 shall be neglected.
 * The weights are initially all set to 1.
 *
 * Parameters:
 * @param gr_img    - the grism image array
 * @param gr_dqval  - the dq-value array
 * @param gr_mask   - the grism mask array
 * @param sc_img    - the scaling image array
 * @param fbck_data - the pixel list structure
 * @param dqmask    - dq value from the configuration file
 *
 * Returns:
 * @return -
 */
void
fill_mask_data(gsl_matrix *gr_img, gsl_matrix *gr_dqval, gsl_matrix *gr_mask,
		gsl_matrix *sc_img, fitbck_data *fbck_data, int dqmask)
{
    int ix=0;
    int iy=0;
    int index=0;

    // intitialize the index for
    // the fit_data structure
    index = 0;

    // go over all pixels
    // in the grism image
    for (ix=0; ix < (int)sc_img->size1; ix++)
      {
      for (iy=0; iy < (int)sc_img->size2; iy++)
        {
        // set the scale image pixel as independent variable
        fbck_data->x_values[index] = gsl_matrix_get(gr_img, ix, iy);

        // set the grism image pixel as dependent variable
        fbck_data->y_values[index] = gsl_matrix_get(sc_img, ix, iy);;

        // set the x- and y-positions
        fbck_data->x_pos[index] = ix;
        fbck_data->y_pos[index] = iy;

        // check whether the pixel was masked out
        // or is part of an object
        if (gsl_matrix_get(sc_img, ix, iy) < 0.0 ||
            gsl_matrix_get(gr_mask, ix, iy) < -900000.0 ||
            ((int)gsl_matrix_get(gr_dqval, ix, iy) & dqmask))
          {
          continue;
          }
        else
          {
          // give the pixel full weight
          //x,y,value_back,value_grism_imag
          fbck_data->e_values[index] = 1.0;

          // enhance the counter
          index++;
          }
        }
      }

    fbck_data->n_data = index;
    fprintf(stdout, "\nNumber of pixels in structure: %i\n\n", fbck_data->n_data);

    if (fbck_data->n_data < 1)
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
          "aXe_SCALEBCK: no data left to determine the scale!\n");
}

/**
 * Function: fill_cont_data
 * Fills the pixel value structure with the data for the x/y-positions,
 * the grism image and the scaling image values and a weigth. dq-flagged
 * pixels and masked pixel are neglected. The mask is supposed to be a geometric
 * contamination where pixel with a value > 0 shall be neglected.
 * The weights are initially all set to 1.
 *
 * Parameters:
 * @param gr_img    - the grism image array
 * @param gr_dqval  - the dq-value array
 * @param gr_mask   - the grism mask array
 * @param sc_img    - the scaling image array
 * @param fbck_data - the pixel list structure
 *
 * Returns:
 * @return -
 */
void
fill_cont_data(gsl_matrix *gr_img, gsl_matrix *gr_dqval, gsl_matrix *gr_mask,
                gsl_matrix *sc_img, fitbck_data *fbck_data, int dqmask)
{
    int ix=0;
    int iy=0;
    int index=0;

    // intitialize the index for
    // the fit_data structure
    index = 0;

    // go over all pixels
    // in the grism image
    for (ix=0; ix < (int)sc_img->size1; ix++)
      {
      for (iy=0; iy < (int)sc_img->size2; iy++)
        {
        // set the scale image pixel as independent variable
        fbck_data->x_values[index] = gsl_matrix_get(sc_img, ix, iy);

        // set the grism image pixel as dependent variable
        fbck_data->y_values[index] = gsl_matrix_get(gr_img, ix, iy);;

        // set the x- and y-positions
        fbck_data->x_pos[index] = ix;
        fbck_data->y_pos[index] = iy;

        // check whether the pixel was masked out
        // or is part of an object
        if (gsl_matrix_get(sc_img, ix, iy) < 0.0 ||
            gsl_matrix_get(gr_mask, ix, iy) > 0.0 ||
            ((int)gsl_matrix_get(gr_dqval, ix, iy) & dqmask))
          continue;
        else
          {
          // give the pixel full weight
          //x,y,value_back,value_grism_imag
          fbck_data->e_values[index] = 1.0;

          // enhance the counter
          index++;
          }
        }
      }

    fbck_data->n_data = index;
    fprintf(stdout, "\nNumber of pixels in structure: %i\n\n", fbck_data->n_data);

    if (fbck_data->n_data < 1)
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
          "aXe_SCALEBCK: no data left to determine the scale!\n");
}

/**
 * Function: make_ksig_scalefit
 * Determines the scale from the values in the pixel value structure.
 * The results are determined iteratively while rejecting pixels via
 * kappa-sigma clipping to get robust measurements. The results are
 * the scale value, the standard deviation, the final number of
 * not rejected points and the number of iterations.
 *
 * Parameters:
 * @param fbck_data - the pixel value structure
 *
 * Returns:
 * @return bck_vals - a vector with the fit results
 */
gsl_vector *
make_ksig_scalefit(fitbck_data *fbck_data)
{
  gsl_vector *bck_vals=NULL;

  float old_scale;
  int index=0;
  int clipped=1;

  bck_vals = gsl_vector_alloc(6);

  // check whether iterations still
  // must be done and whether
  // the data was changed from clipping
  old_scale= 1.0e+06;
  while (index < N_BCKSCALE_ITER && clipped)
    {

    // make a non-weighted linear fit
    get_bck_scale(fbck_data->x_values, fbck_data->y_values, fbck_data->e_values,
        fbck_data->n_data, bck_vals);

    // make a clipping iteration
    clipped = clipp_scale_data(fbck_data, bck_vals, N_BCKSCALE_KAPPA);

    // break the iteration if the scale change is below a certain threshold
    if (fabs((gsl_vector_get(bck_vals, 0)-old_scale)/gsl_vector_get(bck_vals, 0)) < N_BCKSCALE_ACCUR)
      clipped = 0;

    // store the new scale
    old_scale = gsl_vector_get(bck_vals, 0);

    // inhance the clipping counter
    index++;
    }

  // set the number of iterations
  gsl_vector_set(bck_vals, 4, (double)index);

  // return the fit result
  return bck_vals;
}

/**
 * Function: get_bck_scale
 * Computes the scale value from the pixel values in two arrays.
 * The scale value is the median value of all input with weight.
 * Also the standard deviation, the total number of input and
 * the number of input with weight is returned.
 *
 * Parameters:
 * @param xs       - pixel values from one image
 * @param ys       - pixel values from second image
 * @param ws       - weight values
 * @param n_elem   - number of elements
 * @param bck_vals - vector for the result
 *
 * Returns:
 * @return -
 */
void
get_bck_scale(const double *xs, double *ys, double *ws,
		  const int n_elem, gsl_vector *bck_vals)
{
  int i, m;
  double *tmp;
  double median;
  double stdev;

  // allocate space for temporary vectors
  tmp = (double *) malloc (n_elem * sizeof (double));

  // initialize the array
  for (i = 0; i < n_elem; i++)
    tmp[i] = 0.0;

  // fill the temporary vectors
  // with scale values and weights
  m = 0;
  for (i = 0; i < n_elem; i++)
  {
    if (ws[i] > 0.0 && ys[i] != 0.0)
      {
      tmp[m] = xs[i] / ys[i];
      m++;
      }
  }

  // sort the vector
  gsl_sort( tmp, 1, m);

  // just confirm the sorting
  for (i = 1; i < m; i++)
    if (tmp[i] < tmp[i-1])
      fprintf(stdout, "Wrong: %f <--> %f\n", tmp[i],tmp[i-1]);

  // take the home made median
  median = tmp[(int)m/2];

  // get the standard deviation
  stdev = comp_stdev_from_array(tmp, m, median);

  // put the results in the vector
  gsl_vector_set(bck_vals, 0, median);
  gsl_vector_set(bck_vals, 1, stdev);
  gsl_vector_set(bck_vals, 2, (double)n_elem);
  gsl_vector_set(bck_vals, 3, (double)m);

  // release memory
  free(tmp);
}

/**
 * Function: clipp_scale_data
 * Set the weight in the pixel value structure such as to clip
 * point that deviate from the mean/characteristic value by
 * more than the allowed amount.
 *
 * Parameters:
 * @param fbck_data - pixel values from one image
 * @param bck_vals  - vector for the result
 * @param kappa     - vector for the result
 *
 * Returns:
 * @return nclip - integer/boolean indicating new clipping
 */
int
clipp_scale_data(fitbck_data *fbck_data, const gsl_vector *bck_vals,
		const float kappa)
{
    int index;
    int nclip=0;
    int newclip=0;
    double mean;
    //double stdev;
    double absdev;

    // get the mean value
    mean = gsl_vector_get(bck_vals, 0);

    // compute the maximum allowed deviation
    absdev = kappa * gsl_vector_get(bck_vals, 1);

    // go over all data values
    for (index=0; index < fbck_data->n_data; index++)
      {
      // avoid zero values
      if (fbck_data->y_values[index] != 0.0)
        {
        // check whether the actual value is outside the allowed range
        if (fabs(mean - (fbck_data->x_values[index]/fbck_data->y_values[index])) > absdev)
          {
          // check and mark clip changes
          if (fbck_data->e_values[index])
            newclip = 1;

          // set the weight, change the counter
          fbck_data->e_values[index] = 0.0;
          nclip++;
          }
        else
          {
          // set the weight
          fbck_data->e_values[index] = 1.0;
          }
        }
      else
        {
        // set the weight, change the counter
        fbck_data->e_values[index] = 0.0;
        nclip++;
        }
      }

    // return the newclip indicator
    return newclip;
}


#ifndef __NEURALNETWORK_INCLUDED__
#define __NEURALNETWORK_INCLUDED__

#ifdef __APPLE__
#include <Accelerate/Accelerate.h>
#else
#include <cblas.h>
#endif

#include <string>
#include "Frame.h"

using namespace std;

struct Matrix {
  double* data;
  int rows;
  int cols;
};

class NeuralNetwork {
 public:
  NeuralNetwork(string theta1_filename, string theta2_filename);
  ~NeuralNetwork();

  double* predict(Frame* frame);
 private:
  Matrix theta1_;
  Matrix theta2_;
};

Matrix read2DMatrixFromFile(string filename);

double sigmoid(double x);

#endif


#pragma once

#include <algorithm>
#include <array>
#include <cstdint>
#include <initializer_list>
#include <iterator>
#include <sstream>
#include <string>

#include <absl/types/span.h>
#include <gsl/gsl>

#include "chainerx/constant.h"
#include "chainerx/error.h"
#include "chainerx/optional_container_arg.h"
#include "chainerx/stack_vector.h"

namespace chainerx {

class Axes : public StackVector<int8_t, kMaxNdim> {
    using BaseVector = StackVector<int8_t, kMaxNdim>;

public:
    using const_iterator = BaseVector::const_iterator;
    using const_reverse_iterator = BaseVector::const_reverse_iterator;
    // TODO(niboshi): Declare other types required for this class to be a container.

    Axes() = default;

    ~Axes() = default;

    // by iterators
    template <typename InputIt>
    Axes(InputIt first, InputIt last) {
        if (std::distance(first, last) > kMaxNdim) {
            throw DimensionError{"too many dimensions: ", std::distance(first, last)};
        }
        insert(begin(), first, last);
    }

    // by span
    explicit Axes(absl::Span<const int8_t> axes) : Axes{axes.begin(), axes.end()} {}

    // by initializer list
    Axes(std::initializer_list<int8_t> axes) : Axes{axes.begin(), axes.end()} {}

    // copy
    Axes(const Axes&) = default;
    Axes& operator=(const Axes&) = default;

    // move
    Axes(Axes&&) = default;
    Axes& operator=(Axes&&) = default;

    std::string ToString() const;

    int8_t ndim() const noexcept { return gsl::narrow_cast<int8_t>(size()); }

    int8_t& operator[](int8_t index) {
        if (!(0 <= index && static_cast<size_t>(index) < size())) {
            throw DimensionError{"index out of bounds"};
        }
        return this->StackVector::operator[](index);
    }

    const int8_t& operator[](int8_t index) const {
        if (!(0 <= index && static_cast<size_t>(index) < size())) {
            throw DimensionError{"index out of bounds"};
        }
        return this->StackVector::operator[](index);
    }

    // span
    absl::Span<const int8_t> span() const { return {*this}; }
};

std::ostream& operator<<(std::ostream& os, const Axes& axes);

using OptionalAxes = OptionalContainerArg<Axes>;

namespace internal {

bool IsAxesPermutation(const Axes& axes, int8_t ndim);

// Normalizes possibly-negative axis to non-negative axis in [0, ndim).
// If `axis` does not fit in [-ndim, ndim), DimensionError is thrown.
int8_t NormalizeAxis(int8_t axis, int8_t ndim);

// Resolves the axis argument of many operations.
// Negative axes are converted to non-negative ones (by wrapping at ndim).
Axes GetNormalizedAxes(const Axes& axis, int8_t ndim);

// Resolves the axis argument of many operations.
// Negative axes are converted to non-negative ones (by wrapping at ndim).
// Axes are then sorted.
Axes GetSortedAxes(const Axes& axis, int8_t ndim);

// Resolves the axis argument of many operations.
// Negative axes are converted to non-negative ones (by wrapping at ndim).
// Axes are then sorted.
// nullopt is converted to a vector of all axes.
Axes GetSortedAxesOrAll(const OptionalAxes& axis, int8_t ndim);

}  // namespace internal
}  // namespace chainerx


#pragma once

#include "datastructures/MortonIndex.h"

#include <fstream>
#include <gsl/gsl>
#include <iostream>
#include <string>

struct OctreeIndexFileHeader
{
  const char magic[4] = { 'i', 'n', 'd', 'x' };
  uint32_t levels_per_index;
  size_t num_indices;
};

template<unsigned int MaxLevels>
void
write_octree_indices_to_file(const std::string& file_path,
                             gsl::span<MortonIndex<MaxLevels>> indices)
{
  OctreeIndexFileHeader header;
  header.levels_per_index = MaxLevels;
  header.num_indices = indices.size();

  std::ofstream fs{ file_path, std::ios::out | std::ios::binary };
  if (!fs.is_open()) {
    std::cerr << "Could not write index file " << file_path << std::endl;
    return;
  }

  fs.write(reinterpret_cast<char const*>(&header), sizeof(OctreeIndexFileHeader));
  for (auto idx : indices) {
    const auto idx_val = idx.get();
    fs.write(reinterpret_cast<char const*>(&idx_val), sizeof(idx_val));
  }

  fs.flush();
  fs.close();
}

template<unsigned int MaxLevels>
std::vector<MortonIndex<MaxLevels>>
read_octree_indices_from_file(const std::string& file_path)
{
  std::ifstream fs{ file_path, std::ios::in | std::ios::binary };
  if (!fs.is_open()) {
    std::cerr << "Could not read index file " << file_path << std::endl;
    return {};
  }

  fs.unsetf(std::ios::skipws);

  fs.seekg(0, std::ios::end);
  auto fileSize = fs.tellg();
  fs.seekg(0, std::ios::beg);

  std::vector<char> rawData;
  rawData.reserve(fileSize);

  rawData.insert(rawData.begin(), std::istream_iterator<char>(fs), std::istream_iterator<char>());

  const auto& header = *reinterpret_cast<OctreeIndexFileHeader const*>(rawData.data());

  if (header.levels_per_index != MaxLevels) {
    std::cerr << "Reading octree index file with indices that contain " << header.levels_per_index
              << " levels but requested to read into MortonIndexs with " << MaxLevels
              << " levels instead!" << std::endl;
    return {};
  }

  auto indices_begin = rawData.data() + sizeof(OctreeIndexFileHeader);
  const auto indices_end = rawData.data() + rawData.size();

  using IndexType_t = typename MortonIndex<MaxLevels>::Store_t;
  const auto index_binary_size = sizeof(IndexType_t);
  std::vector<MortonIndex<MaxLevels>> indices;
  indices.reserve(header.num_indices);
  for (; indices_begin < indices_end; indices_begin += index_binary_size) {
    indices.emplace_back(*reinterpret_cast<IndexType_t const*>(indices_begin));
  }

  return indices;
}


//
//  particle.h
//  EpiGenMCMC
//
//  Created by Lucy Li on 13/04/2016.
//  Copyright (c) 2016 Lucy Li, Imperial College London. All rights reserved.
//

#ifndef __EpiGenMCMC__particle__
#define __EpiGenMCMC__particle__

#include <vector>
#include <gsl/gsl_randist.h>
#include "trajectory.h"

class Particle {
    int num_particles;
    int num_groups;
    int num_time_steps;
    std::vector <Trajectory*> trajectories;
    std::vector <double> weights;
    std::vector <double> normalised_weights;
    std::vector <unsigned int> next_particles;
    std::vector <int> parents;
    std::vector <double> overall_traj; // Incidence: P1[Group1[T1, T2...], Group2[T1, T2...]...], P2[] ...
    std::vector <double> overall_traj2; // Prevalence: P1[Group1[T1, T2...], Group2[T1, T2...]...], P2[] ...
    std::vector <int> particle_ancestry;
    std::vector <int> non_zero_particles;
    std::vector <double> cumulative_weights;
    std::vector <int> empty;
    void replace(int, int);
    void normalise();
public:
    Particle();
    Particle(int, Trajectory);
    void start_particle_tracing(int, int);
    void save_traj_to_matrix(int, int);
    void save_traj_to_matrix(int, int, int);
    void save_ancestry(int, int);
    void save_ancestry(int, int, int);
    void reset_parents();
    int get_traj_random(gsl_rng *);
    void retrace_traj(Trajectory&, gsl_rng *);
    Trajectory* get_traj(int) const;
    void set_weight(double, int, bool);
    double get_weight(int);
    double get_total_weight();
    double get_ESS();
    void reset_weights();
    void resample(gsl_rng *);
    void clear();
};

#endif /* defined(__EpiGenMCMC__particle__) */


/* Some preprocessor magic for having fast inline functions. */
#include <gsl/gsl_matrix.h>

#ifndef INLINE
#define INLINE static inline
#endif
#ifndef INLINE_DECL
#define INLINE_DECL static inline
#endif

/* Some preprocessor magic for the "restrict" keyword:
	http://www.cellperformance.com/mike_acton/2006/05/demystifying_the_restrict_keyw.html */
#if __STDC_VERSION__ >= 199901
#elif defined (__GNUC__) && __GNUC__ >= 2 && __GNUC_MINOR__ >= 91
	#define restrict __restrict__
#else
	#define restrict
#endif

/* Some preprocessor magic for calling this library from C++ */

#ifdef __cplusplus
	#define restrict /* nothing */
#endif


/*******************************************************************************
NAME: extract_rgb_palette

ALGORITHM DESCRIPTION:
  Dumps RGB indexed color palette from graphics file to stdout

ISSUES:
        Only color indexed TIFF files currently supported

*******************************************************************************/
#include "asf.h"
#include "asf_nan.h"
#include <math.h>
#include <ctype.h>
#include "asf_raster.h"
#include "typlim.h"
#include <float_image.h>
#include <uint8_image.h>
#include <geokeys.h>
#include <geo_tiffp.h>
#include <geo_keyp.h>
#include <geotiff.h>
#include <geotiffio.h>
#include <tiff.h>
#include <tiffio.h>
#include <xtiffio.h>
#include <gsl/gsl_math.h>
#include "geotiff_support.h"
#include "extract_rgb_palette_help.h"

/**** TYPES ****/
typedef enum {
  UNKNOWN_GRAPHICS_TYPE=0,
  ASF_IMG,
  JPEG,
  PGM,
  PPM,
  PBM,
  STD_TIFF,
  GEO_TIFF,
  BMP,
  GIF,
  PNG
} graphics_file_t;

#define MISSING_TIFF_DATA -1
typedef struct {
  uint32 width;
  uint32 height;
  short sample_format;
  short bits_per_sample;
  short planar_config;
  data_type_t data_type; // ASF data type
  short num_bands;
  int is_scanline_format;
  int is_palette_color_tiff;
} tiff_data_t;

#define MISSING_GTIF_DATA -1
typedef struct {
  int gtif_data_exists;
  char *GTcitation;
  char *PCScitation;
  int tie_point_elements; // Number of tie point elements (usually a multiple of 6)
  int num_tie_points; // Number of elements divided by 6 since (i,j,k) maps to (x,y,z)
  double *tie_point;  // Usually only 1, but who knows what evil lurks in the hearts of men?
  int pixel_scale_elements; // Usually a multiple of 3
  int num_pixel_scales;
  double *pixel_scale;  // Should always be 3 of these ...for ScaleX, ScaleY, and ScaleZ
  short model_type;
  short raster_type;
  short linear_units;
  double scale_factor;
  datum_type_t datum;
  char hemisphere;
  unsigned long pro_zone; // UTM zone (UTM only)
  short proj_coords_trans;
  short pcs;
  short geodetic_datum;
  short geographic_datum;
  double false_easting;
  double false_northing;
  double natLonOrigin;
  double lonCenter;
  double falseOriginLon;
  double falseOriginLat;
  double natLatOrigin;
  double latCenter;
  double stdParallel1;
  double stdParallel2;
  double lonPole;
} geotiff_data_t;

/**** PROTOTYPES ****/
graphics_file_t getGraphicsFileType (char *file);
void graphicsFileType_toStr (graphics_file_t type, char *type_str);
float get_maxval(data_type_t data_type);
char *data_type2str(data_type_t data_type);
void get_tiff_info_from_file(char *file, tiff_data_t *t);
void get_tiff_info(TIFF *tif, tiff_data_t *t);
void get_geotiff_keys(char *file, geotiff_data_t *g);
void dump_tiff_rgb_palette(char *inFile);

int main(int argc, char **argv)
{
  char *inFile;
  char msg[1024];
  char type_str[255];

  asfSplashScreen(argc, argv);

  if (argc != 2) {
      usage();
      exit(1);
  }
  else {
      check_for_help(argc, argv);
  }

  inFile=argv[1];

  if (!fileExists(inFile)) {
    sprintf(msg, "File not found: %s\n  => Did you forget to use the filename extension?\n", inFile);
    asfPrintError(msg);
  }
  graphics_file_t type;
  type = getGraphicsFileType(inFile);
  if (type != STD_TIFF &&
      type != GEO_TIFF )
  {
    graphicsFileType_toStr(type, type_str);
    sprintf(msg, "Graphics file type %s is not currently supported (Graphics file: %s)\n",
            type_str, inFile);
    asfPrintError(msg);
  }

  switch (type) {
    case STD_TIFF:
    case GEO_TIFF:
      dump_tiff_rgb_palette(inFile);
      break;
    default:
      sprintf(msg, "Unrecognized image file type found.\n");
      asfPrintError(msg);
      break;
  }

  return (0);
}

graphics_file_t getGraphicsFileType (char *file)
{
  FILE *fp = (FILE *)FOPEN(file, "rb");
  uint8 magic[4];
  graphics_file_t file_type = UNKNOWN_GRAPHICS_TYPE;
  TIFF *itif = NULL;
  GTIF *gtif = NULL;

  if (fp != NULL) {
    int i;

    // Read the magic number from the file header
    for (i=0; i<4; i++) {
      magic[i] = (uint8)fgetc(fp);
    }
    if(fp) FCLOSE(fp);

    // Check for a valid magic number combination
    if (magic[0] == 0xFF && magic[1] == 0xD8) {
      file_type = JPEG;
    }
    else if (magic[0] == 'P' && (magic[1] == '4' || magic[1] == '1')) {
      file_type = PBM;
    }
    else if (magic[0] == 'P' && (magic[1] == '5' || magic[1] == '2')) {
      file_type = PGM;
    }
    else if (magic[0] == 'P' && (magic[1] == '6' || magic[1] == '3')) {
      file_type = PPM;
    }
    else if ((magic[0] == 'I' && magic[1] == 'I') || (magic[0] == 'M' && magic[1] == 'M')) {
      file_type = STD_TIFF;

      itif = XTIFFOpen(file, "rb");
      if (itif != NULL) {
        gtif = GTIFNew(itif);
        if (gtif != NULL) {
          double *tie_point = NULL;
          double *pixel_scale = NULL;
          short model_type, raster_type, linear_units;
          int read_count, tie_points, pixel_scales;

          (gtif->gt_methods.get)(gtif->gt_tif, GTIFF_TIEPOINTS, &tie_points, &tie_point);
          (gtif->gt_methods.get)(gtif->gt_tif, GTIFF_PIXELSCALE, &pixel_scales, &pixel_scale);
          read_count = GTIFKeyGet(gtif, GTModelTypeGeoKey, &model_type, 0, 1);
          read_count += GTIFKeyGet(gtif, GTRasterTypeGeoKey, &raster_type, 0, 0);
          read_count += GTIFKeyGet(gtif, ProjLinearUnitsGeoKey, &linear_units, 0, 1);

          if (tie_points == 6 && pixel_scales == 3 && read_count == 3) {
            file_type = GEO_TIFF;
          }

          if (tie_point != NULL) free(tie_point);
          if (pixel_scale != NULL) free(pixel_scale);
          GTIFFree(gtif);
        }
        XTIFFClose(itif);
      }
    }
    else if (magic[0] == 'B' && magic[1] == 'M') {
      file_type = BMP;
    }
    else if (magic[0] == 'G' && magic[1] == 'I' && magic[2] == 'F') {
      file_type = GIF;
    }
    else if (magic[1] == 'P' && magic[2] == 'N' && magic[3] == 'G') {
      file_type = PNG;
    }
    else {
      file_type = UNKNOWN_GRAPHICS_TYPE;
    }
  }

  return file_type;
}

void graphicsFileType_toStr (graphics_file_t type, char *type_str)
{
  switch (type) {
    case ASF_IMG:
      strcpy (type_str, "ASF_IMG");
      break;
    case JPEG:
      strcpy (type_str, "JPEG");
      break;
    case PBM:
      strcpy (type_str, "PBM");
      break;
    case PGM:
      strcpy (type_str, "PGM");
      break;
    case PPM:
      strcpy (type_str, "PPM");
      break;
    case STD_TIFF:
      strcpy (type_str, "TIFF");
      break;
    case GEO_TIFF:
      strcpy (type_str, "GEOTIFF");
      break;
    case BMP:
      strcpy (type_str, "BMP");
      break;
    case GIF:
      strcpy (type_str, "GIF");
      break;
    case PNG:
      strcpy (type_str, "PNG");
      break;
    case UNKNOWN_GRAPHICS_TYPE:
    default:
      strcpy(type_str, "UNRECOGNIZED");
      break;
  }
}

float get_maxval(data_type_t data_type)
{
  float ret;

  // Only non-complex types with 32 bits or less are supported
  switch (data_type) {
    case BYTE:
      ret = powf(2, sizeof(unsigned char)) - 1.0;
      break;
    case INTEGER16:
      ret = powf(2, sizeof(short int)) - 1.0;
      break;
    case INTEGER32:
      ret = powf(2, sizeof(int)) - 1.0;
      break;
    case REAL32:
      ret = MAXREAL;
      break;
    default:
      ret = 0.0;
      break;
  }

  return ret;
}

// User must free the returned string
char *data_type2str(data_type_t data_type)
{
  char *retstr = (char*)CALLOC(64, sizeof(char));

  switch (data_type) {
    case BYTE:
      strcpy(retstr, "BYTE");
      break;
    case INTEGER16:
      strcpy(retstr, "INTEGER16");
      break;
    case INTEGER32:
      strcpy(retstr, "INTEGER32");
      break;
    case REAL32:
      strcpy(retstr, "REAL32");
      break;
    case REAL64:
      strcpy(retstr, "REAL64");
      break;
    case COMPLEX_BYTE:
      strcpy(retstr, "COMPLEX_BYTE");
      break;
    case COMPLEX_INTEGER16:
      strcpy(retstr, "COMPLEX_INTEGER16");
      break;
    case COMPLEX_INTEGER32:
      strcpy(retstr, "COMPLEX_INTEGER32");
      break;
    case COMPLEX_REAL32:
      strcpy(retstr, "COMPLEX_REAL32");
      break;
    case COMPLEX_REAL64:
      strcpy(retstr, "COMPLEX_REAL64");
      break;
    default:
      strcpy(retstr, "UNKNOWN");
      break;
  }

  return retstr;
}

void get_tiff_info_from_file(char *file, tiff_data_t *t)
{
  TIFF *tif;

  t->sample_format = MISSING_TIFF_DATA;
  t->bits_per_sample = MISSING_TIFF_DATA;
  t->planar_config = MISSING_TIFF_DATA;
  t->data_type = 0;
  t->num_bands = 0;
  t->is_scanline_format = 0;
  t->height = 0;
  t->width = 0;

  tif = XTIFFOpen(file, "rb");
  if (tif != NULL) {
    get_tiff_info(tif, t);
  }

  XTIFFClose(tif);
}

void get_tiff_info(TIFF *tif, tiff_data_t *t)
{
  get_tiff_data_config(tif,
                       &t->sample_format,
                       &t->bits_per_sample,
                       &t->planar_config,
                       &t->data_type,
                       &t->num_bands,
                       &t->is_scanline_format,
                       &t->is_palette_color_tiff,
                       REPORT_LEVEL_NONE);
  TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &t->height);
  TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &t->width);
  if (t->planar_config != PLANARCONFIG_CONTIG &&
      t->planar_config != PLANARCONFIG_SEPARATE &&
      t->num_bands == 1)
  {
    t->planar_config = PLANARCONFIG_CONTIG;
  }
}

void get_geotiff_keys(char *file, geotiff_data_t *g)
{
  TIFF *tif = XTIFFOpen(file, "rb");
  GTIF *gtif = NULL;

  // Init values to 'missing'
  g->gtif_data_exists = 0;
  g->GTcitation = NULL;   // Should be unallocated
  g->PCScitation = NULL;  // Should be unallocated

  // Read geotiff info
  if (tif != NULL) {
    gtif = GTIFNew(tif);
    if (gtif != NULL) {
      int count, read_count;
      int citation_length;
      int typeSize;
      tagtype_t citation_type;

      // Get citations
      citation_length = GTIFKeyInfo(gtif, GTCitationGeoKey, &typeSize, &citation_type);
      if (citation_length > 0) {
        g->GTcitation = (char*)MALLOC(citation_length * typeSize);
        GTIFKeyGet(gtif, GTCitationGeoKey, g->GTcitation, 0, citation_length);
      }
      else {
        g->GTcitation = NULL;
      }
      citation_length = GTIFKeyInfo(gtif, PCSCitationGeoKey, &typeSize, &citation_type);
      if (citation_length > 0) {
        g->PCScitation = (char*)MALLOC(citation_length * typeSize);
        GTIFKeyGet(gtif, PCSCitationGeoKey, g->PCScitation, 0, citation_length);
      }
      else {
        g->PCScitation = NULL;
      }
      if ((g->GTcitation != NULL && strlen(g->GTcitation) > 0) ||
           (g->PCScitation != NULL && strlen(g->PCScitation) > 0))
      {
        g->gtif_data_exists = 1;
      }

      // Get tie points and pixel scale
      (gtif->gt_methods.get)(gtif->gt_tif, GTIFF_TIEPOINTS, &count, &g->tie_point);
      if (count >= 6) {
        g->gtif_data_exists = 1;
        g->tie_point_elements = count;
        g->num_tie_points = count / 6;
      }
      (gtif->gt_methods.get)(gtif->gt_tif, GTIFF_PIXELSCALE, &count, &g->pixel_scale);
      if (count >= 3) {
        g->gtif_data_exists = 1;
        g->pixel_scale_elements = count;
        g->num_pixel_scales = count / 3;
      }

      // Get model type, raster type, and linear units
      read_count = GTIFKeyGet (gtif, GTModelTypeGeoKey, &g->model_type, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      read_count = GTIFKeyGet (gtif, GTRasterTypeGeoKey, &g->raster_type, 0, 0);
      if (read_count >= 1) g->gtif_data_exists = 1;
      read_count = GTIFKeyGet (gtif, ProjLinearUnitsGeoKey, &g->linear_units, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;

      // Get UTM related info if it exists
      read_count = GTIFKeyGet(gtif, ProjectedCSTypeGeoKey, &g->pcs, 0, 1);
      if (read_count == 1 && PCS_2_UTM(g->pcs, &g->hemisphere, &g->datum, &g->pro_zone)) {
        g->gtif_data_exists = 1;
      }
      else {
        read_count = GTIFKeyGet(gtif, ProjectionGeoKey, &g->pcs, 0, 1);
        if (read_count == 1 && PCS_2_UTM(g->pcs, &g->hemisphere, &g->datum, &g->pro_zone)) {
          g->gtif_data_exists = 1;
        }
        else {
          g->hemisphere = '\0';
          g->datum = UNKNOWN_DATUM;
          g->pro_zone = MISSING_GTIF_DATA;
        }
      }

      // Get projection type (ProjCoordTransGeoKey) and other projection parameters
      read_count = GTIFKeyGet(gtif, ProjCoordTransGeoKey, &g->proj_coords_trans, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->proj_coords_trans = MISSING_GTIF_DATA;
      read_count = GTIFKeyGet(gtif, GeographicTypeGeoKey, &g->geographic_datum, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->geographic_datum = MISSING_GTIF_DATA;
      read_count = GTIFKeyGet(gtif, GeogGeodeticDatumGeoKey, &g->geodetic_datum, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->geodetic_datum = MISSING_GTIF_DATA;
      read_count = GTIFKeyGet(gtif, ProjScaleAtNatOriginGeoKey, &g->scale_factor, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->scale_factor = MISSING_GTIF_DATA;

      // Get generic projection parameters (Note: projection type is defined by
      // the g->proj_coords_trans value)
      read_count = GTIFKeyGet (gtif, ProjFalseEastingGeoKey, &g->false_easting, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->false_easting = MISSING_GTIF_DATA;
      read_count = GTIFKeyGet (gtif, ProjFalseNorthingGeoKey, &g->false_northing, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->false_northing = MISSING_GTIF_DATA;
      read_count = GTIFKeyGet (gtif, ProjNatOriginLongGeoKey, &g->natLonOrigin, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->natLonOrigin = MISSING_GTIF_DATA;
      read_count = GTIFKeyGet (gtif, ProjNatOriginLatGeoKey, &g->natLatOrigin, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->natLatOrigin = MISSING_GTIF_DATA;
      read_count = GTIFKeyGet (gtif, ProjStdParallel1GeoKey, &g->stdParallel1, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->stdParallel1 = MISSING_GTIF_DATA;
      read_count = GTIFKeyGet (gtif, ProjStdParallel2GeoKey, &g->stdParallel2, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->stdParallel2 = MISSING_GTIF_DATA;
      read_count = GTIFKeyGet (gtif, ProjCenterLongGeoKey, &g->lonCenter, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->lonCenter = MISSING_GTIF_DATA;
      read_count = GTIFKeyGet (gtif, ProjCenterLatGeoKey, &g->latCenter, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->latCenter = MISSING_GTIF_DATA;
      read_count = GTIFKeyGet (gtif, ProjFalseOriginLongGeoKey, &g->falseOriginLon, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->falseOriginLon = MISSING_GTIF_DATA;
      read_count = GTIFKeyGet (gtif, ProjFalseOriginLatGeoKey, &g->falseOriginLat, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->falseOriginLat = MISSING_GTIF_DATA;
      read_count = GTIFKeyGet (gtif, ProjStraightVertPoleLongGeoKey, &g->lonPole, 0, 1);
      if (read_count >= 1) g->gtif_data_exists = 1;
      else g->lonPole = MISSING_GTIF_DATA;
    }
  }
}

void tiff_get_float_line(TIFF *tif, float *buf, int row, int band_no)
{
  tiff_data_t t;

  get_tiff_info(tif, &t);
  // Note: For single-plane (greyscale) images, planar_config may remain unset so
  // we can't use it as a guide for checking TIFF validity...
  if (t.sample_format == MISSING_TIFF_DATA ||
      t.bits_per_sample == MISSING_TIFF_DATA ||
      t.data_type == 0 ||
      t.num_bands == MISSING_TIFF_DATA ||
      t.is_scanline_format == MISSING_TIFF_DATA ||
      t.height == 0 ||
      t.width == 0)
  {
    asfPrintError("Cannot read tif file\n");
  }

  // Read a scanline
  tsize_t scanlineSize = TIFFScanlineSize(tif);
  tdata_t *tif_buf = _TIFFmalloc(scanlineSize);
  if (t.planar_config == PLANARCONFIG_CONTIG || t.num_bands == 1) {
    TIFFReadScanline(tif, tif_buf, row, 0);
  }
  else {
    // Planar configuration is band-sequential
    TIFFReadScanline(tif, tif_buf, row, band_no);
  }

  int col;
  for (col=0; col<t.width; col++) {
    switch(t.bits_per_sample) {
      case 8:
        switch(t.sample_format) {
          case SAMPLEFORMAT_UINT:
            if (t.planar_config == PLANARCONFIG_CONTIG && t.num_bands > 1) {
              buf[col] = (float)(((uint8*)(tif_buf))[(col*t.num_bands)+band_no]);   // Current sample.
            }
            else {
              // Planar configuration is band-sequential or single-banded
              buf[col] = (float)(((uint8*)(tif_buf))[col]);
            }
            break;
          case SAMPLEFORMAT_INT:
            if (t.planar_config == PLANARCONFIG_CONTIG && t.num_bands > 1) {
              buf[col] = (float)(((int8*)(tif_buf))[(col*t.num_bands)+band_no]);   // Current sample.
            }
            else {
              // Planar configuration is band-sequential or single-banded
              buf[col] = (float)(((int8*)(tif_buf))[col]);   // Current sample.
            }
            break;
          default:
            // There is no such thing as an IEEE 8-bit floating point
            if (tif_buf) _TIFFfree(tif_buf);
            if (tif) XTIFFClose(tif);
            asfPrintError("tiff_get_float_line(): Unexpected data type in TIFF file.\n");
            break;
        }
        break;
      case 16:
        switch(t.sample_format) {
          case SAMPLEFORMAT_UINT:
            if (t.planar_config == PLANARCONFIG_CONTIG && t.num_bands > 1) {
              buf[col] = (float)(((uint16*)(tif_buf))[(col*t.num_bands)+band_no]);   // Current sample.
            }
            else {
              // Planar configuration is band-sequential or single-banded
              buf[col] = (float)(((uint16*)(tif_buf))[col]);   // Current sample.
            }
            break;
          case SAMPLEFORMAT_INT:
            if (t.planar_config == PLANARCONFIG_CONTIG && t.num_bands > 1) {
              buf[col] = (float)(((int16*)(tif_buf))[(col*t.num_bands)+band_no]);   // Current sample.
            }
            else {
              // Planar configuration is band-sequential or single-banded
              buf[col] = (float)(((uint16*)(tif_buf))[col]);   // Current sample.
            }
            break;
          default:
            // There is no such thing as an IEEE 16-bit floating point
            if (tif_buf) _TIFFfree(tif_buf);
            if (tif) XTIFFClose(tif);
            asfPrintError("tiff_get_float_line(): Unexpected data type in TIFF file.\n");
            break;
        }
        break;
      case 32:
        switch(t.sample_format) {
          case SAMPLEFORMAT_UINT:
            if (t.planar_config == PLANARCONFIG_CONTIG && t.num_bands > 1) {
              buf[col] = (float)(((uint32*)(tif_buf))[(col*t.num_bands)+band_no]);   // Current sample.
            }
            else {
              // Planar configuration is band-sequential or single-banded
              buf[col] = (float)(((uint32*)(tif_buf))[col]);   // Current sample.
            }
            break;
          case SAMPLEFORMAT_INT:
            if (t.planar_config == PLANARCONFIG_CONTIG && t.num_bands > 1) {
              buf[col] = (float)(((long*)(tif_buf))[(col*t.num_bands)+band_no]);   // Current sample.
            }
            else {
              // Planar configuration is band-sequential or single-banded
              buf[col] = (float)(((long*)(tif_buf))[col]);   // Current sample.
            }
            break;
          case SAMPLEFORMAT_IEEEFP:
            if (t.planar_config == PLANARCONFIG_CONTIG && t.num_bands > 1) {
              buf[col] = (float)(((float*)(tif_buf))[(col*t.num_bands)+band_no]);   // Current sample.
            }
            else {
              // Planar configuration is band-sequential or single-banded
              buf[col] = (float)(((float*)(tif_buf))[col]);   // Current sample.
            }
            break;
          default:
            if (tif_buf) _TIFFfree(tif_buf);
            if (tif) XTIFFClose(tif);
            asfPrintError("tiff_get_float_line(): Unexpected data type in TIFF file.\n");
            break;
        }
        break;
      default:
        if (tif_buf) _TIFFfree(tif_buf);
        if (tif) XTIFFClose(tif);
        asfPrintError("tiff_get_float_line(): Unexpected data type in TIFF file.\n");
        break;
    }
  }
  if (tif_buf) {
    _TIFFfree(tif_buf);
  }
}

float tiff_image_get_float_pixel(TIFF *tif, int row, int col, int band_no)
{
  tiff_data_t t;
  float cs;

  get_tiff_info(tif, &t);
  // Note: For single-plane (greyscale) images, planar_config may remain unset so
  // we can't use it as a guide for checking TIFF validity...
  if (t.sample_format == MISSING_TIFF_DATA ||
      t.bits_per_sample == MISSING_TIFF_DATA ||
      t.data_type == 0 ||
      t.num_bands == MISSING_TIFF_DATA ||
      t.is_scanline_format == MISSING_TIFF_DATA ||
      t.height == 0 ||
      t.width == 0)
  {
    asfPrintError("Cannot read tif file\n");
  }

  // Get a float line from the file
  float *buf = (float*)MALLOC(t.width*sizeof(float));
  tiff_get_float_line(tif, buf, row, band_no);
  cs = buf[col];
  FREE(buf);

  // Return as float
  return cs;
}

void dump_tiff_rgb_palette(char *inFile)
{
    TIFF *tiff = NULL;
    tiff_data_t tiff_data;

    get_tiff_info_from_file(inFile, &tiff_data);

    tiff = XTIFFOpen(inFile, "rb");
    if (!tiff) asfPrintError("Cannot open TIFF file (%s)\n", inFile);

    int color;
    int num_colors=(int)powl(2,tiff_data.bits_per_sample);
    uint16* red=NULL;
    uint16* green=NULL;
    uint16* blue=NULL;
    uint16 rmin, gmin, bmin, min;
    uint16 rmax, gmax, bmax, max;

    TIFFGetField(tiff, TIFFTAG_COLORMAP, &red, &green, &blue);
    if (red == NULL || green == NULL || blue == NULL) asfPrintError("Cannot read palette from TIFF file (%s)\n", inFile);

    rmin=gmin=bmin=min=65535; // Max uint16
    rmax=gmax=bmax=max=0; // Min uint16
    for (color=0; color<num_colors; color++) {
        unsigned short r, g, b;

        r=red[color];
        g=green[color];
        b=blue[color];

        r = (unsigned short)(((float)r/65535)*255 + .5);
        g = (unsigned short)(((float)g/65535)*255 + .5);
        b = (unsigned short)(((float)b/65535)*255 + .5);

        rmin = (r < rmin) ? r : rmin;
        gmin = (g < gmin) ? g : gmin;
        bmin = (b < bmin) ? b : bmin;
        min = (r < min) ? r : min;
        min = (g < min) ? g : min;
        min = (b < min) ? b : min;

        rmax = (r > rmax) ? r : rmax;
        gmax = (g > gmax) ? g : gmax;
        bmax = (b > bmax) ? b : bmax;
        max = (r > max) ? r : max;
        max = (g > max) ? g : max;
        max = (b > max) ? b : max;
    }
    fprintf(stdout, "# red min = %03d, green min = %03d, blue min = %03d, overall min = %03d\n",
            rmin, gmin, bmin, min);
    fprintf(stdout, "# red max = %03d, green max = %03d, blue max = %03d, overall max = %03d\n",
            rmax, gmax, bmax, max);
    fprintf(stdout, "# \n# color\tred\tgreen\tblue\n");
    for (color=0; color<num_colors; color++) {
        unsigned short r, g, b;

        r=red[color];
        g=green[color];
        b=blue[color];

        r = (unsigned short)(((float)r/65535)*255 + .5);
        g = (unsigned short)(((float)g/65535)*255 + .5);
        b = (unsigned short)(((float)b/65535)*255 + .5);
        fprintf(stdout, "%03d\t%03d\t%03d\t%03d\n",
                color, r, g, b);
    }

    XTIFFClose(tiff);
}


/* $Id$      */
/*--------------------------------------------------------------------*/
/*;  Copyright (C) 2012-2016                                          */
/*;  Associated Universities, Inc. Washington DC, USA.                */
/*;                                                                   */
/*;  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 Massachusetts Ave, Cambridge,     */
/*;  MA 02139, USA.                                                   */
/*;                                                                   */
/*;Correspondence about this software should be addressed as follows: */
/*;         Internet email: bcotton@nrao.edu.                         */
/*;         Postal address: William Cotton                            */
/*;                         National Radio Astronomy Observatory      */
/*;                         520 Edgemont Road                         */
/*;                         Charlottesville, VA 22903-2475 USA        */
/*--------------------------------------------------------------------*/
#ifndef OBITPOLNCALFIT_H
#define OBITPOLNCALFIT_H

#include "Obit.h"
#include "ObitErr.h"
#include "ObitUV.h"
#include "ObitThread.h"
#include "ObitInfoList.h"
#include "ObitSource.h"
#include "ObitSourceList.h"
#include "ObitAntennaList.h"
#include "ObitComplex.h"
#include "ObitTableCP.h"
#include "ObitTablePD.h"
#include "ObitTableBP.h"
#ifdef HAVE_GSL
#include <gsl/gsl_multifit_nlin.h>
#endif /* HAVE_GSL */

/*-------- Obit:  Merx mollis mortibus nuper ------------------*/
/**
 * \file ObitPolnCalFit.h
 *
 * ObitPolnCalFit Class for fitting polarization calibration to UV data.
 *
 * This class does least squares fitting of a full nonlinear model
 * To instrumental polarization and calibration terms and optionally source
 * polarization parameters.
 * Results are stored in a combination of AIPS PD (instrumental poln),
 * AIPS CP (source poln) and AIPS BP (gain corrections including R-L phase).
 *
 * \section ObitPolnCalFitaccess Creators and Destructors
 * An ObitPolnCalFit will usually be created using ObitPolnCalFitCreate which allows
 * specifying a name for the object as well as other information.
 *
 * A copy of a pointer to an ObitPolnCalFit should always be made using the
 * #ObitPolnCalFitRef function which updates the reference count in the object.
 * Then whenever freeing an ObitPolnCalFit or changing a pointer, the function
 * #ObitPolnCalFitUnref will decrement the reference count and destroy the object
 * when the reference count hits 0.
 * There is no explicit destructor.
 */
/*---------------Private structures----------------*/
/**
 * \enum polnParmType
 * enum for I/O file type.
 * This specifies the type of underlying data file.
 */
enum polnParmType {
  /** Unspecified */
  polnParmUnspec=0,
  /** Source parameter */
  polnParmSou,
  /** Antenna parameter */
  polnParmAnt,
  /** Antenna gain */
  polnParmGain,
  /** Phase difference  */
  polnParmPD
}; /* end enum polnParmType */

/** typedef for enum for polnParmType . */
typedef enum polnParmType PolnParmType;

/* Threaded function argument */
typedef struct {
  /* Obit error stack object */
  ObitErr      *err;
  /** thread    */
  ObitThread    *thread;
  /** thread number, >0 -> no threading   */
  olong        ithread;
  /** Do error analysis? */
  gboolean doError;
  /** First 0-rel datum in Data/Wt arrays */
  olong lo;
  /** Last 0-rel datum  in Data/Wt arrays */
  olong hi;
  /** selected antenna, 0-> all */
  olong selAnt;
  /** selected source, 0-> all */
  olong selSou;
 /** Number of visibilities being fitted */
  olong nvis;
 /** Number of data points being fitted (nvis*4) */
  olong ndata;
  /** Array of input data arrays, [10 x nvis]
      each row: 2*parallactic angle (rad), blank,
      RR_r, RR_i, LL_r, LL_i, RL_r, RL_i,  LR_r, LR_i   */
  ofloat *inData;
  /** Array of input data weights (~ 1/var), [4 x nvis]
      each row: RR, LL, RL,  LR   */
  ofloat *inWt;
  /** Source no of visibility, in range [0,nsou-1] */
  olong *souNo;
  /** Antenna numbers  of visibility, in range [0,nant-1] */
  olong *antNo;
  /** Number of antennas */
  olong nant;
  /** Reference antenna */
  olong refAnt;
  /** Fit only reference antenna R-L phase */
  gboolean doFitRL;
  /** Fit global X & Y feed gains?: */
  gboolean doFitGain;
  /** Are the feeds circularly polarized? */
  gboolean isCircFeed;
  /** Reference frequency (Hz) */
  odouble refFreq;
  /** Current frequency (Hz) */
  odouble curFreq;
  /** R-L (or X-Y) phase difference */
  odouble PD;
  /** Error estimate R-L (or X-Y) phase difference */
  odouble PDerr;
  /** Fit Stokes I poln, per cal */
  gboolean *doFitI;
  /** Fit fit source linear poln, per cal */
  gboolean *doFitPol;
  /** Fit fit Stokes V poln, per cal */
  gboolean *doFitV;
  /** Antenna parameters 4 x nant,
      each row: D1_r, D1_i, D2_r, D2_i */
  odouble *antParm;
  /** Antenna error estimates 4 x nant */
  odouble *antErr;
  /** Antenna parameters fit flags, 4 x nant */
  gboolean **antFit;
  /** Antenna parameters number, 4 x nant */
  olong **antPNumb;
  /** Antenna gains 2 x nant, each row: Gain_X, gain_Y */
  odouble *antGain;
  /** Antenna gains error estimates 2 x nant */
  odouble *antGainErr;
  /* Antenna parameters fit flags, 2 x nant */
  gboolean **antGainFit;
  /** Antenna gain parameters number, 2 x nant */
  olong **antGainPNumb;
  /** Number of calibrator sources */
  olong nsou;
  /** Source parameters 4 x nsou,
     each row: I, Poln_amp, Poln_RL (rad), V */
  odouble *souParm;
  /** Source error estimates 4 x nsou, */
  odouble *souErr;
  /** Source parameters fit flags, 4 x nsou */
  gboolean **souFit;
  /** Source parameters number, 4 x nsou */
  olong **souPNumb;
  /** Source Ids in SU table, Data */
  olong *souIDs;
  /** Inverse Source Ids lookup, index of data source ID-1
      gives calibrator number */
  olong *isouIDs;
  /** Number of parameters being fitted */
  olong nparam;
  /** Number of valid parallel observations */
  olong nPobs;
  /** Number of valid cross pol observations */
  olong nXobs;
  /** Chi squared of fit */
  ofloat ChiSq;
  /** Sum of parallel residuals */
  odouble sumParResid;
  /** Sum of cross residuals */
  odouble sumXResid;
  /** Parameter type */
  PolnParmType paramType;
  /** Parameter number */
  olong paramNumber;
  /** Sum of first derivatives */
  odouble sumDeriv;
  /** Sum of second derivatives */
  odouble sumDeriv2;
  /** Sum of weights */
  odouble sumWt;
  /** Frequency of data */
  odouble freq;
  /** R-L Phase of calibrators (rad) at Freq */
  ofloat *RLPhase;
  /** Fractional polarization of calibrators */
  ofloat *PPol;
  /** Derivative of PPol (GHz) of calibrators */
  ofloat *dPPol;
  /** Central channel (1-rel) of fit */
  olong Chan;
  /** IF number (1-rel) */
  olong IFno;
  /** complex work arrays for antenna based parameters, max 100 ant */
  dcomplex RS[100], RD[100], LS[100], LD[100],
    RSc[100], RDc[100], LSc[100], LDc[100],
    PR[100], PRc[100], PL[100], PLc[100];
  odouble SR[100], DR[100], SL[100], DL[100];
  /** Max antenna number */
  olong maxAnt;
  /** Solution method */
  gchar solnType[5];
} PolnFitArg;

/*--------------Class definitions-------------------------------------*/
/** ObitPolnCalFit Class structure. */
typedef struct {
#include "ObitPolnCalFitDef.h"   /* this class definition */
} ObitPolnCalFit;

/*----------------- Macroes ---------------------------*/
/**
 * Macro to unreference (and possibly destroy) an ObitPolnCalFit
 * returns a ObitPolnCalFit*.
 * in = object to unreference
 */
#define ObitPolnCalFitUnref(in) ObitUnref (in)

/**
 * Macro to reference (update reference count) an ObitPolnCalFit.
 * returns a ObitPolnCalFit*.
 * in = object to reference
 */
#define ObitPolnCalFitRef(in) ObitRef (in)

/**
 * Macro to determine if an object is the member of this or a
 * derived class.
 * Returns TRUE if a member, else FALSE
 * in = object to reference
 */
#define ObitPolnCalFitIsA(in) ObitIsA (in, ObitPolnCalFitGetClass())

/*---------------Public functions---------------------------*/
/** Public: Class initializer. */
void ObitPolnCalFitClassInit (void);

/** Public: Default Constructor. */
ObitPolnCalFit* newObitPolnCalFit (gchar* name);

/** Public: Create/initialize ObitPolnCalFit structures */
ObitPolnCalFit* ObitPolnCalFitCreate (gchar* name);
/** Typedef for definition of class pointer structure */
typedef ObitPolnCalFit* (*ObitPolnCalFitCreateFP) (gchar* name);

/** Public: ClassInfo pointer */
gconstpointer ObitPolnCalFitGetClass (void);

/** Public: Copy (deep) constructor. */
ObitPolnCalFit* ObitPolnCalFitCopy  (ObitPolnCalFit *in,
				     ObitPolnCalFit *out, ObitErr *err);

/** Public: Copy structure. */
void ObitPolnCalFitClone (ObitPolnCalFit *in, ObitPolnCalFit *out,
			  ObitErr *err);

/** Public: Fit a UV data */
void ObitPolnCalFitFit (ObitPolnCalFit* in, ObitUV *inUV,
			ObitUV *outUV, ObitErr *err);
/** Typedef for definition of class pointer structure */
typedef void(*ObitPolnCalFitFitFP) (ObitPolnCalFit* in, ObitUV *inUV,
				    ObitUV *outUV, ObitErr *err);

/*----------- ClassInfo Structure -----------------------------------*/
/**
 * ClassInfo Structure.
 * Contains class name, a pointer to any parent class
 * (NULL if none) and function pointers.
 */
typedef struct  {
#include "ObitPolnCalFitClassDef.h"
} ObitPolnCalFitClassInfo;

#endif /* OBITPOLNCALFIT_H */


#pragma once

#include <gsl/span>

#include <cstddef>

namespace imageview {

// Class representing a color in RGB24 color space.
class RGB24 {
 public:
  constexpr RGB24() noexcept = default;
  // Construct an RGB24 color from the given channel components.
  constexpr RGB24(unsigned char red, unsigned char green, unsigned char blue) noexcept
      : red(red), green(green), blue(blue) {}

  unsigned char red = 0;
  unsigned char green = 0;
  unsigned char blue = 0;
};

// Implementation of the PixelFormat concept for RGB24 pixel format.
// In this pixel format the color is represented via 3 8-bit integers,
// specifying the red, green and blue channels. When serializing to /
// deserializing from  a byte array, the order of the channels is RGB (i.e.
// not BGR).
class PixelFormatRGB24 {
 public:
  using color_type = RGB24;
  static constexpr int kBytesPerPixel = 3;

  constexpr color_type read(gsl::span<const std::byte, kBytesPerPixel> data) const;

  constexpr void write(const color_type& color, gsl::span<std::byte, kBytesPerPixel> data) const;
};

constexpr bool operator==(const RGB24& lhs, const RGB24& rhs) {
  return lhs.red == rhs.red && lhs.green == rhs.green && lhs.blue == rhs.blue;
}

constexpr bool operator!=(const RGB24& lhs, const RGB24& rhs) { return !(lhs == rhs); }

constexpr PixelFormatRGB24::color_type PixelFormatRGB24::read(gsl::span<const std::byte, kBytesPerPixel> data) const {
  return color_type(static_cast<unsigned char>(data[0]), static_cast<unsigned char>(data[1]),
                    static_cast<unsigned char>(data[2]));
}

constexpr void PixelFormatRGB24::write(const color_type& color, gsl::span<std::byte, kBytesPerPixel> data) const {
  data[0] = static_cast<std::byte>(color.red);
  data[1] = static_cast<std::byte>(color.green);
  data[2] = static_cast<std::byte>(color.blue);
}

}  // namespace imageview


/*
 *
 * Author : Pierre Schnizer <schnizer@users.sourceforge.net>
 * Date   : 5. October 2003
 * Only used to test the error handling of pygsl.
 */
#include <Python.h>
#include <gsl/gsl_errno.h>
#include <pygsl/error_helpers.h>

static char trigger_doc [] = "Calls gsl_error with the passed error number";
static PyObject *module;

static PyObject*
trigger(PyObject *self, PyObject *args)
{
	int gsl_errno = GSL_SUCCESS;

	FUNC_MESS_BEGIN();
	if (0 == PyArg_ParseTuple(args, "i", &gsl_errno)){
		PyGSL_add_traceback(module, __FILE__, __FUNCTION__, __LINE__ - 1);
		return NULL;
	}
	/*
	 *
	 * 25. October 2008
	 * set the internal gsl_error_handler to off
	 *
	 */
	pygsl_error("Just a test to see what pygsl is doing!",
		  __FILE__, __LINE__, gsl_errno);
	if (PyGSL_ERROR_FLAG(gsl_errno) != GSL_SUCCESS){
		FUNC_MESS_FAILED();
		return NULL;
	}
	FUNC_MESS_END();
	Py_INCREF(Py_None);
	return (Py_None);
}

static PyMethodDef errortestMethods[] = {
     /*densities*/
     {"trigger", trigger, METH_VARARGS, trigger_doc},
     {NULL, NULL, 0, NULL}
};

DL_EXPORT(void) initerrortest(void)
{
     PyObject  *m=NULL;

     m = Py_InitModule("errortest", errortestMethods);
     assert(m);
     module = m;

     init_pygsl();

     return;
}


/* matrix/gsl_matrix_complex_float.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_MATRIX_COMPLEX_FLOAT_H__
#define __GSL_MATRIX_COMPLEX_FLOAT_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_complex_float.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

typedef struct
{
  size_t size1;
  size_t size2;
  size_t tda;
  float * data;
  gsl_block_complex_float * block;
  int owner;
} gsl_matrix_complex_float ;

typedef struct
{
  gsl_matrix_complex_float matrix;
} _gsl_matrix_complex_float_view;

typedef _gsl_matrix_complex_float_view gsl_matrix_complex_float_view;

typedef struct
{
  gsl_matrix_complex_float matrix;
} _gsl_matrix_complex_float_const_view;

typedef const _gsl_matrix_complex_float_const_view gsl_matrix_complex_float_const_view;

/* Allocation */

GSL_EXPORT
gsl_matrix_complex_float *
gsl_matrix_complex_float_alloc (const size_t n1, const size_t n2);

GSL_EXPORT
gsl_matrix_complex_float *
gsl_matrix_complex_float_calloc (const size_t n1, const size_t n2);

GSL_EXPORT
gsl_matrix_complex_float *
gsl_matrix_complex_float_alloc_from_block (gsl_block_complex_float * b,
                                           const size_t offset,
                                           const size_t n1, const size_t n2, const size_t d2);

GSL_EXPORT
gsl_matrix_complex_float *
gsl_matrix_complex_float_alloc_from_matrix (gsl_matrix_complex_float * b,
                                            const size_t k1, const size_t k2,
                                            const size_t n1, const size_t n2);

GSL_EXPORT
gsl_vector_complex_float *
gsl_vector_complex_float_alloc_row_from_matrix (gsl_matrix_complex_float * m,
                                                const size_t i);

GSL_EXPORT
gsl_vector_complex_float *
gsl_vector_complex_float_alloc_col_from_matrix (gsl_matrix_complex_float * m,
                                                const size_t j);

GSL_EXPORT void gsl_matrix_complex_float_free (gsl_matrix_complex_float * m);

/* Views */

GSL_EXPORT
_gsl_matrix_complex_float_view
gsl_matrix_complex_float_submatrix (gsl_matrix_complex_float * m,
                            const size_t i, const size_t j,
                            const size_t n1, const size_t n2);

GSL_EXPORT
_gsl_vector_complex_float_view
gsl_matrix_complex_float_row (gsl_matrix_complex_float * m, const size_t i);

GSL_EXPORT
_gsl_vector_complex_float_view
gsl_matrix_complex_float_column (gsl_matrix_complex_float * m, const size_t j);

GSL_EXPORT
_gsl_vector_complex_float_view
gsl_matrix_complex_float_diagonal (gsl_matrix_complex_float * m);

GSL_EXPORT
_gsl_vector_complex_float_view
gsl_matrix_complex_float_subdiagonal (gsl_matrix_complex_float * m, const size_t k);

GSL_EXPORT
_gsl_vector_complex_float_view
gsl_matrix_complex_float_superdiagonal (gsl_matrix_complex_float * m, const size_t k);

GSL_EXPORT
_gsl_matrix_complex_float_view
gsl_matrix_complex_float_view_array (float * base,
                             const size_t n1,
                             const size_t n2);

GSL_EXPORT
_gsl_matrix_complex_float_view
gsl_matrix_complex_float_view_array_with_tda (float * base,
                                      const size_t n1,
                                      const size_t n2,
                                      const size_t tda);

GSL_EXPORT
_gsl_matrix_complex_float_view
gsl_matrix_complex_float_view_vector (gsl_vector_complex_float * v,
                              const size_t n1,
                              const size_t n2);

GSL_EXPORT
_gsl_matrix_complex_float_view
gsl_matrix_complex_float_view_vector_with_tda (gsl_vector_complex_float * v,
                                       const size_t n1,
                                       const size_t n2,
                                       const size_t tda);

GSL_EXPORT
_gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_submatrix (const gsl_matrix_complex_float * m,
                                  const size_t i, const size_t j,
                                  const size_t n1, const size_t n2);

GSL_EXPORT
_gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_row (const gsl_matrix_complex_float * m,
                            const size_t i);

GSL_EXPORT
_gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_column (const gsl_matrix_complex_float * m,
                               const size_t j);

GSL_EXPORT
_gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_diagonal (const gsl_matrix_complex_float * m);

GSL_EXPORT
_gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_subdiagonal (const gsl_matrix_complex_float * m,
                                    const size_t k);

GSL_EXPORT
_gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_superdiagonal (const gsl_matrix_complex_float * m,
                                      const size_t k);

GSL_EXPORT
_gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_view_array (const float * base,
                                   const size_t n1,
                                   const size_t n2);

GSL_EXPORT
_gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_view_array_with_tda (const float * base,
                                            const size_t n1,
                                            const size_t n2,
                                            const size_t tda);

GSL_EXPORT
_gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_view_vector (const gsl_vector_complex_float * v,
                                    const size_t n1,
                                    const size_t n2);

GSL_EXPORT
_gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_view_vector_with_tda (const gsl_vector_complex_float * v,
                                             const size_t n1,
                                             const size_t n2,
                                             const size_t tda);

/* Operations */

GSL_EXPORT gsl_complex_float gsl_matrix_complex_float_get(const gsl_matrix_complex_float * m, const size_t i, const size_t j);
GSL_EXPORT void gsl_matrix_complex_float_set(gsl_matrix_complex_float * m, const size_t i, const size_t j, const gsl_complex_float x);

GSL_EXPORT gsl_complex_float * gsl_matrix_complex_float_ptr(gsl_matrix_complex_float * m, const size_t i, const size_t j);
GSL_EXPORT const gsl_complex_float * gsl_matrix_complex_float_const_ptr(const gsl_matrix_complex_float * m, const size_t i, const size_t j);

GSL_EXPORT void gsl_matrix_complex_float_set_zero (gsl_matrix_complex_float * m);
GSL_EXPORT void gsl_matrix_complex_float_set_identity (gsl_matrix_complex_float * m);
GSL_EXPORT void gsl_matrix_complex_float_set_all (gsl_matrix_complex_float * m, gsl_complex_float x);

GSL_EXPORT int gsl_matrix_complex_float_fread (FILE * stream, gsl_matrix_complex_float * m) ;
GSL_EXPORT int gsl_matrix_complex_float_fwrite (FILE * stream, const gsl_matrix_complex_float * m) ;
GSL_EXPORT int gsl_matrix_complex_float_fscanf (FILE * stream, gsl_matrix_complex_float * m);
GSL_EXPORT int gsl_matrix_complex_float_fprintf (FILE * stream, const gsl_matrix_complex_float * m, const char * format);

GSL_EXPORT int gsl_matrix_complex_float_memcpy(gsl_matrix_complex_float * dest, const gsl_matrix_complex_float * src);
GSL_EXPORT int gsl_matrix_complex_float_swap(gsl_matrix_complex_float * m1, gsl_matrix_complex_float * m2);

GSL_EXPORT int gsl_matrix_complex_float_swap_rows(gsl_matrix_complex_float * m, const size_t i, const size_t j);
GSL_EXPORT int gsl_matrix_complex_float_swap_columns(gsl_matrix_complex_float * m, const size_t i, const size_t j);
GSL_EXPORT int gsl_matrix_complex_float_swap_rowcol(gsl_matrix_complex_float * m, const size_t i, const size_t j);

GSL_EXPORT int gsl_matrix_complex_float_transpose (gsl_matrix_complex_float * m);
GSL_EXPORT int gsl_matrix_complex_float_transpose_memcpy (gsl_matrix_complex_float * dest, const gsl_matrix_complex_float * src);

GSL_EXPORT int gsl_matrix_complex_float_isnull (const gsl_matrix_complex_float * m);

GSL_EXPORT int gsl_matrix_complex_float_add (gsl_matrix_complex_float * a, const gsl_matrix_complex_float * b);
GSL_EXPORT int gsl_matrix_complex_float_sub (gsl_matrix_complex_float * a, const gsl_matrix_complex_float * b);
GSL_EXPORT int gsl_matrix_complex_float_mul_elements (gsl_matrix_complex_float * a, const gsl_matrix_complex_float * b);
GSL_EXPORT int gsl_matrix_complex_float_div_elements (gsl_matrix_complex_float * a, const gsl_matrix_complex_float * b);
GSL_EXPORT int gsl_matrix_complex_float_scale (gsl_matrix_complex_float * a, const gsl_complex_float x);
GSL_EXPORT int gsl_matrix_complex_float_add_constant (gsl_matrix_complex_float * a, const gsl_complex_float x);
GSL_EXPORT int gsl_matrix_complex_float_add_diagonal (gsl_matrix_complex_float * a, const gsl_complex_float x);

/***********************************************************************/
/* The functions below are obsolete                                    */
/***********************************************************************/
GSL_EXPORT int gsl_matrix_complex_float_get_row(gsl_vector_complex_float * v, const gsl_matrix_complex_float * m, const size_t i);
GSL_EXPORT int gsl_matrix_complex_float_get_col(gsl_vector_complex_float * v, const gsl_matrix_complex_float * m, const size_t j);
GSL_EXPORT int gsl_matrix_complex_float_set_row(gsl_matrix_complex_float * m, const size_t i, const gsl_vector_complex_float * v);
GSL_EXPORT int gsl_matrix_complex_float_set_col(gsl_matrix_complex_float * m, const size_t j, const gsl_vector_complex_float * v);

#ifdef HAVE_INLINE

extern inline
gsl_complex_float
gsl_matrix_complex_float_get(const gsl_matrix_complex_float * m,
                     const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  gsl_complex_float zero = {{0,0}};

  if (i >= m->size1)
    {
      GSL_ERROR_VAL("first index out of range", GSL_EINVAL, zero) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_VAL("second index out of range", GSL_EINVAL, zero) ;
    }
#endif
  return *(gsl_complex_float *)(m->data + 2*(i * m->tda + j)) ;
}

extern inline
void
gsl_matrix_complex_float_set(gsl_matrix_complex_float * m,
                     const size_t i, const size_t j, const gsl_complex_float x)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_VOID("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_VOID("second index out of range", GSL_EINVAL) ;
    }
#endif
  *(gsl_complex_float *)(m->data + 2*(i * m->tda + j)) = x ;
}

extern inline
gsl_complex_float *
gsl_matrix_complex_float_ptr(gsl_matrix_complex_float * m,
                             const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
    }
#endif
  return (gsl_complex_float *)(m->data + 2*(i * m->tda + j)) ;
}

extern inline
const gsl_complex_float *
gsl_matrix_complex_float_const_ptr(const gsl_matrix_complex_float * m,
                                   const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
    }
#endif
  return (const gsl_complex_float *)(m->data + 2*(i * m->tda + j)) ;
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_MATRIX_COMPLEX_FLOAT_H__ */


#pragma once

#include <gsl/gsl>

namespace aas {

	struct SkelBone;

	/**
	* Represents a sphere for cloth collision purposes.
	*/
	struct CollisionSphere {
		int boneId;
		float radius;
		Matrix3x4 worldMatrix;
		Matrix3x4 worldMatrixInverse;
		std::unique_ptr<CollisionSphere> next;
	};

	/**
	* Represents a cylinder for cloth collision purposes.
	*/
	struct CollisionCylinder {
		int boneId;
		float radius;
		float height;
		Matrix3x4 worldMatrix;
		Matrix3x4 worldMatrixInverse;
		std::unique_ptr<CollisionCylinder> next;
	};

	struct CollisionGeometry {
		std::unique_ptr<CollisionSphere> firstSphere;
		std::unique_ptr<CollisionCylinder> firstCylinder;
	};

	/**
	 * Extract the cloth collision information from a list of bones.
	 * The information is parsed from the bone names.
	 */
	CollisionGeometry FindCollisionGeometry(gsl::span<SkelBone> bones);

}


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

#ifndef __NUPM_DAX_DATA_H__
#define __NUPM_DAX_DATA_H__

#include <libpmem.h>
#include <city.h> /* CityHash */
#include <common/pointer_cast.h>
#include <common/string_view.h>
#include <common/types.h>
#include <common/utils.h>
#include <boost/icl/split_interval_map.hpp>
#include <gsl/span>
#include <algorithm>
#include <list>
#include <stdexcept>

namespace
{
	std::uint64_t make_uuid(const common::string_view name_)
	{
		if ( 255 < name_.size() )
		{
			throw std::invalid_argument("invalid file name (too long)");
		}
		auto region_id = ::CityHash64(name_.begin(), name_.size());
		if (region_id == 0) throw std::invalid_argument("invalid region_id (and extreme bad luck!)");
		return region_id;
	}
}

namespace nupm
{
static std::uint32_t constexpr DM_REGION_MAGIC = 0xC0070000;
static unsigned constexpr DM_REGION_NAME_MAX_LEN = 1024;
static std::uint32_t constexpr DM_REGION_VERSION = 3;
static unsigned constexpr dm_region_log_grain_size = DM_REGION_LOG_GRAIN_SIZE; // log2 granularity (CMake default is 25, i.e. 32 MiB)

class DM_undo_log {
  static constexpr unsigned MAX_LOG_COUNT = 4;
  static constexpr unsigned MAX_LOG_SIZE  = 272;
  struct log_entry_t {
    byte   log[MAX_LOG_SIZE];
    void * ptr;
    size_t length; /* zero indicates log freed */
  };

  void log(void *ptr, size_t length)
  {
    assert(length > 0);
    assert(ptr);

    if (length > MAX_LOG_SIZE)
      throw std::invalid_argument("log length exceeds max. space");

    for (unsigned i = 0; i < MAX_LOG_COUNT; i++) {
      if (_log[i].length == 0) {
        _log[i].length = length;
        _log[i].ptr    = ptr;
        pmem_memcpy_nodrain(_log[i].log, ptr, length);
        // TODO
        //memcpy(_log[i].log, ptr, length);
        //mem_flush(&_log[i], sizeof(log_entry_t));
        return;
      }
    }
    throw API_exception("undo log full");
  }

 public:
  template <typename T>
    void log(T *ptr)
    {
      static_assert(sizeof *ptr <= MAX_LOG_SIZE, "object too big to log");
      log(ptr, sizeof *ptr);
    }

  void clear_log()
  {
    for (unsigned i = 0; i < MAX_LOG_COUNT; i++) _log[i].length = 0;
  }

  void check_and_undo()
  {
    for (unsigned i = 0; i < MAX_LOG_COUNT; i++) {
      if (_log[i].length > 0) {
        PLOG("undo log being applied (ptr=%p, len=%lu).", _log[i].ptr,
             _log[i].length);
        // TODO
        pmem_memcpy_persist(_log[i].ptr, _log[i].log, _log[i].length);
        //memcpy(_log[i].ptr, _log[i].log, _log[i].length);
        //        mem_flush_nodrain(_log[i].ptr, _log[i].length);
        _log[i].length = 0;
      }
    }
  }

 private:
  log_entry_t _log[MAX_LOG_COUNT];
} __attribute__((packed));

class DM_region {
public:
  /* "grain" is the unit of suballocation within a region. It is a fixed power
   * of 2 common to all regions in the devdax namespace.
   */
  using grain_offset_t = uint32_t;
  grain_offset_t offset_grain;
  grain_offset_t length_grain;
  uint64_t region_id;
  /* File name. Saved and returned.
   * Internal logic uses region_id (file name hash) to reduce code changes.
   */
  char file_name[256];

 public:
  /* re-zeroing constructor */
  DM_region() : offset_grain(0), length_grain(0), region_id(0) { assert(check_aligned(this, 8)); }

  void initialize(size_t space_size, std::size_t grain_size)
  {
    offset_grain = 0;
    length_grain = boost::numeric_cast<uint32_t>(space_size / grain_size);
    region_id = 0; /* zero indicates free */
  }

  friend class DM_region_header;
} __attribute__((packed));

/* Note: "region" has at least two meanings:
 *  1. The space starting with, and described by, DM_region_header. ndctl calls this a namespace.
 e  2. The space described by DM_region
 */
class DM_region_header {
 private:
  static constexpr uint16_t DEFAULT_MAX_REGIONS = 1024;
  using string_view = common::string_view;

  uint32_t    _magic;         // 4
  uint32_t    _version;       // 8
  uint64_t    _device_size;   // 16
  uint32_t    _region_count;  // 20
  uint16_t    _log_grain_size; // 22
  uint16_t    _resvd;         // 24
  uint8_t     _padding[40];   // 64
  DM_undo_log _undo_log;

  /*
   * Following this data, there is
   * 1. region_table_base, immediately following (this + 1)
   * 2. arena_base, at ptr_cast<byte>(this) + grain_size. Code assumes
   *    (but does not verify) that DM_region_header plus all DM_region
   *    descriptors fit within a single grain.
   *
   * grain 0:
   *    "region_header"
   *    "region_table"
   * grains 1 and following:
   *    "arena"
   */

 public:
  auto grain_size() const { return std::size_t(1) << _log_grain_size; }

  /* Rebuilding constructor */
  DM_region_header(size_t device_size)
    : _magic(DM_REGION_MAGIC)
    , _version(DM_REGION_VERSION)
    , _device_size(device_size)
    , _region_count( (::pmem_flush(this, sizeof(DM_region_header)), DEFAULT_MAX_REGIONS) )
    , _log_grain_size(dm_region_log_grain_size)
    , _resvd()
    , _undo_log()
  {
    (void)_resvd; // unused
    (void)_padding; // unused
    DM_region *region_p = region_table_base();
    /* initialize first region with all capacity with size of space, and size of grain */
    region_p->initialize(device_size - grain_size(), grain_size());
    _undo_log.clear_log();
    region_p++;

    for (uint16_t r = 1; r < _region_count; r++) {
      new (region_p) DM_region();
      _undo_log.clear_log();
      region_p++;
    }
    major_flush();
  }

  void check_undo_logs()
  {
    _undo_log.check_and_undo();
  }

  void debug_dump()
  {
    PINF("DM_region_header:");
    PINF(
        " magic [0x%8x]\n version [%u]\n device_size [%lu]\n region_count [%u]",
        _magic, _version, _device_size, _region_count);
    PINF(" base [%p]", common::p_fmt(this));

    const auto regions = region_table_span();
    for (auto const & reg : regions ) {
      if (reg.region_id > 0) {
        PINF(" - USED: %lu (%lx-%lx)", reg.region_id,
             grain_to_bytes(reg.offset_grain),
             grain_to_bytes(reg.offset_grain + reg.length_grain) - 1);
        assert(reg.length_grain > 0);
      }
      else if (reg.length_grain > 0) {
        PINF(" - FREE: %lu (%lx-%lx)", reg.region_id,
             grain_to_bytes(reg.offset_grain),
             grain_to_bytes(reg.offset_grain + reg.length_grain) - 1);
      }
    }
  }

  void *get_region(string_view name_, size_t *out_size)
  {
    auto region_id = make_uuid(name_);

    const auto regions = region_table_span();
    for (auto const & reg : regions ) {
      if (reg.region_id == region_id) {
#if 0
        PLOG("%s: found matching region (%lx)", __func__, region_id);
#endif
        if (out_size) *out_size = grain_to_bytes(reg.length_grain);
        return arena_base() + grain_to_bytes(reg.offset_grain);
      }
    }
    return nullptr; /* not found */
  }

  void erase_region(string_view name_)
  {
    auto region_id = make_uuid(name_);

    const auto regions = region_table_span();
    for (auto & reg : regions ) {
      if (reg.region_id == region_id) {
        reg.region_id = 0; /* power-fail atomic */
        pmem_flush(&reg.region_id, sizeof(reg.region_id));
        return;
      }
    }
    throw std::runtime_error("region not found");
  }

  void *allocate_region(string_view name_, DM_region::grain_offset_t size_in_grain)
  {
    auto region_id = make_uuid(name_);

    const auto regions = region_table_span();
    for (auto & reg : regions ) {
      if (reg.region_id == region_id)
        throw std::bad_alloc();
    }

    bool found = false;
    for (DM_region & reg : regions)
    {
      /* If we have found a sufficiently large free region */
      if (reg.region_id == 0 && reg.length_grain >= size_in_grain) {
        if (reg.length_grain == size_in_grain) {
          /* exact match */
          void *rp = arena_base() + grain_to_bytes(reg.offset_grain);
          /* write file name to region */
          pmem_memcpy_persist(reg.file_name, name_.begin(), name_.size());
          pmem_memcpy_persist(&reg.file_name[name_.size()], "\0", 1);
          // claim region
          tx_atomic_write(&reg, region_id);
          return rp;
        }
        else {
          /* cut out */
          const uint32_t new_offset = reg.offset_grain;

          const auto changed_length = reg.length_grain - size_in_grain;
          const auto changed_offset = reg.offset_grain + size_in_grain;

	  /* reg_n is the new region for unallocated space */
          auto reg_n = std::find_if(regions.begin(), regions.end(), [] (const DM_region &r) { return r.region_id == 0 && r.length_grain == 0; });
          if ( reg_n != regions.end() )
          {
            void *rp = arena_base() + grain_to_bytes(new_offset);
            /* write file name to region */
            pmem_memcpy_persist(reg.file_name, name_.begin(), name_.size());
            pmem_memcpy_persist(&reg.file_name[name_.size()], "\0", 1);
            // claim region
            tx_atomic_write(&*reg_n, boost::numeric_cast<uint16_t>(changed_offset), boost::numeric_cast<uint16_t>(changed_length), &reg,
                            new_offset, size_in_grain, region_id);
            return rp;
          }
        }
      }
    }
    if (!found)
      throw General_exception("no more regions (size in grain=%u)", size_in_grain);

    throw General_exception("no spare slots");
  }

  size_t get_max_available() const
  {
    auto regions = region_table_span();
    auto max_grain_element =
      std::max_element(
        regions.begin()
	, regions.end()
        , [] (const DM_region &a, const DM_region &b) -> bool { return a.length_grain < b.length_grain; }
      );
    return grain_to_bytes( max_grain_element == regions.end() ? 0 : max_grain_element->length_grain);
  }

  inline size_t grain_to_bytes(unsigned grain) const { return size_t(grain) << _log_grain_size; }

  inline void major_flush()
  {
    pmem_flush(this, sizeof(DM_region_header) + (sizeof(DM_region) * _region_count));
  }

  bool check_magic() const
  {
    return (_magic == DM_REGION_MAGIC) && (_version == DM_REGION_VERSION);
  }

  std::list<std::string> names_list()
  {
    std::list<std::string> nl;
    auto regions = region_table_span();
    for ( const auto &r : regions )
    {
      if ( 0 != r.region_id )
      {
        nl.push_back(std::string(r.file_name));
      }
    }
    return nl;
  }

 private:
  void tx_atomic_write(DM_region *dst, uint64_t region_id)
  {
#pragma GCC diagnostic push
#if 9 <= __GNUC__
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
#endif
    _undo_log.log(&dst->region_id);
#pragma GCC diagnostic pop
    dst->region_id = region_id;
    pmem_flush(&dst->region_id, sizeof(region_id));
    _undo_log.clear_log();
  }

  void tx_atomic_write(DM_region *dst0, // offset0, size0, offset1, size1 all expressed in grains
                       uint32_t   offset0,
                       uint32_t   size0,
                       DM_region *dst1,
                       uint32_t   offset1,
                       uint32_t   size1,
                       uint64_t   region_id1)
  {
    _undo_log.log(dst0);
    _undo_log.log(dst1);

    dst0->offset_grain = offset0;
    dst0->length_grain = size0;
    pmem_flush(dst0, sizeof(DM_region));

    dst1->region_id = region_id1;
    dst1->offset_grain = offset1;
    dst1->length_grain = size1;
    pmem_flush(dst1, sizeof(DM_region));

    _undo_log.clear_log();
  }

  inline unsigned char *arena_base()
  {
    return common::pointer_cast<unsigned char>(this) + grain_size();
  }

  /* region descriptors immediately follow the DM_region_header. */
  inline DM_region *region_table_base() { return common::pointer_cast<DM_region>(this + 1); }
  inline const DM_region *region_table_base() const { return common::pointer_cast<const DM_region>(this + 1); }
  gsl::span<DM_region> region_table_span() { return gsl::span<DM_region>(region_table_base(), _region_count); }
  gsl::span<const DM_region> region_table_span() const { return gsl::span<const DM_region>(region_table_base(), _region_count); }

  inline DM_region *region(size_t idx)
  {
    if (idx >= _region_count) return nullptr;
    DM_region *p = static_cast<DM_region *>(region_table_base());
    return &p[idx];
  }

  void reset_header(size_t device_size)
  {
    _magic       = DM_REGION_MAGIC;
    _version     = DM_REGION_VERSION;
    _device_size = device_size;
    pmem_flush(this, sizeof(DM_region_header));
  }
} __attribute__((packed));

}  // namespace nupm

#endif  //__NUPM_DAX_DATA_H__


// Copyright 2018-2019 TAP, Inc. All Rights Reserved.

#pragma once

#include "targetver.h"

#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>

#include <cstdint>
#include <cassert>
#include <cstdlib>
#include <cstdio>
#include <corecrt_math_defines.h>

#include <array>
#include <vector>
#include <iostream>
#include <sstream>
#include <fstream>
#include <map>
#include <chrono>
#include <filesystem>
#include <thread>
#include <string>
using namespace std::literals::string_literals;

#pragma warning(disable : 4127)
#pragma warning(disable : 4201)
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/string_cast.hpp>
#pragma warning(default : 4201)
#pragma warning(default : 4127)

#pragma warning(disable : 4100)
#pragma warning(disable : 4458)
#include <gli/gli.hpp>
#pragma warning(default : 4458)
#pragma warning(default : 4100)

#define VK_USE_PLATFORM_WIN32_KHR
#include <vulkan/vulkan.h>

#include <vulkan/vk_mem_alloc.h>

#include <entt/entt.hpp>

#include <gsl/gsl>


#pragma once

#include <gsl\gsl>
#include <winrt\Windows.Foundation.h>
#include <d3d11.h>
#include "DrawableGameComponent.h"
#include "MatrixHelper.h"
#include "PointLight.h"
#include "DepthMap.h"
#include "Rectangle.h"
#include "ProjectiveTextureMappingMaterial.h"
#include "RenderStateHelper.h"

namespace DirectX
{
	class SpriteBatch;
}

namespace Library
{
	class ProxyModel;
	class RenderableFrustum;
	class DepthMapMaterial;
}

namespace Rendering
{
	class ProjectiveTextureMappingDemo final : public Library::DrawableGameComponent
	{
	public:
		ProjectiveTextureMappingDemo(Library::Game& game, const std::shared_ptr<Library::Camera>& camera);
		ProjectiveTextureMappingDemo(const ProjectiveTextureMappingDemo&) = delete;
		ProjectiveTextureMappingDemo(ProjectiveTextureMappingDemo&&) = default;
		ProjectiveTextureMappingDemo& operator=(const ProjectiveTextureMappingDemo&) = default;
		ProjectiveTextureMappingDemo& operator=(ProjectiveTextureMappingDemo&&) = default;
		~ProjectiveTextureMappingDemo();

		ProjectiveTextureMappingDrawModes DrawMode() const;
		const std::string& DrawModeString() const;
		void SetDrawMode(ProjectiveTextureMappingDrawModes drawMode);

		float AmbientLightIntensity() const;
		void SetAmbientLightIntensity(float intensity);

		float PointLightIntensity() const;
		void SetPointLightIntensity(float intensity);

		float PointLightRadius() const;
		void SetPointLightRadius(float radius);

		const DirectX::XMFLOAT3& ProjectorPosition() const;
		const DirectX::XMVECTOR ProjectorPositionVector() const;
		void SetProjectorPosition(const DirectX::XMFLOAT3& position);
		void SetProjectorPosition(DirectX::FXMVECTOR position);

		const DirectX::XMFLOAT3& ProjectorDirection() const;
		void RotateProjector(const DirectX::XMFLOAT2& amount);

		virtual void Initialize() override;
		virtual void Update(const Library::GameTime& gameTime) override;
		virtual void Draw(const Library::GameTime& gameTime) override;

	private:
		inline static const std::uint32_t DepthMapWidth{ 1024 };
		inline static const std::uint32_t DepthMapHeight{ 1024 };
		static inline const RECT DepthMapDestinationRectangle{ 0, 512, 256, 768 };

		void DrawWithDepthMap();
		void DrawWithoutDepthMap();
		void UpdateTransforms(ProjectiveTextureMappingMaterial::VertexCBufferPerObject& transforms, DirectX::FXMMATRIX worldViewProjectionMatrix, DirectX::CXMMATRIX worldMatrix, DirectX::CXMMATRIX projectiveTextureMatrix);
		void InitializeProjectedTextureScalingMatrix(uint32_t textureWidth, uint32_t textureHeight);

		ProjectiveTextureMappingMaterial::VertexCBufferPerObject mPlaneTransforms;
		ProjectiveTextureMappingMaterial::VertexCBufferPerObject mTeapotTransforms;
		DirectX::XMFLOAT4X4 mPlaneWorldMatrix{ Library::MatrixHelper::Identity };
		DirectX::XMFLOAT4X4 mTeapotWorldMatrix{ Library::MatrixHelper::Identity };
		DirectX::XMFLOAT4X4 mProjectedTextureScalingMatrix{ Library::MatrixHelper::Zero };
		Library::PointLight mPointLight;
		Library::DepthMap mDepthMap;
		Library::RenderStateHelper mRenderStateHelper;
		std::shared_ptr<ProjectiveTextureMappingMaterial> mMaterial;
		std::shared_ptr<Library::DepthMapMaterial> mDepthMapMaterial;
		winrt::com_ptr<ID3D11Buffer> mPlaneVertexBuffer;
		winrt::com_ptr<ID3D11Buffer> mTeapotVertexBuffer;
		winrt::com_ptr<ID3D11Buffer> mTeapotPositionOnlyVertexBuffer;
		winrt::com_ptr<ID3D11Buffer> mTeapotIndexBuffer;
		std::uint32_t mPlaneVertexCount{ 0 };
		std::uint32_t mTeapotIndexCount{ 0 };
		std::unique_ptr<Library::ProxyModel> mProxyModel;
		std::unique_ptr<Library::Camera> mProjector;
		std::unique_ptr<Library::RenderableFrustum> mRenderableProjectorFrustum;
		std::unique_ptr<DirectX::SpriteBatch> mSpriteBatch;
		bool mUpdateMaterial{ true };
	};
}


#pragma once
/*
 * (C) Copyright 2020-2021 UCAR
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 */
/*! \addtogroup ioda_cxx_variable
 *
 * @{
 * \file FillPolicy.h
 * \brief Default fill values for ioda files.
 */

#include <gsl/gsl-lite.hpp>
#include <memory>
#include <string>

#include "ioda/Variables/Fill.h"
#include "ioda/Exception.h"
#include "ioda/defs.h"

namespace ioda {

/// \brief This option describes the default fill values that will be used if the user does not
/// manually specify a fill value.
/// \ingroup ioda_cxx_variable
enum class FillValuePolicy {
  HDF5,    ///< Set all fill values to zero or null strings.
  NETCDF4  ///< Use NetCDF4 default fill values. This is the default option for ioda files.
};

/// \brief Holds the different default fill values used in ioda files produced by different
///   backends.
/// \ingroup ioda_cxx_variable
/// \details This matters for netCDF4 vs HDF5-produced files. They have different default
///   fill values.
namespace FillValuePolicies {
template <class T>
T HDF5_default() {
  return 0;
}
template <>
inline std::string HDF5_default<std::string>() {
  return std::string();
}

/// \ingroup ioda_cxx_variable
/// \see netcdf.h, starting around line 62, for these values
///   netcdf uses "ints" and "shorts", but these are all defined as fixed-width types.
template <class T>
T netCDF4_default() {
  return 0;
}
template <>
inline std::string netCDF4_default<std::string>() {
  return std::string();
}
template <>
inline signed char netCDF4_default<signed char>() {
  return static_cast<signed char>(-127);
}
template <>
inline char netCDF4_default<char>() {
  return static_cast<char>(0);
}
template <>
inline int16_t netCDF4_default<int16_t>() {
  return static_cast<int16_t>(-32767);
}
template <>
inline int32_t netCDF4_default<int32_t>() {
  return -2147483647;
}
template <>
inline float netCDF4_default<float>() {
  return 9.9692099683868690e+36f;
}
template <>
inline double netCDF4_default<double>() {
  return 9.9692099683868690e+36;
}
template <>
inline unsigned char netCDF4_default<unsigned char>() {
  return static_cast<unsigned char>(255);
}
template <>
inline uint16_t netCDF4_default<uint16_t>() {
  return static_cast<unsigned short>(65535);
}
template <>
inline uint32_t netCDF4_default<uint32_t>() {
  return 4294967295U;
}
template <>
inline int64_t netCDF4_default<int64_t>() {
  return -9223372036854775806LL;
}
template <>
inline uint64_t netCDF4_default<uint64_t>() {
  return 18446744073709551614ULL;
}

/// \brief Applies the fill value policy. This sets default fill values when fill values are not
///   already provided.
/// \ingroup ioda_cxx_variable
template <class T>
void applyFillValuePolicy(FillValuePolicy pol, detail::FillValueData_t& fvd) {
  if (fvd.set_) return;  // If already set, then do nothing.
  if (pol == FillValuePolicy::HDF5)
    detail::assignFillValue(fvd, HDF5_default<T>());
  else if (pol == FillValuePolicy::NETCDF4)
    detail::assignFillValue(fvd, netCDF4_default<T>());
  else
    throw Exception("Unsupported fill value policy.", ioda_Here());
}

}  // namespace FillValuePolicies
}  // namespace ioda

/// @}