///
/// @file
///
/// @brief This file contains functions and macros that are used in the sanity
/// checks.
///
/// @author Mirko Myllykoski (mirkom@cs.umu.se), Umeå University
///
/// @internal LICENSE
///
/// Copyright (c) 2019-2020, Umeå Universitet
///
/// Redistribution and use in source and binary forms, with or without
/// modification, are permitted provided that the following conditions are met:
///
/// 1. Redistributions of source code must retain the above copyright notice,
///    this list of conditions and the following disclaimer.
///
/// 2. 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.
///
/// 3. Neither the name of the copyright holder nor the names of its
///    contributors may be used to endorse or promote products derived from this
///    software without specific prior written permission.
///
/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
/// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
/// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
/// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
/// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
/// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
/// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
/// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
/// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
/// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
/// POSSIBILITY OF SUCH DAMAGE.
///

#ifndef STARNEIG_COMMON_SANITY
#define STARNEIG_COMMON_SANITY

#include <starneig_config.h>
#include <starneig/configuration.h>

#ifdef STARNEIG_ENABLE_SANITY_CHECKS

#include "math.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <cblas.h>

///
/// @brief Reports a sanity check error. Aborts the program.
///
/// @param[in] message
///         Message.
///
#define STARNEIG_SANITY_REPORT(message) { \
    fprintf(stderr, "[starneig][sanity] %s:%d: %s\n", \
        __FILE__, __LINE__, message); \
    abort(); \
}

///
/// @brief Reports a sanity check error. Aborts the program.
///
/// @param[in] message
///         printf formatted message.
///
/// @param[in] ...
///         Additional printf compatible arguments.
///
#define STARNEIG_SANITY_REPORT_ARGS(message, ...) { \
    fprintf(stderr, "[starneig][sanity] %s:%d: %s\n", \
        __FILE__, __LINE__, message, __VA_ARGS__); \
    abort(); \
}

///
/// @brief Checks a conditional statement and reports a sanity check error if
/// the condition is not satisfied. Aborts the program.
///
/// @param[in] cond
///         The conditional statement.
///
/// @param[in] message
///         Message.
///
#define STARNEIG_SANITY_CHECK(cond, message) { \
    if (!(cond)) { \
        fprintf(stderr, "[starneig][sanity] %s:%d: %s\n", \
            __FILE__, __LINE__, message); \
        abort(); \
    } \
}

///
/// @brief Checks a conditional statement and reports a sanity check error if
/// the condition is not satisfied. Aborts the program.
///
/// @param[in] cond
///         The conditional statement.
///
/// @param[in] message
///         printf formatted message.
///
/// @param[in] ...
///         Additional printf compatible arguments.
///
#define STARNEIG_SANITY_CHECK_ARGS(cond, message, ...) { \
    if (!(cond)) { \
        fprintf(stderr, "[starneig][sanity] %s:%d: %s\n", \
            __FILE__, __LINE__, message, __VA_ARGS__); \
        abort(); \
    } \
}

static inline void starneig_sanity_check_inf(
    int rbegin, int rend, int cbegin, int cend, int ldA, double const *A,
    char const *mat, char const *file, int line)
{
    if (A == NULL)
        return;

    for (int i = cbegin; i < cend; i++) {
        for (int j = rbegin; j < rend; j++) {
            if (isinf(A[i*ldA+j])) {
                fprintf(stderr,
                    "[starneig][sanity] %s:%d: Matrix %s has an infinite "
                    "element.\n", file, line, mat);
                abort();
            }
            if (isnan(A[i*ldA+j])) {
                fprintf(stderr,
                    "[starneig][sanity] %s:%d: Matrix %s has a NaN element.\n",
                    file, line, mat);
                abort();
            }
        }
    }
}

#define STARNEIG_SANITY_CHECK_INF(rbegin, rend, cbegin, cend, ldA, A, mat) \
    starneig_sanity_check_inf(rbegin, rend, cbegin, cend, ldA, A, mat, \
        __FILE__, __LINE__)

static inline void starneig_sanity_check_multiplicities(
    int n, double *real, double *imag, char const *file, int line)
{
    for (int i = 0; i < n; i++) {
        double lambda_re = real[i];
        double lambda_im = imag[i];

        if (lambda_im == 0.0) { // real eigenvalue
            for (int j = i+1; j < n; j++) {
                if (real[j] == lambda_re && imag[j] == 0.0) {
                    fprintf(stderr,
                        "[starneig][sanity] %s:%d: Multiple eigenvalues at "
                        "S(%d,%d) and S(%d,%d).\n", file, line, i, i, j, j);
                    abort();
                }
            }
        }
        else { // complex eigenvalue
            for (int j = i+1; j < n; j++) {
                if (real[j] == lambda_re && imag[j] == lambda_im) {
                    fprintf(stderr,
                        "[starneig][sanity] %s:%d: Multiple eigenvalues at "
                        "S(%d:%d,%d:%d) and S(%d:%d,%d:%d).\n",
                        file, line, i, i+1, i, i+1, j, j+1, j, j+1);
                    abort();
                }
            }
        }
    }
}

///
/// @brief A sanity check that checks for multiple eigenvalues.
///
/// @param[in] n
///         The length of the vectors real and imag.
///
/// @param[in] real
///         A vector that contains the real parts of the eigenvalues.
///
/// @param[in] imag
///         A vector that contains the imaginary parts of the eigenvalues.
///
#define STARNEIG_SANITY_CHECK_MULTIPLICITIES(n, real, imag) \
    starneig_sanity_check_multiplicities(n, real, imag, __FILE__, __LINE__)

static inline void starneig_sanity_check_orthogonality(
    int n, int ldQ, double const *Q, char const *mat, char const *file,
    int line)
{
    if (Q == NULL)
        return;

    starneig_sanity_check_inf(0, n, 0, n, ldQ, Q, mat, file, line);

    size_t ldT;
    double *T = starneig_alloc_matrix(n, n, sizeof(double), &ldT);

    cblas_dgemm(CblasColMajor, CblasNoTrans, CblasTrans, n, n, n, 1.0,
        Q, ldQ, Q, ldQ, 0.0, T, ldT);

    double dot = 0.0;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            dot += squ(T[i*ldT+j] - (i == j ? 1.0 : 0.0));

    double norm = ((long long)1<<52) * sqrt(dot)/sqrt(n);

    if (10000 < norm || isnan(norm)) {
        fprintf(stderr,
            "[starneig][sanity] %s:%d: Matrix %s is not orthogonal.\n",
            file, line, mat);
        fprintf(stderr,
            "[starneig][sanity] %s:%d: |%s %s^T - I| / |I| = %.0f u.\n",
            file, line, mat, mat, norm);
        abort();
    }

    starneig_free_matrix(T);
}

///
/// @brief A sanity check that makes sure a matrix Q is orthogonal.
///
/// @param[in] n
///         The order of the matrix Q.
///
/// @param[in] ldQ
///         The leading dimension of the matrix Q.
///
/// @param[in] Q
///         The matrix Q.
///
/// @param[in] mat
///         A string that identifies the matrix Q.
///
#define STARNEIG_SANITY_CHECK_ORTHOGONALITY(n, ldQ, Q, mat) \
    starneig_sanity_check_orthogonality(n, ldQ, Q, mat, __FILE__, __LINE__)

struct starneig_sanity_check_args {
    double *A; size_t ldA;
    double *B; size_t ldB;
};

static inline struct starneig_sanity_check_args *
starneig_sanity_check_residuals_begin(
    int n, int ldQ, int ldZ, int ldA, int ldB, double const *Q, double const *Z,
    double const *A, double const *B, char const *file, int line)
{
    starneig_sanity_check_inf(0, n, 0, n, ldA, A, "A", file, line);
    starneig_sanity_check_inf(0, n, 0, n, ldB, B, "B", file, line);
    starneig_sanity_check_orthogonality(n, ldQ, Q, "Q", file, line);
    starneig_sanity_check_orthogonality(n, ldZ, Z, "Z", file, line);

    if (Z == NULL) {
        Z = Q; ldZ = ldQ;
    }

    struct starneig_sanity_check_args *ret =
        malloc(sizeof(struct starneig_sanity_check_args));

    size_t ldT;
    double *T = starneig_alloc_matrix(n, n, sizeof(double), &ldT);

    ret->A = starneig_alloc_matrix(n, n, sizeof(double), &ret->ldA);
    cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, n, n, n, 1.0,
        Q, ldQ, A, ldA, 0.0, T, ldT);
    cblas_dgemm(CblasColMajor, CblasNoTrans, CblasTrans, n, n, n, 1.0,
        T, ldT, Z, ldZ, 0.0, ret->A, ret->ldA);

    ret->B = NULL; ret->ldB = 0;
    if (B != NULL) {
        ret->B = starneig_alloc_matrix(n, n, sizeof(double), &ret->ldB);
        cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, n, n, n, 1.0,
            Q, ldQ, B, ldB, 0.0, T, ldT);
        cblas_dgemm(CblasColMajor, CblasNoTrans, CblasTrans, n, n, n, 1.0,
            T, ldT, Z, ldZ, 0.0, ret->B, ret->ldB);
    }

    starneig_free_matrix(T);

    starneig_sanity_check_inf(0, n, 0, n, ret->ldA, ret->A, "A", file, line);
    starneig_sanity_check_inf(0, n, 0, n, ret->ldB, ret->B, "B", file, line);

    return ret;
}

///
/// @brief The first part of a sanity check that makes sure a matrix pencil
/// Q (A,B) X^T and an updated matrix pencil ~Q (~A,~B) ~X^T are equivalent.
///
/// @param[in] name
///         An unique identifier.
///
/// @param[in] n
///         The order of the matrices A, B, Q and Z.
///
/// @param[in] ldQ
///         The leading dimension of the matrix Q.
///
/// @param[in] ldZ
///         The leading dimension of the matrix Z.
///
/// @param[in] ldA
///         The leading dimension of the matrix A.
///
/// @param[in] ldB
///         The leading dimension of the matrix B.
///
/// @param[in] Q
///         The matrix Q.
///
/// @param[in] Z
///         The matrix Z.
///
/// @param[in] A
///         The matrix A.
///
/// @param[in] B
///         The matrix B.
///
#define STARNEIG_SANITY_CHECK_RESIDUALS_BEGIN( \
    name, n, ldQ, ldZ, ldA, ldB, Q, Z, A, B) \
    struct starneig_sanity_check_args *name = \
    starneig_sanity_check_residuals_begin( \
        n, ldQ, ldZ, ldA, ldB, Q, Z, A, B, __FILE__, __LINE__)

static inline void starneig_sanity_check_residuals_end(
    int n, int ldQ, int ldZ, int ldA, int ldB, double const *Q, double const *Z,
    double const *A, double const *B,
    struct starneig_sanity_check_args const *args, char const *file, int line)
{
    struct starneig_sanity_check_args *ret =
        starneig_sanity_check_residuals_begin(
            n, ldQ, ldZ, ldA, ldB, Q, Z, A, B, file, line);

    int failure = 0;

    {
        double dot = 0.0;
        for(int i = 0; i < n; ++i )
            for(int j = 0; j < n; ++j )
                dot += squ(ret->A[ret->ldA*i+j] - args->A[args->ldA*i+j]);

        double a_dot = 0.0;
        for(int i = 0; i < n; ++i )
            for(int j = 0; j < n; ++j )
                a_dot += squ(args->A[args->ldA*i+j]);

        double norm = ((long long)1<<52) * sqrt(dot)/sqrt(a_dot);

        if (10000 < norm || isnan(norm)) {
            fprintf(stderr,
                "[starneig][sanity] %s:%d: Residual check failed for the "
                "matrix A.\n", file, line);
            fprintf(stderr,
                "[starneig][sanity] %s:%d: The norm was %.0f u.\n",
                file, line, norm);
            failure++;
        }
    }

    if (B != NULL) {
        double dot = 0.0;
        for(int i = 0; i < n; ++i )
            for(int j = 0; j < n; ++j )
                dot += squ(ret->B[ret->ldB*i+j] - args->B[args->ldB*i+j]);

        double a_dot = 0.0;
        for(int i = 0; i < n; ++i )
            for(int j = 0; j < n; ++j )
                a_dot += squ(args->B[args->ldB*i+j]);

        double norm = ((long long)1<<52) * sqrt(dot)/sqrt(a_dot);

        if (10000 < norm || isnan(norm)) {
            fprintf(stderr,
                "[starneig][sanity] %s:%d: Residual check failed for the "
                "matrix B.\n", file, line);
            fprintf(stderr,
                "[starneig][sanity] %s:%d: The norm was %.0f u.\n",
                file, line, norm);
            failure++;
        }
    }

    if (failure)
        abort();

    starneig_free_matrix(ret->A);
    starneig_free_matrix(ret->B);
    free(ret);
}

///
/// @brief The second part of a sanity check that makes sure a matrix pencil
/// Q (A,B) X^T and an updated matrix pencil ~Q (~A,~B) ~X^T are equivalent.
///
/// @param[in] name
///         An unique identifier.
///
/// @param[in] n
///         The order of the matrices A, B, Q and Z.
///
/// @param[in] ldQ
///         The leading dimension of the matrix Q.
///
/// @param[in] ldZ
///         The leading dimension of the matrix Z.
///
/// @param[in] ldA
///         The leading dimension of the matrix A.
///
/// @param[in] ldB
///         The leading dimension of the matrix B.
///
/// @param[in] Q
///         The matrix Q.
///
/// @param[in] Z
///         The matrix Z.
///
/// @param[in] A
///         The matrix A.
///
/// @param[in] B
///         The matrix B.
///
#define STARNEIG_SANITY_CHECK_RESIDUALS_END( \
    name, n, ldQ, ldZ, ldA, ldB, Q, Z, A, B) \
    starneig_sanity_check_residuals_end( \
        n, ldQ, ldZ, ldA, ldB, Q, Z, A, B, name, __FILE__, __LINE__); \
    if (name != NULL) { \
        starneig_free_matrix(name->A); \
        starneig_free_matrix(name->B); \
        free(name); \
        name = NULL; \
    }

///
/// @brief Skips the second part of a sanity check that makes sure a matrix
/// pencil Q (A,B) X^T and an updated matrix pencil ~Q (~A,~B) ~X^T are
/// equivalent.
///
/// @param[in] name
///         An unique identifier.
///
#define STARNEIG_SANITY_CHECK_RESIDUALS_SKIP(name) \
    if (name != NULL) { \
        starneig_free_matrix(name->A); \
        starneig_free_matrix(name->B); \
        free(name); \
        name = NULL; \
    }

static inline void starneig_sanity_check_bulges(
    int begin, int shifts, int n, int ldA, int ldB,
    double const *A, double const *B, char const *file, int line)
{
    starneig_sanity_check_inf(
        0, n, begin, begin+3*(shifts/2)+1, ldA, A, "A", file, line);
    starneig_sanity_check_inf(
        0, n, begin, begin+3*(shifts/2)+1, ldB, B, "B", file, line);

    if (n < begin+3*(shifts/2)+1) {
        fprintf(stderr,
            "[starneig][sanity] %s:%d: Matrix A has an invalid bulge.\n",
            file, line);
        abort();
    }

    double const *_A = A+begin*ldA+begin;
    double const *_B = B != NULL ? B+begin*ldB+begin : NULL;
    int _n = n - begin;
    for (int i = 0; i < shifts/2; i++) {
        for (int j = 3*i+4; j < _n; j++) {
            if (_A[3*i*ldA+j] != 0.0 || _A[(3*i+1)*ldA+j] != 0.0 ||
            _A[(3*i+2)*ldA+j] != 0.0) {
                fprintf(stderr,
                    "[starneig][sanity] %s:%d: Matrix A has an invalid "
                    "bulge.\n", file, line);
                abort();
            }
        }
        for (int j = 3*i+3; _B != NULL && j < _n; j++) {
            if (_B[3*i*ldB+j] != 0.0 || _B[(3*i+1)*ldB+j] != 0.0 ||
            _B[(3*i+2)*ldB+j] != 0.0) {
                fprintf(stderr,
                    "[starneig][sanity] %s:%d: Matrix B has invalid bulge .\n",
                    file, line);
                abort();
            }
        }
        if (_B != NULL &&
        (_B[3*i*ldB+3*i+1] != 0.0 || _B[3*i*ldB+3*i+2] != 0.0)) {
            fprintf(stderr,
                "[starneig][sanity] %s:%d: Matrix B has invalid bulge.\n",
                file, line);
            abort();
        }
    }
}

///
/// @brief A sanity check that makes sure that matrix pencil (A,B) contains the
/// correct number of bulges.
///
/// @param[in] begin
///         The column that should contain the first bulge.
///
/// @param[in] shifts
///         The number of shifts (shifts/2 bulges).
///
/// @param[in] n
///         The order of the matrices A and B.
///
/// @param[in] ldA
///         The leading dimension of the matrix A.
///
/// @param[in] ldB
///         The leading dimension of the matrix B.
///
/// @param[in] A
///         The matrix A.
///
/// @param[in] B
///         The matrix B.
///
#define STARNEIG_SANITY_CHECK_BULGES(begin, shifts, n, ldA, ldB, A, B) \
    starneig_sanity_check_bulges( \
        begin, shifts, n, ldA, ldB, A, B, __FILE__, __LINE__)

static inline void starneig_sanity_check_schur(
    int begin, int end, int n, int ldA, int ldB,
    double const *A, double const *B, char const *file, int line)
{
    starneig_sanity_check_inf(0, n, begin, end, ldA, A, "A", file, line);
    starneig_sanity_check_inf(0, n, begin, end, ldB, B, "B", file, line);

    const double safmin = dlamch("S");

    int two_by_two = 0;
    for (int i = begin; i < end; i++) {
        if (i+1 < end && A[i*ldA+i+1] != 0.0) {
            if (two_by_two) {
                fprintf(stderr,
                    "[starneig][sanity] %s:%d: Matrix A is not in Schur "
                    "form.\n", file, line);
                abort();
            }

            if (B != NULL) {
                if (B[(i+1)*ldB+i] != 0.0 || B[i*ldB+i+1] != 0.0) {
                    fprintf(stderr,
                        "[starneig][sanity] %s:%d: Matrix pencil (A,B) "
                        "contains a non-normalized 2-by-2 block.\n",
                        file, line);
                    abort();
                }

                if (B[i*ldB+i] == 0.0 || B[(i+1)*ldB+i+1] == 0.0) {
                    fprintf(stderr,
                        "[starneig][sanity] %s:%d: Matrix B is singular.",
                        file, line);
                    abort();
                }

                double s1, s2, wr1, wr2, wi;

                extern void dlag2_(double const *, int const *, double const *,
                    int const *, double const *, double *, double *,
                    double *, double *, double *);

                dlag2_(&A[i*ldA+i], &ldA, &B[i*ldB+i], &ldB, &safmin,
                    &s1, &s2, &wr1, &wr2, &wi);

                if (wi == 0.0) {
                    fprintf(stderr,
                        "[starneig][sanity] %s:%d: Matrix pencil (A,B) "
                        "contains a fake 2-by-2 block.\n", file, line);
                    abort();
                }
            }
            else {
                if (A[i*ldA+i] != A[(i+1)*ldA+i+1]) {
                    fprintf(stderr,
                        "[starneig][sanity] %s:%d: Matrix A contains a "
                        "non-normalized 2-by-2 block.\n",
                        file, line);
                    abort();
                }

                double a[] = {
                    A[i*ldA+i], A[i*ldA+i+1], A[(i+1)*ldA+i], A[(i+1)*ldA+i+1]
                };

                double rt1r, rt1i, rt2r, rt2i, cs, ss;

                extern void dlanv2_(
                    double *, double *, double *, double *, double *,
                    double *, double *, double *, double *, double *);

                dlanv2_(&a[0], &a[2], &a[1], &a[3],
                    &rt1r, &rt1i, &rt2r, &rt2i, &cs, &ss);

                if (rt1i == 0.0 || rt2i == 0.0) {
                    fprintf(stderr,
                        "[starneig][sanity] %s:%d: Matrix A contains a fake "
                        "2-by-2 block.\n", file, line);
                    abort();
                }
            }

            two_by_two = 1;
        }
        else {
            two_by_two = 0;
        }

        for (int j = i+2; j < n; j++) {
            if (A[i*ldA+j] != 0.0) {
                fprintf(stderr,
                    "[starneig][sanity] %s:%d: Matrix A is not in Schur "
                    "form.\n", file, line);
                abort();
            }
        }
    }

    if (B != NULL) {
        for (int i = begin; i < end; i++) {
            for (int j = i+2; j < n; j++) {
                if (B[i*ldB+j] != 0.0) {
                    fprintf(stderr,
                        "[starneig][sanity] %s:%d: Matrix B is not in upper "
                        "Hessenberg form.\n", file, line);
                    abort();
                }
            }
        }
    }
}

///
/// @brief A sanity check that makes sure that matrix pencil (A,B) is in Schur
/// form.
///
/// @param[in] begin
///         The first column to check.
///
/// @param[in] end
///         The last column to check + 1;
///
/// @param[in] n
///         The order of the matrices A and B.
///
/// @param[in] ldA
///         The leading dimension of the matrix A.
///
/// @param[in] ldB
///         The leading dimension of the matrix B.
///
/// @param[in] A
///         The matrix A.
///
/// @param[in] B
///         The matrix B.
///
#define STARNEIG_SANITY_CHECK_SCHUR(begin, end, n, ldA, ldB, A, B) \
    starneig_sanity_check_schur( \
        begin, end, n, ldA, ldB, A, B, __FILE__, __LINE__)

static inline void starneig_sanity_check_hessenberg(
    int begin, int end, int n, int ldA, int ldB,
    double const *A, double const *B, char const *file, int line)
{
    starneig_sanity_check_inf(0, n, begin, end, ldA, A, "A", file, line);
    starneig_sanity_check_inf(0, n, begin, end, ldB, B, "B", file, line);

    for (int i = begin; i < end; i++) {
        for (int j = i+2; j < n; j++) {
            if (A[i*ldA+j] != 0.0) {
                fprintf(stderr,
                    "[starneig][sanity] %s:%d: Matrix A is not in upper "
                    "Hessenberg form.\n", file, line);
                abort();
            }
        }
    }
    for (int i = begin; B != NULL && i < end; i++) {
        for (int j = i+1; j < n; j++) {
            if (B[i*ldB+j] != 0.0) {
                fprintf(stderr,
                    "[starneig][sanity] %s:%d: Matrix B is not in upper "
                    "triangular form.\n", file, line);
                abort();
            }
        }
    }
}

///
/// @brief A sanity check that makes sure that matrix pencil (A,B) is in
/// Hessenberg-triangular form.
///
/// @param[in] begin
///         The first column to check.
///
/// @param[in] end
///         The last column to check + 1;
///
/// @param[in] n
///         The order of the matrices A and B.
///
/// @param[in] ldA
///         The leading dimension of the matrix A.
///
/// @param[in] ldB
///         The leading dimension of the matrix B.
///
/// @param[in] A
///         The matrix A.
///
/// @param[in] B
///         The matrix B.
///
#define STARNEIG_SANITY_CHECK_HESSENBERG(begin, end, n, ldA, ldB, A, B) \
    starneig_sanity_check_hessenberg( \
        begin, end, n, ldA, ldB, A, B, __FILE__, __LINE__)

#else

#define STARNEIG_SANITY_REPORT(message) {}
#define STARNEIG_SANITY_REPORT_ARGS(message, ...) {}
#define STARNEIG_SANITY_CHECK(cond, message) {}
#define STARNEIG_SANITY_CHECK_ARGS(cond, message, ...) {}
#define STARNEIG_SANITY_CHECK_INF(rbegin, rend, cbegin, cend, ldA, A, mat) {}
#define STARNEIG_SANITY_CHECK_MULTIPLICITIES(n, real, imag) {}
#define STARNEIG_SANITY_CHECK_ORTHOGONALITY(n, ldQ, Q, mat) {}
#define STARNEIG_SANITY_CHECK_RESIDUALS_BEGIN( \
    name, n, ldQ, ldZ, ldA, ldB, Q, Z, A, B) {}
#define STARNEIG_SANITY_CHECK_RESIDUALS_END( \
    name, n, ldQ, ldZ, ldA, ldB, Q, Z, A, B) {}
#define STARNEIG_SANITY_CHECK_RESIDUALS_SKIP(name) {}
#define STARNEIG_SANITY_CHECK_BULGES(begin, shifts, n, ldA, ldB, A, B) {}
#define STARNEIG_SANITY_CHECK_SCHUR(begin, end, n, ldA, ldB, A, B) {}
#define STARNEIG_SANITY_CHECK_HESSENBERG(begin, end, n, ldA, ldB, A, B) {}

#endif // STARNEIG_ENABLE_SANITY_CHECKS

#endif // STARNEIG_COMMON_SANITY


#ifndef libceed_solids_examples_cl_options_h
#define libceed_solids_examples_cl_options_h

#include <petsc.h>
#include "../include/structs.h"

// Process general command line options
PetscErrorCode ProcessCommandLineOptions(MPI_Comm comm, AppCtx app_ctx);

#endif // libceed_solids_examples_cl_options_h


#pragma once

#include <emerald/util/foundation.h>

#define gsl_FEATURE_MAKE_SPAN_TO_STD 20
#include <gsl/gsl-lite.hpp>

#include <array>

namespace emerald::shallow_weaver {

using namespace emerald::util;

template <typename T>
using span = gsl::span<T>;

enum class Time_integration { FIRST_ORDER, RUNGE_KUTTA_2, RUNGE_KUTTA_4 };

using int2 = std::array<int, 2>;
using int4 = std::array<int, 4>;
using float2 = std::array<float, 2>;
using float4 = std::array<float, 4>;

}  // namespace emerald::shallow_weaver


/*****************************************************************\
           __
          / /
		 / /                     __  __
		/ /______    _______    / / / / ________   __       __
	   / ______  \  /_____  \  / / / / / _____  | / /      / /
	  / /      | / _______| / / / / / / /____/ / / /      / /
	 / /      / / / _____  / / / / / / _______/ / /      / /
	/ /      / / / /____/ / / / / / / |______  / |______/ /
   /_/      /_/ |________/ / / / /  \_______/  \_______  /
                          /_/ /_/                     / /
			                                         / /
		       High Level Game Framework            /_/

  ---------------------------------------------------------------

  Copyright (c) 2007-2011 - Rodrigo Braz Monteiro.
  This file is subject to the terms of halley_license.txt.

\*****************************************************************/

#pragma once

#include <memory>
#include "halley/data_structures/vector.h"
#include <gsl/gsl>

#include "halley/core/api/audio_api.h"

struct OggVorbis_File;

#if defined(_MSC_VER) || defined(__clang__)
using OggOffsetType = int64_t;
#else
using OggOffsetType = long int;
#endif

namespace Halley {
	class ResourceData;
	class ResourceDataReader;

	class VorbisData {
	public:
		VorbisData(std::shared_ptr<ResourceData> resource, bool open);
		~VorbisData();

		size_t read(gsl::span<Vector<float>> dst);
		size_t read(AudioMultiChannelSamples dst, size_t nChannels);

		size_t getNumSamples() const; // Per channel
		int getSampleRate() const;
		int getNumChannels() const;

		void close();
		void reset();
		void seek(double t);
		void seek(size_t sample);
		size_t tell() const;

		size_t getSizeBytes() const;

	private:
		void open();
		static size_t vorbisRead(void* ptr, size_t size, size_t nmemb, void* datasource);
		static int vorbisSeek(void *datasource, OggOffsetType offset, int whence);
		static int vorbisClose(void *datasource);
		static long vorbisTell(void *datasource);

		std::shared_ptr<ResourceData> resource;
		std::shared_ptr<ResourceDataReader> stream;
		OggVorbis_File* file;

		bool streaming;
		long long pos;
	};
}


#pragma once

#include <gsl/gsl_assert>
#include <src/util/Logging.h>

#ifndef NDEBUG
    //#define MI_EXPECTS(x) Expects(x)
    //#define MI_ENSURES(x) Ensures(x)

#define MI_EXPECTS(x) \
  do { \
    if (!(x)) { \
        LOG(ERROR) << "assertion failed: " << #x; \
        std::terminate(); \
    } \
  } while (0)

#define MI_ENSURES(x) \
  do { \
    if (!(x)) { \
        LOG(ERROR) << "assertion failed: " << #x; \
        std::terminate(); \
    } \
  } while (0)
#else
    #define MI_EXPECTS(x)
    #define MI_ENSURES(x)
#endif


#pragma once

#if SEAL_COMPILER == SEAL_COMPILER_MSVC

// Require Visual Studio 2015 or newer and C++14 support
#if (_MSC_VER < 1900) || (_MSVC_LANG < 201402L)
#error "Microsoft Visual Studio 2015 (14.0) or newer required; C++14 support required"
#endif

// Can work with Visual Studio 2015 (C++14) with some limitations
#if _MSVC_LANG == 1900
#undef SEAL_USE_SHARED_MUTEX_FOR_RW_LOCK
#endif

// Deal with Debug mode in Visual Studio
#ifdef _DEBUG
#define SEAL_DEBUG
#endif

// Cannot use std::shared_mutex when compiling with /clr
#ifdef _M_CEE
#undef SEAL_USE_SHARED_MUTEX_FOR_RW_LOCK
#endif

// Try to check presence of additional headers using __has_include
#ifdef __has_include

// Check for MSGSL
#if __has_include(<gsl/gsl>)
#include <gsl/gsl>
#define SEAL_USE_MSGSL
#else
#undef SEAL_USE_MSGSL
#endif //__has_include(<gsl/gsl>)

#endif

// X64
#ifdef _M_X64

// Use compiler intrinsics for better performance
#define SEAL_USE_INTRIN

#ifdef SEAL_USE_INTRIN
#include <intrin.h>

#pragma intrinsic(_addcarry_u64)
#define SEAL_ADD_CARRY_UINT64(operand1, operand2, carry, result) _addcarry_u64(     \
    carry,                                                                          \
    static_cast<unsigned long long>(operand1),                                      \
    static_cast<unsigned long long>(operand2),                                      \
    reinterpret_cast<unsigned long long*>(result))

#pragma intrinsic(_subborrow_u64)
#define SEAL_SUB_BORROW_UINT64(operand1, operand2, borrow, result) _subborrow_u64(  \
    borrow,                                                                         \
    static_cast<unsigned long long>(operand1),                                      \
    static_cast<unsigned long long>(operand2),                                      \
    reinterpret_cast<unsigned long long*>(result))

#pragma intrinsic(_BitScanReverse64)
#define SEAL_MSB_INDEX_UINT64(result, value) _BitScanReverse64(result, value)

#pragma intrinsic(_umul128)
#define SEAL_MULTIPLY_UINT64(operand1, operand2, result128) {                       \
    result128[0] = _umul128(                                                        \
        static_cast<unsigned long long>(operand1),                                  \
        static_cast<unsigned long long>(operand2),                                  \
        reinterpret_cast<unsigned long long*>(result128 + 1));                      \
}
#define SEAL_MULTIPLY_UINT64_HW64(operand1, operand2, hw64) {                       \
    _umul128(                                                                       \
        static_cast<unsigned long long>(operand1),                                  \
        static_cast<unsigned long long>(operand2),                                  \
        reinterpret_cast<unsigned long long*>(hw64));                               \
}

#endif
#else
#undef SEAL_USE_INTRIN

#endif //_M_X64

#endif


#pragma once

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>
#include <GLES3/gl3platform.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <gsl/gsl>

namespace android::OpenGLHelpers
{
    constexpr GLint GetTextureUnit(GLenum texture)
    {
        return texture - GL_TEXTURE0;
    }

    GLuint CreateShaderProgram(const char* vertShaderSource, const char* fragShaderSource);

    namespace GLTransactions
    {
        inline auto SetCapability(GLenum capability, bool isEnabled)
        {
            const auto setCapability{ [capability](bool isEnabled)
            {
                if (isEnabled)
                {
                    glEnable(capability);
                }
                else
                {
                    glDisable(capability);
                }
            }};

            const auto wasEnabled{ glIsEnabled(capability) };
            setCapability(isEnabled);
            return gsl::finally([wasEnabled, setCapability]() { setCapability(wasEnabled); });
        }

        inline auto BindFrameBuffer(GLuint frameBufferId)
        {
            GLint previousFrameBufferId;
            glGetIntegerv(GL_FRAMEBUFFER_BINDING, &previousFrameBufferId);
            glBindFramebuffer(GL_FRAMEBUFFER, frameBufferId);
            return gsl::finally([previousFrameBufferId]() { glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(previousFrameBufferId)); });
        }

        inline auto DepthMask(GLboolean depthMask)
        {
            GLboolean previousDepthMask;
            glGetBooleanv(GL_DEPTH_WRITEMASK, &previousDepthMask);
            glDepthMask(depthMask);
            return gsl::finally([previousDepthMask]() { glDepthMask(previousDepthMask); });
        }

        inline auto BindSampler(GLenum unit, GLuint id)
        {
            glActiveTexture(unit);
            GLint previousId;
            glGetIntegerv(GL_SAMPLER_BINDING, &previousId);
            glBindSampler(unit - GL_TEXTURE0, id);
            return gsl::finally([unit, id{ previousId }]() { glActiveTexture(unit); glBindSampler(unit - GL_TEXTURE0, id); });
        }

        inline auto MakeCurrent(EGLDisplay display, EGLSurface drawSurface, EGLSurface readSurface, EGLContext context)
        {
            EGLDisplay previousDisplay{ eglGetDisplay(EGL_DEFAULT_DISPLAY) };
            EGLSurface previousDrawSurface{ eglGetCurrentSurface(EGL_DRAW) };
            EGLSurface previousReadSurface{ eglGetCurrentSurface(EGL_READ) };
            EGLContext previousContext{ eglGetCurrentContext() };
            eglMakeCurrent(display, drawSurface, readSurface, context);
            return gsl::finally([previousDisplay, previousDrawSurface, previousReadSurface, previousContext]() { eglMakeCurrent(previousDisplay, previousDrawSurface, previousReadSurface, previousContext); });
        }
    }
}


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

#pragma once

#ifndef json_58ea0e36_7aaa_49a1_b466_3bbbb2483c5b_h
#define json_58ea0e36_7aaa_49a1_b466_3bbbb2483c5b_h

#include <gslib/std.h>

__gslib_begin__

enum json_tag
{
    jst_array,
    jst_table,
    jst_pair,
    jst_value,
};

class json_node_value;

class __gs_novtable json_node abstract
{
public:
    virtual ~json_node() {}
    virtual json_tag get_tag() const = 0;
    virtual const string& get_name() const = 0;
    virtual json_node* duplicate() const = 0;   /* delete */
};

struct json_node_hash
#if defined(_MSC_VER) && (_MSC_VER < 1914)
    : public std::unary_function<json_node*, size_t>
#endif
{
public:
    size_t operator()(const json_node* p) const
    {
        assert(p);
        return string_hash(p->get_name());
    }
};

struct json_node_equalto
#if defined(_MSC_VER) && (_MSC_VER < 1914)
    : public std::binary_function<json_node*, json_node*, bool>
#endif
{
public:
    bool operator()(const json_node* p1, const json_node* p2) const
    {
        assert(p1 && p2);
        return p1->get_name() == p2->get_name();
    }
};

typedef unordered_set<json_node*, json_node_hash, json_node_equalto> json_node_map;
typedef vector<json_node*> json_node_list;

class json_node_array:
    public json_node
{
public:
    virtual ~json_node_array();
    virtual json_tag get_tag() const override { return jst_array; }
    virtual const string& get_name() const override { return _name; }
    virtual json_node* duplicate() const override;

protected:
    string              _name;
    json_node_list      _array;

public:
    void set_name(const gchar* str) { _name.assign(str); }
    void set_name(const gchar* str, int len) { _name.assign(str, len); }
    void set_name(const string& str) { _name = str; }
    bool is_empty() const { return _array.empty(); }
    int get_childs() const { return (int)_array.size(); }
    json_node* at(int i) const { return _array.at(i); }
    int parse(const gchar* src, int len);
    json_node_list& get_container() { return _array; }
};

class json_node_table:
    public json_node
{
public:
    virtual ~json_node_table();
    virtual json_tag get_tag() const override { return jst_table; }
    virtual const string& get_name() const override { return _name; }
    virtual json_node* duplicate() const override;

protected:
    string              _name;
    json_node_map       _table;

public:
    void set_name(const gchar* str) { _name.assign(str); }
    void set_name(const gchar* str, int len) { _name.assign(str, len); }
    void set_name(const string& str) { _name = str; }
    bool is_empty() const { return _table.empty(); }
    int get_childs() const { return (int)_table.size(); }
    json_node* find(const string& name) const;
    int parse(const gchar* src, int len);
    json_node_map& get_container() { return _table; }
};

class json_node_value:
    public json_node
{
public:
    virtual json_tag get_tag() const override { return jst_value; }
    virtual const string& get_name() const override { return _strval; }
    virtual json_node* duplicate() const override;

protected:
    string              _strval;

public:
    void set_value_string(const gchar* str) { _strval.assign(str); }
    void set_value_string(const gchar* str, int len) { _strval.assign(str, len); }
    void set_value_string(const string& str) { _strval = str; }
    int parse(const gchar* src, int len);
};

class json_node_pair:
    public json_node
{
public:
    virtual json_tag get_tag() const override { return jst_pair; }
    virtual const string& get_name() const override { return _name; }
    virtual json_node* duplicate() const override;

protected:
    string              _name;
    json_node_value     _value;

public:
    void set_name(const gchar* str) { _name.assign(str); }
    void set_name(const gchar* str, int len) { _name.assign(str, len); }
    void set_name(const string& str) { _name = str; }
    void set_value(const gchar* str) { _value.set_value_string(str); }
    void set_value(const gchar* str, int len) { _value.set_value_string(str, len); }
    void set_value(const string& str) { _value.set_value_string(str); }
    const json_node_value& get_value() const { return _value; }
    const string& get_value_string() const { return _value.get_name(); }
    int parse(const gchar* src, int len);
};

class json_key:
    public json_node_pair
{
public:
    json_key(const string& name) { _name = name; }
    json_key(const gchar* str) { _name.assign(str); }
    json_key(const gchar* str, int len) { _name.assign(str, len); }
};

class json_parser
{
public:
    json_parser() { _root = nullptr; }
    ~json_parser() { destroy(); }
    bool parse(const gchar* src, int len);
    bool parse(const gchar* filename);
    void destroy();
    json_node* get_root() const { return _root; }

protected:
    json_node*          _root;
};

__gslib_end__

#endif


//        Copyright The Authors 2018.
//    Distributed under the 3-Clause BSD License.
//    (See accompanying file LICENSE or copy at
//   https://opensource.org/licenses/BSD-3-Clause)

#pragma once

#include <cstdint>  // for std::uint8_t
#include <gsl/gsl>  // for gsl::span<std::uint8_t>
#include <vector>   // for std::vector<std::uint8_t>

namespace blocxxi {
namespace p2p {
namespace kademlia {

/// Represents an output buffer to which headers and message bodies can be
/// serialized. Whatever type is used, it needs to be able to expand as needed.
using Buffer = std::vector<std::uint8_t>;

/// Represents a read-only view over the input buffer used to deserialize
/// headers and message bodies.
using BufferReader = gsl::span<std::uint8_t>;

}  // namespace kademlia
}  // namespace p2p
}  // namespace blocxxi


#ifndef PyGSL_RNG_H
#define PyGSL_RNG_H 1

#include <pygsl/intern.h>
#include <gsl/gsl_rng.h>
typedef struct {
  PyObject_HEAD
  gsl_rng * rng;
} PyGSL_rng;

/*
 * Get a gsl_rng object from a PyGSL rng wrapper.
 */
PyGSL_API_EXTERN gsl_rng *
PyGSL_gsl_rng_from_pyobject(PyObject * object);
#ifndef _PyGSL_API_MODULE
#define PyGSL_gsl_rng_from_pyobject \
(*(gsl_rng *  (*) (PyObject *)) PyGSL_API[PyGSL_gsl_rng_from_pyobject_NUM])
#endif /* _PyGSL_API_MODULE */

#define PyGSL_RNG_Check(op) \
   ((op)->ob_type == (PyTypeObject *)PyGSL_API[PyGSL_RNG_ObjectType_NUM])

#define import_pygsl_rng() \
{ \
   PyObject *pygsl = NULL, *c_api = NULL, *md = NULL; \
   if ( \
      (pygsl = PyImport_ImportModule("pygsl.rng"))         != NULL && \
      (md = PyModule_GetDict(pygsl))                       != NULL && \
      (c_api = PyDict_GetItemString(md, "_PYGSL_RNG_API")) != NULL && \
      (PyCObject_Check(c_api))                                        \
     ) { \
	 PyGSL_API = (void **)PyCObject_AsVoidPtr(c_api); \
   } else { \
        PyGSL_API = NULL; \
   } \
   /* fprintf(stderr, "PyGSL_API points to %p\n", (void *) PyGSL_API); */ \
}
#endif  /* PyGSL_RNG_H */


#pragma once
#include <gsl/gsl>
#include <typeindex>
#include "halley/utils/utils.h"
#include "halley/data_structures/maybe.h"
#include "halley/bytes/byte_serializer.h"
#include <cstdint>

namespace Halley
{
	class IMessageStream;
	class MessageQueue;
	class MessageQueueUDP;
	class MessageQueueTCP;

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

		size_t getSerializedSize() const
		{
			if (!serialized) {
				serialized = Serializer::toBytes(*this, getSerializerOptions());
			}
			return serialized->size();
		}

		void serializeTo(gsl::span<gsl::byte> dst) const
		{
			if (!serialized) {
				serialized = Serializer::toBytes(*this, getSerializerOptions());
			}
			memcpy(dst.data(), serialized->data(), serialized->size());
		}

		Bytes getBytes() const
		{
			if (!serialized) {
				serialized = Serializer::toBytes(*this, getSerializerOptions());
			}
			return *serialized;
		}

		virtual void serialize(Serializer& s) const = 0;
		virtual void deserialize(Deserializer& s) = 0;

		void setSeq(uint16_t seq) { this->seq = seq; }
		uint16_t getSeq() const { return seq; }
		void setChannel(uint8_t channel) { this->channel = channel; }
		uint8_t getChannel() const { return channel; }

		static SerializerOptions getSerializerOptions()
		{
			return SerializerOptions(SerializerOptions::maxVersion);
		}

	private:
		uint16_t seq = 0;
		uint8_t channel = -1;

		mutable std::optional<Bytes> serialized;
	};

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

		virtual std::unique_ptr<NetworkMessage> create(gsl::span<const gsl::byte> src) const = 0;
		virtual std::type_index getTypeIndex() const = 0;
	};

	template <typename T>
	class NetworkMessageFactory : public NetworkMessageFactoryBase
	{
	public:
		std::unique_ptr<NetworkMessage> create(gsl::span<const gsl::byte> src) const override
		{
			auto result = std::make_unique<T>();
			auto s = Deserializer(src, T::getSerializerOptions());
			result->deserialize(s);
			return result;
		}

		std::type_index getTypeIndex() const override
		{
			return std::type_index(typeid(T));
		}
	};

	class NetworkMessageFactories
	{
	public:
		template <typename T>
		void addFactory()
		{
			addFactory(std::make_unique<NetworkMessageFactory<T>>());
		}

		uint16_t getMessageType(NetworkMessage& msg) const;
		std::unique_ptr<NetworkMessage> deserializeMessage(gsl::span<const gsl::byte> data, uint16_t msgType, uint16_t seq);

	private:
		std::map<std::type_index, uint16_t> typeToMsgIndex;
		Vector<std::unique_ptr<NetworkMessageFactoryBase>> factories;

		void addFactory(std::unique_ptr<NetworkMessageFactoryBase> factory);
	};
}


// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#pragma once

#include "arcana/utils/serialization/base_stream.h"
#include "arcana/utils/serialization/serializable.h"
#include "arcana/analysis/determinator.h"
#include "Proxies/Proxy.h"
#include "arcana/analysis/binary_iterator.h"
#include "Mapping/MapPointKeyframeAssociations.h"
#include "BundleAdjustment/BundleAdjust.h"
#include "Proxies/KeyframeFields.h"

#include "Tracking/PoseEstimator.h"
#include "Utils/cv.h"

#include <type_traits>
#include <memory>
#include <gsl/gsl>

namespace mage
{
    class MapPoint;
    class Keyframe;

    template<typename IterT>
    inline void binary_iterate(const Id<MapPoint>& id, mira::binary_iterator<IterT>& iterator)
    {
        iterator.iterate(reinterpret_cast<const IdT&>(id));
    }

    template<typename IterT>
    inline void binary_iterate(const Id<Keyframe>& id, mira::binary_iterator<IterT>& iterator)
    {
        iterator.iterate(reinterpret_cast<const IdT&>(id));
    }

    /*
    Serialize only the proxy properties that support serialization
    */
    template<typename IterT, typename T, typename ...Props>
    inline void binary_iterate(const Proxy<T, Props...>& proxy, mira::binary_iterator<IterT>& iterator)
    {
        iterator.iterate(static_cast<const proxy::Id<T>&>(proxy));

        int unused[] = {
            0, // in case it's just a Proxy<MapPoint>, we can't declare an empty c-style array
            (iterator.iterate(static_cast<const Props&>(proxy)) , 0)...
        };
        (void)unused;
    };

    template<typename IterT>
    inline void binary_iterate(const KeyframeReprojection& data, mira::binary_iterator<IterT>& itr)
    {
        itr.iterate(reinterpret_cast<const IdT&>(data.KeyframeId));
        itr.iterate(data.Pose);
        for (auto& point : data.Points)
        {
            itr.iterate(point.As<Proxy<MapPoint, proxy::Position, proxy::UpdateStatistics, proxy::ViewingData>>());
            itr.iterate(*point.GetRepresentativeDescriptor());
        }
    }

    template<typename IterT>
    inline void binary_iterate(const MapPointAssociation& data, mira::binary_iterator<IterT>& itr)
    {
        itr.iterate(&data, sizeof(MapPointAssociation));
    }

    template<typename IterT>
    inline void binary_iterate(const HistoricalFrame& data, mira::binary_iterator<IterT>& itr)
    {
        itr.iterate(data.UpdatedPose);
        itr.iterate(data.Keyframe);
    }

    template<typename IterT>
    inline void binary_iterate(const AdjustableData& data, mira::binary_iterator<IterT>& itr)
    {
        itr.iterate(data.Keyframes);
        itr.iterate(data.ExternallyTetheredKeyframes);
        itr.iterate(data.MapPointAssociations);
        itr.iterate(data.MapPoints);
    }

    template<typename IterT>
    inline void binary_iterate(const typename MapPointAssociations<MapPointTrackingProxy>::Association& assoc, mira::binary_iterator<IterT>& itr)
    {
        itr.iterate_all(assoc.MapPoint, assoc.Index);
    }

    template<typename IterT>
    inline void binary_iterate(const MapPointKeyframeAssociations& data, mira::binary_iterator<IterT>& itr)
    {
        itr.iterate(data.MapPoint);
        itr.iterate(data.Keyframes);
    }

    template<typename IterT>
    inline void binary_iterate(const KeyframeAssociation& data, mira::binary_iterator<IterT>& itr)
    {
        itr.iterate_all(data.KeyframeId, data.KeypointDescriptorIndex);
    }

    template<typename IterT>
    inline void binary_iterate(const FrameId& data, mira::binary_iterator<IterT>& itr)
    {
        itr.iterate(data.CorrelationId);
        itr.iterate(data.Camera);
    }

    template<typename IterT>
    inline void binary_iterate(const AnalyzedImage& data, mira::binary_iterator<IterT>& itr)
    {
        itr.iterate(data.GetFrameId());
        itr.iterate(data.GetTimeStamp().time_since_epoch());
        itr.iterate(data.GetImageData());
        itr.iterate(data.GetDistortedCalibration());
        itr.iterate(data.GetUndistortedCalibration());
        itr.iterate(data.GetKeyPoints());
    }

    template<typename IterT, typename T>
    inline void binary_iterate(const Tether<T>& data, mira::binary_iterator<IterT>& itr)
    {
        itr.iterate(data.Type());
        itr.iterate(data.Weight());
        itr.iterate(data.OriginId());

        if (data.Type() == TetherType::DISTANCE)
        {
            itr.iterate(data.Distance());
        }

        if(data.Type() == TetherType::SIX_DOF || data.Type() == TetherType::EXTRINSIC)
        {
            itr.iterate(data.Position());
        }

        if (data.Type() == TetherType::THREE_DOF || data.Type() == TetherType::SIX_DOF || data.Type() == TetherType::EXTRINSIC)
        {
            itr.iterate(data.Rotation());
        }
    }

    namespace proxy
    {
        // no-ops that we don't check for determinism
        inline void binary_iterate(const Image&, mira::binary_iterator<mira::determinator::iterator_callback>&) {}
        inline void binary_iterate(const Associations<MapPointTrackingProxy>&, mira::binary_iterator<mira::determinator::iterator_callback>&) {}

        inline void binary_iterate(const UnAssociatedMask& data, mira::binary_iterator<mira::determinator::iterator_callback>& itr)
        {
            itr.iterate(data.GetUnassociatedKeypointCount());
            itr.iterate(data.GetUnassociatedKeypointMask());
        }

        template<typename IterT>
        inline void binary_iterate(const PoseConstraints& data, mira::binary_iterator<IterT>& itr)
        {
            itr.iterate(data.IsImmortal());
            itr.iterate(data.IsFixed());
            itr.iterate(data.GetTethers());
        }

        template<typename IterT>
        inline void binary_iterate(const Descriptor& data, mira::binary_iterator<IterT>& itr)
        {
            // proxy::Descriptor is just a pointer to the descriptor
            itr.iterate(data.GetRepresentativeDescriptor()->Data(), mage::ORBDescriptor::DESCRIPTOR_SIZE_BYTES);
        }

        template<typename IterT>
        inline void binary_iterate(const Associations<mage::MapPointTrackingProxy>& data, mira::binary_iterator<IterT>& itr)
        {
            // not the real size as we don't account for the bimap overhead
            std::vector<mage::MapPointAssociations<mage::MapPointTrackingProxy>::Association> assocs;
            data.GetAssociations(assocs);
            itr.iterate(assocs);
        }
    }
}

namespace cv
{
    template<typename IterT>
    inline void binary_iterate(const cv::KeyPoint& data, mira::binary_iterator<IterT>& itr)
    {
        itr.iterate(&data, sizeof(data));
    }

    template<typename IterT>
    inline void binary_iterate(const cv::Vec3f& data, mira::binary_iterator<IterT>& itr)
    {
        itr.iterate(&data, sizeof(data));
    }
}

namespace Eigen
{
    template<typename IterT, typename T>
    inline void binary_iterate(const Quaternion<T>& data, mira::binary_iterator<IterT>& itr)
    {
        itr.iterate(data.coeffs().data(), data.coeffs().size()*sizeof(T));
    }
}


/*
Copyright (c) 2003-2008 Rudi Cilibrasi, Rulers of the RHouse
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. 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.
3. Neither the name of the University 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 RULERS 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 RULERS 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 <stdio.h>
#include <string.h>
#if OPENMP_ENABLED
#include <omp.h>
#endif
#include <assert.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>
#include <glib.h>
#include <glib/gstdio.h>
//#include <gsl/gsl_cblas.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>
#include <libintl.h>
#include <unistd.h>

#include "complearn/util.h"

#define _(O) gettext(O)

void complearn_mrbreaker(void)
{
  printf("break!\n");
}

int complearn_make_directory_if_necessary(const char *name)
{
  return g_mkdir_with_parents(name, 0700);
}

char *complearn_get_hostname()
{
  static char *answer;
  if (answer == NULL) {
    static char buf[1024];
#ifdef __MINGW32__
	strcpy(buf, "localhost");
#else
    gethostname(buf, sizeof(buf));
#endif
    buf[sizeof(buf)-1] = '\0';
    answer = buf;
  }
  return g_strdup(answer);
}

int complearn_get_pid(void)
{
  return getpid();
}

GSList *complearn_read_directory_of_files(const char *dirname)
{
  GError *err = NULL;
  GSList *result = NULL;
  GDir *d = g_dir_open(dirname, 0, &err);
  if (err)
    g_error(_("Cannot open directory %s"), dirname);
  const char *n;
  while ( (n = g_dir_read_name(d)) ) {
    char *cur = g_build_filename(dirname, n, NULL);
    if (g_file_test(cur, G_FILE_TEST_IS_REGULAR)) {
      result = g_slist_append(result, complearn_new_arg(complearn_read_whole_file(cur),cur));
    }
    g_free(cur);
  }
  g_dir_close(d);
  return result;
}

static GSList *complearn_read_list_of_files_or_strings(const char *filename, int isfiles)
{
  GIOChannel *in;
  GError *err = NULL;
  GSList *result = NULL;
  in = g_io_channel_new_file(filename, "r", &err);
  if (err)
    g_error(_("Cannot open file %s"), filename);
  for (;;) {
    GIOStatus s;
    gsize tpos;
    err = NULL;
    GString *block = g_string_new("");
    s = g_io_channel_read_line_string(in, block, &tpos, &err);
    if (s == G_IO_STATUS_EOF)
      break;
    if (err != NULL)
      g_error(_("Cannot read line from file %s: %s"), filename, err->message);
    g_string_truncate(block, tpos);
    GString *tokeep;
    tokeep = (isfiles != 0) ? complearn_read_whole_file(block->str) : g_string_new_len(block->str, block->len);
    result = g_slist_append(result, complearn_new_arg(tokeep, block->str));
  }
  g_io_channel_close(in);
  return result;
}

GSList *complearn_read_list_of_files(const char *filename)
{
  return complearn_read_list_of_files_or_strings(filename, 1);
}

GSList *complearn_read_list_of_strings(const char *filename)
{
  return complearn_read_list_of_files_or_strings(filename, 0);
}

GString *complearn_read_whole_file_ptr(FILE *fp)
{
  GString *result = g_string_new("");
  char buf[512];
  int rl;
  while ( (rl = fread(buf, 1, 512, fp)) == 512)
    g_string_append_len(result, buf, rl);
  if (rl > 0)
    g_string_append_len(result, buf, rl);
  return result;
}

char *complearn_chdir(const char *newdir)
{
  char *retval = g_get_current_dir();
  int rv;
  rv = g_chdir(newdir);
  if (rv != 0) {
    g_error(_("Cannot change directory to %s"), newdir);
  }
  return retval;
}

int complearn_remove_directory_recursively(const char *dirname, int f_err_out)
{
  GError *err = NULL;
  int result = 0;
  GDir *d = g_dir_open(dirname, 0, &err);
  if ( (err) ) {
    if (f_err_out)
      g_error(_("Cannot open directory %s. Error: %s"), dirname, err->message);
    else
      return 1;
  }
  const char *n;
  while ( (n = g_dir_read_name(d)) ) {
    char *cur = g_build_filename(dirname, n, NULL);
    if (g_file_test(cur, G_FILE_TEST_IS_REGULAR))
      g_unlink(cur);
    g_free(cur);
  }
  g_dir_close(d);
  result = g_rmdir(dirname);
  return result;
}

int complearn_write_file(const char *fname, const GString *f)
{
  g_assert(fname != NULL && strlen(fname) > 0);
  FILE *fp = fopen(fname, "wb");
  if (fp == NULL) {
    g_error(_("Cannot open file %s for writing."), fname);
  }
  fwrite(f->str, 1, f->len, fp);
  fclose(fp);
  return 0;
}

GString *complearn_read_whole_file(const char *fname)
{
  FILE *fp = fopen(fname, "rb");
  if (fp == NULL)
    g_error(_("Cannot open file %s for reading"), fname);
  fseek(fp, 0, SEEK_END);
  int sz = ftell(fp);
  fseek(fp, 0, SEEK_SET);
  char *b = calloc(sz, 1);
  GString *result;
  fread(b, 1, sz, fp);
  fclose(fp);
  result = g_string_new_len(b, sz);
  g_free(b);
  return result;
}

char *complearn_make_temp_dir(void)
{
  char *result = complearn_make_temp_dir_name();
#ifdef __MINGW32__
  mkdir(result);
#else
  mkdir(result, 0700);
#endif
  return result;
}

/* different each time */
char *complearn_make_temp_file_name(void)
{
  char *tnam, *result;
  static int i = 0;
  char pidbuf[32];
  sprintf(pidbuf, "%d-%d", ++i, getpid());
  tnam = getenv("TMPDIR");
  if (tnam == NULL)
    tnam = "/tmp";
  result = g_strconcat(tnam, "/clfile", pidbuf, NULL);
  return result;
}

char *complearn_make_temp_dir_name(void)
{
  int tid = 0;
#if OPENMP_ENABLED
    if (!g_thread_supported ()) g_thread_init (NULL);
    tid = omp_get_thread_num();
#endif

  char *result = NULL, *tnam;
  int incr = 0;
  char pidbuf[64];
  for (;;) {
    sprintf(pidbuf, "%dx%dx%d", getpid(),incr,tid);
    tnam = getenv("TMPDIR");
    if (tnam == NULL)
      tnam = "/tmp";
    result = tnam;
    result = g_strconcat(tnam, "/complearn-", pidbuf, NULL);
    if (!g_file_test(result, G_FILE_TEST_EXISTS) && !g_file_test(result, G_FILE_TEST_IS_DIR))
      break;
    incr += 1;
  }
  return result;
}

char ** complearn_dupe_strings(const char * const * str)
{
  int size = complearn_count_strings(str);
  char **result;
  int i;
  result = calloc(size+1, sizeof(gpointer));
  for (i = 0; i < size; i += 1)
    result[i] = g_strdup(str[i]);
  return result;
}

int complearn_count_strings(const char * const * str)
{
  int acc = 0;
  const char * const *cur = str;
  while (cur && *cur) {
    acc += 1;
    cur += 1;
  }
  return acc;
}

CLArgumentEntry *complearn_new_arg(GString *block, const char *label) {
  CLArgumentEntry *cl = calloc(1, sizeof(*cl));
  cl->block = block;
  cl->label = g_strdup(label);
  return cl;
}

/* This function makes the off-diagonal elements symmetric by averaging.  */
gsl_matrix *complearn_average_matrix(gsl_matrix *a)
{
  int i,j;
  gsl_matrix *res;
  res = gsl_matrix_alloc(a->size1, a->size2);

  for (i = 0;i<a->size1;i+=1)
    for (j = 0; j <a->size2; j+=1) {
      double v;
      if (i == j)
        gsl_matrix_set(res,i,j,gsl_matrix_get(a,i,j));
      if (i <= j)
        continue;
      v = (gsl_matrix_get(a,i,j) + gsl_matrix_get(a,j,i))/2.0;
      gsl_matrix_set(res,i,j,v);
      gsl_matrix_set(res,j,i,v);
    }
  return res;
}

gsl_matrix *complearn_svd_project(gsl_matrix *a)
{
  int retval;
  gsl_matrix *res;
  gsl_matrix *u, *v;
  gsl_vector *s;
  u = gsl_matrix_alloc(a->size1, a->size2);
  gsl_matrix_memcpy(u, a);
  v = gsl_matrix_alloc(a->size2, a->size2);
  s = gsl_vector_alloc(a->size1);
  retval = gsl_linalg_SV_decomp_jacobi(u, v, s);
//  g_assert(retval == GSL_OK);
  res = gsl_matrix_alloc(a->size1, a->size2);
  gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, a, u, 0.0, res);
  return res;
}

static gboolean is_last_common_char(char **inp, int which)
{
  int i;
  if (strlen(inp[0]) <= which)
    return FALSE;
  for (i = 1; inp[i]; i += 1)
    if (strlen(inp[i]) <= which || inp[i][strlen(inp[i])-which-1] != inp[0][strlen(inp[0])-which-1])
      return FALSE;
  return TRUE;
}

static gboolean is_common_char(char **inp, int which)
{
  int i;
  if (strlen(inp[0]) <= which)
    return FALSE;
  for (i = 1; inp[i]; i += 1)
    if (strlen(inp[i]) <= which || inp[i][which] != inp[0][which])
      return FALSE;
  return TRUE;
}

static char **chop_strings(char **inp, int prefixc, int suffixc)
{
  int sc = complearn_count_strings((const char * const *) inp);
  char **result = calloc(sc+1,sizeof(gpointer));
  int i;
  for (i = 0; i < sc; i += 1) {
    char *p = inp[i]+prefixc;
    char *res;
    res = g_strdup(p);
    if (suffixc && strlen(p) > suffixc)
      res[strlen(res)-suffixc] = '\0';
    result[i] = res;
  }
  return result;
}

char ** complearn_fix_labels(char **inp)
{
  int prefixc = 0, suffixc = 0;
  if (inp[1] != NULL) {
    while (is_common_char(inp, prefixc))
      prefixc += 1;
    while ( is_last_common_char(inp, suffixc))
      suffixc += 1;
  }
  return chop_strings(inp, prefixc, suffixc);
}


/**
* VGMTrans (c) - 2002-2021
* Licensed under the zlib license
* See the included LICENSE for more information
*/

#pragma once

#include <vector>
#include <fluidsynth.h>
#include <gsl-lite.hpp>

class MusicPlayer {
public:
  MusicPlayer();
  ~MusicPlayer();

  /**
   * Toggles the status of the player
   * @returns true if the player is playing
   */
  bool toggle();
  /**
   * Stops the player (unloads resources)
   */
  void stop();
  /**
   * Moves the player position
   * @param position relative to song start
   */
  void seek(int position);

  /**
   * Loads SF2 and MIDI data into the player; resources are copied
   * @param soundfont_data
   * @param midi_data
   * @return true if data was loaded correctly
   */
  bool loadDataAndPlay(gsl::span<char> soundfont_data, gsl::span<char> midi_data);

  /**
   * Checks whether the player is playing
   * @return true if playing
   */
  [[nodiscard]] bool playing() const;
  /**
   * The number of MIDI ticks elapsed since the player was started
   * @return number of ticks relative to song start
   */
  [[nodiscard]] int elapsedTicks() const;
  /**
   * The total number of MIDI ticks in the song
   * @return total ticks in the song
   */
  [[nodiscard]] int totalTicks() const;

  /**
   * Gets all the audio driver the player supports
   * @return the driver list
   */
  [[nodiscard]] std::vector<const char *> getAvailableDrivers() const;
  /**
   * Changes the current audio driver and restarts playing
   * @param driver_name
   * @return true if the driver changed
   */
  bool setAudioDriver(const char* driver_name);

private:
  fluid_settings_t *m_settings = nullptr;
  fluid_synth_t *m_synth = nullptr;
  fluid_audio_driver_t *m_active_driver = nullptr;
  fluid_player_t *m_active_player = nullptr;

  void makeSettings();
  void makeSynth();
  void makePlayer();
};


/*
 *  Copyright (C) 2021 FISCO BCOS.
 *  SPDX-License-Identifier: Apache-2.0
 *  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.
 *
 * @brief scale encoder
 * @file ScaleEncoderStream.cpp
 */
#pragma once
#include "FixedWidthIntegerCodec.h"
#include "libutilities/FixedBytes.h"
#include <boost/optional.hpp>
#include <boost/variant.hpp>
#include <deque>
#include <gsl/span>
namespace bcos
{
namespace codec
{
namespace scale
{
class ScaleEncoderStream
{
public:
    // special tag to differentiate encoding streams from others
    static constexpr auto is_encoder_stream = true;

    // get the encoded data
    bytes data() const;

    /**
     * @brief scale-encodes pair of values
     * @tparam F first value type
     * @tparam S second value type
     * @param p pair of values to encode
     * @return reference to stream
     */
    template <class F, class S>
    ScaleEncoderStream& operator<<(const std::pair<F, S>& p)
    {
        return *this << p.first << p.second;
    }

    /**
     * @brief scale-encodes tuple
     * @tparam T enumeration of types
     * @param v tuple
     * @return reference to stream
     */
    template <class... Ts>
    ScaleEncoderStream& operator<<(const std::tuple<Ts...>& v)
    {
        if constexpr (sizeof...(Ts) > 0)
        {
            encodeElementOfTuple<0>(v);
        }
        return *this;
    }

    /**
     * @brief scale-encodes variant value
     * @tparam T type list
     * @param v value to encode
     * @return reference to stream
     */
    template <class... T>
    ScaleEncoderStream& operator<<(const boost::variant<T...>& v)
    {
        tryEncodeAsOneOfVariant<0>(v);
        return *this;
    }

    /**
     * @brief scale-encodes sharead_ptr value
     * @tparam T type list
     * @param v value to encode
     * @return reference to stream
     */
    template <class T>
    ScaleEncoderStream& operator<<(const std::shared_ptr<T>& v)
    {
        if (v == nullptr)
        {
            BOOST_THROW_EXCEPTION(ScaleEncodeException()
                                  << errinfo_comment("encode exception for DEREF_NULLPOINTER"));
        }
        return *this << *v;
    }

    /**
     * @brief scale-encodes unique_ptr value
     * @tparam T type list
     * @param v value to encode
     * @return reference to stream
     */
    template <class T>
    ScaleEncoderStream& operator<<(const std::unique_ptr<T>& v)
    {
        if (v == nullptr)
        {
            BOOST_THROW_EXCEPTION(ScaleEncodeException()
                                  << errinfo_comment("encode exception for DEREF_NULLPOINTER"));
        }
        return *this << *v;
    }

    template <unsigned N>
    ScaleEncoderStream& operator<<(const FixedBytes<N>& fixedData)
    {
        return encodeCollection((size_t)FixedBytes<N>::size, fixedData.begin(), fixedData.end());
    }

    /**
     * @brief scale-encodes collection of same time items
     * @tparam T type of item
     * @param c collection to encode
     * @return reference to stream
     */
    template <class T>
    ScaleEncoderStream& operator<<(const std::vector<T>& c)
    {
        return encodeCollection(c.size(), c.begin(), c.end());
    }

    /**
     * @brief scale-encodes collection of same time items
     * @tparam T type of item
     * @param c collection to encode
     * @return reference to stream
     */
    template <class T>
    ScaleEncoderStream& operator<<(const std::list<T>& c)
    {
        return encodeCollection(c.size(), c.begin(), c.end());
    }

    /**
     * @brief scale-encodes optional value
     * @tparam T value type
     * @param v value to encode
     * @return reference to stream
     */
    template <class T>
    ScaleEncoderStream& operator<<(const boost::optional<T>& v)
    {
        // optional bool is a special case of optional values
        // it should be encoded using one byte instead of two
        // as described in specification
        if constexpr (std::is_same<T, bool>::value)
        {
            return encodeOptionalBool(v);
        }
        if (!v.has_value())
        {
            return putByte(0u);
        }
        return putByte(1u) << *v;
    }

    /**
     * @brief appends sequence of bytes
     * @param v bytes sequence
     * @return reference to stream
     */
    template <class T>
    ScaleEncoderStream& operator<<(const gsl::span<T>& v)
    {
        return encodeCollection(v.size(), v.begin(), v.end());
    }

    /**
     * @brief scale-encodes array of items
     * @tparam T item type
     * @tparam size of the array
     * @param a reference to the array
     * @return reference to stream
     */
    template <typename T, size_t size>
    ScaleEncoderStream& operator<<(const std::array<T, size>& a)
    {
        for (const auto& e : a)
        {
            *this << e;
        }
        return *this;
    }

    /**
     * @brief scale-encodes std::reference_wrapper of a type
     * @tparam T underlying type
     * @param v value to encode
     * @return reference to stream;
     */
    template <class T>
    ScaleEncoderStream& operator<<(const std::reference_wrapper<T>& v)
    {
        return *this << static_cast<const T&>(v);
    }

    /**
     * @brief scale-encodes a string view
     * @param sv string_view item
     * @return reference to stream
     */
    ScaleEncoderStream& operator<<(std::string_view sv)
    {
        return encodeCollection(sv.size(), sv.begin(), sv.end());
    }

    /**
     * @brief scale-encodes any integral type including bool
     * @tparam T integral type
     * @param v value of integral type
     * @return reference to stream
     */
    template <typename T, typename I = std::decay_t<T>,
        typename = std::enable_if_t<std::is_integral<I>::value>>
    ScaleEncoderStream& operator<<(T&& v)
    {
        // encode bool
        if constexpr (std::is_same<I, bool>::value)
        {
            uint8_t byte = (v ? 1u : 0u);
            return putByte(byte);
        }
        // put byte
        if constexpr (sizeof(T) == 1u)
        {
            // to avoid infinite recursion
            return putByte(static_cast<uint8_t>(v));
        }
        // encode any other integer
        encodeInteger<I>(v, *this);
        return *this;
    }

    /**
     * @brief scale-encodes CompactInteger value as compact integer
     * @param v value to encode
     * @return reference to stream
     */
    ScaleEncoderStream& operator<<(const CompactInteger& v);

protected:
    template <size_t I, class... Ts>
    void encodeElementOfTuple(const std::tuple<Ts...>& v)
    {
        *this << std::get<I>(v);
        if constexpr (sizeof...(Ts) > I + 1)
        {
            encodeElementOfTuple<I + 1>(v);
        }
    }

    template <uint8_t I, class... Ts>
    void tryEncodeAsOneOfVariant(const boost::variant<Ts...>& v)
    {
        using T = std::tuple_element_t<I, std::tuple<Ts...>>;
        if (v.type() == typeid(T))
        {
            *this << I << boost::get<T>(v);
            return;
        }
        if constexpr (sizeof...(Ts) > I + 1)
        {
            tryEncodeAsOneOfVariant<I + 1>(v);
        }
    }

    /**
     * @brief scale-encodes any collection
     * @tparam It iterator over collection of bytes
     * @param size size of the collection
     * @param begin iterator pointing to the begin of collection
     * @param end iterator pointing to the end of collection
     * @return reference to stream
     */
    template <class It>
    ScaleEncoderStream& encodeCollection(const CompactInteger& size, It&& begin, It&& end)
    {
        *this << size;
        for (auto&& it = begin; it != end; ++it)
        {
            *this << *it;
        }
        return *this;
    }
    /**
     * @brief puts a byte to buffer
     * @param v byte value
     * @return reference to stream
     */
    ScaleEncoderStream& putByte(uint8_t v)
    {
        m_stream.emplace_back(v);
        return *this;
    }

private:
    ScaleEncoderStream& encodeOptionalBool(const boost::optional<bool>& v);
    // std::deque<uint8_t> m_stream;
    std::deque<uint8_t> m_stream;
};
}  // namespace scale
}  // namespace codec
}  // namespace bcos


#pragma once
#include "nkg_point.h"
#include "nkg_rect.h"
#include "utility/types.h"
#include <stb_image.h>
#include <gsl/gsl>
#include <memory>
#include <vector>
#include <stdlib.h>

namespace cws80 {

class im_image {
public:
    im_image();
    ~im_image();

    void load(const char *filename, uint desired_channels);
    void load_from_memory(const u8 *buffer, uint length, uint desired_channels);
    void load_from_memory(gsl::span<const u8> memory, uint desired_channels)
    {
        load_from_memory(memory.data(), memory.size(), desired_channels);
    }

    const u8 *pixel(uint x, uint y) const;
    u8 alpha(uint x, uint y) const;

    bool is_transparent_column(uint x) const;
    bool is_transparent_row(uint y) const;

    im_image cut(const im_recti &r) const;
    std::vector<im_image> hsplit() const;
    std::vector<im_image> vsplit() const;

    im_recti rect() const;
    im_recti crop_alpha_border() const;

    const u8 *data() const { return data_.get(); }
    uint width() const { return w_; }
    uint height() const { return h_; }
    uint channels() const { return c_; }

    im_image(im_image &&other) = default;
    im_image &operator=(im_image &&other) = default;

private:
    uint w_{}, h_{}, n_{}, c_{};
    std::unique_ptr<u8[], void (*)(void *)> data_{nullptr, &stbi_image_free};

public:
    class exception : public std::runtime_error {
    public:
        using std::runtime_error::runtime_error;
    };
};

}  // namespace cws80


//        Copyright The Authors 2018.
//    Distributed under the 3-Clause BSD License.
//    (See accompanying file LICENSE or copy at
//   https://opensource.org/licenses/BSD-3-Clause)

#pragma once

#include <cstdint>   // for uint8_t
#include <gsl/gsl>  // for gsl::span
#include <string>    // for std::string

namespace blocxxi {
namespace codec {
namespace hex {

std::string Encode(gsl::span<const uint8_t> src, bool reverse = false,
                   bool lower_case = false);

void Decode(gsl::span<const char> src, gsl::span<uint8_t> dest,
            bool reverse = false);

}  // namespace hex
}  // namespace codec
}  // namespace blocxxi


#ifndef ARRUS_CORE_DEVICES_US4R_US4ROUTPUTBUFFER_H
#define ARRUS_CORE_DEVICES_US4R_US4ROUTPUTBUFFER_H

#include <mutex>
#include <condition_variable>
#include <gsl/span>
#include <chrono>
#include <iostream>

#include "arrus/core/api/common/types.h"
#include "arrus/core/api/common/exceptions.h"
#include "arrus/common/asserts.h"
#include "arrus/common/format.h"
#include "arrus/core/common/logging.h"
#include "arrus/core/api/framework/DataBuffer.h"

namespace arrus::devices {

using ::arrus::framework::Buffer;
using ::arrus::framework::BufferElement;

class Us4ROutputBuffer;

/**
 * Buffer element owns the data arrrays, which then are returned to user.
 */
class Us4ROutputBufferElement : public BufferElement {
public:
    using AccumulatorType = uint16;

    using SharedHandle = std::shared_ptr<Us4ROutputBufferElement>;

    Us4ROutputBufferElement(int16 *address, size_t size,
                            const framework::NdArray::Shape &elementShape,
                            const framework::NdArray::DataType elementDataType,
                            AccumulatorType filledAccumulator,
                            size_t position)
        : data(address, elementShape, elementDataType,
               DeviceId(DeviceType::Us4R, 0)), size(size),
          filledAccumulator(filledAccumulator),
          position(position)
          {}

    void release() override {
        std::unique_lock<std::mutex> guard(mutex);
        this->accumulator = 0;
        releaseFunction();
    }

    int16 *getAddress() {
        validateState();
        return data.get<int16>();
    }

    framework::NdArray &getData() override {
        validateState();
        return data;
    }

    size_t getSize() override {
        return size;
    }

    size_t getPosition() override {
        return position;
    }

    void registerReleaseFunction(std::function<void()> &func) {
        releaseFunction = func;
    }

    [[nodiscard]] bool isElementReady() {
        std::unique_lock<std::mutex> guard(mutex);
        return accumulator == filledAccumulator;
    }

    void signal(Ordinal n) {
        std::unique_lock<std::mutex> guard(mutex);
        AccumulatorType us4oemPattern = 1ul << n;
        if((accumulator & us4oemPattern) != 0) {
            throw IllegalStateException("Detected data overflow, buffer is in invalid state.");
        }
        accumulator |= us4oemPattern;
    }

    void resetState() {
        accumulator = 0;
    }

    void markAsInvalid() {
        this->isInvalid = true;
    }

    void validateState() {
        if(this->isInvalid) {
            throw ::arrus::IllegalStateException(
                "The buffer is in invalid state "
                "(probably some data transfer overflow happened).");
        }
    }

private:
    std::mutex mutex;
    framework::NdArray data;
    size_t size;
    AccumulatorType accumulator;
    /** A pattern of the filled accumulator, which indicates that the
     * whole element is ready. */
    AccumulatorType filledAccumulator;
    std::function<void()> releaseFunction;
    bool isInvalid{false};
    size_t position;
};

/**
 * Us4R system's output circular FIFO buffer.
 *
 * The buffer has the following relationships:
 * - buffer contains **elements**
 * - the **element** is filled by many us4oems (with given ordinal)
 *
 * A single element is the output of a single data transfer (the result of running a complete sequence once).
 *
 * The state of each buffer element is determined by the field accumulators:
 * - accumulators[element] == 0 means that the buffer element was processed and is ready for new data from the producer.
 * - accumulators[element] > 0 && accumulators[element] != filledAccumulator means that the buffer element is partially
 *   confirmed by some of us4oems
 * - accumulators[element] == filledAccumulator means that the buffer element is ready to be processed by a consumer.
 *
 * The assumption is here that each element of the buffer has the same size (and the same us4oem offsets).
 */
class Us4ROutputBuffer : public framework::DataBuffer {
public:
    static constexpr size_t DATA_ALIGNMENT = 4096;
    using DataType = int16;

    /**
     * Buffer's constructor.
     *
     * @param us4oemOutputSizes number of bytes to allocate for each of the
     *  us4oem output. That is, the i-th value describes how many bytes will
     *  be written by i-th us4oem to generate a single buffer element.
     */
    Us4ROutputBuffer(const std::vector<size_t> &us4oemOutputSizes,
                     const framework::NdArray::Shape &elementShape,
                     const framework::NdArray::DataType elementDataType,
                     const unsigned nElements)
        : elementSize(0) {
        ARRUS_REQUIRES_TRUE(us4oemOutputSizes.size() <= 16,
                            "Currently Us4R data buffer supports up to 16 us4oem modules.");

        size_t nus4oems = us4oemOutputSizes.size();
        Us4ROutputBufferElement::AccumulatorType filledAccumulator((1ul << nus4oems) - 1);
        // Calculate us4oem write offsets for each buffer element.
        size_t us4oemOffset = 0;
        Ordinal us4oemOrdinal = 0;
        for(auto s : us4oemOutputSizes) {
            us4oemOffsets.emplace_back(us4oemOffset);
            us4oemOffset += s;
            if(s == 0) {
                // We should not expect any response from modules, which do not acquire any data.
                filledAccumulator &= ~(1ul << us4oemOrdinal);
            }
            ++us4oemOrdinal;
        }
        elementSize = us4oemOffset;
        // Allocate buffer with an appropriate size.
        dataBuffer = reinterpret_cast<DataType *>(operator new[](elementSize * nElements,
                                                                 std::align_val_t(DATA_ALIGNMENT)));
        getDefaultLogger()->log(LogSeverity::DEBUG,
                                ::arrus::format(
                                    "Allocated {} ({}, {}) bytes of memory, address: {}",
                                    elementSize * nElements, elementSize, nElements, (size_t) dataBuffer));

        for(unsigned i = 0; i < nElements; ++i) {
            auto elementAddress = reinterpret_cast<DataType *>(reinterpret_cast<int8 *>(dataBuffer) + i * elementSize);
            elements.push_back(std::make_shared<Us4ROutputBufferElement>(elementAddress, elementSize,
                                                                         elementShape, elementDataType,
                                                                         filledAccumulator,
                                                                         i));
        }
        this->initialize();
    }

    ~Us4ROutputBuffer() override {
        ::operator delete(dataBuffer, std::align_val_t(DATA_ALIGNMENT));
        getDefaultLogger()->log(LogSeverity::DEBUG, "Released the output buffer.");
    }

    void registerOnNewDataCallback(framework::OnNewDataCallback &callback) override {
        this->onNewDataCallback = callback;
    }

    [[nodiscard]] const framework::OnNewDataCallback &getOnNewDataCallback() const {
        return this->onNewDataCallback;
    }

    void registerOnOverflowCallback(framework::OnOverflowCallback &callback) override {
        this->onOverflowCallback = callback;
    }

    void registerShutdownCallback(framework::OnShutdownCallback &callback) override {
        this->onShutdownCallback = callback;
    }

    [[nodiscard]] size_t getNumberOfElements() const override {
        return elements.size();
    }

    BufferElement::SharedHandle getElement(size_t i) override {
        return std::static_pointer_cast<BufferElement>(elements[i]);
    }

    uint8 *getAddress(uint16 elementNumber, Ordinal us4oem) {
        return reinterpret_cast<uint8 *>(this->elements[elementNumber]->getAddress()) + us4oemOffsets[us4oem];
    }

    /**
     * Returns a total size of the buffer, the number of bytes.
     */
    [[nodiscard]] size_t getElementSize() const override {
        return elementSize;
    }

    /**
     * Signals the readiness of new data acquired by the n-th Us4OEM module.
     *
     * This function should be called by us4oem interrupt callbacks.
     *
     * @param n us4oem ordinal number
     *
     *  @return true if the buffer signal was successful, false otherwise (e.g. the queue was shut down).
     */
    bool signal(Ordinal n, uint16 elementNr) {
        std::unique_lock<std::mutex> guard(mutex);
        if(this->state != State::RUNNING) {
            getDefaultLogger()->log(LogSeverity::DEBUG, "Signal queue shutdown.");
            return false;
        }
        this->validateState();
        auto &element = this->elements[elementNr];
        try {
            element->signal(n);
        } catch(const IllegalArgumentException &e) {
            this->markAsInvalid();
            throw e;
        }
        if(element->isElementReady()) {
            guard.unlock();
            onNewDataCallback(elements[elementNr]);
        } else {
            guard.unlock();
        }
        return true;
    }

    void markAsInvalid() {
        std::unique_lock<std::mutex> guard(mutex);
        if(this->state != State::INVALID) {
            this->state = State::INVALID;
            for(auto &element: elements) {
                element->markAsInvalid();
            }
            this->onOverflowCallback();
        }
    }

    void shutdown() {
        std::unique_lock<std::mutex> guard(mutex);
        this->onShutdownCallback();
        this->state = State::SHUTDOWN;
        guard.unlock();
    }

    void resetState() {
        this->state = State::INVALID;
        this->initialize();
        this->state = State::RUNNING;
    }

    void initialize() {
        for(auto &element: elements) {
            element->resetState();
        }
    }

    void registerReleaseFunction(size_t element, std::function<void()> &releaseFunction) {
        this->elements[element]->registerReleaseFunction(releaseFunction);
    }

private:
    std::mutex mutex;
    /** A size of a single element IN number of BYTES. */
    size_t elementSize;
    /**  Total size in the number of elements. */
    int16 *dataBuffer;
    /** Host buffer elements */
    std::vector<Us4ROutputBufferElement::SharedHandle> elements;
    /** Relative addresses where us4oem modules will write. IN NUMBER OF BYTES. */
    std::vector<size_t> us4oemOffsets;
    // Callback that should be called once new data arrive.
    framework::OnNewDataCallback onNewDataCallback;
    framework::OnOverflowCallback onOverflowCallback{[]() {}};
    framework::OnShutdownCallback onShutdownCallback{[]() {}};

    // State management
    enum class State {
        RUNNING, SHUTDOWN, INVALID
    };
    State state{State::RUNNING};

    /**
     * Throws IllegalStateException when the buffer is in invalid state.
     *
     * @return true if the queue execution should continue, false otherwise.
     */
    void validateState() {
        if(this->state == State::INVALID) {
            throw ::arrus::IllegalStateException(
                "The buffer is in invalid state "
                "(probably some data transfer overflow happened).");
        } else if(this->state == State::SHUTDOWN) {
            throw ::arrus::IllegalStateException(
                "The data buffer has been turned off.");
        }
    }
};

}

#endif //ARRUS_CORE_DEVICES_US4R_US4ROUTPUTBUFFER_H


/*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
**                                                                                **
** This file forms part of the Underworld geophysics modelling application.         **
**                                                                                **
** For full license and copyright information, please refer to the LICENSE.md file  **
** located at the project root, or contact the authors.                             **
**                                                                                **
**~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*/
#include <mpi.h>
#include <StGermain/libStGermain/src/StGermain.h>
#include <petsc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const Type StGermain_Type = "StGermain";

#include "StGermain_Tools.h"

StgData* StgInit( int argc, char* argv[] ) {
   StgData* data = (StgData*) malloc(sizeof(StgData));
   *data = (StgData){.comm = NULL, .rank=-1, .nProcs=-1, .dictionary=NULL, .argcCpy=0, .argvCpy=NULL };

   //lets copy all this data for safety
   data->argcCpy = argc;
   data->argvCpy = (char**) malloc((argc+1)*sizeof(char*));
   int ii;
   for(ii = 0; ii<argc; ii++){
      data->argvCpy[ii] = (char*)malloc(strlen(argv[ii])+1);
      strcpy(data->argvCpy[ii],argv[ii]);
   }
   data->argvCpy[argc] = NULL;  //add sentinel

   int is_inited;
   MPI_Initialized( &is_inited );
   if(!is_inited)
      MPI_Init( &argc, &argv );
   MPI_Comm_dup( MPI_COMM_WORLD, &data->comm );
   MPI_Comm_size( data->comm, &data->nProcs );
   MPI_Comm_rank( data->comm, &data->rank );
   StGermain_Init( &(data->argcCpy), &(data->argvCpy) );

   return data;
}

int StgFinalise(StgData* data){
   /* Close off everything */
//   StGermain_Finalise();   // avoid this finalise, as py interpreter has its own ideas about when it will destroy objects.
   PetscFinalize();
//   MPI_Finalize();

   /* free up these guys created earlier */
   int ii;
   for(ii = 0; ii<data->argcCpy; ii++)
   	  free(data->argvCpy[ii]);
   free(data->argvCpy);
   free(data);
   return 0; /* success */
}

void StgAbort(StgData* data){
     MPI_Abort( data->comm, EXIT_FAILURE );
}


#pragma once

#include <memory>
#include <memory_resource>

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

#include <sysmakeshift/memory.hpp>

namespace thrustshift {

namespace detail {

//! Allocate without initialization
template <typename T, typename A, typename SizeC>
T* allocate_array(A& alloc, SizeC sizeC) {
	return std::allocator_traits<A>::allocate(alloc, std::size_t(sizeC));
}

template <typename ArrayT, typename A>
std::enable_if_t<
    sysmakeshift::detail::extent_only<ArrayT>::value == 0,
    std::unique_ptr<ArrayT, sysmakeshift::allocator_deleter<ArrayT, A>>>
allocate_unique(A alloc, std::size_t size) {
	using T = std::remove_cv_t<sysmakeshift::detail::remove_extent_only_t<ArrayT>>;
	static_assert(
	    std::is_same<typename std::allocator_traits<A>::value_type, T>::value,
	    "allocator has mismatching value_type");

	T* ptr = detail::allocate_array<T>(alloc, size);
	return {ptr, {std::move(alloc), size}};
}

} // namespace detail

/*! \brief Container which provides memory with a custom allocator.
 *
 *  Useful in combination with memory which is not accessible on the
 *  host. E.g. you can use a device memory allocator with this class
 *  to obtain device memory and pass it as a span to a GPU kernel.
 */
template <typename T, class Allocator>
class not_a_vector {

   public:
	not_a_vector(std::size_t size, Allocator& alloc)
	    : ptr_(detail::allocate_unique<T[]>(alloc, size)), size_(size) {
	}

	gsl_lite::span<T> to_span() const {
		return gsl_lite::make_span(ptr_.get(), size_);
	}

   private:
	std::unique_ptr<T[], sysmakeshift::allocator_deleter<T[], Allocator>> ptr_;
	std::size_t size_;
};

template <typename T, class Resource>
auto make_not_a_vector(std::size_t size, Resource& memory_resource) {
	std::pmr::polymorphic_allocator<T> alloc(&memory_resource);
	return not_a_vector<T, decltype(alloc)>(size, alloc);
}

/*! \brief Make not_a_vector object and the span on the corresponding buffer.
 *
 *  This function makes it easier to construct the owning container and the view
 *  in one line of code with structured bindings.
 *
 *  ```cpp
 *
 *  auto [nav, span] = make_not_a_vector_and_span<T>(N, resource);
 *  ```
 */
template <typename T, class Resource>
auto make_not_a_vector_and_span(std::size_t size, Resource& memory_resource) {
	std::pmr::polymorphic_allocator<T> alloc(&memory_resource);
	auto nav = not_a_vector<T, decltype(alloc)>(size, alloc);
	auto s = nav.to_span();
	return std::make_tuple(std::move(nav), s);
}

} // namespace thrustshift