/* bst.c
 *
 * Copyright (C) 2018 Patrick Alken
 *
 * 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.
 */

#include <config.h>
#include <stdlib.h>
#include <math.h>

#include <gsl/gsl_math.h>
#include <gsl/gsl_bst.h>
#include <gsl/gsl_errno.h>

static void * bst_malloc(size_t size, void * params);
static void bst_free(void * block, void * params);

static const gsl_bst_allocator bst_default_allocator =
{
  bst_malloc,
  bst_free
};

/*
gsl_bst_alloc()
  Allocate binary search tree

Inputs: T         - tree type
        allocator - memory allocator
        compare   - comparison function
        params    - parameters to pass to allocator and compare
*/

gsl_bst_workspace *
gsl_bst_alloc(const gsl_bst_type * T, const gsl_bst_allocator * allocator,
              gsl_bst_cmp_function * compare, void * params)
{
  int status;
  gsl_bst_workspace *w;

  w = calloc(1, sizeof(gsl_bst_workspace));
  if (w == NULL)
    {
      GSL_ERROR_NULL("failed to allocate bst workspace", GSL_ENOMEM);
    }

  w->type = T;

  status = (w->type->init)(allocator != NULL ? allocator : &bst_default_allocator, compare, params, (void *) &w->table);
  if (status)
    {
      gsl_bst_free(w);
      GSL_ERROR_NULL("failed to initialize bst", GSL_EFAILED);
    }

  return w;
}

void
gsl_bst_free(gsl_bst_workspace * w)
{
  /* free tree nodes */
  gsl_bst_empty(w);

  free(w);
}

/* delete all nodes from tree */
int
gsl_bst_empty(gsl_bst_workspace * w)
{
  return (w->type->empty)((void *) &w->table);
}

/*
gsl_bst_insert()
  Inserts |item| into tree

AVL: if duplicate found, returns pointer to item without inserting
AVLmult: if duplicate found, increase multiplicity for that node

If no duplicate found, insert item and return pointer to item.
Returns NULL if a memory allocation error occurred.
*/

void *
gsl_bst_insert(void * item, gsl_bst_workspace * w)
{
  return (w->type->insert)(item, (void *) &w->table);
}

void *
gsl_bst_find(const void * item, const gsl_bst_workspace * w)
{
  return (w->type->find)(item, (const void *) &w->table);
}

void *
gsl_bst_remove(const void * item, gsl_bst_workspace * w)
{
  return (w->type->remove)(item, (void *) &w->table);
}

/* return number of nodes in tree */
size_t
gsl_bst_nodes(const gsl_bst_workspace * w)
{
  return (w->type->nodes)((const void *) &w->table);
}

/* return size (in bytes) of each node in tree */
size_t
gsl_bst_node_size(const gsl_bst_workspace * w)
{
  return w->type->node_size;
}

const char *
gsl_bst_name(const gsl_bst_workspace * w)
{
  return w->type->name;
}

/**********************************************
 * INTERNAL ROUTINES                          *
 **********************************************/

static void *
bst_malloc(size_t size, void * params)
{
  (void) params; /* avoid unused parameter warning */
  return malloc(size);
}

static void
bst_free(void * block, void * params)
{
  (void) params; /* avoid unused parameter warning */
  free(block);
}


/*
    -- MAGMA (version 2.5.4) --
       Univ. of Tennessee, Knoxville
       Univ. of California, Berkeley
       Univ. of Colorado, Denver
       @date October 2020
*/

#ifndef MAGMA_TYPES_H
#define MAGMA_TYPES_H

#include <stdint.h>
#include <assert.h>

// for backwards compatability
#ifdef HAVE_clAmdBlas
#define HAVE_clBLAS
#endif

// each implementation of MAGMA defines HAVE_* appropriately.
#if ! defined(HAVE_CUBLAS) && ! defined(HAVE_clBLAS) && ! defined(HAVE_MIC)
#define HAVE_CUBLAS
#endif

// =============================================================================
// C99 standard defines __func__. Some older compilers use __FUNCTION__.
// Note __func__ in C99 is not a macro, so ifndef __func__ doesn't work.
#if __STDC_VERSION__ < 199901L
  #ifndef __func__
    #if __GNUC__ >= 2 || _MSC_VER >= 1300
      #define __func__ __FUNCTION__
    #else
      #define __func__ "<unknown>"
    #endif
  #endif
#endif

// =============================================================================
// To use int64_t, link with mkl_intel_ilp64 or similar (instead of mkl_intel_lp64).
// Similar to magma_int_t we declare magma_index_t used for row/column indices in sparse
#if defined(MAGMA_ILP64) || defined(MKL_ILP64)
typedef long long int magma_int_t;  // MKL uses long long int, not int64_t
#else
typedef int magma_int_t;
#endif

typedef int magma_index_t;
typedef unsigned int magma_uindex_t;

// Define new type that the precision generator will not change (matches PLASMA)
typedef double real_Double_t;

// =============================================================================
// define types specific to implementation (CUDA, OpenCL, MIC)
// define macros to deal with complex numbers
#if defined(HAVE_CUBLAS)
    // include cublas_v2.h, unless cublas.h has already been included, e.g., via magma.h
    #ifndef CUBLAS_H_
    #include <cuda.h>    // for CUDA_VERSION
    #include <cublas_v2.h>
    #endif

    #include <cusparse_v2.h>

    #ifdef __cplusplus
    extern "C" {
    #endif

    // opaque queue structure
    struct magma_queue;
    typedef struct magma_queue* magma_queue_t;
    typedef cudaEvent_t    magma_event_t;
    typedef magma_int_t    magma_device_t;

    // Half precision in CUDA
    #if defined(__cplusplus) && CUDA_VERSION >= 7500
    #include <cuda_fp16.h>
    typedef __half           magmaHalf;
    #else
    // use short for cuda older than 7.5
    // corresponding routines would not work anyway since there is no half precision
    typedef short            magmaHalf;
    #endif    // CUDA_VERSION >= 7500

    typedef cuDoubleComplex magmaDoubleComplex;
    typedef cuFloatComplex  magmaFloatComplex;

    cudaStream_t     magma_queue_get_cuda_stream    ( magma_queue_t queue );
    cublasHandle_t   magma_queue_get_cublas_handle  ( magma_queue_t queue );
    cusparseHandle_t magma_queue_get_cusparse_handle( magma_queue_t queue );

    /// @addtogroup magma_complex
    /// @{

    #define MAGMA_Z_MAKE(r,i)     make_cuDoubleComplex(r, i)    ///< @return complex number r + i*sqrt(-1).
    #define MAGMA_Z_REAL(a)       (a).x                         ///< @return real component of a.
    #define MAGMA_Z_IMAG(a)       (a).y                         ///< @return imaginary component of a.
    #define MAGMA_Z_ADD(a, b)     cuCadd(a, b)                  ///< @return (a + b).
    #define MAGMA_Z_SUB(a, b)     cuCsub(a, b)                  ///< @return (a - b).
    #define MAGMA_Z_MUL(a, b)     cuCmul(a, b)                  ///< @return (a * b).
    #define MAGMA_Z_DIV(a, b)     cuCdiv(a, b)                  ///< @return (a / b).
    #define MAGMA_Z_ABS(a)        cuCabs(a)                     ///< @return absolute value, |a| = sqrt( real(a)^2 + imag(a)^2 ).
    #define MAGMA_Z_ABS1(a)       (fabs((a).x) + fabs((a).y))   ///< @return 1-norm absolute value, | real(a) | + | imag(a) |.
    #define MAGMA_Z_CONJ(a)       cuConj(a)                     ///< @return conjugate of a.

    #define MAGMA_C_MAKE(r,i)     make_cuFloatComplex(r, i)
    #define MAGMA_C_REAL(a)       (a).x
    #define MAGMA_C_IMAG(a)       (a).y
    #define MAGMA_C_ADD(a, b)     cuCaddf(a, b)
    #define MAGMA_C_SUB(a, b)     cuCsubf(a, b)
    #define MAGMA_C_MUL(a, b)     cuCmulf(a, b)
    #define MAGMA_C_DIV(a, b)     cuCdivf(a, b)
    #define MAGMA_C_ABS(a)        cuCabsf(a)
    #define MAGMA_C_ABS1(a)       (fabsf((a).x) + fabsf((a).y))
    #define MAGMA_C_CONJ(a)       cuConjf(a)

    /// @}
    // end group magma_complex

    #ifdef __cplusplus
    }
    #endif
#elif defined(HAVE_clBLAS)
    #include <clBLAS.h>

    #ifdef __cplusplus
    extern "C" {
    #endif

    typedef cl_command_queue  magma_queue_t;
    typedef cl_event          magma_event_t;
    typedef cl_device_id      magma_device_t;

    typedef short         magmaHalf;    // placeholder until FP16 is supported
    typedef DoubleComplex magmaDoubleComplex;
    typedef FloatComplex  magmaFloatComplex;

    cl_command_queue magma_queue_get_cl_queue( magma_queue_t queue );

    #define MAGMA_Z_MAKE(r,i)     doubleComplex(r,i)
    #define MAGMA_Z_REAL(a)       (a).s[0]
    #define MAGMA_Z_IMAG(a)       (a).s[1]
    #define MAGMA_Z_ADD(a, b)     MAGMA_Z_MAKE((a).s[0] + (b).s[0], (a).s[1] + (b).s[1])
    #define MAGMA_Z_SUB(a, b)     MAGMA_Z_MAKE((a).s[0] - (b).s[0], (a).s[1] - (b).s[1])
    #define MAGMA_Z_MUL(a, b)     ((a) * (b))
    #define MAGMA_Z_DIV(a, b)     ((a) / (b))
    #define MAGMA_Z_ABS(a)        magma_cabs(a)
    #define MAGMA_Z_ABS1(a)       (fabs((a).s[0]) + fabs((a).s[1]))
    #define MAGMA_Z_CONJ(a)       MAGMA_Z_MAKE((a).s[0], -(a).s[1])

    #define MAGMA_C_MAKE(r,i)     floatComplex(r,i)
    #define MAGMA_C_REAL(a)       (a).s[0]
    #define MAGMA_C_IMAG(a)       (a).s[1]
    #define MAGMA_C_ADD(a, b)     MAGMA_C_MAKE((a).s[0] + (b).s[0], (a).s[1] + (b).s[1])
    #define MAGMA_C_SUB(a, b)     MAGMA_C_MAKE((a).s[0] - (b).s[0], (a).s[1] - (b).s[1])
    #define MAGMA_C_MUL(a, b)     ((a) * (b))
    #define MAGMA_C_DIV(a, b)     ((a) / (b))
    #define MAGMA_C_ABS(a)        magma_cabsf(a)
    #define MAGMA_C_ABS1(a)       (fabsf((a).s[0]) + fabsf((a).s[1]))
    #define MAGMA_C_CONJ(a)       MAGMA_C_MAKE((a).s[0], -(a).s[1])

    #ifdef __cplusplus
    }
    #endif
#elif defined(HAVE_MIC)
    #include <complex>

    #ifdef __cplusplus
    extern "C" {
    #endif

    typedef int   magma_queue_t;
    typedef int   magma_event_t;
    typedef int   magma_device_t;

    typedef short                 magmaHalf;    // placeholder until FP16 is supported
    typedef std::complex<float>   magmaFloatComplex;
    typedef std::complex<double>  magmaDoubleComplex;

    #define MAGMA_Z_MAKE(r, i)    std::complex<double>(r,i)
    #define MAGMA_Z_REAL(x)       (x).real()
    #define MAGMA_Z_IMAG(x)       (x).imag()
    #define MAGMA_Z_ADD(a, b)     ((a)+(b))
    #define MAGMA_Z_SUB(a, b)     ((a)-(b))
    #define MAGMA_Z_MUL(a, b)     ((a)*(b))
    #define MAGMA_Z_DIV(a, b)     ((a)/(b))
    #define MAGMA_Z_ABS(a)        abs(a)
    #define MAGMA_Z_ABS1(a)       (fabs((a).real()) + fabs((a).imag()))
    #define MAGMA_Z_CONJ(a)       conj(a)

    #define MAGMA_C_MAKE(r, i)    std::complex<float> (r,i)
    #define MAGMA_C_REAL(x)       (x).real()
    #define MAGMA_C_IMAG(x)       (x).imag()
    #define MAGMA_C_ADD(a, b)     ((a)+(b))
    #define MAGMA_C_SUB(a, b)     ((a)-(b))
    #define MAGMA_C_MUL(a, b)     ((a)*(b))
    #define MAGMA_C_DIV(a, b)     ((a)/(b))
    #define MAGMA_C_ABS(a)        abs(a)
    #define MAGMA_C_ABS1(a)       (fabs((a).real()) + fabs((a).imag()))
    #define MAGMA_C_CONJ(a)       conj(a)

    #ifdef __cplusplus
    }
    #endif
#else
    #error "One of HAVE_CUBLAS, HAVE_clBLAS, or HAVE_MIC must be defined. For example, add -DHAVE_CUBLAS to CFLAGS, or #define HAVE_CUBLAS before #include <magma.h>. In MAGMA, this happens in Makefile."
#endif

#ifdef __cplusplus
extern "C" {
#endif

#define MAGMA_Z_EQUAL(a,b)        (MAGMA_Z_REAL(a)==MAGMA_Z_REAL(b) && MAGMA_Z_IMAG(a)==MAGMA_Z_IMAG(b))
#define MAGMA_Z_NEGATE(a)         MAGMA_Z_MAKE( -MAGMA_Z_REAL(a), -MAGMA_Z_IMAG(a))

#define MAGMA_C_EQUAL(a,b)        (MAGMA_C_REAL(a)==MAGMA_C_REAL(b) && MAGMA_C_IMAG(a)==MAGMA_C_IMAG(b))
#define MAGMA_C_NEGATE(a)         MAGMA_C_MAKE( -MAGMA_C_REAL(a), -MAGMA_C_IMAG(a))

#define MAGMA_D_MAKE(r,i)         (r)
#define MAGMA_D_REAL(x)           (x)
#define MAGMA_D_IMAG(x)           (0.0)
#define MAGMA_D_ADD(a, b)         ((a) + (b))
#define MAGMA_D_SUB(a, b)         ((a) - (b))
#define MAGMA_D_MUL(a, b)         ((a) * (b))
#define MAGMA_D_DIV(a, b)         ((a) / (b))
#define MAGMA_D_ABS(a)            ((a)>0 ? (a) : -(a))
#define MAGMA_D_ABS1(a)           ((a)>0 ? (a) : -(a))
#define MAGMA_D_CONJ(a)           (a)
#define MAGMA_D_EQUAL(a,b)        ((a) == (b))
#define MAGMA_D_NEGATE(a)         (-a)

#define MAGMA_S_MAKE(r,i)         (r)
#define MAGMA_S_REAL(x)           (x)
#define MAGMA_S_IMAG(x)           (0.0)
#define MAGMA_S_ADD(a, b)         ((a) + (b))
#define MAGMA_S_SUB(a, b)         ((a) - (b))
#define MAGMA_S_MUL(a, b)         ((a) * (b))
#define MAGMA_S_DIV(a, b)         ((a) / (b))
#define MAGMA_S_ABS(a)            ((a)>0 ? (a) : -(a))
#define MAGMA_S_ABS1(a)           ((a)>0 ? (a) : -(a))
#define MAGMA_S_CONJ(a)           (a)
#define MAGMA_S_EQUAL(a,b)        ((a) == (b))
#define MAGMA_S_NEGATE(a)         (-a)

#define MAGMA_Z_ZERO              MAGMA_Z_MAKE( 0.0, 0.0)
#define MAGMA_Z_ONE               MAGMA_Z_MAKE( 1.0, 0.0)
#define MAGMA_Z_HALF              MAGMA_Z_MAKE( 0.5, 0.0)
#define MAGMA_Z_NEG_ONE           MAGMA_Z_MAKE(-1.0, 0.0)
#define MAGMA_Z_NEG_HALF          MAGMA_Z_MAKE(-0.5, 0.0)

#define MAGMA_C_ZERO              MAGMA_C_MAKE( 0.0, 0.0)
#define MAGMA_C_ONE               MAGMA_C_MAKE( 1.0, 0.0)
#define MAGMA_C_HALF              MAGMA_C_MAKE( 0.5, 0.0)
#define MAGMA_C_NEG_ONE           MAGMA_C_MAKE(-1.0, 0.0)
#define MAGMA_C_NEG_HALF          MAGMA_C_MAKE(-0.5, 0.0)

#define MAGMA_D_ZERO              ( 0.0)
#define MAGMA_D_ONE               ( 1.0)
#define MAGMA_D_HALF              ( 0.5)
#define MAGMA_D_NEG_ONE           (-1.0)
#define MAGMA_D_NEG_HALF          (-0.5)

#define MAGMA_S_ZERO              ( 0.0)
#define MAGMA_S_ONE               ( 1.0)
#define MAGMA_S_HALF              ( 0.5)
#define MAGMA_S_NEG_ONE           (-1.0)
#define MAGMA_S_NEG_HALF          (-0.5)

#ifndef CBLAS_SADDR
#define CBLAS_SADDR(a)  &(a)
#endif

// for MAGMA_[CZ]_ABS
double magma_cabs ( magmaDoubleComplex x );
float  magma_cabsf( magmaFloatComplex  x );

#if defined(HAVE_clBLAS)
    // OpenCL uses opaque memory references on GPU
    typedef cl_mem magma_ptr;
    typedef cl_mem magmaInt_ptr;
    typedef cl_mem magmaIndex_ptr;
    typedef cl_mem magmaFloat_ptr;
    typedef cl_mem magmaDouble_ptr;
    typedef cl_mem magmaFloatComplex_ptr;
    typedef cl_mem magmaDoubleComplex_ptr;

    typedef cl_mem magma_const_ptr;
    typedef cl_mem magmaInt_const_ptr;
    typedef cl_mem magmaIndex_const_ptr;
    typedef cl_mem magmaFloat_const_ptr;
    typedef cl_mem magmaDouble_const_ptr;
    typedef cl_mem magmaFloatComplex_const_ptr;
    typedef cl_mem magmaDoubleComplex_const_ptr;
#else
    // MIC and CUDA use regular pointers on GPU
    typedef void               *magma_ptr;
    typedef magma_int_t        *magmaInt_ptr;
    typedef magma_index_t      *magmaIndex_ptr;
    typedef magma_uindex_t     *magmaUIndex_ptr;
    typedef float              *magmaFloat_ptr;
    typedef double             *magmaDouble_ptr;
    typedef magmaFloatComplex  *magmaFloatComplex_ptr;
    typedef magmaDoubleComplex *magmaDoubleComplex_ptr;
    typedef magmaHalf          *magmaHalf_ptr;

    typedef void               const *magma_const_ptr;
    typedef magma_int_t        const *magmaInt_const_ptr;
    typedef magma_index_t      const *magmaIndex_const_ptr;
    typedef magma_uindex_t     const *magmaUIndex_const_ptr;
    typedef float              const *magmaFloat_const_ptr;
    typedef double             const *magmaDouble_const_ptr;
    typedef magmaFloatComplex  const *magmaFloatComplex_const_ptr;
    typedef magmaDoubleComplex const *magmaDoubleComplex_const_ptr;
    typedef magmaHalf          const *magmaHalf_const_ptr;
#endif

// =============================================================================
// MAGMA constants

// -----------------------------------------------------------------------------
#define MAGMA_VERSION_MAJOR 2
#define MAGMA_VERSION_MINOR 5
#define MAGMA_VERSION_MICRO 4

// stage is "svn", "beta#", "rc#" (release candidate), or blank ("") for final release
#define MAGMA_VERSION_STAGE ""

#define MagmaMaxGPUs 8
#define MagmaMaxAccelerators 8
#define MagmaMaxSubs 16

// trsv template parameter
#define MagmaBigTileSize 1000000

// -----------------------------------------------------------------------------
// Return codes
// LAPACK argument errors are < 0 but > MAGMA_ERR.
// MAGMA errors are < MAGMA_ERR.
/// @addtogroup magma_error_codes
/// @{

#define MAGMA_SUCCESS               0       ///< operation was successful
#define MAGMA_ERR                  -100     ///< unspecified error
#define MAGMA_ERR_NOT_INITIALIZED  -101     ///< magma_init() was not called
#define MAGMA_ERR_REINITIALIZED    -102     // unused
#define MAGMA_ERR_NOT_SUPPORTED    -103     ///< not supported on this GPU
#define MAGMA_ERR_ILLEGAL_VALUE    -104     // unused
#define MAGMA_ERR_NOT_FOUND        -105     ///< file not found
#define MAGMA_ERR_ALLOCATION       -106     // unused
#define MAGMA_ERR_INTERNAL_LIMIT   -107     // unused
#define MAGMA_ERR_UNALLOCATED      -108     // unused
#define MAGMA_ERR_FILESYSTEM       -109     // unused
#define MAGMA_ERR_UNEXPECTED       -110     // unused
#define MAGMA_ERR_SEQUENCE_FLUSHED -111     // unused
#define MAGMA_ERR_HOST_ALLOC       -112     ///< could not malloc CPU host memory
#define MAGMA_ERR_DEVICE_ALLOC     -113     ///< could not malloc GPU device memory
#define MAGMA_ERR_CUDASTREAM       -114     // unused
#define MAGMA_ERR_INVALID_PTR      -115     ///< can't free invalid pointer
#define MAGMA_ERR_UNKNOWN          -116     ///< unspecified error
#define MAGMA_ERR_NOT_IMPLEMENTED  -117     ///< not implemented yet
#define MAGMA_ERR_NAN              -118     ///< NaN (not-a-number) detected

// some MAGMA-sparse errors
#define MAGMA_SLOW_CONVERGENCE     -201
#define MAGMA_DIVERGENCE           -202
#define MAGMA_NONSPD               -203
#define MAGMA_ERR_BADPRECOND       -204
#define MAGMA_NOTCONVERGED         -205

// When adding error codes, please add to interface_cuda/error.cpp

// map cusparse errors to magma errors
#define MAGMA_ERR_CUSPARSE                            -3000
#define MAGMA_ERR_CUSPARSE_NOT_INITIALIZED            -3001
#define MAGMA_ERR_CUSPARSE_ALLOC_FAILED               -3002
#define MAGMA_ERR_CUSPARSE_INVALID_VALUE              -3003
#define MAGMA_ERR_CUSPARSE_ARCH_MISMATCH              -3004
#define MAGMA_ERR_CUSPARSE_MAPPING_ERROR              -3005
#define MAGMA_ERR_CUSPARSE_EXECUTION_FAILED           -3006
#define MAGMA_ERR_CUSPARSE_INTERNAL_ERROR             -3007
#define MAGMA_ERR_CUSPARSE_MATRIX_TYPE_NOT_SUPPORTED  -3008
#define MAGMA_ERR_CUSPARSE_ZERO_PIVOT                 -3009

/// @}
// end group magma_error_codes

// -----------------------------------------------------------------------------
// parameter constants
// numbering is consistent with CBLAS and PLASMA; see plasma/include/plasma.h
// also with lapack_cwrapper/include/lapack_enum.h
// see http://www.netlib.org/lapack/lapwrapc/
typedef enum {
    MagmaFalse         = 0,
    MagmaTrue          = 1
} magma_bool_t;

typedef enum {
    MagmaRowMajor      = 101,
    MagmaColMajor      = 102
} magma_order_t;

// Magma_ConjTrans is an alias for those rare occasions (zlarfb, zun*, zher*k)
// where we want Magma_ConjTrans to convert to MagmaTrans in precision generation.
typedef enum {
    MagmaNoTrans       = 111,
    MagmaTrans         = 112,
    MagmaConjTrans     = 113,
    Magma_ConjTrans    = MagmaConjTrans
} magma_trans_t;

typedef enum {
    MagmaUpper         = 121,
    MagmaLower         = 122,
    MagmaFull          = 123,  /* lascl, laset */
    MagmaHessenberg    = 124   /* lascl */
} magma_uplo_t;

typedef magma_uplo_t magma_type_t;  /* lascl */

typedef enum {
    MagmaNonUnit       = 131,
    MagmaUnit          = 132
} magma_diag_t;

typedef enum {
    MagmaLeft          = 141,
    MagmaRight         = 142,
    MagmaBothSides     = 143   /* trevc */
} magma_side_t;

typedef enum {
    MagmaOneNorm       = 171,  /* lange, lanhe */
    MagmaRealOneNorm   = 172,
    MagmaTwoNorm       = 173,
    MagmaFrobeniusNorm = 174,
    MagmaInfNorm       = 175,
    MagmaRealInfNorm   = 176,
    MagmaMaxNorm       = 177,
    MagmaRealMaxNorm   = 178
} magma_norm_t;

typedef enum {
    MagmaDistUniform   = 201,  /* latms */
    MagmaDistSymmetric = 202,
    MagmaDistNormal    = 203
} magma_dist_t;

typedef enum {
    MagmaHermGeev      = 241,  /* latms */
    MagmaHermPoev      = 242,
    MagmaNonsymPosv    = 243,
    MagmaSymPosv       = 244
} magma_sym_t;

typedef enum {
    MagmaNoPacking     = 291,  /* latms */
    MagmaPackSubdiag   = 292,
    MagmaPackSupdiag   = 293,
    MagmaPackColumn    = 294,
    MagmaPackRow       = 295,
    MagmaPackLowerBand = 296,
    MagmaPackUpeprBand = 297,
    MagmaPackAll       = 298
} magma_pack_t;

typedef enum {
    MagmaNoVec         = 301,  /* geev, syev, gesvd */
    MagmaVec           = 302,  /* geev, syev */
    MagmaIVec          = 303,  /* stedc */
    MagmaAllVec        = 304,  /* gesvd, trevc */
    MagmaSomeVec       = 305,  /* gesvd, trevc */
    MagmaOverwriteVec  = 306,  /* gesvd */
    MagmaBacktransVec  = 307   /* trevc */
} magma_vec_t;

typedef enum {
    MagmaRangeAll      = 311,  /* syevx, etc. */
    MagmaRangeV        = 312,
    MagmaRangeI        = 313
} magma_range_t;

typedef enum {
    MagmaQ             = 322,  /* unmbr, ungbr */
    MagmaP             = 323
} magma_vect_t;

typedef enum {
    MagmaForward       = 391,  /* larfb */
    MagmaBackward      = 392
} magma_direct_t;

typedef enum {
    MagmaColumnwise    = 401,  /* larfb */
    MagmaRowwise       = 402
} magma_storev_t;

typedef enum {
    MagmaHybrid        = 701,
    MagmaNative        = 702
} magma_mode_t;
// -----------------------------------------------------------------------------
// sparse
typedef enum {
    Magma_CSR          = 611,
    Magma_ELLPACKT     = 612,
    Magma_ELL          = 613,
    Magma_DENSE        = 614,
    Magma_BCSR         = 615,
    Magma_CSC          = 616,
    Magma_HYB          = 617,
    Magma_COO          = 618,
    Magma_ELLRT        = 619,
    Magma_SPMVFUNCTION = 620,
    Magma_SELLP        = 621,
    Magma_ELLD         = 622,
    Magma_CSRLIST      = 623,
    Magma_CSRD         = 624,
    Magma_CSRL         = 627,
    Magma_CSRU         = 628,
    Magma_CSRCOO       = 629,
    Magma_CUCSR        = 630,
    Magma_COOLIST      = 631,
    Magma_CSR5         = 632
} magma_storage_t;

typedef enum {
    Magma_CG           = 431,
    Magma_CGMERGE      = 432,
    Magma_GMRES        = 433,
    Magma_BICGSTAB     = 434,
  Magma_BICGSTABMERGE  = 435,
  Magma_BICGSTABMERGE2 = 436,
    Magma_JACOBI       = 437,
    Magma_GS           = 438,
    Magma_ITERREF      = 439,
    Magma_BCSRLU       = 440,
    Magma_PCG          = 441,
    Magma_PGMRES       = 442,
    Magma_PBICGSTAB    = 443,
    Magma_PASTIX       = 444,
    Magma_ILU          = 445,
    Magma_ICC          = 446,
    Magma_PARILU       = 447,
    Magma_PARIC        = 448,
    Magma_BAITER       = 449,
    Magma_LOBPCG       = 450,
    Magma_NONE         = 451,
    Magma_FUNCTION     = 452,
    Magma_IDR          = 453,
    Magma_PIDR         = 454,
    Magma_CGS          = 455,
    Magma_PCGS         = 456,
    Magma_CGSMERGE     = 457,
    Magma_PCGSMERGE    = 458,
    Magma_TFQMR        = 459,
    Magma_PTFQMR       = 460,
    Magma_TFQMRMERGE   = 461,
    Magma_PTFQMRMERGE  = 462,
    Magma_QMR          = 463,
    Magma_PQMR         = 464,
    Magma_QMRMERGE     = 465,
    Magma_PQMRMERGE    = 466,
    Magma_BOMBARD      = 490,
    Magma_BOMBARDMERGE = 491,
    Magma_PCGMERGE     = 492,
    Magma_BAITERO      = 493,
    Magma_IDRMERGE     = 494,
  Magma_PBICGSTABMERGE = 495,
    Magma_PARICT       = 496,
    Magma_CUSTOMIC     = 497,
    Magma_CUSTOMILU    = 498,
    Magma_PIDRMERGE    = 499,
    Magma_BICG         = 500,
    Magma_BICGMERGE    = 501,
    Magma_PBICG        = 502,
    Magma_PBICGMERGE   = 503,
    Magma_LSQR         = 504,
    Magma_PARILUT      = 505,
    Magma_ISAI         = 506,
    Magma_CUSOLVE      = 507,
    Magma_VBJACOBI     = 508,
    Magma_PARDISO      = 509,
    Magma_SYNCFREESOLVE= 510,
    Magma_ILUT         = 511
} magma_solver_type;

typedef enum {
    Magma_CGSO         = 561,
    Magma_FUSED_CGSO   = 562,
    Magma_MGSO         = 563
} magma_ortho_t;

typedef enum {
    Magma_CPU          = 571,
    Magma_DEV          = 572
} magma_location_t;

typedef enum {
    Magma_GENERAL      = 581,
    Magma_SYMMETRIC    = 582
} magma_symmetry_t;

typedef enum {
    Magma_ORDERED      = 591,
    Magma_DIAGFIRST    = 592,
    Magma_UNITY        = 593,
    Magma_VALUE        = 594
} magma_diagorder_t;

typedef enum {
    Magma_DCOMPLEX     = 501,
    Magma_FCOMPLEX     = 502,
    Magma_DOUBLE       = 503,
    Magma_FLOAT        = 504
} magma_precision;

typedef enum {
    Magma_NOSCALE      = 511,
    Magma_UNITROW      = 512,
    Magma_UNITDIAG     = 513,
    Magma_UNITCOL      = 514,
    Magma_UNITROWCOL   = 515, // to be deprecated
    Magma_UNITDIAGCOL  = 516, // to be deprecated
} magma_scale_t;

typedef enum {
    Magma_SOLVE        = 801,
    Magma_SETUPSOLVE   = 802,
    Magma_APPLYSOLVE   = 803,
    Magma_DESTROYSOLVE = 804,
    Magma_INFOSOLVE    = 805,
    Magma_GENERATEPREC = 806,
    Magma_PRECONDLEFT  = 807,
    Magma_PRECONDRIGHT = 808,
    Magma_TRANSPOSE    = 809,
    Magma_SPMV         = 810
} magma_operation_t;

typedef enum {
    Magma_PREC_SS           = 900,
    Magma_PREC_SST          = 901,
    Magma_PREC_HS           = 902,
    Magma_PREC_HST          = 903,
    Magma_PREC_SH           = 904,
    Magma_PREC_SHT          = 905,

    Magma_PREC_XHS_H        = 910,
    Magma_PREC_XHS_HTC      = 911,
    Magma_PREC_XHS_161616   = 912,
    Magma_PREC_XHS_161616TC = 913,
    Magma_PREC_XHS_161632TC = 914,
    Magma_PREC_XSH_S        = 915,
    Magma_PREC_XSH_STC      = 916,
    Magma_PREC_XSH_163232TC = 917,
    Magma_PREC_XSH_323232TC = 918,

    Magma_REFINE_IRSTRS   = 920,
    Magma_REFINE_IRDTRS   = 921,
    Magma_REFINE_IRGMSTRS = 922,
    Magma_REFINE_IRGMDTRS = 923,
    Magma_REFINE_GMSTRS   = 924,
    Magma_REFINE_GMDTRS   = 925,
    Magma_REFINE_GMGMSTRS = 926,
    Magma_REFINE_GMGMDTRS = 927,

    Magma_PREC_HD         = 930,
} magma_refinement_t;

typedef enum {
    Magma_MP_BASE_SS              = 950,
    Magma_MP_BASE_DD              = 951,
    Magma_MP_BASE_XHS             = 952,
    Magma_MP_BASE_XSH             = 953,
    Magma_MP_BASE_XHD             = 954,
    Magma_MP_BASE_XDH             = 955,

    Magma_MP_ENABLE_DFLT_MATH     = 960,
    Magma_MP_ENABLE_TC_MATH       = 961,
    Magma_MP_SGEMM                = 962,
    Magma_MP_HGEMM                = 963,
    Magma_MP_GEMEX_I32_O32_C32    = 964,
    Magma_MP_GEMEX_I16_O32_C32    = 965,
    Magma_MP_GEMEX_I16_O16_C32    = 966,
    Magma_MP_GEMEX_I16_O16_C16    = 967,

    Magma_MP_TC_SGEMM             = 968,
    Magma_MP_TC_HGEMM             = 969,
    Magma_MP_TC_GEMEX_I32_O32_C32 = 970,
    Magma_MP_TC_GEMEX_I16_O32_C32 = 971,
    Magma_MP_TC_GEMEX_I16_O16_C32 = 972,
    Magma_MP_TC_GEMEX_I16_O16_C16 = 973,

} magma_mp_type_t;

// When adding constants, remember to do these steps as appropriate:
// 1)  add magma_xxxx_const()  converter below and in control/constants.cpp
// 2a) add to magma2lapack_constants[] in control/constants.cpp
// 2b) update min & max here, which are used to check bounds for magma2lapack_constants[]
// 2c) add lapack_xxxx_const() converter below and in control/constants.cpp
#define Magma2lapack_Min  MagmaFalse     // 0
#define Magma2lapack_Max  MagmaRowwise   // 402

// -----------------------------------------------------------------------------
// string constants for calling Fortran BLAS and LAPACK
// todo: use translators instead? lapack_const_str( MagmaUpper )
#define MagmaRowMajorStr      "Row"
#define MagmaColMajorStr      "Col"

#define MagmaNoTransStr       "NoTrans"
#define MagmaTransStr         "Trans"
#define MagmaConjTransStr     "ConjTrans"
#define Magma_ConjTransStr    "ConjTrans"

#define MagmaUpperStr         "Upper"
#define MagmaLowerStr         "Lower"
#define MagmaFullStr          "Full"

#define MagmaNonUnitStr       "NonUnit"
#define MagmaUnitStr          "Unit"

#define MagmaLeftStr          "Left"
#define MagmaRightStr         "Right"
#define MagmaBothSidesStr     "Both"

#define MagmaOneNormStr       "1"
#define MagmaTwoNormStr       "2"
#define MagmaFrobeniusNormStr "Fro"
#define MagmaInfNormStr       "Inf"
#define MagmaMaxNormStr       "Max"

#define MagmaForwardStr       "Forward"
#define MagmaBackwardStr      "Backward"

#define MagmaColumnwiseStr    "Columnwise"
#define MagmaRowwiseStr       "Rowwise"

#define MagmaNoVecStr         "NoVec"
#define MagmaVecStr           "Vec"
#define MagmaIVecStr          "IVec"
#define MagmaAllVecStr        "All"
#define MagmaSomeVecStr       "Some"
#define MagmaOverwriteVecStr  "Overwrite"

// -----------------------------------------------------------------------------
// Convert LAPACK character constants to MAGMA constants.
// This is a one-to-many mapping, requiring multiple translators
// (e.g., "N" can be NoTrans or NonUnit or NoVec).
magma_bool_t   magma_bool_const  ( char lapack_char );
magma_order_t  magma_order_const ( char lapack_char );
magma_trans_t  magma_trans_const ( char lapack_char );
magma_uplo_t   magma_uplo_const  ( char lapack_char );
magma_diag_t   magma_diag_const  ( char lapack_char );
magma_side_t   magma_side_const  ( char lapack_char );
magma_norm_t   magma_norm_const  ( char lapack_char );
magma_dist_t   magma_dist_const  ( char lapack_char );
magma_sym_t    magma_sym_const   ( char lapack_char );
magma_pack_t   magma_pack_const  ( char lapack_char );
magma_vec_t    magma_vec_const   ( char lapack_char );
magma_range_t  magma_range_const ( char lapack_char );
magma_vect_t   magma_vect_const  ( char lapack_char );
magma_direct_t magma_direct_const( char lapack_char );
magma_storev_t magma_storev_const( char lapack_char );

// -----------------------------------------------------------------------------
// Convert MAGMA constants to LAPACK(E) constants.
// The generic lapack_const_str works for all cases, but the specific routines
// (e.g., lapack_trans_const) do better error checking.

// magma  defines lapack_const_str, which returns char* to call lapack (Fortran interface).
// plasma defines lapack_const, which is roughly the same as MAGMA's lapacke_const
// (returns a char instead of char*) to call lapacke (C interface).

const char* lapack_const_str   ( int            magma_const );
const char* lapack_bool_const  ( magma_bool_t   magma_const );
const char* lapack_order_const ( magma_order_t  magma_const );
const char* lapack_trans_const ( magma_trans_t  magma_const );
const char* lapack_uplo_const  ( magma_uplo_t   magma_const );
const char* lapack_diag_const  ( magma_diag_t   magma_const );
const char* lapack_side_const  ( magma_side_t   magma_const );
const char* lapack_norm_const  ( magma_norm_t   magma_const );
const char* lapack_dist_const  ( magma_dist_t   magma_const );
const char* lapack_sym_const   ( magma_sym_t    magma_const );
const char* lapack_pack_const  ( magma_pack_t   magma_const );
const char* lapack_vec_const   ( magma_vec_t    magma_const );
const char* lapack_range_const ( magma_range_t  magma_const );
const char* lapack_vect_const  ( magma_vect_t   magma_const );
const char* lapack_direct_const( magma_direct_t magma_const );
const char* lapack_storev_const( magma_storev_t magma_const );

static inline char lapacke_const       ( int magma_const            ) { return *lapack_const_str   ( magma_const ); }
static inline char lapacke_bool_const  ( magma_bool_t   magma_const ) { return *lapack_bool_const  ( magma_const ); }
static inline char lapacke_order_const ( magma_order_t  magma_const ) { return *lapack_order_const ( magma_const ); }
static inline char lapacke_trans_const ( magma_trans_t  magma_const ) { return *lapack_trans_const ( magma_const ); }
static inline char lapacke_uplo_const  ( magma_uplo_t   magma_const ) { return *lapack_uplo_const  ( magma_const ); }
static inline char lapacke_diag_const  ( magma_diag_t   magma_const ) { return *lapack_diag_const  ( magma_const ); }
static inline char lapacke_side_const  ( magma_side_t   magma_const ) { return *lapack_side_const  ( magma_const ); }
static inline char lapacke_norm_const  ( magma_norm_t   magma_const ) { return *lapack_norm_const  ( magma_const ); }
static inline char lapacke_dist_const  ( magma_dist_t   magma_const ) { return *lapack_dist_const  ( magma_const ); }
static inline char lapacke_sym_const   ( magma_sym_t    magma_const ) { return *lapack_sym_const   ( magma_const ); }
static inline char lapacke_pack_const  ( magma_pack_t   magma_const ) { return *lapack_pack_const  ( magma_const ); }
static inline char lapacke_vec_const   ( magma_vec_t    magma_const ) { return *lapack_vec_const   ( magma_const ); }
static inline char lapacke_range_const ( magma_range_t  magma_const ) { return *lapack_range_const ( magma_const ); }
static inline char lapacke_vect_const  ( magma_vect_t   magma_const ) { return *lapack_vect_const  ( magma_const ); }
static inline char lapacke_direct_const( magma_direct_t magma_const ) { return *lapack_direct_const( magma_const ); }
static inline char lapacke_storev_const( magma_storev_t magma_const ) { return *lapack_storev_const( magma_const ); }

// -----------------------------------------------------------------------------
// Convert MAGMA constants to clBLAS constants.
#if defined(HAVE_clBLAS)
clblasOrder          clblas_order_const( magma_order_t order );
clblasTranspose      clblas_trans_const( magma_trans_t trans );
clblasUplo           clblas_uplo_const ( magma_uplo_t  uplo  );
clblasDiag           clblas_diag_const ( magma_diag_t  diag  );
clblasSide           clblas_side_const ( magma_side_t  side  );
#endif

// -----------------------------------------------------------------------------
// Convert MAGMA constants to CUBLAS constants.
#if defined(CUBLAS_V2_H_)
cublasOperation_t    cublas_trans_const ( magma_trans_t trans );
cublasFillMode_t     cublas_uplo_const  ( magma_uplo_t  uplo  );
cublasDiagType_t     cublas_diag_const  ( magma_diag_t  diag  );
cublasSideMode_t     cublas_side_const  ( magma_side_t  side  );
#endif

// -----------------------------------------------------------------------------
// Convert MAGMA constants to CBLAS constants.
#if defined(HAVE_CBLAS)
#include <cblas.h>
enum CBLAS_ORDER     cblas_order_const  ( magma_order_t order );
enum CBLAS_TRANSPOSE cblas_trans_const  ( magma_trans_t trans );
enum CBLAS_UPLO      cblas_uplo_const   ( magma_uplo_t  uplo  );
enum CBLAS_DIAG      cblas_diag_const   ( magma_diag_t  diag  );
enum CBLAS_SIDE      cblas_side_const   ( magma_side_t  side  );
#endif

#ifdef __cplusplus
}
#endif

#endif // MAGMA_TYPES_H


/***************************************************************************
                          misc.h  -  description
                             -------------------
    copyright            : (C) 2005 by MOUSSA
    email                : mmousa@liris.cnrs.fr
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef MISC_H
#define MISC_H

#include "config.h"

#include <gsl/gsl_math.h>
#include <gsl/gsl_monte.h>
#include <gsl/gsl_monte_plain.h>
#include <gsl/gsl_sf.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>

//////////////////////////////////////////////////////////////////////////////
template<class T> void Add_vectors(T *vec1, T *vec2, T *result, int size);
template<class T> void Add_vectors(T *vec1, T *vec2, T *result, int size){
  for(int i=0; i<size; i++){
    result[i] = vec1[i]+vec2[i];
  }
}
//////////////////////////////////////////////////////////////////////////////
template<class T> void Mult_vector_const(T *vec, T c, T *result, int size);
template<class T> void Mult_vector_const(T *vec, T c, T *result, int size){
  for(int i=0; i<size; i++){
    result[i] = vec[i]*c;
  }
}
//////////////////////////////////////////////////////////////////////////////
template<class T>void assign_value(T *vec, T c, int size);
template<class T>void assign_value(T *vec, T c, int size){
  for(int i = 0; i<size; i++) vec[i] = c;
}
//////////////////////////////////////////////////////////////////////////////
template<class T>void copy_vectors(T *src, T *dest, int size);
template<class T>void copy_vectors(T *src, T *dest, int size){
  for(int i = 0; i<size; i++){
    dest[i] = src[i];
  }
}
//////////////////////////////////////////////////////////////////////////////
template<class T> int is_overlaping(T *vec1, T *vec2, int size);
template<class T> int is_overlaping(T *vec1, T *vec2, int size){
  for(int i = 0; i<size; i++){
    if((fabs(vec1[i])>1e-6)&&(fabs(vec2[i])>1e-6)) return true;
  }
  return false;
}
//////////////////////////////////////////////////////////////////////////////
template<class T> void print_vector(T *vec, int size);
template<class T> void print_vector(T *vec, int size){
  for(int i = 0; i<size; i++){
    if(vec[i]>0.0) std::cout<<vec[i]<<std::endl;
  }
}
//////////////////////////////////////////////////////////////////////////////
template<class T> bool AllOnes(T *vec, int size);
template<class T> bool AllOnes(T *vec, int size){
  for(int i = 0; i < size; i++)
    if(fabs(vec[i]) < 1e-6) return false;
  return true;
}
//////////////////////////////////////////////////////////////////////////////
int gettoken(char[30], FILE *);
bool FileName(char[30], char *);
void theta_phi_bounds(Triangle_3 tr, double *mintheta, double *maxtheta, double *minphi, double *maxphi);
void SphericalHarmonic(int, int, double, double, gsl_complex *);
#endif


/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
**                                                                                **
** 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.                             **
**                                                                                **
**~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*/

#ifndef __DRIVERS_common_utils_h__
#define __DRIVERS_common_utils_h__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>

#include <petsc.h>
#include <petscmat.h>
#include <petscvec.h>
#include <petscpc.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 "../../../../StgDomain/Utils/src/PETScCompatibility.h"

#include "pc_GtKG.h"
#include "pc_ScaledGtKG.h"
#include "stokes_block_scaling.h"
#include "stokes_mvblock_scaling.h"
#include "ksp_scale.h"

/* BSSCR_create_execute_script.c */
int BSSCR_create_execute_script( void );

/* create_petsc_header.c */
PetscErrorCode BSSCR_GeneratePetscHeader_for_file( FILE *fd, MPI_Comm comm );
PetscErrorCode BSSCR_GeneratePetscHeader_for_viewer( PetscViewer viewer );

/* MatIdentityOperator.c */
//PetscErrorCode MatCreateIdentityOperator( Mat A, Mat *_I );

/* read_matrices.c */
//PetscErrorCode StokesReadOperators( MPI_Comm comm, Mat *K, Mat *G, Mat *D, Mat *C, Vec *f, Vec *h );

/* stokes_residual.c */
double BSSCR_StokesMomentumResidual( Mat K, Mat G, Vec F, Vec u, Vec p );
double BSSCR_StokesContinuityResidual( Mat G, Mat C, Vec H, Vec u, Vec p );

/* preconditioner.c */
PetscErrorCode BSSCR_StokesReadPCSchurMat( MPI_Comm comm, Mat *S );
PetscErrorCode BSSCR_StokesCreatePCSchur( Mat K, Mat G, PC pc_S );

/* nullspace */
/*
PetscErrorCode BSSCR_MatGtKinvG_ContainsConstantNullSpace(
		Mat K, Mat G, Mat M,Vec t1, Vec ustar,  Vec r, Vec l,
		KSP ksp, PetscTruth *has_cnst_nullsp );
PetscErrorCode BSSCR_VecRemoveConstNullspace(Vec v, Vec nsp_vec);
*/
/* PetscErrorCode BSSCR_MatContainsConstantNullSpace( Mat A, PetscTruth *has_cnst_nullsp ); */
//PetscErrorCode BSSCR_MatContainsConstNullSpace(Mat A, Vec nsp_vec, PetscTruth *has);

/* timed residuals */
//PetscErrorCode KSPLogDestroyMonitor(KSP ksp);
PetscErrorCode BSSCR_KSPLogSetMonitor(KSP ksp,PetscInt len,PetscInt *monitor_index);
PetscErrorCode BSSCR_KSPLogGetTimeHistory(KSP ksp,PetscInt monitor_index,PetscInt *na,PetscLogDouble **log);
PetscErrorCode BSSCR_KSPLogGetResidualHistory(KSP ksp,PetscInt monitor_index,PetscInt *na,PetscReal **rlog);
PetscErrorCode BSSCR_KSPLogGetResidualTimeHistory(KSP ksp,PetscInt monitor_index,PetscInt *na,PetscReal **rlog,PetscLogDouble **tlog);
PetscErrorCode BSSCR_KSPLogSolve(PetscViewer v,PetscInt monitor_index,KSP ksp);
PetscErrorCode BSSCR_BSSCR_KSPLogSolveSummary(PetscViewer v,PetscInt monitor_index,KSP ksp);

/* operator summary */
PetscErrorCode BSSCR_MatInfoLog(PetscViewer v,Mat A,const char name[]);
PetscErrorCode BSSCR_VecInfoLog(PetscViewer v,Vec x,const char name[]);
PetscErrorCode BSSCR_StokesCreateOperatorSummary( Mat K, Mat G, Mat C, Vec f, Vec h, const char filename[] );

/* standard output */
PetscErrorCode BSSCR_solver_output( KSP ksp, PetscInt monitor_index, const char res_file[], const char solver_conf[] );
PetscErrorCode BSSCR_KSPLogConvergenceRate(PetscViewer v,PetscInt monitor_index, KSP ksp);

/* list available operations */
PetscErrorCode BSSCR_MatListOperations( Mat A, PetscViewer v );
PetscErrorCode BSSCR_VecListOperations( Vec x, PetscViewer v );
PetscErrorCode BSSCR_KSPListOperations( KSP ksp, PetscViewer v );
PetscErrorCode BSSCR_PCListOperations( PC pc, PetscViewer v );
PetscErrorCode BSSCR_SNESListOperations( SNES snes, PetscViewer v );

/* Register any petsc objects defined in drivers */
PetscErrorCode BSSCR_PetscExtStokesSolversInitialize( void );
PetscErrorCode BSSCR_PetscExtStokesSolversFinalize( void );

/* stokes block */
/*
PetscErrorCode BSSCR_CreateStokesBlockOperators( MPI_Comm comm,
		Mat K, Mat G, Mat D, Mat C,
		Vec u, Vec p, Vec f, Vec h,
		Mat *A, Vec *x, Vec *b );
*/
/* stokes output */
PetscErrorCode BSSCR_stokes_output( PetscViewer v, Mat stokes_A, Vec stokes_b, Vec stokes_x, KSP ksp, PetscInt monitor_index );

//PetscErrorCode BSSCR_NSPRemoveAll(Vec v, void *_data);
//PetscErrorCode BSSCR_CheckNullspace(KSP ksp_S, Mat S, Vec h_hat, MatStokesBlockScaling BA, Vec * _nsp_vec);
#endif


#ifndef MOBULA_INC_CONTEXT_CPU_CTX_H_
#define MOBULA_INC_CONTEXT_CPU_CTX_H_

#define MOBULA_KERNEL void
#define MOBULA_DEVICE

#include <algorithm>
#include <cmath>
#include <cstring>
#include <mutex>
#include <thread>
#include "../ctypes.h"
#include "./common.h"

namespace mobula {

#if USING_CBLAS
#include <cblas.h>
inline void blas_gemm(const int axis, const bool tA, const bool tB, const int M,
                      const int N, const int K, const float alpha,
                      const float *A, const int lda, const float *B,
                      const int ldb, const float beta, float *C,
                      const int ldc) {
  cblas_sgemm(axis == 0 ? CblasRowMajor : CblasColMajor,
              tA ? CblasTrans : CblasNoTrans, tB ? CblasTrans : CblasNoTrans, M,
              N, K, alpha, A, lda, B, ldb, beta, C, ldc);
}
#endif

using std::abs;
using std::max;
using std::min;

#if HOST_NUM_THREADS > 1 || USING_OPENMP
constexpr int NUM_MOBULA_ATOMIC_ADD_MUTEXES = HOST_NUM_THREADS * 8;
static std::mutex MOBULA_ATOMIC_ADD_MUTEXES[NUM_MOBULA_ATOMIC_ADD_MUTEXES];
inline MOBULA_DEVICE float atomic_add(const float val, float *address) {
  uintptr_t id = (reinterpret_cast<uintptr_t>(address) / sizeof(float)) %
                 NUM_MOBULA_ATOMIC_ADD_MUTEXES;
  MOBULA_ATOMIC_ADD_MUTEXES[id].lock();
  *address += val;
  MOBULA_ATOMIC_ADD_MUTEXES[id].unlock();
  return *address;
}
#else
// no lock for single thread mode
inline MOBULA_DEVICE float atomic_add(const float val, float *address) {
  *address += val;
  return *address;
}
#endif

template <typename T>
T *new_array(size_t size) {
  return new T[size];
}

template <typename T>
void del_array(T *p) {
  delete[] p;
}

template <typename T>
T *MemcpyHostToDev(T *dst, const T *src, size_t size) {
  if (dst == src) return dst;
  return static_cast<T *>(memcpy(dst, src, size));
}

template <typename T>
T *MemcpyDevToHost(T *dst, const T *src, size_t size) {
  if (dst == src) return dst;
  return static_cast<T *>(memcpy(dst, src, size));
}

template <typename T>
T *MemcpyDevToDev(T *dst, const T *src, size_t size) {
  if (dst == src) return dst;
  return static_cast<T *>(memcpy(dst, src, size));
}

}  // namespace mobula

#define KERNEL_RUN_BEGIN(device_id) \
  {                                 \
    UNUSED(device_id)
#define KERNEL_RUN_END() }
#define KERNEL_RUN_STREAM(a, strm) KERNEL_RUN(a)

#if USING_OPENMP
#include "./openmp_ctx.h"
#else
#include "./naive_ctx.h"
#endif

#endif  // MOBULA_INC_CONTEXT_CPU_CTX_H_


#ifdef DFFTW
#include <dfftw.h>
#endif

#ifdef SFFTW
#include <sfftw.h>
#endif

#ifdef FFTW
#include <fftw.h>
#endif

int
main()
{
#ifdef DOUBLE
  /* Cause a compile-time error if fftw_real is not double */
  int _array_ [1 - 2 * !((sizeof(fftw_real)) == sizeof(double))];
#else
  /* Cause a compile-time error if fftw_real is not float */
  int _array_ [1 - 2 * !((sizeof(fftw_real)) == sizeof(float))];
#endif
  return 0;
}


//
// Created by agibsonccc on 2/6/16.
//

#ifndef NATIVEOPERATIONS_CBLAS_ENUM_CONVERSION_H_H
#define NATIVEOPERATIONS_CBLAS_ENUM_CONVERSION_H_H
#include <cblas.h>

/*
enum CBLAS_ORDER {CblasRowMajor=101, CblasColMajor=102 };
enum CBLAS_TRANSPOSE {CblasNoTrans=111, CblasTrans=112, CblasConjTrans=113,
    AtlasConj=114};
enum CBLAS_UPLO  {CblasUpper=121, CblasLower=122};
enum CBLAS_DIAG  {CblasNonUnit=131, CblasUnit=132};
enum CBLAS_SIDE  {CblasLeft=141, CblasRight=142};
*/
#include <dll.h>

#ifdef __cplusplus
extern "C" {
#endif
/**
 * Converts a character
 * to its proper enum
 * for row (c) or column (f) ordering
 * default is row major
 */
CBLAS_ORDER convertOrder(int from);
/**
 * Converts a character to its proper enum
 * t -> transpose
 * n -> no transpose
 * c -> conj
 */
CBLAS_TRANSPOSE convertTranspose(int from);
/**
 * Upper or lower
 * U/u -> upper
 * L/l -> lower
 *
 * Default is upper
 */
CBLAS_UPLO convertUplo(int from);

/**
 * For diagonals:
 * u/U -> unit
 * n/N -> non unit
 *
 * Default: unit
 */
CBLAS_DIAG convertDiag(int from);
/**
 * Side of a matrix, left or right
 * l /L -> left
 * r/R -> right
 * default: left
 */
CBLAS_SIDE convertSide(int from);

#ifdef __cplusplus
}
#endif

#endif //NATIVEOPERATIONS_CBLAS_ENUM_CONVERSION_H_H


/* -*- mode: C; c-basic-offset: 4 -*- */
/* ex: set shiftwidth=4 tabstop=4 expandtab: */
/*
 * Copyright (c) 2018-2019, Colorado School of Mines
 * All rights reserved.
 *
 * Author(s): Neil T. Dantam <ndantam@miens.edu>
 * Georgia Tech Humanoid Robotics Lab
 * Under Direction of Prof. Mike Stilman <mstilman@cc.gatech.edu>
 *
 *
 * This file is provided under the following "BSD-style" License:
 *
 *
 *   Redistribution and use in source and binary forms, with or
 *   without modification, are permitted provided that the following
 *   conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 *   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 *   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *   POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef AMINO_MAT_H
#define AMINO_MAT_H

#include <cblas.h>

/**
 * @file mat.h
 *
 * Block matrix descriptors and linear algebra operations.
 *
 */

typedef size_t aa_la_size;

/**
 * Descriptor for a vector.
 */
struct aa_dvec {
    aa_la_size len;   ///< Number of elements in vector
    double *data; ///< Pointer to data
    aa_la_size inc;   ///< Increment between successive vector elements
};

/**
 * Descriptor for a block matrix.
 */
struct aa_dmat {
    aa_la_size rows;    ///< number of rows in matrix
    aa_la_size cols;    ///< number of columns
    double *data;   ///< Pointer to matrix data
    aa_la_size ld;      ///< Leading dimension of matrix
};

#define AA_DVEC_REF(v,i) ((v)->data[(i) * (v)->inc])

/**
 * Reference a matrix entry.
 *
 * @param M matrix
 * @param i row
 * @param j column
 */
#define AA_DMAT_REF(M,i,j) (((M)->data)[(M)->ld*(j) + (i)])

typedef void
(aa_la_err_fun)( const char *message );

AA_API void
aa_la_err( const char *message );

AA_API void
aa_la_set_err( aa_la_err_fun *fun );

/**
 * BLAS arguments for a vector
 */
#define AA_VEC_ARGS(X) (X->data), ((int)(X->inc))

/**
 * BLAS arguments for a matrix
 */
#define AA_MAT_ARGS(X) (X->data), ((int)(X->ld))

/* Construction */

/**
 * Fill in a vector descriptor.
 *
 * @param len  Number of elements in vector
 * @param data Pointer to vector data
 * @param inc  Increment between sucessive elements
 */
static inline struct aa_dvec
AA_DVEC_INIT( size_t len, double *data, size_t inc )
{
    struct aa_dvec vec;
    vec.len = len;
    vec.data = data;
    vec.inc = inc;
    return vec;
}

/**
 * Fill in a vector descriptor.
 *
 * @param vec  Pointer to descriptor
 * @param len  Number of elements in vector
 * @param data Pointer to vector data
 * @param inc  Increment between sucessive elements
 */
AA_API void
aa_dvec_view( struct aa_dvec *vec, size_t len, double *data, size_t inc );

/**
 * Fill in a matrix descriptor.
 *
 * @param mat   Pointer to descriptor
 * @param rows  Number of rows in matrix
 * @param cols  Number of colums in matrix
 * @param data  Pointer to vector data
 * @param ld    Leading dimension of matrix
 */
AA_API void
aa_dmat_view( struct aa_dmat *mat, size_t rows, size_t cols, double *data, size_t ld );

/**
 * View a block of a matrix.
 */
AA_API void
aa_dmat_view_block( struct aa_dmat *dst,
                    const struct aa_dmat *src,
                    size_t row_start, size_t col_start,
                    size_t rows, size_t cols );

/**
 * View a slice of a vector.
 */
AA_API void
aa_dvec_slice( const struct aa_dvec *src,
               size_t start,
               size_t stop,
               size_t step,
               struct aa_dvec *dst );

/**
 * View a block of a matrix.
 */
AA_API void
aa_dmat_block( const struct aa_dmat *src,
               size_t row_start, size_t col_start,
               size_t row_end, size_t col_end,
               struct aa_dmat *dst );

/**
 * View a row of a matrix as a vector.
 */
AA_API void
aa_dmat_row_vec( const struct aa_dmat *src, size_t row, struct aa_dvec *dst );

/**
 * View a column of a matrix as a vector.
 */
AA_API void
aa_dmat_col_vec( const struct aa_dmat *src, size_t col, struct aa_dvec *dst );

/**
 * View the diagonal of a matrix as a vector.
 */
AA_API void
aa_dmat_diag_vec( const struct aa_dmat *src, struct aa_dvec *dst );

/**
 * Fill in a matrix descriptor.
 *
 * @param rows  Number of rows in matrix
 * @param cols  Number of colums in matrix
 * @param data  Pointer to vector data
 * @param ld    Leading dimension of matrix
 */
static inline struct aa_dmat
AA_DMAT_INIT( size_t rows, size_t cols, double *data, size_t ld )
{
    struct aa_dmat mat;
    mat.rows = rows;
    mat.cols = cols;
    mat.data = data;
    mat.ld = ld;
    return mat;
}

#define AA_MAT_DIAG(VEC,MAT)                                    \
    aa_dvec_view((VEC), (MAT)->cols, (MAT)->data, 1+(MAT)->ld);

/**
 * Region-allocate a vector.
 *
 * When finished, pop the descriptor.
 */
AA_API struct aa_dvec *
aa_dvec_alloc( struct aa_mem_region *reg, size_t len );

/**
 * Duplicate vector out of region
 *
 * When finished, pop the descriptor.
 */
AA_API struct aa_dvec *
aa_dvec_dup( struct aa_mem_region *reg, const struct aa_dvec *src);

/**
 * Duplicate matrix out of region
 *
 * When finished, pop the descriptor.
 */
AA_API struct aa_dmat *
aa_dmat_dup( struct aa_mem_region *reg, const struct aa_dmat *src);

/**
 * Region-allocate a matrix.
 *
 * When finished, pop the descriptor.
 */
AA_API struct aa_dmat *
aa_dmat_alloc( struct aa_mem_region *reg, size_t rows, size_t cols );

/**
 * Heap-allocate a vector.
 *
 * The descriptor and data are contained in a single malloc()'ed block.
 * When finished, call free() on the descriptor.
 */
AA_API struct aa_dvec *
aa_dvec_malloc( size_t len );

/**
 * Heap-allocate a matrix.
 *
 * The descriptor and data are contained in a single malloc()'ed block.
 * When finished, call free() on the descriptor.
 */
AA_API struct aa_dmat *
aa_dmat_malloc( size_t rows, size_t cols );

/**
 * Zero a vector.
 */
AA_API void
aa_dvec_zero( struct aa_dvec *vec );

/**
 * Fill a vector.
 */
AA_API void
aa_dvec_set( struct aa_dvec *vec, double alpha );

/**
 * Fill a matrix diagonal and off-diagonal elements.
 *
 * @param[in] A        The matrix to fill
 * @param[in] alpha    off-diagonal entries of A
 * @param[in] beta     diagonal entries of A
 */
AA_API void
aa_dmat_set( struct aa_dmat *A, double alpha, double beta );

/**
 * Zero a matrix.
 */
AA_API void
aa_dmat_zero( struct aa_dmat *mat );

/* Level 1 BLAS */

/**
 * Swap x and y
 *
 * \f[ \mathbf{x} \leftrightarrow \mathbf{y} \f]
 */
AA_API void
aa_dvec_swap( struct aa_dvec *x, struct aa_dvec *y );

/**
 * Scale x by alpha.
 *
 * \f[ \mathbf{x} \leftarrow \alpha \mathbf{x} \f]
 */
AA_API void
aa_dvec_scal( double alpha, struct aa_dvec *x );

/**
 * Increment x by alpha.
 *
 * \f[ \mathbf{x} \leftarrow \alpha + \mathbf{x} \f]
 */
AA_API void
aa_dvec_inc( double alpha, struct aa_dvec *x );

/**
 * Copy x to y.
 *
 * \f[ \mathbf{y} \leftarrow \mathbf{x} \f]
 */
AA_API void
aa_dvec_copy( const struct aa_dvec *x, struct aa_dvec *y );

/**
 * Alpha x plus y.
 *
 * \f[ \mathbf{y} \leftarrow \alpha \mathbf{x} + \mathbf{y} \f]
 */
AA_API void
aa_dvec_axpy( double a, const struct aa_dvec *x, struct aa_dvec *y );

/**
 * Dot product
 *
 * \f[ \mathbf{x}^T \mathbf{y} \f]
 */
AA_API double
aa_dvec_dot( const struct aa_dvec *x, struct aa_dvec *y );

/**
 * Euclidean Norm
 *
 * \f[ \left\Vert \mathbf{x} \right\Vert_2  \f]
 */
AA_API double
aa_dvec_nrm2( const struct aa_dvec *x );

/* Level 2 BLAS */

/**
 * General Matrix-Vector multiply
 *
 * \f[ \mathbf{y} \leftarrow \alpha \mathbf{A}^{\rm op} \mathbf{x} + \beta \mathbf{y}  \f]
 */
AA_API void
aa_dmat_gemv( CBLAS_TRANSPOSE trans,
              double alpha, const struct aa_dmat *A,
              const struct aa_dvec *x,
              double beta, struct aa_dvec *y );

/* Level 3 BLAS */

/**
 * General Matrix-Matrix multiply
 *
 * \f[ \mathbf{y} \leftarrow \alpha \mathbf{A}^{\rm opA} \mathbf{B}^\rm{opB} + \beta \mathbf{C}  \f]
 */
AA_API void
aa_dmat_gemm( CBLAS_TRANSPOSE transA, CBLAS_TRANSPOSE transB,
              double alpha, const struct aa_dmat *A,
              const struct aa_dmat *B,
              double beta, struct aa_dmat *C );

/* LAPACK */

/**
 * Copies all or part of a two-dimensional matrix A to another
 * matrix B.
 *
 *  @param[in] UPLO
 *          Specifies the part of the matrix A to be copied to B.
 *          - = 'U':      Upper triangular part
 *          - = 'L':      Lower triangular part
 *          - Otherwise:  All of the matrix A
 *
 *  @param[in] A
 *          dimension (LDA,N)
 *          The m by n matrix A.  If UPLO = 'U', only the upper triangle
 *          or trapezoid is accessed; if UPLO = 'L', only the lower
 *          triangle or trapezoid is accessed.
 *
 *  @param[out] B
 *          dimension (LDB,N)
 *          On exit, B = A in the locations specified by UPLO.
 *
 */
AA_API void
aa_dmat_lacpy( const char uplo[1],
               const struct aa_dmat *A,
               struct aa_dmat *B );

/* Matrix/Vector Functions */

/**
 * sum-square-differences of two vectors
 */
AA_API double
aa_dvec_ssd( const struct aa_dvec *x, const struct aa_dvec *y);

/**
 * Y += alpha * X
 */
AA_API void
aa_dmat_axpy( double alpha, const struct aa_dmat *X, struct aa_dmat *Y);

/**
 * sum-square-differences of two matrices
 */
AA_API double
aa_dmat_ssd( const struct aa_dmat *x, const struct aa_dmat *y);

/**
 * Scale the matrix A by alpha
 */
AA_API void
aa_dmat_scal( struct aa_dmat *A, double alpha );

/**
 * Increment the matrix A by alpha
 */
AA_API void
aa_dmat_inc( struct aa_dmat *A, double alpha );

/**
 * Euclidean Norm
 */
AA_API double
aa_dmat_nrm2( const struct aa_dmat *x );

/**
 * Matrix transpose.
 */
AA_API void
aa_dmat_trans( const struct aa_dmat *A, struct aa_dmat *At);

/**
 * Matrix inverse, in-place.
 */
AA_API int
aa_dmat_inv( struct aa_dmat *A);

/**
 * Pseudo-inverse.
 *
 * Singular values less than tol are ignored.  If tol < 0, then a sane
 * default is used.
 */
AA_API int
aa_dmat_pinv( const struct aa_dmat *A, double tol, struct aa_dmat *As);

/**
 * Damped pseudo-inverse.
 */
AA_API int
aa_dmat_dpinv( const struct aa_dmat *A, double k, struct aa_dmat *As);

/**
 * Dead-zone damped pseudo-inverse.
 */
AA_API int
aa_dmat_dzdpinv(  const struct aa_dmat *A, double s_min, struct aa_dmat *As);

/**
 * Copy a matrix
 */
AA_API void
aa_dmat_copy(  const struct aa_dmat *A, struct aa_dmat *B);

#endif /* AMINO_MAT_H */


/**
 * author: Jochen K"upper
 * created: Jan 2002
 * file: pygsl/src/statistics/longmodule.c
 * $Id: intmodule.c,v 1.6 2004/03/24 08:40:45 schnizer Exp $
 *
 *
 *"
 */

#include <Python.h>
#include <pygsl/error_helpers.h>
#include <pygsl/block_helpers.h>
#include <gsl/gsl_statistics.h>

/* include real functions for different data-types */

#define STATMOD_APPEND_PY_TYPE(X) X ## Int
#define STATMOD_APPEND_PYC_TYPE(X) X ## INT
#define STATMOD_FUNC_EXT(X, Y) X ## _int ## Y
#define STATMOD_PY_AS_C PyInt_AsLong
#define STATMOD_C_TYPE int
#define PyGSL_STATISTICS_IMPORT_API
#include "functions.c"

/* initialization */

PyGSL_STATISTICS_INIT(int, "int")

/*
 * Local Variables:
 * mode: c
 * c-file-style: "Stroustrup"
 * End:
 */


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

#pragma once

#ifndef clip_c55b09bc_5fbd_463f_8d45_b12013b57f42_h
#define clip_c55b09bc_5fbd_463f_8d45_b12013b57f42_h

#include <gslib/tree.h>
#include <ariel/painterpath.h>

__ariel_begin__

enum clip_point_tag
{
    cpt_corner = 0x01,
    cpt_interpolate = 0x02,
};

enum clip_fill_type
{
    cft_even_odd = 0,
    cft_non_zero,
    cft_positive,
    cft_negative,
};

struct clip_vec2_hash
{
    size_t operator()(const vec2& pt) const { return hash_bytes((const byte*)&pt, sizeof(pt)); }
};

typedef _treenode_wrapper<painter_path> clip_result_wrapper;
typedef tree<painter_path, clip_result_wrapper> clip_result;
typedef typename clip_result::iterator clip_result_iter;
typedef typename clip_result::const_iterator clip_result_const_iter;
typedef unordered_map<vec2, uint, clip_vec2_hash> clip_point_attr;

ariel_export extern void clip_remap_points(painter_linestrips& output, clip_point_attr& attrmap, const painter_path& input, uint attr_selector, float step_len = -1.f);
ariel_export extern void clip_offset(painter_linestrips& lss, const painter_linestrips& input, float offset);
ariel_export extern void clip_stroker(painter_linestrips& lss, const painter_linestrips& input, float offset);
ariel_export extern void clip_simplify(painter_linestrips& lss, const painter_linestrip& input, clip_fill_type ft = cft_even_odd);
ariel_export extern void clip_simplify(painter_linestrips& lss, const painter_linestrips& input, clip_fill_type ft = cft_even_odd);
ariel_export extern void clip_simplify(clip_result& output, const painter_path& input);
ariel_export extern void clip_union(clip_result& output, const painter_path& subjects, const painter_path& clips);
ariel_export extern void clip_intersect(clip_result& output, const painter_path& subjects, const painter_path& clips);
ariel_export extern void clip_substract(clip_result& output, const painter_path& subjects, const painter_path& clips);
ariel_export extern void clip_exclude(clip_result& output, const painter_path& subjects, const painter_path& clips);
ariel_export extern void clip_convert(painter_path& path, const clip_result& result);

__ariel_end__

#endif


#ifndef __HCORE__
#define __HCORE__

#ifdef MKL
  #include <mkl.h>
  #include <mkl_lapack.h>
  //#pragma message("MKL is used")
#else
  #ifdef ARMPL
    #include <armpl.h>
  #else
    #include <cblas.h>
  #endif
  #ifdef LAPACKE_UTILS
    #include <lapacke_utils.h>
  #endif
  #include <lapacke.h>
  //#pragma message("MKL is NOT used")
#endif

#ifndef hcore_min
#define hcore_min(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef hcore_max
#define hcore_max(a, b) ((a) < (b) ? (b) : (a))
#endif

#define HCORE_NoTrans         111
#define HCORE_Trans           112
#define HCORE_ConjTrans       113

#define HCORE_Upper           121
#define HCORE_Lower           122
#define HCORE_UpperLower      123

#define HCORE_NonUnit         131
#define HCORE_Unit            132

#define HCORE_Left            141
#define HCORE_Right           142

typedef int  HCORE_enum;
#endif


/* rng/gsl_rng.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2004, 2007 James Theiler, 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_RNG_H__
#define __GSL_RNG_H__
#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_inline.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
  {
    const char *name;
    unsigned long int max;
    unsigned long int min;
    size_t size;
    void (*set) (void *state, unsigned long int seed);
    unsigned long int (*get) (void *state);
    double (*get_double) (void *state);
  }
gsl_rng_type;

typedef struct
  {
    const gsl_rng_type * type;
    void *state;
  }
gsl_rng;

/* These structs also need to appear in default.c so you can select
   them via the environment variable GSL_RNG_TYPE */

GSL_VAR const gsl_rng_type *gsl_rng_borosh13;
GSL_VAR const gsl_rng_type *gsl_rng_coveyou;
GSL_VAR const gsl_rng_type *gsl_rng_cmrg;
GSL_VAR const gsl_rng_type *gsl_rng_fishman18;
GSL_VAR const gsl_rng_type *gsl_rng_fishman20;
GSL_VAR const gsl_rng_type *gsl_rng_fishman2x;
GSL_VAR const gsl_rng_type *gsl_rng_gfsr4;
GSL_VAR const gsl_rng_type *gsl_rng_knuthran;
GSL_VAR const gsl_rng_type *gsl_rng_knuthran2;
GSL_VAR const gsl_rng_type *gsl_rng_knuthran2002;
GSL_VAR const gsl_rng_type *gsl_rng_lecuyer21;
GSL_VAR const gsl_rng_type *gsl_rng_minstd;
GSL_VAR const gsl_rng_type *gsl_rng_mrg;
GSL_VAR const gsl_rng_type *gsl_rng_mt19937;
GSL_VAR const gsl_rng_type *gsl_rng_mt19937_1999;
GSL_VAR const gsl_rng_type *gsl_rng_mt19937_1998;
GSL_VAR const gsl_rng_type *gsl_rng_r250;
GSL_VAR const gsl_rng_type *gsl_rng_ran0;
GSL_VAR const gsl_rng_type *gsl_rng_ran1;
GSL_VAR const gsl_rng_type *gsl_rng_ran2;
GSL_VAR const gsl_rng_type *gsl_rng_ran3;
GSL_VAR const gsl_rng_type *gsl_rng_rand;
GSL_VAR const gsl_rng_type *gsl_rng_rand48;
GSL_VAR const gsl_rng_type *gsl_rng_random128_bsd;
GSL_VAR const gsl_rng_type *gsl_rng_random128_glibc2;
GSL_VAR const gsl_rng_type *gsl_rng_random128_libc5;
GSL_VAR const gsl_rng_type *gsl_rng_random256_bsd;
GSL_VAR const gsl_rng_type *gsl_rng_random256_glibc2;
GSL_VAR const gsl_rng_type *gsl_rng_random256_libc5;
GSL_VAR const gsl_rng_type *gsl_rng_random32_bsd;
GSL_VAR const gsl_rng_type *gsl_rng_random32_glibc2;
GSL_VAR const gsl_rng_type *gsl_rng_random32_libc5;
GSL_VAR const gsl_rng_type *gsl_rng_random64_bsd;
GSL_VAR const gsl_rng_type *gsl_rng_random64_glibc2;
GSL_VAR const gsl_rng_type *gsl_rng_random64_libc5;
GSL_VAR const gsl_rng_type *gsl_rng_random8_bsd;
GSL_VAR const gsl_rng_type *gsl_rng_random8_glibc2;
GSL_VAR const gsl_rng_type *gsl_rng_random8_libc5;
GSL_VAR const gsl_rng_type *gsl_rng_random_bsd;
GSL_VAR const gsl_rng_type *gsl_rng_random_glibc2;
GSL_VAR const gsl_rng_type *gsl_rng_random_libc5;
GSL_VAR const gsl_rng_type *gsl_rng_randu;
GSL_VAR const gsl_rng_type *gsl_rng_ranf;
GSL_VAR const gsl_rng_type *gsl_rng_ranlux;
GSL_VAR const gsl_rng_type *gsl_rng_ranlux389;
GSL_VAR const gsl_rng_type *gsl_rng_ranlxd1;
GSL_VAR const gsl_rng_type *gsl_rng_ranlxd2;
GSL_VAR const gsl_rng_type *gsl_rng_ranlxs0;
GSL_VAR const gsl_rng_type *gsl_rng_ranlxs1;
GSL_VAR const gsl_rng_type *gsl_rng_ranlxs2;
GSL_VAR const gsl_rng_type *gsl_rng_ranmar;
GSL_VAR const gsl_rng_type *gsl_rng_slatec;
GSL_VAR const gsl_rng_type *gsl_rng_taus;
GSL_VAR const gsl_rng_type *gsl_rng_taus2;
GSL_VAR const gsl_rng_type *gsl_rng_taus113;
GSL_VAR const gsl_rng_type *gsl_rng_transputer;
GSL_VAR const gsl_rng_type *gsl_rng_tt800;
GSL_VAR const gsl_rng_type *gsl_rng_uni;
GSL_VAR const gsl_rng_type *gsl_rng_uni32;
GSL_VAR const gsl_rng_type *gsl_rng_vax;
GSL_VAR const gsl_rng_type *gsl_rng_waterman14;
GSL_VAR const gsl_rng_type *gsl_rng_zuf;

const gsl_rng_type ** gsl_rng_types_setup(void);

GSL_VAR const gsl_rng_type *gsl_rng_default;
GSL_VAR unsigned long int gsl_rng_default_seed;

gsl_rng *gsl_rng_alloc (const gsl_rng_type * T);
int gsl_rng_memcpy (gsl_rng * dest, const gsl_rng * src);
gsl_rng *gsl_rng_clone (const gsl_rng * r);

void gsl_rng_free (gsl_rng * r);

void gsl_rng_set (const gsl_rng * r, unsigned long int seed);
unsigned long int gsl_rng_max (const gsl_rng * r);
unsigned long int gsl_rng_min (const gsl_rng * r);
const char *gsl_rng_name (const gsl_rng * r);

int gsl_rng_fread (FILE * stream, gsl_rng * r);
int gsl_rng_fwrite (FILE * stream, const gsl_rng * r);

size_t gsl_rng_size (const gsl_rng * r);
void * gsl_rng_state (const gsl_rng * r);

void gsl_rng_print_state (const gsl_rng * r);

const gsl_rng_type * gsl_rng_env_setup (void);

INLINE_DECL unsigned long int gsl_rng_get (const gsl_rng * r);
INLINE_DECL double gsl_rng_uniform (const gsl_rng * r);
INLINE_DECL double gsl_rng_uniform_pos (const gsl_rng * r);
INLINE_DECL unsigned long int gsl_rng_uniform_int (const gsl_rng * r, unsigned long int n);

#ifdef HAVE_INLINE

INLINE_FUN unsigned long int
gsl_rng_get (const gsl_rng * r)
{
  return (r->type->get) (r->state);
}

INLINE_FUN double
gsl_rng_uniform (const gsl_rng * r)
{
  return (r->type->get_double) (r->state);
}

INLINE_FUN double
gsl_rng_uniform_pos (const gsl_rng * r)
{
  double x ;
  do
    {
      x = (r->type->get_double) (r->state) ;
    }
  while (x == 0) ;

  return x ;
}

/* Note: to avoid integer overflow in (range+1) we work with scale =
   range/n = (max-min)/n rather than scale=(max-min+1)/n, this reduces
   efficiency slightly but avoids having to check for the out of range
   value.  Note that range is typically O(2^32) so the addition of 1
   is negligible in most usage. */

INLINE_FUN unsigned long int
gsl_rng_uniform_int (const gsl_rng * r, unsigned long int n)
{
  unsigned long int offset = r->type->min;
  unsigned long int range = r->type->max - offset;
  unsigned long int scale;
  unsigned long int k;

  if (n > range || n == 0)
    {
      GSL_ERROR_VAL ("invalid n, either 0 or exceeds maximum value of generator",
                     GSL_EINVAL, 0) ;
    }

  scale = range / n;

  do
    {
      k = (((r->type->get) (r->state)) - offset) / scale;
    }
  while (k >= n);

  return k;
}
#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_RNG_H__ */


/**
 * This file is part of yasimSBML (http://www.labri.fr/perso/ghozlane/metaboflux/about/yasimSBML.php)
 * Copyright (C) 2010 Amine Ghozlane from LaBRI and University of Bordeaux 1
 *
 * yasimSBML is free software: you can redistribute it and/or modify
 * it under the terms of the Lesser GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * yasimSBML 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 Lesser GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * \file simulation.c
 * \brief Simulate a petri net
 * \author {Amine Ghozlane}
 * \version 1.0
 * \date 27 octobre 2009
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <unistd.h>
#include <gsl/gsl_rng.h>
#include <sbml/SBMLTypes.h>
#include "especes.h"
#include "simulation.h"

/*TODO Verification des BoundaryConditions,Local parameter values, Global parameter values, Reaction, des equations */

/**
 * \fn void SBML_initEspeceAmounts(Model_t *mod, pEspeces molecules, int nbEspeces)
 * \author Amine Ghozlane
 * \brief  Alloc memory and initialize the struct Especes
 * \param  mod Model of the SBML file
 * \param  molecules Struct Especes
 * \param  nbEspeces Number of molecules
 */
void SBML_initEspeceAmounts(Model_t *mod, pEspeces molecules, int nbEspeces) {
  int i;
  Species_t *esp;

  /* Initialisation des quantites des especes*/
  for (i = 0; i < nbEspeces; i++) {
      esp = Model_getSpecies(mod, i);
      /*printf("espece: %s, compartiment : %s\n",Species_getId(esp),Species_getCompartment(esp));*/
      Especes_save(molecules, i, Species_getInitialAmount(esp),
          Species_getId(esp), Species_getCompartment(esp));
  }
}

/**
 * \fn void SBML_setReactions(Model_t *mod, pEspeces molecules, pScore result, double *reactions_ratio, int nbReactions, int nbEspeces)
 * \author Amine Ghozlane
 * \brief  Alloc memory and initialize the struct Especes
 * \param  mod Model of the SBML file
 * \param  molecules Struct Especes
 * \param  result Struct Score
 * \param  reactions_ratio List of computed reaction ratio
 * \param  nbReactions Number of reaction
 * \param  nbEspeces Number of molecules
 */
void SBML_setReactions(Model_t *mod, pEspeces molecules, int nbReactions, int nbEspeces) {
  int ref = 0, i, j;
  SpeciesReference_t *reactif;
  Species_t *especeId;
  Reaction_t *react;
  const char *kf;
  const ASTNode_t *km;
  KineticLaw_t *kl;

  /*fprintf(stdout, "Debut reaction\n");*/
  /* Recherche les reactions ou apparaissent chaque espece */
  for (i = 0; i < nbReactions; i++) {
      react = Model_getReaction(mod, i);
      for (j = 0; j < (int)Reaction_getNumReactants(react); j++) {
          reactif = Reaction_getReactant(react, j);
          especeId = Model_getSpeciesById(mod, SpeciesReference_getSpecies(
              reactif));
          ref = Especes_find(molecules, Species_getId(especeId), nbEspeces);
          /*printf("Reactif :Quantite de ref %d :%f \n", ref,Especes_getQuantite(molecules, ref));*/

          kl = Reaction_getKineticLaw(react);

          if (KineticLaw_isSetFormula(kl)) {
              kf = KineticLaw_getFormula(kl);
              /*printf("c du kf %s\n", kf);*/
              Especes_allocReactions(molecules, ref, react,
                  SBML_evalExpression(kf));
          } else {
              km = KineticLaw_getMath(kl);
              printf("C du kM... cas encore ignore\n");
              exit(1);
          }
      }
  }
  /*fprintf(stdout, "Fin reaction\n");*/
}

/**
 * \fn double SBML_evalExpression(const char *formule)
 * \author Amine Ghozlane
 * \brief  Get the reaction ratio define in the sbml
 * \param  formule Formule SBML
 * \return Return double value of the constraint
 */
double SBML_evalExpression(const char *formule) {
  return atof(formule);
}

/**
 * \fn int SBML_checkQuantite(Model_t *mod, Reaction_t *react, int nbEspeces, pEspeces molecules)
 * \author Amine Ghozlane
 * \brief  Determine the number of reaction for one molecule
 * \param  mod Model of the SBML file
 * \param  react Reaction id
 * \param  nbEspeces Number of molecules
 * \param  molecules Struct Especes
 * \return Number of reaction for one molecule
 */
int SBML_checkQuantite(Model_t *mod, Reaction_t *react, int nbEspeces, pEspeces molecules)
{
  double quantite = 0.0, minStep = 0.0, temp = 0.0;
  int ref = 0, i;
  SpeciesReference_t *reactif;
  Species_t *especeId;

  reactif = Reaction_getReactant(react, 0);
  especeId = Model_getSpeciesById(mod, SpeciesReference_getSpecies(reactif));
  ref = Especes_find(molecules, Species_getId(especeId), nbEspeces);

  if ((quantite = Especes_getQuantite(molecules, ref)) <= 0.0) {
      /*printf("je fais le return\n");*/
      return END;
  }
  /*printf("quantite : %d\n",quantite);*/
  minStep = quantite / SpeciesReference_getStoichiometry(reactif);
  /*printf("quantite : %d, minStep init : %d\n",quantite,minStep);*/
  /* Cas ou le nombre de pas est egal a 0 */
  if(minStep==0.0) return END;

  for (i = 1; i < (int)Reaction_getNumReactants(react); i++) {
      reactif = Reaction_getReactant(react, i);
      especeId = Model_getSpeciesById(mod, SpeciesReference_getSpecies(reactif));
      ref = Especes_find(molecules, Species_getId(especeId), nbEspeces);
      quantite= Especes_getQuantite(molecules, ref);
      /* La quantite est egale a 0 */
      if(quantite<=0.0) return END;
      temp = floor(Especes_getQuantite(molecules, ref)/SpeciesReference_getStoichiometry(reactif));
      /* Cas ou le nombre de pas est egal a 0 */
      if(temp==0.0) return END;
      /* Si le nouveau nombre est inferieur au precedent, on change la valeur de minStep */
      if (minStep > temp) minStep = temp;
  }

  return (int)minStep;
}

/**
 * \fn Reaction_t * SBML_reactChoice(pEspeces molecules, const gsl_rng * r, int ref)
 * \author Amine Ghozlane
 * \brief  Determine randomly the reaction to achieve for several nodes reactions
 * \param  molecules Struct Especes
 * \param  r Random number generator
 * \param  ref Number reference of one molecule
 * \return Id of the selected reaction
 */
Reaction_t * SBML_reactChoice(pEspeces molecules, const gsl_rng * r, int ref) {

  pReaction temp = NULL;
  pReaction Q = molecules[ref].system;
  double value = gsl_rng_uniform(r) * 100.0;
  double choice = 0.0;

  /*printf("Choix de la reaction :\n");
	 printf("value : %f\n", value);*/

  /* Choix de la reaction a realiser */
  if (value < Q->ratio)
    temp = Q;
  else {
      do {
          /*printf("reaction %s : ratio %f\n", Reaction_getId(Q->link), Q->ratio);*/
          choice += Q->ratio;
          /*printf("choice : %f\n", choice);*/
          Q = Q->suivant;
          temp = Q;
          /*printf("ratio suivant : %f\n",Q->ratio );*/
      } while (Q->suivant != NULL && value <= (choice + Q->suivant->ratio)
          && value > choice);
  }
  if (temp == NULL) {
      fprintf(stderr, "on a un probleme de ratio\n");
      exit(EXIT_FAILURE);
  }

  /*printf("Resultat :\n");
	 printf("value : %f, choice :%f\n", value, (choice + Q->ratio));
	 printf("reaction %s : ratio %f\n", Reaction_getId(temp->link), temp->ratio);*/
  /*if (Q->suivant == NULL)
	 printf("choice :%f", choice);*/
  /*printf("c bon\n");*/

  return (temp->link);

}

/**
 * \fn void SBML_reaction(Model_t *mod, pEspeces molecules, Reaction_t *react, int nbEspeces)
 * \author Amine Ghozlane
 * \brief  Simulation of a discrete transision
 * \param  mod Model of the SBML file
 * \param  molecules Struct Especes
 * \param  react Reaction id
 * \param  nbEspeces Number of molecules
 */
void SBML_reaction(Model_t *mod, pEspeces molecules, Reaction_t *react, int nbEspeces)
{
  SpeciesReference_t *reactif;
  Species_t *especeId;
  int i, ref = 0;

  /*boucle pour retirer des reactifs*/
  for (i = 0; i < (int)Reaction_getNumReactants(react); i++) {
      reactif = Reaction_getReactant(react, i);
      especeId = Model_getSpeciesById(mod, SpeciesReference_getSpecies(reactif));
      ref = Especes_find(molecules, Species_getId(especeId), nbEspeces);

      /*printf("Reactif : %s",Species_getId(especeId) );
		 printf("ref : %d\n",ref);
		 printf("Reactif :Quantite de ref %d :%d \n",ref,Espece_getQuantite(molecules,ref));*/
      Especes_setQuantite(molecules, ref,(Especes_getQuantite(molecules, ref)- SpeciesReference_getStoichiometry(reactif)));
      /*printf("Apres Reactif: Quantite de ref %d :%d \n",ref,Espece_getQuantite(molecules,ref));*/
  }

  /*boucle pour ajouter des produits */
  for (i = 0; i < (int)Reaction_getNumProducts(react); i++) {
      reactif = Reaction_getProduct(react, i);
      especeId = Model_getSpeciesById(mod, SpeciesReference_getSpecies(reactif));
      /*printf("Produit : %s",Species_getId(especeId) );*/
      ref = Especes_find(molecules, Species_getId(especeId), /*Species_getCompartment(especeId),*/ nbEspeces);
      /*printf("Produit : ref : %d\n",ref);
		 printf("Produit : Quantite de ref %d :%d \n",ref,Espece_getQuantite(molecules,ref));*/

      Especes_setQuantite(molecules, ref,(Especes_getQuantite(molecules, ref)+ SpeciesReference_getStoichiometry(reactif)));
      /*printf("Apres Produit : Quantite de ref %d :%d \n",ref,Espece_getQuantite(molecules,ref));*/
  }
}

/**
 * \fn void SBML_allocTest(pTestReaction T, int nbReactions)
 * \author Amine Ghozlane
 * \brief  Alloc memory and initialize the struct pTestReaction
 * \param  T Empty struct TestReaction
 * \param  nbReactions Number of reactions
 */
void SBML_allocTest(pTestReaction T, int nbReactions)
{
  int i;

  /* Initialisation des tableaux des reactions */
  T->tabReactions= (Reaction_t **) malloc(nbReactions * sizeof(Reaction_t *));
  T->minStepTab = (int*) malloc(nbReactions * sizeof(int));

  if (T->tabReactions == NULL)
    exit(EXIT_FAILURE);
  if (T->minStepTab == NULL)
    exit(EXIT_FAILURE);

  for (i = 0; i < nbReactions; i++) {
      T->tabReactions[i] = NULL;
      T->minStepTab[i] = 0;
  }
}

/**
 * \fn void SBML_freeTest(pTestReaction T)
 * \author Amine Ghozlane
 * \brief  Free memory of the struct TestReaction
 * \param T Struct TestReaction gives data on reaction
 */
void SBML_freeTest(pTestReaction T)
{
  if (T->tabReactions != NULL)
    free(T->tabReactions);
  if (T->minStepTab != NULL)
    free(T->minStepTab);
}

/**
 * \fn int SBML_EstimationReaction(Model_t *mod, pTestReaction T, pEspeces molecules, int ref, int nbEspeces)
 * \author Amine Ghozlane
 * \brief  Alloc memory and initialize the struct Especes
 * \param  mod Model of the SBML file
 * \param  T Struct TestReaction gives data on reaction
 * \param  molecules Struct Especes
 * \param  ref Number reference of one molecule
 * \param  nbEspeces Number of molecules
 * \return Estimated number of feasible step by reaction
 */
int SBML_EstimationReaction(Model_t *mod, pTestReaction T, pEspeces molecules, int ref, int nbEspeces)
{
  pReaction Q = molecules[ref].system;
  int i = 0, min = 0, curr = 0;

  /* Compte le nombre de reactions rattachees a une espece */
  while (Q != NULL) {
      T->tabReactions[i] = Q->link;
      T->minStepTab[i] = SBML_checkQuantite(mod, Q->link, nbEspeces,
          molecules);
      if (i == 0)
        min = T->minStepTab[i];
      curr = T->minStepTab[i];
      if (T->minStepTab[i] <= 0)
        return END;
      else if (curr < min)
        min = curr;
      Q = Q->suivant;
      i++;
  }

  return min;
}

/**
 * \fn int SBML_simulate(Model_t *mod, pEspeces molecules, const gsl_rng * r, pTestReaction T, char **banned, int nbBanned, int nbEspeces, int ref)
 * \author Amine Ghozlane
 * \brief  Simulate one step of petri net
 * \param  mod Model of the SBML file
 * \param  molecules Struct Especes
 * \param  r Random number generator
 * \param  T Struct TestReaction gives data on reaction
 * \param  banned List of banned compound
 * \param  nbBanned Number of banned compound
 * \param  nbEspeces Number of molecules
 * \param  ref Number reference of one molecule
 * \return Condition of stop/pursue
 */
int SBML_simulate(Model_t *mod, pEspeces molecules, const gsl_rng * r, pTestReaction T, int nbEspeces, int ref)
{
  Reaction_t *react = NULL;
  int minStep = 0, valid = 0/*, i=0*/;
  /*int nbReactions = Especes_getNbreactions(molecules, ref);*/
  int nbReactions = molecules[ref].nbReactions;

  /*printf("nbreactions : %d, ref : %d\n", nbReactions, ref);
	 printf("molecules : %s\n", molecules[ref].id);*/

  /* Probleme ATP, ADP, NADH, NAD+ */
  if (!strcmp(molecules[ref].id, "ADP") || !strcmp(molecules[ref].id, "ATP")
      || !strcmp(molecules[ref].id, "NADH") || !strcmp(molecules[ref].id,
          "NADplus") || !strcmp(molecules[ref].id, "NADPH") || !strcmp(
              molecules[ref].id, "NADPplus")|| !strcmp(molecules[ref].id, "NADplus_g")||
              !strcmp(molecules[ref].id, "NADH_g")|| !strcmp(molecules[ref].id, "ADP_g")||
              !strcmp(molecules[ref].id, "ATP_g")|| !strcmp(molecules[ref].id, "ADP_c")|| !strcmp(molecules[ref].id, "ATP_c")) {
      /*printf("END : molecules : %s\n", molecules[ref].id);*/
      return END;
  }

  /* Elimine les calculs sur ADP... */
  /*for(j=0;j<BANNI;j++){
	 if(!strcmp(tab[i],molecules[ref].id)){
	 printf("test : tab: %s molecules : %s\n",tab[i],molecules[ref].id );
	 return END;
	 }
	 }*/

  /* Variation selon le cas. */
  switch (nbReactions) {
  /* Cas ou aucune reaction n'est possible */
  case 0:
    /*printf("Cas N°1\n");*/
    return END;
    break;
    /* Cas ou une seule reaction est possible */
  case 1:
    /*printf("Cas N°2\n");*/
    react = molecules[ref].system->link;
    if ((minStep = SBML_checkQuantite(mod, react, nbEspeces, molecules))<= END)
      return END;
    /*printf("minstep = %d\n", minStep);*/

    while (minStep > 0) {
        SBML_reaction(mod, molecules, react, nbEspeces);
        minStep--;
    }
    break;
    /* Cas ou  plusieurs reactions sont possibles*/
  default:
    /*printf("Cas N°3\n");*/
    /* Allocation de la memoire au tableau des reactions */
    SBML_allocTest(T, nbReactions);
    valid = SBML_EstimationReaction(mod, T, molecules, ref, nbEspeces);
    if (valid <= END) {
        /*printf("on ne fait rien\n");*/
        return END;
    }
    while (Especes_getQuantite(molecules, ref) > 0.0 && valid > END) {
        react = SBML_reactChoice(molecules, r, ref);
        /*printf("molecule : %s, reaction %s\n", molecules[ref].id, Reaction_getId(react));*/
        /*printf("reaction : %s\n", Reaction_getId(react));*/
        /*printf("valid : %d, i : %d\n", valid, i);*/
        SBML_reaction(mod, molecules, react, nbEspeces);
        /*if(i>=valid) valid=SBML_EstimationReaction(mod, T, molecules, ref, nbEspeces);*/
        /*i++;*/
        valid--;
    }
    /*printf("Fin de simulation\n");*/

    /* Liberation de la memoire allouee au tableau des reactions */
    SBML_freeTest(T);
    break;
  }
  /*printf("On quitte la simulation\n");*/
  return PURSUE;
}

/**
 * \fn void SBML_compute_simulation(pScore result, Model_t *mod, double *reactions_ratio, gsl_rng * r, char **banned, int nbBanned)
 * \author Amine Ghozlane
 * \brief  Simulation of metabolic network
 * \param  result Struct Score
 * \param  mod Model of the SBML file
 * \param  reactions_ratio List of computed reaction ratio
 * \param  r Random number generator
 * \param  banned List of banned compound
 * \param  nbBanned Number of banned compound
 */
void compute_simulation(Model_t *mod)
{
  int i, nbReactions = 0, nbEspeces = 0, temp = 1, tempo = 0;
  pEspeces molecules;
  const gsl_rng_type *T;
  gsl_rng * r;
  pTestReaction TR = NULL;

  /* Verifications */
  /*TODO numGlobalParameters=Model_getNumParameters(mod);
	checkBoundaryConditions(mod);
	if(numGlobalParameters>0){
	    globalParVec=gsl_vector_alloc(numGlobalParameters);
	    setGlobalParVec();
	  }*/
  /* Allocation memoire et initialisation des generateurs de nombre aleatoire */
  gsl_rng_env_setup();
  if (!getenv("GSL_RNG_SEED"))
    gsl_rng_default_seed = time(NULL);
  T = gsl_rng_default;
  r = gsl_rng_alloc(T);
  gsl_rng_default_seed = gsl_rng_uniform(r);

  /* Allocation memoire */
  TR = (pTestReaction) malloc(1 * sizeof(TestReaction));
  if (T == NULL)
    exit(EXIT_FAILURE);

  /* File information */
  nbReactions = Model_getNumReactions(mod);
  nbEspeces = Model_getNumSpecies(mod);
  /*printf("Nom du model : %s\n",Model_getId(mod));*/
  molecules = Especes_alloc(nbEspeces);
  /* Initialisation de la quantite des especes */
  SBML_initEspeceAmounts(mod, molecules, nbEspeces);
  /* Initialisation des reactions et des ratios*/
  SBML_setReactions(mod, molecules, nbReactions, nbEspeces);

  /*TODO checkRatio(mod,molecules);*/

  /* Test des donnees enregistrees */
  printf("\nEtat des especes :\n\n");
  Especes_print(molecules, nbEspeces);

  printf("\nDebut de simulation ...\n");
  /* Simulation des reactions */
  while (temp > END) {
      temp = 0;
      for (i = 0; i < nbEspeces; i++) {
          tempo = SBML_simulate(mod, molecules, r, TR, nbEspeces, i);
          /*if (tempo != END) {
              Especes_print_2(molecules, nbEspeces);
				 printf("\n");
          }*/
          temp += tempo;
      }
  }
  printf("Fin de simulation...\n");
  printf("\nEtat des especes :\n");
  Especes_print_2(molecules, nbEspeces);
  /* Liberation de la memoire des generateurs aleatoire */
  gsl_rng_free(r);

  /* Liberation de la memoire de la structure Especes */
  Especes_free(molecules, nbEspeces);
  if (TR != NULL)
    free(TR);
}

/**
 * \fn void SBML_score_add(pScore result, pScore result_temp, FILE *debugFile)
 * \author Amine Ghozlane
 * \brief  Add scores
 * \param  result Struct Score used for all the simulation
 * \param  result_temp Struct Score used at each simulation step
 * \param  debugFile File use for debug
 */
void SBML_score_add(pScore result, pScore result_temp, int nbEspeces)
{
  /* Addition des scores */
  int i;

  /* Copie des resultats */
  for(i=0;i<nbEspeces;i++){
      if(result->name[i]==NULL){
          result->name[i]=(char*)malloc(((int)strlen(result_temp->name[i])+1)*sizeof(char));
          assert(result->name[i]!=NULL);
          strcpy(result->name[i], result_temp->name[i]);
      }
      result->quantite[i]+=result_temp->quantite[i];
  }
}

/**
 * \fn void SBML_score_mean(pScore result, int n)
 * \author Amine Ghozlane
 * \brief  Mean quantities for score
 * \param  result Struct Score
 * \param  n Number of simulation step
 */
void SBML_score_mean(pScore result, int nbEspeces, int n)
{
  /* Moyenne des resultats */
  int i;
  for(i=0;i<nbEspeces;i++){
      result->quantite[i]/=n;
  }
}

/**
 * \fn pScore SBML_scoreAlloc(pListParameters a)
 * \author Amine Ghozlane
 * \brief  Allocation of the struct Score
 * \param  a Global parameters : struct ListParameters
 * \return Allocated struct Score
 */
pScore SBML_scoreAlloc(int nbEspeces)
{
  int i;
  /* Allocation de la structure de score */
  pScore result=NULL;
  result=(pScore)malloc(1*sizeof(Score));
  assert(result!=NULL);

  result->name=NULL;
  result->quantite=NULL;
  result->name=(char**)malloc(nbEspeces*sizeof(char*));
  assert(result->name!=NULL);
  result->quantite=(double*)malloc(nbEspeces*sizeof(double));
  assert(result->quantite!=NULL);
  for(i=0;i<nbEspeces;i++){
      result->name[i]=NULL;
      result->quantite[i]=0.0;
  }
  return result;
}

/**
 * \fn void SBML_scoreFree(pScore out)
 * \author Amine Ghozlane
 * \brief  Free the struct Score
 * \param  out Struct score
 */
void SBML_scoreFree(pScore out, int nbEspeces)
{
  int i;
  /* Libere la memoire alloue a la structure de score */
  for(i=0;i<nbEspeces;i++){
      if(out->name[i]!=NULL) free(out->name[i]);
  }
  if(out->name!=NULL) free(out->name);
  if(out->quantite!=NULL) free(out->quantite);
  if(out!=NULL) free(out);
}

/* Affiche le score moyen */

/**
 * \fn void SBML_scoreFree(pScore out)
 * \author Amine Ghozlane
 * \brief  Free the struct Score
 * \param  out Struct score
 * \param  nbEspeces
 */
void SBML_score_print(pScore result, int nbEspeces)
{
  int i;
  /* Print head*/
  /*for(i=0;i<nbEspeces;i++){
      printf("%s;",result->name[i]);
  }
  printf("\n");*/
  /* Print Value */
  for(i=0;i<nbEspeces;i++){
      /*printf("Molecule: %s - Biomass: %.0f\n",result->name[i],result->quantite[i]);*/
      printf("%.0f;",result->quantite[i]);
  }
  printf("\n");
}

/**
 * \fn void SBML_compute_simulation_mean(Model_t *mod, int nb_simulation)
 * \author Amine Ghozlane
 * \brief  X time simulation of metabolic network
 * \param  mod Model of the SBML file
 * \param  nb_simulation Number of simulation step
 */
void SBML_compute_simulation_mean(Model_t *mod, int nb_simulation)
{
  /* Simulation du reseau metabolique  */
  int i, j,nbReactions = 0, nbEspeces = 0, temp = 1, tempo=0, test=0;
  pEspeces molecules=NULL;
  pTestReaction TR=NULL;
  const gsl_rng_type *T;
  gsl_rng * r;
  pScore result=NULL,result_temp=NULL;

  /* Allocation memoire et initialisation des generateurs de nombre aleatoire */
  gsl_rng_env_setup();
  if (!getenv("GSL_RNG_SEED"))
    gsl_rng_default_seed = time(NULL)+(double)getpid();
  T = gsl_rng_default;
  r = gsl_rng_alloc(T);
  /*gsl_rng_default_seed = time(NULL)+(double)getpid();*/

  /* Allocation memoire */
  TR=(pTestReaction)malloc(1*sizeof(TestReaction));
  assert(TR!=NULL);

  /* File information */
  nbReactions = (int)Model_getNumReactions(mod);
  nbEspeces = (int)Model_getNumSpecies(mod);
  molecules = Especes_alloc(nbEspeces);

  /* Initialisation de la structure de score temporaire */
  result=SBML_scoreAlloc(nbEspeces);
  result_temp=SBML_scoreAlloc(nbEspeces);

  /* Initialisation de la quantite des especes */
  SBML_initEspeceAmounts(mod, molecules, nbEspeces);
  /* Initialisation des reactions et des ratios*/
  SBML_setReactions(mod, molecules, nbReactions, nbEspeces);

  /*printf("Start the simulation\n");*/
  /* SIMULATION */
  for(j=0;j<nb_simulation;j++){
      /* Simulation des reactions */
      while (temp > END) {
          temp = 0;
          for (i = 0; i < nbEspeces; i++) {
              tempo = SBML_simulate(mod, molecules, r, TR, nbEspeces, i);
              temp +=tempo;
          }
          test+=1;
      }
      temp=1;
      tempo=0;
      /*printf("Nombre de tour %d\n",test);*/
      /*Score */
      /* Enregistre le score des especes */
      Especes_scoreSpecies(molecules, nbEspeces, result_temp->name, result_temp->quantite);
      SBML_score_add(result,result_temp,nbEspeces);
      SBML_initEspeceAmounts(mod, molecules, nbEspeces);
  }
  SBML_score_mean(result,nbEspeces,nb_simulation);

  /*printf("End of the simulation...\n");
  printf("Final state of the molecules :\n");*/
  SBML_score_print(result, nbEspeces);

  /* Liberation de la memoire des generateurs aleatoire */
  gsl_rng_free(r);

  /* Liberation de la memoire du score */
  SBML_scoreFree(result, nbEspeces);
  SBML_scoreFree(result_temp, nbEspeces);

  /* Liberation de la memoire de la structure Especes */
  Especes_free(molecules, nbEspeces);
  if(TR!=NULL) free(TR);
}


#ifndef PROB_FUNCTIONS_H
#define PROB_FUNCTIONS_H

#include <gsl/gsl_vector.h>
#include "data.h"

double my_f (const gsl_vector *x, void *params);
void my_df (const gsl_vector *x, void *params, gsl_vector *g);
void my_fdf (const gsl_vector *x, void *params, double *f, gsl_vector *g);
void gradientQ (Dataset *data, double *dQdAlpha, double *dQdBeta);
double computeLikelihood (Dataset *data);
double computeQ (Dataset *data);
double logProbL (int l, int z, double alphaI, double betaJ);
double prob(double alpha, double beta);
void EStep (Dataset *data);
void MStep (Dataset *data);

#endif