//! \file neutronics_driver.h
//! Base class for single-physics neutronics solver
#ifndef NEUTRONICS_DRIVER_H
#define NEUTRONICS_DRIVER_H

#include "enrico/driver.h"
#include "enrico/geom.h"
#include "enrico/mpi_types.h"

#include <gsl/gsl>
#include <xtensor/xtensor.hpp>

#include <vector>

namespace enrico {

using CellHandle = gsl::index;

//! Base class for driver that controls a neutronics solve
class NeutronicsDriver : public Driver {
public:
  explicit NeutronicsDriver(MPI_Comm comm)
    : Driver(comm)
  {}

  virtual ~NeutronicsDriver() = default;

  //! Get energy deposition in each material normalized to a given power
  //! \param power User-specified power in [W]
  //! \return Heat source in each material as [W/cm3]
  virtual xt::xtensor<double, 1> heat_source(double power) const = 0;

  //! Find cells corresponding to a vector of positions
  //! \param positions (x,y,z) coordinates to search for
  //! \return Handles to cells
  virtual std::vector<CellHandle> find(const std::vector<Position>& positions) = 0;

  //! Set the density of the material in a cell
  //! \param cell Handle to a cell
  //! \param rho Density in [g/cm^3]
  virtual void set_density(CellHandle cell, double rho) const = 0;

  //! Set the temperature of a cell
  //! \param cell Handle to a cell
  //! \param T Temperature in [K]
  virtual void set_temperature(CellHandle cell, double T) const = 0;

  //! Get the density of a cell
  //! \param cell Handle to a cell
  //! \return Cell density in [g/cm^3]
  virtual double get_density(CellHandle cell) const = 0;

  //! Get the temperature of a cell
  //! \param cell Handle to a cell
  //! \return Temperature in [K]
  virtual double get_temperature(CellHandle cell) const = 0;

  //! Get the volume of a cell
  //! \param cell Handle to a cell
  //! \return Volume in [cm^3]
  virtual double get_volume(CellHandle cell) const = 0;

  //! Detemrine whether a cell contains fissionable nuclides
  //! \param cell Handle to a cell
  //! \return Whether the cell contains fissionable nuclides
  virtual bool is_fissionable(CellHandle cell) const = 0;

  //! Determine number of cells participating in coupling
  //! \return Number of cells
  virtual std::size_t n_cells() const = 0;

  //! Create energy production tallies
  virtual void create_tallies() = 0;

  //! Get a label for a cell
  //! \param cell Handle to a clel
  //! \return Label for the cell
  virtual std::string cell_label(CellHandle cell) const = 0;
};

} // namespace enrico

#endif // NEUTRONICS_DRIVER_H


#ifndef MODULE_WASM_LINEAR_MEMORY_H
#define MODULE_WASM_LINEAR_MEMORY_H
#include <array>
#include <vector>
#include <algorithm>
#include "wasm_base.h"
#include "wasm_value.h"
#include <stdexcept>
#include <iostream>
#include <cstring>
#include <gsl/span>
#include "utilities/endianness.h"

namespace wasm {

struct WasmLinearMemory
{
	static constexpr const std::size_t page_size = 65536;
	using page_type = alignas(page_size) char[page_size];
private:
	using vector_type = wasm::SimpleVector<page_type>;
public:
	using wasm_external_kind_type = parse::Memory;

	using page_iterator = page_type*;
	using const_page_iterator = const page_type*;
	using page_pointer = page_type*;
	using const_page_pointer = const page_type*;
	using page_reference = page_type&;
	using const_page_reference = const page_type&;

	using page_span = gsl::span<const page_type>;
	using const_page_span = gsl::span<const page_type>;

	using value_type = char;
	using iterator = value_type*;
	using const_iterator = const value_type*;
	using pointer = value_type*;
	using const_pointer = const value_type*;
	using reference = value_type&;
	using const_reference = const value_type&;

	using raw_span = gsl::span<const value_type>;
	using const_raw_span = gsl::span<const value_type>;

	using size_type = vector_type::size_type;
	using difference_type = vector_type::difference_type;

	WasmLinearMemory(const parse::Memory& def, const parse::DataSegment& seg);

	friend const_page_span pages(const WasmLinearMemory& self)
	{ return const_page_span(self.memory_.data(), self.memory_.size()); }

	friend page_span pages(WasmLinearMemory& self)
	{ return page_span(self.memory_.data(), self.memory_.size()); }

	friend const_raw_span data(const WasmLinearMemory& self)
	{
		return raw_span(
			static_cast<const_pointer>(self.vec_.data()),
			page_size * self.vec_.size()
		);
	}

	friend raw_span data(WasmLinearMemory& self)
	{
		return raw_span(
			static_cast<pointer>(self.vec_.data()),
			page_size * self.vec_.size()
		);
	}

	friend wasm_sint32_t grow_memory(WasmLinearMemory& self, wasm_uint32_t delta)
	{
		wasm_uint32_t prev = static_cast<wasm_uint32_t>(pages(self).size());
		assert(prev == pages(self).size());
		if(delta > 0u)
		{
			std::size_t new_size = self.vec_.size() + delta;
			if(new_size > maximum_)
				return -1;
			try
			{
				self.resize(self.vec_.size() + delta);
			}
			catch(std::bad_alloc& e)
			{
				return -1;
			}
		}
		return reinterpret_cast<const wasm_sint32_t&>(prev);
	}

	friend bool matches(const WasmLinearMemory& self, const parse::Memory& tp)
	{
		return self.memory_.size() >= tp.size() and (
			self.maximum_ == tp.maximum.value_or(std::numeric_limits<std::size_t>::max())
		);
	}

private:
	void resize(std::size_t n)
	{
		assert(n <= maximum_);
		using std::swap;
		vector_type tmp(n);
		swap(memory_, tmp);
		std::memcpy(v.data(), tmp.data(), tmp.size() * sizeof(tmp.front()));
		std::memset(v.data() + tmp.size(), 0, v.size() - tmp.size());
	}
	vector_type memory_;
	const std::size_t maximum_ = std::numeric_limits<std::size_t>::max();
};

LanguageType index_type(const WasmLinearMemory& self)
{
	return
}

WasmLinearMemory::size_type current_memory(const WasmLinearMemory& self)
{ return pages(self).size(); }

WasmLinearMemory::size_type page_count(const WasmLinearMemory& self)
{ return current_memory(self); }

WasmLinearMemory::size_type size(const WasmLinearMemory& self)
{ return data(self).size(); }

WasmLinearMemory::WasmLinearMemory(const parse::Memory& def):
	memory_(def.initial),
	maximum_(def.maximum.value_or(std::numeric_limits<std::size_t>::max())
{
	auto bytes = data(*this);
	std::fill(bytes.begin(), bytes.end(), 0);
}

gsl::span<const char> compute_effective_address(
	const WasmLinearMemory& self,
	wasm_uint32_t base,
	wasm_uint32_t offset,
	std::size_t size
)
{
	auto mem = data(self);
	if(mem.size() <= base)
		throw std::out_of_range("Base address is too large while computing linear memory effective address.");
	std::size_t effective_address = base;
	effective_address += offset;
	if(mem.size() <= effective_address)
		throw std::out_of_range("Computed effective address is too large for linear memory.");
	if(std::size_t remaining = mem.size() - effective_address; remaining < sizeof(Type))
		throw std::out_of_range("Linear memory access partially acesses out-of-bounds memory.");
	return mem.subspan(effective_address, size);
}

gsl::span<char> compute_effective_address(
	WasmLinearMemory& self,
	wasm_uint32_t base,
	wasm_uint32_t offset,
	std::size_t size
)
{
	auto addr = compute_effective_address(std::as_const(self), base, offset, size);
	const char* data = addr.data();
	auto size = addr.data();
	return gsl::span<char>(const_cast<char*>(data), size);
}

template <class Type>
Type load_little_endian(const WasmLinearMemory& self, wasm_uint32_t base, wasm_uint32_t offset)
{
	static_assert(std::is_trivially_copyable_v<Type>);
	static_assert(std::is_arithmetic_v<Type>);
	Type result;
	auto addr = compute_effective_address(self, base, offset, sizeof(Type));
	assert(addr.size() == sizeof(result));
	std::memcpy(&result, addr.data(), sizeof(result));
	if(not system_is_little_endian())
	{
		static_assert(std::is_same_v<decltype(byte_swap(value)), decltype(value)>);
		result = byte_swap(result);
	}
	return result;
}

template <class Type, class PassedType>
void store_little_endian(const WasmLinearMemory& self, wasm_uint32_t base, wasm_uint32_t offset, PassedType value)
{
	static_assert(std::is_trivially_copyable_v<Type>);
	static_assert(std::is_arithmetic_v<Type>);
	static_assert(std::is_same_v<Type, PassedType>);
	auto pos = compute_effective_address(self, base, offset, sizeof(Type));
	assert(addr.size() == sizeof(value));
	if(not system_is_little_endian())
	{
		static_assert(std::is_same_v<decltype(byte_swap(value)), decltype(value)>);
		value = byte_swap(value);
	}
	std::memcpy(pos, &value, sizeof(value));
}

} /* namespace wasm */

#endif /* MODULE_WASM_LINEAR_MEMORY_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 <mpi.h>
#include <StGermain/StGermain.h>
#include <StgDomain/StgDomain.h>
#include <StgFEM/StgFEM.h>
#include "types.h"
#include <petsc.h>
#include <petscvec.h>
#include <petscmat.h>
#include <petscksp.h>
#include <petscpc.h>
#include <petscsnes.h>
#include <petscis.h>
#include <petscviewer.h>
#include <petscsys.h>
#include <petscversion.h>

#if ( (PETSC_VERSION_MAJOR >= 3) && (PETSC_VERSION_MINOR >=3) )
  #if (PETSC_VERSION_MINOR >=6)
     #include "petsc/private/kspimpl.h"
  #else
     #include "petsc-private/kspimpl.h"   /*I "petscksp.h" I*/
  #endif
#else
  #include "private/kspimpl.h"   /*I "petscksp.h" I*/
#endif

//#include "ksptypes.h"
#include "ksp-register.h"
#include "StokesBlockKSPInterface.h"

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

/* Macro for checking number integrity - i.e. checks if number is infinite or "not a number" */
#define SBKSP_isGoodNumber( number ) ( (! isnan( number ) ) && ( ! isinf( number ) ) )
#define SBKSP_GetPetscMatrix( matrix ) ( (Mat)(matrix) )
#define SBKSP_GetPetscVector( vector ) ( (Vec)(vector) )
#define SBKSP_GetPetscKSP( solver ) ( (KSP)(solver)  )
PetscErrorCode _BlockSolve( void* solver, void* _stokesSLE );

const Type StokesBlockKSPInterface_Type = "StokesBlockKSPInterface";

void* _StokesBlockKSPInterface_DefaultNew( Name name ) {
    SizeT                                              _sizeOfSelf = sizeof(StokesBlockKSPInterface);
    Type                                                      type = StokesBlockKSPInterface_Type;
    Stg_Class_DeleteFunction*                              _delete = _SLE_Solver_Delete;
    Stg_Class_PrintFunction*                                _print = _SLE_Solver_Print;
    Stg_Class_CopyFunction*                                  _copy = _SLE_Solver_Copy;
    Stg_Component_BuildFunction*                            _build = _StokesBlockKSPInterface_Build;
    Stg_Component_InitialiseFunction*                  _initialise = _StokesBlockKSPInterface_Initialise;
    Stg_Component_ExecuteFunction*                        _execute = _SLE_Solver_Execute;
    Stg_Component_DestroyFunction*                        _destroy = _SLE_Solver_Destroy;
    SLE_Solver_GetResidualFunc*                       _getResidual = NULL;
    Stg_Component_DefaultConstructorFunction*  _defaultConstructor = _StokesBlockKSPInterface_DefaultNew;
    Stg_Component_ConstructFunction*                    _construct = _StokesBlockKSPInterface_AssignFromXML;
    SLE_Solver_SolverSetupFunction*                   _solverSetup = _StokesBlockKSPInterface_SolverSetup;
    SLE_Solver_SolveFunction*                               _solve = _StokesBlockKSPInterface_Solve;

    AllocationType  nameAllocationType = NON_GLOBAL /* default value NON_GLOBAL */;
    return (void*) _StokesBlockKSPInterface_New( STOKESBLOCKKSPINTERFACE_PASSARGS );
}

/* Creation implementation / Virtual constructor */
/* Set up function pointers */
StokesBlockKSPInterface* _StokesBlockKSPInterface_New( STOKESBLOCKKSPINTERFACE_DEFARGS )
{
    StokesBlockKSPInterface* self;
    /* Allocate memory */
    assert( _sizeOfSelf >= sizeof(StokesBlockKSPInterface) );

    self = (StokesBlockKSPInterface*) _SLE_Solver_New( SLE_SOLVER_PASSARGS );

    /* Virtual info */
    return self;
}

void _StokesBlockKSPInterface_Init(
		StokesBlockKSPInterface*      self,
		StiffnessMatrix*   preconditioner,
		Stokes_SLE *       st_sle,
		PETScMGSolver *    mg,
        Name   filename,
        char * string,
		StiffnessMatrix*  k2StiffMat,
		StiffnessMatrix*  mStiffMat,
		ForceVector*	  f2ForceVec,
		ForceVector*	  jForceVec,
		double            penaltyNumber,
		double            hFactor,
		StiffnessMatrix*  vmStiffMat,
		ForceVector*	  vmForceVec  )
{
	self->preconditioner = preconditioner;
	self->st_sle = st_sle;
	self->mg     = mg;
    self->optionsFile = filename;
    self->optionsString = string;
	self->k2StiffMat = k2StiffMat;
	self->f2ForceVec = f2ForceVec;
	self->penaltyNumber = penaltyNumber;
	self->hFactor       = hFactor;
	self->mStiffMat = mStiffMat;
	self->jForceVec = jForceVec;
	self->vmStiffMat = vmStiffMat;
	self->vmForceVec = vmForceVec;
	/* add the vecs and matrices to the Base SLE class's dynamic lists, so they can be
	initialised and built properly */

	/*
	if (k2StiffMat )
	    SystemLinearEquations_AddStiffnessMatrix( st_sle, k2StiffMat );

	if (f2ForceVec )
	    SystemLinearEquations_AddForceVector( st_sle, f2ForceVec );

	if (mStiffMat )
	    SystemLinearEquations_AddStiffnessMatrix( st_sle, mStiffMat );

	if (jForceVec )
	    SystemLinearEquations_AddForceVector( st_sle, jForceVec );

	if (vmStiffMat )
	    SystemLinearEquations_AddStiffnessMatrix( st_sle, vmStiffMat );

	if (vmForceVec )
	    SystemLinearEquations_AddForceVector( st_sle, vmForceVec );
    */
}

void _StokesBlockKSPInterface_Build( void* solver, void* sle ) {/* it is the sle here being passed in*/
	StokesBlockKSPInterface*	self  = (StokesBlockKSPInterface*)solver;

	Stream_IndentBranch( StgFEM_Debug );

	/* Build Preconditioner */
	if ( self->preconditioner ) {
		Stg_Component_Build( self->preconditioner, sle, False );
		SystemLinearEquations_AddStiffnessMatrix( self->st_sle, self->preconditioner );

	}
	if( self->mg ){
	    Stg_Component_Build( self->mg, sle, False );
	}

	Stream_UnIndentBranch( StgFEM_Debug );
}

void _StokesBlockKSPInterface_AssignFromXML( void* solver, Stg_ComponentFactory* cf, void* data ) {
	StokesBlockKSPInterface* self         = (StokesBlockKSPInterface*) solver;
	//double                  tolerance;
	//Iteration_Index         maxUzawaIterations, minUzawaIterations;
	StiffnessMatrix*  preconditioner;
	StiffnessMatrix*  k2StiffMat;
	ForceVector*	  f2ForceVec;
	StiffnessMatrix*  mStiffMat;
	ForceVector*	  jForceVec;
	double            penaltyNumber;
	double            hFactor;
	StiffnessMatrix*  vmStiffMat;
	ForceVector*	  vmForceVec;
	//Bool                    useAbsoluteTolerance;
	//Bool                    monitor;
	Stokes_SLE *            st_sle;
	PETScMGSolver *         mg;
	//Name                filename;
	//char* 		        string;

	_SLE_Solver_AssignFromXML( self, cf, data );

	preconditioner = Stg_ComponentFactory_ConstructByKey( cf, self->name, (Dictionary_Entry_Key)"Preconditioner", StiffnessMatrix, False, data  );
	st_sle  = Stg_ComponentFactory_ConstructByKey( cf, self->name, (Dictionary_Entry_Key)"stokesEqn", Stokes_SLE, True, data  );
	mg      = Stg_ComponentFactory_ConstructByKey( cf, self->name, (Dictionary_Entry_Key)"mgSolver", PETScMGSolver, False, data);
	k2StiffMat = Stg_ComponentFactory_ConstructByKey( cf, self->name, (Dictionary_Entry_Key)"2ndStressTensorMatrix", StiffnessMatrix, False, data  );
	f2ForceVec = Stg_ComponentFactory_ConstructByKey( cf, self->name, (Dictionary_Entry_Key)"2ndForceVector", ForceVector, False, data  );
	penaltyNumber   = Stg_ComponentFactory_GetDouble( cf, self->name, (Dictionary_Entry_Key)"penaltyNumber", 0.0  );
	hFactor         = Stg_ComponentFactory_GetDouble( cf, self->name, (Dictionary_Entry_Key)"hFactor", 0.0  );
	mStiffMat = Stg_ComponentFactory_ConstructByKey( cf, self->name, (Dictionary_Entry_Key)"MassMatrix", StiffnessMatrix, False, data  );
	jForceVec = Stg_ComponentFactory_ConstructByKey( cf, self->name, (Dictionary_Entry_Key)"JunkForceVector", ForceVector, False, data  );
	vmStiffMat = Stg_ComponentFactory_ConstructByKey( cf, self->name, (Dictionary_Entry_Key)"VelocityMassMatrix", StiffnessMatrix, False, data  );
	vmForceVec = Stg_ComponentFactory_ConstructByKey( cf, self->name, (Dictionary_Entry_Key)"VMassForceVector", ForceVector, False, data  );

	_StokesBlockKSPInterface_Init( self, preconditioner, st_sle, mg, NULL, NULL, k2StiffMat, mStiffMat,
                                   f2ForceVec, jForceVec, penaltyNumber, hFactor, vmStiffMat, vmForceVec);

}

void _StokesBlockKSPInterface_Initialise( void* solver, void* data ) {
	StokesBlockKSPInterface* self = (StokesBlockKSPInterface*) solver;
	Stokes_SLE*             sle  = (Stokes_SLE*)  self->st_sle;

	/* Initialise Parent */
	_SLE_Solver_Initialise( self, sle );

	KSPRegisterAllKSP("Solvers/KSPSolvers/src");
}

/* SolverSetup */

void _StokesBlockKSPInterface_SolverSetup( void* solver, void* stokesSLE ) {
	StokesBlockKSPInterface* self = (StokesBlockKSPInterface*) solver;
	//Stokes_SLE*             sle  = (Stokes_SLE*)             stokesSLE;

 	Journal_DPrintf( self->debug, "In %s:\n", __func__ );
	Stream_IndentBranch( StgFEM_Debug );

	Stream_UnIndentBranch( StgFEM_Debug );
}
void SBKSP_SetSolver( void* solver, void* stokesSLE ) {
	SLE_Solver* self = (SLE_Solver*) solver;
	Stokes_SLE*              sle  = (Stokes_SLE*) stokesSLE;

    sle->solver=self;

}
void SBKSP_SetPenalty( void* solver, double penalty ) {
  StokesBlockKSPInterface* self = (StokesBlockKSPInterface*) solver;
  self->penaltyNumber=penalty;
}

int SBKSP_GetPressureIts(void *solver){
  StokesBlockKSPInterface* self = (StokesBlockKSPInterface*) solver;
  return self->stats.pressure_its;
}

/***********************************************************************************************************/
/***********************************************************************************************************/
/***********************************************************************************************************/

void SBKSP_GetStokesOperators(
		Stokes_SLE *stokesSLE,
		Mat *K,Mat *G,Mat *D,Mat *C,Mat *approxS,
		Vec *f,Vec *h,Vec *u,Vec *p )
{

	*K = *G = *D = *C = PETSC_NULL;
	if (stokesSLE->kStiffMat){      *K = SBKSP_GetPetscMatrix( stokesSLE->kStiffMat->matrix );     }
	if (stokesSLE->gStiffMat){      *G = SBKSP_GetPetscMatrix( stokesSLE->gStiffMat->matrix );     }
	if (stokesSLE->dStiffMat){      *D = SBKSP_GetPetscMatrix( stokesSLE->dStiffMat->matrix );     }
	if (stokesSLE->cStiffMat){      *C = SBKSP_GetPetscMatrix( stokesSLE->cStiffMat->matrix );     }

	/* preconditioner */
	*approxS = PETSC_NULL;
	if( ((StokesBlockKSPInterface*)stokesSLE->solver)->preconditioner ) {
		StiffnessMatrix *preconditioner;

		preconditioner = ((StokesBlockKSPInterface*)stokesSLE->solver)->preconditioner;
		*approxS = SBKSP_GetPetscMatrix( preconditioner->matrix );
	}

	*f = *h = PETSC_NULL;
	if (stokesSLE->fForceVec){      *f = SBKSP_GetPetscVector( stokesSLE->fForceVec->vector );     }
	if (stokesSLE->hForceVec){      *h = SBKSP_GetPetscVector( stokesSLE->hForceVec->vector );     }

	*u = *p = PETSC_NULL;
	if (stokesSLE->uSolnVec){       *u = SBKSP_GetPetscVector( stokesSLE->uSolnVec->vector );      }
	if (stokesSLE->pSolnVec){       *p = SBKSP_GetPetscVector( stokesSLE->pSolnVec->vector );      }

}
/***********************************************************************************************************/
/***********************************************************************************************************/
/***********************************************************************************************************/
/* Sets up Solver to be a custom ksp (KSP_BSSCR) solve by default: */
void _StokesBlockKSPInterface_Solve( void* solver, void* _stokesSLE ) {
  StokesBlockKSPInterface* self    = (StokesBlockKSPInterface*)solver;
  PetscLogDouble flopsA,flopsB;
  PetscTruth found, get_flops;

  found = PETSC_FALSE;
  get_flops = PETSC_FALSE;
  PetscOptionsGetTruth( PETSC_NULL, "-get_flops", &get_flops, &found);
  if(get_flops){
    PetscGetFlops(&flopsA); }

  _BlockSolve(solver, _stokesSLE);

  if(get_flops){
    PetscGetFlops(&flopsB);
    self->stats.total_flops=(double)(flopsB-flopsA); }
}
PetscErrorCode _BlockSolve( void* solver, void* _stokesSLE ) {
  Stokes_SLE*  stokesSLE  = (Stokes_SLE*)_stokesSLE;
  StokesBlockKSPInterface* Solver    = (StokesBlockKSPInterface*)solver;

  /* Create shortcuts to stuff needed on sle */
  Mat       K;
  Mat       G;
  Mat       Gt;
  Mat       D;
  Mat       C;
  Mat       approxS;
  Vec       u;
  Vec       p;
  Vec       f;
  Vec       h;
  Mat stokes_P;
  Mat stokes_A;
  Vec stokes_x;
  Vec stokes_b;
  Mat a[2][2];
  Vec x[2];
  Vec b[2];
  KSP stokes_ksp;
  PC  stokes_pc;
  PetscTruth sym,flg;
  PetscErrorCode ierr;

  PetscInt   N,n;

  SBKSP_GetStokesOperators( stokesSLE, &K,&G,&D,&C, &approxS, &f,&h, &u,&p );

  /* create Gt */
  if( !D ) {
    ierr = MatTranspose( G, MAT_INITIAL_MATRIX, &Gt);CHKERRQ(ierr);
    sym = PETSC_TRUE;
    Solver->DIsSym = sym;
  }
  else {
    Gt = D;
    sym = PETSC_FALSE;
    Solver->DIsSym = sym;
  }
  flg=PETSC_FALSE;
  PetscOptionsHasName(PETSC_NULL,"-use_petsc_ksp",&flg);
  if (flg) {
    if( !C ) {
      /* Everything in this bracket, dependent on !C, is to build
         a matrix with diagonals of 0 for C the previous comment ways

      need a 'zero' matrix to keep fieldsplit happy in petsc? */
      MatType mtype;
      Vec V;
      //MatGetSize( G, &M, &N );
      VecGetSize(p, &N);
      VecGetLocalSize( p, &n );
      MatCreate( PetscObjectComm((PetscObject) K), &C );
      MatSetSizes( C, PETSC_DECIDE ,PETSC_DECIDE, N, N );
#if (((PETSC_VERSION_MAJOR==3) && (PETSC_VERSION_MINOR>=3)) || (PETSC_VERSION_MAJOR>3) )
      MatSetUp(C);
#endif
      MatGetType( G, &mtype );
      MatSetType( C, mtype );
      MatGetVecs( G, &V, PETSC_NULL );
      VecSet(V, 0.0);
      //VecSet(h, 1.0);
      ierr = VecAssemblyBegin( V );CHKERRQ(ierr);
      ierr = VecAssemblyEnd  ( V );CHKERRQ(ierr);
      ierr = MatDiagonalSet(C,V,INSERT_VALUES);CHKERRQ(ierr);
      ierr = MatAssemblyBegin( C, MAT_FINAL_ASSEMBLY );CHKERRQ(ierr);
      ierr = MatAssemblyEnd  ( C, MAT_FINAL_ASSEMBLY );CHKERRQ(ierr);
    }
  }
  a[0][0]=K;  a[0][1]=G;
  a[1][0]=Gt; a[1][1]=C;
  ierr = MatCreateNest(PetscObjectComm((PetscObject) K), 2, NULL, 2, NULL, (Mat *)a, &stokes_A);CHKERRQ(ierr);
  ierr = MatAssemblyBegin( stokes_A, MAT_FINAL_ASSEMBLY );CHKERRQ(ierr);
  ierr = MatAssemblyEnd( stokes_A, MAT_FINAL_ASSEMBLY );CHKERRQ(ierr);

  x[0]=u;
  x[1]=p;
  ierr = VecCreateNest(PetscObjectComm((PetscObject) u), 2, NULL, x, &stokes_x);CHKERRQ(ierr);
  ierr = VecAssemblyBegin( stokes_x );CHKERRQ(ierr);
  ierr = VecAssemblyEnd( stokes_x);CHKERRQ(ierr);

  b[0]=f;
  b[1]=h;
  ierr = VecCreateNest(PetscObjectComm((PetscObject) f), 2, NULL, b, &stokes_b);CHKERRQ(ierr);
  ierr = VecAssemblyBegin( stokes_b );CHKERRQ(ierr);
  ierr = VecAssemblyEnd( stokes_b);CHKERRQ(ierr);

  /* if( approxS ) { */
  /*   a[0][0]=K;    a[0][1]=G; */
  /*   a[1][0]=NULL; a[1][1]=approxS; */
  /*   ierr = MatCreateNest(PetscObjectComm((PetscObject) K), 2, NULL, 2, NULL, (Mat *)a, &stokes_P);CHKERRQ(ierr); */
  /*   ierr = MatAssemblyBegin( stokes_P, MAT_FINAL_ASSEMBLY );CHKERRQ(ierr); */
  /*   ierr = MatAssemblyEnd( stokes_P, MAT_FINAL_ASSEMBLY );CHKERRQ(ierr); */
  /* } */
  /* else { */
    stokes_P = stokes_A;
  /* } */

  /* probably should make a Destroy function for these two */
  /* Update options from file and/or string here so we can change things on the fly */
  //PetscOptionsInsertFile(PETSC_COMM_WORLD, Solver->optionsFile, PETSC_FALSE);
  //PetscOptionsInsertString(Solver->optionsString);

  ierr = KSPCreate( PETSC_COMM_WORLD, &stokes_ksp );CHKERRQ(ierr);
  Stg_KSPSetOperators( stokes_ksp, stokes_A, stokes_P, SAME_NONZERO_PATTERN );
  ierr = KSPSetType( stokes_ksp, "bsscr" );/* i.e. making this the default solver : calls KSPCreate_XXX */CHKERRQ(ierr);

  ierr = KSPGetPC( stokes_ksp, &stokes_pc );CHKERRQ(ierr);
  ierr = PCSetType( stokes_pc, PCNONE );CHKERRQ(ierr);
  ierr = KSPSetInitialGuessNonzero( stokes_ksp, PETSC_TRUE );CHKERRQ(ierr);
  ierr = KSPSetFromOptions( stokes_ksp );CHKERRQ(ierr);

  /*
    Doing this so the KSP Solver has access to the StgFEM Multigrid struct (PETScMGSolver).
    As well as any custom stuff on the Stokes_SLE struct
  */
  if( stokes_ksp->data ){/* then ksp->data has been created in a KSpSetUp_XXX function */
    /* testing for our KSP types that need the data that is on Solver... */
    /* for the moment then, this function not completely agnostic about our KSPs */
    //if(!strcmp("bsscr",stokes_ksp->type_name)){/* if is bsscr then set up the data on the ksp */
    flg=PETSC_FALSE;
    PetscOptionsHasName(PETSC_NULL,"-use_petsc_ksp",&flg);
    if (!flg) {
      ((KSP_COMMON*)(stokes_ksp->data))->st_sle         = Solver->st_sle;
      ((KSP_COMMON*)(stokes_ksp->data))->mg             = Solver->mg;
      ((KSP_COMMON*)(stokes_ksp->data))->DIsSym         = Solver->DIsSym;
      ((KSP_COMMON*)(stokes_ksp->data))->preconditioner = Solver->preconditioner;
      ((KSP_COMMON*)(stokes_ksp->data))->solver         = Solver;
    }
  }

  ierr = KSPSolve( stokes_ksp, stokes_b, stokes_x );CHKERRQ(ierr);

  Stg_KSPDestroy(&stokes_ksp );
  //if( ((StokesBlockKSPInterface*)stokesSLE->solver)->preconditioner )
  if(stokes_P != stokes_A) { Stg_MatDestroy(&stokes_P ); }

  Stg_MatDestroy(&stokes_A );

  Stg_VecDestroy(&stokes_x);
  Stg_VecDestroy(&stokes_b);

  if(!D){ Stg_MatDestroy(&Gt); }
  if(C && (stokesSLE->cStiffMat->matrix != C) ){ Stg_MatDestroy(&C); }

  PetscFunctionReturn(0);
}


#pragma once

#include <type_traits>

#include <cuda/runtime_api.hpp>
#include <gsl-lite/gsl-lite.hpp>

namespace thrustshift {

namespace kernel {

template <typename MapT, typename SrcT, typename DstT>
__global__ void gather(gsl_lite::span<const MapT> map, gsl_lite::span<const SrcT> src, gsl_lite::span<DstT> dst) {

	const auto gtid = threadIdx.x + blockIdx.x * blockDim.x;
	if (gtid < src.size()) {
		dst[gtid] = src[map[gtid]];
	}
}

} // namespace kernel

namespace async {

template <class MapRange, class SrcRange, class DstRange>
void gather(cuda::stream_t& stream, MapRange&& map, SrcRange&& src, DstRange&& dst) {
	gsl_Expects(src.size() == dst.size());
	gsl_Expects(src.size() == map.size());
	gsl_Expects(src.data() != dst.data());

	if (src.empty()) {
		return;
	}

	using map_index_type =
	    typename std::remove_reference<MapRange>::type::value_type;
	using src_value_type =
	    typename std::remove_reference<SrcRange>::type::value_type;
	using dst_value_type =
	    typename std::remove_reference<DstRange>::type::value_type;

	constexpr cuda::grid::block_dimension_t block_dim = 128;
	const cuda::grid::dimension_t grid_dim =
	    (src.size() + block_dim - 1) / block_dim;
	auto c = cuda::make_launch_config(grid_dim, block_dim);
	auto k = kernel::gather<map_index_type, src_value_type, dst_value_type>;
	cuda::enqueue_launch(k, stream, c, map, src, dst);
}

} // namespace async

} // namespace thrustshift


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

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

gsl_integration_workspace *
gsl_integration_workspace_alloc (const size_t n)
{
  gsl_integration_workspace * w ;

  if (n == 0)
    {
      GSL_ERROR_VAL ("workspace length n must be positive integer",
			GSL_EDOM, 0);
    }

  w = (gsl_integration_workspace *)
    malloc (sizeof (gsl_integration_workspace));

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

  w->alist = (double *) malloc (n * sizeof (double));

  if (w->alist == 0)
    {
      free (w);		/* exception in constructor, avoid memory leak */

      GSL_ERROR_VAL ("failed to allocate space for alist ranges",
			GSL_ENOMEM, 0);
    }

  w->blist = (double *) malloc (n * sizeof (double));

  if (w->blist == 0)
    {
      free (w->alist);
      free (w);		/* exception in constructor, avoid memory leak */

      GSL_ERROR_VAL ("failed to allocate space for blist ranges",
			GSL_ENOMEM, 0);
    }

  w->rlist = (double *) malloc (n * sizeof (double));

  if (w->rlist == 0)
    {
      free (w->blist);
      free (w->alist);
      free (w);		/* exception in constructor, avoid memory leak */

      GSL_ERROR_VAL ("failed to allocate space for rlist ranges",
			GSL_ENOMEM, 0);
    }

  w->elist = (double *) malloc (n * sizeof (double));

  if (w->elist == 0)
    {
      free (w->rlist);
      free (w->blist);
      free (w->alist);
      free (w);		/* exception in constructor, avoid memory leak */

      GSL_ERROR_VAL ("failed to allocate space for elist ranges",
			GSL_ENOMEM, 0);
    }

  w->order = (size_t *) malloc (n * sizeof (size_t));

  if (w->order == 0)
    {
      free (w->elist);
      free (w->rlist);
      free (w->blist);
      free (w->alist);
      free (w);		/* exception in constructor, avoid memory leak */

      GSL_ERROR_VAL ("failed to allocate space for order ranges",
			GSL_ENOMEM, 0);
    }

  w->level = (size_t *) malloc (n * sizeof (size_t));

  if (w->level == 0)
    {
      free (w->order);
      free (w->elist);
      free (w->rlist);
      free (w->blist);
      free (w->alist);
      free (w);		/* exception in constructor, avoid memory leak */

      GSL_ERROR_VAL ("failed to allocate space for order ranges",
			GSL_ENOMEM, 0);
    }

  w->size = 0 ;
  w->limit = n ;
  w->maximum_level = 0 ;

  return w ;
}

void
gsl_integration_workspace_free (gsl_integration_workspace * w)
{
  free (w->level) ;
  free (w->order) ;
  free (w->elist) ;
  free (w->rlist) ;
  free (w->blist) ;
  free (w->alist) ;
  free (w) ;
}

/*
size_t
gsl_integration_workspace_limit (gsl_integration_workspace * w)
{
  return w->limit ;
}

size_t
gsl_integration_workspace_size (gsl_integration_workspace * w)
{
  return w->size ;
}
*/


///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
//
// This code is licensed under the MIT License (MIT).
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef GSL_UTIL_H
#define GSL_UTIL_H

#include <gsl/gsl_assert> // for Expects

#include <array>
#include <cstddef>          // for ptrdiff_t, size_t
#include <exception>        // for exception
#include <initializer_list> // for initializer_list
#include <type_traits>      // for is_signed, integral_constant
#include <utility>          // for forward

#if defined(_MSC_VER)

#pragma warning(push)
#pragma warning(disable : 4127) // conditional expression is constant

#if _MSC_VER < 1910
#pragma push_macro("constexpr")
#define constexpr /*constexpr*/
#endif            // _MSC_VER < 1910
#endif            // _MSC_VER

namespace gsl
{
//
// GSL.util: utilities
//

// index type for all container indexes/subscripts/sizes
using index = std::ptrdiff_t;

// final_action allows you to ensure something gets run at the end of a scope
template <class F>
class final_action
{
public:
    explicit final_action(F f) noexcept : f_(std::move(f)) {}

    final_action(final_action&& other) noexcept : f_(std::move(other.f_)), invoke_(other.invoke_)
    {
        other.invoke_ = false;
    }

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

    GSL_SUPPRESS(f.6) // NO-FORMAT: attribute // terminate if throws
    ~final_action() noexcept
    {
        if (invoke_) f_();
    }

private:
    F f_;
    bool invoke_{true};
};

// finally() - convenience function to generate a final_action
template <class F>
final_action<F> finally(const F& f) noexcept
{
    return final_action<F>(f);
}

template <class F>
final_action<F> finally(F&& f) noexcept
{
    return final_action<F>(std::forward<F>(f));
}

// narrow_cast(): a searchable way to do narrowing casts of values
template <class T, class U>
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
constexpr T narrow_cast(U&& u) noexcept
{
    return static_cast<T>(std::forward<U>(u));
}

struct narrowing_error : public std::exception
{
};

namespace details
{
    template <class T, class U>
    struct is_same_signedness
        : public std::integral_constant<bool, std::is_signed<T>::value == std::is_signed<U>::value>
    {
    };
} // namespace details

// narrow() : a checked version of narrow_cast() that throws if the cast changed the value
template <class T, class U>
GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
GSL_SUPPRESS(f.6) // NO-FORMAT: attribute // TODO: MSVC /analyze does not recognise noexcept(false)
T narrow(U u) noexcept(false)
{
    T t = narrow_cast<T>(u);
    if (static_cast<U>(t) != u) gsl::details::throw_exception(narrowing_error());
    if (!details::is_same_signedness<T, U>::value && ((t < T{}) != (u < U{})))
        gsl::details::throw_exception(narrowing_error());
    return t;
}

//
// at() - Bounds-checked way of accessing builtin arrays, std::array, std::vector
//
template <class T, std::size_t N>
GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute
constexpr T& at(T (&arr)[N], const index i)
{
    Expects(i >= 0 && i < narrow_cast<index>(N));
    return arr[narrow_cast<std::size_t>(i)];
}

template <class Cont>
GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute
GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute
constexpr auto at(Cont& cont, const index i) -> decltype(cont[cont.size()])
{
    Expects(i >= 0 && i < narrow_cast<index>(cont.size()));
    using size_type = decltype(cont.size());
    return cont[narrow_cast<size_type>(i)];
}

template <class T>
GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute
constexpr T at(const std::initializer_list<T> cont, const index i)
{
    Expects(i >= 0 && i < narrow_cast<index>(cont.size()));
    return *(cont.begin() + i);
}

} // namespace gsl

#if defined(_MSC_VER)
#if _MSC_VER < 1910
#undef constexpr
#pragma pop_macro("constexpr")

#endif // _MSC_VER < 1910

#pragma warning(pop)

#endif // _MSC_VER

#endif // GSL_UTIL_H


#include <stdlib.h>

#include <gsl/gsl_statistics_double.h>
#include <hdf5.h>
#include <hdf5_hl.h>

#include "qdm.h"

qdm_ijk *
qdm_ijk_alloc(
    size_t size1,
    size_t size2,
    size_t size3
)
{
  qdm_ijk *t = malloc(sizeof(qdm_ijk));

  t->size1 = size1;
  t->size2 = size2;
  t->size3 = size3;

  t->data = malloc(sizeof(double) * size1 * size2 * size3);
  t->owner = true;

  return t;
}

qdm_ijk *
qdm_ijk_calloc(
    size_t size1,
    size_t size2,
    size_t size3
)
{
  qdm_ijk *t = malloc(sizeof(qdm_ijk));

  t->size1 = size1;
  t->size2 = size2;
  t->size3 = size3;

  t->data = calloc(sizeof(double), size1 * size2 * size3);
  t->owner = true;

  return t;
}

void
qdm_ijk_free(
    qdm_ijk *t
)
{
  if (t == NULL) {
    return;
  }

  if (t->owner) {
    free(t->data);
  }

  free(t);
}

qdm_ijk_view
qdm_ijk_view_array(
    double *base,
    size_t size1,
    size_t size2,
    size_t size3
)
{
  qdm_ijk_view v = {
    .ijk = {
      .size1 = size1,
      .size2 = size2,
      .size3 = size3,

      .data = base,
      .owner = false,
    },
  };

  return v;
}

gsl_vector_view
qdm_ijk_get_k(
    qdm_ijk *t,
    size_t i,
    size_t j
)
{
  return gsl_vector_view_array_with_stride(
      &t->data[i * t->size2 + j],
      t->size1 * t->size2,
      t->size3
  );
}

gsl_matrix_view
qdm_ijk_get_ij(
    qdm_ijk *t,
    size_t k
)
{
  return gsl_matrix_view_array(
      &t->data[k * t->size1 * t->size2],
      t->size1,
      t->size2
  );
}

double
qdm_ijk_get(
    qdm_ijk *t,
    size_t i,
    size_t j,
    size_t k
)
{
  gsl_matrix_view ij = qdm_ijk_get_ij(t, k);

  return gsl_matrix_get(&ij.matrix, i, j);
}

gsl_matrix *
qdm_ijk_cov(
    qdm_ijk *t
)
{
  size_t d = t->size1 * t->size2;
  size_t dr = 0;

  gsl_matrix *r = gsl_matrix_calloc(d, d);

  for (size_t i0 = 0; i0 < t->size1; i0++) {
    for (size_t j0 = 0; j0 < t->size2; j0++) {
      gsl_vector_view k0 = qdm_ijk_get_k(t, i0, j0);

      /* TODO: When we have time (and if it matters), this is symmetric and we
       * don't need to do a bunch of these if we get a little clever about
       * copying the mirror'd results.
       */
      for (size_t ip = 0; ip < t->size1; ip++) {
        for (size_t jp = 0; jp < t->size2; jp++) {
          gsl_vector_view kp = qdm_ijk_get_k(t, ip, jp);

          double cov = gsl_stats_covariance(
              k0.vector.data,
              k0.vector.stride,
              kp.vector.data,
              kp.vector.stride,
              kp.vector.size
          );

          gsl_matrix_set(r, dr, ip * t->size2 + jp, cov);
        }
      }

      dr++;
    }
  }

  return r;
}

int
qdm_ijk_write(
    hid_t id,
    const char *name,
    const qdm_ijk *t
)
{
  int status = 0;

  hid_t datatype  = -1;
  hid_t dataspace = -1;
  hid_t dataset   = -1;

  if (t == NULL) {
    goto cleanup;
  }

  datatype = H5Tcopy(H5T_NATIVE_DOUBLE);

  status = H5Tset_order(datatype, H5T_ORDER_LE);
  if (status != 0) {
    goto cleanup;
  }

  hsize_t dims[3] = {
    t->size3,
    t->size1,
    t->size2,
  };
  dataspace = H5Screate_simple(3, dims, NULL);
  if (dataspace < 0) {
    status = dataspace;
    goto cleanup;
  }

  dataset = H5Dcreate(id, name, datatype, dataspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  if (dataset < 0) {
    status = dataset;
    goto cleanup;
  }

  status = H5Dwrite(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, t->data);
  if (status != 0) {
    goto cleanup;
  }

cleanup:
  if (dataset >= 0) {
    H5Dclose(dataset);
  }

  if (dataspace >= 0) {
    H5Sclose(dataspace);
  }

  if (datatype >= 0) {
    H5Tclose(datatype);
  }

  H5Oflush(id);

  return status;
}

int
qdm_ijk_read(
    hid_t id,
    const char *name,
    qdm_ijk **t
)
{
  int status = 0;

  int rank = 0;

  status = H5LTget_dataset_ndims(id, name, &rank);
  if (status < 0) {
    return status;
  }

  if (rank != 3) {
    return -1;
  }

  hsize_t dims[3];

  status = H5LTget_dataset_info(id, name, dims, NULL, NULL);
  if (status < 0) {
    return status;
  }

  size_t size3 = dims[0];
  size_t size1 = dims[1];
  size_t size2 = dims[2];

  qdm_ijk *tmp = qdm_ijk_alloc(size1, size2, size3);

  status = H5LTread_dataset_double(id, name, tmp->data);
  if (status < 0) {
    qdm_ijk_free(tmp);

    return status;
  }

  *t = tmp;

  return status;
}


#ifndef UBLAS_LIB_ATLAS_H
#define UBLAS_LIB_ATLAS_H

#include <stdio.h>
#include <ublas_types.h>
#include <cblas.h>

int ubf_init(void **ctx);
int ubf_gemm(void *ctx, ublas_matrix *a, ublas_matrix *b, ublas_matrix *c, double alpha, double beta);
int ubf_free(void *ctx);

#endif


#ifndef _CCS_RNG_H
#define _CCS_RNG_H

#ifdef __cplusplus
extern "C" {
#endif

#include <gsl/gsl_rng.h>

/**
 * @file rng.h
 * CCS rng define random number generators. For now they are wrappers over gsl
 * random number generators.
 */

/**
 * Create a new random number generator using the gsl default type (see
 * gsl_rng_default).
 * @param [out] rng_ret a pointer to the variable that will contain the returned
 *              random number generator
 * @return #CCS_SUCCESS on success
 * @return -#CCS_INVALID_VALUE if \p rng_ret is NULL
 * @return -#CCS_OUT_OF_MEMORY if there was not enough memory to allocate the
 *                             new random number generator
 */
extern ccs_result_t
ccs_rng_create(ccs_rng_t *rng_ret);

/**
 * Create a new random number generator using the provided gsl type (see
 * gsl_rng_type).
 * @param [in] rng_type a pointer to the type of gsl random number generator to
 *                      use.
 * @param [out] rng_ret a pointer to the variable that will contain the returned
 *              random number generator
 * @return #CCS_SUCCESS on success
 * @return -#CCS_INVALID_VALUE if \p rng_ret or \p rng_type are NULL
 * @return -#CCS_OUT_OF_MEMORY if there was not enough memory to allocate the
 *                             new random number generator
 */
extern ccs_result_t
ccs_rng_create_with_type(const gsl_rng_type *rng_type,
                         ccs_rng_t          *rng_ret);

/**
 * Get the gsl type of a random number generator.
 * @param [in] rng
 * @param [out] rng_type_ret a pointer that will contained a pointer to the
 *                           returned gsl random number generator type.
 * @return #CCS_SUCCESS on success
 * @return -#CCS_INVALID_OBJECT if \p rng is not a valid CCS random number
 *                              generator
 * @return -#CCS_INVALID_VALUE if \p rng_type_ret is NULL
 */
extern ccs_result_t
ccs_rng_get_type(ccs_rng_t            rng,
                 const gsl_rng_type **rng_type_ret);

/**
 * Set the seed of a random number generator.
 * @param [in] rng
 * @param [in] seed the seed to use with the random number generator
 * @return #CCS_SUCCESS on success
 * @return -#CCS_INVALID_OBJECT if \p rng is not a valid CCS random number
 *                              generator
 */
extern ccs_result_t
ccs_rng_set_seed(ccs_rng_t         rng,
                 unsigned long int seed);

/**
 * Get a random integer from a random number generator. Integer is contained
 * between the value returned by #ccs_rng_min and #ccs_rng_max, both included.
 * @param [in] rng
 * @param [out] value_ret a pointer to the variable that will contain the
 *                        returned value
 * @return #CCS_SUCCESS on success
 * @return -#CCS_INVALID_OBJECT if \p rng is not a valid CCS random number
 *                              generator
 * @return -#CCS_INVALID_VALUE if \p value_ret is NULL
 */
extern ccs_result_t
ccs_rng_get(ccs_rng_t          rng,
            unsigned long int *value_ret);

/**
 * Get a random floating point value uniformly sampled in the interval [0.0,
 * 1.0).
 * @param [in] rng
 * @param [out] value_ret a pointer to the variable that will contain the
 *                        returned value
 * @return #CCS_SUCCESS on success
 * @return -#CCS_INVALID_OBJECT if \p rng is not a valid CCS random number
 *                              generator
 * @return -#CCS_INVALID_VALUE if \p value_ret is NULL
 */
extern ccs_result_t
ccs_rng_uniform(ccs_rng_t    rng,
                ccs_float_t *value_ret);

/**
 * Get the underlying gsl random number generator.
 * @param [in] rng
 * @param [out] gsl_rng_ret a pointer to the variable that will contain a
 *                          pointer to the underlying random number generator
 * @return #CCS_SUCCESS on success
 * @return -#CCS_INVALID_OBJECT if \p rng is not a valid CCS random number
 *                              generator
 * @return -#CCS_INVALID_VALUE if \p gsl_rng_ret is NULL
 */
extern ccs_result_t
ccs_rng_get_gsl_rng(ccs_rng_t   rng,
                    gsl_rng   **gsl_rng_ret);

/**
 * Get the minimum value that can be returned by #ccs_rng_get.
 * @param [in] rng
 * @param [out] value_ret a pointer to the variable that will contain the
 *                        returned value
 * @return #CCS_SUCCESS on success
 * @return -#CCS_INVALID_OBJECT if \p rng is not a valid CCS random number
 *                              generator
 * @return -#CCS_INVALID_VALUE if \p value_ret is NULL
 */
extern ccs_result_t
ccs_rng_min(ccs_rng_t          rng,
            unsigned long int *value_ret);

/**
 * Get the maximum value that can be returned by #ccs_rng_get.
 * @param [in] rng
 * @param [out] value_ret a pointer to the variable that will contain the
 *                        returned value
 * @return #CCS_SUCCESS on success
 * @return -#CCS_INVALID_OBJECT if \p rng is not a valid CCS random number
 *                              generator
 * @return -#CCS_INVALID_VALUE if \p value_ret is NULL
 */
extern ccs_result_t
ccs_rng_max(ccs_rng_t          rng,
            unsigned long int *value_ret);

#ifdef __cplusplus
}
#endif

#endif //_CCS_RNG_H


//setzt constanten auf
#define EXTERN_VARIABLES
#include <stdio.h>
#include <math.h>
#include <gsl/gsl_sf_bessel.h>
#include "global.h"
#include "utility.h"
#include "steppmethods.h"
#include <gsl/gsl_cblas.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <omp.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

void Set_Target_Length(double * TARGET_LENGTH, double volfrac,  void (*Stepper_Method)(int N,  double  x[N/2] , double v[N/2], double a[N/2], double *t,
					void (*deriv) (double *y, double *ans, double t,int N)),
					void (*Poti_Handle) (double *y, double *ans, double t,int N))
{	int FLAG = 1;
	*TARGET_LENGTH = 0.0;
	if (Stepper_Method == VVerlet_Step_deriv)
	{
		 *TARGET_LENGTH = 0.0;
		printf(" No  Targets\n");
		FLAG = 0;
	}
	//if (Stepper_Method == VVerlet_Step_Target_Circle)
	//{
	//	*TARGET_LENGTH = LATTICE_SPACING * sqrt(volfrac/ M_PI);
	//	printf(" Targets set to hard circles\n");
	//}

	if (Stepper_Method == VVerlet_Step_hardsphere_reflect)
	{
		 *TARGET_LENGTH = LATTICE_SPACING * sqrt(volfrac/ M_PI);
		printf(" Targets set to hard circles\n");
		FLAG = 0;
	}

	if (Stepper_Method == VVerlet_Step_hardsphere_reflect_Kupf)
	{
		 *TARGET_LENGTH = LATTICE_SPACING * sqrt(volfrac/ M_PI);
		printf(" Targets set to hard circles Kupferman \n");
		FLAG = 0;
	}

	if (Stepper_Method == VVerlet_Step_hardsphere_reflect_all)
	{
		 *TARGET_LENGTH = LATTICE_SPACING * sqrt(volfrac/ M_PI);
		printf(" Targets set to hard circles, reflect ALL particles\n");
		FLAG = 0;
	}

	if (Stepper_Method == VVerlet_Step_deriv_Kupf)
	{
		*TARGET_LENGTH = 0.0;
		printf(" No  Targets\n, Kupfermann bath");
		FLAG = 0;
	}

	if (Stepper_Method == VVerlet_Step_deriv_Box)
	{
		 //*TARGET_LENGTH = LATTICE_SPACING * sqrt(volfrac/ M_PI/5.0);
		//printf(" Targets set to soft Yukawa circles in 3 per pore corner\n");
		*TARGET_LENGTH = 0;
		FLAG = 0;
		if (DIM > 1)
		{
			printf("wrong Dimension, please switch to 1d");
			exit(1);
		}
		if (volfrac > 0.0)
		{
			printf("no need for volume fractions >0 \n");
			exit(1);
		}
		printf("box set up with hard wall \n");
	}
	if(FLAG)
	{
		printf("Kein bekannter Stepper in global, cannot set tarhet length!\n");
		exit(1);
	}
}

void Constants(){
// call to setup the bathh parameters and give rudimentary output for the run
	int i;
	OUTPUT_FLAG = 1;
	printf ("OSSZI = %d  DIM =%d  ORDER =%d  \n", OSSZI, DIM, ORDER);
	printf ("KBOLTZ = %e \nTIME_STEPS =%f  \nTIME_END =%f  \n", KBOLTZ, TIME_STEPS, TIME_END);
	for(i=0;i<OSSZI;i++){
		coupling[i] = 0.0;
	}
	for(i=0;i<OSSZI;i++){
		ommega[i] = 0.0;
	}
	for(i=0;i<OSSZI;i++){
		massq[i] = 1.0;
	}
	for(i=0;i<ORDER;i++){
		y[i] = 1.0;
	}
	printf("ALPHA = %1.2E  GAMMA = %1.2E  mass  = %1.2E \n", ALPHA, GAMMA, mass);
	printf("NUMBER_TO_INF = %d  \nTIME_ARRIVAL =%1.2E  \n ", NUMBER_TO_INF, TIME_ARRIVAL);
	printf("Startbedingungen = ");
	printf("%s",LABEL);
	printf("\n");
	if (!(OMMEGA_READ_BINARY))
	{
		// random Number Preparation for Taus GSL genrator
		gsl_rng * r;
		const gsl_rng_type * T;
		gsl_rng_env_setup();
		T = gsl_rng_taus2;
		r = gsl_rng_alloc (T);
		gsl_rng_set(r, time(NULL)); // Seed with time
		// Random number end //
	    Ommega_Setup(r);
	    gsl_rng_free (r);
	}else	// read in from ommega.dat
	{
		FILE *fp;
		fp = fopen("ommega.dat", "r");
		for (i=0;i<OSSZI;i++)
		{
			if (fscanf(fp, "%lf", &ommega[i]) != 1)
			{
	            printf("ERROT READING ommega.dat");
	            break;
        	}
		}
		fclose(fp);
		printf("read in ommega.dat\n");
	}
	if(VIRTUAL_FLAG) printf("Virtuelles Teilchen benutzt, ohne Badrückkopplung");
  	Gamma_Setup(coupling);

  	double ommega_max = ommega[cblas_idamax(OSSZI, ommega, 1)];
  	double coupling_max = coupling[cblas_idamax(OSSZI, coupling, 1)];
  	printf("MAX coupling =  %1.2e\n",coupling_max);
  	if(!(MAXSTEPSIZE_BINARY))
	{
  		MAXSTEPSIZE = (2*M_PI/ommega[cblas_idamax(OSSZI, ommega, 1)] * MAXSTEPSIZEMULT);
  		printf("MAXSTEPSIZE =  %1.2e set according to smallest period times %3.3e\n",MAXSTEPSIZE, MAXSTEPSIZEMULT);
  	}else
  	{
  		MAXSTEPSIZE = (MAXSTEPSIZEMULT / (double) OSSZI);
  		printf("MAXSTEPSIZE =  %1.2e set according to OSSZInr times Stepsize dt = %3.3e\n",MAXSTEPSIZE, MAXSTEPSIZEMULT);
  	}
  	Sum_Method =  SUMMATION_METHOD;
  	Stepper_Method = STEPPER_METHOD;
  	Poti_Handle = POTI_HANDLE;

  	int m = ipow(NUMBER_TO_INF,DIM); int n = DIM;
  	// Setze Gitter auf auf globale Matrix POSITIONS
	POSITIONS = (double **) malloc(m * sizeof(int *));
	POSITIONS[0] = (double *) malloc(m* n * sizeof(int));
	for (i=1; i<m; i++) POSITIONS[i] = POSITIONS[0] + n *i;
	vec_zero_i(lattice_position,DIM);
	printf("lattice allocated, current lattice cell set to zero\n");
	TARGET_CONTACT = 0;
	OUTPUT_FLAG = 0; // subsequent setup calls should not display text
	printf("Running on %d threads, optimally DIM = %d\n", THREADS, DIM);

	// set masses for chosen type of coupling
	if( !(KUPF_BINARY) )
	{
		for (int osi = 0; osi < OSSZI; osi ++)
		{
			massq[osi] 		= 1.0;
		}
		printf("Set all masses équal\n");
	}else
	{
		for (int osi = 0; osi < OSSZI; osi ++)
		{
			massq[osi] 		= coupling[osi] / ommega[osi] / ommega[osi];

		}
		printf("Set all masses according to k = m w^2l\n");
	}
	// Setup masses of bath for simpsons rule, split ends, odd and even parts

	if (SIMPSON_BINARY)
	{
		for (int osi = 1; osi < OSSZI/2-1; osi ++)
		{
			massq[osi * 2] 		*= 2.0/3.0;
			massq[osi * 2 + 1] 	*= 4.0/3.0;
		}
		massq[0] 		*= 1.0/3.0;;
		massq[OSSZI-1] 	*= 1.0/3.0;

		if (KUPF_BINARY)
		{
			for (int osi = 0; osi < OSSZI; osi ++)
			{
				coupling[osi] = massq[osi] * ommega[osi] * ommega[osi];
			}
		}
	}
}


/* interpolation/gsl_interp.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2004 Gerard Jungman
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 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.
 */

/* Author:  G. Jungman
 */
#ifndef __GSL_INTERP_H__
#define __GSL_INTERP_H__
#include <stdlib.h>
#include <gsl/gsl_inline.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

/* evaluation accelerator */
typedef struct {
  size_t  cache;        /* cache of index   */
  size_t  miss_count;   /* keep statistics  */
  size_t  hit_count;
}
gsl_interp_accel;

/* interpolation object type */
typedef struct {
  const char * name;
  unsigned int min_size;
  void *  (*alloc) (size_t size);
  int     (*init)    (void *, const double xa[], const double ya[], size_t size);
  int     (*eval)    (const void *, const double xa[], const double ya[], size_t size, double x, gsl_interp_accel *, double * y);
  int     (*eval_deriv)  (const void *, const double xa[], const double ya[], size_t size, double x, gsl_interp_accel *, double * y_p);
  int     (*eval_deriv2) (const void *, const double xa[], const double ya[], size_t size, double x, gsl_interp_accel *, double * y_pp);
  int     (*eval_integ)  (const void *, const double xa[], const double ya[], size_t size, gsl_interp_accel *, double a, double b, double * result);
  void    (*free)         (void *);

} gsl_interp_type;

/* general interpolation object */
typedef struct {
  const gsl_interp_type * type;
  double  xmin;
  double  xmax;
  size_t  size;
  void * state;
} gsl_interp;

/* available types */
GSL_VAR const gsl_interp_type * gsl_interp_linear;
GSL_VAR const gsl_interp_type * gsl_interp_polynomial;
GSL_VAR const gsl_interp_type * gsl_interp_cspline;
GSL_VAR const gsl_interp_type * gsl_interp_cspline_periodic;
GSL_VAR const gsl_interp_type * gsl_interp_akima;
GSL_VAR const gsl_interp_type * gsl_interp_akima_periodic;

gsl_interp_accel *
gsl_interp_accel_alloc(void);

int
gsl_interp_accel_reset (gsl_interp_accel * a);

void
gsl_interp_accel_free(gsl_interp_accel * a);

gsl_interp *
gsl_interp_alloc(const gsl_interp_type * T, size_t n);

int
gsl_interp_init(gsl_interp * obj, const double xa[], const double ya[], size_t size);

const char * gsl_interp_name(const gsl_interp * interp);
unsigned int gsl_interp_min_size(const gsl_interp * interp);

int
gsl_interp_eval_e(const gsl_interp * obj,
                  const double xa[], const double ya[], double x,
                  gsl_interp_accel * a, double * y);

double
gsl_interp_eval(const gsl_interp * obj,
                const double xa[], const double ya[], double x,
                gsl_interp_accel * a);

int
gsl_interp_eval_deriv_e(const gsl_interp * obj,
                        const double xa[], const double ya[], double x,
                        gsl_interp_accel * a,
                        double * d);

double
gsl_interp_eval_deriv(const gsl_interp * obj,
                      const double xa[], const double ya[], double x,
                      gsl_interp_accel * a);

int
gsl_interp_eval_deriv2_e(const gsl_interp * obj,
                         const double xa[], const double ya[], double x,
                         gsl_interp_accel * a,
                         double * d2);

double
gsl_interp_eval_deriv2(const gsl_interp * obj,
                       const double xa[], const double ya[], double x,
                       gsl_interp_accel * a);

int
gsl_interp_eval_integ_e(const gsl_interp * obj,
                        const double xa[], const double ya[],
                        double a, double b,
                        gsl_interp_accel * acc,
                        double * result);

double
gsl_interp_eval_integ(const gsl_interp * obj,
                      const double xa[], const double ya[],
                      double a, double b,
                      gsl_interp_accel * acc);

void
gsl_interp_free(gsl_interp * interp);

INLINE_DECL size_t
gsl_interp_bsearch(const double x_array[], double x,
                   size_t index_lo, size_t index_hi);

#ifdef HAVE_INLINE

/* Perform a binary search of an array of values.
 *
 * The parameters index_lo and index_hi provide an initial bracket,
 * and it is assumed that index_lo < index_hi. The resulting index
 * is guaranteed to be strictly less than index_hi and greater than
 * or equal to index_lo, so that the implicit bracket [index, index+1]
 * always corresponds to a region within the implicit value range of
 * the value array.
 *
 * Note that this means the relationship of 'x' to x_array[index]
 * and x_array[index+1] depends on the result region, i.e. the
 * behaviour at the boundaries may not correspond to what you
 * expect. We have the following complete specification of the
 * behaviour.
 * Suppose the input is x_array[] = { x0, x1, ..., xN }
 *    if ( x == x0 )           then  index == 0
 *    if ( x > x0 && x <= x1 ) then  index == 0, and sim. for other interior pts
 *    if ( x == xN )           then  index == N-1
 *    if ( x > xN )            then  index == N-1
 *    if ( x < x0 )            then  index == 0
 */

INLINE_FUN size_t
gsl_interp_bsearch(const double x_array[], double x,
                   size_t index_lo, size_t index_hi)
{
  size_t ilo = index_lo;
  size_t ihi = index_hi;
  while(ihi > ilo + 1) {
    size_t i = (ihi + ilo)/2;
    if(x_array[i] > x)
      ihi = i;
    else
      ilo = i;
  }

  return ilo;
}
#endif

INLINE_DECL size_t
gsl_interp_accel_find(gsl_interp_accel * a, const double x_array[], size_t size, double x);

#ifdef HAVE_INLINE
INLINE_FUN size_t
gsl_interp_accel_find(gsl_interp_accel * a, const double xa[], size_t len, double x)
{
  size_t x_index = a->cache;

  if(x < xa[x_index]) {
    a->miss_count++;
    a->cache = gsl_interp_bsearch(xa, x, 0, x_index);
  }
  else if(x >= xa[x_index + 1]) {
    a->miss_count++;
    a->cache = gsl_interp_bsearch(xa, x, x_index, len-1);
  }
  else {
    a->hit_count++;
  }

  return a->cache;
}
#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_INTERP_H__ */


/*
Copyright (c) 2017, LibMars Developers.
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 the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

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

#ifndef MARS_HEADER_H
#define MARS_HEADER_H 1

#include <iostream>
#include <cstring>
#include <math.h>
#include <inttypes.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include <map>
#include <iomanip>
#include <iterator>
#include <chrono>
#include <typeinfo>
#include <type_traits>
#include <complex>
#include <vector>
#include <algorithm>
#include <assert.h>
#include <cassert>
#include <csignal>
#include <ctime>
#include <stdarg.h>
#include <stdexcept>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <list>
#include <set>
#include <utility>
#include <ctype.h>
#include <deque>
#include <initializer_list>
#include <unordered_map>
#include <unordered_set>
#include <cstdint>
#include <limits.h>

#include <new>
#include <locale>

#define UNW_LOCAL_ONLY

//#include <thread>
#include <cxxabi.h>

#include <dirent.h>
#include <errno.h>
#include <exception>
#include <execinfo.h>
#include <fcntl.h>

#include <libgen.h>
#include <libunwind.h>

#include <regex>
#include <semaphore.h>

#include <signal.h>

#include <sys/mman.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/resource.h>

#include <unistd.h>

#include <gsl/gsl_cdf.h>
#include <gsl/gsl_histogram.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_sf.h>
#include <gsl/gsl_statistics.h>

#include <fftw3.h>

#include <boost/range/irange.hpp>
#include <boost/functional/hash.hpp> /* hash std::pair<long,long> */

#define MARS_COLOR_MODE   1
#ifdef  MARS_COLOR_MODE

#define MARS_COLOR_RED     "\033[22;31m"
#define MARS_COLOR_GREEN   "\033[22;32m"
#define MARS_COLOR_YELLOW  "\033[22;33m"
#define MARS_COLOR_BLUE    "\033[22;34m"
#define MARS_COLOR_MAGENTA "\033[22;35m"
//#define MARS_COLOR_CYAN    "\033[22;36m"
#define MARS_COLOR_CYAN    "\e[1;36m" // bright cyan
#define MARS_COLOR_WHITE   "\e[1;37m"

#define codef          "\033[0;22m"
#else
#define MARS_COLOR_RED     ""
#define MARS_COLOR_GREEN   ""
#define MARS_COLOR_YELLOW  ""
#define MARS_COLOR_BLUE    ""
#define MARS_COLOR_MAGENTA ""
#define MARS_COLOR_CYAN    ""

#define CODEF         ""
#endif

#define COFUNC   MARS_COLOR_CYAN
#define COFILE   MARS_COLOR_CYAN
#define COFOLDER MARS_COLOR_BLUE
#define COVAR    MARS_COLOR_RED
#define COVAL    MARS_COLOR_WHITE
#define COERR    MARS_COLOR_RED
#define COWARN   MARS_COLOR_YELLOW

#if __STDC_VERSION__ < 199901L
#if __GNUC__ >= 2
#define __FCN__ __PRETTY_FUNCTION__
#else
#define __FCN__ __func__
#endif
#endif

#endif


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

#ifndef __GSL_ERRNO_H__
#define __GSL_ERRNO_H__

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

enum {
  GSL_SUCCESS  = 0,
  GSL_FAILURE  = -1,
  GSL_CONTINUE = -2,  /* iteration has not converged */
  GSL_EDOM     = 1,   /* input domain error, e.g sqrt(-1) */
  GSL_ERANGE   = 2,   /* output range error, e.g. exp(1e100) */
  GSL_EFAULT   = 3,   /* invalid pointer */
  GSL_EINVAL   = 4,   /* invalid argument supplied by user */
  GSL_EFAILED  = 5,   /* generic failure */
  GSL_EFACTOR  = 6,   /* factorization failed */
  GSL_ESANITY  = 7,   /* sanity check failed - shouldn't happen */
  GSL_ENOMEM   = 8,   /* malloc failed */
  GSL_EBADFUNC = 9,   /* problem with user-supplied function */
  GSL_ERUNAWAY = 10,  /* iterative process is out of control */
  GSL_EMAXITER = 11,  /* exceeded max number of iterations */
  GSL_EZERODIV = 12,  /* tried to divide by zero */
  GSL_EBADTOL  = 13,  /* user specified an invalid tolerance */
  GSL_ETOL     = 14,  /* failed to reach the specified tolerance */
  GSL_EUNDRFLW = 15,  /* underflow */
  GSL_EOVRFLW  = 16,  /* overflow  */
  GSL_ELOSS    = 17,  /* loss of accuracy */
  GSL_EROUND   = 18,  /* failed because of roundoff error */
  GSL_EBADLEN  = 19,  /* matrix, vector lengths are not conformant */
  GSL_ENOTSQR  = 20,  /* matrix not square */
  GSL_ESING    = 21,  /* apparent singularity detected */
  GSL_EDIVERGE = 22,  /* integral or series is divergent */
  GSL_EUNSUP   = 23,  /* requested feature is not supported by the hardware */
  GSL_EUNIMPL  = 24,  /* requested feature not (yet) implemented */
  GSL_ECACHE   = 25,  /* cache limit exceeded */
  GSL_ETABLE   = 26,  /* table limit exceeded */
  GSL_ENOPROG  = 27,  /* iteration is not making progress towards solution */
  GSL_ENOPROGJ = 28,  /* jacobian evaluations are not improving the solution */
  GSL_ETOLF    = 29,  /* cannot reach the specified tolerance in F */
  GSL_ETOLX    = 30,  /* cannot reach the specified tolerance in X */
  GSL_ETOLG    = 31,  /* cannot reach the specified tolerance in gradient */
  GSL_EOF      = 32   /* end of file */
} ;

void gsl_error (const char * reason, const char * file, int line,
                int gsl_errno);

void gsl_stream_printf (const char *label, const char *file,
                        int line, const char *reason);

const char * gsl_strerror (const int gsl_errno);

typedef void gsl_error_handler_t (const char * reason, const char * file,
                                  int line, int gsl_errno);

typedef void gsl_stream_handler_t (const char * label, const char * file,
                                   int line, const char * reason);

gsl_error_handler_t *
gsl_set_error_handler (gsl_error_handler_t * new_handler);

gsl_error_handler_t *
gsl_set_error_handler_off (void);

gsl_stream_handler_t *
gsl_set_stream_handler (gsl_stream_handler_t * new_handler);

FILE * gsl_set_stream (FILE * new_stream);

/* GSL_ERROR: call the error handler, and return the error code */

#define GSL_ERROR(reason, gsl_errno) \
       do { \
       gsl_error (reason, __FILE__, __LINE__, gsl_errno) ; \
       return gsl_errno ; \
       } while (0)

/* GSL_ERROR_VAL: call the error handler, and return the given value */

#define GSL_ERROR_VAL(reason, gsl_errno, value) \
       do { \
       gsl_error (reason, __FILE__, __LINE__, gsl_errno) ; \
       return value ; \
       } while (0)

/* GSL_ERROR_VOID: call the error handler, and then return
   (for void functions which still need to generate an error) */

#define GSL_ERROR_VOID(reason, gsl_errno) \
       do { \
       gsl_error (reason, __FILE__, __LINE__, gsl_errno) ; \
       return ; \
       } while (0)

/* GSL_ERROR_NULL suitable for out-of-memory conditions */

#define GSL_ERROR_NULL(reason, gsl_errno) GSL_ERROR_VAL(reason, gsl_errno, 0)

/* Sometimes you have several status results returned from
 * function calls and you want to combine them in some sensible
 * way. You cannot produce a "total" status condition, but you can
 * pick one from a set of conditions based on an implied hierarchy.
 *
 * In other words:
 *    you have: status_a, status_b, ...
 *    you want: status = (status_a if it is bad, or status_b if it is bad,...)
 *
 * In this example you consider status_a to be more important and
 * it is checked first, followed by the others in the order specified.
 *
 * Here are some dumb macros to do this.
 */
#define GSL_ERROR_SELECT_2(a,b)       ((a) != GSL_SUCCESS ? (a) : ((b) != GSL_SUCCESS ? (b) : GSL_SUCCESS))
#define GSL_ERROR_SELECT_3(a,b,c)     ((a) != GSL_SUCCESS ? (a) : GSL_ERROR_SELECT_2(b,c))
#define GSL_ERROR_SELECT_4(a,b,c,d)   ((a) != GSL_SUCCESS ? (a) : GSL_ERROR_SELECT_3(b,c,d))
#define GSL_ERROR_SELECT_5(a,b,c,d,e) ((a) != GSL_SUCCESS ? (a) : GSL_ERROR_SELECT_4(b,c,d,e))

#define GSL_STATUS_UPDATE(sp, s) do { if ((s) != GSL_SUCCESS) *(sp) = (s);} while(0)

__END_DECLS

#endif /* __GSL_ERRNO_H__ */