#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include "cimple_polytope_library.h"

/**
 * "Constructor" Dynamically allocates the space a polytope needs
 */
struct polytope *polytope_alloc(size_t k,
                                size_t n)
{

    struct polytope *return_polytope = malloc (sizeof (struct polytope));

    return_polytope->H = gsl_matrix_alloc(k, n);
    if (return_polytope->H == NULL) {
        free (return_polytope);
        return NULL;
    }

    return_polytope->G = gsl_vector_alloc(k);
    if (return_polytope->H == NULL) {
        free (return_polytope);
        return NULL;
    }

    return_polytope->chebyshev_center = malloc(n* sizeof (double));
    if (return_polytope->chebyshev_center == NULL) {
        free (return_polytope);
        return NULL;
    }

    return return_polytope;
};

/**
 * "Destructor" Deallocates the dynamically allocated memory of the polytope
 */
void polytope_free(polytope *polytope)
{
    gsl_matrix_free(polytope->H);
    gsl_vector_free(polytope->G);
    free(polytope->chebyshev_center);
    free(polytope);
};

/**
 * "Constructor" Dynamically allocates the space a polytope needs
 */
struct cell *cell_alloc(size_t k,
                        size_t n,
                        int time_horizon)
{

    struct cell *return_cell = malloc (sizeof (struct cell));

    return_cell->safe_mode = malloc(sizeof(polytope)*time_horizon);
    if (return_cell->safe_mode == NULL) {
        free (return_cell);
        return NULL;
    }

    return_cell->polytope_description = polytope_alloc(k,n);
    if (return_cell->polytope_description == NULL) {
        free (return_cell);
        return NULL;
    }
    return return_cell;
};

/**
 * "Destructor" Deallocates the dynamically allocated memory of the region of polytopes
 */
void cell_free(cell *cell)
{
    polytope_free(cell->polytope_description);
    free(cell->safe_mode);
    free(cell);

};

/**
 * "Constructor" Dynamically allocates the space a region of polytope needs
 */
struct abstract_state *abstract_state_alloc(size_t *k,
                                            size_t k_hull,
                                            size_t n,
                                            int transitions_in_count,
                                            int transitions_out_count,
                                            int cells_count,
                                            int time_horizon)
{

    struct abstract_state *return_abstract_state = malloc (sizeof (struct abstract_state));

    /*
     * Default values: at initialization safe mode is not yet computed.
     * Thus it is assumed that the state does not contain an invariant set => invariant_set = NULL
     * next_state (next state the system has to transition to reach an invariant set) is unknown at initialization.
     */

    return_abstract_state->next_state = NULL;
    return_abstract_state->invariant_set = NULL;
//    return_abstract_state->distance_invariant_set = INFINITY;

    return_abstract_state->cells = malloc(sizeof(cell)*cells_count);
    if (return_abstract_state->cells == NULL) {
        free (return_abstract_state);
        return NULL;
    }

    for(int i = 0; i < cells_count; i++){
        return_abstract_state->cells[i] = cell_alloc(*(k+i), n, time_horizon);
        if (return_abstract_state->cells[i] == NULL) {
            free (return_abstract_state);
            return NULL;
        }
    }

    return_abstract_state->cells_count = cells_count;

    return_abstract_state->transitions_in = malloc(sizeof(struct abstract_state) * transitions_in_count);
    if (return_abstract_state->transitions_in == NULL) {
        free (return_abstract_state);
        return NULL;
    }

    return_abstract_state->transitions_in_count = transitions_in_count;

    return_abstract_state->transitions_out = malloc(sizeof(struct abstract_state) * transitions_out_count);
    if (return_abstract_state->transitions_out == NULL) {
        free (return_abstract_state);
        return NULL;
    }

    return_abstract_state->transitions_out_count = transitions_out_count;

    return_abstract_state->convex_hull = polytope_alloc(k_hull, n);
    if (return_abstract_state->convex_hull == NULL) {
        free (return_abstract_state);
        return NULL;
    }

    return return_abstract_state;
};

/**
 * "Destructor" Deallocates the dynamically allocated memory of the region of polytopes
 */
void abstract_state_free(abstract_state * abstract_state){
    polytope_free(abstract_state->convex_hull);
    for(int i = 0; i< abstract_state->cells_count; i++){
        cell_free(abstract_state->cells[i]);
    }
    free(abstract_state->transitions_out);
    free(abstract_state->transitions_in);
    free(abstract_state->cells);
    free(abstract_state);

};

/**
 * Converts two C arrays to a polytope consistent of a left side matrix (i.e. H) and right side vector (i.e. G)
 */
void polytope_from_arrays(polytope *polytope,
                          double *left_side,
                          double *right_side,
                          double *cheby,
                          char*name)
{

    gsl_matrix_from_array(polytope->H, left_side, name);
    gsl_vector_from_array(polytope->G, right_side, name);
    for(int i = 0; i < polytope->H->size2; i++){
        polytope->chebyshev_center[i] = cheby[i];
    }
};

/**
 * Converts a polytope in gsl form to cdd constraint form
 */
dd_PolyhedraPtr polytope_to_cdd(polytope *original,
                                dd_ErrorType *err)
{
    dd_PolyhedraPtr new;
    dd_MatrixPtr constraints;
    constraints = dd_CreateMatrix(original->H->size1, (original->H->size2+1));
    for (size_t k = 0; k < (original->H->size1); k++) {
        double value = gsl_vector_get(original->G, k);
        dd_set_d(constraints->matrix[k][0],value);
    }
    for(size_t i = 0; i<original->H->size1; i++){
        for (size_t j = 1; j < (original->H->size2+1); j++) {
            double value = gsl_matrix_get(original->H, i, j-1);
            dd_set_d(constraints->matrix[i][j],-1*value);
        }
    }
    constraints->representation=dd_Inequality;
    new = dd_DDMatrix2Poly(constraints, err);
    dd_FreeMatrix(constraints);
    return new;
};

/**
 * Converts a polytope in cdd constraint form to gsl form
 */
polytope * cdd_to_polytope(dd_PolyhedraPtr *original)
{

    dd_MatrixPtr constraints;
    constraints = dd_CopyInequalities(*original);
    polytope *new = polytope_alloc(constraints->rowsize, constraints->colsize-1);
    for (size_t k = 0; k < (constraints->rowsize); k++) {
        double value = dd_get_d(constraints->matrix[k][0]);
        gsl_vector_set(new->G, k, value);
    }
    for(size_t i = 0; i<constraints->rowsize; i++){
        for (size_t j = 0; j < (constraints->colsize-1); j++) {
            double value = dd_get_d(constraints->matrix[i][j+1]);
            gsl_matrix_set(new->H,i,j,-value);
        }
    }
    dd_FreeMatrix(constraints);
    return new;

};

/**
 * Generate a polytope representing a scaled unit cube
 */
polytope * polytope_scaled_unit_cube(double scale,
                                     int dimensions)
{

    dd_PolyhedraPtr cube = cdd_scaled_unit_cube(scale, dimensions);
    polytope * return_cube = cdd_to_polytope(&cube);
    dd_FreePolyhedra(cube);

    return return_cube;
};

/**
 * Checks whether a state is in a certain polytope
 */
bool polytope_check_state(polytope *polytope,
                         gsl_vector *x)
{
    gsl_vector * result = gsl_vector_alloc(polytope->G->size);
    gsl_blas_dgemv(CblasNoTrans, 1.0, polytope->H, x, 0.0, result);
    for(size_t i = 0; i< polytope->G->size; i++){
        if(gsl_vector_get(result, i) > gsl_vector_get(polytope->G, i)){
            gsl_vector_free(result);
            return false;
        }
    }
    gsl_vector_free(result);
    return true;
};

/**
 * Checks whether polytope P1 \ issubset P2
 */
bool polytope_is_subset(polytope *P1,
                        polytope *P2)
{
    dd_ErrorType err = dd_NoError;
    dd_PolyhedraPtr first = polytope_to_cdd(P1, &err);
    dd_MatrixPtr verticesFirst = dd_CopyGenerators(first);
    gsl_vector *vertex = gsl_vector_alloc(P1->H->size2);
    gsl_vector_set_zero(vertex);
    int is_included;
    bool is_subset = true;
    for(int i = 0; i<verticesFirst->rowsize;i++){
        double valueFirst0 = dd_get_d(verticesFirst->matrix[i][0]);
        if(valueFirst0 == 1){
            for(int j = 0; j<vertex->size; j++){
                double valueFirstj = dd_get_d(verticesFirst->matrix[i][j+1]);
                gsl_vector_set(vertex,(size_t)j, valueFirstj);
            }
            is_included = polytope_check_state(P2,vertex);
            if(!is_included){
                is_subset = false;
                break;
            }
        }
    }
    gsl_vector_free(vertex);
    dd_FreePolyhedra(first);
    dd_FreeMatrix(verticesFirst);
    return is_subset;
};

/**
 * Unite inequalities of P1 and P2 in new polytope and remove redundancies
 */
polytope * polytope_unite_inequalities(polytope *P1,
                                       polytope *P2)
{
    polytope *united = polytope_alloc(P1->H->size1+P2->H->size1,P1->H->size2);
    //Unite H
    gsl_matrix_set_zero(united->H);
    gsl_matrix_view H_P1 = gsl_matrix_submatrix(united->H, 0, 0, P1->H->size1, P1->H->size2);
    gsl_matrix_memcpy(&H_P1.matrix, P1->H);
    gsl_matrix_view H_P2 = gsl_matrix_submatrix(united->H, P1->H->size1, 0, P2->H->size1, P2->H->size2);
    gsl_matrix_memcpy(&H_P2.matrix, P2->H);

    //Unite G
    gsl_vector_set_zero(united->G);
    gsl_vector_view G_P1 = gsl_vector_subvector(united->G, 0, P1->G->size);
    gsl_vector_memcpy(&G_P1.vector, P1->G);
    gsl_vector_view G_P2 = gsl_vector_subvector(united->G, P1->G->size,P2->G->size);
    gsl_vector_memcpy(&G_P2.vector, P2->G);
    polytope * return_polytope = polytope_minimize(united);

    polytope_free(united);

    return return_polytope;

};

/**
 * Project original polytope (in cdd format) to the first n dimensions
 */
polytope * polytope_projection(polytope * original,
                               size_t n)
{

    dd_ErrorType err;
    dd_PolyhedraPtr orig_cdd = polytope_to_cdd(original, &err);
    dd_PolyhedraPtr new_cdd = NULL;

    cdd_projection(&orig_cdd, &new_cdd, n, &err);
    polytope * new = cdd_to_polytope(&new_cdd);
    dd_FreePolyhedra(orig_cdd);
    dd_FreePolyhedra(new_cdd);

    return new;

};

polytope * polytope_linear_transform(polytope *original,
                                     gsl_matrix *scale){

    dd_ErrorType err = dd_NoError;
    dd_PolyhedraPtr orig_cdd = polytope_to_cdd(original, &err);

    dd_MatrixPtr vertices = dd_CopyGenerators(orig_cdd);

    dd_MatrixPtr transformed_vertices = dd_CreateMatrix(1, scale->size1+1);
    dd_MatrixPtr transformed_vertex = dd_CreateMatrix(1, scale->size1+1);
    bool new_matrix_started = false;
    gsl_vector *vertex = gsl_vector_alloc((vertices->colsize - 1));
    gsl_vector *scaled_vertex = gsl_vector_alloc(scale->size1);
    gsl_vector_set_zero(vertex);
    gsl_vector_set_zero(scaled_vertex);

    for(int i = 0; i<vertices->rowsize; i++) {
        //Check whether row represents ray or vertex
        double is_vertex = dd_get_d(vertices->matrix[i][0]);
        if (is_vertex == 1) {
            for (size_t j = 1; j < vertices->colsize; j++) {
                double value = dd_get_d(vertices->matrix[i][j]);
                gsl_vector_set(vertex, j-1, value);
            }
            gsl_blas_dgemv(CblasNoTrans, 1.0, scale, vertex, 0.0, scaled_vertex);

            if (new_matrix_started) {
                dd_set_d(transformed_vertex->matrix[0][0], 1);
                for (size_t j = 0; j < scaled_vertex->size; j++) {
                    dd_set_d(transformed_vertex->matrix[0][j + 1], gsl_vector_get(scaled_vertex, j));
                }
                dd_MatrixAppendTo(&transformed_vertices, transformed_vertex);
            } else {
                dd_set_d(transformed_vertices->matrix[0][0], 1);
                for (size_t j = 0; j < scaled_vertex->size; j++) {
                    dd_set_d(transformed_vertices->matrix[0][j + 1], gsl_vector_get(scaled_vertex, j));
                }
                new_matrix_started = true;
            }

        }
    }

    transformed_vertices->representation = dd_Generator;
    dd_PolyhedraPtr transformed_cdd = dd_DDMatrix2Poly(transformed_vertices, &err);
    polytope *transformed = cdd_to_polytope(&transformed_cdd);

    //Clean up
    dd_FreeMatrix(transformed_vertex);
    dd_FreeMatrix(transformed_vertices);
    dd_FreePolyhedra(orig_cdd);
    dd_FreePolyhedra(transformed_cdd);
    dd_FreeMatrix(vertices);
    gsl_vector_free(vertex);
    gsl_vector_free(scaled_vertex);

    return transformed;

};
/**
 * Remove redundancies from gsl polytope inequalities
 */
polytope * polytope_minimize(polytope *original)
{

    dd_ErrorType err = dd_NoError;
    dd_MatrixPtr min_matrix;

    dd_PolyhedraPtr cdd_original = polytope_to_cdd(original, &err);

    dd_PolyhedraPtr cdd_minimized = cdd_minimize(&cdd_original, &err);
    min_matrix = dd_CopyInequalities(cdd_minimized);

    polytope * minimized = cdd_to_polytope(&cdd_minimized);
    dd_FreeMatrix(min_matrix);
    dd_FreePolyhedra(cdd_original);
    dd_FreePolyhedra(cdd_minimized);

    return minimized;

};

/**
 * Compute Minkowski sum of two polytopes
 */
polytope * polytope_minkowski(polytope *P1,
                              polytope *P2)
{
    dd_ErrorType err = dd_NoError;
    dd_PolyhedraPtr A = polytope_to_cdd(P1, &err);
    dd_PolyhedraPtr B = polytope_to_cdd(P2, &err);
    dd_PolyhedraPtr C = cdd_minkowski(A,B);
    polytope * returnPolytope = cdd_to_polytope(&C);
    dd_FreePolyhedra(A);
    dd_FreePolyhedra(B);
    dd_FreePolyhedra(C);
    return returnPolytope;
};

/**
 * Compute Pontryagin difference C = A-B s.t.:
 * A-B = {c \in A-B| c+b \in A, \forall b \in B}
 */
polytope * polytope_pontryagin(polytope* A,
                               polytope* B)
{

    dd_ErrorType err;
    dd_MatrixPtr verticesA, verticesB;
    //create cddPoly A,B
    dd_PolyhedraPtr cddA = polytope_to_cdd(A, &err);

    dd_PolyhedraPtr cddB = polytope_to_cdd(B, &err);

    polytope *C = NULL;

    verticesA = dd_CopyGenerators(cddA);
    verticesB = dd_CopyGenerators(cddB);
    for(int i = 0; i<verticesB->rowsize; i++){
        //Check whether row represents ray or vertex
        double value_B0 = dd_get_d(verticesB->matrix[i][0]);
        if(value_B0 == 1){
            dd_MatrixPtr tempA;
            //A-b (where b is the vertex)
            tempA = dd_CreateMatrix(verticesA->rowsize,verticesA->colsize);
            //each vertex of A displaced by b
            for(int j = 0; j<verticesA->rowsize; j++){
                double value_A0 = dd_get_d(verticesA->matrix[j][0]);
                if(value_A0 == 1){
                    dd_set_d(tempA->matrix[j][0], 1);
                    for(int k = 1; k<verticesA->colsize; k++){
                        double value_Ak = dd_get_d(verticesA->matrix[j][k]);
                        double value_Bk = dd_get_d(verticesB->matrix[i][k]);
                        dd_set_d(tempA->matrix[j][k],(value_Ak-value_Bk));
                    }
                }else{
                    dd_set_d(tempA->matrix[j][0], 0);
                    for(int k = 1; k<verticesA->colsize; k++){
                        double value_Ak = dd_get_d(verticesA->matrix[j][k]);
                        dd_set_d(tempA->matrix[j][k],(value_Ak));
                    }
                }
            }
            dd_PolyhedraPtr cdd_temp;
            tempA->representation = dd_Generator;
            cdd_temp = dd_DDMatrix2Poly(tempA, &err);
            polytope *tempC = cdd_to_polytope(&cdd_temp);
            dd_FreePolyhedra(cdd_temp);
            if(C == NULL){
                C = polytope_alloc(tempC->H->size1,tempC->H->size2);
                gsl_matrix_memcpy(C->H,tempC->H);
                gsl_vector_memcpy(C->G,tempC->G);
                polytope_free(tempC);
            }else{
                polytope *copyC = C;
                C = polytope_unite_inequalities(copyC, tempC);
                polytope_free(tempC);
                polytope_free(copyC);
            }
            dd_FreeMatrix(tempA);
        }
    }

    dd_FreeMatrix(verticesA);
    dd_FreeMatrix(verticesB);
    dd_FreePolyhedra(cddA);
    dd_FreePolyhedra(cddB);
    return C;
};

/**
 * Set up constraints in quadratic problem for GUROBI
 */
int polytope_to_constraints_gurobi(polytope *constraints,
                                   GRBmodel *model,
                                   size_t N)
{

    int error = 0;
    double constraint_val[N];
    int ind[N];
    for(size_t i = 0; i < constraints->H->size1;i++){
        char constraint_name[5];
        sprintf(constraint_name, "c_%d", (int)i);
        for(size_t j = 0; j < N;j++){
            ind[j] = (int)j;
            constraint_val[j] = gsl_matrix_get(constraints->H,i,j);
        }

        error = GRBaddconstr(model, (int)N, ind, constraint_val, GRB_LESS_EQUAL, gsl_vector_get(constraints->G,i), constraint_name);

        if(error){
            return error;
        }
    }
    return error;
};

/**
 * Generate a polytope representing a scaled unit cube
 */
dd_PolyhedraPtr cdd_scaled_unit_cube(double scale,
                                     int dimensions)
{

    dd_MatrixPtr constraints;
    dd_PolyhedraPtr cube = NULL;
    dd_ErrorType err = dd_NoError;
    constraints = dd_CreateMatrix(dimensions*2,dimensions+1);
    for(int i = 0; i<(dimensions); i++){

            dd_set_d(constraints->matrix[2*i][0],(scale*0.5));
            dd_set_d(constraints->matrix[2*i][i+1],-1);
            dd_set_d(constraints->matrix[2*i+1][0],(scale*0.5));
            dd_set_d(constraints->matrix[2*i+1][i+1],1);
    }
    constraints->representation=dd_Inequality;
    cube = dd_DDMatrix2Poly(constraints, &err);
    dd_FreeMatrix(constraints);

    return cube;
};

/**
 * Project original polytope (in cdd format) to the first n dimensions
 */
void cdd_projection(dd_PolyhedraPtr *original,
                    dd_PolyhedraPtr *new,
                    size_t n,
                    dd_ErrorType *err)
{
    dd_MatrixPtr full=NULL,projected=NULL;
    full = dd_CopyInequalities(*original);
    dd_colrange j,d;
    dd_rowset redset,impl_linset;
    dd_colset delset;
    dd_rowindex newpos;

    d=full->colsize;
    set_initialize(&delset, d);
    for (j=n+1; j<d; j++){
        set_addelem(delset, j+1);
    }

    projected=dd_BlockElimination(full, delset, err);

    dd_MatrixCanonicalize(&projected,&impl_linset,&redset,&newpos,err);

    dd_FreeMatrix(full);
    projected->representation = dd_Inequality;
    *new = dd_DDMatrix2Poly(projected, err);
    dd_FreeMatrix(projected);
    set_free(delset);
    set_free(redset);
    set_free(impl_linset);
    free(newpos);

};

/**
 * Remove redundancies from cdd polytope inequalities
 */
dd_PolyhedraPtr cdd_minimize(dd_PolyhedraPtr *original,
                             dd_ErrorType *err)
{

    dd_rowset redset,impl_linset;
    dd_rowindex newpos;
    dd_MatrixPtr full=NULL;
    full = dd_CopyInequalities(*original);
    dd_MatrixCanonicalize(&full,&impl_linset,&redset,&newpos,err);

    full->representation = dd_Inequality;
    dd_PolyhedraPtr new = dd_DDMatrix2Poly(full, err);
    dd_FreeMatrix(full);
    set_free(redset);
    set_free(impl_linset);
    free(newpos);
    return new;

};


/* spmatrix/gsl_spmatrix_float.h
 *
 * Copyright (C) 2012, 2013, 2014, 2015, 2016, 2017, 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.
 */

#ifndef __GSL_SPMATRIX_COMPLEX_FLOAT_H__
#define __GSL_SPMATRIX_COMPLEX_FLOAT_H__

#include <stdlib.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_bst.h>
#include <gsl/gsl_vector_complex_float.h>
#include <gsl/gsl_matrix_complex_float.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

/*
 * COO format:
 *
 * If data[n] = A_{ij}, then:
 *   i = A->i[n]
 *   j = A->p[n]
 *
 * Compressed column format (CSC):
 *
 * If data[n] = A_{ij}, then:
 *   i = A->i[n]
 *   A->p[j] <= n < A->p[j+1]
 * so that column j is stored in
 * [ data[p[j]], data[p[j] + 1], ..., data[p[j+1] - 1] ]
 *
 * Compressed row format (CSR):
 *
 * If data[n] = A_{ij}, then:
 *   j = A->i[n]
 *   A->p[i] <= n < A->p[i+1]
 * so that row i is stored in
 * [ data[p[i]], data[p[i] + 1], ..., data[p[i+1] - 1] ]
 */

typedef struct
{
  size_t size1;              /* number of rows */
  size_t size2;              /* number of columns */

  /* i (size nzmax) contains:
   *
   * COO/CSC: row indices
   * CSR: column indices
   */
  int *i;

  float *data;               /* matrix elements of size nzmax */

  /*
   * COO: p[n] = column number of element data[n]
   * CSC: p[j] = index in data of first non-zero element in column j
   * CSR: p[i] = index in data of first non-zero element in row i
   */
  int *p;

  size_t nzmax;              /* maximum number of matrix elements */
  size_t nz;                 /* number of non-zero values in matrix */

  gsl_bst_workspace *tree;   /* binary tree structure */
  gsl_spmatrix_pool *pool;   /* memory pool for binary tree nodes */
  size_t node_size;          /* size of individual tree node in bytes */

  /*
   * workspace of size 2*MAX(size1,size2)*MAX(sizeof(float),sizeof(int))
   * used in various routines
   */
  union
    {
      void *work_void;
      int *work_int;
      float *work_atomic;
    } work;

  int sptype;                /* sparse storage type */
  size_t spflags;            /* GSL_SPMATRIX_FLG_xxx */
} gsl_spmatrix_complex_float;

/*
 * Prototypes
 */

/* allocation / initialization */

gsl_spmatrix_complex_float * gsl_spmatrix_complex_float_alloc (const size_t n1, const size_t n2);
gsl_spmatrix_complex_float * gsl_spmatrix_complex_float_alloc_nzmax (const size_t n1, const size_t n2,
                                                                     const size_t nzmax, const int sptype);
void gsl_spmatrix_complex_float_free (gsl_spmatrix_complex_float * m);
int gsl_spmatrix_complex_float_realloc (const size_t nzmax, gsl_spmatrix_complex_float * m);
size_t gsl_spmatrix_complex_float_nnz (const gsl_spmatrix_complex_float * m);
const char * gsl_spmatrix_complex_float_type (const gsl_spmatrix_complex_float * m);
int gsl_spmatrix_complex_float_set_zero (gsl_spmatrix_complex_float * m);
int gsl_spmatrix_complex_float_tree_rebuild (gsl_spmatrix_complex_float * m);

/* compress */

int gsl_spmatrix_complex_float_csc (gsl_spmatrix_complex_float * dest, const gsl_spmatrix_complex_float * src);
int gsl_spmatrix_complex_float_csr (gsl_spmatrix_complex_float * dest, const gsl_spmatrix_complex_float * src);
gsl_spmatrix_complex_float * gsl_spmatrix_complex_float_compress (const gsl_spmatrix_complex_float * src, const int sptype);
gsl_spmatrix_complex_float * gsl_spmatrix_complex_float_compcol (const gsl_spmatrix_complex_float * src);
gsl_spmatrix_complex_float * gsl_spmatrix_complex_float_ccs (const gsl_spmatrix_complex_float * src);
gsl_spmatrix_complex_float * gsl_spmatrix_complex_float_crs (const gsl_spmatrix_complex_float * src);

/* copy */

int gsl_spmatrix_complex_float_memcpy (gsl_spmatrix_complex_float * dest, const gsl_spmatrix_complex_float * src);

/* file I/O */

int gsl_spmatrix_complex_float_fprintf (FILE * stream, const gsl_spmatrix_complex_float * m, const char * format);
gsl_spmatrix_complex_float * gsl_spmatrix_complex_float_fscanf (FILE * stream);
int gsl_spmatrix_complex_float_fwrite (FILE * stream, const gsl_spmatrix_complex_float * m);
int gsl_spmatrix_complex_float_fread (FILE * stream, gsl_spmatrix_complex_float * m);

/* get/set */

gsl_complex_float gsl_spmatrix_complex_float_get (const gsl_spmatrix_complex_float * m, const size_t i, const size_t j);
int gsl_spmatrix_complex_float_set (gsl_spmatrix_complex_float * m, const size_t i, const size_t j, const gsl_complex_float x);
gsl_complex_float * gsl_spmatrix_complex_float_ptr (const gsl_spmatrix_complex_float * m, const size_t i, const size_t j);

/* operations */

int gsl_spmatrix_complex_float_scale (gsl_spmatrix_complex_float * m, const gsl_complex_float x);
int gsl_spmatrix_complex_float_scale_columns (gsl_spmatrix_complex_float * m, const gsl_vector_complex_float * x);
int gsl_spmatrix_complex_float_scale_rows (gsl_spmatrix_complex_float * m, const gsl_vector_complex_float * x);
int gsl_spmatrix_complex_float_add (gsl_spmatrix_complex_float * c, const gsl_spmatrix_complex_float * a, const gsl_spmatrix_complex_float * b);
int gsl_spmatrix_complex_float_d2sp (gsl_spmatrix_complex_float * T, const gsl_matrix_complex_float * A);
int gsl_spmatrix_complex_float_sp2d (gsl_matrix_complex_float * A, const gsl_spmatrix_complex_float * S);

/* properties */

int gsl_spmatrix_complex_float_equal (const gsl_spmatrix_complex_float * a, const gsl_spmatrix_complex_float * b);

/* swap */

int gsl_spmatrix_complex_float_transpose (gsl_spmatrix_complex_float * m);
int gsl_spmatrix_complex_float_transpose2 (gsl_spmatrix_complex_float * m);
int gsl_spmatrix_complex_float_transpose_memcpy (gsl_spmatrix_complex_float * dest, const gsl_spmatrix_complex_float * src);

__END_DECLS

#endif /* __GSL_SPMATRIX_COMPLEX_FLOAT_H__ */


/* Copyright (c) 2016, 2017 Bradley Worley <geekysuavo@gmail.com>
 * Released under the MIT License
 */

/* ensure once-only inclusion. */
#ifndef __MATTE_OBJECT_H__
#define __MATTE_OBJECT_H__

/* include required standard c library headers. */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <stdbool.h>

/* include required c math library headers. */
#include <math.h>
#include <complex.h>

/* include the blas/lapack headers. */
#include <cblas.h>
#include <clapack.h>

/* include the required matte headers. */
#include <matte/zone.h>

/* type definitions of blas/lapack enumerations:
 *  MatteTranspose: transpose options.
 *  MatteTriangle: triangle options.
 *  MatteDiagonal: diagonal options.
 */
typedef enum CBLAS_TRANSPOSE MatteTranspose;
typedef enum CBLAS_UPLO MatteTriangle;
typedef enum CBLAS_DIAG MatteDiagonal;

/* MATTE_TYPE: macro to obtain the type structure pointer of an object.
 */
#define MATTE_TYPE(obj) \
  (((Object) obj)->type)

/* MATTE_TYPE_CHECK: macro to check that a matte object has a specific type.
 */
#define MATTE_TYPE_CHECK(obj,typ) \
  ((obj) && MATTE_TYPE(obj) == typ)

/* OBJECT_BASE: macro to place at the beginning of every matte object.
 * this allows all objects to be directly cast into the base object
 * type for memory management, dynamic dispatch, etc.
 */
#define OBJECT_BASE \
  Object base;

/* ObjectType: pointer to a struct _ObjectType. */
typedef struct _ObjectType *ObjectType;

/* Object: pointer to a struct _Object. */
typedef struct _Object *Object;

/* ObjectMethods: pointer to a struct _ObjectMethod. */
typedef struct _ObjectMethod *ObjectMethods;

/* general-purpose function pointer type definition:
 */
typedef Object (*matte_func) (Zone, Object);

/* function pointer type definitions for matte objects:
 */
typedef Object (*obj_constructor) (Zone, Object);
typedef void   (*obj_destructor)  (Zone, Object);
typedef int    (*obj_display)     (Zone, Object);
typedef int    (*obj_assert)      (Object);
typedef Object (*obj_unary)       (Zone, Object);
typedef Object (*obj_binary)      (Zone, Object, Object);
typedef Object (*obj_ternary)     (Zone, Object, Object, Object);
typedef Object (*obj_variadic)    (Zone, int, va_list);
typedef Object (*obj_method)      (Zone, Object, Object);

/* _ObjectMethod: structure that holds information about a matte object
 * method.
 */
struct _ObjectMethod {
  /* @name: string name of the method.
   * @fn: method function pointer.
   */
  const char *name;
  obj_method fn;
};

/* _ObjectType: structure that holds the core set of information required
 * by the matte object system.
 */
struct _ObjectType {
  /* core object information:
   *  @name: string name of the matte type.
   *  @size: base size of the type struct.
   *  @precedence: object precedence for dynamic dispatch.
   */
  const char *name;
  unsigned int size;
  unsigned int precedence;

  /* core object method table:
   */
  obj_constructor fn_new;
  obj_constructor fn_copy;
  obj_destructor  fn_delete;
  obj_display     fn_disp;
  obj_assert      fn_true;

  /* numeric method table:
   */
  obj_binary   fn_plus;        /* a+b      binary addition.             */
  obj_binary   fn_minus;       /* a-b      binary subtraction.          */
  obj_unary    fn_uminus;      /* -a       unary minus.                 */
  obj_binary   fn_times;       /* a.*b     element-wise multiplication. */
  obj_binary   fn_mtimes;      /* a*b      matrix multiplication.       */
  obj_binary   fn_rdivide;     /* a./b     element-wise right division. */
  obj_binary   fn_ldivide;     /* a.\b     element-wise left division.  */
  obj_binary   fn_mrdivide;    /* a/b      matrix right division.       */
  obj_binary   fn_mldivide;    /* a\b      matrix left division.        */
  obj_binary   fn_power;       /* a.^b     element-wise power.          */
  obj_binary   fn_mpower;      /* a^b      matrix power.                */
  obj_binary   fn_lt;          /* a<b      less than.                   */
  obj_binary   fn_gt;          /* a>b      greater than.                */
  obj_binary   fn_le;          /* a<=b     less than or equal to.       */
  obj_binary   fn_ge;          /* a>=b     greater than or equal to.    */
  obj_binary   fn_ne;          /* a!=b     inequality.                  */
  obj_binary   fn_eq;          /* a==b     equality.                    */
  obj_binary   fn_and;         /* a&b      logical and.                 */
  obj_binary   fn_or;          /* a|b      logical or.                  */
  obj_binary   fn_mand;        /* a&&b     matrix logical and.          */
  obj_binary   fn_mor;         /* a||b     matrix logical or.           */
  obj_unary    fn_not;         /* !a       logical negation.            */
  obj_ternary  fn_colon;       /* a:d:b    colon operator.              */
  obj_unary    fn_ctranspose;  /* a'       conjugate transpose.         */
  obj_unary    fn_transpose;   /* a.'      matrix transpose.            */
  obj_variadic fn_horzcat;     /* [a,b]    horizontal concatenation.    */
  obj_variadic fn_vertcat;     /* [a;b]    vertical concatenation.      */
  obj_binary   fn_subsref;     /* a(s)     subscripted reference.       */
  obj_ternary  fn_subsasgn;    /* a(s)=b   subscripted assignment.      */
  obj_unary    fn_subsindex;   /* b(a)     subscript index.             */

  /* general-purpose method table:
   */
  ObjectMethods methods;
};

/* _Object: structure for type-casting between any number of matte objects.
 * holds a pointer to the object type structure as its first element.
 */
struct _Object {
  /* @type: the type of matte object that is located
   * at the current address in memory.
   */
  ObjectType type;

  /* in all other matte objects, object instance variables go here. */
};

/* function declarations (object.c): */

Object object_alloc (Zone z, ObjectType type);

Object object_copy (Zone z, Object obj);

void object_free (Zone z, void *ptr);

void object_free_all (Zone z);

int object_disp (Zone z, Object obj);

int object_display (Zone z, Object obj, const char *var);

int object_true (Object obj);

/* object method declarations (object.c): */

Object object_plus       (Zone z, Object a, Object b);
Object object_minus      (Zone z, Object a, Object b);
Object object_uminus     (Zone z, Object a);
Object object_times      (Zone z, Object a, Object b);
Object object_mtimes     (Zone z, Object a, Object b);
Object object_rdivide    (Zone z, Object a, Object b);
Object object_ldivide    (Zone z, Object a, Object b);
Object object_mrdivide   (Zone z, Object a, Object b);
Object object_mldivide   (Zone z, Object a, Object b);
Object object_power      (Zone z, Object a, Object b);
Object object_mpower     (Zone z, Object a, Object b);
Object object_lt         (Zone z, Object a, Object b);
Object object_gt         (Zone z, Object a, Object b);
Object object_le         (Zone z, Object a, Object b);
Object object_ge         (Zone z, Object a, Object b);
Object object_ne         (Zone z, Object a, Object b);
Object object_eq         (Zone z, Object a, Object b);
Object object_and        (Zone z, Object a, Object b);
Object object_or         (Zone z, Object a, Object b);
Object object_mand       (Zone z, Object a, Object b);
Object object_mor        (Zone z, Object a, Object b);
Object object_not        (Zone z, Object a);
Object object_colon      (Zone z, Object a, Object b, Object c);
Object object_ctranspose (Zone z, Object a);
Object object_transpose  (Zone z, Object a);
Object object_horzcat    (Zone z, int n, ...);
Object object_vertcat    (Zone z, int n, ...);
Object object_subsref    (Zone z, Object a, Object b);
Object object_subsasgn   (Zone z, Object a, Object b, Object c);
Object object_subsindex  (Zone z, Object a);

/* utility function declarations: */

char *strdup (const char *s);

#endif /* !__MATTE_OBJECT_H__ */


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

#ifndef __StgFEM_Discretisation_Discretisation_h__
#define __StgFEM_Discretisation_Discretisation_h__

	#include "types.h"

	#include <petsc.h>
	#include <petscvec.h>
	#include <petscmat.h>
	#include <petscksp.h>
#if( (PETSC_VERSION_MAJOR==3 && PETSC_VERSION_MINOR==0) || (PETSC_VERSION_MAJOR<3) )
	#include <petscmg.h>
#endif
	#include <petscsnes.h>
	#include "PETScErrorChecking.h"

	#include "FeMesh_Algorithms.h"
	#include "FeMesh_ElementType.h"
	#include "ElementType.h"
	#include "ElementType_Register.h"
	#include "ConstantElementType.h"
	#include "LinearElementType.h"
	#include "BilinearElementType.h"
	#include "TrilinearElementType.h"
	#include "Biquadratic.h"
	#include "Triquadratic.h"
	#include "LinearTriangleElementType.h"
	#include "BilinearInnerElType.h"
	#include "TrilinearInnerElType.h"
    #include "dQ13DElementType.h"
    #include "dQ12DElementType.h"
	#include "dQ1Generator.h"

    #include "Element.h"
	#include "FeMesh.h"
	#include "C0Generator.h"
	#include "C2Generator.h"
	#include "Inner2DGenerator.h"
	#include "LinkedDofInfo.h"
	#include "FeEquationNumber.h"
	#include "FeVariable.h"

    #include "IrregularMeshGaussLayout.h"

	#include "Init.h"
	#include "Finalise.h"

#endif /* __StgFEM_Discretisation_Discretisation_h__ */


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

#ifndef __GSL_VECTOR_COMPLEX_DOUBLE_H__
#define __GSL_VECTOR_COMPLEX_DOUBLE_H__

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

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_double.h>
#include <gsl/gsl_vector_complex.h>
#include <gsl/gsl_block_complex_double.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size;
  size_t stride;
  double *data;
  gsl_block_complex *block;
  int owner;
} gsl_vector_complex;

typedef struct
{
  gsl_vector_complex vector;
} _gsl_vector_complex_view;

typedef _gsl_vector_complex_view gsl_vector_complex_view;

typedef struct
{
  gsl_vector_complex vector;
} _gsl_vector_complex_const_view;

typedef const _gsl_vector_complex_const_view gsl_vector_complex_const_view;

/* Allocation */

GSL_FUN gsl_vector_complex *gsl_vector_complex_alloc (const size_t n);
GSL_FUN gsl_vector_complex *gsl_vector_complex_calloc (const size_t n);

GSL_FUN gsl_vector_complex *
gsl_vector_complex_alloc_from_block (gsl_block_complex * b,
                                           const size_t offset,
                                           const size_t n,
                                           const size_t stride);

GSL_FUN gsl_vector_complex *
gsl_vector_complex_alloc_from_vector (gsl_vector_complex * v,
                                             const size_t offset,
                                             const size_t n,
                                             const size_t stride);

GSL_FUN void gsl_vector_complex_free (gsl_vector_complex * v);

/* Views */

GSL_FUN _gsl_vector_complex_view
gsl_vector_complex_view_array (double *base,
                                     size_t n);

GSL_FUN _gsl_vector_complex_view
gsl_vector_complex_view_array_with_stride (double *base,
                                                 size_t stride,
                                                 size_t n);

GSL_FUN _gsl_vector_complex_const_view
gsl_vector_complex_const_view_array (const double *base,
                                           size_t n);

GSL_FUN _gsl_vector_complex_const_view
gsl_vector_complex_const_view_array_with_stride (const double *base,
                                                       size_t stride,
                                                       size_t n);

GSL_FUN _gsl_vector_complex_view
gsl_vector_complex_subvector (gsl_vector_complex *base,
                                         size_t i,
                                         size_t n);

GSL_FUN _gsl_vector_complex_view
gsl_vector_complex_subvector_with_stride (gsl_vector_complex *v,
                                                size_t i,
                                                size_t stride,
                                                size_t n);

GSL_FUN _gsl_vector_complex_const_view
gsl_vector_complex_const_subvector (const gsl_vector_complex *base,
                                               size_t i,
                                               size_t n);

GSL_FUN _gsl_vector_complex_const_view
gsl_vector_complex_const_subvector_with_stride (const gsl_vector_complex *v,
                                                      size_t i,
                                                      size_t stride,
                                                      size_t n);

GSL_FUN _gsl_vector_view
gsl_vector_complex_real (gsl_vector_complex *v);

GSL_FUN _gsl_vector_view
gsl_vector_complex_imag (gsl_vector_complex *v);

GSL_FUN _gsl_vector_const_view
gsl_vector_complex_const_real (const gsl_vector_complex *v);

GSL_FUN _gsl_vector_const_view
gsl_vector_complex_const_imag (const gsl_vector_complex *v);

/* Operations */

GSL_FUN void gsl_vector_complex_set_zero (gsl_vector_complex * v);
GSL_FUN void gsl_vector_complex_set_all (gsl_vector_complex * v,
                                       gsl_complex z);
GSL_FUN int gsl_vector_complex_set_basis (gsl_vector_complex * v, size_t i);

GSL_FUN int gsl_vector_complex_fread (FILE * stream,
                                    gsl_vector_complex * v);
GSL_FUN int gsl_vector_complex_fwrite (FILE * stream,
                                     const gsl_vector_complex * v);
GSL_FUN int gsl_vector_complex_fscanf (FILE * stream,
                                     gsl_vector_complex * v);
GSL_FUN int gsl_vector_complex_fprintf (FILE * stream,
                                      const gsl_vector_complex * v,
                                      const char *format);

GSL_FUN int gsl_vector_complex_memcpy (gsl_vector_complex * dest, const gsl_vector_complex * src);

GSL_FUN int gsl_vector_complex_reverse (gsl_vector_complex * v);

GSL_FUN int gsl_vector_complex_swap (gsl_vector_complex * v, gsl_vector_complex * w);
GSL_FUN int gsl_vector_complex_swap_elements (gsl_vector_complex * v, const size_t i, const size_t j);

GSL_FUN int gsl_vector_complex_equal (const gsl_vector_complex * u,
                                    const gsl_vector_complex * v);

GSL_FUN int gsl_vector_complex_isnull (const gsl_vector_complex * v);
GSL_FUN int gsl_vector_complex_ispos (const gsl_vector_complex * v);
GSL_FUN int gsl_vector_complex_isneg (const gsl_vector_complex * v);
GSL_FUN int gsl_vector_complex_isnonneg (const gsl_vector_complex * v);

GSL_FUN int gsl_vector_complex_add (gsl_vector_complex * a, const gsl_vector_complex * b);
GSL_FUN int gsl_vector_complex_sub (gsl_vector_complex * a, const gsl_vector_complex * b);
GSL_FUN int gsl_vector_complex_mul (gsl_vector_complex * a, const gsl_vector_complex * b);
GSL_FUN int gsl_vector_complex_div (gsl_vector_complex * a, const gsl_vector_complex * b);
GSL_FUN int gsl_vector_complex_scale (gsl_vector_complex * a, const gsl_complex x);
GSL_FUN int gsl_vector_complex_add_constant (gsl_vector_complex * a, const gsl_complex x);

GSL_FUN INLINE_DECL gsl_complex gsl_vector_complex_get (const gsl_vector_complex * v, const size_t i);
GSL_FUN INLINE_DECL void gsl_vector_complex_set (gsl_vector_complex * v, const size_t i, gsl_complex z);
GSL_FUN INLINE_DECL gsl_complex *gsl_vector_complex_ptr (gsl_vector_complex * v, const size_t i);
GSL_FUN INLINE_DECL const gsl_complex *gsl_vector_complex_const_ptr (const gsl_vector_complex * v, const size_t i);

#ifdef HAVE_INLINE

INLINE_FUN
gsl_complex
gsl_vector_complex_get (const gsl_vector_complex * v,
                              const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      gsl_complex zero = {{0, 0}};
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, zero);
    }
#endif
  return *GSL_COMPLEX_AT (v, i);
}

INLINE_FUN
void
gsl_vector_complex_set (gsl_vector_complex * v,
                              const size_t i, gsl_complex z)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  *GSL_COMPLEX_AT (v, i) = z;
}

INLINE_FUN
gsl_complex *
gsl_vector_complex_ptr (gsl_vector_complex * v,
                              const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return GSL_COMPLEX_AT (v, i);
}

INLINE_FUN
const gsl_complex *
gsl_vector_complex_const_ptr (const gsl_vector_complex * v,
                                    const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return GSL_COMPLEX_AT (v, i);
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_COMPLEX_DOUBLE_H__ */


/*
  Copyright [2021] [IBM Corporation]
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
  http://www.apache.org/licenses/LICENSE-2.0
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#ifndef _MCAS_NUMA_NODE_MASK__H_
#define _MCAS_NUMA_NODE_MASK__H_

#include <numa.h> /* bitmask */
#include <common/string_view.h>
#include <gsl/pointers> /* not_null */
#include <memory> /* unique_ptr */

struct numa_node_mask_dtor
{
	void operator()(bitmask *b) { numa_free_nodemask(b); }
};

struct numa_node_mask
{
	numa_node_mask(common::string_view mask_)
		: _mask(std::unique_ptr<bitmask, numa_node_mask_dtor>(mask_not_null(std::string(mask_))))
	{
	}
	numa_node_mask(const bitmask *b)
		: _mask(std::unique_ptr<bitmask, numa_node_mask_dtor>(numa_allocate_nodemask()))
	{
		copy_bitmask_to_bitmask(const_cast<bitmask *>(b), get());
	}
	const bitmask *get() const { return _mask.get().get(); }
	bitmask *get() { return _mask.get().get(); }
	/* get first 64 bits in mask */
	uint64_t get64()
	{
		uint64_t r = 0;
		for ( unsigned i = 0; i != 64; ++i )
		{
			if ( numa_bitmask_isbitset(get(), i) )
			{
				r |= ( 1U << i );
			}
		}
		return r;
	}
private:
	static bitmask *mask_not_null(common::string_view mask_)
	{
		auto m = numa_parse_nodestring(std::string(mask_).c_str());
		return m ? m : numa_allocate_nodemask();
	}
	gsl::not_null<std::unique_ptr<bitmask, numa_node_mask_dtor>> _mask;
};

#endif


#pragma once

#include <gsl/span>

#include <cstddef>

namespace imageview {

// Implementation of the PixelFormat concept for the 8-bit grayscale pixel
// format.
class PixelFormatGrayscale8 {
 public:
  using color_type = unsigned char;
  static constexpr int kBytesPerPixel = 1;

  constexpr color_type read(gsl::span<const std::byte, kBytesPerPixel> data) const;

  constexpr void write(const color_type& color, gsl::span<std::byte, kBytesPerPixel> data) const;
};

constexpr PixelFormatGrayscale8::color_type PixelFormatGrayscale8::read(
    gsl::span<const std::byte, kBytesPerPixel> data) const {
  return static_cast<color_type>(data[0]);
}

constexpr void PixelFormatGrayscale8::write(const color_type& color, gsl::span<std::byte, kBytesPerPixel> data) const {
  data[0] = static_cast<std::byte>(color);
}

}  // namespace imageview


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

#include <config.h>
#include <stdio.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_block.h>
#include <gsl/gsl_histogram.h>

int
gsl_histogram_fread (FILE * stream, gsl_histogram * h)
{
  int status = gsl_block_raw_fread (stream, h->range, h->n + 1, 1);

  if (status)
    return status;

  status = gsl_block_raw_fread (stream, h->bin, h->n, 1);
  return status;
}

int
gsl_histogram_fwrite (FILE * stream, const gsl_histogram * h)
{
  int status = gsl_block_raw_fwrite (stream, h->range, h->n + 1, 1);

  if (status)
    return status;

  status = gsl_block_raw_fwrite (stream, h->bin, h->n, 1);
  return status;
}

int
gsl_histogram_fprintf (FILE * stream, const gsl_histogram * h,
		       const char *range_format, const char *bin_format)
{
  size_t i;
  const size_t n = h->n;

  for (i = 0; i < n; i++)
    {
      int status = fprintf (stream, range_format, h->range[i]);

      if (status < 0)
	{
	  GSL_ERROR ("fprintf failed", GSL_EFAILED);
	}

      status = putc (' ', stream);

      if (status == EOF)
	{
	  GSL_ERROR ("putc failed", GSL_EFAILED);
	}

      status = fprintf (stream, range_format, h->range[i + 1]);

      if (status < 0)
	{
	  GSL_ERROR ("fprintf failed", GSL_EFAILED);
	}

      status = putc (' ', stream);

      if (status == EOF)
	{
	  GSL_ERROR ("putc failed", GSL_EFAILED);
	}

      status = fprintf (stream, bin_format, h->bin[i]);

      if (status < 0)
	{
	  GSL_ERROR ("fprintf failed", GSL_EFAILED);
	}

      status = putc ('\n', stream);

      if (status == EOF)
	{
	  GSL_ERROR ("putc failed", GSL_EFAILED);
	}
    }

  return GSL_SUCCESS;
}

int
gsl_histogram_fscanf (FILE * stream, gsl_histogram * h)
{
  size_t i;
  const size_t n = h->n;
  double upper;

  for (i = 0; i < n; i++)
    {
      int status = fscanf (stream,
			   "%lg %lg %lg", h->range + i, &upper,
			   h->bin + i);

      if (status != 3)
	{
	  GSL_ERROR ("fscanf failed", GSL_EFAILED);
	}
    }

  h->range[n] = upper;

  return GSL_SUCCESS;
}


#pragma once

#include <string>
#include <gsl/span>

namespace crypto {

	/*
		Use the Windows CNG API to compute a MD5 hash of the
		provided data view and return it as a lowercase hex
		string.
	*/
	std::string MD5AsString(gsl::span<uint8_t> data);

}


#pragma once

#include <gsl/span>
#include <array>
#include "halley/core/api/audio_api.h"

namespace Halley
{
	using AudioSourceData = std::array<gsl::span<AudioConfig::SampleFormat>, AudioConfig::maxChannels>;

	class AudioSource {
	public:
		virtual ~AudioSource() {}

		virtual size_t getNumberOfChannels() const = 0;
		virtual bool isReady() const { return true; }
		virtual bool getAudioData(size_t numSamples, AudioSourceData& dst) = 0;
	};
}


#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include <gbpLib.h>
#include <gbpRNG.h>
#include <gbpMCMC.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_fit.h>
#include <gsl/gsl_interp.h>

void restart_MCMC(MCMC_info *MCMC) {
    SID_log("Resetting MCMC structure for a fresh run...", SID_LOG_OPEN);

    MCMC->flag_integrate_on     = GBP_FALSE;
    MCMC->flag_analysis_on      = GBP_TRUE;
    MCMC->first_map_call        = GBP_TRUE;
    MCMC->first_link_call       = GBP_TRUE;
    MCMC->flag_init_chain       = GBP_TRUE;
    MCMC->first_chain_call      = GBP_TRUE;
    MCMC->first_parameter_call  = GBP_TRUE;
    MCMC->first_likelihood_call = GBP_TRUE;
    MCMC->ln_likelihood_last    = 0.;
    MCMC->ln_likelihood_new     = 0.;
    MCMC->ln_likelihood_chain   = 0.;
    MCMC->n_success             = 0;
    MCMC->n_propositions        = 0;
    MCMC->n_map_calls           = 0;

    free_MCMC_covariance(MCMC);
    free_MCMC_arrays(MCMC);

    SID_log("Done.", SID_LOG_CLOSE);
}


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

#ifndef __GSL_MATRIX_COMPLEX_FLOAT_H__
#define __GSL_MATRIX_COMPLEX_FLOAT_H__

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

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_complex_float.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size1;
  size_t size2;
  size_t tda;
  float * data;
  gsl_block_complex_float * block;
  int owner;
} gsl_matrix_complex_float ;

typedef struct
{
  gsl_matrix_complex_float matrix;
} _gsl_matrix_complex_float_view;

typedef _gsl_matrix_complex_float_view gsl_matrix_complex_float_view;

typedef struct
{
  gsl_matrix_complex_float matrix;
} _gsl_matrix_complex_float_const_view;

typedef const _gsl_matrix_complex_float_const_view gsl_matrix_complex_float_const_view;

/* Allocation */

GSL_FUN gsl_matrix_complex_float *
gsl_matrix_complex_float_alloc (const size_t n1, const size_t n2);

GSL_FUN gsl_matrix_complex_float *
gsl_matrix_complex_float_calloc (const size_t n1, const size_t n2);

GSL_FUN gsl_matrix_complex_float *
gsl_matrix_complex_float_alloc_from_block (gsl_block_complex_float * b,
                                           const size_t offset,
                                           const size_t n1, const size_t n2, const size_t d2);

GSL_FUN gsl_matrix_complex_float *
gsl_matrix_complex_float_alloc_from_matrix (gsl_matrix_complex_float * b,
                                            const size_t k1, const size_t k2,
                                            const size_t n1, const size_t n2);

GSL_FUN gsl_vector_complex_float *
gsl_vector_complex_float_alloc_row_from_matrix (gsl_matrix_complex_float * m,
                                                const size_t i);

GSL_FUN gsl_vector_complex_float *
gsl_vector_complex_float_alloc_col_from_matrix (gsl_matrix_complex_float * m,
                                                const size_t j);

GSL_FUN void gsl_matrix_complex_float_free (gsl_matrix_complex_float * m);

/* Views */

GSL_FUN _gsl_matrix_complex_float_view
gsl_matrix_complex_float_submatrix (gsl_matrix_complex_float * m,
                            const size_t i, const size_t j,
                            const size_t n1, const size_t n2);

GSL_FUN _gsl_vector_complex_float_view
gsl_matrix_complex_float_row (gsl_matrix_complex_float * m, const size_t i);

GSL_FUN _gsl_vector_complex_float_view
gsl_matrix_complex_float_column (gsl_matrix_complex_float * m, const size_t j);

GSL_FUN _gsl_vector_complex_float_view
gsl_matrix_complex_float_diagonal (gsl_matrix_complex_float * m);

GSL_FUN _gsl_vector_complex_float_view
gsl_matrix_complex_float_subdiagonal (gsl_matrix_complex_float * m, const size_t k);

GSL_FUN _gsl_vector_complex_float_view
gsl_matrix_complex_float_superdiagonal (gsl_matrix_complex_float * m, const size_t k);

GSL_FUN _gsl_vector_complex_float_view
gsl_matrix_complex_float_subrow (gsl_matrix_complex_float * m,
                                 const size_t i, const size_t offset,
                                 const size_t n);

GSL_FUN _gsl_vector_complex_float_view
gsl_matrix_complex_float_subcolumn (gsl_matrix_complex_float * m,
                                    const size_t j, const size_t offset,
                                    const size_t n);

GSL_FUN _gsl_matrix_complex_float_view
gsl_matrix_complex_float_view_array (float * base,
                             const size_t n1,
                             const size_t n2);

GSL_FUN _gsl_matrix_complex_float_view
gsl_matrix_complex_float_view_array_with_tda (float * base,
                                      const size_t n1,
                                      const size_t n2,
                                      const size_t tda);

GSL_FUN _gsl_matrix_complex_float_view
gsl_matrix_complex_float_view_vector (gsl_vector_complex_float * v,
                              const size_t n1,
                              const size_t n2);

GSL_FUN _gsl_matrix_complex_float_view
gsl_matrix_complex_float_view_vector_with_tda (gsl_vector_complex_float * v,
                                       const size_t n1,
                                       const size_t n2,
                                       const size_t tda);

GSL_FUN _gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_submatrix (const gsl_matrix_complex_float * m,
                                  const size_t i, const size_t j,
                                  const size_t n1, const size_t n2);

GSL_FUN _gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_row (const gsl_matrix_complex_float * m,
                            const size_t i);

GSL_FUN _gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_column (const gsl_matrix_complex_float * m,
                               const size_t j);

GSL_FUN _gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_diagonal (const gsl_matrix_complex_float * m);

GSL_FUN _gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_subdiagonal (const gsl_matrix_complex_float * m,
                                    const size_t k);

GSL_FUN _gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_superdiagonal (const gsl_matrix_complex_float * m,
                                      const size_t k);

GSL_FUN _gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_subrow (const gsl_matrix_complex_float * m,
                                       const size_t i, const size_t offset,
                                       const size_t n);

GSL_FUN _gsl_vector_complex_float_const_view
gsl_matrix_complex_float_const_subcolumn (const gsl_matrix_complex_float * m,
                                          const size_t j, const size_t offset,
                                          const size_t n);

GSL_FUN _gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_view_array (const float * base,
                                   const size_t n1,
                                   const size_t n2);

GSL_FUN _gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_view_array_with_tda (const float * base,
                                            const size_t n1,
                                            const size_t n2,
                                            const size_t tda);

GSL_FUN _gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_view_vector (const gsl_vector_complex_float * v,
                                    const size_t n1,
                                    const size_t n2);

GSL_FUN _gsl_matrix_complex_float_const_view
gsl_matrix_complex_float_const_view_vector_with_tda (const gsl_vector_complex_float * v,
                                             const size_t n1,
                                             const size_t n2,
                                             const size_t tda);

/* Operations */

GSL_FUN void gsl_matrix_complex_float_set_zero (gsl_matrix_complex_float * m);
GSL_FUN void gsl_matrix_complex_float_set_identity (gsl_matrix_complex_float * m);
GSL_FUN void gsl_matrix_complex_float_set_all (gsl_matrix_complex_float * m, gsl_complex_float x);

GSL_FUN int gsl_matrix_complex_float_fread (FILE * stream, gsl_matrix_complex_float * m) ;
GSL_FUN int gsl_matrix_complex_float_fwrite (FILE * stream, const gsl_matrix_complex_float * m) ;
GSL_FUN int gsl_matrix_complex_float_fscanf (FILE * stream, gsl_matrix_complex_float * m);
GSL_FUN int gsl_matrix_complex_float_fprintf (FILE * stream, const gsl_matrix_complex_float * m, const char * format);

GSL_FUN int gsl_matrix_complex_float_memcpy(gsl_matrix_complex_float * dest, const gsl_matrix_complex_float * src);
GSL_FUN int gsl_matrix_complex_float_swap(gsl_matrix_complex_float * m1, gsl_matrix_complex_float * m2);

GSL_FUN int gsl_matrix_complex_float_swap_rows(gsl_matrix_complex_float * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_complex_float_swap_columns(gsl_matrix_complex_float * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_complex_float_swap_rowcol(gsl_matrix_complex_float * m, const size_t i, const size_t j);

GSL_FUN int gsl_matrix_complex_float_transpose (gsl_matrix_complex_float * m);
GSL_FUN int gsl_matrix_complex_float_transpose_memcpy (gsl_matrix_complex_float * dest, const gsl_matrix_complex_float * src);

GSL_FUN int gsl_matrix_complex_float_isnull (const gsl_matrix_complex_float * m);
GSL_FUN int gsl_matrix_complex_float_ispos (const gsl_matrix_complex_float * m);
GSL_FUN int gsl_matrix_complex_float_isneg (const gsl_matrix_complex_float * m);
GSL_FUN int gsl_matrix_complex_float_isnonneg (const gsl_matrix_complex_float * m);

GSL_FUN int gsl_matrix_complex_float_add (gsl_matrix_complex_float * a, const gsl_matrix_complex_float * b);
GSL_FUN int gsl_matrix_complex_float_sub (gsl_matrix_complex_float * a, const gsl_matrix_complex_float * b);
GSL_FUN int gsl_matrix_complex_float_mul_elements (gsl_matrix_complex_float * a, const gsl_matrix_complex_float * b);
GSL_FUN int gsl_matrix_complex_float_div_elements (gsl_matrix_complex_float * a, const gsl_matrix_complex_float * b);
GSL_FUN int gsl_matrix_complex_float_scale (gsl_matrix_complex_float * a, const gsl_complex_float x);
GSL_FUN int gsl_matrix_complex_float_add_constant (gsl_matrix_complex_float * a, const gsl_complex_float x);
GSL_FUN int gsl_matrix_complex_float_add_diagonal (gsl_matrix_complex_float * a, const gsl_complex_float x);

/***********************************************************************/
/* The functions below are obsolete                                    */
/***********************************************************************/
GSL_FUN int gsl_matrix_complex_float_get_row(gsl_vector_complex_float * v, const gsl_matrix_complex_float * m, const size_t i);
GSL_FUN int gsl_matrix_complex_float_get_col(gsl_vector_complex_float * v, const gsl_matrix_complex_float * m, const size_t j);
GSL_FUN int gsl_matrix_complex_float_set_row(gsl_matrix_complex_float * m, const size_t i, const gsl_vector_complex_float * v);
GSL_FUN int gsl_matrix_complex_float_set_col(gsl_matrix_complex_float * m, const size_t j, const gsl_vector_complex_float * v);
/***********************************************************************/

/* inline functions if you are using GCC */

GSL_FUN INLINE_DECL gsl_complex_float gsl_matrix_complex_float_get(const gsl_matrix_complex_float * m, const size_t i, const size_t j);
GSL_FUN INLINE_DECL void gsl_matrix_complex_float_set(gsl_matrix_complex_float * m, const size_t i, const size_t j, const gsl_complex_float x);

GSL_FUN INLINE_DECL gsl_complex_float * gsl_matrix_complex_float_ptr(gsl_matrix_complex_float * m, const size_t i, const size_t j);
GSL_FUN INLINE_DECL const gsl_complex_float * gsl_matrix_complex_float_const_ptr(const gsl_matrix_complex_float * m, const size_t i, const size_t j);

#ifdef HAVE_INLINE

INLINE_FUN
gsl_complex_float
gsl_matrix_complex_float_get(const gsl_matrix_complex_float * m,
                     const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      gsl_complex_float zero = {{0,0}};

      if (i >= m->size1)
        {
          GSL_ERROR_VAL("first index out of range", GSL_EINVAL, zero) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_VAL("second index out of range", GSL_EINVAL, zero) ;
        }
    }
#endif
  return *(gsl_complex_float *)(m->data + 2*(i * m->tda + j)) ;
}

INLINE_FUN
void
gsl_matrix_complex_float_set(gsl_matrix_complex_float * m,
                     const size_t i, const size_t j, const gsl_complex_float x)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_VOID("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_VOID("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  *(gsl_complex_float *)(m->data + 2*(i * m->tda + j)) = x ;
}

INLINE_FUN
gsl_complex_float *
gsl_matrix_complex_float_ptr(gsl_matrix_complex_float * m,
                             const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  return (gsl_complex_float *)(m->data + 2*(i * m->tda + j)) ;
}

INLINE_FUN
const gsl_complex_float *
gsl_matrix_complex_float_const_ptr(const gsl_matrix_complex_float * m,
                                   const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  return (const gsl_complex_float *)(m->data + 2*(i * m->tda + j)) ;
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_MATRIX_COMPLEX_FLOAT_H__ */


#ifndef __SAMPLER_H__
#define __SAMPLER_H__

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

class Sampler {

  dsfmt_t dsfmt;

  void init_random(unsigned params_random_seed) {
#ifdef USE_MT_RANDOM
  dsfmt_init_gen_rand(&dsfmt, params_random_seed);
#else
  srand(params_random_seed);
#endif
  };

 protected:

  /* gsl random number generator */
  gsl_rng* _gsl_rng;

  /* default constructor */
  Sampler(unsigned int random_seed) {
    /* do i still need this ? */
    init_random(random_seed);
    /* init gsl random number generator */
    _gsl_rng = gsl_rng_alloc( gsl_rng_taus );
  };

  double sample_uniform();
  unsigned long int sample_integer_uniform(unsigned int from, unsigned int to);

};

#endif


#pragma once

#include "schedule.h"
#include <gsl/gsl-lite.hpp>
#include <range/v3/view/span.hpp>
#include <vector>

namespace angonoka::stun {
using ranges::span;
struct ScheduleParams;

/**
    Makespan estimator.

    Further reading:
    https://en.wikipedia.org/wiki/Makespan
*/
class Makespan {
public:
    /**
        Constructor.

        @param params       An instance of ScheduleParams
        @param tasks_count  Total number of tasks
        @param agents_count Total number of agents
    */
    Makespan(const ScheduleParams& params);

    Makespan(const Makespan& other);
    Makespan& operator=(const Makespan& other) noexcept;
    Makespan(Makespan&& other) noexcept;
    Makespan& operator=(Makespan&& other) noexcept;
    ~Makespan() noexcept;

    /**
        Calculate the makespan of a given scheduling configuration.

        @param schedule Scheduling configuration

        @return Makespan in seconds
    */
    float operator()(Schedule schedule) noexcept;

    /**
        Get the current ScheduleParams object.

        @return Schedule parameters.
    */
    [[nodiscard]] const ScheduleParams& params() const;

    /**
        Set the ScheduleParams object.

        @param params ScheduleParams object
    */
    void params(const ScheduleParams& params);

private:
    struct Impl;
    gsl::not_null<const ScheduleParams*> params_;
    std::vector<float> sum_buffer;
    span<float> task_done;
    span<float> work_done;
};

class RandomUtils;

/**
    Shuffle tasks and agents in-place.

    Randomly swaps two adjacent tasks within the schedule and
    reassigns an agent of a random task.
*/
class Mutator {
public:
    /**
        Mutator options.

        @var params Schedule parameters
        @var random Random utils.
    */
    struct Options {
        gsl::not_null<const ScheduleParams*> params;
        gsl::not_null<RandomUtils*> random;
    };

    /**
        Constructor.

        @param params An instance of ScheduleParams
        @param random An instance of RandomUtils
    */
    Mutator(const ScheduleParams& params, RandomUtils& random);
    Mutator(const Options& options);

    /**
        Mutates the scheduling configuration in-place.

        @param schedule Scheduling configuration
    */
    void operator()(MutSchedule schedule) const noexcept;

    /**
        Get current options.

        @return Options.
    */
    [[nodiscard]] Options options() const;

    /**
        Set options.

        @param options Options.
    */
    void options(const Options& options);

private:
    struct Impl;
    gsl::not_null<const ScheduleParams*> params;
    gsl::not_null<RandomUtils*> random;
};
} // namespace angonoka::stun


/*
 * Implement Heap sort -- direct and indirect sorting
 * Based on descriptions in Sedgewick "Algorithms in C"
 *
 * Copyright (C) 1999  Thomas Walter
 *
 * 18 February 2000: Modified for GSL by Brian Gough
 *
 * This 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, or (at your option) any
 * later version.
 *
 * This source 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.
 */

#include <config.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_sort.h>
#include <gsl/gsl_sort_vector.h>

#define BASE_LONG_DOUBLE
#include "templates_on.h"
#include "sortvecind_source.c"
#include "templates_off.h"
#undef  BASE_LONG_DOUBLE

#define BASE_DOUBLE
#include "templates_on.h"
#include "sortvecind_source.c"
#include "templates_off.h"
#undef  BASE_DOUBLE

#define BASE_FLOAT
#include "templates_on.h"
#include "sortvecind_source.c"
#include "templates_off.h"
#undef  BASE_FLOAT

#define BASE_ULONG
#include "templates_on.h"
#include "sortvecind_source.c"
#include "templates_off.h"
#undef  BASE_ULONG

#define BASE_LONG
#include "templates_on.h"
#include "sortvecind_source.c"
#include "templates_off.h"
#undef  BASE_LONG

#define BASE_UINT
#include "templates_on.h"
#include "sortvecind_source.c"
#include "templates_off.h"
#undef  BASE_UINT

#define BASE_INT
#include "templates_on.h"
#include "sortvecind_source.c"
#include "templates_off.h"
#undef  BASE_INT

#define BASE_USHORT
#include "templates_on.h"
#include "sortvecind_source.c"
#include "templates_off.h"
#undef  BASE_USHORT

#define BASE_SHORT
#include "templates_on.h"
#include "sortvecind_source.c"
#include "templates_off.h"
#undef  BASE_SHORT

#define BASE_UCHAR
#include "templates_on.h"
#include "sortvecind_source.c"
#include "templates_off.h"
#undef  BASE_UCHAR

#define BASE_CHAR
#include "templates_on.h"
#include "sortvecind_source.c"
#include "templates_off.h"
#undef  BASE_CHAR


#pragma once

#include <type_traits>

#include <cuda/define_specifiers.hpp>
#include <cuda/runtime_api.hpp>

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

#include <thrustshift/fill.h>

namespace thrustshift {

namespace kernel {

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

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

template <typename SrcT, typename DstT, typename T, typename I>
__global__ void copy_find(gsl_lite::span<const SrcT> src,
                          gsl_lite::span<DstT> dst,
                          T value,
                          I* pos) {

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

} // namespace kernel

namespace async {

//! thrust uses sometimes a cudaMemcpyAsync instead of a copy kernel
template <class SrcRange, class DstRange>
void copy(cuda::stream_t& stream, SrcRange&& src, DstRange&& dst) {
	gsl_Expects(src.size() == dst.size());

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

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

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

//! Copy and search for an element. If the element occurs more than once, it
//! is undefined which of the valid positions is returned. If the element does
//! not occur `pos` is unchanged.
template <class SrcRange, class DstRange, typename T, typename I>
void copy_find(cuda::stream_t& stream,
               SrcRange&& src,
               DstRange&& dst,
               const T& value,
               I* pos) {
	gsl_Expects(src.size() == dst.size());
	gsl_Expects(pos != nullptr);

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

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

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

} // namespace async

namespace detail {

template <int BLOCK_DIM,
          int NUM_ELEMENTS,
          int LD_FIRST = NUM_ELEMENTS,
          int LD_RESULT = NUM_ELEMENTS,
          int NUM_PER_ROW_FIRST = NUM_ELEMENTS,
          int NUM_PER_ROW_RESULT = NUM_ELEMENTS>
struct helper_t {
	template <class iteratorA_t, class iteratorB_t, class F>
	CUDA_FHD static void block_copy_even(iteratorA_t first,
	                                     iteratorB_t result,
	                                     int tid,
	                                     F f) {
		if (BLOCK_DIM < NUM_ELEMENTS) {
// pragma unroll is device code specific
#ifdef __CUDA_ARCH__
#pragma unroll
#endif
			for (int i = 0; i < NUM_ELEMENTS - BLOCK_DIM; i += BLOCK_DIM) {
				f(first,
				  result,
				  i + ((i + tid) / NUM_PER_ROW_FIRST) *
				          (LD_FIRST - NUM_PER_ROW_FIRST) + tid,
				  i + ((i + tid) / NUM_PER_ROW_RESULT) *
				          (LD_RESULT - NUM_PER_ROW_RESULT) + tid);
			}
		}
	}

	template <class iteratorA_t, class iteratorB_t, class F>
	CUDA_FHD static void block_copy_tail(iteratorA_t first,
	                                     iteratorB_t result,
	                                     int tid,
	                                     F f) {
		if (NUM_ELEMENTS % BLOCK_DIM == 0) {
			f(first,
			  result,
			  (NUM_ELEMENTS - BLOCK_DIM) +
			      (((NUM_ELEMENTS - BLOCK_DIM) + tid) / NUM_PER_ROW_FIRST) *
			          (LD_FIRST - NUM_PER_ROW_FIRST) + tid,
			  (NUM_ELEMENTS - BLOCK_DIM) +
			      (((NUM_ELEMENTS - BLOCK_DIM) + tid) / NUM_PER_ROW_RESULT) *
			          (LD_RESULT - NUM_PER_ROW_RESULT) + tid);
		}
		else if (tid + (NUM_ELEMENTS / BLOCK_DIM) * BLOCK_DIM < NUM_ELEMENTS) {
			constexpr int j = (NUM_ELEMENTS / BLOCK_DIM) * BLOCK_DIM;
			f(first,
			  result,
			  j + ((j + tid) / NUM_PER_ROW_FIRST) *
			          (LD_FIRST - NUM_PER_ROW_FIRST) + tid,
			  j + ((j + tid) / NUM_PER_ROW_RESULT) *
			          (LD_RESULT - NUM_PER_ROW_RESULT) + tid);
		}
	}
}; // helper

template <int BLOCK_DIM, int NUM_ELEMENTS>
struct helper_t<BLOCK_DIM,
                NUM_ELEMENTS,
                NUM_ELEMENTS,
                NUM_ELEMENTS,
                NUM_ELEMENTS,
                NUM_ELEMENTS> {
	template <class iteratorA_t, class iteratorB_t, class F>
	CUDA_FHD static void block_copy_even(iteratorA_t first,
	                                     iteratorB_t result,
	                                     int tid,
	                                     F f) {
		if (BLOCK_DIM < NUM_ELEMENTS) {
// pragma unroll is device code specific
#ifdef __CUDA_ARCH__
#pragma unroll
#endif
			for (int i = 0; i < NUM_ELEMENTS - BLOCK_DIM; i += BLOCK_DIM) {
				const int j = i + tid;
				f(first, result, j, j);
			}
		}
	}

	template <class iteratorA_t, class iteratorB_t, class F>
	CUDA_FHD static void block_copy_tail(iteratorA_t first,
	                                     iteratorB_t result,
	                                     int tid,
	                                     F f) {
		if (NUM_ELEMENTS % BLOCK_DIM == 0) {
			const int j = NUM_ELEMENTS - BLOCK_DIM + tid;
			f(first,
			  result,
			  j,
			  j);
		}
		else if (tid + (NUM_ELEMENTS / BLOCK_DIM) * BLOCK_DIM < NUM_ELEMENTS) {
			const int j = (NUM_ELEMENTS / BLOCK_DIM) * BLOCK_DIM + tid;
			f(first, result, j, j);
		}
	}
}; // struct helper_t

//! Specialization for equal block dimension and number of elements
template <int BD_AND_NE>
struct helper_t<BD_AND_NE,
                BD_AND_NE,
                BD_AND_NE,
                BD_AND_NE,
                BD_AND_NE,
                BD_AND_NE> {
	template <class iteratorA_t, class iteratorB_t, class F>
	CUDA_FHD static void block_copy_even(iteratorA_t first,
	                                     iteratorB_t result,
	                                     int tid,
	                                     F f) {
	}

	template <class iteratorA_t, class iteratorB_t, class F>
	CUDA_FHD static void block_copy_tail(iteratorA_t first,
	                                     iteratorB_t result,
	                                     int tid,
	                                     F f) {
		f(first, result, tid, tid);
	}

}; // struct helper_t

}; // namespace detail

/* \brief Copy data blockwise with efficient loop unrolling.
 *
 * This function implements an efficient memory copy. This is in particular
 * useful if you use only a few threads to copy data, since this is limited
 * by register dependencies if trivially implemented. The algorithm is
 * implemented in two steps. First we copy elements subsequently and
 * block wise with the whole thread block:
 *
 *     N = 11, bdim = 4: x x x x | x x x x | o o o
 *     N = 12, bdim = 4: x x x x | x x x x | o o o o
 *     N = 13, bdim = 4: x x x x | x x x x | x x x x | o
 *
 * Thus the last elements are not copied (denoted by `o`).
 * Afterwards we must copy the remaining elements, which is
 * trivial if N % bdim == 0.
 *
 * \param BLOCK_DIM Current thread block dimension
 * \param NUM_ELEMENTS total amount of elements to copy
 * \param LD_RESULT Leading dimension on result. Only used in case of two dimensional
 *        arranged data. This is equal to the number of elements, which
 *        must be skipped to get an element in the next row. This is
 *        useful if you want to skip some of the columns of the 2D data:
 *
 *           |    ld     |
 *            x x x x o o
 *            x x x x o o
 *            x x x x o o
 *            x x x x o o
 *           |  nepr |
 *
 *        `nepr` = num elements per row
 *        `ld` = leading dimension
 *        If you want to copy only the `x` elements.
 *
 * \param NUM_PER_ROW Only used in case of two dimensional data copies
 * \param first iterator to first read element
 * \param result iterator to first result element
 * \param f A functor to write the data, e.g.
 *      ```
 *      auto default_f = [](iteratorA_t first,
 *                          iteratorB_t result,
 *                          int i_first,
 *                          int i_result) {
 *          result[i_result] = first[i_first];
 *      };
 *      ```
 *     `i_first` and `i_result` are the global indices on iterator `first` and `result`. With such
 *     a functor it is possible to modify the read and write process. E.g. you can read
 *     the data in a permuted way.
 *
 */
template <int BLOCK_DIM,
          int NUM_ELEMENTS,
          class iteratorA_t,
          class iteratorB_t,
          int LD_FIRST = NUM_ELEMENTS,
          int LD_RESULT = NUM_ELEMENTS,
          int NUM_PER_ROW_FIRST = NUM_ELEMENTS,
          int NUM_PER_ROW_RESULT = NUM_ELEMENTS,
          class F>
CUDA_FHD void block_copy(iteratorA_t first,
                         iteratorB_t result,
                         int tid,
                         F f) {

	detail::helper_t<BLOCK_DIM,
	                 NUM_ELEMENTS,
	                 LD_FIRST,
	                 LD_RESULT,
	                 NUM_PER_ROW_FIRST,
	                 NUM_PER_ROW_RESULT>::block_copy_even(first,
	                                                      result,
	                                                      tid,
	                                                      f);
	detail::helper_t<BLOCK_DIM,
	                 NUM_ELEMENTS,
	                 LD_FIRST,
	                 LD_RESULT,
	                 NUM_PER_ROW_FIRST,
	                 NUM_PER_ROW_RESULT>::block_copy_tail(first,
	                                                      result,
	                                                      tid,
	                                                      f);
}

template <int BLOCK_DIM,
          int NUM_ELEMENTS,
          int LD_FIRST = NUM_ELEMENTS,
          int LD_RESULT = NUM_ELEMENTS,
          int NUM_PER_ROW_FIRST = NUM_ELEMENTS,
          int NUM_PER_ROW_RESULT = NUM_ELEMENTS,
          class iteratorA_t,
          class iteratorB_t>
CUDA_FHD void block_copy(iteratorA_t first,
                         iteratorB_t result,
                         int tid = threadIdx.x) {

	auto default_f = [](iteratorA_t first,
	                    iteratorB_t result,
	                    int i_first,
	                    int i_result) {
		result[i_result] = first[i_first];
	};

	block_copy<BLOCK_DIM,
	           NUM_ELEMENTS,
	           iteratorA_t,
	           iteratorB_t,
	           LD_FIRST,
	           LD_RESULT,
	           NUM_PER_ROW_FIRST,
	           NUM_PER_ROW_RESULT>(first, result, tid, default_f);
}

//! copy data without loops unrolled
template <class iteratorA_t, class iteratorB_t>
CUDA_FHD void block_copy(iteratorA_t first,
                        int num_elements,
                        iteratorB_t result,
                        int group_dim,
                        int ld_first,
                        int ld_result,
                        int num_per_row_first,
                        int num_per_row_result,
                        int tid) {
	auto f = [](iteratorA_t first,
	            iteratorB_t result,
	            int i_first,
	            int i_result) { result[i_result] = first[i_first]; };
	//////////
	// HEAD //
	//////////
	if (group_dim < num_elements) {
		for (int i = 0; i < num_elements - group_dim; i += group_dim) {
			f(first,
			  result,
			  i + ((i + tid) / num_per_row_first) *
			          (ld_first - num_per_row_first) + tid,
			  i + ((i + tid) / num_per_row_result) *
			          (ld_result - num_per_row_result) + tid);
		}
	}
	//////////
	// TAIL //
	//////////
	if (num_elements % group_dim == 0) {
		f(first,
		  result,
		  (num_elements - group_dim) +
		      (((num_elements - group_dim) + tid) / num_per_row_first) *
		          (ld_first - num_per_row_first) + tid,
		  (num_elements - group_dim) +
		      (((num_elements - group_dim) + tid) / num_per_row_result) *
		          (ld_result - num_per_row_result) + tid);
	}
	else if (tid + (num_elements / group_dim) * group_dim < num_elements) {
		const int j = (num_elements / group_dim) * group_dim;
		f(first,
		  result,
		  j + ((j + tid) / num_per_row_first) * (ld_first - num_per_row_first) + tid,
		  j + ((j + tid) / num_per_row_result) *
		          (ld_result - num_per_row_result) + tid);
	}
}

} // namespace thrustshift


#ifndef DEINOS_ALGORITHM_H
#define DEINOS_ALGORITHM_H
#include "chess.h"
#include <vector>
#include <array>
#include <gsl/pointers>
#include <mutex>
#include <variant>
#include <functional>
#include <string>
#include <thread>
#include <future>
#include <condition_variable>

//memory consmption ideas:
//can halve Move size by removing piece type tracking and putting boold inside promotion piece
//can reduce AnalysedPosition size by doubling up control inside one byte
//perhaps dynamically delete parts of the tree e.g. only positions and regenerate if necessary
//deletion thread?

//Other
//use gsl::index in for loops?

namespace algorithm {
	class AnalysedPosition{
	public:
		constexpr AnalysedPosition() = default;
		explicit AnalysedPosition(const chess::Position&); //generate from scratch

		void advance_by(chess::MoveRecord);
		struct occlusion_info {
			std::array<std::array<chess::Square, 16>, 2> squares;
			std::array<int, 2> counts = {0};
		};
		AnalysedPosition::occlusion_info get_occlusion(chess::MoveRecord);

		inline const chess::Position& pos() const {return m_position;}
		inline uint8_t ctrl(chess::Almnt a, chess::Square s) const {return m_control[chess::as_index(a)].get(s.file(), s.rank());}
		inline const std::vector<chess::MoveRecord>& moves(chess::Almnt a) const {return m_moves[chess::as_index(a)];}
		inline const std::vector<chess::MoveRecord>& moves() const {return moves(pos().to_move());}
		inline chess::Move get_move(int index) const {return chess::Move(pos(), moves().at(index));}
		inline const chess::Square king_sq(chess::Almnt a) const {return m_king_sq[chess::as_index(a)];}
		inline bool in_check(chess::Almnt a) const {return ctrl(!a, king_sq(a)) > 0;}
		inline bool legal_check() const {return in_check(pos().to_move());}
		inline bool illegal_check() const {return in_check(!pos().to_move());}
		std::optional<chess::Move> find_record(const std::string& name) const;
		friend std::ostream& operator<<(std::ostream& os, const AnalysedPosition& ap);

	private:
		void append_calculation(chess::Square start); //calculate data associated with this square and append to state (for initialisation)
		void append_castling();
		std::array<chess::Square, 2> m_king_sq;
		chess::Position m_position;
		std::array<chess::HalfByteBoard, 2> m_control;
		std::array<std::vector<chess::MoveRecord>, 2> m_moves = {};
	};
	std::ostream& operator<<(std::ostream& os, const AnalysedPosition& ap);

	class Node;

	struct Edge {
		//std::mutex ptr_mutex;
		float total_value;
		int visits = 0;
		//bool legal = true;
		inline bool legal() const {return visits != -1;}
		std::unique_ptr<Node> node = nullptr;
	};

	struct EdgeDatum {
		float total_value = 0.0;
		int visits = 0;
		bool legal = true;
	};

	struct EdgeData {
		EdgeData(int moves_num) : edges(std::vector<EdgeDatum>(moves_num)) {}
		int total_n = 1;
		std::vector<EdgeDatum> edges;
	};

	class Node { //TODO
	public:
		explicit Node(std::unique_ptr<const AnalysedPosition> t_apos);
		explicit Node(const AnalysedPosition& t_apos);
		int preferred_index();
		std::unique_ptr<Node>* find_child(const std::string& fen);
		std::unique_ptr<Node>* find_child(const chess::Move& mv);
		inline Node* child(int index) const {return edges[index].node.get();}
		const chess::Move best_move() {return chess::Move(apos->pos(), apos->moves()[preferred_index()]);}
		inline std::optional<chess::GameResult> result() const {return m_result;}
		//inline int res_dist() const {return m_res_dist;} //TODO
		std::string display() const;

		inline int total_n() const {return m_total_n;} //synchronise?

		const std::unique_ptr<const AnalysedPosition> apos;
		const bool white_to_play;

	private:
		void update(int index, float t_value);
		void increment_n();
		void set_illegal(int index);

		std::optional<chess::GameResult> m_result = std::nullopt;
		//int m_res_dist = 0; //TODO
		//std::mutex data_mutex2;
		//EdgeData data;
		int m_total_n = 1;
		std::vector<Edge> edges;
		//std::vector<Edge> edges2;
		std::mutex data_mutex; //40B

		std::array<uint8_t, 3> node_prefetch = {0};

		friend class Tree;
	};

	class Tree {
	public:
		Tree(const AnalysedPosition& base_apos, std::function<float(const AnalysedPosition&)> t_value_fn,
			std::function<float(const AnalysedPosition&, const chess::Move&)> t_prior_fn, float t_expl_c = 0.2)
			: base(std::make_unique<Node>(base_apos)), value_fn(t_value_fn), prior_fn(t_prior_fn), expl_c(t_expl_c) {}
		void search(bool update_prefetch = false);
		std::unique_ptr<Node> base;
		std::function<float(const AnalysedPosition&)> value_fn;
		std::function<float(const AnalysedPosition&, const chess::Move&)> prior_fn;
		float expl_c = 0.2; //exploration coefficient

	private:
		float evaluate_node(Node& node); //used in search() for recursion
		void update_node(int index, float t_value);
		std::optional<int> edge_to_search(Node& node);
		void update_prefetch(Node& node);
	};

	class TreeEngine {
	public:
		TreeEngine(
			const AnalysedPosition& initial_position,
			std::function<float(const AnalysedPosition&)> t_value_fn,
			std::function<float(const AnalysedPosition&, const chess::Move&)> t_prior_fn,
			float exploration_coefficient);

		~TreeEngine() {
			halt_promise.set_value();
			for (auto& t : m_threads) t.join();
		}

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

		//void start();
		const chess::Move choose_move(); //maybe add exploration?
		bool advance_to(const std::string& fen);
		bool advance_by(const chess::Move& mv); //TODO
		std::string display() const; //TODO

		inline int total_n() const {return m_tree.base->total_n();};

	private:
		bool should_pause();
		void pause(); //blocks until all threads are paused
		void resume(); //blocks until all threads are resumed

		Tree m_tree;
		std::promise<void> halt_promise;
		std::mutex pause_mx;
		std::condition_variable pause_cv;
		bool pause_bool = false;
		int pause_count = 0;
		std::vector<std::thread> m_threads;
	};
}
#endif