/*
Copyright 2016, D. E. Shaw Research.
All rights reserved.

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 D. E. Shaw Research 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
OWNER 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.
*/

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#pragma GCC diagnostic ignored "-Wconversion"
#endif

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wexpansion-to-defined"
#pragma clang diagnostic ignored "-Wreserved-id-macro"
#endif

#ifdef _MSC_FULL_VER
// 4311 - 'type cast': pointer truncation from 'char[54]' to 'long'
// 4202 - nonstandard extension used: non-constant aggregate initializer
// 4127 - conditional expression is constant
// 4302 - 'type cast': truncation from 'const char *' to 'long'
#pragma warning(push)
#pragma warning(disable : 4311 4204 4127 4302)
#endif

#include "rng/config.h"

#include "Random123/conventional/gsl_cbrng.h"
#include "ut_gsl.h"
#include <assert.h>
#include <gsl/gsl_randist.h>
#include <stdio.h>

/* Exercise the GSL_CBRNG macro */

GSL_CBRNG(cbrng, threefry4x64); /* creates gsl_rng_cbrng */

int main(int argc, char **argv) {
  int i;
  gsl_rng *r;
  gsl_rng *rcopy;
  unsigned long x;
  rngRemember(unsigned long save);
  rngRemember(unsigned long saved[5]);
  double sum = 0.;
  (void)argc;
  (void)argv; /* unused */

  /* Silence an unused-parameter warning. */
  (void)argc;

  r = gsl_rng_alloc(gsl_rng_cbrng);
  assert(gsl_rng_min(r) == 0);
  assert(gsl_rng_max(r) == 0xffffffffUL); /* Not necessarily ~0UL */
  assert(gsl_rng_size(r) > 0);

  printf("%s\nulongs from %s in initial state\n", argv[0], gsl_rng_name(r));
  for (i = 0; i < 5; i++) {
    x = gsl_rng_get(r);
    rngRemember(saved[i] = x);
    printf("%d: 0x%lx\n", i, x);
    assert(x != 0);
  }
  printf("uniforms from %s\n", gsl_rng_name(r));
  for (i = 0; i < 5; i++) {
    double z = gsl_rng_uniform(r);
    sum += z;
    printf("%d: %.4g\n", i, z);
  }
  assert(sum < 0.9 * 5 && sum > 0.1 * 5 &&
         (long)"sum must be reasonably close  to 0.5*number of trials");
  rngRemember(save =) gsl_rng_get(r);

  gsl_rng_set(r, 0xdeadbeef); /* set a non-zero seed */
  printf("ulongs from %s after seed\n", gsl_rng_name(r));
  for (i = 0; i < 5; i++) {
    x = gsl_rng_get(r);
    printf("%d: 0x%lx\n", i, x);
    assert(x != 0);
  }
  /* make a copy of the total state */
  rcopy = gsl_rng_alloc(gsl_rng_cbrng);
  gsl_rng_memcpy(rcopy, r);
  printf("uniforms from %s\n", gsl_rng_name(r));
  sum = 0.;
  for (i = 0; i < 5; i++) {
    double x2 = gsl_rng_uniform(r);
    rngRemember(double y = gsl_rng_uniform(rcopy));
    printf("%d: %.4g\n", i, x2);
    sum += x2;
    assert(x2 == y);
  }
  assert(gsl_rng_get(r) != save);
  assert(sum < 0.9 * 5 && sum > 0.1 * 5 &&
         (long)"sum must be reasonably close  to 0.5*number of trials");

  /* gsl_rng_set(*, 0) is supposed to recover the default seed */
  gsl_rng_set(r, 0);
  printf("ulongs from %s after restore to initial\n", gsl_rng_name(r));
  for (i = 0; i < 5; i++) {
    x = gsl_rng_get(r);
    assert(x == saved[i]);
    printf("%d: 0x%lx\n", i, x);
    assert(x != 0);
  }
  printf("uniforms from %s\n", gsl_rng_name(r));
  for (i = 0; i < 5; i++) {
    printf("%d: %.4g\n", i, gsl_rng_uniform(r));
  }

  gsl_rng_free(rcopy);
  gsl_rng_free(r);

  printf("ut_gsl: OK\n");
  return 0;
}

#ifdef _MSC_FULL_VER
#pragma warning(pop)
#endif

#ifdef __clang__
// Restore clang diagnostics to previous state.
#pragma clang diagnostic pop
#endif

#ifdef __GNUC__
// Restore GCC diagnostics to previous state.
#pragma GCC diagnostic pop
#endif


#ifndef matops_h
#define matops_h

#include <ceed.h>
#include <petsc.h>
#include <petscdmplex.h>

#include "structs.h"

PetscErrorCode MatGetDiag(Mat A, Vec D);
PetscErrorCode ApplyLocal_Ceed(Vec X, Vec Y, UserO user);
PetscErrorCode MatMult_Ceed(Mat A, Vec X, Vec Y);
PetscErrorCode FormResidual_Ceed(SNES snes, Vec X, Vec Y, void *ctx);
PetscErrorCode MatMult_Prolong(Mat A, Vec X, Vec Y);
PetscErrorCode MatMult_Restrict(Mat A, Vec X, Vec Y);
PetscErrorCode ComputeErrorMax(UserO user, CeedOperator op_error,
                               Vec X, CeedVector target, PetscReal *max_error);

#endif // matops_h


/* histogram/gsl_histogram.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_HISTOGRAM_H__
#define __GSL_HISTOGRAM_H__

#include <stdlib.h>
#include <stdio.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

typedef struct {
  size_t n ;
  double * range ;
  double * bin ;
} gsl_histogram ;

typedef struct {
  size_t n ;
  double * range ;
  double * sum ;
} gsl_histogram_pdf ;

GSL_EXPORT gsl_histogram * gsl_histogram_alloc (size_t n);

GSL_EXPORT gsl_histogram * gsl_histogram_calloc (size_t n);
GSL_EXPORT gsl_histogram * gsl_histogram_calloc_uniform (const size_t n, const double xmin, const double xmax);
GSL_EXPORT void gsl_histogram_free (gsl_histogram * h);
GSL_EXPORT int gsl_histogram_increment (gsl_histogram * h, double x);
GSL_EXPORT int gsl_histogram_accumulate (gsl_histogram * h, double x, double weight);
GSL_EXPORT int gsl_histogram_find (const gsl_histogram * h,
                                   const double x, size_t * i);

GSL_EXPORT double gsl_histogram_get (const gsl_histogram * h, size_t i);
GSL_EXPORT int gsl_histogram_get_range (const gsl_histogram * h, size_t i,
                                        double * lower, double * upper);

GSL_EXPORT double gsl_histogram_max (const gsl_histogram * h);
GSL_EXPORT double gsl_histogram_min (const gsl_histogram * h);
GSL_EXPORT size_t gsl_histogram_bins (const gsl_histogram * h);

GSL_EXPORT void gsl_histogram_reset (gsl_histogram * h);

GSL_EXPORT gsl_histogram * gsl_histogram_calloc_range(size_t n, double * range);

GSL_EXPORT
int
gsl_histogram_set_ranges (gsl_histogram * h, const double range[], size_t size);
GSL_EXPORT
int
gsl_histogram_set_ranges_uniform (gsl_histogram * h, double xmin, double xmax);

GSL_EXPORT
int
gsl_histogram_memcpy(gsl_histogram * dest, const gsl_histogram * source);

GSL_EXPORT
gsl_histogram *
gsl_histogram_clone(const gsl_histogram * source);

GSL_EXPORT double gsl_histogram_max_val (const gsl_histogram * h);

GSL_EXPORT size_t gsl_histogram_max_bin (const gsl_histogram * h);

GSL_EXPORT double gsl_histogram_min_val (const gsl_histogram * h);

GSL_EXPORT size_t gsl_histogram_min_bin (const gsl_histogram * h);

GSL_EXPORT
int
gsl_histogram_equal_bins_p(const gsl_histogram *h1, const gsl_histogram *h2);

GSL_EXPORT
int
gsl_histogram_add(gsl_histogram *h1, const gsl_histogram *h2);

GSL_EXPORT
int
gsl_histogram_sub(gsl_histogram *h1, const gsl_histogram *h2);

GSL_EXPORT
int
gsl_histogram_mul(gsl_histogram *h1, const gsl_histogram *h2);

GSL_EXPORT
int
gsl_histogram_div(gsl_histogram *h1, const gsl_histogram *h2);

GSL_EXPORT
int
gsl_histogram_scale(gsl_histogram *h, double scale);

GSL_EXPORT
int
gsl_histogram_shift (gsl_histogram * h, double shift);

GSL_EXPORT double gsl_histogram_sigma (const gsl_histogram * h);

GSL_EXPORT double gsl_histogram_mean (const gsl_histogram * h);

GSL_EXPORT double gsl_histogram_sum (const gsl_histogram * h);

GSL_EXPORT int gsl_histogram_fwrite (FILE * stream, const gsl_histogram * h) ;
GSL_EXPORT int gsl_histogram_fread (FILE * stream, gsl_histogram * h);
GSL_EXPORT int gsl_histogram_fprintf (FILE * stream, const gsl_histogram * h,
                                      const char * range_format, const char * bin_format);
GSL_EXPORT int gsl_histogram_fscanf (FILE * stream, gsl_histogram * h);

GSL_EXPORT gsl_histogram_pdf * gsl_histogram_pdf_alloc (const size_t n);
GSL_EXPORT int gsl_histogram_pdf_init (gsl_histogram_pdf * p, const gsl_histogram * h);
GSL_EXPORT void gsl_histogram_pdf_free (gsl_histogram_pdf * p);
GSL_EXPORT double gsl_histogram_pdf_sample (const gsl_histogram_pdf * p, double r);

__END_DECLS

#endif /* __GSL_HISTOGRAM_H__ */


/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
**                                                                                **
** This file forms part of the Underworld geophysics modelling application.         **
**                                                                                **
** For full license and copyright information, please refer to the LICENSE.md file  **
** located at the project root, or contact the authors.                             **
**                                                                                **
**~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*/
#include <petsc.h>
#include <petscvec.h>
#include <petscmat.h>

#include <StGermain/libStGermain/src/StGermain.h>
#include <StgDomain/libStgDomain/src/StgDomain.h>

#include "common-driver-utils.h"

/*
name[] is operator name
*/
PetscErrorCode BSSCR_MatInfoLog(PetscViewer v,Mat A,const char name[])
{
	MatInfo i;
	PetscReal nrm_1,nrm_f,nrm_inf;
	MatType mtype;
	PetscInt M,N;
	PetscViewerType vtype;
	PetscTruth isascii;

	PetscViewerGetType( v, &vtype );
	Stg_PetscObjectTypeCompare( (PetscObject)v,PETSC_VIEWER_ASCII,&isascii );
	if (!isascii) { PetscFunctionReturn(0); }

	MatGetSize( A, &M,&N );
	MatGetInfo(A,MAT_GLOBAL_SUM,&i);

	MatGetType( A, &mtype );
	MatNorm( A, NORM_1, &nrm_1 );
	MatNorm( A, NORM_FROBENIUS, &nrm_f );
	MatNorm( A, NORM_INFINITY, &nrm_inf );

	PetscViewerASCIIPrintf( v, "MatInfo: %s \n", name );
	PetscViewerASCIIPushTab(v);

	PetscViewerASCIIPrintf( v, "type=%s \n", mtype );
	PetscViewerASCIIPrintf( v, "dimension=%Dx%D \n", M,N );
	PetscViewerASCIIPrintf( v, "nnz=%D (total)\n", (PetscInt)i.nz_used );
	PetscViewerASCIIPrintf( v, "nnz=%D (allocated)\n", (PetscInt)i.nz_allocated );
	PetscViewerASCIIPrintf( v, "|A|_1         = %1.12e\n", nrm_1 );
	PetscViewerASCIIPrintf( v, "|A|_frobenius = %1.12e\n", nrm_f );
	PetscViewerASCIIPrintf( v, "|A|_inf       = %1.12e\n", nrm_inf );

	PetscViewerASCIIPopTab(v);

	PetscFunctionReturn(0);
}

/*
name[] is operator name
*/
PetscErrorCode BSSCR_VecInfoLog(PetscViewer v,Vec x,const char name[])
{
	PetscReal nrm_1,nrm_f,nrm_inf;
	VecType vectype;
	PetscInt M;
	PetscViewerType vtype;
	PetscTruth isascii;

	PetscViewerGetType( v, &vtype );
	Stg_PetscObjectTypeCompare( (PetscObject)v,PETSC_VIEWER_ASCII,&isascii );
	if (!isascii) { PetscFunctionReturn(0); }

	VecGetSize( x, &M);

	VecGetType( x, &vectype );
	VecNorm( x, NORM_1, &nrm_1 );
	VecNorm( x, NORM_2, &nrm_f );
	VecNorm( x, NORM_INFINITY, &nrm_inf );

	PetscViewerASCIIPrintf( v, "VecInfo: %s \n", name );
	PetscViewerASCIIPushTab(v);

	PetscViewerASCIIPrintf( v, "type=%s \n", vectype );
	PetscViewerASCIIPrintf( v, "dimension=%Dx1 \n", M);
	PetscViewerASCIIPrintf( v, "|x|_1   = %1.12e\n", nrm_1 );
	PetscViewerASCIIPrintf( v, "|x|_2   = %1.12e\n", nrm_f );
	PetscViewerASCIIPrintf( v, "|x|_inf = %1.12e\n", nrm_inf );

	PetscViewerASCIIPopTab(v);

	PetscFunctionReturn(0);
}

PetscErrorCode BSSCR_StokesCreateOperatorSummary( Mat K, Mat G, Mat C, Vec f, Vec h, const char filename[] )
{
	MPI_Comm comm;
	PetscViewer v;
	char op_name[PETSC_MAX_PATH_LEN];
	PetscTruth flg;

	PetscObjectGetComm( (PetscObject)K, &comm );
	PetscViewerASCIIOpen( comm, filename, &v );

	BSSCR_GeneratePetscHeader_for_viewer( v );
	PetscViewerASCIIPrintf( v, "\nInput matrices:\n");
	PetscViewerASCIIPushTab(v);

	PetscOptionsGetString( PETSC_NULL,"-stokes_A11",op_name,PETSC_MAX_PATH_LEN-1,&flg );
	if (flg) {	PetscViewerASCIIPrintf( v, "A11: %s\n", op_name );	}

	PetscOptionsGetString( PETSC_NULL,"-stokes_A12",op_name,PETSC_MAX_PATH_LEN-1,&flg );
	if (flg) {	PetscViewerASCIIPrintf( v, "A12: %s\n", op_name );	}

	PetscOptionsGetString( PETSC_NULL,"-stokes_A22",op_name,PETSC_MAX_PATH_LEN-1,&flg );
	if (flg) {	PetscViewerASCIIPrintf( v, "A22: %s\n", op_name );	}

	PetscOptionsGetString( PETSC_NULL,"-stokes_Smat",op_name,PETSC_MAX_PATH_LEN-1,&flg );
	if (flg) {	PetscViewerASCIIPrintf( v, "Smat: %s\n", op_name );	}

	PetscOptionsGetString( PETSC_NULL,"-stokes_b1",op_name,PETSC_MAX_PATH_LEN-1,&flg );
	if (flg) {	PetscViewerASCIIPrintf( v, "b1: %s\n", op_name );	}

	PetscOptionsGetString( PETSC_NULL,"-stokes_b2",op_name,PETSC_MAX_PATH_LEN-1,&flg );
	if (flg) {	PetscViewerASCIIPrintf( v, "b2: %s\n", op_name );	}

	PetscViewerASCIIPopTab(v);
	PetscViewerASCIIPrintf( v, "\n");

	BSSCR_MatInfoLog(v,K,"stokes_A11");			PetscViewerASCIIPrintf( v, "\n");

	BSSCR_MatInfoLog(v,G,"stokes_A12");			PetscViewerASCIIPrintf( v, "\n");
	if (C) { BSSCR_MatInfoLog(v,C,"stokes_A22"); 		PetscViewerASCIIPrintf( v, "\n");	}

	BSSCR_VecInfoLog(v,f,"stokes_b1");			PetscViewerASCIIPrintf( v, "\n");
	BSSCR_VecInfoLog(v,h,"stokes_b2");			PetscViewerASCIIPrintf( v, "\n");

	Stg_PetscViewerDestroy(&v );

	PetscFunctionReturn(0);
}


/*
 This file is part of the Princeton FCMA Toolbox
 Copyright (c) 2013 the authors (see AUTHORS file)
 For license terms, please see the LICENSE file.
*/

#ifndef COMMON_H
#define COMMON_H

#include <vector>
#include <map>
#include <list>
#include <algorithm>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <climits>
#include <iomanip>
#include <stdlib.h>
// define NDEBUG here to disable assertions
// if/when "release" builds are supported
// #define NDEBUG
#include <cassert>

// nifti1_io.h is from nifticlib http://nifti.nimh.nih.gov
// see README in fcma-toolbox.git/deps/
#include <nifti1_io.h>

// SSE/AVX instrinsics
#if defined(_MSC_VER)
// Microsoft C/C++-compatible compiler */
#include <intrin.h>
#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
// GCC-compatible compiler (gcc,clang,icc) targeting x86/x86-64
#include <x86intrin.h>
#endif

// OpenMP : gcc >= 4.7 or clang-omp http://clang-omp.github.io
#include <omp.h>

// Aligned memory blocks
#ifdef __INTEL_COMPILER
#include <offload.h>
#else
#include <mm_malloc.h>
#endif
#if defined(__GNUC__)
// gcc supports this syntax
#define ALIGNED(x) __attribute__((aligned(x)))
#else
// visual c++, clang, icc
#define ALIGNED(x) __declspec(align(x))
#endif

// BLAS dependency
#ifdef USE_MKL
#include <mkl.h>
#else
typedef int MKL_INT;
#if defined __APPLE__
#include <Accelerate/Accelerate.h>
#else
extern "C" {
#include <cblas.h>
}
#endif
#endif

// Matrix multiplication parameters
#define TINYNUM 1e-4
#define LOGISTICTRHEHOLD 1e-6
#define MAXFILENAMELENGTH 300
#define MAXSUBJS 100
#define MAXTRIALPERSUBJ 100
#define MAXSVMITERATION 20000000

// MPI communication tags
#define COMPUTATIONTAG 1
#define LENGTHTAG 2
#define VOXELCLASSIFIERTAG 3
#define ELAPSETAG 4
#define POSITIONTAG 5
#define SECONDORDERTAG 6

enum Task {
  Corr_Based_SVM = 0,
  Corr_Based_Dis,
  Acti_Based_SVM,
  Corr_Sum,
  Corr_Mask_Classification,
  Corr_Mask_Cross_Validation,
  Acti_Mask_Classification,
  Acti_Mask_Cross_Validation,
  Corr_Visualization,
  Marginal_Screening,
  Error_Type = -1
};

typedef unsigned long long uint64;
typedef unsigned short uint16;

typedef struct raw_matrix_t {
  std::string sname;  // subject name (file name without extension)
  int sid;            // subject id
  int row;
  int col;
  int nx, ny, nz;
  float* matrix;
} RawMatrix;

typedef struct trial_data_t {
  int nTrials;
  int nVoxels;
  int nCols;
  int* trialLengths;  // keep all trials data, subject by subject
  int* scs;
  float* data;  // normalized data for correlation
  trial_data_t(int x, int y) : nTrials(x), nVoxels(y) {}
} TrialData;

typedef struct corr_matrix_t {
  int sid;      // subject id
  int tlabel;   // trial label
  int sr;       // starting row id
  int step;     // row of this matrix
  int nVoxels;  // col of this matrix
  float* matrix;
} CorrMatrix;

// Trial: data structure for the start and end point of a trial
typedef struct trial_t {
  int tid;
  int sid;
  int label;
  int sc, ec;
  // tid_withinsubj: block id within each subject,
  // to be used in averaged matrix of searchlight
  int tid_withinsubj;
} Trial;

typedef struct voxel_t {
  int* vid;  // contains global voxel ids
  float* corr_vecs;
  float* kernel_matrices;  // contains precomputed kernel matrix
  int nTrials;  // row
  int nVoxels;  // col
  // voxel_t(int x, int y, int z) : vid(x), nTrials(y), nVoxels(z) {}
} Voxel;

typedef struct voxel_Score_t {
  int vid;
  float score;
} VoxelScore;

// VoxelXYZ: voxel's 3-d coordinates in mm
typedef struct voxelxyz_t {
  int x, y, z;
} VoxelXYZ;

typedef ALIGNED(64) struct WIDELOCK_T {
  omp_lock_t lock;
} widelock_t;

extern unsigned long long total_count;

#endif


/* matrix/gsl_matrix_uint.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_MATRIX_UINT_H__
#define __GSL_MATRIX_UINT_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_inline.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_uint.h>
#include <gsl/gsl_blas_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

typedef struct
{
  size_t size1;
  size_t size2;
  size_t tda;
  unsigned int * data;
  gsl_block_uint * block;
  int owner;
} gsl_matrix_uint;

typedef struct
{
  gsl_matrix_uint matrix;
} _gsl_matrix_uint_view;

typedef _gsl_matrix_uint_view gsl_matrix_uint_view;

typedef struct
{
  gsl_matrix_uint matrix;
} _gsl_matrix_uint_const_view;

typedef const _gsl_matrix_uint_const_view gsl_matrix_uint_const_view;

/* Allocation */

GSL_FUN gsl_matrix_uint *
gsl_matrix_uint_alloc (const size_t n1, const size_t n2);

GSL_FUN gsl_matrix_uint *
gsl_matrix_uint_calloc (const size_t n1, const size_t n2);

GSL_FUN gsl_matrix_uint *
gsl_matrix_uint_alloc_from_block (gsl_block_uint * b,
                                   const size_t offset,
                                   const size_t n1,
                                   const size_t n2,
                                   const size_t d2);

GSL_FUN gsl_matrix_uint *
gsl_matrix_uint_alloc_from_matrix (gsl_matrix_uint * m,
                                    const size_t k1,
                                    const size_t k2,
                                    const size_t n1,
                                    const size_t n2);

GSL_FUN gsl_vector_uint *
gsl_vector_uint_alloc_row_from_matrix (gsl_matrix_uint * m,
                                        const size_t i);

GSL_FUN gsl_vector_uint *
gsl_vector_uint_alloc_col_from_matrix (gsl_matrix_uint * m,
                                        const size_t j);

GSL_FUN void gsl_matrix_uint_free (gsl_matrix_uint * m);

/* Views */

GSL_FUN _gsl_matrix_uint_view
gsl_matrix_uint_submatrix (gsl_matrix_uint * m,
                            const size_t i, const size_t j,
                            const size_t n1, const size_t n2);

GSL_FUN _gsl_vector_uint_view
gsl_matrix_uint_row (gsl_matrix_uint * m, const size_t i);

GSL_FUN _gsl_vector_uint_view
gsl_matrix_uint_column (gsl_matrix_uint * m, const size_t j);

GSL_FUN _gsl_vector_uint_view
gsl_matrix_uint_diagonal (gsl_matrix_uint * m);

GSL_FUN _gsl_vector_uint_view
gsl_matrix_uint_subdiagonal (gsl_matrix_uint * m, const size_t k);

GSL_FUN _gsl_vector_uint_view
gsl_matrix_uint_superdiagonal (gsl_matrix_uint * m, const size_t k);

GSL_FUN _gsl_vector_uint_view
gsl_matrix_uint_subrow (gsl_matrix_uint * m, const size_t i,
                         const size_t offset, const size_t n);

GSL_FUN _gsl_vector_uint_view
gsl_matrix_uint_subcolumn (gsl_matrix_uint * m, const size_t j,
                            const size_t offset, const size_t n);

GSL_FUN _gsl_matrix_uint_view
gsl_matrix_uint_view_array (unsigned int * base,
                             const size_t n1,
                             const size_t n2);

GSL_FUN _gsl_matrix_uint_view
gsl_matrix_uint_view_array_with_tda (unsigned int * base,
                                      const size_t n1,
                                      const size_t n2,
                                      const size_t tda);

GSL_FUN _gsl_matrix_uint_view
gsl_matrix_uint_view_vector (gsl_vector_uint * v,
                              const size_t n1,
                              const size_t n2);

GSL_FUN _gsl_matrix_uint_view
gsl_matrix_uint_view_vector_with_tda (gsl_vector_uint * v,
                                       const size_t n1,
                                       const size_t n2,
                                       const size_t tda);

GSL_FUN _gsl_matrix_uint_const_view
gsl_matrix_uint_const_submatrix (const gsl_matrix_uint * m,
                                  const size_t i, const size_t j,
                                  const size_t n1, const size_t n2);

GSL_FUN _gsl_vector_uint_const_view
gsl_matrix_uint_const_row (const gsl_matrix_uint * m,
                            const size_t i);

GSL_FUN _gsl_vector_uint_const_view
gsl_matrix_uint_const_column (const gsl_matrix_uint * m,
                               const size_t j);

GSL_FUN _gsl_vector_uint_const_view
gsl_matrix_uint_const_diagonal (const gsl_matrix_uint * m);

GSL_FUN _gsl_vector_uint_const_view
gsl_matrix_uint_const_subdiagonal (const gsl_matrix_uint * m,
                                    const size_t k);

GSL_FUN _gsl_vector_uint_const_view
gsl_matrix_uint_const_superdiagonal (const gsl_matrix_uint * m,
                                      const size_t k);

GSL_FUN _gsl_vector_uint_const_view
gsl_matrix_uint_const_subrow (const gsl_matrix_uint * m, const size_t i,
                               const size_t offset, const size_t n);

GSL_FUN _gsl_vector_uint_const_view
gsl_matrix_uint_const_subcolumn (const gsl_matrix_uint * m, const size_t j,
                                  const size_t offset, const size_t n);

GSL_FUN _gsl_matrix_uint_const_view
gsl_matrix_uint_const_view_array (const unsigned int * base,
                                   const size_t n1,
                                   const size_t n2);

GSL_FUN _gsl_matrix_uint_const_view
gsl_matrix_uint_const_view_array_with_tda (const unsigned int * base,
                                            const size_t n1,
                                            const size_t n2,
                                            const size_t tda);

GSL_FUN _gsl_matrix_uint_const_view
gsl_matrix_uint_const_view_vector (const gsl_vector_uint * v,
                                    const size_t n1,
                                    const size_t n2);

GSL_FUN _gsl_matrix_uint_const_view
gsl_matrix_uint_const_view_vector_with_tda (const gsl_vector_uint * v,
                                             const size_t n1,
                                             const size_t n2,
                                             const size_t tda);

/* Operations */

GSL_FUN void gsl_matrix_uint_set_zero (gsl_matrix_uint * m);
GSL_FUN void gsl_matrix_uint_set_identity (gsl_matrix_uint * m);
GSL_FUN void gsl_matrix_uint_set_all (gsl_matrix_uint * m, unsigned int x);

GSL_FUN int gsl_matrix_uint_fread (FILE * stream, gsl_matrix_uint * m) ;
GSL_FUN int gsl_matrix_uint_fwrite (FILE * stream, const gsl_matrix_uint * m) ;
GSL_FUN int gsl_matrix_uint_fscanf (FILE * stream, gsl_matrix_uint * m);
GSL_FUN int gsl_matrix_uint_fprintf (FILE * stream, const gsl_matrix_uint * m, const char * format);

GSL_FUN int gsl_matrix_uint_memcpy(gsl_matrix_uint * dest, const gsl_matrix_uint * src);
GSL_FUN int gsl_matrix_uint_swap(gsl_matrix_uint * m1, gsl_matrix_uint * m2);
GSL_FUN int gsl_matrix_uint_tricpy(CBLAS_UPLO_t Uplo, CBLAS_DIAG_t Diag, gsl_matrix_uint * dest, const gsl_matrix_uint * src);

GSL_FUN int gsl_matrix_uint_swap_rows(gsl_matrix_uint * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_uint_swap_columns(gsl_matrix_uint * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_uint_swap_rowcol(gsl_matrix_uint * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_uint_transpose (gsl_matrix_uint * m);
GSL_FUN int gsl_matrix_uint_transpose_memcpy (gsl_matrix_uint * dest, const gsl_matrix_uint * src);
GSL_FUN int gsl_matrix_uint_transpose_tricpy (CBLAS_UPLO_t Uplo_src, CBLAS_DIAG_t Diag, gsl_matrix_uint * dest, const gsl_matrix_uint * src);

GSL_FUN unsigned int gsl_matrix_uint_max (const gsl_matrix_uint * m);
GSL_FUN unsigned int gsl_matrix_uint_min (const gsl_matrix_uint * m);
GSL_FUN void gsl_matrix_uint_minmax (const gsl_matrix_uint * m, unsigned int * min_out, unsigned int * max_out);

GSL_FUN void gsl_matrix_uint_max_index (const gsl_matrix_uint * m, size_t * imax, size_t *jmax);
GSL_FUN void gsl_matrix_uint_min_index (const gsl_matrix_uint * m, size_t * imin, size_t *jmin);
GSL_FUN void gsl_matrix_uint_minmax_index (const gsl_matrix_uint * m, size_t * imin, size_t * jmin, size_t * imax, size_t * jmax);

GSL_FUN int gsl_matrix_uint_equal (const gsl_matrix_uint * a, const gsl_matrix_uint * b);

GSL_FUN int gsl_matrix_uint_isnull (const gsl_matrix_uint * m);
GSL_FUN int gsl_matrix_uint_ispos (const gsl_matrix_uint * m);
GSL_FUN int gsl_matrix_uint_isneg (const gsl_matrix_uint * m);
GSL_FUN int gsl_matrix_uint_isnonneg (const gsl_matrix_uint * m);

GSL_FUN unsigned int gsl_matrix_uint_norm1 (const gsl_matrix_uint * m);

GSL_FUN int gsl_matrix_uint_add (gsl_matrix_uint * a, const gsl_matrix_uint * b);
GSL_FUN int gsl_matrix_uint_sub (gsl_matrix_uint * a, const gsl_matrix_uint * b);
GSL_FUN int gsl_matrix_uint_mul_elements (gsl_matrix_uint * a, const gsl_matrix_uint * b);
GSL_FUN int gsl_matrix_uint_div_elements (gsl_matrix_uint * a, const gsl_matrix_uint * b);
GSL_FUN int gsl_matrix_uint_scale (gsl_matrix_uint * a, const double x);
GSL_FUN int gsl_matrix_uint_scale_rows (gsl_matrix_uint * a, const gsl_vector_uint * x);
GSL_FUN int gsl_matrix_uint_scale_columns (gsl_matrix_uint * a, const gsl_vector_uint * x);
GSL_FUN int gsl_matrix_uint_add_constant (gsl_matrix_uint * a, const double x);
GSL_FUN int gsl_matrix_uint_add_diagonal (gsl_matrix_uint * a, const double x);

/***********************************************************************/
/* The functions below are obsolete                                    */
/***********************************************************************/
GSL_FUN int gsl_matrix_uint_get_row(gsl_vector_uint * v, const gsl_matrix_uint * m, const size_t i);
GSL_FUN int gsl_matrix_uint_get_col(gsl_vector_uint * v, const gsl_matrix_uint * m, const size_t j);
GSL_FUN int gsl_matrix_uint_set_row(gsl_matrix_uint * m, const size_t i, const gsl_vector_uint * v);
GSL_FUN int gsl_matrix_uint_set_col(gsl_matrix_uint * m, const size_t j, const gsl_vector_uint * v);
/***********************************************************************/

/* inline functions if you are using GCC */

GSL_FUN INLINE_DECL unsigned int   gsl_matrix_uint_get(const gsl_matrix_uint * m, const size_t i, const size_t j);
GSL_FUN INLINE_DECL void    gsl_matrix_uint_set(gsl_matrix_uint * m, const size_t i, const size_t j, const unsigned int x);
GSL_FUN INLINE_DECL unsigned int * gsl_matrix_uint_ptr(gsl_matrix_uint * m, const size_t i, const size_t j);
GSL_FUN INLINE_DECL const unsigned int * gsl_matrix_uint_const_ptr(const gsl_matrix_uint * m, const size_t i, const size_t j);

#ifdef HAVE_INLINE
INLINE_FUN
unsigned int
gsl_matrix_uint_get(const gsl_matrix_uint * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_VAL("first index out of range", GSL_EINVAL, 0) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_VAL("second index out of range", GSL_EINVAL, 0) ;
        }
    }
#endif
  return m->data[i * m->tda + j] ;
}

INLINE_FUN
void
gsl_matrix_uint_set(gsl_matrix_uint * m, const size_t i, const size_t j, const unsigned int x)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      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
  m->data[i * m->tda + j] = x ;
}

INLINE_FUN
unsigned int *
gsl_matrix_uint_ptr(gsl_matrix_uint * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      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 (unsigned int *) (m->data + (i * m->tda + j)) ;
}

INLINE_FUN
const unsigned int *
gsl_matrix_uint_const_ptr(const gsl_matrix_uint * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      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 unsigned int *) (m->data + (i * m->tda + j)) ;
}

#endif

__END_DECLS

#endif /* __GSL_MATRIX_UINT_H__ */


/////////////////////////////////////////////////////////////////////
// = NMatrix
//
// A linear algebra library for scientific computation in Ruby.
// NMatrix is part of SciRuby.
//
// NMatrix was originally inspired by and derived from NArray, by
// Masahiro Tanaka: http://narray.rubyforge.org
//
// == Copyright Information
//
// SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation
// NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation
//
// Please see LICENSE.txt for additional copyright notices.
//
// == Contributing
//
// By contributing source code to SciRuby, you agree to be bound by
// our Contributor Agreement:
//
// * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement
//
// == inc.h
//
// Includes needed for LAPACK, CLAPACK, and CBLAS functions.
//

#ifndef INC_H
# define INC_H

extern "C" { // These need to be in an extern "C" block or you'll get all kinds of undefined symbol errors.
#if defined HAVE_CBLAS_H
  #include <cblas.h>
#elif defined HAVE_ATLAS_CBLAS_H
  #include <atlas/cblas.h>
#endif

#if defined HAVE_CLAPACK_H
  #include <clapack.h>
#elif defined HAVE_ATLAS_CLAPACK_H
  #include <atlas/clapack.h>
#endif
}

#endif // INC_H


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <unistd.h>
#include <math.h>
#include "bigfile-mpi.h"
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

void usage() {
    fprintf(stderr, "usage: bigfile-sample-mpi [-r ratio] [-N Nfile] [-f newfilepath] filepath block newblock\n");
    exit(1);

}
#define DONE_TAG 1293
#define ERROR_TAG 1295
#define DIE_TAG 1290
#define WORK_TAG 1291

MPI_Datatype MPI_TYPE_WORK;
BigFile bf = {0};
BigFile bfnew = {0};
BigBlock bb = {0};
BigBlock bbnew = {0};
int verbose = 0;
int Nfile = -1;
size_t CHUNKSIZE = 1 * 1024 * 1024;
int ThisTask, NTask;
char * newfilepath = NULL;
void slave(void);
void server(void);

double ratio = 1.0;
struct work {
    int64_t offset;
    int64_t seed;
    int64_t chunksize;
    int64_t offsetnew;
    int64_t nsel;
};

static size_t filesize();

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

    MPI_Init(&argc, &argv);

    MPI_Comm_rank(MPI_COMM_WORLD, &ThisTask);
    MPI_Comm_size(MPI_COMM_WORLD, &NTask);

    MPI_Type_contiguous(sizeof(struct work), MPI_BYTE, &MPI_TYPE_WORK);
    MPI_Type_commit(&MPI_TYPE_WORK);

    int ch;
    while(-1 != (ch = getopt(argc, argv, "n:N:vf:r:"))) {
        switch(ch) {
            case 'r':
                ratio = atof(optarg);
                break;
            case 'N':
            case 'n':
                Nfile = atoi(optarg);
                break;
            case 'f':
                newfilepath = optarg;
                break;
            case 'v':
                verbose = 1;
                break;
            default:
                usage();
        }
    }
    if(argc - optind + 1 != 4) {
        usage();
    }
    argv += optind - 1;
    if(0 != big_file_mpi_open(&bf, argv[1], MPI_COMM_WORLD)) {
        fprintf(stderr, "failed to open: %s\n", big_file_get_error_message());
        exit(1);
    }
    if(0 != big_file_mpi_open_block(&bf, &bb, argv[2], MPI_COMM_WORLD)) {
        fprintf(stderr, "failed to open: %s\n", big_file_get_error_message());
        exit(1);
    }
    if(Nfile == -1 || bb.Nfile == 0) {
        Nfile = bb.Nfile;
    }
    if(newfilepath == NULL) {
        newfilepath = argv[1];
    }
    if(0 != big_file_mpi_create(&bfnew, newfilepath, MPI_COMM_WORLD)) {
        fprintf(stderr, "failed to open: %s\n", big_file_get_error_message());
        exit(1);
    }
    size_t newsize = filesize();
    if(0 != big_file_mpi_create_block(&bfnew, &bbnew, argv[3], bb.dtype, bb.nmemb, Nfile, newsize, MPI_COMM_WORLD)) {
        fprintf(stderr, "failed to create temp: %s\n", big_file_get_error_message());
        exit(1);
    }

    /* copy attrs */
    size_t nattr;
    BigAttr * attrs = big_block_list_attrs(&bb, &nattr);
    int i;
    for(i = 0; i < nattr; i ++) {
        BigAttr * attr = &attrs[i];
        big_block_set_attr(&bbnew, attr->name, attr->data, attr->dtype, attr->nmemb);
    }

    if(bb.nmemb > 0 && bb.size > 0) {
    /* copy data */
        if(ThisTask == 0) {
            server();
        } else {
            slave();
        }
    }
    if(0 != big_block_mpi_close(&bbnew, MPI_COMM_WORLD)) {
        fprintf(stderr, "failed to close new: %s\n", big_file_get_error_message());
        exit(1);
    }
    big_block_mpi_close(&bb, MPI_COMM_WORLD);
    big_file_mpi_close(&bf, MPI_COMM_WORLD);
    big_file_mpi_close(&bfnew, MPI_COMM_WORLD);
    return 0;
}
static size_t filesize() {
    gsl_rng * rng = gsl_rng_alloc(gsl_rng_mt19937);
    gsl_rng_set(rng, 1984);
    int64_t offset = 0;
    int64_t offsetnew = 0;
    struct work work;
    for(offset = 0; offset < bb.size; ) {
        int64_t chunksize = CHUNKSIZE;

        /* never read beyond my end (read_simple caps at EOF) */
        if(offset + chunksize >= bb.size) {
            /* this is the last chunk */
            chunksize = bb.size - offset;
        }
        work.offset = offset;
        work.chunksize = chunksize;
        work.seed = gsl_rng_get(rng);
        work.offsetnew = offsetnew;
        if(ratio == 1.0) {
            work.nsel = chunksize;
        } else {
            work.nsel = gsl_ran_poisson(rng, chunksize * ratio);
        }

        offset += chunksize;
        offsetnew += work.nsel;
    }
    return offsetnew;
}
void server() {
    int64_t offset = 0;
    int64_t offsetnew = 0;
    struct work work;
    gsl_rng * rng = gsl_rng_alloc(gsl_rng_mt19937);
    gsl_rng_set(rng, 1984);
    for(offset = 0; offset < bb.size; ) {
        int64_t chunksize = CHUNKSIZE;
        MPI_Status status;
        int result = 0;
        MPI_Recv(&result, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD,
                &status);
        if(status.MPI_TAG == ERROR_TAG) {
            break;
        }

        /* never read beyond my end (read_simple caps at EOF) */
        if(offset + chunksize >= bb.size) {
            /* this is the last chunk */
            chunksize = bb.size - offset;
        }
        work.offset = offset;
        work.chunksize = chunksize;
        work.seed = gsl_rng_get(rng);
        work.offsetnew = offsetnew;
        if(ratio == 1.0) {
            work.nsel = chunksize;
        } else {
            work.nsel = gsl_ran_poisson(rng, chunksize * ratio);
        }
        MPI_Send(&work, 1, MPI_TYPE_WORK, status.MPI_SOURCE, WORK_TAG, MPI_COMM_WORLD);

        offset += chunksize;
        offsetnew += work.nsel;
        if(verbose) {
            fprintf(stderr, "%td / %td done (%0.4g%%)\r", offset, bb.size, (100. / bb.size) * offset);
        }
    }
    int i;
    for(i = 1; i < NTask; i ++) {
        struct work work;
        MPI_Send(&work, 1, MPI_TYPE_WORK, i, DIE_TAG, MPI_COMM_WORLD);
    }

}
void slave() {
    gsl_rng * rng = gsl_rng_alloc(gsl_rng_mt19937);
    int result = 0;
    MPI_Send(&result, 1, MPI_INT, 0, DONE_TAG, MPI_COMM_WORLD);
    while(1) {
        struct work work;
        MPI_Status status;
        MPI_Recv(&work, 1, MPI_TYPE_WORK, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status);

        if(status.MPI_TAG == DIE_TAG) {
            break;
        }
        gsl_rng_set(rng, work.seed);

        int64_t offset = work.offset;
        int64_t chunksize = work.chunksize;
        int64_t offsetnew = work.offsetnew;
        int64_t nsel = work.nsel;
        BigArray array;
        BigBlockPtr ptrnew;
        BigArray arraynew;

        size_t dims[2];
        void * buffer = malloc(dtype_itemsize(bb.dtype) * bb.nmemb * nsel);
        dims[0] = nsel;
        dims[1] = bb.nmemb;
        big_array_init(&arraynew, buffer, bb.dtype, 2, dims, NULL);

        ptrdiff_t i;
        size_t step = dtype_itemsize(bb.dtype) * bb.nmemb;
        size_t leftover = chunksize;
        char * p = buffer;
        char * q;
        if(0 != big_block_read_simple(&bb, offset, chunksize, &array, NULL)) {
            fprintf(stderr, "failed to read original: %s\n", big_file_get_error_message());
            result = -1;
            goto bad;
        }
        q = array.data;

//        printf("%ld %ld\n", nsel, leftover);
        for(i = 0; i < chunksize; i ++) {
            int64_t r = gsl_rng_uniform_int(rng, leftover);
            if(r < nsel) {
                memcpy(p, q, step);
                p += step;
                nsel --;
            }
            if(nsel == 0) break;
            leftover --;
            q += step;
        }
        if(nsel != 0) abort();
        free(array.data);
        if(0 != big_block_seek(&bbnew, &ptrnew, offsetnew)) {
            fprintf(stderr, "failed to seek new: %s\n", big_file_get_error_message());
            result = -1;
            free(arraynew.data);
            goto bad;
        }

        if(0 != big_block_write(&bbnew, &ptrnew, &arraynew)) {
            fprintf(stderr, "failed to write new: %s\n", big_file_get_error_message());
            result = -1;
            free(arraynew.data);
            goto bad;
        }

        free(arraynew.data);
        MPI_Send(&result, 1, MPI_INT, 0, DONE_TAG, MPI_COMM_WORLD);
        continue;
    bad:
        MPI_Send(&result, 1, MPI_INT, 0, ERROR_TAG, MPI_COMM_WORLD);
        continue;
    }
    return;
}


/* testutilities.c
 *
 * 24-May-2008 Initial write: Ting Zhao
 */

#include <stdlib.h>
#include <string.h>
#include <utilities.h>
#include <math.h>
#include "tz_utilities.h"
#include "tz_error.h"
#include "tz_file_list.h"
#include "tz_image_lib_defs.h"
#include "tz_stack_utils.h"

/*
#include "tz_matlabio.h"
#include "tz_rastergeom.h"
#include <gsl/gsl_math.h>
*/
int main(int argc, char *argv[])
{
  static char *Spec[] = {"[-t]", NULL};

  Process_Arguments(argc, argv, Spec, 1);

  if (Is_Arg_Matched("-t")) {
    /* example test */

    /* extract file name without extension from a path */
    const char *path = "../data/test.tif";
    char *name = fname(path, NULL);
    if (strcmp(name, "test") != 0) {
      PRINT_EXCEPTION("Bug?", "Unexpected file name.");
      return 1;
    }

    /* The user is responsible for freeing the returned value */
    free(name);

    /* The routine returns NULL when no name can be extracted */
    path = ".";
    if (fname(path, NULL) != NULL) {
      PRINT_EXCEPTION("Bug?", "Unexpected file name.");
      return 1;
    }

    path = "../data/";
    if (fname(path, NULL) != NULL) {
      PRINT_EXCEPTION("Bug?", "Unexpected file name.");
      return 1;
    }

    /* Different parts can be put together to form a path */
    char new_path[500];
    fullpath_e("../data/", "test", "tif", new_path);
    if (strcmp(new_path, "../data/test.tif") != 0) {
      printf("%s\n", new_path);
      PRINT_EXCEPTION("Bug?", "Unexpected file path.");
      return 1;
    }

    fullpath_e("", "test", "tif", new_path);
    if (strcmp(new_path, "test.tif") != 0) {
      printf("%s\n", new_path);
      PRINT_EXCEPTION("Bug?", "Unexpected file path.");
      return 1;
    }

    { /* unit test */
      char buffer[500];

      if (fname(NULL, buffer) != NULL) {
	PRINT_EXCEPTION("Bug?", "Unexpected file name.");
	return 1;
      }

      if (fname("\0", buffer) != NULL) {
	PRINT_EXCEPTION("Bug?", "Unexpected file name.");
	return 1;
      }

      if (fname(".", buffer) != NULL) {
	PRINT_EXCEPTION("Bug?", "Unexpected file name.");
	return 1;
      }

      if (strcmp(fname("t", buffer), "t") != 0) {
	PRINT_EXCEPTION("Bug?", "Unexpected file name.");
	return 1;
      }

      if (fname("/", buffer) != NULL) {
	PRINT_EXCEPTION("Bug?", "Unexpected file name.");
	return 1;
      }

      if (fname("test/", buffer) != NULL) {
	PRINT_EXCEPTION("Bug?", "Unexpected file name.");
	return 1;
      }

      if (strcmp(fname("test", buffer), "test") != 0) {
	PRINT_EXCEPTION("Bug?", "Unexpected file name.");
	return 1;
      }

      if (strcmp(fname("./test", buffer), "test") != 0) {
	PRINT_EXCEPTION("Bug?", "Unexpected file name.");
	return 1;
      }

      if (strcmp(fname("/test", buffer), "test") != 0) {
	PRINT_EXCEPTION("Bug?", "Unexpected file name.");
	return 1;
      }

      if (strcmp(fname("/.test", buffer), ".test") != 0) {
	PRINT_EXCEPTION("Bug?", "Unexpected file name.");
	return 1;
      }

      if (strcmp(fname("t.est", buffer), "t") != 0) {
	PRINT_EXCEPTION("Bug?", "Unexpected file name.");
	return 1;
      }

      if (strcmp(fname("/t.est", buffer), "t") != 0) {
	PRINT_EXCEPTION("Bug?", "Unexpected file name.");
	return 1;
      }

      if (fname("/.", buffer) != NULL) {
	PRINT_EXCEPTION("Bug?", "Unexpected file name.");
	return 1;
      }

      if (fname("data/.", buffer) != NULL) {
	PRINT_EXCEPTION("Bug?", "Unexpected file name.");
	return 1;
      }

      if (strcmp(fullpath_e("dir", "file", "ext", buffer),
		 "dir/file.ext") != 0) {
	PRINT_EXCEPTION("Bug?", "Unexpected path name.");
	return 1;
      }

      if (strcmp(fullpath_e("dir/", "file", "ext", buffer),
		 "dir/file.ext") != 0) {
	PRINT_EXCEPTION("Bug?", "Unexpected path name.");
	return 1;
      }

      if (strcmp(fullpath_e("dir", "file", NULL, buffer),
		 "dir/file") != 0) {
	PRINT_EXCEPTION("Bug?", "Unexpected path name.");
	return 1;
      }

      if (strcmp(fullpath_e("dir", "file", ".", buffer),
		 "dir/file.") != 0) {
	printf("%s\n", buffer);
	PRINT_EXCEPTION("Bug?", "Unexpected path name.");
	return 1;
      }

      if (strcmp(fullpath_e("dir", "file", ".ext", buffer),
		 "dir/file.ext") != 0) {
	printf("%s\n", buffer);
	PRINT_EXCEPTION("Bug?", "Unexpected path name.");
	return 1;
      }

    }

    printf(":) Testing passed.\n");
    return 0;
  }

#if 0
  FILE *fp = fopen("../data/test.fbd", "r");

  size_t len;
  char *line;
  while (!feof(fp)) {
    if ((line = fgetln(fp, &len)) != NULL) {
      printf("read %lu: ", len);
      line[len-1] = '\0';
      printf("%s\n", line);
    }
  }

  fclose(fp);
#endif

#if 0
  char path[100];
  Find_Matlab_Binpath(path);
  printf("%s : %lu\n", path, strlen(path));
#endif

#if 0
  int a = 10000;
  int b = 11;
  XOR_SWAP(a, b);

  printf("%d %d\n", a, b);
#endif

#if 0
  printf("%d\n", 2 << 2);
  printf("%d\n", Compare_Float(1.01, 1.002, 0.0005));
  printf("%d\n", gsl_fcmp(1.01, 1.002, 0.0005));
#endif

#if 0
  char *s1 = "hello";
  printf("%p\n", s1);
  s1[0] = 't';
  printf("%s\n", s1);
#endif

#if 0
  double x = -100.4350;
  printf("%g\n", Cube_Root(x) * Cube_Root(x) * Cube_Root(x));
#endif

#if 0
  printf("%d\n", Raster_Line_Map(852, 451, 296));
  return 1;

  int m = 33;
  int n = 10;
  int i;
  for (i = 0; i < m; i++) {
    printf("%d ", Raster_Line_Map(m, n, i));
  }
  printf("\n");
#endif

#if 0
  int w1 = 302;
  int h1 = 586;
  int nw2;
  int nh2;
  Raster_Ratio_Scale(w1, h1, 425, 236, &nw2, &nh2);
  printf("%d x %d\n", nw2, nh2);
  printf("%g => %g\n", (double) w1 / h1, (double) nw2 / nh2);
#endif

#if 0
  //printf("%u\n", Hexstr_To_Uint("BA9B"));
  char str[9];
  printf("%s\n", Uint_To_Hexstr(262, str));
#endif

#if 0
  FILE *fp = fopen("/Users/zhaot/Work/neurolabi/data/movie/a0.BMP", "r");
  Fprint_File_Binary(fp, 20, stdout);
  fclose(fp);
#endif

#if 0
  printf("%d\n", Round_Div(110, -19));
#endif

#if 0
  printf("%d\n", Raster_Linear_Map(31, 10, 100, 0, 231));
#endif

#if 0
  printf("%d\n", Raster_Point_Zoom_Offset(0, 2, 100, 512, 500, 1));
#endif

#if 0
  fcopy("../data/test.tif", "../data/test2.tif");
#endif

#if 0
  printf("%d bytes\n", fsize("../data/test.tif"));
#endif

#if 0
  printf("%g %g %g\n", Infinity, -Infinity, NaN);
#endif

#if 0
  size_t x = 1000;
  printf("%zd\n", (size_t) 1000 * 1000 * 1000 * 4);
#endif

#if 0
  size_t index = Stack_Util_Offset(1000, 1000, 1000, 2000, 2000, 2000);
  printf("%zd\n", index);
  int x, y, z;
  Stack_Util_Coord(index, 2000, 2000, &x, &y, &z);
  printf("%d %d %d\n", x, y, z);
#endif

#if 0
  printf("%s\n", fname("../data/test.dot", NULL));
  printf("%s\n", dname("../data/test.dot", NULL));
  printf("%s\n", dname("/test.dot", NULL));
  printf("%s\n", dname("test.dot", NULL));
#endif

#if 0
  char *line = malloc(2048 * 2048 * 4);
  char *data = malloc(1024 * 1024 * 4);
  int i;
  int j;
  tic();
  for (i = 0; i < 2048; i++) {
    for (j = 0; j < 2048; j++) {
      if (*line != *data++) {
	*line++ = *data;
	*line++ = *data;
	*line = *data;
	line += 2;
      }
      /*
      memset(line, *data++, 3);
      line += 4;
      */
    }
  }
  printf("%lld\n", toc());
#endif

#if 0
  char buf[11];
  Memset_Pattern4( buf, "1234", 10 );
  buf[10] = '\0';
  printf("%s\n", buf);

  Memset_Pattern4( buf, "4321", 8 );
  buf[8] = '\0';
  printf("%s\n", buf);

  Memset_Pattern4( buf, "4321", 2);
  buf[2] = '\0';
  printf("%s\n", buf);
#endif

#if 0
  char *block[1000000];
  int i;

  tic();
  for (i = 0; i < 1000000; i++) {
    block[i] = (char*) malloc(i % 991 + 10);
  }
  printf("%lld\n", toc());

  int index[1000000];
  int j;
  int offset = 0;
  for (i = 0; i < 100; i++) {
    for (j = 0; j < 10000; j++) {
      index[offset++] = j * 100 + i;
    }
  }

  tic();
  for (i = 999999; i >= 0; i--) {
    free(block[index[i]]);
  }
  printf("%lld\n", toc());
#endif

#if 0
  int (*test)[10];
  int test2[10];
  test = &test2;
  printf("%p\n", test);
  printf("%p\n", *test);
  printf("%p\n", test2);
#endif

#if 0
  int a[100];
  int i = 0;
  i[a] = 0;
  printf("%d\n", i[a]);
#endif

#if 0
  free(NULL);
#endif

#if 0
  int i;
  for (i = 0; i < 10; i++) {
    int n = 0;
    n++;
    printf("%d\n", n);
  }
#endif

#if 0
  printf("%g\n", round(0.5));
  printf("%g\n", floor(0.5));
#endif

#if 0
  if (fcmp("../data/test2.tb", "../data/test3.tb")) {
    printf("The files are different.\n");
  } else {
    printf("The files are the same.\n");
  }
#endif

#if 0
  char str[65];
  printf("%s\n", double_binstr(0.0, str));
#endif

#if 0
  printf("%d\n", MEDIAN3(0, 1, 2));
  printf("%d\n", MEDIAN3(0, 2, 1));
  printf("%d\n", MEDIAN3(1, 0, 2));
  printf("%d\n", MEDIAN3(2, 1, 0));
  printf("%d\n", MEDIAN3(1, 2, 0));
  printf("%d\n", MEDIAN3(2, 0, 1));
  printf("%d\n", MEDIAN3(0, 1, 1));
  printf("%d\n", MEDIAN3(1, 0, 1));
  printf("%d\n", MEDIAN3(1, 1, 0));
  printf("%d\n", MEDIAN3(2, 1, 1));
  printf("%d\n", MEDIAN3(1, 2, 1));
  printf("%d\n", MEDIAN3(1, 1, 2));
#endif

#if 0
  unsigned char ch1 = 255;
  char ch = ch1;
  printf("%x\n", ch);

  ch1 = -1;
  printf("%x\n", ch1);
#endif

#if 0
  //size_t fs = fsize("../data/spikes/pm002.nev");
  FILE *fp = fopen("../data/spikes/pm002.nev", "r");
  /*
  fseek(fp, fs - 104*5, SEEK_SET);
  uint32_t tmp1;
  uint16_t tmp2;
  uint8_t tmp3;
  fread(&tmp1, 4, 1, fp);
  fread(&tmp2, 2, 1, fp);
  fread(&tmp3, 1, 1, fp);
  printf("%u, %u, %u\n", tmp1, tmp2, tmp3);
  */

  uint32_t head_size;
  fseek(fp, 12, SEEK_SET);
  fread(&head_size, 4, 1, fp);
  fseek(fp, head_size, SEEK_SET);
  int packetcnt = 4325366;
  char *buffer = (char*) malloc(packetcnt * 104);
  fread(buffer, 1, packetcnt * 104, fp);
  int offset = 0;
  uint32_t tmp1;
  uint16_t tmp2;
  uint8_t tmp3;

  int i;
  for (i = 0; i < packetcnt; i++) {
    tmp1 = *((uint32_t*) (buffer + offset));
    offset += 4;
    tmp2 = *((uint16_t*) (buffer + offset));
    offset += 2;
    tmp3 = *((uint8_t*) (buffer + offset));
    if (i > 4325360) {
      printf("%u %u %u\n", tmp1, tmp2, tmp3);
    }
    offset += 1;
    offset += 97;
  }

  fclose(fp);
#endif

#if 0
  File_List *list = New_File_List();

  //File_List_Load_Dir("/home/zhaot/Work/neutube/neurolabi/data/test", "tif", list);
  File_List_Load_Dir("../data", "tif", list);

  File_List_Sort_By_Number(list);

  Print_File_List(list);
#endif

#if 0
  int start, end;
  dir_fnum_pair("../data/ting_example_stack/superpixel_maps", ".*\\.png",
      &start, &end);
  printf("%d, %d\n", start, end);
#endif

#if 0
  color_t color;
  uint8_t overflow = Value_To_Color(0, color);

  printf("%u %u %u %u\n", overflow, color[2], color[1], color[0]);
#endif

#if 0
  printf("%*sD\n", 3, "t");
#endif

#if 1
#endif

  return 0;
}


#include <stdio.h>
#include <gsl/gsl_ieee_utils.h>

int
main (void)
{
  float f = 1.0/3.0;
  double d = 1.0/3.0;

  double fd = f; /* promote from float to double */

  printf (" f="); gsl_ieee_printf_float(&f);
  printf ("\n");

  printf ("fd="); gsl_ieee_printf_double(&fd);
  printf ("\n");

  printf (" d="); gsl_ieee_printf_double(&d);
  printf ("\n");

  return 0;
}


/*
 * 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 painterport_eca0917d_5109_4de2_ae6f_18d5c163d02c_h
#define painterport_eca0917d_5109_4de2_ae6f_18d5c163d02c_h

#include <gslib/rtree.h>
#include <ariel/painter.h>

__ariel_begin__

class painter_obj;

typedef rtree_entity<painter_obj*> painter_obj_entity;
typedef rtree_node<painter_obj_entity> painter_obj_node;
typedef _tree_allocator<painter_obj_node> painter_obj_alloc;
typedef tree<painter_obj_entity, painter_obj_node, painter_obj_alloc> painter_obj_tree;
typedef rtree<painter_obj_entity, quadratic_split_alg<25, 10, painter_obj_tree>, painter_obj_node, painter_obj_alloc> painter_obj_rtree;
typedef vector<painter_obj*> painter_objs;

class __gs_novtable painter_obj abstract
{
public:
    enum type
    {
        po_line,
        po_rect,
        po_path,
        po_text,
    };

public:
    painter_obj(const painter_context& ctx): _ctx(ctx) {}
    virtual ~painter_obj() {}
    virtual type get_type() const = 0;
    virtual rectf& get_rect(rectf& rc) const = 0;

protected:
    painter_context         _ctx;

public:
    const painter_context& get_context() const { return _ctx; }
};

class painter_line_obj:
    public painter_obj
{
public:
    painter_line_obj(const painter_context& ctx, const pointf& p1, const pointf& p2): painter_obj(ctx), _p1(p1), _p2(p2) {}
    virtual type get_type() const override { return po_line; }
    virtual rectf& get_rect(rectf& rc) const override;

private:
    pointf                  _p1;
    pointf                  _p2;
};

class painter_rect_obj:
    public painter_obj
{
public:
    painter_rect_obj(const painter_context& ctx,  const rectf& rc): painter_obj(ctx), _rc(rc) {}
    virtual type get_type() const override { return po_rect; }
    virtual rectf& get_rect(rectf& rc) const override { return rc = _rc; }

private:
    rectf                   _rc;
};

class painter_path_obj:
    public painter_obj
{
public:
    painter_path_obj(const painter_context& ctx, painter_path& take_me): painter_obj(ctx) { _path.swap(take_me); }
    painter_path_obj(const painter_context& ctx, const painter_path& path): painter_obj(ctx), _path(path) {}
    virtual type get_type() const override { return po_path; }
    virtual rectf& get_rect(rectf& rc) const override;
    rectf& rebuild_rect(rectf& rc) const;

private:
    painter_path            _path;
    mutable bool            _rc_valid = false;
    mutable rectf           _rc;
};

class painter_text_obj:
    public painter_obj
{
public:
    painter_text_obj(const painter_context& ctx, const string& txt, const pointf& p, const color& cr): painter_obj(ctx), _text(txt), _pos(p), _cr(cr) {}
    virtual type get_type() const override { return po_text; }
    virtual rectf& get_rect(rectf& rc) const override;

private:
    string                  _text;
    pointf                  _pos;
    color                   _cr;
};

class painterport:
    public painter
{
public:
    virtual ~painterport();
    virtual void resize(int w, int h) override {}
    virtual void draw_path(const painter_path& path) override;
    virtual void draw_line(const vec2& p1, const vec2& p2) override;
    virtual void draw_rect(const rectf& rc) override;
    virtual void draw_text(const gchar* str, float x, float y, const color& cr, int length) override;
    virtual void on_draw_begin() override;

protected:
    painter_obj_rtree       _rtree;

public:
    rectf& get_area_rect(rectf& rc) const;
    void query_objs(painter_objs& objs, const rectf& rc);

protected:
    void add_obj(painter_obj* obj);
    void clear_objs();
};

__ariel_end__

#endif


# ifndef LANGIL_H
# define LANGIL_H

///////////////////////////////////////////////////////
//////   GILLESPIE CLASS
//////
//////						R. Perez-Carrasco
//////                               Created: 16 Nov '13
///////////////////////////////////////////////////////
/* Class for simulating master equation through langil algorythm. It defines three classes:
		- langil: In charge of the integration
		- species: contains a name a number and methods to change number
		- reaction: contains a name, a stoiciometry associated and a pointer to a propensity computing function
*/

#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <string>
#include <fstream>
#include <iostream>
#include <unordered_map>
#include <vector>
#include <math.h>
#include <time.h>
#include <unistd.h>
#include <iomanip>
//#include <omp.h>

#include "species.h"
#include "cell_cycle.h"

#define VERBOSE 1
#define NON_VERBOSE 0

#define TIMESTEPMULT 3
#define TIMESTEP 0
#define ALL 1
#define NOWRITE 2
#define LASTPOINT 4

#define GILLESPIE 0
#define MACROSCOPIC 2
#define CLEEULER 1
#define CLEMILSTEIN 3

#define ADIABATIC true
#define NONADIABATIC false

#define GEOMETRIC true
#define NONGEOMETRIC false

#define DETERMINISTIC_TIME true
#define STOCHASTIC_TIME false

#define EXPONENTIAL_REACTION 0
#define GEOMETRIC_REACTION 1
#define DETERMINISTIC_REACTION 2
#define ADIABATIC_REACTION 3

using namespace std;

struct StructDivisionState{
	double time;
	double species0before;
	double species1before;
	double species0after;
	double species1after;
};

class reaction; // defined after langil
class species; // defined after reaction
class expressionzone; // defined in a separate file

class langil{

	friend class expressionzone;

//	friend class reaction; // reaction have access to the state of the system

	public:

		langil(string outputfilename, double vol=100, int seed=-1);
		void SetState(string aname, float anumber); //Set Initial conditions
		double GetState(string a); // get the state of the selected species
		void SetTime(double time); //Set current time
		void AddSpecies(string aname, float anum); // Add a new molecular species
		void AddSpeciesTimeTracking(string aname, float anum, double lifetime); //  Add a molecular species with a timer
		void AddCellCycleSpecies(); // add a cell cycle to the system
		void addCellPhase(double duration, int type_phase); // add a cell phase to the cell cycle
		void PrintSummarySpecies();
		string StateString();
		void Add_Reaction(string name,vector<int> stoich,double (*prop_f)(v_species&),int typeofrec = EXPONENTIAL_REACTION); // Add a reaction to the system overloaded to create directly
		void UpdateTime(); // Update times for the deterministic events
		void SetRunType(int rt,double dt=0);
		double GillespieStep(double timelimit = -1); // Advance one step in the Gillespie algorithm. Returns time of the step. Timelimit does not react if the reaction would take more than timelimit
		double LangevinEulerStep(double timelimit = -1); // Make an Euler-Maruyama integration step
		double LangevinMilsteinStep(double timelimit = -1); // Make an SDE integration step with the Milstein algorithm (not tested)
		double MacroStep(double timlimit = -1); // Make an Euler (deterministic) step
		double MacroAdiabaticStep(double dtt); //  Make an Euler step if the Adiabaitc species
		double Run(double time, bool verbose=true); // React until reach time time
		double RunTimes(int N,double time, bool verbose=true); // Run Run() N times
		double RunTransition(bool (*prop_f)(v_species&), bool (*prop_g)(v_species&), int loops, double maxT); // Run transition between two points, if loops=1 it also records the transition trajectory
		void WriteState(); // Write state in file
		void WriteTempState(); // Write state in file
		void WritePrevrecState(); // Write previous recorded state in file
		void SetWriteState(int flag, double tl=0);// Switch betweem different states:
		double (langil::*MakeStep)(double); // pointer to the actual integration chosen
			// TIMESTEP: write the state every tl time lapse
			// ALL: write after each reaction takes place
			// NOWRITE: don't write anything in output file
		double SetLangevinTimeStep(double dx); // set dt to a characteristic time that will increase with the size of the system
		void Add_TimeAction(double (*actionfunc)(double));
		void RunTimeAction();
		void setPhaseDuration(int phase, double duration, int type_phase);
		void Set_AdiabaticReaction(string aname);
		void Set_GeometricReaction(string aname);
		void Set_Boundary_Behaviour(void (*b_b)(v_species&, v_species&)); // Boundary behaviour
		void Set_StoreDivisionTimes();// activate the storage of division times in DivisionTimes vector
		void (*Boundary_Behaviour)(v_species&, v_species&); // boundary behaviour function
		vector<StructDivisionState> Get_HistoryDivision();
		void Reset_HistoryDivision();
		void SetDivisionHistory(int b,int a);

	protected:

		v_species x; //value of each species. shared_ptr is used since the vector can contain species and also childrens of that class (i.e. different classes)
		vector<reaction> r;//vector with all the reactions
 		double time; // current time of simulation
		gsl_rng * rng; // allocator for the rng generator
		vector<double> rnd; // rnd numbers
		vector<double> xtemp; // temporal vector for integrations
		v_species xtemp0,xtemp1; // temporal vector for integrations
		double pro,pro0; // auxiliar variables for the program
		int sto;  // auxiliar value for stoichiometry
		double totalprop,cumprop, detprop; // sum of the propensities
		double nexttau; // time of the next step
		vector<reaction>::iterator nextreaction; // reaction selected to react
		vector<int>* nextstoichiometry;
		double Omega; // volume of the system to change between concentrations and absolute numbers
		double signal; // variable that can modulate an external signal its time course
						// may be changed through time actions with Add_TimeAction
		int celldivided; // flag to track possible celldivision
		int geostoichiometry; // dummy variable to set random geometric stoichiometries

		bool record; // Set record of the trajectory
		ofstream trajfile; // File for output traj
		string outputfilename; // File for output name
		int fwrite; // flag for write state when integrating
		double timelapse; // dt for recording if TIMESTEP
		double nextrectime; // t for recording if TIMESTEP
		double totaltime; // total integration time for the trajectory
		double dt; // timestep used in integrations for macroscopic and langevin (if negative, it is used by Langevin as a prediction for dx)

		int cellcyclespeciesidx;  // index of the species vector that tracks cell cycle phases
 		cell_cycle cell; // cell_cycle object to manage cell cycle events

 		bool fStoreDivisionTimes; // flag to store Division Times
 		vector<StructDivisionState> DivisionHistoryVector; // Division history vector. Each component is a vector
 		StructDivisionState divisionstate;
		vector<double(*)(double)> actionfuncvec; // vector to time action functions

};

////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
class reaction{//Class containing the info of each reaction. Each reaction added
// through the method Add_Reaction(reaction) will be stored as an element in a vector
// by langil class. The pointer x, points out to the state of the langil system and
// is managed automatically by langil when a reaction is added to a langil instance
// It has also the property "adiabatic" meaning that it is a fast variable and can always be simulated
// from its deterministic behaviour

	friend class langil;

	public:

	reaction(string aname,vector<int> asto,double (*aprop_f)(v_species&)=NULL,int reacttype= EXPONENTIAL_REACTION,
		    vector<reaction>* x0=NULL){
			name=aname;
			stoichiometry=asto;
			prop_f=aprop_f;
			reactiontype=reacttype;
		}
		double GetPropensity(){
			return prop_f(*x);
		}
		double GetPropensity(v_species& x0){
			return prop_f(x0);
		}
		string GetName(){
			return name;
		}
		void SetPropensity(double (*p_f)(v_species&)){
			prop_f=p_f;
		}
		void SetState(v_species& x0){
			x=&x0;
		}
		vector<int>* GetStoichiometry(){
			return &stoichiometry;
		}
		bool IsAdiabatic(){
			return (reactiontype == ADIABATIC_REACTION);
		}
		bool IsNotAdiabatic(){
			return (reactiontype != ADIABATIC_REACTION);
		}
		bool IsGeometric(){
			return (reactiontype == GEOMETRIC_REACTION);
		}
		bool IsNotGeometric(){
			return (reactiontype != GEOMETRIC_REACTION);
		}
		bool IsNotDetermTime(){
			return (reactiontype != (DETERMINISTIC_REACTION));
		}
		bool IsDetermTime(){
			return (reactiontype == (DETERMINISTIC_REACTION));
		}
		void SetAdiabatic(){
			reactiontype = ADIABATIC_REACTION;
		}
		void SetGeometric(){
			reactiontype = GEOMETRIC_REACTION;
		}
		void SetDeterministicTime(){
			reactiontype = DETERMINISTIC_REACTION;
		}

	protected:

		string name; // name of the reactio
		vector<int> stoichiometry; // stoichiometry
		double (*prop_f)(v_species&); // propensity function
		v_species* x;  //value of each species. shared_ptr is used since the vector can contain species and also childrens of that class (i.e. different classes)
		int reactiontype; // can be any of the macros EXPONENTIAL_REACTION, DETERMINISITIC_REACTION, etc.

 // of the system

};

#endif


#ifndef EXT_VANILLAGENERATOR_LIB_OBJECTS_BIOMEARRAY_H_
#define EXT_VANILLAGENERATOR_LIB_OBJECTS_BIOMEARRAY_H_

#include <cstddef>
#include <gsl/span>

class BiomeArray {
 public:
  static const size_t DATA_SIZE = 256;

  explicit BiomeArray(const gsl::span<const uint_fast8_t, BiomeArray::DATA_SIZE> &values);

  auto Get(uint8_t x, uint8_t z) const -> uint_fast8_t;
  auto Set(uint8_t x, uint8_t z, uint_fast8_t value) -> void;

  [[nodiscard]] gsl::span<const uint_fast8_t, DATA_SIZE> GetRawData() const;

 private:
  std::array<uint_fast8_t, 256> mValues;

  static inline void Index(uint8_t x, uint8_t z, uint_fast8_t &offset);
};

#endif // EXT_VANILLAGENERATOR_LIB_OBJECTS_BIOMEARRAY_H_


/* vector/gsl_vector_ulong.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_VECTOR_ULONG_H__
#define __GSL_VECTOR_ULONG_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_inline.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_block_ulong.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;
  unsigned long *data;
  gsl_block_ulong *block;
  int owner;
}
gsl_vector_ulong;

typedef struct
{
  gsl_vector_ulong vector;
} _gsl_vector_ulong_view;

typedef _gsl_vector_ulong_view gsl_vector_ulong_view;

typedef struct
{
  gsl_vector_ulong vector;
} _gsl_vector_ulong_const_view;

typedef const _gsl_vector_ulong_const_view gsl_vector_ulong_const_view;

/* Allocation */

GSL_FUN gsl_vector_ulong *gsl_vector_ulong_alloc (const size_t n);
GSL_FUN gsl_vector_ulong *gsl_vector_ulong_calloc (const size_t n);

GSL_FUN gsl_vector_ulong *gsl_vector_ulong_alloc_from_block (gsl_block_ulong * b,
                                                     const size_t offset,
                                                     const size_t n,
                                                     const size_t stride);

GSL_FUN gsl_vector_ulong *gsl_vector_ulong_alloc_from_vector (gsl_vector_ulong * v,
                                                      const size_t offset,
                                                      const size_t n,
                                                      const size_t stride);

GSL_FUN void gsl_vector_ulong_free (gsl_vector_ulong * v);

/* Views */

GSL_FUN _gsl_vector_ulong_view
gsl_vector_ulong_view_array (unsigned long *v, size_t n);

GSL_FUN _gsl_vector_ulong_view
gsl_vector_ulong_view_array_with_stride (unsigned long *base,
                                         size_t stride,
                                         size_t n);

GSL_FUN _gsl_vector_ulong_const_view
gsl_vector_ulong_const_view_array (const unsigned long *v, size_t n);

GSL_FUN _gsl_vector_ulong_const_view
gsl_vector_ulong_const_view_array_with_stride (const unsigned long *base,
                                               size_t stride,
                                               size_t n);

GSL_FUN _gsl_vector_ulong_view
gsl_vector_ulong_subvector (gsl_vector_ulong *v,
                            size_t i,
                            size_t n);

GSL_FUN _gsl_vector_ulong_view
gsl_vector_ulong_subvector_with_stride (gsl_vector_ulong *v,
                                        size_t i,
                                        size_t stride,
                                        size_t n);

GSL_FUN _gsl_vector_ulong_const_view
gsl_vector_ulong_const_subvector (const gsl_vector_ulong *v,
                                  size_t i,
                                  size_t n);

GSL_FUN _gsl_vector_ulong_const_view
gsl_vector_ulong_const_subvector_with_stride (const gsl_vector_ulong *v,
                                              size_t i,
                                              size_t stride,
                                              size_t n);

/* Operations */

GSL_FUN void gsl_vector_ulong_set_zero (gsl_vector_ulong * v);
GSL_FUN void gsl_vector_ulong_set_all (gsl_vector_ulong * v, unsigned long x);
GSL_FUN int gsl_vector_ulong_set_basis (gsl_vector_ulong * v, size_t i);

GSL_FUN int gsl_vector_ulong_fread (FILE * stream, gsl_vector_ulong * v);
GSL_FUN int gsl_vector_ulong_fwrite (FILE * stream, const gsl_vector_ulong * v);
GSL_FUN int gsl_vector_ulong_fscanf (FILE * stream, gsl_vector_ulong * v);
GSL_FUN int gsl_vector_ulong_fprintf (FILE * stream, const gsl_vector_ulong * v,
                              const char *format);

GSL_FUN int gsl_vector_ulong_memcpy (gsl_vector_ulong * dest, const gsl_vector_ulong * src);

GSL_FUN int gsl_vector_ulong_reverse (gsl_vector_ulong * v);

GSL_FUN int gsl_vector_ulong_swap (gsl_vector_ulong * v, gsl_vector_ulong * w);
GSL_FUN int gsl_vector_ulong_swap_elements (gsl_vector_ulong * v, const size_t i, const size_t j);

GSL_FUN unsigned long gsl_vector_ulong_max (const gsl_vector_ulong * v);
GSL_FUN unsigned long gsl_vector_ulong_min (const gsl_vector_ulong * v);
GSL_FUN void gsl_vector_ulong_minmax (const gsl_vector_ulong * v, unsigned long * min_out, unsigned long * max_out);

GSL_FUN size_t gsl_vector_ulong_max_index (const gsl_vector_ulong * v);
GSL_FUN size_t gsl_vector_ulong_min_index (const gsl_vector_ulong * v);
GSL_FUN void gsl_vector_ulong_minmax_index (const gsl_vector_ulong * v, size_t * imin, size_t * imax);

GSL_FUN int gsl_vector_ulong_add (gsl_vector_ulong * a, const gsl_vector_ulong * b);
GSL_FUN int gsl_vector_ulong_sub (gsl_vector_ulong * a, const gsl_vector_ulong * b);
GSL_FUN int gsl_vector_ulong_mul (gsl_vector_ulong * a, const gsl_vector_ulong * b);
GSL_FUN int gsl_vector_ulong_div (gsl_vector_ulong * a, const gsl_vector_ulong * b);
GSL_FUN int gsl_vector_ulong_scale (gsl_vector_ulong * a, const unsigned long x);
GSL_FUN int gsl_vector_ulong_add_constant (gsl_vector_ulong * a, const double x);
GSL_FUN int gsl_vector_ulong_axpby (const unsigned long alpha, const gsl_vector_ulong * x, const unsigned long beta, gsl_vector_ulong * y);
GSL_FUN unsigned long gsl_vector_ulong_sum (const gsl_vector_ulong * a);

GSL_FUN int gsl_vector_ulong_equal (const gsl_vector_ulong * u,
                            const gsl_vector_ulong * v);

GSL_FUN int gsl_vector_ulong_isnull (const gsl_vector_ulong * v);
GSL_FUN int gsl_vector_ulong_ispos (const gsl_vector_ulong * v);
GSL_FUN int gsl_vector_ulong_isneg (const gsl_vector_ulong * v);
GSL_FUN int gsl_vector_ulong_isnonneg (const gsl_vector_ulong * v);

GSL_FUN INLINE_DECL unsigned long gsl_vector_ulong_get (const gsl_vector_ulong * v, const size_t i);
GSL_FUN INLINE_DECL void gsl_vector_ulong_set (gsl_vector_ulong * v, const size_t i, unsigned long x);
GSL_FUN INLINE_DECL unsigned long * gsl_vector_ulong_ptr (gsl_vector_ulong * v, const size_t i);
GSL_FUN INLINE_DECL const unsigned long * gsl_vector_ulong_const_ptr (const gsl_vector_ulong * v, const size_t i);

#ifdef HAVE_INLINE

INLINE_FUN
unsigned long
gsl_vector_ulong_get (const gsl_vector_ulong * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return v->data[i * v->stride];
}

INLINE_FUN
void
gsl_vector_ulong_set (gsl_vector_ulong * v, const size_t i, unsigned long x)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  v->data[i * v->stride] = x;
}

INLINE_FUN
unsigned long *
gsl_vector_ulong_ptr (gsl_vector_ulong * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (unsigned long *) (v->data + i * v->stride);
}

INLINE_FUN
const unsigned long *
gsl_vector_ulong_const_ptr (const gsl_vector_ulong * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (const unsigned long *) (v->data + i * v->stride);
}
#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_ULONG_H__ */


/*
 * Copyright (C) 2015  University of Oregon
 *
 * You may distribute under the terms of either the GNU General Public
 * License or the Apache License, as specified in the LICENSE file.
 *
 * For more information, see the LICENSE file.
 */
/* aslmirtime.h */
/*---------------------------------------------------------------------------*/
/*                                                                           */
/* aslmirtime.h: Global includes and defines                                 */
/*                                                                           */
/* Copyright (C) 2011 Paul Kinchesh                                          */
/*                                                                           */
/* This file is part of aslmirtime.                                          */
/*                                                                           */
/* aslmirtime is free software: you can redistribute it and/or modify        */
/* it under the terms of the GNU General Public License as published by      */
/* the Free Software Foundation, either version 3 of the License, or         */
/* (at your option) any later version.                                       */
/*                                                                           */
/* aslmirtime 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 aslmirtime. If not, see <http://www.gnu.org/licenses/>.        */
/*                                                                           */
/*---------------------------------------------------------------------------*/
/**/

/*--------------------------------------------------------*/
/*---- Wrap the header to prevent multiple inclusions ----*/
/*--------------------------------------------------------*/
#ifndef _H_aslmirtime_H
#define _H_aslmirtime_H

/*----------------------------------*/
/*---- Include standard headers ----*/
/*----------------------------------*/
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <stddef.h>
#include <sys/time.h>

/*-----------------------------*/
/*---- Include GSL Headers ----*/
/*-----------------------------*/
#include <gsl/gsl_multimin.h>

/*-------------------------------------------*/
/*---- Make sure __FUNCTION__ is defined ----*/
/*-------------------------------------------*/
#ifndef __FUNCTION__
#define __FUNCTION__ __func__
#endif

/*------------------------------------------------------*/
/*---- Floating point comparison macros (as in SGL) ----*/
/*------------------------------------------------------*/
/* EPSILON is the largest allowable deviation due to floating point storage */
#define EPSILON 1e-9
#define FP_LT(A,B)  (((A)<(B)) && (fabs((A)-(B))>EPSILON))  /* A less than B */
#define FP_GT(A,B)  (((A)>(B)) && (fabs((A)-(B))>EPSILON))  /* A greater than B */
#define FP_EQ(A,B)  (fabs((A)-(B))<=EPSILON)                /* A equal to B */
#define FP_NEQ(A,B) (!FP_EQ(A,B))                           /* A not equal to B */
#define FP_GTE(A,B) (FP_GT(A,B) || FP_EQ(A,B))              /* A greater than or equal to B */
#define FP_LTE(A,B) (FP_LT(A,B) || FP_EQ(A,B))              /* A less than or equal to B */

/*---------------------------------------------------------------*/
/*---- So we can simply include aslmirtime.h in aslmirtime.c ----*/
/*---------------------------------------------------------------*/
#ifdef LOCAL
#define EXTERN
#else
#define EXTERN extern
#endif

/*-----------------------------------*/
/*---- Functions in aslmirtime.c ----*/
/*-----------------------------------*/
double my_f (const gsl_vector *v, void *params);
void my_df (const gsl_vector *v, void *params, gsl_vector *df);
void my_fdf (const gsl_vector *x, void *params, double *f, gsl_vector *df);
void getinput(double **pars,int argc,char *argv[]);
int nomem(char *file,const char *function,int line);
int usage_terminate();

/*----------------------------*/
/*---- End of header wrap ----*/
/*----------------------------*/
#endif


/*
 * Copyright 2012-2016 M. Andersen and L. Vandenberghe.
 * Copyright 2010-2011 L. Vandenberghe.
 * Copyright 2004-2009 J. Dahl and L. Vandenberghe.
 *
 * This file is part of CVXOPT.
 *
 * CVXOPT is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * CVXOPT 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, see <http://www.gnu.org/licenses/>.
 */

#include "cvxopt.h"

#include <complex.h>

#include "misc.h"

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

#include <time.h>

PyDoc_STRVAR(gsl__doc__,"Random Module.");

static unsigned long seed = 0;
static const gsl_rng_type *rng_type;
static gsl_rng *rng;

static char doc_getseed[] =
  "Returns the seed value for the random number generator.\n\n"
  "getseed()";

static PyObject * getseed(PyObject *self)
{
  return Py_BuildValue("l",seed);
}

static char doc_setseed[] =
  "Sets the seed value for the random number generator.\n\n"
  "setseed(value = 0)\n\n"
  "ARGUMENTS\n"
  "value     integer seed. If the value is 0, then the system clock\n"
  "          measured in seconds is used instead";

static PyObject * setseed(PyObject *self, PyObject *args)
{
  unsigned long seed_ = 0;
  time_t seconds;

  if (!PyArg_ParseTuple(args, "|l", &seed_))
    return NULL;

  if (!seed_) {
    time(&seconds);
    seed = (unsigned long)seconds;
  }
  else seed = seed_;

  return Py_BuildValue("");
}

static char doc_normal[] =
  "Randomly generates a matrix with normally distributed entries.\n\n"
  "normal(nrows, ncols=1, mean=0, std=1)\n\n"
  "PURPOSE\n"
  "Returns a matrix with typecode 'd' and size nrows by ncols, with\n"
  "its entries randomly generated from a normal distribution with mean\n"
  "m and standard deviation std.\n\n"
  "ARGUMENTS\n"
  "nrows     number of rows\n\n"
  "ncols     number of columns\n\n"
  "mean      approximate mean of the distribution\n\n"
  "std       standard deviation of the distribution";
static PyObject *
normal(PyObject *self, PyObject *args, PyObject *kwrds)
{
  matrix *obj;
  int i, nrows, ncols = 1;
  double m = 0, s = 1;
  char *kwlist[] = {"nrows", "ncols", "mean", "std",  NULL};

  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "i|idd", kwlist,
	  &nrows, &ncols, &m, &s)) return NULL;

  if (s < 0.0) PY_ERR(PyExc_ValueError, "std must be non-negative");

  if ((nrows<0) || (ncols<0)) {
    PyErr_SetString(PyExc_TypeError, "dimensions must be non-negative");
    return NULL;
  }

  if (!(obj = Matrix_New(nrows, ncols, DOUBLE)))
    return PyErr_NoMemory();

  gsl_rng_env_setup();
  rng_type = gsl_rng_default;
  rng = gsl_rng_alloc (rng_type);
  gsl_rng_set(rng, seed);

  for (i = 0; i < nrows*ncols; i++)
    MAT_BUFD(obj)[i] = gsl_ran_gaussian (rng, s) + m;

  seed = gsl_rng_get (rng);
  gsl_rng_free(rng);

  return (PyObject *)obj;
}

static char doc_uniform[] =
  "Randomly generates a matrix with uniformly distributed entries.\n\n"
  "uniform(nrows, ncols=1, a=0, b=1)\n\n"
  "PURPOSE\n"
  "Returns a matrix with typecode 'd' and size nrows by ncols, with\n"
  "its entries randomly generated from a uniform distribution on the\n"
  "interval (a,b).\n\n"
  "ARGUMENTS\n"
  "nrows     number of rows\n\n"
  "ncols     number of columns\n\n"
  "a         lower bound\n\n"
  "b         upper bound";

static PyObject *
uniform(PyObject *self, PyObject *args, PyObject *kwrds)
{
  matrix *obj;
  int i, nrows, ncols = 1;
  double a = 0, b = 1;

  char *kwlist[] = {"nrows", "ncols", "a", "b", NULL};

  if (!PyArg_ParseTupleAndKeywords(args, kwrds, "i|idd", kwlist,
	  &nrows, &ncols, &a, &b)) return NULL;

  if (a>b) PY_ERR(PyExc_ValueError, "a must be less than b");

  if ((nrows<0) || (ncols<0))
    PY_ERR_TYPE("dimensions must be non-negative");

  if (!(obj = (matrix *)Matrix_New(nrows, ncols, DOUBLE)))
    return PyErr_NoMemory();

  gsl_rng_env_setup();
  rng_type = gsl_rng_default;
  rng = gsl_rng_alloc (rng_type);
  gsl_rng_set(rng, seed);

  for (i= 0; i < nrows*ncols; i++)
    MAT_BUFD(obj)[i] = gsl_ran_flat (rng, a, b);

  seed = gsl_rng_get (rng);
  gsl_rng_free(rng);

  return (PyObject *)obj;
}

static PyMethodDef gsl_functions[] = {
{"getseed", (PyCFunction)getseed, METH_VARARGS|METH_KEYWORDS, doc_getseed},
{"setseed", (PyCFunction)setseed, METH_VARARGS|METH_KEYWORDS, doc_setseed},
{"normal", (PyCFunction)normal, METH_VARARGS|METH_KEYWORDS, doc_normal},
{"uniform", (PyCFunction)uniform, METH_VARARGS|METH_KEYWORDS, doc_uniform},
{NULL}  /* Sentinel */
};

#if PY_MAJOR_VERSION >= 3

static PyModuleDef gsl_module = {
    PyModuleDef_HEAD_INIT,
    "gsl",
    gsl__doc__,
    -1,
    gsl_functions,
    NULL, NULL, NULL, NULL
};

PyMODINIT_FUNC PyInit_gsl(void)
{
  PyObject *m;
  if (!(m = PyModule_Create(&gsl_module))) return NULL;
  if (import_cvxopt() < 0) return NULL;
  return m;
}

#else

PyMODINIT_FUNC initgsl(void)
{
  PyObject *m;
  m = Py_InitModule3("cvxopt.gsl", gsl_functions, gsl__doc__);
  if (import_cvxopt() < 0) return;
}

#endif


/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 * All rights reserved.
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree.
 */
#pragma once
#include <chrono>
#include <functional>
#include <vector>

#include <immintrin.h>

#ifdef USE_BLAS
#if __APPLE__
// not sure whether need to differentiate TARGET_OS_MAC or TARGET_OS_IPHONE,
// etc.
#include <Accelerate/Accelerate.h>
#else
#include <cblas.h>
#endif
#endif

#ifdef _OPENMP
#include <omp.h>
#endif

#ifdef USE_MKL
#include <mkl.h>
#endif

#include "./AlignedVec.h"
#include "fbgemm/FbgemmBuild.h"
#include "fbgemm/FbgemmPackMatrixB.h"
#include "src/RefImplementations.h"

namespace fbgemm {

template <typename T>
void randFill(aligned_vector<T>& vec, T low, T high);

void llc_flush(std::vector<char>& llc);

// Same as omp_get_max_threads() when OpenMP is available, otherwise 1
int fbgemm_get_max_threads();
// Same as omp_get_num_threads() when OpenMP is available, otherwise 1
int fbgemm_get_num_threads();
// Same as omp_get_thread_num() when OpenMP is available, otherwise 0
int fbgemm_get_thread_num();

template <typename T>
NOINLINE float cache_evict(const T& vec) {
  auto const size = vec.size();
  auto const elemSize = sizeof(typename T::value_type);
  auto const dataSize = size * elemSize;

  const char* data = reinterpret_cast<const char*>(vec.data());
  constexpr int CACHE_LINE_SIZE = 64;
  // Not having this dummy computation significantly slows down the computation
  // that follows.
  float dummy = 0.0f;
  for (std::size_t i = 0; i < dataSize; i += CACHE_LINE_SIZE) {
    dummy += data[i] * 1.0f;
    _mm_mfence();
#ifndef _MSC_VER
    asm volatile("" ::: "memory");
#endif
    _mm_clflush(&data[i]);
  }

  return dummy;
}

/**
 * Parse application command line arguments
 *
 */
int parseArgumentInt(
    int argc,
    const char* argv[],
    const char* arg,
    int non_exist_val,
    int def_val);
bool parseArgumentBool(
    int argc,
    const char* argv[],
    const char* arg,
    bool def_val);

namespace {
struct empty_flush {
  void operator()() const {}
};
} // namespace

/**
 * @param Fn functor to execute
 * @param Fe data eviction functor
 */
template <class Fn, class Fe = std::function<void()>>
double measureWithWarmup(
    Fn&& fn,
    int warmupIterations,
    int measuredIterations,
    const Fe& fe = empty_flush(),
    bool useOpenMP = false) {
  for (int i = 0; i < warmupIterations; ++i) {
    // Evict data first
    fe();
    fn();
  }

  double ttot = 0.0;

#ifdef _OPENMP
#pragma omp parallel if (useOpenMP)
  {
#endif
    for (int i = 0; i < measuredIterations; ++i) {
      int thread_id = 0;
      std::chrono::time_point<std::chrono::high_resolution_clock> start, end;

#ifdef _OPENMP
      if (useOpenMP) {
        thread_id = omp_get_thread_num();
      }
#endif

      if (thread_id == 0) {
        fe();
      }

#ifdef _OPENMP
      if (useOpenMP) {
#pragma omp barrier
      }
#endif
      start = std::chrono::high_resolution_clock::now();

      fn();

#ifdef _OPENMP
      if (useOpenMP) {
#pragma omp barrier
      }
#endif

      end = std::chrono::high_resolution_clock::now();
      auto dur =
          std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);

      if (thread_id == 0) {
        // TODO: measure load imbalance
        ttot += dur.count();
      }
    }

#ifdef _OPENMP
  }
#endif
  return ttot / 1e9 / measuredIterations;
}

/*
 * @brief Out-of-place transposition for M*N matrix ref.
 * @param M number of rows in input
 * @param K number of columns in input
 */
template <typename T>
void transpose_matrix(
    int M,
    int N,
    const T* src,
    int ld_src,
    T* dst,
    int ld_dst) {
  for (int i = 0; i < N; ++i) {
    for (int j = 0; j < M; ++j) {
      dst[i * ld_dst + j] = src[i + j * ld_src];
    }
  } // for each output row
}

/*
 * @brief In-place transposition for nxk matrix ref.
 * @param n number of rows in input (number of columns in output)
 * @param k number of columns in input (number of rows in output)
 */
template <typename T>
void transpose_matrix(T* ref, int n, int k) {
  std::vector<T> local(n * k);
  transpose_matrix(n, k, ref, k, local.data(), n);
  memcpy(ref, local.data(), n * k * sizeof(T));
}

#if defined(USE_MKL)
void test_xerbla(char* srname, const int* info, int);
#endif

#define dataset 1

template <typename btype>
void performance_test(
    int num_instances,
    bool flush,
    int repetitions,
    bool is_mkl) {
#if defined(USE_MKL)
  mkl_set_xerbla((XerblaEntry)test_xerbla);
#endif

  float alpha = 1.f, beta = 1.f;
  matrix_op_t btran = matrix_op_t::Transpose;

#if dataset == 1
  const int NITER = (flush) ? 10 : 100;
  std::vector<std::vector<int>> shapes;
  for (auto m = 1; m < 120; m++) {
    // shapes.push_back({m, 128, 512});
    shapes.push_back({m, 512, 512});
  }

#elif dataset == 2
  const int NITER = (flush) ? 10 : 100;
#include "shapes_dataset.h"

#else
  flush = false;
  constexpr int NITER = 1;
  std::vector<std::vector<int>> shapes;
  std::random_device r;
  std::default_random_engine generator(r());
  std::uniform_int_distribution<int> dm(1, 100);
  std::uniform_int_distribution<int> dnk(1, 1024);
  for (int i = 0; i < 1000; i++) {
    int m = dm(generator);
    int n = dnk(generator);
    int k = dnk(generator);
    shapes.push_back({m, n, k});
  }
#endif

  std::string type;
  double gflops, gbs, ttot;
  for (auto s : shapes) {
    int m = s[0];
    int n = s[1];
    int k = s[2];

    // initialize with small numbers
    aligned_vector<int> Aint(m * k);
    randFill(Aint, 0, 4);
    std::vector<aligned_vector<float>> A;
    for (int i = 0; i < num_instances; ++i) {
      A.push_back(aligned_vector<float>(Aint.begin(), Aint.end()));
    }

    aligned_vector<int> Bint(k * n);
    randFill(Bint, 0, 4);
    aligned_vector<float> B(Bint.begin(), Bint.end());
    std::vector<std::unique_ptr<PackedGemmMatrixB<btype>>> Bp;
    for (int i = 0; i < num_instances; ++i) {
      Bp.emplace_back(std::unique_ptr<PackedGemmMatrixB<btype>>(
          new PackedGemmMatrixB<btype>(btran, k, n, alpha, B.data())));
    }
    auto kAligned = ((k * sizeof(float) + 64) & ~63) / sizeof(float);
    auto nAligned = ((n * sizeof(float) + 64) & ~63) / sizeof(float);
    std::vector<aligned_vector<float>> Bt(num_instances);
    auto& Bt_ref = Bt[0];

    if (btran == matrix_op_t::Transpose) {
      Bt_ref.resize(k * nAligned);
      for (auto row = 0; row < k; ++row) {
        for (auto col = 0; col < n; ++col) {
          Bt_ref[row * nAligned + col] = alpha * B[col * k + row];
        }
      }
    } else {
      Bt_ref.resize(kAligned * n);
      for (auto row = 0; row < k; ++row) {
        for (auto col = 0; col < n; ++col) {
          Bt_ref[col * kAligned + row] = alpha * B[col * k + row];
        }
      }
    }

    for (auto i = 1; i < num_instances; ++i) {
      Bt[i] = Bt_ref;
    }

    std::vector<aligned_vector<float>> C_ref;
    std::vector<aligned_vector<float>> C_fb;
    if (beta != 0.0f) {
      aligned_vector<int> Cint(m * n);
      randFill(Cint, 0, 4);
      for (int i = 0; i < num_instances; ++i) {
        C_ref.push_back(aligned_vector<float>(Cint.begin(), Cint.end()));
        C_fb.push_back(aligned_vector<float>(Cint.begin(), Cint.end()));
      }
    } else {
      for (int i = 0; i < num_instances; ++i) {
        C_ref.push_back(aligned_vector<float>(m * n, 1.f));
        C_fb.push_back(aligned_vector<float>(m * n, NAN));
      }
    }

    double nflops = 2.0 * m * n * k;
    double nbytes = 4.0 * m * k + sizeof(btype) * 1.0 * k * n + 4.0 * m * n;

    // warm up MKL and fbgemm
    // check correctness at the same time
    for (auto w = 0; w < 3; w++) {
#if defined(USE_MKL) || defined(USE_BLAS)
      cblas_sgemm(
          CblasRowMajor,
          CblasNoTrans,
          CblasNoTrans, // B is pretransposed, if required by operation
          m,
          n,
          k,
          1.0, // Mutliplication by Alpha is done during transpose of B
          A[0].data(),
          k,
          Bt[0].data(),
          btran == matrix_op_t::NoTranspose ? kAligned : nAligned,
          beta,
          C_ref[0].data(),
          n);
#else
      cblas_sgemm_ref(
          matrix_op_t::NoTranspose,
          matrix_op_t::NoTranspose,
          m,
          n,
          k,
          1.0,
          A[0].data(),
          k,
          Bt[0].data(),
          (btran == matrix_op_t::NoTranspose) ? kAligned : nAligned,
          beta,
          C_ref[0].data(),
          n);
#endif
#ifdef _OPENMP
#pragma omp parallel if (num_instances == 1)
#endif
      {
        int num_threads = num_instances == 1 ? fbgemm_get_num_threads() : 1;
        int tid = num_instances == 1 ? fbgemm_get_thread_num() : 0;
        cblas_gemm_compute(
            matrix_op_t::NoTranspose,
            m,
            A[0].data(),
            *Bp[0],
            beta,
            C_fb[0].data(),
            tid,
            num_threads);
      }

#if defined(USE_MKL) || defined(USE_BLAS)
      // Compare results
      for (auto i = 0; i < C_ref[0].size(); i++) {
        if (std::abs(C_ref[0][i] - C_fb[0][i]) > 1e-3) {
          fprintf(
              stderr,
              "Error: too high diff between fp32 ref %f and fp16 %f at %d\n",
              C_ref[0][i],
              C_fb[0][i],
              i);
          return;
        }
      }
#endif
    }

#if defined(USE_MKL)
    if (is_mkl) {
      // Gold via MKL sgemm
      type = "MKL_FP32";
#elif defined(USE_BLAS)
    type = "BLAS_FP32";
#else
    type = "REF_FP32";
#endif

      ttot = measureWithWarmup(
          [&]() {
            int copy = num_instances == 1 ? 0 : fbgemm_get_thread_num();
            for (int i = 0; i < repetitions; ++i) {
#if defined(USE_MKL) || defined(USE_BLAS)
              cblas_sgemm(
                  CblasRowMajor,
                  CblasNoTrans,
                  CblasNoTrans,
                  m,
                  n,
                  k,
                  1.0,
                  A[copy].data(),
                  k,
                  Bt[copy].data(),
                  btran == matrix_op_t::NoTranspose ? kAligned : nAligned,
                  beta,
                  C_ref[copy].data(),
                  n);
#else
            cblas_sgemm_ref(
                matrix_op_t::NoTranspose,
                matrix_op_t::NoTranspose,
                m,
                n,
                k,
                1.0,
                A[copy].data(),
                k,
                Bt[copy].data(),
                (btran == matrix_op_t::NoTranspose) ? kAligned : nAligned,
                beta,
                C_ref[copy].data(),
                n);
#endif
            }
          },
          3,
          NITER,
          [&]() {
            if (flush) {
              int copy = num_instances == 1 ? 0 : fbgemm_get_thread_num();
              cache_evict(A[copy]);
              cache_evict(Bt[copy]);
              cache_evict(C_ref[copy]);
            }
          },
          // Use OpenMP if num instances > 1
          num_instances > 1);

      gflops = nflops / ttot / 1e9;
      gbs = nbytes / ttot / 1e9;
      printf(
          "\n%30s m = %5d n = %5d k = %5d Gflops = %8.4lf GBytes = %8.4lf\n",
          type.c_str(),
          m,
          n,
          k,
          gflops * repetitions,
          gbs * repetitions);
#ifdef USE_MKL
    }
#endif
    type = "FBP_" + std::string(typeid(btype).name());

    ttot = measureWithWarmup(
        [&]() {
          // When executing in data decomposition (single-instance) mode
          // Different threads will access different regions of the same
          // matrices. Thus, copy to be used is always 0. The numbers of
          // threads would be the as number of threads in the parallel
          // region.
          // When running in functional decomposition (multi-instance) mode
          // different matrices are used. The copy to be used selected by
          // thread_id (thread_num), and the number of threads performance
          // the compute of the same instance is 1.
          int copy = num_instances == 1 ? 0 : fbgemm_get_thread_num();
          int num_threads = num_instances == 1 ? fbgemm_get_num_threads() : 1;
          int tid = num_instances == 1 ? fbgemm_get_thread_num() : 0;

          for (int i = 0; i < repetitions; ++i) {
            cblas_gemm_compute(
                matrix_op_t::NoTranspose,
                m,
                A[copy].data(),
                *Bp[copy],
                beta,
                C_fb[copy].data(),
                tid,
                num_threads);
          }
        },
        3,
        NITER,
        [&]() {
          if (flush) {
            int copy = num_instances == 1 ? 0 : fbgemm_get_thread_num();
            cache_evict(A[copy]);
            cache_evict(*Bp[copy]);
            cache_evict(C_fb[copy]);
          }
        },
        true /*useOpenMP*/);

    gflops = nflops / ttot / 1e9;
    gbs = nbytes / ttot / 1e9;
    printf(
        "%30s m = %5d n = %5d k = %5d Gflops = %8.4lf GBytes = %8.4lf\n",
        type.c_str(),
        m,
        n,
        k,
        gflops * repetitions,
        gbs * repetitions);
  }
}

} // namespace fbgemm