/* Copyright (C) 2015 Atsushi Togo */
/* All rights reserved. */

/* This file is part of phonopy. */

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

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

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

/* * Neither the name of the phonopy project nor the names of its */
/*   contributors may be used to endorse or promote products derived */
/*   from this software without specific prior written permission. */

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

#include <Python.h>
#include <stdio.h>
#include <stdlib.h>
#include <numpy/arrayobject.h>
#include <lapacke.h>
#include "phonoc_array.h"
#include "phonon4_h/fc4.h"
#include "phonon4_h/real_to_reciprocal.h"
#include "phonon4_h/frequency_shift.h"

static PyObject * py_get_fc4_normal_for_frequency_shift(PyObject *self, PyObject *args);
static PyObject * py_get_fc4_frequency_shifts(PyObject *self, PyObject *args);
static PyObject * py_real_to_reciprocal4(PyObject *self, PyObject *args);
static PyObject * py_reciprocal_to_normal4(PyObject *self, PyObject *args);
static PyObject * py_set_phonons_grid_points(PyObject *self, PyObject *args);
static PyObject * py_distribute_fc4(PyObject *self, PyObject *args);
static PyObject * py_rotate_delta_fc3s_elem(PyObject *self, PyObject *args);
static PyObject * py_set_translational_invariance_fc4(PyObject *self,
						      PyObject *args);
static PyObject * py_set_permutation_symmetry_fc4(PyObject *self,
						  PyObject *args);
static PyObject * py_get_drift_fc4(PyObject *self, PyObject *args);

static PyMethodDef functions[] = {
  {"fc4_normal_for_frequency_shift", py_get_fc4_normal_for_frequency_shift, METH_VARARGS, "Calculate fc4 normal for frequency shift"},
  {"fc4_frequency_shifts", py_get_fc4_frequency_shifts, METH_VARARGS, "Calculate fc4 frequency shift"},
  {"real_to_reciprocal4", py_real_to_reciprocal4, METH_VARARGS, "Transform fc4 of real space to reciprocal space"},
  {"reciprocal_to_normal4", py_reciprocal_to_normal4, METH_VARARGS, "Transform fc4 of reciprocal space to normal coordinate in special case for frequency shift"},
  {"phonons_grid_points", py_set_phonons_grid_points, METH_VARARGS, "Set phonons on grid points"},
  {"distribute_fc4", py_distribute_fc4, METH_VARARGS, "Distribute least fc4 to full fc4"},
  {"rotate_delta_fc3s_elem", py_rotate_delta_fc3s_elem, METH_VARARGS, "Rotate delta fc3s for a set of atomic indices"},
  {"translational_invariance_fc4", py_set_translational_invariance_fc4, METH_VARARGS, "Set translational invariance for fc4"},
  {"permutation_symmetry_fc4", py_set_permutation_symmetry_fc4, METH_VARARGS, "Set permutation symmetry for fc4"},
  {"drift_fc4", py_get_drift_fc4, METH_VARARGS, "Get drifts of fc4"},
  {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC init_phono4py(void)
{
  Py_InitModule3("_phono4py", functions, "C-extension for phono4py\n\n...\n");
  return;
}

static PyObject * py_get_fc4_normal_for_frequency_shift(PyObject *self,
							PyObject *args)
{
  PyArrayObject* fc4_normal_py;
  PyArrayObject* frequencies_py;
  PyArrayObject* eigenvectors_py;
  PyArrayObject* grid_points1_py;
  PyArrayObject* grid_address_py;
  PyArrayObject* mesh_py;
  PyArrayObject* fc4_py;
  PyArrayObject* shortest_vectors_py;
  PyArrayObject* multiplicity_py;
  PyArrayObject* masses_py;
  PyArrayObject* p2s_map_py;
  PyArrayObject* s2p_map_py;
  PyArrayObject* band_indicies_py;
  double cutoff_frequency;
  int grid_point0;

  if (!PyArg_ParseTuple(args, "OOOiOOOOOOOOOOd",
			&fc4_normal_py,
			&frequencies_py,
			&eigenvectors_py,
			&grid_point0,
			&grid_points1_py,
			&grid_address_py,
			&mesh_py,
			&fc4_py,
			&shortest_vectors_py,
			&multiplicity_py,
			&masses_py,
			&p2s_map_py,
			&s2p_map_py,
			&band_indicies_py,
			&cutoff_frequency)) {
    return NULL;
  }

  double* fc4_normal = (double*)fc4_normal_py->data;
  double* freqs = (double*)frequencies_py->data;
  /* npy_cdouble and lapack_complex_double may not be compatible. */
  /* So eigenvectors should not be used in Python side */
  lapack_complex_double* eigvecs =
    (lapack_complex_double*)eigenvectors_py->data;
  Iarray* grid_points1 = convert_to_iarray(grid_points1_py);
  const int* grid_address = (int*)grid_address_py->data;
  const int* mesh = (int*)mesh_py->data;
  double* fc4 = (double*)fc4_py->data;
  Darray* svecs = convert_to_darray(shortest_vectors_py);
  Iarray* multi = convert_to_iarray(multiplicity_py);
  const double* masses = (double*)masses_py->data;
  const int* p2s = (int*)p2s_map_py->data;
  const int* s2p = (int*)s2p_map_py->data;
  Iarray* band_indicies = convert_to_iarray(band_indicies_py);

  get_fc4_normal_for_frequency_shift(fc4_normal,
				     freqs,
				     eigvecs,
				     grid_point0,
				     grid_points1,
				     grid_address,
				     mesh,
				     fc4,
				     svecs,
				     multi,
				     masses,
				     p2s,
				     s2p,
				     band_indicies,
				     cutoff_frequency);

  free(grid_points1);
  free(svecs);
  free(multi);
  free(band_indicies);

  Py_RETURN_NONE;
}

static PyObject * py_get_fc4_frequency_shifts(PyObject *self, PyObject *args)
{
  PyArrayObject* frequency_shifts_py;
  PyArrayObject* fc4_normal_py;
  PyArrayObject* frequencies_py;
  PyArrayObject* grid_points1_py;
  PyArrayObject* temperatures_py;
  PyArrayObject* band_indicies_py;
  double unit_conversion_factor;

  if (!PyArg_ParseTuple(args, "OOOOOOd",
			&frequency_shifts_py,
			&fc4_normal_py,
			&frequencies_py,
			&grid_points1_py,
			&temperatures_py,
			&band_indicies_py,
			&unit_conversion_factor)) {
    return NULL;
  }

  double* freq_shifts = (double*)frequency_shifts_py->data;
  double* fc4_normal = (double*)fc4_normal_py->data;
  double* freqs = (double*)frequencies_py->data;
  Iarray* grid_points1 = convert_to_iarray(grid_points1_py);
  Darray* temperatures = convert_to_darray(temperatures_py);
  int* band_indicies = (int*)band_indicies_py->data;
  const int num_band0 = (int)band_indicies_py->dimensions[0];
  const int num_band = (int)frequencies_py->dimensions[1];

  get_fc4_frequency_shifts(freq_shifts,
			   fc4_normal,
			   freqs,
			   grid_points1,
			   temperatures,
			   band_indicies,
			   num_band0,
			   num_band,
			   unit_conversion_factor);

  free(grid_points1);
  free(temperatures);

  Py_RETURN_NONE;
}

static PyObject * py_real_to_reciprocal4(PyObject *self, PyObject *args)
{
  PyArrayObject* fc4_py;
  PyArrayObject* fc4_reciprocal_py;
  PyArrayObject* q_py;
  PyArrayObject* shortest_vectors;
  PyArrayObject* multiplicity;
  PyArrayObject* p2s_map;
  PyArrayObject* s2p_map;

  if (!PyArg_ParseTuple(args, "OOOOOOO",
			&fc4_reciprocal_py,
			&fc4_py,
			&q_py,
			&shortest_vectors,
			&multiplicity,
			&p2s_map,
			&s2p_map)) {
    return NULL;
  }

  double* fc4 = (double*)fc4_py->data;
  lapack_complex_double* fc4_reciprocal =
    (lapack_complex_double*)fc4_reciprocal_py->data;
  Darray* svecs = convert_to_darray(shortest_vectors);
  Iarray* multi = convert_to_iarray(multiplicity);
  const int* p2s = (int*)p2s_map->data;
  const int* s2p = (int*)s2p_map->data;
  const double* q = (double*)q_py->data;

  real_to_reciprocal4(fc4_reciprocal,
		      q,
		      fc4,
		      svecs,
		      multi,
		      p2s,
		      s2p);

  free(svecs);
  free(multi);

  Py_RETURN_NONE;
}

static PyObject * py_reciprocal_to_normal4(PyObject *self, PyObject *args)
{
  PyArrayObject* fc4_normal_py;
  PyArrayObject* fc4_reciprocal_py;
  PyArrayObject* frequencies_py;
  PyArrayObject* eigenvectors_py;
  PyArrayObject* grid_points_py;
  PyArrayObject* masses_py;
  PyArrayObject* band_indicies_py;
  double cutoff_frequency;

  if (!PyArg_ParseTuple(args, "OOOOOOOd",
			&fc4_normal_py,
			&fc4_reciprocal_py,
			&frequencies_py,
			&eigenvectors_py,
			&grid_points_py,
			&masses_py,
			&band_indicies_py,
			&cutoff_frequency)) {
    return NULL;
  }

  lapack_complex_double* fc4_normal =
    (lapack_complex_double*)fc4_normal_py->data;
  const lapack_complex_double* fc4_reciprocal =
    (lapack_complex_double*)fc4_reciprocal_py->data;
  const lapack_complex_double* eigenvectors =
    (lapack_complex_double*)eigenvectors_py->data;
  const double* frequencies = (double*)frequencies_py->data;
  const int* grid_points = (int*)grid_points_py->data;
  const double* masses = (double*)masses_py->data;
  const int* band_indices = (int*)band_indicies_py->data;
  const int num_band0 = (int)band_indicies_py->dimensions[0];
  const int num_band = (int)frequencies_py->dimensions[1];

  reciprocal_to_normal4(fc4_normal,
			fc4_reciprocal,
			frequencies + grid_points[0] * num_band,
			frequencies + grid_points[1] * num_band,
			eigenvectors + grid_points[0] * num_band * num_band,
			eigenvectors + grid_points[1] * num_band * num_band,
			masses,
			band_indices,
			num_band0,
			num_band,
			cutoff_frequency);

  Py_RETURN_NONE;
}

static PyObject * py_set_phonons_grid_points(PyObject *self, PyObject *args)
{
  PyArrayObject* frequencies;
  PyArrayObject* eigenvectors;
  PyArrayObject* phonon_done_py;
  PyArrayObject* grid_points_py;
  PyArrayObject* grid_address_py;
  PyArrayObject* mesh_py;
  PyArrayObject* shortest_vectors_fc2;
  PyArrayObject* multiplicity_fc2;
  PyArrayObject* fc2_py;
  PyArrayObject* atomic_masses_fc2;
  PyArrayObject* p2s_map_fc2;
  PyArrayObject* s2p_map_fc2;
  PyArrayObject* reciprocal_lattice;
  PyArrayObject* born_effective_charge;
  PyArrayObject* q_direction;
  PyArrayObject* dielectric_constant;
  double nac_factor, unit_conversion_factor;
  char uplo;

  if (!PyArg_ParseTuple(args, "OOOOOOOOOOOOdOOOOdc",
			&frequencies,
			&eigenvectors,
			&phonon_done_py,
			&grid_points_py,
			&grid_address_py,
			&mesh_py,
			&fc2_py,
			&shortest_vectors_fc2,
			&multiplicity_fc2,
			&atomic_masses_fc2,
			&p2s_map_fc2,
			&s2p_map_fc2,
			&unit_conversion_factor,
			&born_effective_charge,
			&dielectric_constant,
			&reciprocal_lattice,
			&q_direction,
			&nac_factor,
			&uplo)) {
    return NULL;
  }

  double* born;
  double* dielectric;
  double *q_dir;
  Darray* freqs = convert_to_darray(frequencies);
  /* npy_cdouble and lapack_complex_double may not be compatible. */
  /* So eigenvectors should not be used in Python side */
  Carray* eigvecs = convert_to_carray(eigenvectors);
  char* phonon_done = (char*)phonon_done_py->data;
  Iarray* grid_points = convert_to_iarray(grid_points_py);
  const int* grid_address = (int*)grid_address_py->data;
  const int* mesh = (int*)mesh_py->data;
  Darray* fc2 = convert_to_darray(fc2_py);
  Darray* svecs_fc2 = convert_to_darray(shortest_vectors_fc2);
  Iarray* multi_fc2 = convert_to_iarray(multiplicity_fc2);
  const double* masses_fc2 = (double*)atomic_masses_fc2->data;
  const int* p2s_fc2 = (int*)p2s_map_fc2->data;
  const int* s2p_fc2 = (int*)s2p_map_fc2->data;
  const double* rec_lat = (double*)reciprocal_lattice->data;
  if ((PyObject*)born_effective_charge == Py_None) {
    born = NULL;
  } else {
    born = (double*)born_effective_charge->data;
  }
  if ((PyObject*)dielectric_constant == Py_None) {
    dielectric = NULL;
  } else {
    dielectric = (double*)dielectric_constant->data;
  }
  if ((PyObject*)q_direction == Py_None) {
    q_dir = NULL;
  } else {
    q_dir = (double*)q_direction->data;
  }

  set_phonons_for_frequency_shift(freqs,
				  eigvecs,
				  phonon_done,
				  grid_points,
				  grid_address,
				  mesh,
				  fc2,
				  svecs_fc2,
				  multi_fc2,
				  masses_fc2,
				  p2s_fc2,
				  s2p_fc2,
				  unit_conversion_factor,
				  born,
				  dielectric,
				  rec_lat,
				  q_dir,
				  nac_factor,
				  uplo);

  free(freqs);
  free(eigvecs);
  free(grid_points);
  free(fc2);
  free(svecs_fc2);
  free(multi_fc2);

  Py_RETURN_NONE;
}

static PyObject * py_distribute_fc4(PyObject *self, PyObject *args)
{
  PyArrayObject* fc4_copy_py;
  PyArrayObject* fc4_py;
  int fourth_atom;
  PyArrayObject* rotation_cart_inv;
  PyArrayObject* atom_mapping_py;

  if (!PyArg_ParseTuple(args, "OOiOO",
			&fc4_copy_py,
			&fc4_py,
			&fourth_atom,
			&atom_mapping_py,
			&rotation_cart_inv)) {
    return NULL;
  }

  double* fc4_copy = (double*)fc4_copy_py->data;
  const double* fc4 = (double*)fc4_py->data;
  const double* rot_cart_inv = (double*)rotation_cart_inv->data;
  const int* atom_mapping = (int*)atom_mapping_py->data;
  const int num_atom = (int)atom_mapping_py->dimensions[0];

  return PyInt_FromLong((long) distribute_fc4(fc4_copy,
					      fc4,
					      fourth_atom,
					      atom_mapping,
					      num_atom,
					      rot_cart_inv));
}

static PyObject * py_rotate_delta_fc3s_elem(PyObject *self, PyObject *args)
{
  PyArrayObject* rotated_delta_fc3s_py;
  PyArrayObject* delta_fc3s_py;
  PyArrayObject* atom_mappings_of_rotations_py;
  PyArrayObject* site_symmetries_cartesian_py;
  int atom1, atom2, atom3;

  if (!PyArg_ParseTuple(args, "OOOOiii",
			&rotated_delta_fc3s_py,
			&delta_fc3s_py,
			&atom_mappings_of_rotations_py,
			&site_symmetries_cartesian_py,
			&atom1,
			&atom2,
			&atom3)) {
    return NULL;
  }

  double* rotated_delta_fc3s = (double*)rotated_delta_fc3s_py->data;
  const double* delta_fc3s = (double*)delta_fc3s_py->data;
  const int* rot_map_syms = (int*)atom_mappings_of_rotations_py->data;
  const double* site_syms_cart = (double*)site_symmetries_cartesian_py->data;
  const int num_rot = (int)site_symmetries_cartesian_py->dimensions[0];
  const int num_delta_fc3s = (int)delta_fc3s_py->dimensions[0];
  const int num_atom = (int)delta_fc3s_py->dimensions[1];

  return PyInt_FromLong((long) rotate_delta_fc3s_elem(rotated_delta_fc3s,
						      delta_fc3s,
						      rot_map_syms,
						      site_syms_cart,
						      num_rot,
						      num_delta_fc3s,
						      atom1,
						      atom2,
						      atom3,
						      num_atom));
}

static PyObject * py_set_translational_invariance_fc4(PyObject *self,
						      PyObject *args)
{
  PyArrayObject* fc4_py;
  int index;

  if (!PyArg_ParseTuple(args, "Oi",
			&fc4_py,
			&index)) {
    return NULL;
  }

  double* fc4 = (double*)fc4_py->data;
  const int num_atom = (int)fc4_py->dimensions[0];

  set_translational_invariance_fc4_per_index(fc4, num_atom, index);

  Py_RETURN_NONE;
}

static PyObject * py_set_permutation_symmetry_fc4(PyObject *self, PyObject *args)
{
  PyArrayObject* fc4_py;

  if (!PyArg_ParseTuple(args, "O",
			&fc4_py)) {
    return NULL;
  }

  double* fc4 = (double*)fc4_py->data;
  const int num_atom = (int)fc4_py->dimensions[0];

  set_permutation_symmetry_fc4(fc4, num_atom);

  Py_RETURN_NONE;
}

static PyObject * py_get_drift_fc4(PyObject *self, PyObject *args)
{
  PyArrayObject* fc4_py;

  if (!PyArg_ParseTuple(args, "O",
			&fc4_py)) {
    return NULL;
  }

  double* fc4 = (double*)fc4_py->data;
  const int num_atom = (int)fc4_py->dimensions[0];

  int i;
  double drift[4];
  PyObject* drift_py;

  get_drift_fc4(drift, fc4, num_atom);
  drift_py = PyList_New(4);

  for (i = 0; i < 4; i++) {
    PyList_SetItem(drift_py, i, PyFloat_FromDouble(drift[i]));
  }

  return drift_py;
}


#ifndef libceed_solids_examples_setup_libceed_h
#define libceed_solids_examples_setup_libceed_h

#include <ceed.h>
#include <petsc.h>
#include "../include/structs.h"

// -----------------------------------------------------------------------------
// libCEED Functions
// -----------------------------------------------------------------------------
// Destroy libCEED objects
PetscErrorCode CeedDataDestroy(CeedInt level, CeedData data);

// Utility function - essential BC dofs are encoded in closure indices as -(i+1)
PetscInt Involute(PetscInt i);

// Utility function to create local CEED restriction from DMPlex
PetscErrorCode CreateRestrictionFromPlex(Ceed ceed, DM dm, CeedInt height,
    DMLabel domain_label, CeedInt value, CeedElemRestriction *elem_restr);

// Utility function to get Ceed Restriction for each domain
PetscErrorCode GetRestrictionForDomain(Ceed ceed, DM dm, CeedInt height,
                                       DMLabel domain_label, PetscInt value,
                                       CeedInt Q, CeedInt q_data_size,
                                       CeedElemRestriction *elem_restr_q,
                                       CeedElemRestriction *elem_restr_x,
                                       CeedElemRestriction *elem_restr_qd_i);

// Set up libCEED for a given degree
PetscErrorCode SetupLibceedFineLevel(DM dm, DM dm_energy, DM dm_diagnostic,
                                     Ceed ceed, AppCtx app_ctx,
                                     CeedQFunctionContext phys_ctx,
                                     ProblemData problem_data,
                                     PetscInt fine_level, PetscInt num_comp_u,
                                     PetscInt U_g_size, PetscInt U_loc_size,
                                     CeedVector force_ceed,
                                     CeedVector neumann_ceed, CeedData *data);

// Set up libCEED multigrid level for a given degree
PetscErrorCode SetupLibceedLevel(DM dm, Ceed ceed, AppCtx app_ctx,
                                 ProblemData problem_data, PetscInt level,
                                 PetscInt num_comp_u, PetscInt U_g_size,
                                 PetscInt U_loc_size, CeedVector fine_mult,
                                 CeedData *data);

#endif // libceed_solids_examples_setup_libceed_h


/* multimin/fminimizer.c
 *
 * Copyright (C) 2002 Tuomo Keskitalo, Ivo Alxneit
 *
 * 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.
 */

#include <config.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_multimin.h>

gsl_multimin_fminimizer *
gsl_multimin_fminimizer_alloc (const gsl_multimin_fminimizer_type * T,
                               size_t n)
{
  int status;

  gsl_multimin_fminimizer *s =
    (gsl_multimin_fminimizer *) malloc (sizeof (gsl_multimin_fminimizer));

  if (s == 0)
    {
      GSL_ERROR_VAL ("failed to allocate space for minimizer struct",
                     GSL_ENOMEM, 0);
    }

  s->type = T;

  s->x = gsl_vector_calloc (n);

  if (s->x == 0)
    {
      free (s);
      GSL_ERROR_VAL ("failed to allocate space for x", GSL_ENOMEM, 0);
    }

  s->state = malloc (T->size);

  if (s->state == 0)
    {
      gsl_vector_free (s->x);
      free (s);
      GSL_ERROR_VAL ("failed to allocate space for minimizer state",
                     GSL_ENOMEM, 0);
    }

  status = (T->alloc) (s->state, n);

  if (status != GSL_SUCCESS)
    {
      free (s->state);
      gsl_vector_free (s->x);
      free (s);

      GSL_ERROR_VAL ("failed to initialize minimizer state", GSL_ENOMEM, 0);
    }

  return s;
}

int
gsl_multimin_fminimizer_set (gsl_multimin_fminimizer * s,
                             gsl_multimin_function * f,
                             const gsl_vector * x,
                             const gsl_vector * step_size)
{
  if (s->x->size != f->n)
    {
      GSL_ERROR ("function incompatible with solver size", GSL_EBADLEN);
    }

  if (x->size != f->n || step_size->size != f->n)
    {
      GSL_ERROR ("vector length not compatible with function", GSL_EBADLEN);
    }

  s->f = f;

  gsl_vector_memcpy (s->x,x);

  return (s->type->set) (s->state, s->f, s->x, &(s->size), step_size);
}

void
gsl_multimin_fminimizer_free (gsl_multimin_fminimizer * s)
{
  (s->type->free) (s->state);
  free (s->state);
  gsl_vector_free (s->x);
  free (s);
}

int
gsl_multimin_fminimizer_iterate (gsl_multimin_fminimizer * s)
{
  return (s->type->iterate) (s->state, s->f, s->x, &(s->size), &(s->fval));
}

const char *
gsl_multimin_fminimizer_name (const gsl_multimin_fminimizer * s)
{
  return s->type->name;
}

gsl_vector *
gsl_multimin_fminimizer_x (const gsl_multimin_fminimizer * s)
{
  return s->x;
}

double
gsl_multimin_fminimizer_minimum (const gsl_multimin_fminimizer * s)
{
  return s->fval;
}

double
gsl_multimin_fminimizer_size (const gsl_multimin_fminimizer * s)
{
  return s->size;
}


#pragma once

#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
#pragma warning(disable:4238) // nonstandard extension used : class rvalue used as lvalue
#pragma warning(disable:4324) // structure was padded due to __declspec(align())

#ifndef WIN32_LEAN_AND_MEAN
    #define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
    #define NOMINMAX
#endif
#include <windows.h>

#include <d3d12.h>

#pragma comment(lib, "d3d12.lib")
#pragma comment(lib, "dxgi.lib")

#define MY_IID_PPV_ARGS IID_PPV_ARGS
#define D3D12_GPU_VIRTUAL_ADDRESS_NULL      ((D3D12_GPU_VIRTUAL_ADDRESS)0)
#define D3D12_GPU_VIRTUAL_ADDRESS_UNKNOWN   ((D3D12_GPU_VIRTUAL_ADDRESS)-1)

#pragma region
#include <d3dx12.h>
#include <d3d12shader.h>
#include <d3dcompiler.h>
#pragma endregion

#pragma region

#pragma comment (lib, "d3dcompiler.lib")

#pragma endregion

#include <cstdint>
#include <cstdio>
#include <cstdarg>
#include <vector>
#include <memory>
#include <string>
#include <exception>
#include <numeric>
#include <wrl.h>
#include <ppltasks.h>
#include <gsl\gsl>

#include "Utility.h"
#include "VectorMath.h"
#include "EngineTuning.h"
#include "EngineProfiling.h"

template< typename T >
using NotNull = gsl::not_null< T >;

#define SHADER_ARGS(shader) (shader), \
								 sizeof(shader)

template<typename T, std::size_t N>
struct Array :public std::array<T, N>
{
    static inline std::function<T(T, T)> multiply = [](T priview, T current) {
        return priview * current;
    };
public:
    T product()
    {
        return std::accumulate(begin(), end(), 1, multiply);
    }
};
using U8 = uint8_t;
using U32 = uint32_t;
using U32x1 = U32;
using U32x2 = Array<U32, 2>;
using U32x3 = Array<U32, 3>;
using U32x4 = Array<U32, 4>;
using F32 = float;
using F32x2 = Array<F32, 2>;
using F32x3 = Array<F32, 3>;
using F32x4 = Array<F32, 4>;

#pragma region voxel
#define VOXEL_RESOLUTION 128
#define CLIP_REGION_COUNT 6
#define FACE_COUNT 6
#pragma endregion

template <typename T, size_t N > Array<T, N> DivideByMultiple(Array <T, N> value, Array <T, N> alignment)
{
    Array <T, N> result;
    for (size_t i = 0; i < N; i++)
    {
        result[i] = (T)((value[i] + alignment[i] - 1) / alignment[i]);
    }
    return result;
}


#pragma once

#include "cell.h"
#include <boost/mp11/algorithm.hpp>
#include <array>
#include <gsl/span>
#include <type_traits>
#include <tuple>

using ColumnId = CellId;

template <typename T>
constexpr ColumnId GetColumnId() {
    return CellMetaT<T>().id;
}

template <typename T>
constexpr auto storage_required_v = std::is_empty_v<T> ? 0 : sizeof(T);

// Sorts by alignment, storage size, and then name.
template <typename L, typename R>
struct ColumnId_less {
    static constexpr bool value = CellMetaT<L>() > CellMetaT<R>();
};

// List is assumed to be unique and sorted by criteria listed above
class ColumnList : public gsl::span<const ColumnId> {
public:
    using span::span;

    bool ContainsAll(const ColumnList& rhs) const {
        auto curL = begin(), endL = end();
        auto curR = rhs.begin(), endR = rhs.end();
        for (; curR != endR; ++curR) {
            for (;; ++curL) {
                if (curL == endL)
                    return false;
                if (*curL == *curR)
                    break;
            }
        }
        return true;
    }
};

template <typename U>
struct extraction;

template <typename... Us>
struct extraction<std::tuple<Us...>> {
    static constexpr std::array<ColumnId, sizeof...(Us)> ids = { GetColumnId<Us>()... };
    static constexpr std::array<CellMeta, sizeof...(Us)> metas = { CellMetaT<Us>()... };
};

template <typename... Ts>
struct ColumnListT : ColumnList {
    using unique = boost::mp11::mp_unique<std::tuple<Ts...>>;
    using sorted = boost::mp11::mp_sort<unique, ColumnId_less>;
    using types = sorted;

    static constexpr auto ids = extraction<types>::ids;
    static constexpr auto metas = extraction<types>::metas;
    constexpr ColumnListT() : ColumnList(ids) { }
};


#ifndef __INC_ESTIMATE_THREADED__
#define __INC_ESTIMATE_THREADED__

#include <unistd.h>
#include <pthread.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>

/* common data block for most options to be passed around */

struct optstruct;
struct modelstruct;

//! used to pass the args into estimate_thread_function
struct estimate_thetas_params{
	// these are direct copies of the respective data structures
	struct optstruct* options;
	struct modelstruct* the_model;
	// things needed specifically for the estimation
	gsl_rng* random_number;
	gsl_matrix* h_matrix;
	int max_tries;
	int success_count;
	double lhood_current;
	double my_best; // want to check on the local best values
} estimate_thetas_params;

#define USEMUTEX

#include "../modelstruct.h"
#include "../optstruct.h"

void estimate_thetas_threaded(modelstruct* the_model, optstruct* options);

// this won't work unless it has access to the nasty globals in estimate_threaded.c
void* estimate_thread_function(void* args);
int get_number_cpus(void);

void setup_params(struct estimate_thetas_params *params, modelstruct* the_model, optstruct* options, int nthreads, int max_tries);

void fprintPt(FILE *f, pthread_t pt);

// see the source for defs of the number of threads etc etc
#endif


/*
 *  parameters.h
 *  Spike
 *
 *  Created by Ben Evans on 18/08/2008.
 *  Copyright 2008 University of Oxford. All rights reserved.
 *
 */

#ifndef _PARAMETERS_H
#define _PARAMETERS_H

#include <stdbool.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_version.h>

typedef int tstep; // Signed to allow spikeTimes[0] = -BIG //long long

typedef unsigned char uchar;

struct SIMULATION{
	bool Xgrid;
	tstep tally;
	tstep ptTS;
	tstep trainTS;
	tstep testTS;
	tstep totTS;
	float minTau;
	double start;
	double elapsed;
	double realSecPerSimSec;
} SIM;

typedef struct DIMENSIONS {
	int nRows;
	int nCols;
	int nFilt;
} DIM;

//extern SIMULATION SIM;

/*typedef enum {
	Off,
	//Uniform,
	Gaussian
} NOISE;*/

typedef enum {
	MinD,
	ConstD,
	UniformD,
	GaussD,
	SOMD
} DELAY;

typedef enum {
	//Zero,
	Constant, // Zero set using appropriate Dg modifier
	Uniform,
	Gaussian,
	SOM
} INITIALISATION;

typedef enum {
	None,
	MaintainLength,
	MaintainSum
} NORMALISATION;

/********************** Main Network Parmeters *************************/
// Update read_parameters and defaults.m when a new parameter is added
typedef struct {
	// Simulation
	float DT;
    bool initialised; // Internal
	int loops;
	bool pretrain;// = true;
	bool train;// = true;
	bool priorPhases;
	bool isolateEfE;
	bool trainPause;
	bool noise; // int
	float noiseScale;
	float SigmaE;
	float SigmaI;
	NORMALISATION normalise;
	int nRecordsPL;// = 0;
	int nRecords;
	int * vRecords;
	//float TotalTime;
	float MaxTime;
	float EpochTime;
	float TestTime;
	int EpochMS;
	int TestMS;
	int TotalMS;
	int RecordMS;
	//tstep TotalTS;
	int TSperMS;
	int spkBuffer;
	//int inpSpkBuff;
	bool printConnections;
	bool saveInputSpikes;
	bool probConnect;
	bool loadWeights;

	// Stimuli
	char * imgDir;
	char * imgList;
	bool randStimOrder;// = true;
	bool randTransOrder;
	bool randTransDirection;
	bool interleaveTrans;
	bool localRep;// = true;
	float current;// = 1.25e-9;
	float currentSpread;
	bool loadStimuli;
	bool stimGroups;
	int nBG; // # shared between groups
	int nWG; // # specific within each group
	int nGroups;
	int nStimuli;
	int nTransPS;
	bool newTestSet;
	int M; // Degree of training i.e. train with M of the NCK
	int K; // Number of stimuli presented simultaneously
	//int nTestGroups;
	int nTestStimuli;
	int nTestTransPS;
	float transP_Train;
	float transP_Test;
	int shift;
	int nFiringNeurons;
	float a; // Sparseness of stimuli
	bool useFilteredImages;
	bool gabor;
	int nScales;
	int * vScales;
	int nOrients;
	int * vOrients;
	int nPhases;
	int * vPhases;
	int sInputs;
	int nRows;
	int nCols;

	// Network
	int nLayers;// = 2;
	int nWLayers;
	bool inputInhib;
	int nExcit;// = 120;
	int * vExcit;
	int LvExcit;
	int nSynEfE;
	float * pCnxEfE;
	int LpEfE;
	int nSynElE;
	float * pCnxElE;
	int LpElE;
	int nSynIE;
	float * pCnxIE;
	int LpIE;
	int nInhib;// = 40;
	float rInhib;
	int * vInhib;
	int LvInhib;
	int nSynEI;
	float * pCnxEI;
	int LpEI;
	int nSynII;
	float * pCnxII;
	int LpII;
	INITIALISATION initEfE;
	float iEfE;
	bool axonDelay; //DELAY axonDelay;
    DELAY delayEfE;
    DELAY delayElE;
    DELAY delayEI;
	float d_const;
	float d_min;
	float d_max;
	float d_mean;
	float d_sd;
	float spatialScale;
	float condSpeed;
	float maxDelay;
	bool SOM;
	bool SOMinput;
	float SOMsigE;
	float SOMsigI;
	float SOMclip;
	//float SOMstrE;
	//float SOMstrI;
	bool trainEfE; // Internal var modified through isolateEfE
	bool trainElE;
	INITIALISATION initElE;
	float iElE;
	DIM * layDim;
	int * vSquare;

	// Cell bodies
	float capE;// = 2.0e-10;
	float capI;// = 10.0e-12;
	float gLeakE;// = 10.0e-9;
	float gLeakI;// = 5.0e-9;
	float VrestE;
	float VrestI;
	float VhyperE;
	float VhyperI;
	float ThreshE;
	float ThreshI;
	float VrevE;
	float VrevI;
	float refract;// = 0.002
	bool adaptation;
	float alphaCa;
	float tauCa;
	float gAHP;
	float VK;

	// Synapses (afferent axons)
    bool noSTDPdelay;
	float alphaC;// = 0.5;
	float tauC;// = 0.005;
	float alphaD;// = 0.5;
	float tauD;// = 0.009;
	float learnR;// = 0.1;
	float DgEfE; //modEf; // alter the strength of Excit f-f synapses
	//float tauEfE;// tauEE = 0.01;
	float DgElE;//Dg_ElE
	//float tauElE;
	float tauEE;
	float DgIE;//Dg_IE = 0.5;
	float tauIE;// = 0.001
	float DgEI;//Dg_EI = 0.5;
	float tauEI;// = 0.001;
	float DgII;//Dg_II = 0.5;
	float tauII;// = 0.001;
	float gMax;// = 48.0e-10;
} PARAMS;

//PARAMS * mp;
extern PARAMS * mp;
extern gsl_rng * mSeed;
extern gsl_rng ** states;

#endif


#ifndef SIMULATION_STEPS

#define SIMULATION_STEPS

#include <gsl/gsl_matrix.h>

extern void calculateLIFTVolMat(int N, int Np, gsl_matrix **LIFT, gsl_matrix **VolMat, gsl_matrix** MassMatrix);

extern void store_mesh(char *Mesh);
extern void initialize();
extern void time_evolution(double FinalTime);
extern void boundary_conditions();
#endif


#include <errno.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <setjmp.h>

#include <glib.h>
#if GLIB_CHECK_VERSION (2, 6, 0)
#  include <glib/gstdio.h>
#endif
#include <gsl/gsl_spline.h>
#include <gsl/gsl_histogram.h>
#include <gsl/gsl_math.h>

#include "asf.h"
#include "float_image.h"
#include "asf_endian.h"
#include "asf_jpeg.h"

#include "asf_view.h"

#ifndef linux
#ifndef darwin
#ifndef win32
static double
round (double arg)
{
  return floor (arg + 0.5);
}
#endif // #ifndef win32
#endif // #ifndef darwin
#endif // #ifndef linux

#include "asf_glib.h"

// at 64MB tiles, this is ~1.5GB
static const int MAX_TILES = 24;

// quit blathering?
int quiet = FALSE;

static int data_size(CachedImage *self)
{
    switch (self->data_type) {
        case GREYSCALE_FLOAT:
            return 4;
        case RGB_BYTE:
            return 3;
        case GREYSCALE_BYTE:
            return 1;
        case RGB_FLOAT:
            return 12;
        default:
            assert(FALSE);
            return 0;
    }
}

static void print_cache_size(CachedImage *self)
{
    int i;
    int size=0;
    int ds = data_size(self);
    for (i=0; i<self->n_tiles; ++i)
        size += ds*self->rows_per_tile*self->ns;

    asfPrintStatus("Cache size is %.1f megabytes.\n",
        (float)size/1024./1024.);
}

static unsigned char *get_pixel(CachedImage *self, int line, int samp)
{
    // check if outside the image
    static unsigned char zero = 0;
    if (line<0 || samp<0 || line >= self->nl || samp >= self->ns)
        return &zero;

    // size of each pixel
    int ds = data_size(self);

    int i;
    for (i=0; i<self->n_tiles; ++i) {
        int rs = self->rowstarts[i];
        if (rs >= 0) {
            if (line >= rs && line < rs+self->rows_per_tile) {
                // found the right cache
                assert(self->cache[i]);

                // this probably won't ever happen, but here we go anyway
                if (self->n_access > 1024*1024*1024) {
                    asfPrintStatus("Resetting n_access.\n");
                    for (i=0; i<self->n_tiles; ++i)
                        self->access_counts[i] = 0;
                    self->n_access = 1;
                }

                // mark this as the most recently accessed
                self->access_counts[i] = self->n_access++;

                // return pointer to the cached value
                return &self->cache[i][((line-rs)*self->ns + samp)*ds];
            }
        }
    }

    int spot = 0;
    if (!self->reached_max_tiles) {
        assert(self->cache[self->n_tiles] == NULL);
        unsigned char *data = malloc(ds*self->ns*self->rows_per_tile);
        if (!data) {
            // if this is the first tile -- abort, we are out of memory
            if (self->n_tiles == 0)
                asfPrintError("Failed to allocate cache of %ld bytes.\n"
                              "Out of memory.\n",
                              ds*self->ns*self->rows_per_tile);
            // couldn't allocate the next tile -- must dump existing
            if (!quiet)
                asfPrintStatus("reached max # of tiles: %d\n", self->n_tiles);
            print_cache_size(self);
            self->reached_max_tiles = TRUE;
        } else {
            spot = self->n_tiles;
            self->cache[spot] = data;
            ++self->n_tiles;
        }
    }

    if (self->reached_max_tiles) {
        // dump an existing cached tile
        // not found in the cache -- find least used spot
        int least_access_count = self->access_counts[0];
        for (i=0; i<self->n_tiles; ++i) {
            if (self->access_counts[i] < least_access_count) {
                least_access_count = self->access_counts[i];
                spot = i;
            }
        }
    }

    if (!self->reached_max_tiles && self->n_tiles == MAX_TILES) {
        if (!quiet)
            asfPrintStatus("Fully loaded with %d tiles.\n", self->n_tiles);
        print_cache_size(self);
        self->reached_max_tiles = TRUE;
    }

    // load info from file
    assert(spot >= 0 && spot < self->n_tiles);
    assert(self->cache[spot] != NULL);

    // clear out the cache -- we may not fill up the tile, if
    // we are near the end of the file, and we don't want old data
    // to appear
    int ns = self->ns;
    memset(self->cache[spot], 0, ds*ns*self->rows_per_tile);

    // update where this cache entry starts
    int rs = (line / self->rows_per_tile) * self->rows_per_tile;
    self->rowstarts[spot] = rs;

    // mark this tile as the most recently accessed
    self->access_counts[spot] = self->n_access++;

    //printf("Updated entry #%d:\n"
    //    "     row start: %d\n"
    //    "     row end: %d\n"
    //    "     access count: %d\n", spot,
    //    rs, rs+self->rows_per_tile-1, self->access_counts[spot]);

    // ensure we don't read past the end of the file
    int rows_to_get = self->rows_per_tile;
    if (rs + self->rows_per_tile > self->nl)
        rows_to_get = self->nl - rs;

    if (!quiet) {
        asfPrintStatus("Cache: loading into spot #%d: rows %d-%d\n",
            spot, rs, rs+rows_to_get);
        //print_cache_size(self);
    }

    self->client->read_fn(rs, rows_to_get, (void*)(self->cache[spot]),
        self->client->read_client_info, self->meta, self->client->data_type);

    assert((line-rs)*self->ns + samp <= self->ns*self->rows_per_tile);
    return &self->cache[spot][((line-rs)*self->ns + samp)*ds];
}

void load_thumbnail_data(CachedImage *self, int thumb_size_x, int thumb_size_y,
                         void *dest_void)
{
    if (self->entire_image_fits || !self->client->thumb_fn) {
        // Either we don't have thumbnailing support from the client,
        // or the image will fit entirely in memory.  In both cases, we
        // can just call get_pixel() on the subset necessary to show
        // the image, which will load the entire image into cache.
        // (... well, unless it is so huge that our tiles fit between
        //      the thumbnail grid ... but that's crazy talk)
        int ds = data_size(self);
        unsigned char *dest = (unsigned char*)dest_void;

        // this will fill the cache with the image data
        int sf = self->meta->general->line_count / thumb_size_y;
        //assert(sf == self->meta->general->sample_count / thumb_size_x);

        // supress the "populating cache" msgs when loading the whole thing
        quiet=TRUE;

        int i,j;
        for (i=0; i<thumb_size_y; ++i) {
            for (j=0; j<thumb_size_x; ++j) {
                // make this independent of the data type
                unsigned char *p = get_pixel(self,i*sf,j*sf);
                memcpy(dest+(i*thumb_size_x+j)*ds, p, ds);
            }
            asfPercentMeter((float)i/(thumb_size_y-1));
        }

        quiet=FALSE;
    } else {
        self->client->thumb_fn(thumb_size_x, thumb_size_y,
            self->meta, self->client->read_client_info, dest_void,
            self->client->data_type);
    }
}

CachedImage * cached_image_new_from_file(
    const char *file, meta_parameters *meta, ClientInterface *client,
    ImageStats *stats, ImageStatsRGB *stats_r, ImageStatsRGB *stats_g,
    ImageStatsRGB *stats_b)
{
    CachedImage *self = MALLOC(sizeof(CachedImage));

    asfPrintStatus("Opening cache: %s\n", file);

    self->data_type = client->data_type;
    assert(self->data_type != UNDEFINED);

    self->client = client;     // take ownership of this
    self->meta = meta;         // do NOT take ownership of this

    self->stats = stats;       // do NOT take ownership of this

    self->stats_r = stats_r;   // do NOT take ownership of this
    self->stats_g = stats_g;   // do NOT take ownership of this
    self->stats_b = stats_b;   // do NOT take ownership of this

    // line line_count may have been fudges, if we are multilooking
    self->nl = meta->general->line_count;
    self->ns = meta->general->sample_count;
    asfPrintStatus("Image is %dx%d LxS\n", self->nl, self->ns);

    if (client->require_full_load) {
        // Use only 1 tile -- load entire image into it
        // (client tells us we should do it this way)
        // will it fit?  Who knows.  Assume it will, MALLOC will fail
        // if it actually does not.
        self->rows_per_tile = self->nl;
    } else {
        // how many rows per tile?
        // We will use ~64 Meg tiles
        self->rows_per_tile = 64*1024*1024 / (self->ns*data_size(self));

        // test line -- uncomment this for very small tiles
        //self->rows_per_tile = 2*1024*1024 / (self->ns*data_size(self));
    }

    asfPrintStatus("Using %d rows per tile.\n", self->rows_per_tile);

    int n_tiles_required = (int)ceil((double)self->nl / self->rows_per_tile);
    self->entire_image_fits = n_tiles_required <= MAX_TILES;
    // self->entire_image_fits = FALSE; // uncomment to test thumb_fn

    // at the beginning, we have no tiles
    self->n_tiles = 0;
    self->reached_max_tiles = FALSE;

    int i;
    self->rowstarts = MALLOC(sizeof(int)*MAX_TILES);
    self->cache = MALLOC(sizeof(float*)*MAX_TILES);
    self->access_counts = MALLOC(sizeof(int)*MAX_TILES);
    for (i=0; i<MAX_TILES; ++i) {
        self->rowstarts[i] = -1;
        self->cache[i] = NULL;
        self->access_counts[i] = 0;
    }

    self->n_access = 0;

    asfPrintStatus("Number of tiles required for the entire image: %d\n",
        n_tiles_required);
    asfPrintStatus("Fits in memory: %s\n",
        self->entire_image_fits ? "Yes" : "No");

    return self;
}

float cached_image_get_pixel (CachedImage *self, int line, int samp)
{
    if (self->data_type == GREYSCALE_FLOAT) {
        return *((float*)get_pixel(self, line, samp));
    }
    else if (self->data_type == GREYSCALE_BYTE) {
        return (float) *(get_pixel(self, line, samp));
    }
    else if (self->data_type == RGB_BYTE || self->data_type == RGB_FLOAT) {
        unsigned char r, g, b;
        cached_image_get_rgb(self, line, samp, &r, &g, &b);
        return ((float)r + (float)g + (float)b)/3.;
    }

    // not reached
    assert(0);
    return 0;
}

void cached_image_get_rgb(CachedImage *self, int line, int samp,
                          unsigned char *r, unsigned char *g,
                          unsigned char *b)
{
    if (self->data_type == GREYSCALE_FLOAT) {
        float f = cached_image_get_pixel(self, line, samp);
        if (have_lut()) {
            // do not scale in the case of a lut
            apply_lut((int)f, r, g, b);
        } else {
            *r = *g = *b =
              (unsigned char)calc_scaled_pixel_value(self->stats, f);
        }
    }
    else if (self->data_type == GREYSCALE_BYTE) {
        if (have_lut()) {
            apply_lut((int)(*(get_pixel(self, line, samp))), r, g, b);
        }
        else {
            float f = (float) *(get_pixel(self, line, samp));
            *r = *g = *b =
                (unsigned char)calc_scaled_pixel_value(self->stats, f);
        }
    }
    else if (self->data_type == RGB_BYTE) {
        unsigned char *uc = get_pixel(self, line, samp);

        *r = (unsigned char)calc_rgb_scaled_pixel_value(self->stats_r,
                                                        (float)uc[0]);
        *g = (unsigned char)calc_rgb_scaled_pixel_value(self->stats_g,
                                                        (float)uc[1]);
        *b = (unsigned char)calc_rgb_scaled_pixel_value(self->stats_b,
                                                        (float)uc[2]);
    }
    else if (self->data_type == RGB_FLOAT) {
        float *f = (float*)get_pixel(self, line, samp);

        *r = (unsigned char)calc_rgb_scaled_pixel_value(self->stats_r,f[0]);
        *g = (unsigned char)calc_rgb_scaled_pixel_value(self->stats_g,f[1]);
        *b = (unsigned char)calc_rgb_scaled_pixel_value(self->stats_b,f[2]);
    }
    else {
        // impossible!
        assert(0);
        *r = *g = *b = 0;
    }
}

void cached_image_get_rgb_float(CachedImage *self, int line, int samp,
                                float *r, float *g, float *b)
{
    if (self->data_type == GREYSCALE_FLOAT) {
        float f = cached_image_get_pixel(self, line, samp);
        *r = *g = *b = calc_scaled_pixel_value(self->stats, f);
    }
    else if (self->data_type == GREYSCALE_BYTE) {
        *r = *g = *b = (float)(*(get_pixel(self, line, samp)));
    }
    else if (self->data_type == RGB_BYTE) {
        unsigned char *uc = get_pixel(self, line, samp);

        *r = (float)(uc[0]);
        *g = (float)(uc[1]);
        *b = (float)(uc[2]);
    }
    else if (self->data_type == RGB_FLOAT) {
        float *f = (float*)get_pixel(self, line, samp);

        *r = f[0];
        *g = f[1];
        *b = f[2];
    }
    else {
        // impossible!
        assert(0);
        *r = *g = *b = 0.;
    }
}

void cached_image_free (CachedImage *self)
{
    int i;
    for (i=0; i<self->n_tiles; ++i) {
        if (self->cache[i])
            free(self->cache[i]);
    }

    if (self->client->free_fn)
      self->client->free_fn(self->client->read_client_info);

    free(self->rowstarts);
    free(self->access_counts);
    free(self->cache);
    free(self->client);

    // we do not own the metadata -- don't free it!

    free(self);
}


/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef base_type_2aa01495_7feb_4c5e_ae38_9c775c29c9f1_h
#define base_type_2aa01495_7feb_4c5e_ae38_9c775c29c9f1_h

#include <assert.h>
#include <stdint.h>
#include <gslib/config.h>

__gslib_begin__

#ifdef _GS_X86

typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef wchar_t wchar;
typedef float real32;
typedef double real;
typedef uint8_t byte;
typedef uint16_t word;
typedef uint32_t dword;
typedef uint64_t qword;
typedef uint32_t uint;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef void* addrptr;

#ifdef _UNICODE
typedef wchar gchar;
#else
typedef char gchar;
#endif

template<class t>
struct typeof { typedef void type; };

/* virtual class ptr convert tools */
template<class ccp, class bcp>
inline int virtual_bias()
{
    static typeof<ccp>::type c;
    static const int bias = (int)&c - (int)static_cast<bcp>(&c);
    return bias;
}

template<class ccp, class bcp>
inline ccp virtual_cast(bcp p)
{
    byte* ptr = (byte*)p;
    ptr += virtual_bias<ccp, bcp>();
    return reinterpret_cast<ccp>(ptr);
}

#endif  /* end of _GS_X86 */

template<class _ty>
inline _ty gs_min(_ty a, _ty b) { return a < b ? a : b; }
template<class _ty>
inline _ty gs_max(_ty a, _ty b) { return a > b ? a : b; }
template<class _ty>
inline _ty gs_clamp(_ty v, _ty a, _ty b)
{
    assert(a <= b);
    return gs_min(b, gs_max(a, v));
}

template<class _ty>
inline void gs_swap(_ty& a, _ty& b)
{
    auto t = a;
    a = b;
    b = t;
}

__gslib_end__

#endif


/* Starting from version 7.8, MATLAB BLAS expects ptrdiff_t arguments for integers */
#if MATLAB_VERSION >= 0x0708
#include <stddef.h>
#include <stdlib.h>
#endif

/* Starting from version 7.6, MATLAB BLAS is seperated */
#if MATLAB_VERSION >= 0x0705
#include <blas.h>
#endif
#include <lapack.h>

#ifndef min
#define min(a,b) ((a) <= (b) ? (a) : (b))
#endif


/*
** read data matrix, map of voxel addresses
**
** G.Lohmann, Jan 2013
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

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

#include "viaio/Vlib.h"
#include "viaio/VImage.h"
#include "viaio/mu.h"

#define SQR(x) ((x) * (x))
#define ABS(x) ((x) > 0 ? (x) : -(x))

VImage *VImagePointer(VAttrList list,int *nt)
{
  VImage tmp;
  VAttrListPosn posn;
  int i,ntimesteps,nrows,ncols,nslices;

  /* get image dimensions, read functional data */
  nslices = 0;
  for (VFirstAttr (list, & posn); VAttrExists (& posn); VNextAttr (& posn)) {
    if (VGetAttrRepn (& posn) != VImageRepn) continue;
    VGetAttrValue (& posn, NULL,VImageRepn, & tmp);
    if (VPixelRepn(tmp) != VShortRepn) continue;
    nslices++;
  }
  /* VDestroyImage(tmp); */
  if (nslices < 1) VError(" no slices");

  VImage *src = (VImage *) VCalloc(nslices,sizeof(VImage));
  i = ntimesteps = nrows = ncols = 0;
  for (VFirstAttr (list, & posn); VAttrExists (& posn); VNextAttr (& posn)) {
    if (VGetAttrRepn (& posn) != VImageRepn) continue;
    VGetAttrValue (& posn, NULL,VImageRepn, & src[i]);
    if (VPixelRepn(src[i]) != VShortRepn) continue;
    if (VImageNBands(src[i]) > ntimesteps) ntimesteps = VImageNBands(src[i]);
    if (VImageNRows(src[i])  > nrows)  nrows = VImageNRows(src[i]);
    if (VImageNColumns(src[i]) > ncols) ncols = VImageNColumns(src[i]);
    i++;
  }
  nslices = i;
  if (nslices < 1) return NULL;
  if (ntimesteps < 2) return NULL;
  *nt = ntimesteps;
  return src;
}

/* read functional data */
void VReadImagePointer(VAttrList list,VImage *src)
{
  VAttrListPosn posn;

  int i = 0;
  for (VFirstAttr (list, & posn); VAttrExists (& posn); VNextAttr (& posn)) {
    if (VGetAttrRepn (& posn) != VImageRepn) continue;
    VGetAttrValue (& posn, NULL,VImageRepn, & src[i]);
    if (VPixelRepn(src[i]) != VShortRepn) continue;
    i++;
  }
}

/* check if mask covers data. If not, set surplus voxels to zero */
long VMaskCoverage(VAttrList list,VImage mask)
{
  VImage src=NULL;
  VAttrListPosn posn;
  int b,r,c;
  long count=0;

  b = 0;
  for (VFirstAttr (list, & posn); VAttrExists (& posn); VNextAttr (& posn)) {
    if (VGetAttrRepn (& posn) != VImageRepn) continue;
    VGetAttrValue (& posn, NULL,VImageRepn, & src);
    if (VPixelRepn(src) != VShortRepn) continue;
    if (VImageNRows(src) < 3) continue;

    if (VImageNRows(src) != VImageNRows(mask))
      VError(" inconsistent nrows: %d,  mask: %d",VImageNRows(src),VImageNRows(mask));
    if (VImageNColumns(src) != VImageNColumns(mask))
      VError(" inconsistent ncols: %d,  mask: %d",VImageNColumns(src),VImageNColumns(mask));

    for (r=0; r<VImageNRows(src); r++) {
      for (c=0; c<VImageNColumns(src); c++) {
	if (b >= VImageNBands(mask)) VError("VMaskCoverage, illegal addr, band= %d",b);
	float u = VGetPixel(mask,b,r,c);
	int j = (int)VPixel(src,0,r,c,VShort);
	if (j == 0 && u > 0.3) {
	  VSetPixel(mask,b,r,c,0);
	  count++;
	}
      }
    }
    b++;
  }
  return count;
}

/* check if data matrix contains zero voxels */
void VCheckMatrix(gsl_matrix_float *X)
{
  long i,j,nvox=X->size1,nt=X->size2;
  double sum1,sum2,nx,mean,var,tiny=1.0e-4;

  long count = 0;
  nx = (double)nt;
  for (i=0; i<nvox; i++) {
    const float *arr1 = gsl_matrix_float_const_ptr(X,i,0);
    sum1 = sum2 = 0;
    for (j=0; j<nt; j++) {
      const double u = (double)arr1[j];
      sum1 += u;
      sum2 += u*u;
    }
    mean = sum1/nx;
    var = (sum2 - nx * mean * mean) / (nx - 1.0);
    if (var < tiny) count++;
  }
  if (count > 0)
    VWarning(" number of empty voxels: %ld",count);
}

VImage VoxelMap(VImage mask,size_t *nvoxels)
{
  int b,r,c;
  int nslices = VImageNBands(mask);
  int nrows = VImageNRows(mask);
  int ncols = VImageNColumns(mask);

  /* count number of non-zero voxels */
  size_t nvox = 0;
  for (b=0; b<nslices; b++) {
    for (r=0; r<nrows; r++) {
      for (c=0; c<ncols; c++) {
	float u = VGetPixel(mask,b,r,c);
	if (ABS(u) < 0.5) continue;
	nvox++;
      }
    }
  }
  (*nvoxels) = nvox;

  /* voxel addresses */
  VImage map = VCreateImage(1,4,nvox,VShortRepn);
  if (map == NULL) VError(" error allocating addr map");
  VFillImage(map,VAllBands,0);
  VImage xmap=NULL;
  VExtractAttr (VImageAttrList(mask),"map",NULL,VImageRepn,&xmap,FALSE);
  VCopyImageAttrs (mask,map);
  VSetAttr(VImageAttrList(map),"nvoxels",NULL,VLongRepn,(VLong)nvox);
  VSetAttr(VImageAttrList(map),"nslices",NULL,VLongRepn,(VLong)nslices);
  VSetAttr(VImageAttrList(map),"nrows",NULL,VLongRepn,(VLong)nrows);
  VSetAttr(VImageAttrList(map),"ncols",NULL,VLongRepn,(VLong)ncols);

  VPixel(map,0,3,0,VShort) = nslices;
  VPixel(map,0,3,1,VShort) = nrows;
  VPixel(map,0,3,2,VShort) = ncols;

  size_t i = 0;
  for (b=0; b<nslices; b++) {
    for (r=0; r<nrows; r++) {
      for (c=0; c<ncols; c++) {
	float u = VGetPixel(mask,b,r,c);
	if (ABS(u) < 0.5) continue;
	VPixel(map,0,0,i,VShort) = b;
	VPixel(map,0,1,i,VShort) = r;
	VPixel(map,0,2,i,VShort) = c;
	i++;
      }
    }
  }
  return map;
}

/* two-pass formula, correct for round-off error */
void VNormalize(float *data,int nt,VBoolean stddev)
{
  int j;
  float ave,var,sd,nx,s,u,tiny=1.0e-6;

  nx = (float)nt;
  ave = 0;
  for (j=0; j<nt; j++) ave += data[j];
  ave /= nx;

  var = u = 0;
  for (j=0; j<nt; j++) {
    s = data[j]-ave;
    u   += s;
    var += s*s;
  }
  var=(var-u*u/nx)/(nx-1);
  sd = sqrt(var);
  if (sd < tiny) {
    for (j=0; j<nt; j++) data[j] = 0;
  }
  else {
    if (stddev==FALSE) sd = 1.0;  /* only subtract mean !!!! */
    for (j=0; j<nt; j++) {
      u = data[j];
      data[j] = (u-ave)/sd;
    }
  }
}

/*  copy data to input matrix X, contains fMRI time series */
void VDataMatrix(VImage *src,int first,int len,VImage map,gsl_matrix_float *X)
{
  long i,j,k,nvox;
  int b,r,c;
  float *data=NULL;

  data = (float *) VCalloc(len,sizeof(float));

  gsl_matrix_float_set_zero (X);
  nvox = VImageNColumns(map);
  if (nvox != (long)X->size1) VError(" err, %ld %ld",(long)X->size1,nvox);
  if (len  != (long)X->size2) VError(" err, %ld %ld",(long)X->size2,len);

  for (i=0; i<nvox; i++) {

    b = VPixel(map,0,0,i,VShort);
    r = VPixel(map,0,1,i,VShort);
    c = VPixel(map,0,2,i,VShort);

    if ((first+len) > VImageNBands(src[b])) VError(" illegal len addr, %d  %d %d %d",
						   b,first,len,VImageNBands(src[b]));
    if (r >= VImageNRows(src[b])) VError(" illegal row addr");
    if (c >= VImageNColumns(src[b])) VError(" illegal column addr");

    k = 0;
    for (j=first; j<first+len; j++) {
      data[k] = (float) VGetPixel(src[b],j,r,c);
      k++;
    }
    VNormalize(data,len,TRUE);

    float *ptr = gsl_matrix_float_ptr(X,i,0);
    for (j=0; j<len; j++) *ptr++ = data[j];
  }
  VFree(data);
}


#ifndef OPENMC_TALLIES_FILTER_TIME_H
#define OPENMC_TALLIES_FILTER_TIME_H

#include <gsl/gsl-lite.hpp>

#include "openmc/tallies/filter.h"
#include "openmc/vector.h"

namespace openmc {

//==============================================================================
//! Bins the incident particle time.
//==============================================================================

class TimeFilter : public Filter {
public:
  //----------------------------------------------------------------------------
  // Constructors, destructors

  ~TimeFilter() = default;

  //----------------------------------------------------------------------------
  // Methods

  std::string type() const override { return "time"; }

  void from_xml(pugi::xml_node node) override;

  void get_all_bins(const Particle& p, TallyEstimator estimator,
    FilterMatch& match) const override;

  void to_statepoint(hid_t filter_group) const override;

  std::string text_label(int bin) const override;

  //----------------------------------------------------------------------------
  // Accessors

  const vector<double>& bins() const { return bins_; }
  void set_bins(gsl::span<const double> bins);

protected:
  //----------------------------------------------------------------------------
  // Data members

  vector<double> bins_;
};

} // namespace openmc
#endif // OPENMC_TALLIES_FILTER_ENERGY_H


/* gsl_histogram_copy.c
 * Copyright (C) 2000  Simone Piccardi
 *
 * This library 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 library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
/***************************************************************
 *
 * File gsl_histogram_copy.c:
 * Routine to copy an histogram.
 * Need GSL library and headers.
 *
 * Author: S. Piccardi
 * Jan. 2000
 *
 ***************************************************************/
#include <config.h>
#include <stdlib.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_histogram.h>

/*
 * gsl_histogram_copy:
 * copy the contents of an histogram into another
 */

int
gsl_histogram_memcpy (gsl_histogram * dest, const gsl_histogram * src)
{
  size_t n = src->n;
  size_t i;

  if (dest->n != src->n)
    {
      GSL_ERROR ("histograms have different sizes, cannot copy",
                 GSL_EINVAL);
    }

  for (i = 0; i <= n; i++)
    {
      dest->range[i] = src->range[i];
    }

  for (i = 0; i < n; i++)
    {
      dest->bin[i] = src->bin[i];
    }

  return GSL_SUCCESS;
}

/*
 * gsl_histogram_duplicate:
 * duplicate an histogram creating
 * an identical new one
 */

gsl_histogram *
gsl_histogram_clone (const gsl_histogram * src)
{
  size_t n = src->n;
  size_t i;
  gsl_histogram *h;

  h = gsl_histogram_calloc_range (n, src->range);

  if (h == 0)
    {
      GSL_ERROR_VAL ("failed to allocate space for histogram struct",
                        GSL_ENOMEM, 0);
    }

  for (i = 0; i < n; i++)
    {
      h->bin[i] = src->bin[i];
    }

  return h;
}


/* vector/gsl_vector_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_VECTOR_FLOAT_H__
#define __GSL_VECTOR_FLOAT_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_block_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 size;
  size_t stride;
  float *data;
  gsl_block_float *block;
  int owner;
}
gsl_vector_float;

typedef struct
{
  gsl_vector_float vector;
} _gsl_vector_float_view;

typedef _gsl_vector_float_view gsl_vector_float_view;

typedef struct
{
  gsl_vector_float vector;
} _gsl_vector_float_const_view;

typedef const _gsl_vector_float_const_view gsl_vector_float_const_view;

/* Allocation */

gsl_vector_float *gsl_vector_float_alloc (const size_t n);
gsl_vector_float *gsl_vector_float_calloc (const size_t n);

gsl_vector_float *gsl_vector_float_alloc_from_block (gsl_block_float * b,
                                                     const size_t offset,
                                                     const size_t n,
                                                     const size_t stride);

gsl_vector_float *gsl_vector_float_alloc_from_vector (gsl_vector_float * v,
                                                      const size_t offset,
                                                      const size_t n,
                                                      const size_t stride);

void gsl_vector_float_free (gsl_vector_float * v);

/* Views */

_gsl_vector_float_view
gsl_vector_float_view_array (float *v, size_t n);

_gsl_vector_float_view
gsl_vector_float_view_array_with_stride (float *base,
                                         size_t stride,
                                         size_t n);

_gsl_vector_float_const_view
gsl_vector_float_const_view_array (const float *v, size_t n);

_gsl_vector_float_const_view
gsl_vector_float_const_view_array_with_stride (const float *base,
                                               size_t stride,
                                               size_t n);

_gsl_vector_float_view
gsl_vector_float_subvector (gsl_vector_float *v,
                            size_t i,
                            size_t n);

_gsl_vector_float_view
gsl_vector_float_subvector_with_stride (gsl_vector_float *v,
                                        size_t i,
                                        size_t stride,
                                        size_t n);

_gsl_vector_float_const_view
gsl_vector_float_const_subvector (const gsl_vector_float *v,
                                  size_t i,
                                  size_t n);

_gsl_vector_float_const_view
gsl_vector_float_const_subvector_with_stride (const gsl_vector_float *v,
                                              size_t i,
                                              size_t stride,
                                              size_t n);

/* Operations */

float gsl_vector_float_get (const gsl_vector_float * v, const size_t i);
void gsl_vector_float_set (gsl_vector_float * v, const size_t i, float x);

float *gsl_vector_float_ptr (gsl_vector_float * v, const size_t i);
const float *gsl_vector_float_const_ptr (const gsl_vector_float * v, const size_t i);

void gsl_vector_float_set_zero (gsl_vector_float * v);
void gsl_vector_float_set_all (gsl_vector_float * v, float x);
int gsl_vector_float_set_basis (gsl_vector_float * v, size_t i);

int gsl_vector_float_fread (FILE * stream, gsl_vector_float * v);
int gsl_vector_float_fwrite (FILE * stream, const gsl_vector_float * v);
int gsl_vector_float_fscanf (FILE * stream, gsl_vector_float * v);
int gsl_vector_float_fprintf (FILE * stream, const gsl_vector_float * v,
                              const char *format);

int gsl_vector_float_memcpy (gsl_vector_float * dest, const gsl_vector_float * src);

int gsl_vector_float_reverse (gsl_vector_float * v);

int gsl_vector_float_swap (gsl_vector_float * v, gsl_vector_float * w);
int gsl_vector_float_swap_elements (gsl_vector_float * v, const size_t i, const size_t j);

float gsl_vector_float_max (const gsl_vector_float * v);
float gsl_vector_float_min (const gsl_vector_float * v);
void gsl_vector_float_minmax (const gsl_vector_float * v, float * min_out, float * max_out);

size_t gsl_vector_float_max_index (const gsl_vector_float * v);
size_t gsl_vector_float_min_index (const gsl_vector_float * v);
void gsl_vector_float_minmax_index (const gsl_vector_float * v, size_t * imin, size_t * imax);

int gsl_vector_float_add (gsl_vector_float * a, const gsl_vector_float * b);
int gsl_vector_float_sub (gsl_vector_float * a, const gsl_vector_float * b);
int gsl_vector_float_mul (gsl_vector_float * a, const gsl_vector_float * b);
int gsl_vector_float_div (gsl_vector_float * a, const gsl_vector_float * b);
int gsl_vector_float_scale (gsl_vector_float * a, const double x);
int gsl_vector_float_add_constant (gsl_vector_float * a, const double x);

int gsl_vector_float_isnull (const gsl_vector_float * v);
int gsl_vector_float_ispos (const gsl_vector_float * v);
int gsl_vector_float_isneg (const gsl_vector_float * v);

#ifdef HAVE_INLINE

extern inline
float
gsl_vector_float_get (const gsl_vector_float * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return v->data[i * v->stride];
}

extern inline
void
gsl_vector_float_set (gsl_vector_float * v, const size_t i, float x)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  v->data[i * v->stride] = x;
}

extern inline
float *
gsl_vector_float_ptr (gsl_vector_float * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (float *) (v->data + i * v->stride);
}

extern inline
const float *
gsl_vector_float_const_ptr (const gsl_vector_float * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (const float *) (v->data + i * v->stride);
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_FLOAT_H__ */


#pragma once

#include "Rendering/Utility/peBxDF.h"
#include "Type/peColor.h"
#include <gsl.h>

namespace pe {

enum class ToneMapping { Saturate, Linear, Log };

//! \brief Performs tone mapping from the given input image to a displayable
//! 8-bit RGBA output image \param result Result image \param input Input image
//! \param imageWidth Width of the image
//! \param imageHeight Height of the image
//! \param strategy The tone-mapping strategy to use
void ToneMap(gsl::span<RGBA_8Bit> result, const gsl::span<Spectrum_t> input,
             const uint32_t imageWidth, const uint32_t imageHeight,
             const ToneMapping strategy);

} // namespace pe


/* sys/gsl_sys.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_SYS_H__
#define __GSL_SYS_H__

#include <gsl/gsl_types.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

GSL_EXPORT double gsl_log1p (const double x);
GSL_EXPORT double gsl_expm1 (const double x);
GSL_EXPORT double gsl_hypot (const double x, const double y);
GSL_EXPORT double gsl_acosh (const double x);
GSL_EXPORT double gsl_asinh (const double x);
GSL_EXPORT double gsl_atanh (const double x);

GSL_EXPORT int gsl_isnan (const double x);
GSL_EXPORT int gsl_isinf (const double x);
GSL_EXPORT int gsl_finite (const double x);

GSL_EXPORT double gsl_nan (void);
GSL_EXPORT double gsl_posinf (void);
GSL_EXPORT double gsl_neginf (void);
GSL_EXPORT double gsl_fdiv (const double x, const double y);

GSL_EXPORT double gsl_coerce_double (const double x);
GSL_EXPORT float gsl_coerce_float (const float x);
GSL_EXPORT long double gsl_coerce_long_double (const long double x);

GSL_EXPORT double gsl_ldexp(const double x, const int e);
GSL_EXPORT double gsl_frexp(const double x, int * e);

GSL_EXPORT int gsl_fcmp (const double x1, const double x2, const double epsilon);

__END_DECLS

#endif /* __GSL_SYS_H__ */


#include "ccv.h"
#include <ctype.h>
#include <getopt.h>
#ifdef HAVE_GSL
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#endif

#ifdef HAVE_GSL
static ccv_dense_matrix_t* _ccv_aflw_slice_with_rect(gsl_rng* rng, ccv_dense_matrix_t* image, ccv_rect_t rect, ccv_size_t size, ccv_margin_t margin, float deform_angle, float deform_scale, float deform_shift)
{
	ccv_dense_matrix_t* resize = 0;
	ccv_slice(image, (ccv_matrix_t**)&resize, 0, rect.y, rect.x, rect.height, rect.width);
	assert(rect.width == rect.height);
	float scale = gsl_rng_uniform(rng);
	// to make the scale evenly distributed, for example, when deforming of 1/2 ~ 2, we want it to distribute around 1, rather than any average of 1/2 ~ 2
	scale = (1 + deform_scale * scale) / (1 + deform_scale * (1 - scale));
	int new_width = (int)(rect.width * scale + 0.5);
	int new_height = (int)(rect.height * scale + 0.5);
	ccv_point_t offset = ccv_point((int)((deform_shift * 2 * gsl_rng_uniform(rng) - deform_shift) * rect.width + 0.5 + (rect.width - new_width) * 0.5), (int)((deform_shift * 2 * gsl_rng_uniform(rng) - deform_shift) * rect.height + 0.5 + (rect.height - new_height) * 0.5));
	rect.x += offset.x;
	rect.y += offset.y;
	ccv_dense_matrix_t* b = 0;
	if (size.width > rect.width)
		ccv_resample(resize, &b, 0, size.height + margin.top + margin.bottom, size.width + margin.left + margin.right, CCV_INTER_CUBIC);
	else
		ccv_resample(resize, &b, 0, size.height + margin.top + margin.bottom, size.width + margin.left + margin.right, CCV_INTER_AREA);
	ccv_matrix_free(resize);
	return b;
}
#endif

int main(int argc, char** argv)
{
#ifdef HAVE_GSL
	assert(argc == 4);
	gsl_rng* rng = gsl_rng_alloc(gsl_rng_default);
	FILE* r = fopen(argv[1], "r");
	char* base_dir = argv[2];
	int dirlen = (base_dir != 0) ? strlen(base_dir) + 1 : 0;
	char* file = (char*)malloc(1024);
	int i = 0;
	ccv_rect_t rect;
	ccv_decimal_pose_t pose;
	// rect.x, rect.y, rect.width, rect.height roll pitch yaw
	while (fscanf(r, "%s %d %d %d %d %f %f %f", file, &rect.x, &rect.y, &rect.width, &rect.height, &pose.roll, &pose.pitch, &pose.yaw) != EOF)
	{
		if (pose.pitch < CCV_PI * 22.5 / 180 && pose.pitch > -CCV_PI * 22.5 / 180 &&
			pose.roll < CCV_PI * 22.5 / 180 && pose.roll > -CCV_PI * 22.5 / 180 &&
			pose.yaw < CCV_PI * 20 / 180 && pose.yaw > -CCV_PI * 20 / 180 &&
			rect.width >= 15 && rect.height >= 15)
		{
			// resize to a more proper sizes
			char* filename = (char*)malloc(1024);
			strncpy(filename, base_dir, 1024);
			filename[dirlen - 1] = '/';
			strncpy(filename + dirlen, file, 1024 - dirlen);
			ccv_dense_matrix_t* image = 0;
			ccv_read(filename, &image, CCV_IO_ANY_FILE | CCV_IO_GRAY);
			char* savefile = (char*)malloc(1024);
			ccv_dense_matrix_t* b = _ccv_aflw_slice_with_rect(rng, image, rect, ccv_size(48, 48), ccv_margin(0, 0, 0, 0), 10, 0.1, 0.05);
			snprintf(savefile, 1024, "%s/aflw-%07d-bw.png", argv[3], i);
			ccv_write(b, savefile, 0, CCV_IO_PNG_FILE, 0);
			ccv_matrix_free(b);
			ccv_matrix_free(image);
			image = 0;
			ccv_read(filename, &image, CCV_IO_ANY_FILE | CCV_IO_RGB_COLOR);
			b = _ccv_aflw_slice_with_rect(rng, image, rect, ccv_size(48, 48), ccv_margin(0, 0, 0, 0), 10, 0.1, 0.05);
			snprintf(savefile, 1024, "%s/aflw-%07d-rgb.png", argv[3], i);
			ccv_write(b, savefile, 0, CCV_IO_PNG_FILE, 0);
			ccv_matrix_free(b);
			ccv_matrix_free(image);
			i++;
			free(savefile);
			free(filename);
		}
	}
	fclose(r);
	free(file);
	gsl_rng_free(rng);
#else
	assert(0 && "aflw requires GSL library support");
#endif
	return 0;
}


/* monte/gsl_monte_miser.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Michael Booth
 *
 * 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.
 */

/* Author: MJB */

#ifndef __GSL_MONTE_MISER_H__
#define __GSL_MONTE_MISER_H__

#include <gsl/gsl_rng.h>
#include <gsl/gsl_monte.h>
#include <gsl/gsl_monte_plain.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 min_calls;
  size_t min_calls_per_bisection;
  double dither;
  double estimate_frac;
  double alpha;
  size_t dim;
  int estimate_style;
  int depth;
  int verbose;
  double * x;
  double * xmid;
  double * sigma_l;
  double * sigma_r;
  double * fmax_l;
  double * fmax_r;
  double * fmin_l;
  double * fmin_r;
  double * fsum_l;
  double * fsum_r;
  double * fsum2_l;
  double * fsum2_r;
  size_t * hits_l;
  size_t * hits_r;
} gsl_monte_miser_state;

int gsl_monte_miser_integrate(gsl_monte_function * f,
                              const double xl[], const double xh[],
                              size_t dim, size_t calls,
                              gsl_rng *r,
                              gsl_monte_miser_state* state,
                              double *result, double *abserr);

gsl_monte_miser_state* gsl_monte_miser_alloc(size_t dim);

int gsl_monte_miser_init(gsl_monte_miser_state* state);

void gsl_monte_miser_free(gsl_monte_miser_state* state);

__END_DECLS

#endif /* __GSL_MONTE_MISER_H__ */


/* specfunc/gsl_sf_result.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman
 *
 * 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.
 */

/* Author:  G. Jungman */

#ifndef __GSL_SF_RESULT_H__
#define __GSL_SF_RESULT_H__

#include <gsl/gsl_types.h>

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

__BEGIN_DECLS

struct gsl_sf_result_struct {
  double val;
  double err;
};
typedef struct gsl_sf_result_struct gsl_sf_result;

#define GSL_SF_RESULT_SET(r,v,e) do { (r)->val=(v); (r)->err=(e); } while(0)

struct gsl_sf_result_e10_struct {
  double val;
  double err;
  int    e10;
};
typedef struct gsl_sf_result_e10_struct gsl_sf_result_e10;

GSL_EXPORT int gsl_sf_result_smash_e(const gsl_sf_result_e10 * re, gsl_sf_result * r);

__END_DECLS

#endif /* __GSL_SF_RESULT_H__ */


#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

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

#include "my_signal.h"
#include "my_socket.h"
#include "set_timer.h"
#include "get_num.h"

int debug = 0;
gsl_histogram *histo;
unsigned long histo_overflow = 0;
unsigned long total_bytes    = 0;
unsigned long read_count     = 0;
struct timeval start, stop;
int bufsize = 2*1024*1024;

int usage()
{
    //char msg[] = "Usage: ./read-sock-histo [-b bufsize] [-w bin_width] [-B n_bin] [-t TIMEOUT] ip_address:port x_min x_max n_bin\n"
    char msg[] = "Usage: ./read-sock-histo [-b bufsize] [-t TIMEOUT] ip_address:port x_min x_max n_bin\n"
                 "example of ip_address:port\n"
                 "remote_host:1234\n"
                 "192.168.10.16:24\n"
                 "default port: 1234\n"
                 " Options:\n"
                 "    -b bufsize: suffix k for kilo (1024), m for mega(1024*1024) (default 2MB)\n"
                 "    -t TIMEOUT: seconds.  (default: 10 seconds)\n";
    fprintf(stderr, "%s\n", msg);

    return 0;
}

void sig_int(int signo)
{
    struct timeval elapse;
    gettimeofday(&stop, NULL);
    timersub(&stop, &start, &elapse);
    fprintf(stderr, "bufsize: %.3f kB\n", bufsize/1024.0);
    fprintf(stderr, "total bytes: %ld bytes\n", total_bytes);
    fprintf(stderr, "read count : %ld\n", read_count);
    fprintf(stderr, "running %ld.%06ld sec\n", elapse.tv_sec, elapse.tv_usec);
    double elapsed_time        = elapse.tv_sec + 0.000001*elapse.tv_usec;
    double transfer_rate_MB_s  = (double)total_bytes / elapsed_time / 1024.0 / 1024.0;
    double transfer_rate_Gbps  = (double)total_bytes * 8 / elapsed_time / 1000000000.0;
    double read_bytes_per_read = (double)total_bytes / (double)read_count / 1024.0;
    fprintf(stderr, "transfer_rate: %.3f MB/s %.3f Gbps\n", transfer_rate_MB_s, transfer_rate_Gbps);
    fprintf(stderr, "read_bytes_per_read: %.3f kB/read\n", read_bytes_per_read);

    gsl_histogram_fprintf(stdout, histo, "%g", "%g");
    gsl_histogram_free(histo);
    //if (histo_overflow > 0) {
    printf("# overflow: %ld\n", histo_overflow);
    //}

    exit(0);
}

int main(int argc, char *argv[])
{
    int c;
    int period = 10; /* default run time (10 seconds) */

    while ( (c = getopt(argc, argv, "b:dht:w:B:")) != -1) {
        switch (c) {
            case 'b':
                bufsize = get_num(optarg);
                break;
            case 'h':
                usage();
                exit(0);
                break; /* NOTREACHED */
            case 'd':
                debug = 1;
                break;
            case 't':
                period = strtol(optarg, NULL, 0);
                break;
            default:
                break;
        }
    }

    argc -= optind;
    argv += optind;

    if (argc != 4) {
        usage();
        exit(1);
    }

    int port = 1234;
    char *remote_host_info = argv[0];
    char *tmp = strdup(remote_host_info);
    char *remote_host = strsep(&tmp, ":");
    if (tmp != NULL) {
        port = strtol(tmp, NULL, 0);
    }

    double x_min = (double) get_num(argv[1]);
    double x_max = (double) get_num(argv[2]);
    int n_bin    = strtol(argv[3], NULL, 0);

    my_signal(SIGINT,  sig_int);
    my_signal(SIGTERM, sig_int);
    my_signal(SIGALRM, sig_int);

    histo = gsl_histogram_alloc(n_bin);
    gsl_histogram_set_ranges_uniform(histo, x_min, x_max);

    int sockfd = tcp_socket();
    if (sockfd < 0) {
        errx(1, "tcp_socket");
    }

    if (connect_tcp(sockfd, remote_host, port) < 0) {
        errx(1, "connect_tcp");
    }

    set_timer(period, 0, period, 0);
    gettimeofday(&start, NULL);

    char *buf = malloc(bufsize);
    if (buf == NULL) {
        err(1, "malloc for buf");
    }

    for ( ; ; ) {
        int n, m;
        n = read(sockfd, buf, bufsize);
        total_bytes += n;
        read_count ++;
        m = gsl_histogram_increment(histo, n);
        if (m == GSL_EDOM) {
            histo_overflow += 1;
        }
    }
}