/* vector/gsl_vector_complex_long_double.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_COMPLEX_LONG_DOUBLE_H__
#define __GSL_VECTOR_COMPLEX_LONG_DOUBLE_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_complex.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_long_double.h>
#include <gsl/gsl_vector_complex.h>
#include <gsl/gsl_block_complex_long_double.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;
  long double *data;
  gsl_block_complex_long_double *block;
  int owner;
} gsl_vector_complex_long_double;

typedef struct
{
  gsl_vector_complex_long_double vector;
} _gsl_vector_complex_long_double_view;

typedef _gsl_vector_complex_long_double_view gsl_vector_complex_long_double_view;

typedef struct
{
  gsl_vector_complex_long_double vector;
} _gsl_vector_complex_long_double_const_view;

typedef const _gsl_vector_complex_long_double_const_view gsl_vector_complex_long_double_const_view;

/* Allocation */

GSL_FUN gsl_vector_complex_long_double *gsl_vector_complex_long_double_alloc (const size_t n);
GSL_FUN gsl_vector_complex_long_double *gsl_vector_complex_long_double_calloc (const size_t n);

GSL_FUN gsl_vector_complex_long_double *
gsl_vector_complex_long_double_alloc_from_block (gsl_block_complex_long_double * b,
                                           const size_t offset,
                                           const size_t n,
                                           const size_t stride);

GSL_FUN gsl_vector_complex_long_double *
gsl_vector_complex_long_double_alloc_from_vector (gsl_vector_complex_long_double * v,
                                             const size_t offset,
                                             const size_t n,
                                             const size_t stride);

GSL_FUN void gsl_vector_complex_long_double_free (gsl_vector_complex_long_double * v);

/* Views */

GSL_FUN _gsl_vector_complex_long_double_view
gsl_vector_complex_long_double_view_array (long double *base,
                                     size_t n);

GSL_FUN _gsl_vector_complex_long_double_view
gsl_vector_complex_long_double_view_array_with_stride (long double *base,
                                                 size_t stride,
                                                 size_t n);

GSL_FUN _gsl_vector_complex_long_double_const_view
gsl_vector_complex_long_double_const_view_array (const long double *base,
                                           size_t n);

GSL_FUN _gsl_vector_complex_long_double_const_view
gsl_vector_complex_long_double_const_view_array_with_stride (const long double *base,
                                                       size_t stride,
                                                       size_t n);

GSL_FUN _gsl_vector_complex_long_double_view
gsl_vector_complex_long_double_subvector (gsl_vector_complex_long_double *base,
                                         size_t i,
                                         size_t n);

GSL_FUN _gsl_vector_complex_long_double_view
gsl_vector_complex_long_double_subvector_with_stride (gsl_vector_complex_long_double *v,
                                                size_t i,
                                                size_t stride,
                                                size_t n);

GSL_FUN _gsl_vector_complex_long_double_const_view
gsl_vector_complex_long_double_const_subvector (const gsl_vector_complex_long_double *base,
                                               size_t i,
                                               size_t n);

GSL_FUN _gsl_vector_complex_long_double_const_view
gsl_vector_complex_long_double_const_subvector_with_stride (const gsl_vector_complex_long_double *v,
                                                      size_t i,
                                                      size_t stride,
                                                      size_t n);

GSL_FUN _gsl_vector_long_double_view
gsl_vector_complex_long_double_real (gsl_vector_complex_long_double *v);

GSL_FUN _gsl_vector_long_double_view
gsl_vector_complex_long_double_imag (gsl_vector_complex_long_double *v);

GSL_FUN _gsl_vector_long_double_const_view
gsl_vector_complex_long_double_const_real (const gsl_vector_complex_long_double *v);

GSL_FUN _gsl_vector_long_double_const_view
gsl_vector_complex_long_double_const_imag (const gsl_vector_complex_long_double *v);

/* Operations */

GSL_FUN void gsl_vector_complex_long_double_set_zero (gsl_vector_complex_long_double * v);
GSL_FUN void gsl_vector_complex_long_double_set_all (gsl_vector_complex_long_double * v,
                                       gsl_complex_long_double z);
GSL_FUN int gsl_vector_complex_long_double_set_basis (gsl_vector_complex_long_double * v, size_t i);

GSL_FUN int gsl_vector_complex_long_double_fread (FILE * stream,
                                    gsl_vector_complex_long_double * v);
GSL_FUN int gsl_vector_complex_long_double_fwrite (FILE * stream,
                                     const gsl_vector_complex_long_double * v);
GSL_FUN int gsl_vector_complex_long_double_fscanf (FILE * stream,
                                     gsl_vector_complex_long_double * v);
GSL_FUN int gsl_vector_complex_long_double_fprintf (FILE * stream,
                                      const gsl_vector_complex_long_double * v,
                                      const char *format);

GSL_FUN int gsl_vector_complex_long_double_memcpy (gsl_vector_complex_long_double * dest, const gsl_vector_complex_long_double * src);

GSL_FUN int gsl_vector_complex_long_double_reverse (gsl_vector_complex_long_double * v);

GSL_FUN int gsl_vector_complex_long_double_swap (gsl_vector_complex_long_double * v, gsl_vector_complex_long_double * w);
GSL_FUN int gsl_vector_complex_long_double_swap_elements (gsl_vector_complex_long_double * v, const size_t i, const size_t j);

GSL_FUN int gsl_vector_complex_long_double_equal (const gsl_vector_complex_long_double * u,
                                    const gsl_vector_complex_long_double * v);

GSL_FUN int gsl_vector_complex_long_double_isnull (const gsl_vector_complex_long_double * v);
GSL_FUN int gsl_vector_complex_long_double_ispos (const gsl_vector_complex_long_double * v);
GSL_FUN int gsl_vector_complex_long_double_isneg (const gsl_vector_complex_long_double * v);
GSL_FUN int gsl_vector_complex_long_double_isnonneg (const gsl_vector_complex_long_double * v);

GSL_FUN int gsl_vector_complex_long_double_add (gsl_vector_complex_long_double * a, const gsl_vector_complex_long_double * b);
GSL_FUN int gsl_vector_complex_long_double_sub (gsl_vector_complex_long_double * a, const gsl_vector_complex_long_double * b);
GSL_FUN int gsl_vector_complex_long_double_mul (gsl_vector_complex_long_double * a, const gsl_vector_complex_long_double * b);
GSL_FUN int gsl_vector_complex_long_double_div (gsl_vector_complex_long_double * a, const gsl_vector_complex_long_double * b);
GSL_FUN int gsl_vector_complex_long_double_scale (gsl_vector_complex_long_double * a, const gsl_complex_long_double x);
GSL_FUN int gsl_vector_complex_long_double_add_constant (gsl_vector_complex_long_double * a, const gsl_complex_long_double x);
GSL_FUN int gsl_vector_complex_long_double_axpby (const gsl_complex_long_double alpha, const gsl_vector_complex_long_double * x, const gsl_complex_long_double beta, gsl_vector_complex_long_double * y);

GSL_FUN INLINE_DECL gsl_complex_long_double gsl_vector_complex_long_double_get (const gsl_vector_complex_long_double * v, const size_t i);
GSL_FUN INLINE_DECL void gsl_vector_complex_long_double_set (gsl_vector_complex_long_double * v, const size_t i, gsl_complex_long_double z);
GSL_FUN INLINE_DECL gsl_complex_long_double *gsl_vector_complex_long_double_ptr (gsl_vector_complex_long_double * v, const size_t i);
GSL_FUN INLINE_DECL const gsl_complex_long_double *gsl_vector_complex_long_double_const_ptr (const gsl_vector_complex_long_double * v, const size_t i);

#ifdef HAVE_INLINE

INLINE_FUN
gsl_complex_long_double
gsl_vector_complex_long_double_get (const gsl_vector_complex_long_double * v,
                              const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      gsl_complex_long_double zero = {{0, 0}};
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, zero);
    }
#endif
  return *GSL_COMPLEX_LONG_DOUBLE_AT (v, i);
}

INLINE_FUN
void
gsl_vector_complex_long_double_set (gsl_vector_complex_long_double * v,
                              const size_t i, gsl_complex_long_double z)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  *GSL_COMPLEX_LONG_DOUBLE_AT (v, i) = z;
}

INLINE_FUN
gsl_complex_long_double *
gsl_vector_complex_long_double_ptr (gsl_vector_complex_long_double * 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 GSL_COMPLEX_LONG_DOUBLE_AT (v, i);
}

INLINE_FUN
const gsl_complex_long_double *
gsl_vector_complex_long_double_const_ptr (const gsl_vector_complex_long_double * 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 GSL_COMPLEX_LONG_DOUBLE_AT (v, i);
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_COMPLEX_LONG_DOUBLE_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 <petscksp.h>
#include <petscsnes.h>

#include <petscversion.h>
#if ( (PETSC_VERSION_MAJOR >= 3) && (PETSC_VERSION_MINOR >=3) )
  #if (PETSC_VERSION_MINOR >=6)
     #include "petsc/private/vecimpl.h"
     #include "petsc/private/matimpl.h"
     #include "petsc/private/pcimpl.h"
     #include "petsc/private/kspimpl.h"
     #include "petsc/private/snesimpl.h"
  #else
     #include "petsc-private/vecimpl.h"
     #include "petsc-private/matimpl.h"
     #include "petsc-private/pcimpl.h"
     #include "petsc-private/kspimpl.h"
     #include "petsc-private/snesimpl.h"
  #endif
#else
  #include "private/vecimpl.h"
  #include "private/matimpl.h"
  #include "private/pcimpl.h"
  #include "private/kspimpl.h"
  #include "private/snesimpl.h"
#endif

#include "common-driver-utils.h"

#define BSSCR_report_op(op,v,i,func_name) \
  if((op)) { PetscViewerASCIIPrintf((v), "[%.2d] %s: valid operation \n", i, func_name ); } \
  else { PetscViewerASCIIPrintf((v),     "[%.2d] %s: OPERATION NOT DEFINED \n", i, func_name ); } \

PetscErrorCode BSSCR_MatListOperations( Mat A, PetscViewer v )
{
	MatOps ops = A->ops;
	MatType type;
	PetscInt i;

	MatGetType( A, &type );
	PetscViewerASCIIPrintf(v, "Operations available for MatType: %s \n", type );

	PetscViewerASCIIPushTab( v );

	i=0;
	PetscViewerASCIIPrintf(v, "------------------------------------------------\n");
	BSSCR_report_op( ops->setvalues, v,  i++, "MatSetValues" );
	BSSCR_report_op( ops->getrow, v,     i++, "MatSetValues" );
	BSSCR_report_op( ops->restorerow, v, i++, "MatRestoreRow" );
	BSSCR_report_op( ops->mult, v,       i++, "MatMult" );
	BSSCR_report_op( ops->multadd, v,    i++, "MatMultAdd" );

	BSSCR_report_op( ops->multtranspose, v,    i++, "MatMultTranspose" );
	BSSCR_report_op( ops->multtransposeadd, v, i++, "MatMultTransposeAdd" );
	BSSCR_report_op( ops->solve, v,            i++, "MatSolve" );
	BSSCR_report_op( ops->solveadd, v,         i++, "MatSolveAdd" );
	BSSCR_report_op( ops->solvetranspose, v,   i++, "MatSolveTranspose" );

	PetscViewerASCIIPrintf(v, "------------------------------------------------\n");
	BSSCR_report_op( ops->solvetransposeadd, v,  i++, "MatSolveTransposeAdd" );
	BSSCR_report_op( ops->lufactor, v,           i++, "MatLUFactor" );
	BSSCR_report_op( ops->choleskyfactor, v,     i++, "MatCholeskyFactor" );
#if( ( PETSC_VERSION_MAJOR >= 3 ) && ( PETSC_VERSION_MINOR > 0 ) )
	BSSCR_report_op( ops->sor, v,                i++, "MatSor" );
#else
	BSSCR_report_op( ops->relax, v,              i++, "MatRelax" );
#endif
	BSSCR_report_op( ops->transpose, v,          i++, "MatTranspose" );

	BSSCR_report_op( ops->getinfo, v,       i++, "MatGetInfo" );
	BSSCR_report_op( ops->equal, v,         i++, "MatEqual" );
	BSSCR_report_op( ops->getdiagonal, v,   i++, "MatGetDiagonal" );
	BSSCR_report_op( ops->diagonalscale, v, i++, "MatDiagonalScale" );
	BSSCR_report_op( ops->norm, v,          i++, "MatNorm" );

	PetscViewerASCIIPrintf(v, "------------------------------------------------\n");
	BSSCR_report_op( ops->assemblybegin, v, i++, "MatAssemblyBegin" );
	BSSCR_report_op( ops->assemblyend, v,   i++, "MatAssemblyEnd" );
#if( ( ( PETSC_VERSION_MAJOR == 3 ) && ( PETSC_VERSION_MINOR < 1 ) ) || ( PETSC_VERSION_MAJOR <= 2 ) )
	BSSCR_report_op( ops->compress, v,      i++, "MatCompress" );
#endif
	BSSCR_report_op( ops->setoption, v,     i++, "MatSetOption" );
	BSSCR_report_op( ops->zeroentries, v,   i++, "MatZeroEntries" );

	BSSCR_report_op( ops->zerorows, v,               i++, "Stg_MatZeroRows" );
	BSSCR_report_op( ops->lufactorsymbolic, v,       i++, "MatLUFactorSymbolic" );
	BSSCR_report_op( ops->lufactornumeric, v,        i++, "MatLUFactorNumeric" );
	BSSCR_report_op( ops->choleskyfactorsymbolic, v, i++, "MatCholeskyFactorSymbolic" );
	BSSCR_report_op( ops->choleskyfactornumeric, v,  i++, "MatCholeskyFactorNumeric" );

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

	BSSCR_report_op( ops->ilufactorsymbolic, v,  i++, "MatILUFactorSymbolic" );
	BSSCR_report_op( ops->iccfactorsymbolic, v,  i++, "MatICCFactorSymbolic" );

	BSSCR_report_op( ops->duplicate, v,     i++, "MatDuplicate" );
	BSSCR_report_op( ops->forwardsolve, v,  i++, "MatForwardSolve" );
	BSSCR_report_op( ops->backwardsolve, v, i++, "MatBackwardSolve" );
	BSSCR_report_op( ops->ilufactor, v,     i++, "MatILUFactor" );
	BSSCR_report_op( ops->iccfactor, v,     i++, "MatICCFactor" );

	PetscViewerASCIIPrintf(v, "------------------------------------------------\n");
	BSSCR_report_op( ops->axpy, v,            i++, "MatAXPY" );

#if( (PETSC_VERSION_MAJOR == 3) &&  (PETSC_VERSION_MINOR > 7) )
    BSSCR_report_op( ops->createsubmatrices, v,  i++, "MatCreateSubMatrices" );
#else
    BSSCR_report_op( ops->getsubmatrices, v,  i++, "MatGetSubMatrices" );
#endif

    BSSCR_report_op( ops->increaseoverlap, v, i++, "MatIncreaseOverlap" );
	BSSCR_report_op( ops->getvalues, v,       i++, "MatGetValues" );
	BSSCR_report_op( ops->copy, v,            i++, "MatCopy" );

	BSSCR_report_op( ops->getrowmax, v,   i++, "MatGetRowMax" );
	BSSCR_report_op( ops->scale, v,       i++, "MatScale" );
	BSSCR_report_op( ops->shift, v,       i++, "MatShift" );
	BSSCR_report_op( ops->diagonalset, v, i++, "MatDiagonalSet" );
#if( ( ( PETSC_VERSION_MAJOR == 3 ) && ( PETSC_VERSION_MINOR < 1 ) ) || ( PETSC_VERSION_MAJOR <= 2 ) )
	BSSCR_report_op( ops->iludtfactor, v, i++, "MatILUDTFactor" );
#endif

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

	BSSCR_report_op( ops->getrowij, v,        i++, "MatGetRowIJ" );
	BSSCR_report_op( ops->restorerowij, v,    i++, "MatRestoreRowIJ" );
	BSSCR_report_op( ops->getcolumnij, v,     i++, "MatGetColumnIJ" );
	BSSCR_report_op( ops->restorecolumnij, v, i++, "MatRestoreColumnIJ" );

	BSSCR_report_op( ops->fdcoloringcreate, v, i++, "MatFDColoringCreate" );
	BSSCR_report_op( ops->coloringpatch, v,    i++, "MatColoringPatch" );
	BSSCR_report_op( ops->setunfactored, v,    i++, "MatSetUnFactored" );
	BSSCR_report_op( ops->permute, v,          i++, "MatPermute" );
	BSSCR_report_op( ops->setvaluesblocked, v, i++, "MatSetValuesBlocked" );

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

#if( (PETSC_VERSION_MAJOR == 3) &&  (PETSC_VERSION_MINOR > 7) )
    BSSCR_report_op( ops->createsubmatrix, v,  i++, "MatCreateSubMatrix" );
#else
    BSSCR_report_op( ops->getsubmatrix, v,  i++, "MatGetSubMatrix" );
#endif

	BSSCR_report_op( ops->destroy, v,       i++, "MatDestroy" );
	BSSCR_report_op( ops->view, v,          i++, "MatView" );
	BSSCR_report_op( ops->convertfrom, v,   i++, "MatConvertFrom" );

	BSSCR_report_op( ops->setlocaltoglobalmapping, v, i++, "MatSetLocalToGlobalMapping" );
	BSSCR_report_op( ops->setvalueslocal, v,          i++, "MatSetValuesLocal" );
	BSSCR_report_op( ops->zerorowslocal, v,           i++, "Stg_MatZeroRowsLocal" );

	PetscViewerASCIIPrintf(v, "------------------------------------------------\n");
	BSSCR_report_op( ops->getrowmaxabs, v,    i++, "MatGetRowMaxAbs" );
	BSSCR_report_op( ops->convert, v,         i++, "MatConvert" );
//    BSSCR_report_op( ops->setcoloring, v,     i++, "MatSetColoring" );  NOT SURE IF THIS IS AVAIL IN 3.8

	BSSCR_report_op( ops->setvaluesadifor, v, i++, "MatSetValuesAdicfor" );

	BSSCR_report_op( ops->fdcoloringapply, v,              i++, "MatFDCloringApply" );
	BSSCR_report_op( ops->setfromoptions, v,               i++, "MatSetFromOptions" );
	BSSCR_report_op( ops->multconstrained, v,              i++, "MatMultConstrained" );
	BSSCR_report_op( ops->multtransposeconstrained, v,     i++, "MatMultTransposeConstrained" );
	/* data member depreciated from PETSc v. 3.0.0 */
#if( PETSC_VERSION_MAJOR <= 2 )
	BSSCR_report_op( ops->ilufactorsymbolicconstrained, v, i++, "MatILUFactorSymbolicConstrained" );
#endif
	PetscViewerASCIIPrintf(v, "------------------------------------------------\n");
#if( ( ( PETSC_VERSION_MAJOR == 3 ) && ( PETSC_VERSION_MINOR < 2 ) ) )
	BSSCR_report_op( ops->permutesparsify, v, i++, "MatPermuteSparsify" );
#endif
	BSSCR_report_op( ops->mults, v,           i++, "MatMults" );
	BSSCR_report_op( ops->solves, v,          i++, "MatSolves" );
	BSSCR_report_op( ops->getinertia, v,      i++, "MatGetInertia" );
	BSSCR_report_op( ops->load, v,            i++, "MatLoad" );

	BSSCR_report_op( ops->issymmetric, v,             i++, "MatIsSymmetric" );
	BSSCR_report_op( ops->ishermitian, v,             i++, "MatIsHermitian" );
	BSSCR_report_op( ops->isstructurallysymmetric, v, i++, "MatIsStructurallySymmetric" );
#if( ( ( PETSC_VERSION_MAJOR == 3 ) && ( PETSC_VERSION_MINOR < 1 ) ) || ( PETSC_VERSION_MAJOR <= 2 ) )
	BSSCR_report_op( ops->pbrelax, v,                 i++, "MatPBRelax" );
#endif
	BSSCR_report_op( ops->getvecs, v,                 i++, "MatGetVec" );

	PetscViewerASCIIPrintf(v, "------------------------------------------------\n");
	BSSCR_report_op( ops->matmult, v,         i++, "MatMatMult" );
	BSSCR_report_op( ops->matmultsymbolic, v, i++, "MatMatMultSymbolic" );
	BSSCR_report_op( ops->matmultnumeric, v,  i++, "MatMatMultNumeric" );
	BSSCR_report_op( ops->ptap, v,            i++, "MatPtAP" );
	BSSCR_report_op( ops->ptapsymbolic, v,    i++, "MatPtAPSymbolic" );

	BSSCR_report_op( ops->ptapnumeric, v,              i++, "MatPtAPNumeric" );

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

	BSSCR_report_op( ops->conjugate, v,           i++, "MatConjugate" );
	//BSSCR_report_op( ops->setsizes, v,            i++, "MatSetSizes" );

	BSSCR_report_op( ops->setvaluesrow, v,              i++, "MatSetValuesRow" );
	BSSCR_report_op( ops->realpart, v,                  i++, "MatRealPart" );
	BSSCR_report_op( ops->imaginarypart, v,             i++, "MatImaginaryPart" );
	BSSCR_report_op( ops->getrowuppertriangular, v,     i++, "MatGetRowUpperTriangular" );
	BSSCR_report_op( ops->restorerowuppertriangular, v, i++, "MatRestoreRowUpperTriangular" );

	PetscViewerASCIIPrintf(v, "------------------------------------------------\n");
	BSSCR_report_op( ops->matsolve, v,           i++, "MatMatSolve" );
#if ( (PETSC_VERSION_MAJOR >= 3) && (PETSC_VERSION_MINOR <6) )
	BSSCR_report_op( ops->getredundantmatrix, v, i++, "MatGetRedundantMatrix" );
#endif
	BSSCR_report_op( ops->getrowmin, v,          i++, "MatGetRowMin" );
	BSSCR_report_op( ops->getcolumnvector, v,    i++, "MatGetColumnVector" );

	PetscViewerASCIIPopTab( v );

	PetscFunctionReturn(0);
}

PetscErrorCode BSSCR_VecListOperations( Vec x, PetscViewer v )
{
	VecOps ops = x->ops;
	VecType type;
	PetscInt i;

	VecGetType( x, &type );
	PetscViewerASCIIPrintf(v, "Operations available for VecType: %s \n", type );

	PetscViewerASCIIPushTab( v );

	PetscViewerASCIIPopTab( v );

	PetscFunctionReturn(0);
}

PetscErrorCode BSSCR_KSPListOperations( KSP ksp, PetscViewer v )
{
	KSPOps ops = ksp->ops;
	KSPType type;
	PetscInt i;

	KSPGetType( ksp, &type );
	PetscViewerASCIIPrintf(v, "Operations available for KSPType: %s \n", type );

	PetscViewerASCIIPushTab( v );

	i=0;
	PetscViewerASCIIPrintf(v, "------------------------------------------------\n");
	BSSCR_report_op( ops->buildsolution, v,  i++, "KSPBuildSolution" );
	BSSCR_report_op( ops->buildresidual, v,  i++, "KSPBuildResidual" );
	BSSCR_report_op( ops->solve, v,          i++, "KSPSolve" );
	BSSCR_report_op( ops->setup, v,          i++, "KSPSetUp" );
	BSSCR_report_op( ops->setfromoptions, v, i++, "KSPSetFromOptions" );

	BSSCR_report_op( ops->publishoptions, v,               i++, "KSPPublishOptions" );
	BSSCR_report_op( ops->computeextremesingularvalues, v, i++, "KSPComputeExtremeSingularValues" );
	BSSCR_report_op( ops->computeeigenvalues, v,           i++, "KSPComputeEigenvalues" );
	BSSCR_report_op( ops->destroy, v, i++, "BSSCR_KSPDestroy" );
	BSSCR_report_op( ops->view, v,    i++, "KSPView" );

	PetscViewerASCIIPopTab( v );

	PetscFunctionReturn(0);
}

PetscErrorCode BSSCR_PCListOperations( PC pc, PetscViewer v )
{
	PCOps ops = pc->ops;
	PCType type;
	PetscInt i;

	PCGetType( pc, &type );
	PetscViewerASCIIPrintf(v, "Operations available for PCType: %s \n", type );

	PetscViewerASCIIPushTab( v );

	i=0;
	PetscViewerASCIIPrintf(v, "------------------------------------------------\n");
	BSSCR_report_op( ops->setup, v,           i++, "PCSetUp" );
	BSSCR_report_op( ops->apply, v,           i++, "PCApply" );
	BSSCR_report_op( ops->applyrichardson, v, i++, "PCApplyRichardson" );
	BSSCR_report_op( ops->applyBA, v,         i++, "PCApplyBA" );
	BSSCR_report_op( ops->applytranspose, v,  i++, "PCApplyTranspose" );

	BSSCR_report_op( ops->applyBAtranspose, v,  i++, "PCApplyBATranspose" );
	BSSCR_report_op( ops->setfromoptions, v,    i++, "PCSetFromOptions" );
	BSSCR_report_op( ops->presolve, v,          i++, "PCPreSolve" );
	BSSCR_report_op( ops->postsolve, v,         i++, "PCPostSolve" );
	BSSCR_report_op( ops->getfactoredmatrix, v, i++, "PCGetFactoredMatrix" );

	PetscViewerASCIIPrintf(v, "------------------------------------------------\n");
	BSSCR_report_op( ops->applysymmetricleft, v,  i++, "PCApplySymmetricLeft" );
	BSSCR_report_op( ops->applysymmetricright, v, i++, "PCApplySymmetricRight" );
	BSSCR_report_op( ops->setuponblocks, v,       i++, "PCSetupOnBlocks" );
	BSSCR_report_op( ops->destroy, v,             i++, "PCDestroy" );
	BSSCR_report_op( ops->view, v,                i++, "PCView" );

	PetscViewerASCIIPopTab( v );

	PetscFunctionReturn(0);
}

PetscErrorCode BSSCR_SNESListOperations( SNES snes, PetscViewer v )
{
	SNESOps ops = snes->ops;
	SNESType type;
	PetscInt i;

	SNESGetType( snes, &type );
	PetscViewerASCIIPrintf(v, "Operations available for SNESType: %s \n", type );

	PetscViewerASCIIPushTab( v );

	i=0;
	PetscViewerASCIIPrintf(v, "------------------------------------------------\n");

	BSSCR_report_op( ops->computescaling, v,  i++, "SNESComputeScaling" );
	BSSCR_report_op( ops->update, v,          i++, "SNESUpdate" );
	BSSCR_report_op( ops->converged, v,       i++, "SNESConverged" );

	BSSCR_report_op( ops->setup, v,           i++, "SNESSetUp" );
	BSSCR_report_op( ops->solve, v,           i++, "SNESSolve" );
	BSSCR_report_op( ops->view, v,            i++, "SNESView" );
	BSSCR_report_op( ops->setfromoptions, v,  i++, "SNESSetFromOptions" );
	BSSCR_report_op( ops->destroy, v,         i++, "SNESDestroy" );

	PetscViewerASCIIPopTab( v );

	PetscFunctionReturn(0);
}


#ifndef QUAC_P_H_
#define QUAC_P_H_
#include <petsc.h>
extern int  petsc_initialized;
PetscLogEvent add_lin_event,add_to_ham_event,add_lin_recovery_event,add_encoded_gate_to_circuit_event;
PetscLogEvent _qc_event_function_event,_qc_postevent_function_event,_apply_gate_event;
PetscClassId quac_class_id;
PetscLogStage pre_solve_stage,solve_stage,post_solve_stage;
#endif


// -*- c++ -*-

#if 1
#include <string>
#include <vector>
#include <utility> // for std::pair
#include <stdint.h>

#include <iostream>
class Foo
{
public:
  static std::string GetName() { return "Foo"; }

  Foo();
  ~Foo();

  void setDouble(double d) { m_d = d;}
  void setDouble(double d, float x) { m_d = d+x;}
  //void setFoo(const std::string &s) {std::cout << s << std::endl;}
  void setFoo(const std::string &s="f", double x=1) {std::cout << s << x << std::endl;}
  void setInt(int i) { std::cout << i << std::endl;}
  void setInt(long i, float x=2) {std::cout << i+x << std::endl;}
  double getDouble() { return m_d; }

  void setUint64(uint64_t v) { m_uint64 = v; }
  uint64_t getUint64() { return m_uint64; }

  std::vector<Foo> getCollection() { return std::vector<Foo>(1, *this); }
  std::vector<std::vector<std::pair<int, int> > > getMatrix() { return std::vector<std::vector<std::pair<int,int> > >(); }

  std::vector< int>::iterator getIterator() { return std::vector<int>().begin(); }

  static void frobnicate() { std::cout << "frobnicate"; }
  //void frobnicate() { std::cout << "frobnicate -- non static"; }

  Foo operator+(const Foo& a) { return Foo(); }
  Foo& operator+=(const Foo& a) {
    this->m_d += a.m_d;
    this->m_uint64 += a.m_uint64;
    return *this;
  }

  Foo& operator=(const Foo& rhs) {
    this->m_d = rhs.m_d;
    this->m_uint64 = rhs.m_uint64;
    return *this;
  }

  void save(std::ostream &out) { out << m_d << m_uint64; }

  double getme(double d) { return d; }
  int    getme(int i) { return i; }
  void   getme() const { return ; }
  void   getme()       { return ; }

private:
  double m_d;
  uint64_t m_uint64;
};

Foo::Foo()
{}

Foo::~Foo()
{}

class IFoo
{
public:

  virtual
  void virtual_meth() const {}

  virtual
  void pure_virtual_meth() const = 0;

};

//template class std::vector<Foo>;
namespace Math {
  void do_hello() {
    std::cout << "helllo" << std::endl;
  }
  double do_add(double i, double j=2) { return i+j; }
  double do_add(int i, int j=2, int k=3, int l=4) { return i+j+k+l; }
  double do_add() { return 42; }

  const std::string& say_hello() { static std::string hi="hi"; return hi;}

  double adder(double i, double j) { return i+j; }
}

namespace Math2 {
  // std::string do_hello() {
  //   return "hi";
  // }
  std::string do_hello(const std::string& name = "you") {
    return std::string("hello ") + name;
  }
  std::string do_hello(const char* name) {
    return std::string("hello -- ") + std::string(name);
  }

  std::string do_hello_c(const char* name) {
    return std::string("hello -- ") + std::string(name);
  }
}

namespace NS {
  class Bar {
  public:
    Bar() {}
    void syHello() {
      std::cout << "hello" << std::endl;
    }
  };

  template<class F> F tmpl_fct() { return F(); }

  template<> int tmpl_fct<int>() { return 42; }

  template<int N> int tmpl_fct_n() { return N; }
  template<> int tmpl_fct_n<42>() { return 42; }
  template<> int tmpl_fct_n<-1>() { return -1; }
}

namespace TT {
  typedef int    foo_t;
  typedef foo_t* bar_t;
  typedef const bar_t baz_t;
  //'int *'    'foo *'    'bar'
}

#endif

#define BIT(n)       (1ULL << (n))
enum MyEnum {
  kValue = BIT(2)
};

typedef int            Ssiz_t;      //String size (int)
struct LongStr_t
{
  Ssiz_t    fCap;    // Max string length (including null)
  Ssiz_t    fSize;   // String length (excluding null)
  char     *fData;   // Long string data
};

enum Enum0 {
  kMinCap = (sizeof(LongStr_t) - 1)/sizeof(char) <= 2 ?
            2 : (sizeof(LongStr_t) - 1)/sizeof(char),
  kMin1 = 1 >= 2 ? 1 : 2
};

typedef void (*Func_t)();

class TVersionCheck {
public:
   TVersionCheck(int versionCode);  // implemented in TSystem.cxx
};

//static TVersionCheck gVersionCheck(335360);
//#define ROOT_VERSION_CODE 335360
//static TVersionCheck gVersionCheck = ROOT_VERSION_CODE;

class EnumNs
{
public:
   enum ESTLtype { kSTL       = 300 /* TVirtualStreamerInfo::kSTL */,
                   kSTLbitset =  8
   };
   // TStreamerElement status bits
   enum {
      kHasRange     = BIT(6),
      kCache        = BIT(9),
      kRepeat       = BIT(10),
      kRead         = BIT(11),
      kWrite        = BIT(12),
      kDoNotDelete  = BIT(13)
   };

};

class Base
{
public:
  virtual void initialize() = 0;
};

class Base2
{
public:
  virtual void execute() = 0;
};

class Alg : public Base, virtual public Base2
{
public:
  virtual void initialize() { std::cout << "::initialize();\n"; }
  virtual void execute() { std::cout << "::execute();\n"; }
};

class WithPrivateBase: public Base, private Base2
{
public:
  virtual void initialize() { std::cout << "::initialize();\n"; }
  virtual void execute() { std::cout << "::execute();\n"; }

  WithPrivateBase() {}
  WithPrivateBase(int /*i*/) {}

  Enum0 myenum;

  operator double() { return myenum; }

  enum Enum1 {
    kMinCap = (sizeof(LongStr_t) - 1)/sizeof(char) <= 2 ?
              2 : (sizeof(LongStr_t) - 1)/sizeof(char),
    kMin1 = 1 >= 2 ? 1 : 2
  };

private:
  void some_private_method() { std::cout << "::private()\n"; }
};

// #include <cblas.h>


#ifndef _SPC_DRIZ_H
#define _SPC_DRIZ_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_vector.h>

double sgarea(double x1, double y1, double x2, double y2, int is, int js);
double boxer(int is,int js,double *x,double *y);

#endif


#ifndef OPENMC_TALLIES_FILTER_H
#define OPENMC_TALLIES_FILTER_H

#include <cstdint>
#include <string>
#include <unordered_map>

#include "pugixml.hpp"
#include <gsl/gsl>

#include "openmc/constants.h"
#include "openmc/hdf5_interface.h"
#include "openmc/memory.h"
#include "openmc/particle.h"
#include "openmc/tallies/filter_match.h"
#include "openmc/vector.h"

namespace openmc {

//==============================================================================
//! Modifies tally score events.
//==============================================================================

class Filter
{
public:
  //----------------------------------------------------------------------------
  // Constructors, destructors, factory functions

  Filter();
  virtual ~Filter();

  //! Create a new tally filter
  //
  //! \tparam T Type of the filter
  //! \param[in] id  Unique ID for the filter. If none is passed, an ID is
  //!    automatically assigned
  //! \return Pointer to the new filter object
  template<typename T>
  static T* create(int32_t id = -1);

  //! Create a new tally filter
  //
  //! \param[in] type  Type of the filter
  //! \param[in] id  Unique ID for the filter. If none is passed, an ID is
  //!    automatically assigned
  //! \return Pointer to the new filter object
  static Filter* create(const std::string& type, int32_t id = -1);

  //! Create a new tally filter from an XML node
  //
  //! \param[in] node XML node
  //! \return Pointer to the new filter object
  static Filter* create(pugi::xml_node node);

  //! Uses an XML input to fill the filter's data fields.
  virtual void from_xml(pugi::xml_node node) = 0;

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

  virtual std::string type() const = 0;

  //! Matches a tally event to a set of filter bins and weights.
  //!
  //! \param[in] p Particle being tracked
  //! \param[in] estimator Tally estimator being used
  //! \param[out] match will contain the matching bins and corresponding
  //!   weights; note that there may be zero matching bins
  virtual void
  get_all_bins(const Particle& p, TallyEstimator estimator, FilterMatch& match) const = 0;

  //! Writes data describing this filter to an HDF5 statepoint group.
  virtual void
  to_statepoint(hid_t filter_group) const
  {
    write_dataset(filter_group, "type", type());
    write_dataset(filter_group, "n_bins", n_bins_);
  }

  //! Return a string describing a filter bin for the tallies.out file.
  //
  //! For example, an `EnergyFilter` might return the string
  //! "Incoming Energy [0.625E-6, 20.0)".
  virtual std::string text_label(int bin) const = 0;

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

  //! Get unique ID of filter
  //! \return Unique ID
  int32_t id() const { return id_; }

  //! Assign a unique ID to the filter
  //! \param[in]  Unique ID to assign. A value of -1 indicates that an ID should
  //!   be automatically assigned
  void set_id(int32_t id);

  //! Get number of bins
  //! \return Number of bins
  int n_bins() const { return n_bins_; }

  gsl::index index() const { return index_; }

  //----------------------------------------------------------------------------
  // Data members

protected:
  int n_bins_;
private:
  int32_t id_ {C_NONE};
  gsl::index index_;
};

//==============================================================================
// Global variables
//==============================================================================

namespace model {
  extern "C" int32_t n_filters;
  extern std::unordered_map<int, int> filter_map;
  extern vector<unique_ptr<Filter>> tally_filters;
}

//==============================================================================
// Non-member functions
//==============================================================================

//! Make sure index corresponds to a valid filter
int verify_filter(int32_t index);

} // namespace openmc
#endif // OPENMC_TALLIES_FILTER_H


/* Xrecon.h */
/*---------------------------------------------------------------------------*/
/*                                                                           */
/* Xrecon.h: Global includes and defines                                     */
/*                                                                           */
/* Copyright (C) 2012 Paul Kinchesh                                          */
/*               2012 Martyn Klassen                                         */
/*               2012 Margaret Kritzer                                       */
/*                                                                           */
/* This file is part of Xrecon.                                              */
/*                                                                           */
/* Xrecon 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.                                       */
/*                                                                           */
/* Xrecon 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 Xrecon. If not, see <http://www.gnu.org/licenses/>.            */
/*                                                                           */
/*---------------------------------------------------------------------------*/
/**/

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

/*------------------------------------------------------------*/
/*---- If C++ compiler is used it needs to know this is C ----*/
/*------------------------------------------------------------*/
//#ifdef __cplusplus
//extern "C" {
//#endif

/*----------------------------------*/
/*---- 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 <sys/types.h>
#include <unistd.h>
#include <signal.h>

/*-----------------------------*/
/*---- Include GSL Headers ----*/
/*-----------------------------*/
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_sf_bessel.h>

/*-----------------------------*/
/*---- Include fftw header ----*/
/*-----------------------------*/
#include <fftw3.h>

/*--------------------------------*/
/*---- Include complex header ----*/
/*--------------------------------*/
/* NB If you have a C compiler, such as gcc, that supports the recent C99 standard,
   and you #include <complex.h> before <fftw3.h>, then fftw_complex is the native
   double-precision complex type and you can manipulate it with ordinary arithmetic.
   Otherwise, FFTW defines its own complex type, which is bit-compatible with the
   C99 complex type. */
/* We will wait for C99 standard to become a bit more standard */
//#include <complex.h>

#ifdef DOTIFF
/*---------------------------------*/
/*---- Include tiff I/O header ----*/
/*---------------------------------*/
#include <tiffio.h>
#endif

/*------------------------------------------*/
/*---- Include Varian data file handler ----*/
/*------------------------------------------*/
#include "data.h"

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

/*------------------------------*/
/*---- Include bool headers ----*/
/*------------------------------*/
#ifdef _STDC_C99
#include <stdbool.h>
#else
#ifndef bool
#define bool int
#endif
#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif
#endif

/*----------------------------*/
/*---- Some basic defines ----*/
/*----------------------------*/
#define TRUE       1
#define FALSE      0
#define ACTIVE     1
#define NACTIVE    0
#define NACQ       2

/* image scaling */
#define FFT_SCALE 0.5e-6

/*-------------------------*/
/*---- Some data types ----*/
/*-------------------------*/
#define INT16      1  /* 16 bit integer */
#define INT32      2  /* 32 bit integer */
#define FLT32      3  /* 32 bit float */
#define DBL64      4  /* 64 bit double */

/*-------------------------*/
/*---- Some data modes ----*/
/*-------------------------*/
#define NONE       0  /* No data */
#define FID        1  /* Raw */
#define IMAGE      2  /* Image */
#define RE         3  /* Real */
#define IM         4  /* Imaginary */
#define MG         5  /* Magnitude */
#define PH         6  /* Phase */
#define CX         7  /* Complex */
#define STD        8  /* Standard */
#define READ       9  /* Read */
#define PHASE     10  /* Phase */
#define PHASE2    11  /* 2nd Phase */
#define REF       12  /* Reference */
#define REFREAD   13  /* Reference Read */
#define REFPHASE  14  /* Reference Phase */
#define REFPHASE2 15  /* Reference Phase 2 */
#define MK        16  /* Mask */
#define MKREAD    17  /* Mask Read */
#define MKPHASE   18  /* Mask Phase */
#define MKPHASE2  19  /* Mask Phase 2 */
#define RMK       20  /* Reverse Mask of magnitude */
#define MKROI     21  /* Mask ROI */
#define SM        22  /* Sensitivity map */
#define SMREAD    23  /* Sensitivity Map Read */
#define SMPHASE   24  /* Sensitivity Map Phase */
#define SMPHASE2  25  /* Sensitivity Map Phase 2 */
#define GF        26  /* Geometry Factor */
#define RS        27  /* Relative SNR */
#define EPIREF    28  /* EPI reference */
#define VJ        29  /* Defined by VnmrJ parameters */
#define SPATIAL   30  /* for csi */
#define SPECTRAL  31  /* for csi */
#define SPATSPECT 32  /* for csi */
#define PHASE3    33  /* for 3D CSI */

/*---------------------------------------------------------*/
/*---- Some data status bits (0-15) for each dimension ----*/
/*---------------------------------------------------------*/
#define DATA     0x1  /* 0 = no data, 1= data */
#define SHIFT    0x2  /* 0 = no shift, 1 = shift */
#define ZEROFILL 0x4  /* 0 = no zerofill, 1 = zerofill */
#define FFT      0x8  /* 0 = no FFT, 1 = FFT */
/* Bits 4-15 are currently unused */

/*-----------------------------*/
/*---- Some sequence modes ----*/
/*-----------------------------*/
/* For 1D */
#define IM1D      100
#define IM0DCSI   101  /* single voxel csi */

/* For 2D multislice 200 < seqmode < 300 */
#define IM2D      200
#define IM2DCC    201  /* seqcon = "*ccnn" */
#define IM2DCS    202  /* seqcon = "*csnn" */
#define IM2DSC    203  /* seqcon = "*scnn" */
#define IM2DSS    204  /* seqcon = "*ssnn" */
#define IM2DCCFSE 205  /* apptype = "im2Dfse", seqcon = "nccnn" */
#define IM2DCSFSE 206  /* apptype = "im2Dfse", seqcon = "ncsnn" */
#define IM2DSCFSE 207  /* apptype = "im2Dfse", seqcon = "nscnn" */
#define IM2DSSFSE 208  /* apptype = "im2Dfse", seqcon = "nssnn" */
#define IM2DEPI   209  /* apptype = "im2Depi" */
#define IM1DCSI   210  /* csi has one extra dimension */

#define IM2DCCLL  211  /* looklocker seqcon = "*ccnn" */
#define IM2DCSLL  212  /* looklocker seqcon = "*csnn" */
#define IM2DSCLL  213  /* looklocker seqcon = "*scnn" */
#define IM2DSSLL  214  /* looklocker seqcon = "*ssnn" */

/* For 3D 300 < seqmode < 400 */
#define IM3D      300
#define IM3DCC    301  /* seqcon = "**ccn" */
#define IM3DCS    302  /* seqcon = "**csn" */
#define IM3DSC    303  /* seqcon = "**scn" */
#define IM3DSS    304  /* seqcon = "**ssn" */
#define IM3DCFSE  305  /* apptype = "im3Dfse", seqcon = "ncccn" */
#define IM3DSFSE  306  /* apptype = "im3Dfse", seqcon = "nccsn" */
#define IM2DCSCSI   307  /* apptype = "im2DCSI", seqcon = "nncsn" */
#define IM2DSCCSI   308  /* apptype = "im2DCSI", seqcon = "nnscn" */
#define IM2DSSCSI   309  /* apptype = "im2DCSI", seqcon = "ncssn" */
#define IM2DCCCSI   310  /* apptype = "im2DCSI", seqcon = "nnccn" */

/* For 4D */
#define IM4D      400
#define IM3DCCCCSI   401  /* csi has one extra dimension */
#define IM3DCCSCSI   402
#define IM3DCSCCSI   403
#define IM3DCSSCSI   404
#define IM3DSCCCSI   405
#define IM3DSCSCSI   406
#define IM3DSSCCSI   407
#define IM3DSSSCSI   408

 /* max dimensions */
#define MAXDIM    500

/*-------------------------*/
/*---- For TIF scaling ----*/
/*-------------------------*/
#define NSCALE     0  /* Don't scale data */
#define SCALE     -1  /* Scale to maximum of data in each volume */
#define NOISE     -2  /* Scale to see noise */

/*--------------------------------*/
/*---- For NIFTI-1/Analyze7.5 ----*/
/*--------------------------------*/
#define NIFTI      1  /* NIFTI-1 format */
#define ANALYZE    2  /* Analyze7.5 format */

/*---------------------------------------------------------------------------*/
/* Modes for DC correction using dbh.lvl and dbh.tlt in getvol(d,index,mode) */
/*---------------------------------------------------------------------------*/
#define NDCC       0  /* No DC correction using dbh.lvl and dbh.tlt */
#define DCC        1  /* DC correction using dbh.lvl and dbh.tlt */

/*-----------------------------------------------------------------------------------------*/
/* Modes for writing raw data in wrawbin3D(struct data *d,int mode,int type,int precision) */
/*-----------------------------------------------------------------------------------------*/
#define D1         1  /* 1D data mode, data[nr][dim3][dim1] */
#define D12        2  /* 2D/3D data mode, data[nr][dim3][dim2*dim1] */
#define D3         3  /* 3D block processing mode, data[nr][dim2*dim1][dim3] */

/*-------------------*/
/*---- Constants ----*/
/*-------------------*/
#ifndef M_PI
#define M_PI 3.14159265358979323846 /* pi */
#endif
#define DEG2RAD 0.017453292         /* convert degrees to radians */
#define MAXRCVRS 2048               /* maximum number of receivers */

/*------------------------------------------------------*/
/*---- 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 Xrecon.h in Xrecon.c ----*/
/*-------------------------------------------------------*/
#ifdef LOCAL
#define EXTERN
#else
#define EXTERN extern
#endif

int interupt;  // for killing the process

/*-------------------------------------------*/
/*---- Structure to hold input filenames ----*/
/*-------------------------------------------*/
struct file
{
  int      nfiles;    /* number of files */
  char     **fid;     /* fid files */
  char     **procpar; /* procpar files */
};

/*------------------------------------------------*/
/*---- Structure to hold 'procpar' parameters ----*/
/*------------------------------------------------*/
struct pars
{
  int      npars;   /* number of parameters in structure */
  char     **name;  /* parameter name */
  int      *type;   /* parameter type */
  int      *active; /* parameter state */
  int      *nvals;  /* number of values of parameters */
  int      **i;     /* integer values */
  double   **d;     /* real values */
  char     ***s;    /* strings */
};

/*---------------------------------------------------------*/
/*---- Structure to hold maximum value and coordinates ----*/
/*---------------------------------------------------------*/
struct max
{
  int      data;    /* Data flag 0/FALSE=no data, or 1/TRUE=data */
  double   Mval;    /* Maximum magnitude value */
  double   Rval;    /* Maximum real value */
  double   Ival;    /* Maximum imaginary value */
  int      np;      /* position of maximum along np */
  int      nv;      /* position of maximum along nv */
  int      nv2;     /* position of maximum along nv2 */
  int      nv3;     /* position of maximum along nv3 */
};

/*----------------------------------------------*/
/*---- Structure to hold noise measurements ----*/
/*----------------------------------------------*/
struct noise
{
  int      data;    /* Data flag 0/FALSE=no data, or 1/TRUE=data */
  int      zero;    /* Zero data flag 0/FALSE=noise data not zero, or 1/TRUE=noise data is zero */
  int      equal;   /* Equalize flag 0/FALSE=not equal, or 1/TRUE=equal */
  int      samples; /* Number of noise samples */
  double   *M;      /* Mean magnitude */
  double   *M2;     /* Mean magnitude^2 */
  double   *Re;     /* Mean real */
  double   *Im;     /* Mean imaginary */
  double   avM;     /* Averaged over all receivers */
  double   avM2;    /* Averaged over all receivers */
  double   avRe;    /* Averaged over all receivers */
  double   avIm;    /* Averaged over all receivers */
  gsl_matrix_complex   **mat;    /* Noise matrix */
};

/*--------------------------------*/
/*---- Structure to hold data ----*/
/*--------------------------------*/
struct data
{
  FILE                    *fp;         /* fid file pointer */
  FILE                    *datafp;     /* data file pointer */
  FILE                    *phasfp;     /* phasefile file pointer */
  FILE                    *fidfp;      /* for writing in fid format */
  char                    *file;       /* fid file */
  char                    *procpar;    /* procpar file */
  struct datafilehead     fh;          /* data file header */
  struct datablockhead    bh;          /* data block header */
  struct hypercmplxbhead  hcbh;        /* hypercomplex block header */
  struct stat             buf;         /* data file info */

  int                     nvols;       /* number of volumes at end of expt */
  int                     startvol;    /* start volume for processing */
  int                     endvol;      /* end volume for processing */
  int                     vol;         /* current volume counter */
  int                     outvol;      /* output volume (not counting reference volumes) */

  int                     nblocks;     /* number of blocks */
  int                     startpos;    /* start position of block for processing */
  int                     endpos;      /* end position of block for processing */
  int                     block;       /* current block counter */

  int                     np,nv,nv2,nv3; /* number of points and views including nv3 for CSI */
  int                     nseg,etl;    /* number of segments and echo train length */
  int                     ne;          /* number of echoes */
  int                     fn,fn1,fn2,fn3;  /* zerofilling */
  int                     nr,ns;       /* number of receivers and slices */
  int                     korder;       /* for csi, indicates elliptical or otherwise acquired */
  int                     synclist[3];  /* indicates which dimensions are part of sync'ed lists such as pelist, pe2list, etc. */
  int                     startd1, startd2, startd3;  // for csi cropping
  int                     cropd1, cropd2, cropd3;     // for csi cropping

  int                     profile;     /* profile flag, 0/FALSE=not profile, 1/TRUE=profile */
  int                     proj2D;      /* 2D projection flag, 0/FALSE=not 2D projection, 1/TRUE=2D projection */

  int                     nav;         /* navigator flag, 0/FALSE=none, 1/TRUE=navigators */
  int                     nnav;        /* number of navigators */
  int                     *navpos;     /* position(s) of navigator data amongst actual data */

  int                     seqmode;     /* sequence mode depends on seqcon and apptype */

  int                     *dim2order;  /* dim2 order in fid file */
  int                     *dim3order;  /* dim3 order in fid file */
  int                     *dim4order;  /* dim4 order in fid file */
  int                     *pssorder;   /* slice order in fid file */

  int                     *dim2orderR; /* Table for reduced dim2 order */
  int                     *dim3orderR; /* Table for reduced dim3 order */
  int                     *dim4orderR; /* Table for reduced dim4 order */
  int                     dim2R,dim3R, dim4R; /* Size of dim2 and dim3 and dim4 BEFORE reduction */
  int					  d1rev, d2rev, d3rev; /* flag for csi dimension reversal */

  fftw_complex            ***data;     /* actual data */
  fftw_complex	          **csi_data; /* different organization */
  int                     **mask;      /* data mask */

  int                     ndim;        /* number of data dimensions */
  int                     *dimstatus;  /* status of each dimension */

  int                     datamode;    /* data mode to flag different types of data, eg. see default EPI recon */

  struct max              max;         /* maximum value and coordinates */
  struct noise            noise;       /* noise measurement data */

  struct pars             p;           /* 'procpar' parameters */
  struct pars             a;           /* 'array' parameters */
  struct pars             s;           /* 'sviblist' parameters */

  char                    *fdfhdr;     /* fdf header */
};

/*-------------------------------------*/
/*---- Structure to hold dimstatus ----*/
/*-------------------------------------*/
struct dimstatus
{
  int ndim;        /* number of data dimensions */
  int *dimstatus;  /* status of each dimension */
};

/*-------------------------------------------------*/
/*---- Structure to hold segment scaling (EPI) ----*/
/*-------------------------------------------------*/
struct segscale
{
  int data;
  double ***value;
};

/*------------------------------*/
/*---- Main recon functions ----*/
/*------------------------------*/
void recon1D(struct data *d);    /* recon1D.c */
void recon2D(struct data *d);    /* recon2D.c */
void reconEPI(struct data *d);   /* reconEPI.c */
void recon3D(struct data *d);    /* recon3D.c */
void recon2DCSI(struct data *d);

/*---------------------------------*/
/*---- Default recon functions ----*/
/*---------------------------------*/
void default1D(struct data *d);     /* default1D.c */
void default2D(struct data *d);     /* default2D.c */
void defaultEPI(struct data *d);    /* defaultEPI.c */
void default3D(struct data *d);     /* default3D.c */

/*-------------------------------*/
/*---- default1D.c functions ----*/
/*-------------------------------*/
void refcorr1D(struct data *d,struct data *ref);
void combine1D(struct data *d);

/*-------------------------------*/
/*---- profile1D.c functions ----*/
/*-------------------------------*/
void profile1D(struct data *d);

/*----------------------------*/
/*---- proj2D.c functions ----*/
/*----------------------------*/
void proj2D(struct data *d);

/*------------------------*/
/*---- CSI  functions ----*/
/*------------------------*/
void reconCSI(struct data *d);
void reconCSI2D(struct data *d);
void reconCSI3D(struct data *d);
void dimorderCSI2D(struct data *d);
void dimorderCSI3D(struct data *d);
void getblockCSI2D(struct data *d,int volindex,int DCCflag);
void getblockCSI3D(struct data *d,int volindex,int DCCflag);
void w2DCSI(struct data *d,int receiver,int d4, int d3, int d2,int fileid);
void wdfhCSI(struct data *d,int fileid);
void zerofill2DCSI(struct data *d, int mode);
void phaseramp2DCSI(struct data *d, int d4index, int mode);
void shiftdim3dataCSI(struct data *d,int shft);
void phaserampdim3CSI(struct data *d, int mode);
void regridEPSI(struct data *d);

/*----------------------------*/
/*---- mask2D.c functions ----*/
/*----------------------------*/
void mask2D(struct data *d);
void add2mask2D(struct data *d);
int read2Dmask(struct data *d,int mode);
void mask2Ddata(struct data *d1,struct data *d2);

/*-----------------------------*/
/*---- sense2D.c functions ----*/
/*-----------------------------*/
void sense2D(struct data *d);
void sense2Dunfold(struct data *d,struct data *ref);
void svd2Dinmem(struct data *ref);
void setref2Dmatrix(struct data *ref,struct data *d);
int checksenseref(struct data *d,struct data *ref);

/*-----------------------------*/
/*---- sensi2D.c functions ----*/
/*-----------------------------*/
void sensibility2D(struct data *d);
void gmap2D(struct data *d);

/*----------------------------*/
/*---- smap2D.c functions ----*/
/*----------------------------*/
void smap2D(struct data *d,int mode);
int smap2Dvcoil(struct data *d,struct data *ref,int mode);
int smap2Dacoil(struct data *d,int mode);
int setvcoil2D(struct data *d,struct data *ref,struct file *fref);
void gen2Dsmapsos(struct data *d);
void gen2Dsmapsuper(struct data *d);
void gen2Dsmapvcoil(struct data *d,struct data *ref);

/*-------------------------------*/
/*---- asltest2D.c functions ----*/
/*-------------------------------*/
void asltest2D(struct data *d);

/*-----------------------------------*/
/*---- multiblock3D.c  functions ----*/
/*-----------------------------------*/
void multiblock3D(struct data *d);

/*------------------------------*/
/*---- dprocEPI.c functions ----*/
/*------------------------------*/
void addscaledEPIref(struct data *d,struct data *ref1,struct data *ref2);
void addEPIref(struct data *d,struct data *ref);
void ftnvEPI(struct data *d);
void nvfillEPI(struct data *d);
void ftnpEPI(struct data *d);
void zoomEPI(struct data *d);
void revreadEPI(struct data *d);
void prepEPIref(struct data *ref1,struct data *ref2);
void phaseEPIref(struct data *ref1,struct data *ref2,struct data *ref3);
void phaseEPI(struct data *d,struct data *ref);
void phaserampEPI(struct data *d,int mode);
void navcorrEPI(struct data *d);
void stripEPInav(struct data *d);
void weightnavs(struct data *d,double gf);
void setsegscale(struct data *d,struct segscale *scale);
void segscale(struct data *d,struct segscale *scale);
void analyseEPInav(struct data *d);
void getblockEPI(struct data *d,int volindex,int DCCflag);
void setblockEPI(struct data *d);
void setoutvolEPI(struct data *d);
int outvolEPI(struct data *d);
void setnvolsEPI(struct data *d);
void setblockSGE(struct data *d);

/*---------------------------------*/
/*---- prescanEPI.c functions -----*/
/*---------------------------------*/
void prescanEPI(struct data *d);
void settep(struct data *d, int mode);

/*---------------------------*/
/*---- dproc.c functions ----*/
/*---------------------------*/
int *sliceorder(struct data *d,int dim,char *par);
int *phaseorder(struct data *d,int views,int dim,char *par);
int *phaselist(struct data *d,char *par);
void getmax(struct data *d);
void getnoise(struct data *d,int mode);
void equalizenoise(struct data *d,int mode);
void dccorrect(struct data *d);
void scaledata(struct data *d,double factor);
void combine_channels(struct data *d);
void opti_comb(struct data *d, int offset);
double *unwrap1D(double *angles, int nangles);

/*-----------------------------*/
/*---- dproc1D.c functions ----*/
/*-----------------------------*/
void fft1D(struct data *d,int dataorder);
void shiftdata1D(struct data *d,int mode,int dataorder);
void shift1Ddata(struct data *d,int shft,int dataorder);
void weightdata1D(struct data *d,int mode,int dataorder);
void zerofill1D(struct data *d,int mode,int dataorder);
void phaseramp1D(struct data *d,int mode);
void phasedata1D(struct data *d,int mode,int dataorder);
void getmaxtrace1D(struct data *d,int trace);

/*-----------------------------*/
/*---- dproc2D.c functions ----*/
/*-----------------------------*/
void dimorder2D(struct data *d);
void fft2D(struct data *d,int mode);
void ifft2D(struct data *d,int mode);
void shiftdata2D(struct data *d,int mode);
void shift2Ddata(struct data *d,int npshft,int nvshft);
void shift2DCSIdata(struct data *d,int nvshft,int nv2shft);
void weightdata2D(struct data *d,int mode);
void zerofill2D(struct data *d,int mode);
void phaseramp2D(struct data *d,int mode);
void phasedata2D(struct data *d,int mode);
void zoomdata2D(struct data *d,int startdim1, int widthdim1, int startdim2, int widthdim2);
void revread(struct data *d);
void navcorr(struct data *d,struct data *nav);

/*-----------------------------*/
/*---- dproc3D.c functions ----*/
/*-----------------------------*/
void dimorder3D(struct data *d);
void getblock3D(struct data *d,int volindex,int DCCflag);
void getnavblock3D(struct data *d,int volindex,int DCCflag);
void shiftdatadim3(struct data *d,int dataorder,int mode);
void shiftdim3data(struct data *d,int dim3shft);
void shiftD3data(struct data *d,int dim3shft);
void weightdatadim3(struct data *d,int mode);
void zerofilldim3(struct data *d,int mode);
void fftdim3(struct data *d);
void phaserampdim3(struct data *d,int mode);
void phasedatadim3(struct data *d,int mode);

/*-----------------------------*/
/*---- noise2D.c functions ----*/
/*-----------------------------*/
void get2Dnoisematrix(struct data *d,int mode);
void zero2Dnoisematrix(struct data *d);
void print2Dnoisematrix(struct data *d);

/*-----------------------------*/
/*---- dmask2D.c functions ----*/
/*-----------------------------*/
void get2Dmask(struct data *d,int mode);
void fill2Dmask(struct data *d,int mode);

/*---------------------------*/
/*---- dread.c functions ----*/
/*---------------------------*/
void setblock(struct data *d,int dim);
void getblock(struct data *d,int volindex,int DCCflag);
int readfblock(struct data *d,int volindex,int DCCflag);
int readlblock(struct data *d,int volindex,int DCCflag);
int readsblock(struct data *d,int volindex,int DCCflag);
int setoffset(struct data *d,int volindex,int receiver,int dim3index,int dim2index);
void getnavblock(struct data *d,int volindex,int DCCflag);
int readnavfblock(struct data *d,int volindex,int DCCflag);
int readnavlblock(struct data *d,int volindex,int DCCflag);
int readnavsblock(struct data *d,int volindex,int DCCflag);
int setnavoffset(struct data *d,int volindex,int receiver,int dim3index,int seg,int nav);
int sliceindex(struct data *d,int slice);
int segindex(struct data *d,int phase);
int phaseindex(struct data *d,int phase);
int phase2index(struct data *d,int phase2);
int phase3index(struct data *d,int phase3);
void synctablesort2D(struct data *d, int d2, int d3, int *p2, int *p3);
int synctablesort3D(struct data *d, int d2, int d3, int d4, int *p2, int *p3, int *p4);

/*-----------------------------*/
/*---- dread1D.c functions ----*/
/*-----------------------------*/
void getblock1D(struct data *d,int volindex,int DCCflag);

/*-----------------------------*/
/*---- dread2D.c functions ----*/
/*-----------------------------*/
void getblock2D(struct data *d,int volindex,int DCCflag);
void getnavblock2D(struct data *d,int volindex,int DCCflag);
int process2Dblock(struct data *d,char *startpar,char *endpar);

/*---------------------------*/
/*---- dhead.c functions ----*/
/*---------------------------*/
void getdfh(struct data *d);
void getdbh(struct data *d,int blockindex);
void gethcbh(struct data *d,int blockindex);
void reversedfh(struct datafilehead *dfh);
void reversedbh(struct datablockhead *dbh);
void reversehcbh(struct hypercmplxbhead *hcbh);
void printfileheader(struct datafilehead *dfh);
void printfilestatus(struct datafilehead *dfh);
void printblockheader(struct datablockhead *dbh,int blockindex);
void printhcblockheader(struct hypercmplxbhead *hcbh,int blockindex);

/*----------------------------*/
/*---- dutils.c functions ----*/
/*----------------------------*/
void opendata(char *datafile,struct data *d);
void setnvols(struct data *d);
void setstartendvol(struct data *d);
void setdatapars(struct data *d);
void setseqmode(struct data *d);
void setdim(struct data *d);
int spar(struct data *d,char *par,char *str);
int im1D(struct data *d);
int im2D(struct data *d);
int im2DLL(struct data *d);
int im3D(struct data *d);
int im4D(struct data *d);
int imCSI(struct data *d);
double getelem(struct data *d,char *par,int image);
int wprocpar(struct data *d,char *filename);
void copydbh(struct datablockhead *dbh1,struct datablockhead *dbh2);
void setfile(struct file *datafile,char *datadir);
void setreffile(struct file *datafile,struct data *d,char *refpar);
void setfn(struct data *d1,struct data *d2,double multiplier);
void setfn1(struct data *d1,struct data *d2,double multiplier);
void setdimstatus(struct data *d,struct dimstatus *status,int block);
void copynblocks(struct data *d1,struct data *d2);
void copymaskpars(struct data *d1,struct data *d2);
void copysmappars(struct data *d1,struct data *d2);
void copysensepars(struct data *d1,struct data *d2);
int checkequal(struct data *d1,struct data *d2,char *par,char *comment);
void initdatafrom(struct data *d1, struct data *d2);
void initdata(struct data *d);
void nulldata(struct data *d);
void cleardimorder(struct data *d);
void clearstatus(struct data *d);
void zeromax(struct data *d);
void zeronoise(struct data *d);
void initnoise(struct data *d);
void nullnoise(struct data *d);
void closedata(struct data *d);

/*------------------------------*/
/*---- dutils1D.c functions ----*/
/*------------------------------*/
void clear1Dall(struct data *d);
void clear1Ddata(struct data *d);
void clearnoise1D(struct data *d);
void clear1Dmask(struct data *d);

/*------------------------------*/
/*---- dutils2D.c functions ----*/
/*------------------------------*/
int check2Dref(struct data *d,struct data *ref);
void copy2Ddata(struct data *d1,struct data *d2);
void clear2Dall(struct data *d);
void clear2Ddata(struct data *d);
void clearCSIdata(struct data *d);
void clearnoise2D(struct data *d);
void clear2Dmask(struct data *d);
void checkCrop(struct data *d);
void checkCrop3D(struct data *d);

/*------------------------------------*/
/*---- pars.c 'procpar' functions ----*/
/*------------------------------------*/
void getpars(char *procpar,struct data *d);
void getprocparpars(char *procpar,struct pars *p);
void skipprocparvals(FILE *fp,struct pars *p,int type,int nvals);
void procparerror(const char *function,char *par,char *str);
void setarray(struct pars *p,struct pars *a);
int list2array(char *par,struct pars *p1,struct pars *p2);
void setval(struct pars *p,char *par,double value);
void setsval(struct pars *p,char *par,char *svalue);
void cppar(char *par1,struct pars *p1,char *par2,struct pars *p2);
void copypar(char *par,struct pars *p1,struct pars *p2);
void copypars(struct pars *p1, struct pars *p2);
void copyvalues(struct pars *p1,int ix1,struct pars *p2,int ix2);
void mallocnpars(struct pars *p);
void freenpars(struct pars *p);
void printarray(struct pars *a,char *array);
void printprocpar(struct pars *p);
int ptype(char *par,struct pars *p);
int nvals(char *par,struct pars *p);
int *ival(char *par,struct pars *p);
double *val(char *par,struct pars *p);
char **sval(char *par,struct pars *p);
int parindex(char *par,struct pars *p);
int arraycheck(char *par,struct pars *a);
int getcycle(char *par,struct pars *a);
int cyclefaster(char *par1,char *par2,struct pars *a);
void clearpars(struct pars *p);
void cleararray(struct pars *a);
void nullpars(struct pars *p);

/*-------------------------------------*/
/*---- utils.c utilities functions ----*/
/*-------------------------------------*/
int check_CPU_type();
void complexmatrixmultiply(gsl_matrix_complex *A,gsl_matrix_complex *B,gsl_matrix_complex *C);
void complexmatrixconjugate(gsl_matrix_complex *A);
int round2int(double dval);
void reverse2ByteOrder(int nele,char *ptr);
void reverse4ByteOrder(int nele,char *ptr);
void reverse8ByteOrder(int nele,char *ptr);
int doublecmp(const void *double1,const void *double2);
int nomem(char *file,const char *function,int line);
int createdir(char *dirname);
int checkdir(char *dirname);
int checkfile(char *filename);

/*-----------------------------*/
/*---- write1D.c functions ----*/
/*-----------------------------*/
void w1Dtrace(struct data *d,int receiver,int trace,int fileid);
void w1Dblock(struct data *d,int receiver,int fileid);
void wdfh(struct data *d,int fileid);
void wdbh(struct data *d,int fileid);
void openfpw(struct data *d,int fileid);
void closefp(struct data *d,int fileid);

/*--------------------------------*/
/*---- fdfwrite2D.c functions ----*/
/*--------------------------------*/
void w2Dfdfs(struct data *d,int type,int scalevalue,int volindex);
int magnitude2Dfdfs(struct data *d,char *par,char *parIR,char *outdir,int type,int precision,int volindex);
void other2Dfdfs(struct data *d,char *par,char *outdir,int type,int precision,int volindex);
void gen2Dfdfs(struct data *d,int mode,char *outdir,int type,int precision,int volindex);
void w2Dfdf(char *filename,struct data *d,int image,int slice,int sliceindex,int echo,int receiver,int type,int precision);
void wcomb2Dfdf(char *filename,struct data *d,int image,int slice,int sliceindex,int echo,int type,int precision);
void gen2Dfdfhdr(struct data *d,int image,int slice,int echo,int receiver,int type,int precision);
int validtype(int type);
int addpar2hdr(struct pars *p,int ix);
double sliceposition(struct data *d,int slice);

/*--------------------------------*/
/*---- fdfwrite3D.c functions ----*/
/*--------------------------------*/
void w3Dfdfs(struct data *d,int type,int scalevalue,int volindex);
int magnitude3Dfdfs(struct data *d,char *par,char *parIR,char *outdir,int type,int precision,int volindex);
void other3Dfdfs(struct data *d,char *par,char *outdir,int type,int precision,int volindex);
void gen3Dfdfs(struct data *d,int mode,char *outdir,int type,int precision,int volindex);
void w3Dfdf(char *filename,struct data *d,int image,int slab,int echo,int receiver,int type,int precision);
void wcomb3Dfdf(char *filename,struct data *d,int image,int slab,int echo,int type,int precision);
void gen3Dfdfhdr(struct data *d,int image,int slice,int echo,int receiver,int type,int precision);

/*-----------------------------*/
/*---- rawIO3D.c functions ----*/
/*-----------------------------*/
void wrawbin3D(struct data *d,int dataorder,int type,int precision);
void gen3Draw(struct data *d,int output,char *outdir,int dataorder,int type,int precision);
void w3Draw(char *filename,struct data *d,int receiver,int type,int precision);
void w3DrawD3(char *filename,struct data *d,int receiver);
void wcomb3Draw(char *filename,struct data *d,int type,int precision);
void cleardim3data(struct data *d);
void rrawbin3D(struct data *d,int dataorder,int type,int precision);
void get3Draw(struct data *d,int mode,char *indir,int dataorder);
void r3Draw(char *filename,struct data *d,int receiver);
void r3DrawD3(char *filename,struct data *d,int receiver);
void delrawbin3D(struct data *d,int dataorder,int type);
void del3Draw(struct data *d,char *dir);

/*--------------------------------*/
/*---- niftiwrite.c functions ----*/
/*--------------------------------*/
void wnifti(struct data *d,int type,int precision,int volindex);
void writenifti(struct data *d,int type,int precision,int format,int volindex);
int magnifti(struct data *d,char *par,char *parIR,char *outdir,int type,int precision,int format,int volindex);
void othernifti(struct data *d,char *par,char *outdir,int type,int precision,int format,int volindex);
void gennifti(struct data *d,int mode,char *outdir,int type,int precision,int format,int volindex);
void wniftihdr(char *filebase,struct data *d,int precision,int format,int image);
void wniftidata(char *filebase,struct data *d,int receiver,int type,int precision,int format,int newfile);
void wcombniftidata(char *filebase,struct data *d,int type,int precision,int format,int newfile);

/*--------------------------------*/
/*---- tifwrite2D.c functions ----*/
/*--------------------------------*/
int wtifs2D(struct data *d,int type,int scalevalue,int volindex);
int wtif2D(char *filename,struct data *d,int receiver,int slice,int type,double scale);

/*--------------------------------*/
/*---- rawwrite2D.c functions ----*/
/*--------------------------------*/
int wrawbin2D(struct data *d,int type,int precision,int volindex);
int wraw2D(char *filename,struct data *d,int receiver,int slice,int type,int precision);

/*-----------------------------*/
/*---- options.c functions ----*/
/*-----------------------------*/
void getoptions(struct file *f,int argc,char *argv[]);

/*---------------------------*/
/*---- For byte swapping ----*/
/*---------------------------*/
EXTERN int reverse_byte_order;

/*-------------------------*/
/*---- For VnmrJ recon ----*/
/*-------------------------*/
EXTERN int vnmrj_recon;
EXTERN char vnmrj_path[MAXPATHLEN];

/*-------------------------------------------------------------*/
/*---- If C++ compiler is used it needs to know above is C ----*/
/*-------------------------------------------------------------*/
//#ifdef __cplusplus
//}
//#endif

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


#pragma once

#include <gsl/span>
#include <utility>

namespace hz::range {
    template<typename T>
    using contiguous_view = gsl::span<T>;

    template<typename Container>
    auto make_contiguous_view(Container && c) noexcept -> contiguous_view<std::remove_reference_t<decltype(std::data(*std::forward<Container>(c)))>> {
        return gsl::make_span(std::forward<Container>(c));
    }
}


#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <signal.h>
#include <gsl/gsl_rng.h>

#include "allvars.h"
#include "proto.h"

#ifdef DEBUG
#include <fenv.h>
void enable_core_dumps_and_fpu_exceptions(void)
{
  struct rlimit rlim;
  extern int feenableexcept(int __excepts);

  /* enable floating point exceptions */

  /*
     feenableexcept(FE_DIVBYZERO | FE_INVALID);
   */

  /* Note: FPU exceptions appear not to work properly
   * when the Intel C-Compiler for Linux is used
   */

  /* set core-dump size to infinity */
  getrlimit(RLIMIT_CORE, &rlim);
  rlim.rlim_cur = RLIM_INFINITY;
  setrlimit(RLIMIT_CORE, &rlim);

  /* MPICH catches the signales SIGSEGV, SIGBUS, and SIGFPE....
   * The following statements reset things to the default handlers,
   * which will generate a core file.
   */
  /*
     signal(SIGSEGV, catch_fatal);
     signal(SIGBUS, catch_fatal);
     signal(SIGFPE, catch_fatal);
     signal(SIGINT, catch_fatal);
   */

  signal(SIGSEGV, SIG_DFL);
  signal(SIGBUS, SIG_DFL);
  signal(SIGFPE, SIG_DFL);
  signal(SIGINT, SIG_DFL);

  /* Establish a handler for SIGABRT signals. */
  signal(SIGABRT, catch_abort);
}

void catch_abort(int sig)
{
  MPI_Finalize();
  exit(0);
}

void catch_fatal(int sig)
{
  terminate_processes();
  MPI_Finalize();

  signal(sig, SIG_DFL);
  raise(sig);
}

void terminate_processes(void)
{
  pid_t my_pid;
  char buf[500], hostname[500], *cp;
  char commandbuf[500];
  FILE *fd;
  int i, pid;

  sprintf(buf, "%s%s", All.OutputDir, "PIDs.txt");

  my_pid = getpid();

  if((fd = fopen(buf, "r")))
    {
      for(i = 0; i < NTask; i++)
	{
	  int ret;

	  ret = fscanf(fd, "%s %d", hostname, &pid);

	  cp = hostname;
	  while(*cp)
	    {
	      if(*cp == '.')
		*cp = 0;
	      else
		cp++;
	    }

	  if(my_pid != pid)
	    {
	      sprintf(commandbuf, "ssh %s kill -ABRT %d", hostname, pid);
	      printf("--> %s\n", commandbuf);
	      fflush(stdout);
#ifndef NOCALLSOFSYSTEM
	      ret = system(commandbuf);
#endif
	    }
	}

      fclose(fd);
    }
}

void write_pid_file(void)
{
  pid_t my_pid;
  char mode[8], buf[500];
  FILE *fd;
  int i;

  my_pid = getpid();

  sprintf(buf, "%s%s", All.OutputDir, "PIDs.txt");

  if(RestartFlag == 0)
    strcpy(mode, "w");
  else
    strcpy(mode, "a");

  for(i = 0; i < NTask; i++)
    {
      if(ThisTask == i)
	{
	  if(ThisTask == 0)
	    sprintf(mode, "w");
	  else
	    sprintf(mode, "a");

	  if((fd = fopen(buf, mode)))
	    {
	      fprintf(fd, "%s %d\n", getenv("HOST"), (int) my_pid);
	      fclose(fd);
	    }
	}

      MPI_Barrier(MPI_COMM_WORLD);
    }
}
#endif

double get_random_number(unsigned int id)
{
  return RndTable[(id % RNDTABLE)];
}

void set_random_numbers(void)
{
  int i;

  for(i = 0; i < RNDTABLE; i++)
    RndTable[i] = gsl_rng_uniform(random_generator);
}

/* returns the number of cpu-ticks in seconds that
 * have elapsed. (or the wall-clock time)
 */
double second(void)
{
#ifdef WALLCLOCK
  return MPI_Wtime();
#else
  return ((double) clock()) / CLOCKS_PER_SEC;
#endif

  /* note: on AIX and presumably many other 32bit systems,
   * clock() has only a resolution of 10ms=0.01sec
   */
}

double measure_time(void)	/* strategy: call this at end of functions to account for time in this function, and before another (nontrivial) function is called */
{
  double t, dt;

  t = second();
  dt = t - WallclockTime;
  WallclockTime = t;

  return dt;
}

/* returns the time difference between two measurements
 * obtained with second(). The routine takes care of the
 * possible overflow of the tick counter on 32bit systems.
 */
double timediff(double t0, double t1)
{
  double dt;

  dt = t1 - t0;

  if(dt < 0)			/* overflow has occured (for systems with 32bit tick counter) */
    {
#ifdef WALLCLOCK
      dt = 0;
#else
      dt = t1 + pow(2, 32) / CLOCKS_PER_SEC - t0;
#endif
    }

  return dt;
}

#ifdef X86FIX

#define _FPU_SETCW(x) asm volatile ("fldcw %0": :"m" (x));
#define _FPU_GETCW(x) asm volatile ("fnstcw %0":"=m" (x));
#define _FPU_EXTENDED 0x0300
#define _FPU_DOUBLE   0x0200

void x86_fix(void)
{
  unsigned short dummy, new_cw;
  unsigned short *old_cw;

  old_cw = &dummy;

  _FPU_GETCW(*old_cw);
  new_cw = (*old_cw & ~_FPU_EXTENDED) | _FPU_DOUBLE;
  _FPU_SETCW(new_cw);
}

#endif

void sumup_large_ints(int n, int *src, long long *res)
{
  int i, j, *numlist;

  numlist = (int *) mymalloc(NTask * n * sizeof(int));
  MPI_Allgather(src, n, MPI_INT, numlist, n, MPI_INT, MPI_COMM_WORLD);

  for(j = 0; j < n; j++)
    res[j] = 0;

  for(i = 0; i < NTask; i++)
    for(j = 0; j < n; j++)
      res[j] += numlist[i * n + j];

  myfree(numlist);
}

void sumup_longs(int n, long long *src, long long *res)
{
  int i, j;
  long long *numlist;

  numlist = (long long *) mymalloc(NTask * n * sizeof(long long));
  MPI_Allgather(src, n * sizeof(long long), MPI_BYTE, numlist, n * sizeof(long long), MPI_BYTE,
		MPI_COMM_WORLD);

  for(j = 0; j < n; j++)
    res[j] = 0;

  for(i = 0; i < NTask; i++)
    for(j = 0; j < n; j++)
      res[j] += numlist[i * n + j];

  myfree(numlist);
}

size_t sizemax(size_t a, size_t b)
{
  if(a < b)
    return b;
  else
    return a;
}

void report_VmRSS(void)
{
  pid_t my_pid;
  FILE *fd;
  char buf[1024];

  my_pid = getpid();

  sprintf(buf, "/proc/%d/status", my_pid);

  if((fd = fopen(buf, "r")))
    {
      while(1)
	{
	  if(fgets(buf, 500, fd) != buf)
	    break;

	  if(strncmp(buf, "VmRSS", 5) == 0)
	    {
	      printf("ThisTask=%d: %s", ThisTask, buf);
	    }
	  if(strncmp(buf, "VmSize", 6) == 0)
	    {
	      printf("ThisTask=%d: %s", ThisTask, buf);
	    }
	}
      fclose(fd);
    }
}


#pragma once

#include <gsl/span>

#include "halley/text/halleystring.h"
#include "halley/resources/resource.h"
#include "halley/maths/range.h"
#include "halley/maths/vector4.h"
#include "halley/data_structures/maybe.h"
#include <map>
#include <vector>

#include "halley/utils/type_traits.h"

#if defined(DEV_BUILD)
#define STORE_CONFIG_NODE_PARENTING
#endif

namespace Halley {
	class Serializer;
	class Deserializer;
	class ConfigNode;

	template<typename T>
	struct HasToConfigNode
	{
	private:
		typedef std::true_type yes;
		typedef std::false_type no;
		template<typename U> static auto test(int) -> decltype(std::declval<U>().toConfigNode(), yes());
		template<typename> static no test(...);

	public:
		static constexpr bool value = std::is_same<decltype(test<T>(0)),yes>::value;
	};

	template<typename T>
	struct HasConfigNodeConstructor
	{
	private:
		typedef std::true_type yes;
		typedef std::false_type no;
		template<typename U> static auto test(int) -> decltype(U(std::declval<ConfigNode>()), yes());
		template<typename> static no test(...);

	public:
		static constexpr bool value = std::is_same<decltype(test<T>(0)),yes>::value;
	};

    enum class ConfigNodeType
    {
        Undefined,
        String,
        Sequence,
        Map,
        Int,
        Float,
        Int2,
        Float2,
        Int3,
        Float3,
        Int4,
        Float4,
        Bytes,
        DeltaSequence, // For delta coding
        DeltaMap,      // For delta coding
        Noop,          // For delta coding
        Idx,           // For delta coding
        Del            // For delta coding
    };

    template <>
    struct EnumNames< ConfigNodeType >
    {
        constexpr std::array< const char*, 17 > operator()() const
        {
            return { { "undefined",
                "string",
                "sequence",
                "map",
                "int",
                "float",
                "int2",
                "float2",
                "int3",
                "float3",
                "int4",
                "float4",
                "bytes",
                "deltaSequence",
                "deltaMap",
                "noop",
                "idx"
                "del" } };
        }
    };

    class ConfigFile;

    class ConfigNode
    {
        friend class ConfigFile;

    public:
        using MapType = std::map< String, ConfigNode >;
        using SequenceType = std::vector< ConfigNode >;

        struct NoopType
        {
        };
        struct DelType
        {
        };
        struct IdxType
        {
            int start;
            int len;
            IdxType() = default;
            IdxType( int start, int len ) :
                start( start ), len( len ) { }
        };

        ConfigNode();
        explicit ConfigNode( const ConfigNode& other );
        ConfigNode( ConfigNode&& other ) noexcept;
        ConfigNode( MapType entryMap );
        ConfigNode( SequenceType entryList );
        explicit ConfigNode( String value );
        explicit ConfigNode( const char* value );
        explicit ConfigNode( bool value );
        explicit ConfigNode( int value );
        explicit ConfigNode( float value );
        explicit ConfigNode( Angle1f value );
        explicit ConfigNode( Vector2i value );
        explicit ConfigNode( Vector2f value );
        explicit ConfigNode( Vector3i value );
        explicit ConfigNode( Vector3f value );
        explicit ConfigNode( Vector4i value );
        explicit ConfigNode( Vector4f value );
        explicit ConfigNode( Bytes value );
        explicit ConfigNode( NoopType value );
        explicit ConfigNode( DelType value );
        explicit ConfigNode( IdxType value );

		template <typename T>
		explicit ConfigNode(const std::vector<T>& sequence)
		{
			SequenceType seq;
			seq.reserve(sequence.size());
			for (auto& e: sequence) {
				if constexpr (HasToConfigNode<T>::value) {
					seq.push_back(e.toConfigNode());
				} else {
					seq.push_back(ConfigNode(e));
				}
			}
			*this = seq;
		}

        ~ConfigNode();

        ConfigNode& operator=( const ConfigNode& other );
        ConfigNode& operator=( ConfigNode&& other ) noexcept;
        ConfigNode& operator=( bool value );
        ConfigNode& operator=( int value );
        ConfigNode& operator=( float value );
        ConfigNode& operator=( Angle1f value );
        ConfigNode& operator=( Vector2i value );
        ConfigNode& operator=( Vector2f value );
        ConfigNode& operator=( Vector3i value );
        ConfigNode& operator=( Vector3f value );
        ConfigNode& operator=( Vector4i value );
        ConfigNode& operator=( Vector4f value );

        ConfigNode& operator=( MapType entryMap );
        ConfigNode& operator=( SequenceType entryList );
        ConfigNode& operator=( String value );
        ConfigNode& operator=( Bytes value );
        ConfigNode& operator=( gsl::span< const gsl::byte > bytes );

        ConfigNode& operator=( const char* value );

        ConfigNode& operator=( NoopType value );
        ConfigNode& operator=( DelType value );
        ConfigNode& operator=( IdxType value );

		template <typename T>
		ConfigNode& operator=(const std::vector<T>& sequence)
		{
			SequenceType seq;
			seq.reserve(sequence.size());
			for (auto& e: sequence) {
				if constexpr (HasToConfigNode<T>::value) {
					seq.push_back(e.toConfigNode());
				} else {
					seq.push_back(ConfigNode(e));
				}
			}
			return *this = seq;
		}

        bool operator==( const ConfigNode& other ) const;
        bool operator!=( const ConfigNode& other ) const;

        ConfigNodeType getType() const;

        void serialize( Serializer& s ) const;
        void deserialize( Deserializer& s );

        int asInt() const;
        float asFloat() const;
        bool asBool() const;
        Angle1f asAngle1f() const;
        Vector2i asVector2i() const;
        Vector2f asVector2f() const;
        Vector3i asVector3i() const;
        Vector3f asVector3f() const;
        Vector4i asVector4i() const;
        Vector4f asVector4f() const;
        Range< float > asFloatRange() const;
        String asString() const;
        const Bytes& asBytes() const;

        int asInt( int defaultValue ) const;
        float asFloat( float defaultValue ) const;
        bool asBool( bool defaultValue ) const;
        String asString( const String& defaultValue ) const;
        Angle1f asAngle1f( Angle1f defaultValue ) const;
        Vector2i asVector2i( Vector2i defaultValue ) const;
        Vector2f asVector2f( Vector2f defaultValue ) const;
        Vector3i asVector3i( Vector3i defaultValue ) const;
        Vector3f asVector3f( Vector3f defaultValue ) const;
        Vector4i asVector4i( Vector4i defaultValue ) const;
        Vector4f asVector4f( Vector4f defaultValue ) const;

		template <typename T>
		std::vector<T> asVector() const
		{
			if (type == ConfigNodeType::Sequence) {
				std::vector<T> result;
				result.reserve(asSequence().size());
				for (const auto& e : asSequence()) {
					if constexpr (HasConfigNodeConstructor<T>::value) {
						result.emplace_back(T(e));
					} else {
						result.emplace_back(e.convertTo(Tag<T>()));
					}
				}
				return result;
			} else {
				throw Exception("Can't convert " + getNodeDebugId() + " from " + toString(getType()) + " to std::vector<T>.", HalleyExceptions::Resources);
			}
		}

		template <typename T>
		std::vector<T> asVector(const std::vector<T>& defaultValue) const
		{
			if (type == ConfigNodeType::Sequence) {
				return asVector<T>();
			} else {
				return defaultValue;
			}
		}

        const SequenceType& asSequence() const;
        const MapType& asMap() const;
        SequenceType& asSequence();
        MapType& asMap();

        void ensureType( ConfigNodeType type );

        bool hasKey( const String& key ) const;
        void removeKey( const String& key );

        ConfigNode& operator[]( const String& key );
        ConfigNode& operator[]( size_t idx );

        const ConfigNode& operator[]( const String& key ) const;
        const ConfigNode& operator[]( size_t idx ) const;

        SequenceType::iterator begin();
        SequenceType::iterator end();

        SequenceType::const_iterator begin() const;
        SequenceType::const_iterator end() const;

        void reset();
        void setOriginalPosition( int line, int column );
        void setParent( const ConfigNode* parent, int idx );
        void propagateParentingInformation( const ConfigFile* parentFile );

        inline void assertValid() const
        {
            Expects( intData != 0xCDCDCDCD );
            Expects( intData != 0xDDDDDDDD );
        }

        struct BreadCrumb
        {
            const BreadCrumb* prev = nullptr;
            String key;
            OptionalLite< int > idx;
            int depth = 0;

            BreadCrumb() = default;
            BreadCrumb( const BreadCrumb& prev, String key ) :
                prev( &prev ), key( std::move( key ) ), depth( prev.depth + 1 ) { }
            BreadCrumb( const BreadCrumb& prev, int index ) :
                prev( &prev ), idx( index ), depth( prev.depth + 1 ) { }

            bool hasKeyAt( const String& key, int depth ) const;
            bool hasIndexAt( int idx, int depth ) const;
        };

        class IDeltaCodeHints
        {
        public:
            virtual ~IDeltaCodeHints() = default;

            virtual std::optional< size_t > getSequenceMatch( const SequenceType& seq, const ConfigNode& newValue, size_t curIdx, const BreadCrumb& breadCrumb ) const = 0;
            virtual bool doesSequenceOrderMatter( const BreadCrumb& breadCrumb ) const { return true; }
            virtual bool canDeleteKey( const String& key, const BreadCrumb& breadCrumb ) const { return true; }
            virtual bool canDeleteAnyKey() const { return true; }
            virtual bool shouldBypass( const BreadCrumb& breadCrumb ) const { return false; }
            virtual bool areNullAndEmptyEquivalent( const BreadCrumb& breadCrumb ) const { return false; }
        };

        static ConfigNode createDelta( const ConfigNode& from, const ConfigNode& to, const IDeltaCodeHints* hints = nullptr );
        static ConfigNode applyDelta( const ConfigNode& from, const ConfigNode& delta );
        void applyDelta( const ConfigNode& delta );
        void decayDeltaArtifacts();

    private:
        template < typename T >
        class Tag
        {
        };

        union {
            String* strData;
            MapType* mapData;
            SequenceType* sequenceData;
            Bytes* bytesData;
            void* rawPtrData;
            int intData;
            float floatData;
            Angle1f ang1fData;
            Vector2i vec2iData;
            Vector2f vec2fData;
            Vector3i vec3iData;
            Vector3f vec3fData;
            Vector4i vec4iData;
            Vector4f vec4fData;
        };
        ConfigNodeType type = ConfigNodeType::Undefined;
        int auxData = 0; // Used by delta coding

#if defined(STORE_CONFIG_NODE_PARENTING)
        struct ParentingInfo
        {
            int line = 0;
            int column = 0;
            int idx = 0;
            const ConfigNode* node = nullptr;
            const ConfigFile* file = nullptr;
        };
        std::unique_ptr< ParentingInfo > parent;
#endif
        static ConfigNode undefinedConfigNode;
        static String undefinedConfigNodeName;

        template < typename T >
        void deserializeContents( Deserializer& s )
        {
            T v;
            s >> v;
            *this = std::move( v );
        }

        String getNodeDebugId() const;
        String backTrackFullNodeName() const;

        int convertTo( Tag< int > tag ) const;
        float convertTo( Tag< float > tag ) const;
        bool convertTo( Tag< bool > tag ) const;
        Angle1f convertTo( Tag< Angle1f > tag ) const;
        Vector2i convertTo( Tag< Vector2i > tag ) const;
        Vector2f convertTo( Tag< Vector2f > tag ) const;
        Vector3i convertTo( Tag< Vector3i > tag ) const;
        Vector3f convertTo( Tag< Vector3f > tag ) const;
        Vector4i convertTo( Tag< Vector4i > tag ) const;
        Vector4f convertTo( Tag< Vector4f > tag ) const;
        Range< float > convertTo( Tag< Range< float > > tag ) const;
        String convertTo( Tag< String > tag ) const;
        const Bytes& convertTo( Tag< Bytes& > tag ) const;

        bool isNullOrEmpty() const;

        static ConfigNode doCreateDelta( const ConfigNode& from, const ConfigNode& to, const BreadCrumb& breadCrumb, const IDeltaCodeHints* hints );
        static ConfigNode createMapDelta( const ConfigNode& from, const ConfigNode& to, const BreadCrumb& breadCrumb, const IDeltaCodeHints* hints );
        static ConfigNode createSequenceDelta( const ConfigNode& from, const ConfigNode& to, const BreadCrumb& breadCrumb, const IDeltaCodeHints* hints );
        void applyMapDelta( const ConfigNode& delta );
        void applySequenceDelta( const ConfigNode& delta );

        bool isEquivalent( const ConfigNode& other ) const;
        bool isEquivalentStrictOrder( const ConfigNode& other ) const;
    };
} // namespace Halley


/*  Copyright 2017 International Business Machines Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.  */

#pragma once

#include <kernelpp/types.h>
#include <kernelpp/kernel.h>

#include <gsl.h>

namespace mwe {
namespace kernels
{
    using kernelpp::compute_mode;
    using kernelpp::error_code;

    /*  Declare a kernel 'add' which is overloaded to operate
     *  on single or array-like inputs.
     *
     *  Also, declare the compute modes (CPU,GPU) which
     *  this kernel will support.
     */
    KERNEL_DECL(add,
        compute_mode::CPU, compute_mode::CUDA)
    {
        template <compute_mode> static kernelpp::variant<int, error_code> op(
            int a, int b
            );

        template <compute_mode> static error_code op(
            const gsl::span<int> a, const gsl::span<int> b, gsl::span<int> result
            );
    };
}
}


//
// Created by Huy Vo on 2019-06-29.
//

#ifndef PACMENSL_SRC_PETSCWRAP_PETSCWRAP_H_
#define PACMENSL_SRC_PETSCWRAP_PETSCWRAP_H_
#include <petsc.h>
#include <iostream>
#include "Sys.h"
namespace pacmensl {

template<typename PetscT>
class Petsc {
 protected:
  PetscT dat = nullptr;

  int Destroy(PetscT *obj);

 public:

  Petsc() {
    static_assert(std::is_convertible<PetscT, Vec>::value || std::is_convertible<PetscT, Mat>::value
                      || std::is_convertible<PetscT, IS>::value || std::is_convertible<PetscT, VecScatter>::value
                      || std::is_convertible<PetscT, KSP>::value || std::is_convertible<PetscT, TS>::value,
                  "pacmensl::Petsc can only wrap PETSc objects.");
  }

  PetscT *mem() { return &dat; }

  const PetscT *mem() const { return &dat; }

  bool IsEmpty() { return (dat == nullptr); }

  operator PetscT() { return dat; }

  ~Petsc() {
    Destroy(&dat);
  }
};

template<>
int Petsc<Vec>::Destroy(Vec *obj);

template<>
int Petsc<Mat>::Destroy(Mat *obj);

template<>
int Petsc<IS>::Destroy(IS *obj);

template<>
int Petsc<VecScatter>::Destroy(VecScatter *obj);

template<>
int Petsc<KSP>::Destroy(KSP *obj);

template<>
int Petsc<TS>::Destroy(TS *obj);

PacmenslErrorCode ExpandVec(Petsc<Vec> &p, const std::vector<PetscInt> &new_indices, const PetscInt new_local_size);
PacmenslErrorCode ExpandVec(Vec &p, const std::vector<PetscInt> &new_indices, const PetscInt new_local_size);
}
#endif //PACMENSL_SRC_PETSCWRAP_PETSCWRAP_H_