/* ******************************************************************************
 *
 *
 * This program and the accompanying materials are made available under the
 * terms of the Apache License, Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0.
 *
 *  See the NOTICE file distributed with this work for additional
 *  information regarding copyright ownership.
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 ******************************************************************************/

//
// Created by agibsonccc on 1/26/16.
//

#ifndef NATIVEOPERATIONS_CBLAS_H
#define NATIVEOPERATIONS_CBLAS_H

#ifndef __STANDALONE_BUILD__
#include "config.h"
#endif

#ifdef __MKL_CBLAS_H__
// CBLAS from MKL is already included
#define CBLAS_H
#endif

#ifdef HAVE_OPENBLAS
// include CBLAS from OpenBLAS
#ifdef __GNUC__
#include_next <cblas.h>
#else
#include <cblas.h>
#endif
#define CBLAS_H
#endif

#ifndef CBLAS_H

#ifdef __cplusplus
extern "C" {
#endif
#ifndef CBLAS_ENUM_DEFINED_H
#define CBLAS_ENUM_DEFINED_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 };
#endif

#ifndef CBLAS_ENUM_ONLY
#define CBLAS_H
#define CBLAS_INDEX int

int cblas_errprn(int ierr, int info, char *form, ...);
void cblas_xerbla(int p, char *rout, char *form, ...);

#ifdef __MKL
void MKL_Set_Num_Threads(int num);
int MKL_Domain_Set_Num_Threads(int num, int domain);
int MKL_Set_Num_Threads_Local(int num);
#elif __OPENBLAS
void openblas_set_num_threads(int num);
#else
// do nothing
#endif

/*
 * ===========================================================================
 * Prototypes for level 1 BLAS functions (complex are recast as routines)
 * ===========================================================================
 */
float cblas_sdsdot(int N, float alpha, float *X, int incX, float *Y, int incY);
double cblas_dsdot(int N, float *X, int incX, float *Y, int incY);
float cblas_sdot(int N, float *X, int incX, float *Y, int incY);
double cblas_ddot(int N, double *X, int incX, double *Y, int incY);
/*
 * Functions having prefixes Z and C only
 */
void cblas_cdotu_sub(int N, void *X, int incX, void *Y, int incY, void *dotu);
void cblas_cdotc_sub(int N, void *X, int incX, void *Y, int incY, void *dotc);

void cblas_zdotu_sub(int N, void *X, int incX, void *Y, int incY, void *dotu);
void cblas_zdotc_sub(int N, void *X, int incX, void *Y, int incY, void *dotc);

/*
 * Functions having prefixes S D SC DZ
 */
float cblas_snrm2(int N, float *X, int incX);
float cblas_sasum(int N, float *X, int incX);

double cblas_dnrm2(int N, double *X, int incX);
double cblas_dasum(int N, double *X, int incX);

float cblas_scnrm2(int N, void *X, int incX);
float cblas_scasum(int N, void *X, int incX);

double cblas_dznrm2(int N, void *X, int incX);
double cblas_dzasum(int N, void *X, int incX);

/*
 * Functions having standard 4 prefixes (S D C Z)
 */
CBLAS_INDEX cblas_isamax(int N, float *X, int incX);
CBLAS_INDEX cblas_idamax(int N, double *X, int incX);
CBLAS_INDEX cblas_icamax(int N, void *X, int incX);
CBLAS_INDEX cblas_izamax(int N, void *X, int incX);

/*
 * ===========================================================================
 * Prototypes for level 1 BLAS routines
 * ===========================================================================
 */

/*
 * Routines with standard 4 prefixes (s, d, c, z)
 */
void cblas_sswap(int N, float *X, int incX, float *Y, int incY);
void cblas_scopy(int N, float *X, int incX, float *Y, int incY);
void cblas_saxpy(int N, float alpha, float *X, int incX, float *Y, int incY);
void catlas_saxpby(int N, float alpha, float *X, int incX, float beta, float *Y, int incY);
void catlas_sset(int N, float alpha, float *X, int incX);

void cblas_dswap(int N, double *X, int incX, double *Y, int incY);
void cblas_dcopy(int N, double *X, int incX, double *Y, int incY);
void cblas_daxpy(int N, double alpha, double *X, int incX, double *Y, int incY);
void catlas_daxpby(int N, double alpha, double *X, int incX, double beta, double *Y, int incY);
void catlas_dset(int N, double alpha, double *X, int incX);

void cblas_cswap(int N, void *X, int incX, void *Y, int incY);
void cblas_ccopy(int N, void *X, int incX, void *Y, int incY);
void cblas_caxpy(int N, void *alpha, void *X, int incX, void *Y, int incY);
void catlas_caxpby(int N, void *alpha, void *X, int incX, void *beta, void *Y, int incY);
void catlas_cset(int N, void *alpha, void *X, int incX);

void cblas_zswap(int N, void *X, int incX, void *Y, int incY);
void cblas_zcopy(int N, void *X, int incX, void *Y, int incY);
void cblas_zaxpy(int N, void *alpha, void *X, int incX, void *Y, int incY);
void catlas_zaxpby(int N, void *alpha, void *X, int incX, void *beta, void *Y, int incY);
void catlas_zset(int N, void *alpha, void *X, int incX);

/*
 * Routines with S and D prefix only
 */
void cblas_srotg(float *a, float *b, float *c, float *s);
void cblas_srotmg(float *d1, float *d2, float *b1, float b2, float *P);
void cblas_srot(int N, float *X, int incX, float *Y, int incY, float c, float s);
void cblas_srotm(int N, float *X, int incX, float *Y, int incY, float *P);

void cblas_drotg(double *a, double *b, double *c, double *s);
void cblas_drotmg(double *d1, double *d2, double *b1, double b2, double *P);
void cblas_drot(int N, double *X, int incX, double *Y, int incY, double c, double s);
void cblas_drotm(int N, double *X, int incX, double *Y, int incY, double *P);

/*
 * Routines with S D C Z CS and ZD prefixes
 */
void cblas_sscal(int N, float alpha, float *X, int incX);
void cblas_dscal(int N, double alpha, double *X, int incX);
void cblas_cscal(int N, void *alpha, void *X, int incX);
void cblas_zscal(int N, void *alpha, void *X, int incX);
void cblas_csscal(int N, float alpha, void *X, int incX);
void cblas_zdscal(int N, double alpha, void *X, int incX);

/*
 * Extra reference routines provided by ATLAS, but not mandated by the standard
 */
void cblas_crotg(void *a, void *b, void *c, void *s);
void cblas_zrotg(void *a, void *b, void *c, void *s);
void cblas_csrot(int N, void *X, int incX, void *Y, int incY, float c, float s);
void cblas_zdrot(int N, void *X, int incX, void *Y, int incY, double c, double s);

/*
 * ===========================================================================
 * Prototypes for level 2 BLAS
 * ===========================================================================
 */

/*
 * Routines with standard 4 prefixes (S, D, C, Z)
 */
void cblas_sgemv(enum CBLAS_ORDER Order, enum CBLAS_TRANSPOSE TransA, int M, int N, float alpha, float *A, int lda,
                 float *X, int incX, float beta, float *Y, int incY);
void cblas_sgbmv(enum CBLAS_ORDER Order, enum CBLAS_TRANSPOSE TransA, int M, int N, int KL, int KU, float alpha,
                 float *A, int lda, float *X, int incX, float beta, float *Y, int incY);
void cblas_strmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 float *A, int lda, float *X, int incX);
void cblas_stbmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 int K, float *A, int lda, float *X, int incX);
void cblas_stpmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 float *Ap, float *X, int incX);
void cblas_strsv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 float *A, int lda, float *X, int incX);
void cblas_stbsv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 int K, float *A, int lda, float *X, int incX);
void cblas_stpsv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 float *Ap, float *X, int incX);

void cblas_dgemv(enum CBLAS_ORDER Order, enum CBLAS_TRANSPOSE TransA, int M, int N, double alpha, double *A, int lda,
                 double *X, int incX, double beta, double *Y, int incY);
void cblas_dgbmv(enum CBLAS_ORDER Order, enum CBLAS_TRANSPOSE TransA, int M, int N, int KL, int KU, double alpha,
                 double *A, int lda, double *X, int incX, double beta, double *Y, int incY);
void cblas_dtrmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 double *A, int lda, double *X, int incX);
void cblas_dtbmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 int K, double *A, int lda, double *X, int incX);
void cblas_dtpmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 double *Ap, double *X, int incX);
void cblas_dtrsv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 double *A, int lda, double *X, int incX);
void cblas_dtbsv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 int K, double *A, int lda, double *X, int incX);
void cblas_dtpsv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 double *Ap, double *X, int incX);

void cblas_cgemv(enum CBLAS_ORDER Order, enum CBLAS_TRANSPOSE TransA, int M, int N, void *alpha, void *A, int lda,
                 void *X, int incX, void *beta, void *Y, int incY);
void cblas_cgbmv(enum CBLAS_ORDER Order, enum CBLAS_TRANSPOSE TransA, int M, int N, int KL, int KU, void *alpha,
                 void *A, int lda, void *X, int incX, void *beta, void *Y, int incY);
void cblas_ctrmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 void *A, int lda, void *X, int incX);
void cblas_ctbmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 int K, void *A, int lda, void *X, int incX);
void cblas_ctpmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 void *Ap, void *X, int incX);
void cblas_ctrsv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 void *A, int lda, void *X, int incX);
void cblas_ctbsv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 int K, void *A, int lda, void *X, int incX);
void cblas_ctpsv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 void *Ap, void *X, int incX);

void cblas_zgemv(enum CBLAS_ORDER Order, enum CBLAS_TRANSPOSE TransA, int M, int N, void *alpha, void *A, int lda,
                 void *X, int incX, void *beta, void *Y, int incY);
void cblas_zgbmv(enum CBLAS_ORDER Order, enum CBLAS_TRANSPOSE TransA, int M, int N, int KL, int KU, void *alpha,
                 void *A, int lda, void *X, int incX, void *beta, void *Y, int incY);
void cblas_ztrmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 void *A, int lda, void *X, int incX);
void cblas_ztbmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 int K, void *A, int lda, void *X, int incX);
void cblas_ztpmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 void *Ap, void *X, int incX);
void cblas_ztrsv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 void *A, int lda, void *X, int incX);
void cblas_ztbsv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 int K, void *A, int lda, void *X, int incX);
void cblas_ztpsv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA, enum CBLAS_DIAG Diag, int N,
                 void *Ap, void *X, int incX);

/*
 * Routines with S and D prefixes only
 */
void cblas_ssymv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, float alpha, float *A, int lda, float *X,
                 int incX, float beta, float *Y, int incY);
void cblas_ssbmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, int K, float alpha, float *A, int lda, float *X,
                 int incX, float beta, float *Y, int incY);
void cblas_sspmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, float alpha, float *Ap, float *X, int incX,
                 float beta, float *Y, int incY);
void cblas_sger(enum CBLAS_ORDER Order, int M, int N, float alpha, float *X, int incX, float *Y, int incY, float *A,
                int lda);
void cblas_ssyr(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, float alpha, float *X, int incX, float *A,
                int lda);
void cblas_sspr(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, float alpha, float *X, int incX, float *Ap);
void cblas_ssyr2(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, float alpha, float *X, int incX, float *Y,
                 int incY, float *A, int lda);
void cblas_sspr2(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, float alpha, float *X, int incX, float *Y,
                 int incY, float *A);

void cblas_dsymv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, double alpha, double *A, int lda, double *X,
                 int incX, double beta, double *Y, int incY);
void cblas_dsbmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, int K, double alpha, double *A, int lda,
                 double *X, int incX, double beta, double *Y, int incY);
void cblas_dspmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, double alpha, double *Ap, double *X, int incX,
                 double beta, double *Y, int incY);
void cblas_dger(enum CBLAS_ORDER Order, int M, int N, double alpha, double *X, int incX, double *Y, int incY, double *A,
                int lda);
void cblas_dsyr(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, double alpha, double *X, int incX, double *A,
                int lda);
void cblas_dspr(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, double alpha, double *X, int incX, double *Ap);
void cblas_dsyr2(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, double alpha, double *X, int incX, double *Y,
                 int incY, double *A, int lda);
void cblas_dspr2(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, double alpha, double *X, int incX, double *Y,
                 int incY, double *A);

/*
 * Routines with C and Z prefixes only
 */
void cblas_chemv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, void *alpha, void *A, int lda, void *X, int incX,
                 void *beta, void *Y, int incY);
void cblas_chbmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, int K, void *alpha, void *A, int lda, void *X,
                 int incX, void *beta, void *Y, int incY);
void cblas_chpmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, void *alpha, void *Ap, void *X, int incX,
                 void *beta, void *Y, int incY);
void cblas_cgeru(enum CBLAS_ORDER Order, int M, int N, void *alpha, void *X, int incX, void *Y, int incY, void *A,
                 int lda);
void cblas_cgerc(enum CBLAS_ORDER Order, int M, int N, void *alpha, void *X, int incX, void *Y, int incY, void *A,
                 int lda);
void cblas_cher(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, float alpha, void *X, int incX, void *A, int lda);
void cblas_chpr(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, float alpha, void *X, int incX, void *A);
void cblas_cher2(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, void *alpha, void *X, int incX, void *Y, int incY,
                 void *A, int lda);
void cblas_chpr2(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, void *alpha, void *X, int incX, void *Y, int incY,
                 void *Ap);

void cblas_zhemv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, void *alpha, void *A, int lda, void *X, int incX,
                 void *beta, void *Y, int incY);
void cblas_zhbmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, int K, void *alpha, void *A, int lda, void *X,
                 int incX, void *beta, void *Y, int incY);
void cblas_zhpmv(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, void *alpha, void *Ap, void *X, int incX,
                 void *beta, void *Y, int incY);
void cblas_zgeru(enum CBLAS_ORDER Order, int M, int N, void *alpha, void *X, int incX, void *Y, int incY, void *A,
                 int lda);
void cblas_zgerc(enum CBLAS_ORDER Order, int M, int N, void *alpha, void *X, int incX, void *Y, int incY, void *A,
                 int lda);
void cblas_zher(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, double alpha, void *X, int incX, void *A, int lda);
void cblas_zhpr(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, double alpha, void *X, int incX, void *A);
void cblas_zher2(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, void *alpha, void *X, int incX, void *Y, int incY,
                 void *A, int lda);
void cblas_zhpr2(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, int N, void *alpha, void *X, int incX, void *Y, int incY,
                 void *Ap);

/*
 * ===========================================================================
 * Prototypes for level 3 BLAS
 * ===========================================================================
 */

/*
 * Routines with standard 4 prefixes (S, D, C, Z)
 */
void cblas_sgemm(enum CBLAS_ORDER Order, enum CBLAS_TRANSPOSE TransA, enum CBLAS_TRANSPOSE TransB, int M, int N, int K,
                 float alpha, float *A, int lda, float *B, int ldb, float beta, float *C, int ldc);
void cblas_ssymm(enum CBLAS_ORDER Order, enum CBLAS_SIDE Side, enum CBLAS_UPLO Uplo, int M, int N, float alpha,
                 float *A, int lda, float *B, int ldb, float beta, float *C, int ldc);
void cblas_ssyrk(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE Trans, int N, int K, float alpha,
                 float *A, int lda, float beta, float *C, int ldc);
void cblas_ssyr2k(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE Trans, int N, int K, float alpha,
                  float *A, int lda, float *B, int ldb, float beta, float *C, int ldc);
void cblas_strmm(enum CBLAS_ORDER Order, enum CBLAS_SIDE Side, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA,
                 enum CBLAS_DIAG Diag, int M, int N, float alpha, float *A, int lda, float *B, int ldb);
void cblas_strsm(enum CBLAS_ORDER Order, enum CBLAS_SIDE Side, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA,
                 enum CBLAS_DIAG Diag, int M, int N, float alpha, float *A, int lda, float *B, int ldb);

void cblas_dgemm(enum CBLAS_ORDER Order, enum CBLAS_TRANSPOSE TransA, enum CBLAS_TRANSPOSE TransB, int M, int N, int K,
                 double alpha, double *A, int lda, double *B, int ldb, double beta, double *C, int ldc);
void cblas_dsymm(enum CBLAS_ORDER Order, enum CBLAS_SIDE Side, enum CBLAS_UPLO Uplo, int M, int N, double alpha,
                 double *A, int lda, double *B, int ldb, double beta, double *C, int ldc);
void cblas_dsyrk(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE Trans, int N, int K, double alpha,
                 double *A, int lda, double beta, double *C, int ldc);
void cblas_dsyr2k(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE Trans, int N, int K, double alpha,
                  double *A, int lda, double *B, int ldb, double beta, double *C, int ldc);
void cblas_dtrmm(enum CBLAS_ORDER Order, enum CBLAS_SIDE Side, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA,
                 enum CBLAS_DIAG Diag, int M, int N, double alpha, double *A, int lda, double *B, int ldb);
void cblas_dtrsm(enum CBLAS_ORDER Order, enum CBLAS_SIDE Side, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA,
                 enum CBLAS_DIAG Diag, int M, int N, double alpha, double *A, int lda, double *B, int ldb);

void cblas_cgemm(enum CBLAS_ORDER Order, enum CBLAS_TRANSPOSE TransA, enum CBLAS_TRANSPOSE TransB, int M, int N, int K,
                 void *alpha, void *A, int lda, void *B, int ldb, void *beta, void *C, int ldc);
void cblas_csymm(enum CBLAS_ORDER Order, enum CBLAS_SIDE Side, enum CBLAS_UPLO Uplo, int M, int N, void *alpha, void *A,
                 int lda, void *B, int ldb, void *beta, void *C, int ldc);
void cblas_csyrk(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE Trans, int N, int K, void *alpha,
                 void *A, int lda, void *beta, void *C, int ldc);
void cblas_csyr2k(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE Trans, int N, int K, void *alpha,
                  void *A, int lda, void *B, int ldb, void *beta, void *C, int ldc);
void cblas_ctrmm(enum CBLAS_ORDER Order, enum CBLAS_SIDE Side, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA,
                 enum CBLAS_DIAG Diag, int M, int N, void *alpha, void *A, int lda, void *B, int ldb);
void cblas_ctrsm(enum CBLAS_ORDER Order, enum CBLAS_SIDE Side, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA,
                 enum CBLAS_DIAG Diag, int M, int N, void *alpha, void *A, int lda, void *B, int ldb);

void cblas_zgemm(enum CBLAS_ORDER Order, enum CBLAS_TRANSPOSE TransA, enum CBLAS_TRANSPOSE TransB, int M, int N, int K,
                 void *alpha, void *A, int lda, void *B, int ldb, void *beta, void *C, int ldc);
void cblas_zsymm(enum CBLAS_ORDER Order, enum CBLAS_SIDE Side, enum CBLAS_UPLO Uplo, int M, int N, void *alpha, void *A,
                 int lda, void *B, int ldb, void *beta, void *C, int ldc);
void cblas_zsyrk(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE Trans, int N, int K, void *alpha,
                 void *A, int lda, void *beta, void *C, int ldc);
void cblas_zsyr2k(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE Trans, int N, int K, void *alpha,
                  void *A, int lda, void *B, int ldb, void *beta, void *C, int ldc);
void cblas_ztrmm(enum CBLAS_ORDER Order, enum CBLAS_SIDE Side, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA,
                 enum CBLAS_DIAG Diag, int M, int N, void *alpha, void *A, int lda, void *B, int ldb);
void cblas_ztrsm(enum CBLAS_ORDER Order, enum CBLAS_SIDE Side, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE TransA,
                 enum CBLAS_DIAG Diag, int M, int N, void *alpha, void *A, int lda, void *B, int ldb);

/*
 * Routines with prefixes C and Z only
 */
void cblas_chemm(enum CBLAS_ORDER Order, enum CBLAS_SIDE Side, enum CBLAS_UPLO Uplo, int M, int N, void *alpha, void *A,
                 int lda, void *B, int ldb, void *beta, void *C, int ldc);
void cblas_cherk(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE Trans, int N, int K, float alpha,
                 void *A, int lda, float beta, void *C, int ldc);
void cblas_cher2k(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE Trans, int N, int K, void *alpha,
                  void *A, int lda, void *B, int ldb, float beta, void *C, int ldc);
void cblas_zhemm(enum CBLAS_ORDER Order, enum CBLAS_SIDE Side, enum CBLAS_UPLO Uplo, int M, int N, void *alpha, void *A,
                 int lda, void *B, int ldb, void *beta, void *C, int ldc);
void cblas_zherk(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE Trans, int N, int K, double alpha,
                 void *A, int lda, double beta, void *C, int ldc);
void cblas_zher2k(enum CBLAS_ORDER Order, enum CBLAS_UPLO Uplo, enum CBLAS_TRANSPOSE Trans, int N, int K, void *alpha,
                  void *A, int lda, void *B, int ldb, double beta, void *C, int ldc);

int cblas_errprn(int ierr, int info, char *form, ...);
#ifdef __cplusplus
}
#endif
#endif /* end #ifdef CBLAS_ENUM_ONLY */
#endif
#endif  // NATIVEOPERATIONS_CBLAS_H


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

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

#include "norm.h"
#include "constants.h"

int readFileVector( gsl_vector *vector, char *filePath, char *fileName )                //read file vals into vector
{
	char tmpFilepath[120] = "\0";
	char tmpReading[20];
	char *tmp2;
	double reading;                                 //WARNING this is similar to global var $readings, consider renaming

	strcat( tmpFilepath, filePath );                //TODO replace this and the first instance of strcat into strcpy, that should work
	strcat( tmpFilepath, fileName );        //combines the filepath and name into one searchable /path/file
	FILE *file = fopen( tmpFilepath, "rt");
	printf("%s\n", tmpFilepath);                    //TODO DELETE THIS
	if ( file == NULL ) {                   //check if file exists
		fprintf( stderr, "Error, unable to locate the Mu data file\n");
		exit(100);
	}

	for ( int i = 0; i < skip; i++)         //move past header of read files
	{
		fgets( tmpReading, 19, file );
	}

	for ( int i = 0; i < readings; i++ )
	{
		fgets( tmpReading, 19, file );

		tmp2 = strrchr( tmpReading, '\t' );
		tmp2 = tmp2 + 1;
		reading = atof(tmp2);
		gsl_vector_set( vector, i, reading);
	}
	fclose(file);
	return 0;
}

int readFileMatrix( gsl_matrix *matrix, char *filePath, int argc, char *argv[] )
{
	char *tmp2;
	double reading;
	for ( int j = 2; j < argc; j++ )
	{
		gsl_vector_view column;
		char tmpFilepath[120] = "\0";                           //TODO replace this and the first instance of strcat into strcpy, that should work
		char tmpReading[20];
		strcat( tmpFilepath, filePath );
		strcat( tmpFilepath, argv[j] );         //combines the filepath and name into one searchable /path/file
		FILE *file = fopen( tmpFilepath, "rt");
		printf("%s\n", tmpFilepath);                            //TODO DELETE THIS
		if ( file == NULL ) {                   //check if file exists
			fprintf( stderr, "Error, unable to locate a reference data file\n");
			exit(100);
		}
		for ( int i = 0; i < skip; i++)         //move past header of read files
		{
			fgets( tmpReading, 20, file);
		}
		for ( int i = 0; i < readings; i++ ) //fill the ith element of the jth column
		{
			fgets( tmpReading, 19, file );

			tmp2 = strrchr( tmpReading, '\t' );
			tmp2 = tmp2 + 1;
			reading = atof(tmp2);
			gsl_matrix_set( matrix, i, j-2, reading );
		}
		fclose(file);
		column = gsl_matrix_column( matrix, j-2 );                              //NORMALISE EACH REFERENCE VECOTR.
		normalizeVector( &column.vector );
	}

	return 0;
}


#pragma once

#include <gsl/gsl_math.h>
#include <gsl/gsl_linalg.h>

#include "../vector.h"

namespace gsl_wrapper::bits
{
  class MatrixRow
  {
  public:
    // Consructor
    MatrixRow(gsl_vector_view view);

    // Operators
    operator ::gsl_wrapper::Vector() const;

    auto operator[](const size_t index) -> double &;
    auto operator[](const size_t index) const -> const double &;

  private:
    gsl_vector_view m_view;
  };

  inline MatrixRow::MatrixRow(gsl_vector_view view)
      : m_view{view}
  {
  }

  inline MatrixRow::operator ::gsl_wrapper::Vector() const
  {
    auto space = gsl_vector_calloc(m_view.vector.size);
    gsl_vector_memcpy(space, &m_view.vector);
    return ::gsl_wrapper::Vector(space);
  }

  inline auto MatrixRow::operator[](const size_t index) -> double &
  {
    return *gsl_vector_ptr(&m_view.vector, index);
  }

  inline auto MatrixRow::operator[](const size_t index) const -> const double &
  {
    return *gsl_vector_const_ptr(&m_view.vector, index);
  }

}


/*
MIT License

Copyright (c) 2021 Shunji Uno <shunji_uno@iscpc.jp>

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
#include <stdint.h>
#include <math.h>
#include <float.h>

#ifdef MKL
#include <mkl.h>
#ifdef MKL_SBLAS
#include <mkl_spblas.h>
#endif /* MKL_SBLAS */
#else
#include <cblas.h>
#endif /* MKL */

#ifdef SXAT
#include <sblas.h>
#endif

#define DNRM2(N,X) cblas_dnrm2(N,X,1)
#define DDOT(N,X,Y) cblas_ddot(N,X,1,Y,1)

#define MATRIX_TYPE_INDEX0     (0)
#define MATRIX_TYPE_INDEX1     (1)
#define MATRIX_TYPE_ASYMMETRIC (0<<4)
#define MATRIX_TYPE_SYMMETRIC  (1<<4)
#define MATRIX_TYPE_LOWER      (1<<5)
#define MATRIX_TYPE_UPPER      (0)
#define MATRIX_TYPE_UNIT       (1<<6)
#define MATRIX_TYPE_NON_UNIT   (0)
#define MATRIX_TYPE_CSC        (0<<8)
#define MATRIX_TYPE_CSR        (1<<8)
#define MATRIX_TYPE_ELLPACK    (2<<8)
#define MATRIX_TYPE_JAD        (3<<8)
#define MATRIX_TYPE_DCSC       (8<<8)
#define MATRIX_TYPE_DCSR       (9<<8)
#define MATRIX_TYPE_MASK       (0xf<<8)

#define MATRIX_INDEX_TYPE(A)    (((A)->flags)&0xf)
#define MATRIX_IS_SYMMETRIC(A)  (((A)->flags)&MATRIX_TYPE_SYMMETRIC)
#define MATRIX_IS_LOWER(A)      (((A)->flags)&MATRIX_TYPE_LOWER)
#define MATRIX_IS_UNIT(A)       (((A)->flags)&MATRIX_TYPE_UNIT)
#define MATRIX_IS_CSC(A)        ((((A)->flags)&MATRIX_TYPE_MASK) == MATRIX_TYPE_CSC)
#define MATRIX_IS_CSR(A)        ((((A)->flags)&MATRIX_TYPE_MASK) == MATRIX_TYPE_CSR)

#ifdef __cplusplus
extern "C" {
#endif

typedef struct ellpack_info {
    int32_t nw;
    double* COEF;
    int32_t* ICOL;
} ellpack_info_t;

typedef struct jad_info {
    int32_t* perm;
    int32_t* ptr;
    int32_t* index;
    double* value;
    int32_t maxnzr;
} jad_info_t;

typedef struct Matrix {
    int NROWS;
    int NNZ;
    uint32_t flags;
    int* pointers;
    int* indice;
    double* values;
    ellpack_info_t _ellpack;
    jad_info_t _jad;
    void* info;
#ifdef MKL
    sparse_matrix_t hdl;
#endif
#ifdef SXAT
    sblas_handle_t hdl;
#endif
#ifdef SSL2
    double *w;
    int *iw;
#endif
    int optimized;
} Matrix_t;

/*
 * Generic Matrix operations
 */
void Matrix_init_generic(Matrix_t *A);
void Matrix_free_generic(Matrix_t *A);
int Matrix_optimize_generic(Matrix_t *A);
int Matrix_MV_generic(const Matrix_t *A, const double alpha, const double* x, const double beta, const double* y, double* z);

/*
 * Architecture dependent Matrix operations (prototype for override)
 */
void Matrix_init(Matrix_t *A);
void Matrix_free(Matrix_t *A);
int Matrix_optimize(Matrix_t *A);
int Matrix_MV(const Matrix_t *A, const double alpha, const double* x, const double beta, const double* y, double* z);

/*
 * Architecture independent functions
 */
void Matrix_setMatrixCSR(Matrix_t *A, const int nrows, const int nnz, const int *aptr,
    const int *aind, const double *aval, const uint32_t flags);
Matrix_t* Matrix_duplicate(const Matrix_t* A);
int Matrix_convert_index(Matrix_t* A, int base);
int Matrix_transpose(Matrix_t* A);
int Matrix_extract_symmetric(Matrix_t* A);
int Matrix_create_ellpack(Matrix_t* A);
int Matrix_create_jad(Matrix_t* A);

#ifdef __cplusplus
}
#endif


/*
#
# * The source code in this file is developed independently by NEC Corporation.
#
# # NLCPy License #
#
#     Copyright (c) 2020-2021 NEC Corporation
#     All rights reserved.
#
#     Redistribution and use in source and binary forms, with or without
#     modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright notice,
#       this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright notice,
#       this list of conditions and the following disclaimer in the documentation
#       and/or other materials provided with the distribution.
#     * Neither NEC Corporation nor the names of its contributors may be
#       used to endorse or promote products derived from this software
#       without specific prior written permission.
#
#     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
#     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
#     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
*/

#include <cblas.h>
#include "nlcpy.h"

uint64_t wrapper_cblas_sdot(ve_arguments *args, int32_t *psw)
{
#ifdef _OPENMP
#pragma omp single
#endif /* _OPENMP */
{
    ve_array *x = &(args->binary.x);
    ve_array *y = &(args->binary.y);
    ve_array *z = &(args->binary.z);

    float *px = (float *)x->ve_adr;
    if (px == NULL) {
        px = (float *)nlcpy__get_scalar(x);
        if (px == NULL) {
            return NLCPY_ERROR_MEMORY;
        }
    }
    float *py = (float *)y->ve_adr;
    if (py == NULL) {
        py = (float *)nlcpy__get_scalar(y);
        if (py == NULL) {
            return NLCPY_ERROR_MEMORY;
        }
    }
    float *pz = (float *)z->ve_adr;
    if (pz == NULL) {
       return (uint64_t)NLCPY_ERROR_MEMORY;
    }
    assert(x->ndim <= 1);
    assert(y->ndim <= 1);
    assert(z->ndim <= 1);
    assert(x->size == y->size);

    *pz = cblas_sdot(x->size, px, x->strides[0] / x->itemsize,
                            py, y->strides[0] / y->itemsize);
} /* omp single */
    retrieve_fpe_flags(psw);
    return (uint64_t)NLCPY_ERROR_OK;
}

uint64_t wrapper_cblas_ddot(ve_arguments *args, int32_t *psw)
{
#ifdef _OPENMP
#pragma omp single
#endif /* _OPENMP */
{
    ve_array *x = &(args->binary.x);
    ve_array *y = &(args->binary.y);
    ve_array *z = &(args->binary.z);

    double *px = (double *)x->ve_adr;
    if (px == NULL) {
        px = (double *)nlcpy__get_scalar(x);
        if (px == NULL) {
            return NLCPY_ERROR_MEMORY;
        }
    }
    double *py = (double *)y->ve_adr;
    if (py == NULL) {
        py = (double *)nlcpy__get_scalar(y);
        if (py == NULL) {
            return NLCPY_ERROR_MEMORY;
        }
    }
    double *pz = (double *)z->ve_adr;
    if (pz == NULL) {
       return (uint64_t)NLCPY_ERROR_MEMORY;
    }
    assert(x->ndim <= 1);
    assert(y->ndim <= 1);
    assert(z->ndim <= 1);
    assert(x->size == y->size);

    *pz = cblas_ddot(x->size, px, x->strides[0] / x->itemsize,
                            py, y->strides[0] / y->itemsize);
} /* omp single */
    retrieve_fpe_flags(psw);
    return (uint64_t)NLCPY_ERROR_OK;
}

uint64_t wrapper_cblas_cdotu_sub(ve_arguments *args, int32_t *psw)
{
#ifdef _OPENMP
#pragma omp single
#endif /* _OPENMP */
{
    ve_array *x = &(args->binary.x);
    ve_array *y = &(args->binary.y);
    ve_array *z = &(args->binary.z);

    float _Complex *px = (float _Complex *)x->ve_adr;
    if (px == NULL) {
        px = (float _Complex *)nlcpy__get_scalar(x);
        if (px == NULL) {
            return NLCPY_ERROR_MEMORY;
        }
    }
    float _Complex *py = (float _Complex *)y->ve_adr;
    if (py == NULL) {
        py = (float _Complex *)nlcpy__get_scalar(y);
        if (py == NULL) {
            return NLCPY_ERROR_MEMORY;
        }
    }
    float _Complex *pz = (float _Complex *)z->ve_adr;
    if (pz == NULL) {
       return (uint64_t)NLCPY_ERROR_MEMORY;
    }
    assert(x->ndim <= 1);
    assert(y->ndim <= 1);
    assert(z->ndim <= 1);
    assert(x->size == y->size);

    cblas_cdotu_sub(x->size, px, x->strides[0] / x->itemsize,
                        py, y->strides[0] / y->itemsize, pz);
} /* omp single */
    retrieve_fpe_flags(psw);
    return (uint64_t)NLCPY_ERROR_OK;
}

uint64_t wrapper_cblas_zdotu_sub(ve_arguments *args, int32_t *psw)
{
#ifdef _OPENMP
#pragma omp single
#endif /* _OPENMP */
{
    ve_array *x = &(args->binary.x);
    ve_array *y = &(args->binary.y);
    ve_array *z = &(args->binary.z);

    double _Complex *px = (double _Complex *)x->ve_adr;
    if (px == NULL) {
        px = (double _Complex *)nlcpy__get_scalar(x);
        if (px == NULL) {
            return NLCPY_ERROR_MEMORY;
        }
    }
    double _Complex *py = (double _Complex *)y->ve_adr;
    if (py == NULL) {
        py = (double _Complex *)nlcpy__get_scalar(y);
        if (py == NULL) {
            return NLCPY_ERROR_MEMORY;
        }
    }
    double _Complex *pz = (double _Complex *)z->ve_adr;
    if (pz == NULL) {
       return (uint64_t)NLCPY_ERROR_MEMORY;
    }
    assert(x->ndim <= 1);
    assert(y->ndim <= 1);
    assert(z->ndim <= 1);
    assert(x->size == y->size);

    cblas_zdotu_sub(x->size, px, x->strides[0] / x->itemsize,
                        py, y->strides[0] / y->itemsize, pz);
} /* omp single */
    retrieve_fpe_flags(psw);
    return (uint64_t)NLCPY_ERROR_OK;
}

uint64_t wrapper_cblas_sgemm(ve_arguments *args, int32_t *psw)
{
    const int32_t order = args->gemm.order;
    const int32_t transA = args->gemm.transA;
    const int32_t transB = args->gemm.transB;
    const int32_t m = args->gemm.m;
    const int32_t n = args->gemm.n;
    const int32_t k = args->gemm.k;
    const float alpha = *((float *)nlcpy__get_scalar(&(args->gemm.alpha)));
    float* const a = (float *)args->gemm.a.ve_adr;
    const int32_t lda = args->gemm.lda;
    float* const b = (float *)args->gemm.b.ve_adr;
    const int32_t ldb = args->gemm.ldb;
    const float beta = *((float *)nlcpy__get_scalar(&(args->gemm.beta)));
    float* const c = (float *)args->gemm.c.ve_adr;
    const int32_t ldc = args->gemm.ldc;

    if (a == NULL || b == NULL || c == NULL) {
        return NLCPY_ERROR_MEMORY;
    }

#ifdef _OPENMP
    const int32_t nt = omp_get_num_threads();
    const int32_t it = omp_get_thread_num();
#else
    const int32_t nt = 1;
    const int32_t it = 0;
#endif /* _OPENMP */

    const int32_t m_s = m * it / nt;
    const int32_t m_e = m * (it + 1) / nt;
    const int32_t m_d = m_e - m_s;
    const int32_t n_s = n * it / nt;
    const int32_t n_e = n * (it + 1) / nt;
    const int32_t n_d = n_e - n_s;

    int32_t mode = 1;
    if ( n > nt ) {
        mode = 2;
    }
    int32_t iar, iac, ibr, ibc, icr, icc;
    if (transA == CblasNoTrans ) {
        iar = 1;
        iac = lda;
    } else {
        iar = lda;
        iac = 1;
    }
    if (transB == CblasNoTrans ) {
        ibr = 1;
        ibc = ldb;
    } else {
        ibr = ldb;
        ibc = 1;
    }
    if (order == CblasColMajor ) {
        icr = 1;
        icc = ldc;
    } else {
        icr = ldc;
        icc = 1;
    }

    if (order == CblasColMajor) {
        if ( mode == 1 ) {
            // split 'm'
            cblas_sgemm(order, transA, transB, m_d, n, k, alpha, a + m_s * iar, lda, b, ldb, beta, c + m_s * icr, ldc);
        } else {
            // split 'n'
            cblas_sgemm(order, transA, transB, m, n_d, k, alpha, a, lda, b + n_s * ibc, ldb, beta, c + n_s * icc, ldc);
        }
    } else {
        if ( mode == 1 ) {
            // split 'm'
            cblas_sgemm(order, transA, transB, m_d, n, k, alpha, a + m_s * iac, lda, b, ldb, beta, c + m_s * icr, ldc);
        } else {
            // split 'n'
            cblas_sgemm(order, transA, transB, m, n_d, k, alpha, a, lda, b + n_s * ibr, ldb, beta, c + n_s * icc, ldc);
        }
    }

    retrieve_fpe_flags(psw);
    return (uint64_t)NLCPY_ERROR_OK;
}

uint64_t wrapper_cblas_dgemm(ve_arguments *args, int32_t *psw)
{
    const int32_t order = args->gemm.order;
    const int32_t transA = args->gemm.transA;
    const int32_t transB = args->gemm.transB;
    const int32_t m = args->gemm.m;
    const int32_t n = args->gemm.n;
    const int32_t k = args->gemm.k;
    const double alpha = *((double *)nlcpy__get_scalar(&(args->gemm.alpha)));
    double* const a = (double *)args->gemm.a.ve_adr;
    const int32_t lda = args->gemm.lda;
    double* const b = (double *)args->gemm.b.ve_adr;
    const int32_t ldb = args->gemm.ldb;
    const double beta = *((double *)nlcpy__get_scalar(&(args->gemm.beta)));
    double* const c = (double *)args->gemm.c.ve_adr;
    const int32_t ldc = args->gemm.ldc;

    if (a == NULL || b == NULL || c == NULL) {
        return NLCPY_ERROR_MEMORY;
    }

#ifdef _OPENMP
    const int32_t nt = omp_get_num_threads();
    const int32_t it = omp_get_thread_num();
#else
    const int32_t nt = 1;
    const int32_t it = 0;
#endif /* _OPENMP */

    const int32_t m_s = m * it / nt;
    const int32_t m_e = m * (it + 1) / nt;
    const int32_t m_d = m_e - m_s;
    const int32_t n_s = n * it / nt;
    const int32_t n_e = n * (it + 1) / nt;
    const int32_t n_d = n_e - n_s;

    int32_t mode = 1;
    if ( n > nt ) {
        mode = 2;
    }
    int32_t iar, iac, ibr, ibc, icr, icc;
    if (transA == CblasNoTrans ) {
        iar = 1;
        iac = lda;
    } else {
        iar = lda;
        iac = 1;
    }
    if (transB == CblasNoTrans ) {
        ibr = 1;
        ibc = ldb;
    } else {
        ibr = ldb;
        ibc = 1;
    }
    if (order == CblasColMajor ) {
        icr = 1;
        icc = ldc;
    } else {
        icr = ldc;
        icc = 1;
    }

    if (order == CblasColMajor) {
        if ( mode == 1 ) {
            // split 'm'
            cblas_dgemm(order, transA, transB, m_d, n, k, alpha, a + m_s * iar, lda, b, ldb, beta, c + m_s * icr, ldc);
        } else {
            // split 'n'
            cblas_dgemm(order, transA, transB, m, n_d, k, alpha, a, lda, b + n_s * ibc, ldb, beta, c + n_s * icc, ldc);
        }
    } else {
        if ( mode == 1 ) {
            // split 'm'
            cblas_dgemm(order, transA, transB, m_d, n, k, alpha, a + m_s * iac, lda, b, ldb, beta, c + m_s * icr, ldc);
        } else {
            // split 'n'
            cblas_dgemm(order, transA, transB, m, n_d, k, alpha, a, lda, b + n_s * ibr, ldb, beta, c + n_s * icc, ldc);
        }
    }

    retrieve_fpe_flags(psw);
    return (uint64_t)NLCPY_ERROR_OK;
}

uint64_t wrapper_cblas_cgemm(ve_arguments *args, int32_t *psw)
{
    const int32_t order = args->gemm.order;
    const int32_t transA = args->gemm.transA;
    const int32_t transB = args->gemm.transB;
    const int32_t m = args->gemm.m;
    const int32_t n = args->gemm.n;
    const int32_t k = args->gemm.k;
    const void *alpha = (void *)nlcpy__get_scalar(&(args->gemm.alpha));
    if (alpha == NULL) return (uint64_t)NLCPY_ERROR_MEMORY;
    float  _Complex* const a = (float  _Complex *)args->gemm.a.ve_adr;
    const int32_t lda = args->gemm.lda;
    float  _Complex* const b = (float  _Complex *)args->gemm.b.ve_adr;
    const int32_t ldb = args->gemm.ldb;
    const void *beta = (void *)nlcpy__get_scalar(&(args->gemm.beta));
    if (beta == NULL) return (uint64_t)NLCPY_ERROR_MEMORY;
    float  _Complex* const c = (float  _Complex *)args->gemm.c.ve_adr;
    const int32_t ldc = args->gemm.ldc;

    if (a == NULL || b == NULL || c == NULL) {
        return NLCPY_ERROR_MEMORY;
    }

#ifdef _OPENMP
    const int32_t nt = omp_get_num_threads();
    const int32_t it = omp_get_thread_num();
#else
    const int32_t nt = 1;
    const int32_t it = 0;
#endif /* _OPENMP */

    const int32_t m_s = m * it / nt;
    const int32_t m_e = m * (it + 1) / nt;
    const int32_t m_d = m_e - m_s;
    const int32_t n_s = n * it / nt;
    const int32_t n_e = n * (it + 1) / nt;
    const int32_t n_d = n_e - n_s;

    int32_t mode = 1;
    if ( n > nt ) {
        mode = 2;
    }
    int32_t iar, iac, ibr, ibc, icr, icc;
    if (transA == CblasNoTrans ) {
        iar = 1;
        iac = lda;
    } else {
        iar = lda;
        iac = 1;
    }
    if (transB == CblasNoTrans ) {
        ibr = 1;
        ibc = ldb;
    } else {
        ibr = ldb;
        ibc = 1;
    }
    if (order == CblasColMajor ) {
        icr = 1;
        icc = ldc;
    } else {
        icr = ldc;
        icc = 1;
    }

    if (order == CblasColMajor) {
        if ( mode == 1 ) {
            // split 'm'
            cblas_cgemm(order, transA, transB, m_d, n, k, alpha, a + m_s * iar, lda, b, ldb, beta, c + m_s * icr, ldc);
        } else {
            // split 'n'
            cblas_cgemm(order, transA, transB, m, n_d, k, alpha, a, lda, b + n_s * ibc, ldb, beta, c + n_s * icc, ldc);
        }
    } else {
        if ( mode == 1 ) {
            // split 'm'
            cblas_cgemm(order, transA, transB, m_d, n, k, alpha, a + m_s * iac, lda, b, ldb, beta, c + m_s * icr, ldc);
        } else {
            // split 'n'
            cblas_cgemm(order, transA, transB, m, n_d, k, alpha, a, lda, b + n_s * ibr, ldb, beta, c + n_s * icc, ldc);
        }
    }

    retrieve_fpe_flags(psw);
    return (uint64_t)NLCPY_ERROR_OK;
}

uint64_t wrapper_cblas_zgemm(ve_arguments *args, int32_t *psw)
{
    const int32_t order = args->gemm.order;
    const int32_t transA = args->gemm.transA;
    const int32_t transB = args->gemm.transB;
    const int32_t m = args->gemm.m;
    const int32_t n = args->gemm.n;
    const int32_t k = args->gemm.k;
    const void *alpha = (void *)nlcpy__get_scalar(&(args->gemm.alpha));
    if (alpha == NULL) return (uint64_t)NLCPY_ERROR_MEMORY;
    double _Complex* const a = (double _Complex *)args->gemm.a.ve_adr;
    const int32_t lda = args->gemm.lda;
    double _Complex* const b = (double _Complex *)args->gemm.b.ve_adr;
    const int32_t ldb = args->gemm.ldb;
    const void *beta = (void *)nlcpy__get_scalar(&(args->gemm.beta));
    if (beta == NULL) return (uint64_t)NLCPY_ERROR_MEMORY;
    double _Complex* const c = (double _Complex *)args->gemm.c.ve_adr;
    const int32_t ldc = args->gemm.ldc;

    if (a == NULL || b == NULL || c == NULL) {
        return NLCPY_ERROR_MEMORY;
    }

#ifdef _OPENMP
    const int32_t nt = omp_get_num_threads();
    const int32_t it = omp_get_thread_num();
#else
    const int32_t nt = 1;
    const int32_t it = 0;
#endif /* _OPENMP */

    const int32_t m_s = m * it / nt;
    const int32_t m_e = m * (it + 1) / nt;
    const int32_t m_d = m_e - m_s;
    const int32_t n_s = n * it / nt;
    const int32_t n_e = n * (it + 1) / nt;
    const int32_t n_d = n_e - n_s;

    int32_t mode = 1;
    if ( n > nt ) {
        mode = 2;
    }
    int32_t iar, iac, ibr, ibc, icr, icc;
    if (transA == CblasNoTrans ) {
        iar = 1;
        iac = lda;
    } else {
        iar = lda;
        iac = 1;
    }
    if (transB == CblasNoTrans ) {
        ibr = 1;
        ibc = ldb;
    } else {
        ibr = ldb;
        ibc = 1;
    }
    if (order == CblasColMajor ) {
        icr = 1;
        icc = ldc;
    } else {
        icr = ldc;
        icc = 1;
    }

    if (order == CblasColMajor) {
        if ( mode == 1 ) {
            // split 'm'
            cblas_zgemm(order, transA, transB, m_d, n, k, alpha, a + m_s * iar, lda, b, ldb, beta, c + m_s * icr, ldc);
        } else {
            // split 'n'
            cblas_zgemm(order, transA, transB, m, n_d, k, alpha, a, lda, b + n_s * ibc, ldb, beta, c + n_s * icc, ldc);
        }
    } else {
        if ( mode == 1 ) {
            // split 'm'
            cblas_zgemm(order, transA, transB, m_d, n, k, alpha, a + m_s * iac, lda, b, ldb, beta, c + m_s * icr, ldc);
        } else {
            // split 'n'
            cblas_zgemm(order, transA, transB, m, n_d, k, alpha, a, lda, b + n_s * ibr, ldb, beta, c + n_s * icc, ldc);
        }
    }

    retrieve_fpe_flags(psw);
    return (uint64_t)NLCPY_ERROR_OK;
}


/**
 * Author : Pierre Schnizer
 * Date: January 2003
 */
#ifndef PyGSL_FUNCTION_HELPERS_H
#define  PyGSL_FUNCTION_HELPERS_H 1
/*   -------------------------------------------------------------------------
     See gsl_functions_reference.txt for a compilation of the different
     callbacks found in GSL.

     Todo:
           Perhaps split the file in general helpers and
	   special helpers ????
	   Make all helpers reporting error via
           PyGSL_set_error_string_for_callback

	   List all functions in these file in a header
   ------------------------------------------------------------------------- */
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_roots.h>
#include <gsl/gsl_min.h>
#include <gsl/gsl_multiroots.h>
#include <gsl/gsl_multimin.h>
#include <gsl/gsl_multifit_nlin.h>
#include <gsl/gsl_monte.h>
#include <pygsl/utils.h>
#include <pygsl/intern.h>
#include <pygsl/error_helpers.h>
#include <pygsl/general_helpers.h>
#include <pygsl/block_helpers.h>
#include <pygsl/function_helpers.h>
#include <math.h>
#include <setjmp.h>

/* -------------------------------------------------------------------------
   Helper Structs

   *_func_name : a descriptive message for the internal function used
                 when reporting an error to the user.
   buffer_is_set : It depends on the user that she/he uses the
                   *BUFFER in the function interfaces. So this variable is
                  set to zero when the struct is generated. I hope this
		  will stop the wrapper trying to jump to NIRVANA.
   ------------------------------------------------------------------------- */
/*
 *  11 December 2003
 *  I return the flag that GSL returned using the flag argument to longjmp.
 *  This flag must be different from zero to be useful. GSL uses
 *  0 (== GSL_SUCCESS) to indicate it. I check for that here to see if it is
 *  always like that.
 */
#if GSL_SUCCESS != 0
#error "The function helpers use longjmp. GSL_SUCCESS must be zero. Pygsl Design error"
#endif
typedef struct {
     PyObject *function;
     PyObject *arguments;
     const char * c_func_name;
     jmp_buf buffer;
     int buffer_is_set;
} callback_function_params;

typedef struct {
     PyObject *f;
     PyObject *df;
     PyObject *fdf;
     PyObject *arguments;
     const char * c_f_func_name;
     const char * c_df_func_name;
     const char * c_fdf_func_name;
     jmp_buf buffer;
     int buffer_is_set;
} callback_function_params_fdf;

/* -------------------------------------------------------------------------
   Copy PyArray to gsl vector, gslarray and vice versa
   Are these functions ever needed by pure vector or matrix conversion?
   If so these functions should go into gsl_block_helpers.i
   ------------------------------------------------------------------------- */

/* 1. A_n O -> A_p  */
int
PyGSL_function_wrap_Op_On(const gsl_vector * x, gsl_vector *f, PyObject *callback,
			  PyObject * arguments, int n, int p, const char *c_func_name);
/* 2. A_n O -> A_n_p */
int
PyGSL_function_wrap_Op_Opn(const gsl_vector * x, gsl_matrix *f, PyObject *callback,
			   PyObject *arguments, int n, int p, const char * c_func_name);

/* 3   dO -> d      gsl_function     */
/* 3.1 dO -> d d    gsl_function_fdf */
PyGSL_API_EXTERN int
PyGSL_function_wrap_helper(double x, double * result, double *result2,
			   PyObject *callback, PyObject *arguments,
			   const char *c_func_name);

/*
 * Pass a NULL pointer for result 2, if not needed.
 */
/* 4. A_n O   ->  d (A_n) */
 int
PyGSL_function_wrap_On_O(const gsl_vector * x, PyObject *callback,
			PyObject *arguments, double *result1,
			 gsl_vector *result2, int n, const char * c_func_name);

/* 5. A_n O -> A_n A_n_p */
 int
PyGSL_function_wrap_Op_On_Opn(const gsl_vector * x, gsl_vector *f1,
			      gsl_matrix *f2, PyObject *callback,
			      PyObject *arguments, int n, int p,
			      const char * c_func_name);

/* -------------------------------------------------------------------------
      Register Python Call backs

      Generic Helper Functions
   ------------------------------------------------------------------------ */
/* Callbacks using one function */
callback_function_params *
PyGSL_convert_to_generic_function(PyObject *object, int *size, int *size2, const char * c_func_name);
/* Callbacks using 3  functions */
callback_function_params_fdf *
PyGSL_convert_to_generic_function_fdf(PyObject *object, int *size, int *size2,
				      const char * c_f_func_name, const char * c_df_func_name, const char * c_fdf_func_name);
void
PyGSL_params_free(callback_function_params *p);
void
PyGSL_params_free_fdf(callback_function_params_fdf *p);

double
PyGSL_function_wrap(double x, void * params);
double
PyGSL_function_wrap_f(double x, void * params);
double
PyGSL_function_wrap_df(double x, void * params);
void
PyGSL_function_wrap_fdf(double x,  void * params, double *f, double * fdf);
gsl_function *
PyGSL_convert_to_gsl_function(PyObject * object);
gsl_function_fdf *
PyGSL_convert_to_gsl_function_fdf(PyObject * object);

/* Specialised functions ... should they go to callbacks? */

int
PyGSL_multiroot_function_wrap(const gsl_vector *x, void *params, gsl_vector *f);
int
PyGSL_multiroot_function_wrap_f(const gsl_vector *x, void *params, gsl_vector *f);
int
PyGSL_multiroot_function_wrap_df(const gsl_vector *x, void *params, gsl_matrix *J);
int
PyGSL_multiroot_function_wrap_fdf(const gsl_vector *x, void *params, gsl_vector *f, gsl_matrix *J);
gsl_multiroot_function *
PyGSL_convert_to_gsl_multiroot_function(PyObject * object);
gsl_multiroot_function_fdf *
PyGSL_convert_to_gsl_multiroot_function_fdf(PyObject * object);

double
PyGSL_multimin_function_wrap(const gsl_vector *x, void *params);
double
PyGSL_multimin_function_wrap_f(const gsl_vector *x, void *params);
void
PyGSL_multimin_function_wrap_df(const gsl_vector *x, void *params, gsl_vector *g);
void
PyGSL_multimin_function_wrap_fdf(const gsl_vector *x, void *params, double *f, gsl_vector *g);
gsl_multimin_function *
PyGSL_convert_to_gsl_multimin_function(PyObject * object);
gsl_multimin_function_fdf *
PyGSL_convert_to_gsl_multimin_function_fdf(PyObject * object);

int
PyGSL_multifit_function_wrap(const gsl_vector *x, void *params, gsl_vector *f);
int
PyGSL_multifit_function_wrap_f(const gsl_vector *x, void *params, gsl_vector *f);
int
PyGSL_multifit_function_wrap_df(const gsl_vector *x, void *params, gsl_matrix *df);
int
PyGSL_multifit_function_wrap_fdf(const gsl_vector *x, void *params, gsl_vector *f, gsl_matrix *df);
gsl_multifit_function *
PyGSL_convert_to_gsl_multifit_function(PyObject * object);
gsl_multifit_function_fdf *
PyGSL_convert_to_gsl_multifit_function_fdf(PyObject * object);
gsl_monte_function *
PyGSL_convert_to_gsl_monte_function(PyObject * object);

/* gsl_function */
extern char * pygsl_gsl_function;
extern char * pygsl_gsl_f_function;
extern char * pygsl_gsl_df_function;
extern char * pygsl_gsl_fdf_function;
/* gsl_multifit */
extern char * pygsl_multifit_function;
extern char * pygsl_multifit_f_function;
extern char * pygsl_multifit_df_function;
extern char * pygsl_multifit_fdf_function;

extern char * pygsl_multimin_function;
extern char * pygsl_multimin_f_function;
extern char * pygsl_multimin_df_function;
extern char * pygsl_multimin_fdf_function;
/* gsl_multiroot */
extern char * pygsl_multiroot_function;
extern char * pygsl_multiroot_f_function;
extern char * pygsl_multiroot_df_function;
extern char * pygsl_multiroot_fdf_function;

/* monte */
extern char * pygsl_monte_function;
#ifndef _PyGSL_API_MODULE
#define PyGSL_function_wrap_helper \
(*(int (*) (double, double *, double *, PyObject *, PyObject *, const char *)) PyGSL_API[PyGSL_function_wrap_helper_NUM])
#endif  /* _PyGSL_API_MODULE */
#endif  /* PyGSL_FUNCTION_HELPERS_H */


#ifndef MATHLAB_H
#define MATHLAB_H

#define USE_MKL 0
#define USE_OPENBLAS 1

#if USE_MKL
#include <mkl.h>

#elif USE_OPENBLAS
#include <cblas.h>

#endif

#endif


////////////////////////////////////////////////////////////
//
// Copyright (c) 2018 Jan Filipowicz, Filip Turobos
//
// 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.
//
////////////////////////////////////////////////////////////

#ifndef GENETIC_ALGORITHM_LIBRARY_ELITIST_SELECTION_H
#define GENETIC_ALGORITHM_LIBRARY_ELITIST_SELECTION_H

#include <algorithm>
#include <cstddef>
#include <functional>
#include <vector>
#include <gsl/gsl_assert>

template<class Compare = std::less<>>
class elitist_selection {
public:
	explicit elitist_selection(const Compare& comp = Compare()) noexcept(noexcept(Compare(comp)));
	template<class Specimen>
	void operator()(std::vector<Specimen>& specimens, std::size_t n) const;
private:
	Compare comparator;
};

template<class Compare>
inline elitist_selection<Compare>::elitist_selection(const Compare& comp) noexcept(noexcept(Compare(comp)))
	: comparator(comp) {}

template<class Compare>
template<class Specimen>
inline void elitist_selection<Compare>::operator()(std::vector<Specimen>& specimens, std::size_t n) const {
	Expects(specimens.size() >= n);
	std::nth_element(specimens.begin(), specimens.begin() + n, specimens.end(), [this](const Specimen& lhs, const Specimen& rhs) {
		return comparator(rhs.rating(), lhs.rating());
	});
	specimens.resize(n);
}

#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__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif
#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_inline.h>

#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;

GSL_FUN 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_FUN gsl_rng *gsl_rng_alloc (const gsl_rng_type * T);
GSL_FUN int gsl_rng_memcpy (gsl_rng * dest, const gsl_rng * src);
GSL_FUN gsl_rng *gsl_rng_clone (const gsl_rng * r);

GSL_FUN void gsl_rng_free (gsl_rng * r);

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

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

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

GSL_FUN void gsl_rng_print_state (const gsl_rng * r);

GSL_FUN const gsl_rng_type * gsl_rng_env_setup (void);

GSL_FUN INLINE_DECL unsigned long int gsl_rng_get (const gsl_rng * r);
GSL_FUN INLINE_DECL double gsl_rng_uniform (const gsl_rng * r);
GSL_FUN INLINE_DECL double gsl_rng_uniform_pos (const gsl_rng * r);
GSL_FUN 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__ */


// Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
// reserved. See files LICENSE and NOTICE for details.
//
// This file is part of CEED, a collection of benchmarks, miniapps, software
// libraries and APIs for efficient high-order finite element and spectral
// element discretizations for exascale applications. For more information and
// source code availability see http://github.com/ceed.
//
// The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
// a collaborative effort of two U.S. Department of Energy organizations (Office
// of Science and the National Nuclear Security Administration) responsible for
// the planning and preparation of a capable exascale ecosystem, including
// software, applications, hardware, advanced system engineering and early
// testbed platforms, in support of the nation's exascale computing imperative.

#ifndef setuparea_h
#define setuparea_h

#include <ceed.h>
#include <petsc.h>
#include <petscdmplex.h>
#include <petscfe.h>
#include <stdbool.h>
#include <string.h>
#include "qfunctions/area/areacube.h"
#include "qfunctions/area/areasphere.h"

#if PETSC_VERSION_LT(3,14,0)
#  define DMPlexGetClosureIndices(a,b,c,d,e,f,g,h,i) DMPlexGetClosureIndices(a,b,c,d,f,g,i)
#  define DMPlexRestoreClosureIndices(a,b,c,d,e,f,g,h,i) DMPlexRestoreClosureIndices(a,b,c,d,f,g,i)
#endif

#if PETSC_VERSION_LT(3,14,0)
#  define DMPlexCreateSphereMesh(a,b,c,d,e) DMPlexCreateSphereMesh(a,b,c,e)
#endif

// -----------------------------------------------------------------------------
// PETSc Operator Structs
// -----------------------------------------------------------------------------

// Data for PETSc
typedef struct User_ *User;
struct User_ {
  MPI_Comm comm;
  DM dm;
  Vec Xloc, Yloc, diag;
  CeedVector xceed, yceed;
  CeedOperator op;
  Ceed ceed;
};

// -----------------------------------------------------------------------------
// libCEED Data Struct
// -----------------------------------------------------------------------------

// libCEED data struct for level
typedef struct CeedData_ *CeedData;
struct CeedData_ {
  Ceed ceed;
  CeedBasis basisx, basisu;
  CeedElemRestriction Erestrictx, Erestrictu, Erestrictqdi;
  CeedQFunction qf_apply;
  CeedOperator op_apply, op_restrict, op_interp;
  CeedVector qdata, uceed, vceed;
};

// -----------------------------------------------------------------------------
// Problem Option Data
// -----------------------------------------------------------------------------

// Problem options
typedef enum {
  CUBE = 0, SPHERE = 1
} problemType;
static const char *const problemTypes[] = {"cube", "sphere",
                                           "problemType", "AREA", NULL
                                          };

// Problem specific data
typedef struct {
  CeedInt ncompu, ncompx, qdatasize, qextra, topodim;
  CeedQFunctionUser setupgeo, apply;
  const char *setupgeofname, *applyfname;
  CeedEvalMode inmode, outmode;
  CeedQuadMode qmode;
} problemData;

static problemData problemOptions[6] = {
  [CUBE] = {
    .ncompx = 3,
    .ncompu = 1,
    .topodim = 2,
    .qdatasize = 1,
    .qextra = 1,
    .setupgeo = SetupMassGeoCube,
    .apply = Mass,
    .setupgeofname = SetupMassGeoCube_loc,
    .applyfname = Mass_loc,
    .inmode = CEED_EVAL_INTERP,
    .outmode = CEED_EVAL_INTERP,
    .qmode = CEED_GAUSS
  },
  [SPHERE] = {
    .ncompx = 3,
    .ncompu = 1,
    .topodim = 2,
    .qdatasize = 1,
    .qextra = 1,
    .setupgeo = SetupMassGeoSphere,
    .apply = Mass,
    .setupgeofname = SetupMassGeoSphere_loc,
    .applyfname = Mass_loc,
    .inmode = CEED_EVAL_INTERP,
    .outmode = CEED_EVAL_INTERP,
    .qmode = CEED_GAUSS
  }
};

// -----------------------------------------------------------------------------
// PETSc sphere auxiliary functions
// -----------------------------------------------------------------------------

// Utility function taken from petsc/src/dm/impls/plex/examples/tutorials/ex7.c
static PetscErrorCode ProjectToUnitSphere(DM dm) {
  Vec            coordinates;
  PetscScalar   *coords;
  PetscInt       Nv, v, dim, d;
  PetscErrorCode ierr;

  PetscFunctionBeginUser;
  ierr = DMGetCoordinatesLocal(dm, &coordinates); CHKERRQ(ierr);
  ierr = VecGetLocalSize(coordinates, &Nv); CHKERRQ(ierr);
  ierr = VecGetBlockSize(coordinates, &dim); CHKERRQ(ierr);
  Nv  /= dim;
  ierr = VecGetArray(coordinates, &coords); CHKERRQ(ierr);
  for (v = 0; v < Nv; ++v) {
    PetscReal r = 0.0;

    for (d = 0; d < dim; ++d) r += PetscSqr(PetscRealPart(coords[v*dim+d]));
    r = PetscSqrtReal(r);
    for (d = 0; d < dim; ++d) coords[v*dim+d] /= r;
  }
  ierr = VecRestoreArray(coordinates, &coords); CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

// -----------------------------------------------------------------------------
// PETSc Finite Element space setup
// -----------------------------------------------------------------------------

// Create FE by degree
static int PetscFECreateByDegree(DM dm, PetscInt dim, PetscInt Nc,
                                 PetscBool isSimplex, const char prefix[],
                                 PetscInt order, PetscFE *fem) {
  PetscQuadrature q, fq;
  DM              K;
  PetscSpace      P;
  PetscDualSpace  Q;
  PetscInt        quadPointsPerEdge;
  PetscBool       tensor = isSimplex ? PETSC_FALSE : PETSC_TRUE;
  PetscErrorCode  ierr;

  PetscFunctionBeginUser;
  /* Create space */
  ierr = PetscSpaceCreate(PetscObjectComm((PetscObject) dm), &P); CHKERRQ(ierr);
  ierr = PetscObjectSetOptionsPrefix((PetscObject) P, prefix); CHKERRQ(ierr);
  ierr = PetscSpacePolynomialSetTensor(P, tensor); CHKERRQ(ierr);
  ierr = PetscSpaceSetFromOptions(P); CHKERRQ(ierr);
  ierr = PetscSpaceSetNumComponents(P, Nc); CHKERRQ(ierr);
  ierr = PetscSpaceSetNumVariables(P, dim); CHKERRQ(ierr);
  ierr = PetscSpaceSetDegree(P, order, order); CHKERRQ(ierr);
  ierr = PetscSpaceSetUp(P); CHKERRQ(ierr);
  ierr = PetscSpacePolynomialGetTensor(P, &tensor); CHKERRQ(ierr);
  /* Create dual space */
  ierr = PetscDualSpaceCreate(PetscObjectComm((PetscObject) dm), &Q);
  CHKERRQ(ierr);
  ierr = PetscDualSpaceSetType(Q,PETSCDUALSPACELAGRANGE); CHKERRQ(ierr);
  ierr = PetscObjectSetOptionsPrefix((PetscObject) Q, prefix); CHKERRQ(ierr);
  ierr = PetscDualSpaceCreateReferenceCell(Q, dim, isSimplex, &K); CHKERRQ(ierr);
  ierr = PetscDualSpaceSetDM(Q, K); CHKERRQ(ierr);
  ierr = DMDestroy(&K); CHKERRQ(ierr);
  ierr = PetscDualSpaceSetNumComponents(Q, Nc); CHKERRQ(ierr);
  ierr = PetscDualSpaceSetOrder(Q, order); CHKERRQ(ierr);
  ierr = PetscDualSpaceLagrangeSetTensor(Q, tensor); CHKERRQ(ierr);
  ierr = PetscDualSpaceSetFromOptions(Q); CHKERRQ(ierr);
  ierr = PetscDualSpaceSetUp(Q); CHKERRQ(ierr);
  /* Create element */
  ierr = PetscFECreate(PetscObjectComm((PetscObject) dm), fem); CHKERRQ(ierr);
  ierr = PetscObjectSetOptionsPrefix((PetscObject) *fem, prefix); CHKERRQ(ierr);
  ierr = PetscFESetFromOptions(*fem); CHKERRQ(ierr);
  ierr = PetscFESetBasisSpace(*fem, P); CHKERRQ(ierr);
  ierr = PetscFESetDualSpace(*fem, Q); CHKERRQ(ierr);
  ierr = PetscFESetNumComponents(*fem, Nc); CHKERRQ(ierr);
  ierr = PetscFESetUp(*fem); CHKERRQ(ierr);
  ierr = PetscSpaceDestroy(&P); CHKERRQ(ierr);
  ierr = PetscDualSpaceDestroy(&Q); CHKERRQ(ierr);
  /* Create quadrature */
  quadPointsPerEdge = PetscMax(order + 1,1);
  if (isSimplex) {
    ierr = PetscDTStroudConicalQuadrature(dim,   1, quadPointsPerEdge, -1.0, 1.0,
                                          &q); CHKERRQ(ierr);
    ierr = PetscDTStroudConicalQuadrature(dim-1, 1, quadPointsPerEdge, -1.0, 1.0,
                                          &fq); CHKERRQ(ierr);
  } else {
    ierr = PetscDTGaussTensorQuadrature(dim,   1, quadPointsPerEdge, -1.0, 1.0,
                                        &q); CHKERRQ(ierr);
    ierr = PetscDTGaussTensorQuadrature(dim-1, 1, quadPointsPerEdge, -1.0, 1.0,
                                        &fq); CHKERRQ(ierr);
  }
  ierr = PetscFESetQuadrature(*fem, q); CHKERRQ(ierr);
  ierr = PetscFESetFaceQuadrature(*fem, fq); CHKERRQ(ierr);
  ierr = PetscQuadratureDestroy(&q); CHKERRQ(ierr);
  ierr = PetscQuadratureDestroy(&fq); CHKERRQ(ierr);

  PetscFunctionReturn(0);
}

// -----------------------------------------------------------------------------
// PETSc Setup for Level
// -----------------------------------------------------------------------------

// This function sets up a DM for a given degree
static int SetupDMByDegree(DM dm, PetscInt degree, PetscInt ncompu,
                           PetscInt dim) {
  PetscInt ierr;
  PetscFE fe;

  PetscFunctionBeginUser;

  // Setup FE
  ierr = PetscFECreateByDegree(dm, dim, ncompu, PETSC_FALSE, NULL, degree, &fe);
  CHKERRQ(ierr);
  ierr = DMAddField(dm, NULL, (PetscObject)fe); CHKERRQ(ierr);

  // Setup DM
  ierr = DMCreateDS(dm); CHKERRQ(ierr);
  ierr = DMPlexSetClosurePermutationTensor(dm, PETSC_DETERMINE, NULL);
  CHKERRQ(ierr);
  ierr = PetscFEDestroy(&fe); CHKERRQ(ierr);

  PetscFunctionReturn(0);
}

// -----------------------------------------------------------------------------
// libCEED Setup for Level
// -----------------------------------------------------------------------------

// Destroy libCEED operator objects
static PetscErrorCode CeedDataDestroy(CeedData data) {
  PetscInt ierr;

  CeedVectorDestroy(&data->qdata);
  CeedVectorDestroy(&data->uceed);
  CeedVectorDestroy(&data->vceed);
  CeedBasisDestroy(&data->basisx);
  CeedBasisDestroy(&data->basisu);
  CeedElemRestrictionDestroy(&data->Erestrictu);
  CeedElemRestrictionDestroy(&data->Erestrictx);
  CeedElemRestrictionDestroy(&data->Erestrictqdi);
  CeedQFunctionDestroy(&data->qf_apply);
  CeedOperatorDestroy(&data->op_apply);
  ierr = PetscFree(data); CHKERRQ(ierr);

  PetscFunctionReturn(0);
}

// Auxiliary function to define CEED restrictions from DMPlex data
static int CreateRestrictionPlex(Ceed ceed, CeedInt P, CeedInt ncomp,
                                 CeedElemRestriction *Erestrict, DM dm) {
  PetscInt ierr;
  PetscInt c, cStart, cEnd, nelem, nnodes, *erestrict, eoffset;
  PetscSection section;
  Vec Uloc;

  PetscFunctionBegin;

  // Get Nelem
  ierr = DMGetSection(dm, &section); CHKERRQ(ierr);
  ierr = DMPlexGetHeightStratum(dm, 0, &cStart,& cEnd); CHKERRQ(ierr);
  nelem = cEnd - cStart;

  // Get indices
  ierr = PetscMalloc1(nelem*P*P, &erestrict); CHKERRQ(ierr);
  for (c=cStart, eoffset = 0; c<cEnd; c++) {
    PetscInt numindices, *indices, i;
    ierr = DMPlexGetClosureIndices(dm, section, section, c, PETSC_TRUE,
                                   &numindices, &indices, NULL, NULL);
    CHKERRQ(ierr);
    for (i=0; i<numindices; i+=ncomp) {
      for (PetscInt j=0; j<ncomp; j++) {
        if (indices[i+j] != indices[i] + (PetscInt)(copysign(j, indices[i])))
          SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP,
                   "Cell %D closure indices not interlaced", c);
      }
      // NO BC on closed surfaces
      PetscInt loc = indices[i];
      erestrict[eoffset++] = loc;
    }
    ierr = DMPlexRestoreClosureIndices(dm, section, section, c, PETSC_TRUE,
                                       &numindices, &indices, NULL, NULL);
    CHKERRQ(ierr);
  }

  // Setup CEED restriction
  ierr = DMGetLocalVector(dm, &Uloc); CHKERRQ(ierr);
  ierr = VecGetLocalSize(Uloc, &nnodes); CHKERRQ(ierr);

  ierr = DMRestoreLocalVector(dm, &Uloc); CHKERRQ(ierr);
  CeedElemRestrictionCreate(ceed, nelem, P*P, ncomp, 1, nnodes,
                            CEED_MEM_HOST, CEED_COPY_VALUES, erestrict,
                            Erestrict);
  ierr = PetscFree(erestrict); CHKERRQ(ierr);

  PetscFunctionReturn(0);
}

// Set up libCEED for a given degree
static int SetupLibceedByDegree(DM dm, Ceed ceed, CeedInt degree,
                                CeedInt topodim, CeedInt qextra,
                                PetscInt ncompx, PetscInt ncompu,
                                PetscInt xlsize, problemType problemChoice,
                                CeedData data) {
  int ierr;
  DM dmcoord;
  Vec coords;
  const PetscScalar *coordArray;
  CeedBasis basisx, basisu;
  CeedElemRestriction Erestrictx, Erestrictu, Erestrictqdi;
  CeedQFunction qf_setupgeo, qf_apply;
  CeedOperator op_setupgeo, op_apply;
  CeedVector xcoord, qdata, uceed, vceed;
  CeedInt P, Q, cStart, cEnd, nelem,
          qdatasize = problemOptions[problemChoice].qdatasize;

  // CEED bases
  P = degree + 1;
  Q = P + qextra;
  CeedBasisCreateTensorH1Lagrange(ceed, topodim, ncompu, P, Q,
                                  problemOptions[problemChoice].qmode, &basisu);
  CeedBasisCreateTensorH1Lagrange(ceed, topodim, ncompx, 2, Q,
                                  problemOptions[problemChoice].qmode, &basisx);

  // CEED restrictions
  ierr = DMGetCoordinateDM(dm, &dmcoord); CHKERRQ(ierr);
  ierr = DMPlexSetClosurePermutationTensor(dmcoord, PETSC_DETERMINE, NULL);
  CHKERRQ(ierr);

  ierr = CreateRestrictionPlex(ceed, 2, ncompx, &Erestrictx, dmcoord);
  CHKERRQ(ierr);
  ierr = CreateRestrictionPlex(ceed, P, ncompu, &Erestrictu, dm); CHKERRQ(ierr);

  ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd); CHKERRQ(ierr);
  nelem = cEnd - cStart;

  CeedElemRestrictionCreateStrided(ceed, nelem, Q*Q, qdatasize,
                                   qdatasize*nelem*Q*Q,
                                   CEED_STRIDES_BACKEND, &Erestrictqdi);

  // Element coordinates
  ierr = DMGetCoordinatesLocal(dm, &coords); CHKERRQ(ierr);
  ierr = VecGetArrayRead(coords, &coordArray); CHKERRQ(ierr);

  CeedElemRestrictionCreateVector(Erestrictx, &xcoord, NULL);
  CeedVectorSetArray(xcoord, CEED_MEM_HOST, CEED_COPY_VALUES,
                     (PetscScalar *)coordArray);
  ierr = VecRestoreArrayRead(coords, &coordArray);

  // Create the vectors that will be needed in setup and apply
  CeedInt nqpts;
  CeedBasisGetNumQuadraturePoints(basisu, &nqpts);
  CeedVectorCreate(ceed, qdatasize*nelem*nqpts, &qdata);
  CeedVectorCreate(ceed, xlsize, &uceed);
  CeedVectorCreate(ceed, xlsize, &vceed);

  // Create the Q-function that builds the operator (i.e. computes its
  // quadrature data) and set its context data
  CeedQFunctionCreateInterior(ceed, 1, problemOptions[problemChoice].setupgeo,
                              problemOptions[problemChoice].setupgeofname,
                              &qf_setupgeo);
  CeedQFunctionAddInput(qf_setupgeo, "x", ncompx, CEED_EVAL_INTERP);
  CeedQFunctionAddInput(qf_setupgeo, "dx", ncompx*topodim, CEED_EVAL_GRAD);
  CeedQFunctionAddInput(qf_setupgeo, "weight", 1, CEED_EVAL_WEIGHT);
  CeedQFunctionAddOutput(qf_setupgeo, "qdata", qdatasize, CEED_EVAL_NONE);

  // Set up the mass operator
  CeedQFunctionCreateInterior(ceed, 1, problemOptions[problemChoice].apply,
                              problemOptions[problemChoice].applyfname,
                              &qf_apply);
  CeedQFunctionAddInput(qf_apply, "u", ncompu,
                        problemOptions[problemChoice].inmode);
  CeedQFunctionAddInput(qf_apply, "qdata", qdatasize, CEED_EVAL_NONE);
  CeedQFunctionAddOutput(qf_apply, "v", ncompu,
                         problemOptions[problemChoice].outmode);

  // Create the operator that builds the quadrature data for the operator
  CeedOperatorCreate(ceed, qf_setupgeo, NULL, NULL, &op_setupgeo);
  CeedOperatorSetField(op_setupgeo, "x", Erestrictx, basisx,
                       CEED_VECTOR_ACTIVE);
  CeedOperatorSetField(op_setupgeo, "dx", Erestrictx, basisx,
                       CEED_VECTOR_ACTIVE);
  CeedOperatorSetField(op_setupgeo, "weight", CEED_ELEMRESTRICTION_NONE, basisx,
                       CEED_VECTOR_NONE);
  CeedOperatorSetField(op_setupgeo, "qdata", Erestrictqdi,
                       CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);

  // Create the mass or diff operator
  CeedOperatorCreate(ceed, qf_apply, NULL, NULL, &op_apply);
  CeedOperatorSetField(op_apply, "u", Erestrictu, basisu, CEED_VECTOR_ACTIVE);
  CeedOperatorSetField(op_apply, "qdata", Erestrictqdi, CEED_BASIS_COLLOCATED,
                       qdata);
  CeedOperatorSetField(op_apply, "v", Erestrictu, basisu, CEED_VECTOR_ACTIVE);

  // Setup qdata
  CeedOperatorApply(op_setupgeo, xcoord, qdata, CEED_REQUEST_IMMEDIATE);

  // Cleanup
  CeedQFunctionDestroy(&qf_setupgeo);
  CeedOperatorDestroy(&op_setupgeo);
  CeedVectorDestroy(&xcoord);

  // Save libCEED data
  data->basisx = basisx;
  data->basisu = basisu;
  data->Erestrictx = Erestrictx;
  data->Erestrictu = Erestrictu;
  data->Erestrictqdi = Erestrictqdi;
  data->qf_apply = qf_apply;
  data->op_apply = op_apply;
  data->qdata = qdata;
  data->uceed = uceed;
  data->vceed = vceed;

  PetscFunctionReturn(0);
}

#endif // setuparea_h