/*
 * author: Pierre Schnizer
 * created: March 2004
 * file: pygsl/src/initmodule.c
 * $Id: initmodule.c,v 1.14 2008/10/25 20:45:04 schnizer Exp $
 *
 * Changes:
 *     7. October 2003:
 *     Removed the error handler from this file. It is now in
 *     Lib/error_helpers.c Each module must call init_pygsl()
 *     in its init routine. This is necessary to support platforms
 *     where the gsl library is statically linked to the various
 *     modules.
 */
#define  _PyGSL_API_MODULE 1
#include <pygsl/intern.h>
#include <pygsl/utils.h>
#include <pygsl/error_helpers.h>

#include <gsl/gsl_rng.h>
#include <gsl/gsl_ieee_utils.h>
#include <gsl/gsl_version.h>

/* Taken from Modules/getbuildinfo */
#ifndef DATE
#ifdef __DATE__
#define DATE __DATE__
#else
#define DATE "xx/xx/xx"
#endif
#endif

#ifndef TIME
#ifdef __TIME__
#define TIME __TIME__
#else
#define TIME "xx:xx:xx"
#endif
#endif
/* End */

/*
 * Used as a buffer to generate error messages.
 */
static char pygsl_error_str[512];
#include "profile.c"
#include "error_helpers.c"
#include "general_helpers.c"
#include "complex_helpers.c"
#include "block_helpers.c"
#include "function_helpers.c"
#include "rng_helpers.c"

static PyObject * debuglist = NULL;

static int
PyGSL_register_debug_flag(int * ptr, const char * module_name)
{
#if DEBUG == 1
     PyObject * cobj;
     FUNC_MESS_BEGIN();
     if ((cobj = PyCObject_FromVoidPtr((void *) ptr, NULL)) == NULL){
	  fprintf(stderr, "Could not create PyCObject for ptr %p to debug flag for module %s\n",
		  (void * ) ptr, module_name);
	  return GSL_EFAILED;
     }
     DEBUG_MESS(2, "Registering ptr %p for module %s", (void *) ptr,  module_name);
     if(PyList_Append(debuglist, cobj) != 0){
	  return GSL_EFAILED;
     }
     *ptr = pygsl_debug_level;
     FUNC_MESS_END();
     return GSL_SUCCESS;
#endif
     PyGSL_ERROR("Why does it try to register the debug level callback? Should"
	       " use the compile time value DEBUG!", GSL_ESANITY);
}

static PyObject *
PyGSL_set_debug_level(PyObject *self, PyObject *args)
{

     FUNC_MESS_BEGIN();
#if DEBUG == 1
     PyObject *o;
     int tmp, i, max, *ptr;
     if(!PyArg_ParseTuple(args, "i", &tmp))
	  return NULL;
     if(tmp >= 0 && tmp <PyGSL_DEBUG_MAX)
	  ;
     else
	  PyGSL_ERROR_VAL("Only accept debug levels between 0 and PyGSL_DEBUG_MAX", GSL_EINVAL, NULL);

     pygsl_debug_level = tmp;
     max = PySequence_Size(debuglist);
     DEBUG_MESS(3, "Setting debug level to %d for %d modules", pygsl_debug_level, max);
     for(i = 0; i < max; ++i){
	  if((o = PySequence_GetItem(debuglist, i)) == NULL){
	       fprintf(stderr, "In file %s at line %d; Could not get element %d\n",
		       __FILE__, __LINE__, i);
	       continue;
	  }
	  ptr = (int *)PyCObject_AsVoidPtr(o);
	  DEBUG_MESS(2, "Setting info ptr %p", (void *) ptr);
	  *ptr = tmp;
     }
     Py_INCREF(Py_None);
     FUNC_MESS_END();
     return Py_None;
#else
     PyGSL_ERROR_NULL("PyGSL was not compiled with DEBUG = 1; Can not set DEBUG level!", GSL_EUNIMPL);
#endif
}

static PyObject *
PyGSL_get_debug_level(PyObject *self, PyObject *args)
{
     int tmp;
#if DEBUG == 1
     tmp = (int) pygsl_debug_level;
#else
     tmp = DEBUG;
#endif
     return PyInt_FromLong(tmp);
}

static void * _PyGSL_API[PyGSL_NENTRIES_NUM];

static PyMethodDef initMethods[] = {
     {"get_debug_level", PyGSL_get_debug_level, METH_NOARGS, NULL},
     {"set_debug_level", PyGSL_set_debug_level, METH_VARARGS, NULL},
     {"vector_transform_counter",  PyGSL_get_vector_transform_counter ,METH_NOARGS, NULL},
     {"matrix_transform_counter",  PyGSL_get_matrix_transform_counter ,METH_NOARGS, NULL},
     {"complex_transform_counter", PyGSL_get_complex_transform_counter,METH_NOARGS, NULL},
     {"float_transform_counter",   PyGSL_get_float_transform_counter  ,METH_NOARGS, NULL},
     {"register_exceptions",       PyGSL_register_exceptions,          METH_VARARGS,NULL},
     {"register_warnings",         PyGSL_register_warnings,            METH_VARARGS,NULL},
     {NULL,     NULL, 0, NULL}        /* Sentinel */
};

static void
PyGSL_init_api(void)
{
     int i;

     for(i=0;i<PyGSL_NENTRIES_NUM; ++i){
	  _PyGSL_API[i] = NULL;
     }
     _PyGSL_API[PyGSL_api_version_NUM                          ] = (void *) PyGSL_API_VERSION;
     _PyGSL_API[PyGSL_RNG_ObjectType_NUM                       ] = NULL;
     _PyGSL_API[PyGSL_error_flag_NUM                           ] = (void *) &PyGSL_error_flag;
     _PyGSL_API[PyGSL_error_flag_to_pyint_NUM                  ] = (void *) &PyGSL_error_flag_to_pyint;
     _PyGSL_API[PyGSL_add_traceback_NUM                        ] = (void *) &PyGSL_add_traceback;
     _PyGSL_API[PyGSL_module_error_handler_NUM                 ] = (void *) &PyGSL_module_error_handler;

     _PyGSL_API[PyGSL_error_string_for_callback_NUM            ] = (void *) & PyGSL_set_error_string_for_callback        ;
     _PyGSL_API[PyGSL_pyfloat_to_double_NUM                    ] = (void *) & PyGSL_pyfloat_to_double                    ;
     _PyGSL_API[PyGSL_pylong_to_ulong_NUM                      ] = (void *) & PyGSL_pylong_to_ulong                      ;
     _PyGSL_API[PyGSL_pylong_to_uint_NUM                       ] = (void *) & PyGSL_pylong_to_uint                       ;
     _PyGSL_API[PyGSL_check_python_return_NUM                  ] = (void *) & PyGSL_check_python_return                  ;
     _PyGSL_API[PyGSL_clear_name_NUM                           ] = (void *) & PyGSL_clear_name                           ;

     _PyGSL_API[PyGSL_PyComplex_to_gsl_complex_NUM             ] = (void *) & PyGSL_PyComplex_to_gsl_complex             ;
     _PyGSL_API[PyGSL_PyComplex_to_gsl_complex_float_NUM       ] = (void *) & PyGSL_PyComplex_to_gsl_complex_float       ;
     _PyGSL_API[PyGSL_PyComplex_to_gsl_complex_long_double_NUM ] = (void *) & PyGSL_PyComplex_to_gsl_complex_long_double ;

     _PyGSL_API[PyGSL_stride_recalc_NUM                        ] = (void *) & PyGSL_stride_recalc                        ;
     _PyGSL_API[PyGSL_PyArray_new_NUM                          ] = (void *) & PyGSL_New_Array                            ;
     _PyGSL_API[PyGSL_PyArray_copy_NUM                         ] = (void *) & PyGSL_Copy_Array                           ;
/*
     _PyGSL_API[PyGSL_PyArray_prepare_gsl_vector_view_NUM      ] = (void *) & PyGSL_PyArray_prepare_gsl_vector_view      ;
     _PyGSL_API[PyGSL_PyArray_prepare_gsl_matrix_view_NUM      ] = (void *) & PyGSL_PyArray_prepare_gsl_matrix_view      ;
*/
     _PyGSL_API[PyGSL_PyArray_generate_gsl_vector_view_NUM     ] = (void *) & PyGSL_PyArray_generate_gsl_vector_view     ;
     _PyGSL_API[PyGSL_PyArray_generate_gsl_matrix_view_NUM     ] = (void *) & PyGSL_PyArray_generate_gsl_matrix_view     ;
     _PyGSL_API[PyGSL_copy_pyarray_to_gslvector_NUM            ] = (void *) & PyGSL_copy_pyarray_to_gslvector            ;
     _PyGSL_API[PyGSL_copy_pyarray_to_gslmatrix_NUM            ] = (void *) & PyGSL_copy_pyarray_to_gslmatrix            ;
     _PyGSL_API[PyGSL_copy_gslvector_to_pyarray_NUM            ] = (void *) & PyGSL_copy_gslvector_to_pyarray            ;
     _PyGSL_API[PyGSL_copy_gslmatrix_to_pyarray_NUM            ] = (void *) & PyGSL_copy_gslmatrix_to_pyarray            ;

     _PyGSL_API[PyGSL_gsl_rng_from_pyobject_NUM                ] = (void *) & PyGSL_gsl_rng_from_pyobject                ;
     _PyGSL_API[PyGSL_function_wrap_helper_NUM                 ] = (void *) & PyGSL_function_wrap_helper                 ;
     _PyGSL_API[PyGSL_register_debug_flag_NUM                  ] = (void *) & PyGSL_register_debug_flag                  ;
     _PyGSL_API[PyGSL_vector_or_double_NUM                     ] = (void *) & PyGSL_vector_or_double                     ;
     _PyGSL_API[PyGSL_warning_NUM                              ] = (void *) & PyGSL_warning                              ;
     _PyGSL_API[PyGSL_pyint_to_int_NUM                         ] = (void *) & PyGSL_pyint_to_int                         ;
     _PyGSL_API[PyGSL_vector_check_NUM                         ] = (void *) & PyGSL_vector_check                         ;
     _PyGSL_API[PyGSL_matrix_check_NUM                         ] = (void *) & PyGSL_matrix_check                         ;
     _PyGSL_API[PyGSL_array_check_NUM                          ] = (void *) & PyGSL_array_check                          ;

}

DL_EXPORT(void) initinit(void)
{
  PyObject *m = NULL, *d = NULL, *version=NULL, *date=NULL, *api = NULL;

  m = Py_InitModule("pygsl.init", initMethods);
  import_array();

  if(m == NULL){
       fprintf(stderr, "I could not init pygsl.init!");
       return;
  }

  d = PyModule_GetDict(m);
  if(d == NULL){
       fprintf(stderr, "I could not get the module dict for  pygsl.init!");
       return;
  }

  PyGSL_init_api();

  if(PyGSL_init_errno() != 0){
       PyErr_SetString(PyExc_ImportError, "Failed to init errno handling!");
  }
  PyGSL_API = _PyGSL_API;
  PyGSL_SET_ERROR_HANDLER();

  api = PyCObject_FromVoidPtr((void *) PyGSL_API, NULL);
  assert(api);
  if (PyDict_SetItemString(d, "_PYGSL_API", api) != 0){
       PyErr_SetString(PyExc_ImportError,
		       "I could not add  _PYGSL_API!");
       return;
  }

  version = PyString_FromString(GSL_VERSION);
  if(version == NULL){
       fprintf(stderr, "I could not create the version string for pygsl.init!");
       return;
  }
  if(PyDict_SetItemString(d, "compiled_gsl_version", version) != 0){
       fprintf(stderr, "I could not add the compile version string to the module dict of pygsl.init!");
       return;
  }

  version = PyString_FromString(gsl_version);
  if(version == NULL){
       fprintf(stderr, "I could not create the version string for pygsl.init!");
       return;
  }
  if(PyDict_SetItemString(d, "run_gsl_version", version) != 0){
       fprintf(stderr, "I could not add the run version string to the module dict of pygsl.init!");
       return;
  }

  date = PyString_FromString(DATE " " TIME);
  if(version == NULL){
       fprintf(stderr, "I could not create the date string for pygsl.init!");
       return;
  }
  if(PyDict_SetItemString(d, "compile_date", date) != 0){
       fprintf(stderr, "I could not add the date version string to the module dict of pygsl.init!");
       return;
  }

  if((debuglist = PyList_New(0)) == NULL){
       fprintf(stderr, "Failed to init Debug list!\n");
  }
  /*
   * These functions will be moved to the approbriate modules and the user will
   * have to call them explicitly when needed.
   */

  return;
}


/*
  Copyright (c) 2016-2017 Hong Xu

  This file is part of WCSPLift.

  WCSPLift 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.

  WCSPLift 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 WCSPLift.  If not, see <http://www.gnu.org/licenses/>.
*/

/** \file WCSPInstance.h
 *
 * Define the definition of WCSP instances and the constraints in it. Algorithms that can be applied
 * directly on WCSP instances, i.e., the min-sum message passing algorithm \cite xkk17 and integer
 * linear programming \cite xkk17a, are also included in this file.
 */

#ifndef WCSPINSTANCE_H_
#define WCSPINSTANCE_H_

#include <array>
#include <cstdint>
#include <istream>
#include <map>
#include <numeric>
#include <set>
#include <stdexcept>
#include <utility>
#include <vector>

#include <boost/dynamic_bitset.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <cblas.h>

#include "global.h"
#include "RunningTime.h"
#include "LinearProgramSolver.h"

/** \brief Weighted constraint.
 *
 * This class describes a weighted constraint. It consists of the IDs of the variables as well as
 * how the weight corresponding to each assignment.
 *
 * \tparam VarIdType The type of variable IDs. It must be an integer type and defaults to \p
 * intmax_t.
 *
 * \tparam WeightType The type of weights. It must be a numeric type and defaults to \p double.
 *
 * \tparam ValueType The type of values of all variables in the constraint. It should usually be a
 * bitset type and defaults to \p boost::dynamic_bitset<>.
 */
template < class VarIdType = intmax_t,
           class WeightType = double,
           class ValueType = boost::dynamic_bitset<>,  // The type of the values to represent each constraint
           class NonBooleanValueType = size_t>
class Constraint
{
public:
    /** Alias of \p VarIDType. */
    typedef VarIdType variable_id_t;

    /** Alias of \p ValueType. */
    typedef ValueType value_t;

    /** Alias of \p WeightType. */
    typedef WeightType weight_t;

    /** Alias of \p NonBooleanValueType. */
    typedef NonBooleanValueType non_boolean_value_t;

private:
    // This class is only used for Polynomial key comparison. This function ensures keys with
    // a smaller number of variables must precede larger number of variables.
    class PolynomialKeyComparison
    {
    public:
        bool operator () (const std::set<variable_id_t>& a, const std::set<variable_id_t>& b) const
        {
            if (a.size() > b.size())
                return true;

            if (a.size() < b.size())
                return false;

            return a < b;
        }
    };
public:
    /** The polynomial form of constraints consisting of the coefficient of each term (each term is
     * represented by an assignment of values to variables). */
    typedef std::map<std::set<variable_id_t>, weight_t, PolynomialKeyComparison> Polynomial;

private:
    std::vector<variable_id_t> nonBooleanVariables;
    std::vector<variable_id_t> variables;
    std::map<value_t, weight_t> weights;
    std::map<std::vector<non_boolean_value_t>, weight_t> nonBooleanWeights;

public:
    /** Alias of \p Constraint<VarIdType, WeightType, ValueType, NonBooleanValueType>, i.e., the
     * class type itself. */
    typedef Constraint<VarIdType, WeightType, ValueType, NonBooleanValueType> self_t;

    /** \brief Get the list of the IDs of non-Boolean variables in the constraint.
     *
     * \return A list of variable IDs.
     */
    inline const std::vector<variable_id_t>& getNonBooleanVariables() const noexcept
    {
        return nonBooleanVariables;
    }

    /** \brief Get the list of the IDs of variables in the constraint.
     *
     * \return A list of variable IDs.
     */
    inline const std::vector<variable_id_t>& getVariables() const noexcept
    {
        return variables;
    }

    /** \brief Get the ID of the <tt>i</tt>'th variable.
     *
     * \param[in] i The index of the variable of which to get the ID.
     *
     * \return The ID of the <tt>i</tt>'th variable.
     */
    inline variable_id_t getVariable(size_t i) const noexcept
    {
        return variables.at(i);
    }

    /** \brief Set the IDs of the variables in the constraint from a \p std::vector<variable_id_t>
     * object.
     *
     * \param[in] variables: A list of variable IDs to set.
     */
    inline void setVariables(const std::vector<variable_id_t>& variables) noexcept
    {
        this->variables = variables;
        this->weights.clear();
    }

    /** \brief Set the IDs of the non-Boolean variables in the constraint from a \p
     * std::vector<variable_id_t> object.
     *
     * \param[in] variables: A list of variable IDs to set.
     */
    inline void setNonBooleanVariables(const std::vector<variable_id_t>& variables) noexcept
    {
        this->nonBooleanVariables = variables;
        this->weights.clear();
    }

    /** \brief Set the IDs of the variables in the constraint from a range of iterators.
     *
     * \param[in] b the beginning iterator.
     *
     * \param[in] e the ending iterator.
     */
    template<class Iter>
    inline void setVariables(Iter b, Iter e) noexcept
    {
        variables.clear();
        std::copy(b, e, std::back_inserter(variables));
    }

    /** \brief Set the weight of a given assignment of values to variables specified by \p v to \p
     * w.
     *
     * \param[in] v An assignment of values to variables.
     *
     * \param[in] w A weight.
     *
     * \return A reference to the class object itself.
     */
    inline self_t& setWeight(const value_t& v, weight_t w)
    {
        weights[v] = w;
        return *this;
    }

    /** \brief Set the weight of a given assignment of values to non-Boolean variables specified by
     * \p v to \p w.
     *
     * \param[in] v An assignment of values to variables.
     *
     * \param[in] w A weight.
     *
     * \return A reference to the class object itself.
     */
    inline self_t& setWeight(const std::vector<non_boolean_value_t>& v, weight_t w)
    {
        nonBooleanWeights[v] = w;
        return *this;
    }

    /** \brief Get the weight of a given assignment of values to variables specified by \p v.
     *
     * \param[in] v The assignment of values to variables.
     *
     * \return The weight of a given assignment of values to variables specified by \p v.
     */
    inline weight_t getWeight(const value_t& v) const noexcept
    {
        auto it = weights.find(v);
        if (it == weights.end()) // not found, return 0
            return weight_t();

        return it->second;
    }

    /** \brief Get the weight of a given assignment of values to non-Boolean variables specified by
     * \p v.
     *
     * \param[in] v The assignment of values to variables.
     *
     * \return The weight of a given assignment of values to variables specified by \p v.
     */
    inline weight_t getWeight(const std::vector<non_boolean_value_t>& v) const noexcept
    {
        auto it = nonBooleanWeights.find(v);
        if (it == nonBooleanWeights.end()) // not found, return 0
            return weight_t();

        return it->second;
    }

    /** \brief Get a map object that maps assignments of values to variables to weights.
     *
     * \return A map object that maps assignments of values to variables to weights.
     */
    inline const std::map<value_t, weight_t> getWeights() const noexcept
    {
        return weights;
    }

    /** \brief Represent the constraint using a \p boost::property_tree::ptree object.
     *
     * \param[out] t A \p boost::property_tree::ptree object that represents the constraint.
     */
    void toPropertyTree(boost::property_tree::ptree& t) const
    {
        using namespace boost::property_tree;

        t.clear();

        // put the variables
        ptree vs;
        for (auto& v : variables)
            vs.push_back(
                std::make_pair("", ptree(std::to_string(v))));

        t.put_child("variables", vs);

        // put the weights
        ptree ws;
        for (auto& w : weights)
        {
            ptree we;

            for (size_t i = 0; i < getVariables().size(); ++ i)
            {
                if (w.first[i])
                    we.push_back(std::make_pair("", ptree("1")));
                else
                    we.push_back(std::make_pair("", ptree("0")));
            }

            we.push_back(std::make_pair("", ptree(std::to_string(w.second))));

            ws.push_back(std::make_pair("", we));
        }

        t.put_child("weights", ws);
    }

    /** \brief Compute the coefficients of the polynomial converted from a constraint according to
     * \cite k08.
     *
     * \param[out] p A Constraint::Polynomial object corresponding to the polynomial representation
     * of the constraint.
     */
    void toPolynomial(Polynomial& p) const noexcept
    {
        size_t s = getVariables().size();

        // we basically solve A * x = b

        size_t max_int = ((size_t) 1) << s;

        // matrix a
        double * a = new double[max_int * max_int];
        memset(a, 0, max_int * max_int * sizeof(double));

/// \cond
#define ENTRY(a, x, y) a[(y) * max_int + (x)]
/// \endcond
        // set the first column to 1
        for (size_t i = 0; i < max_int; ++ i)
            ENTRY(a, i, 0) = 1.0;
        // Set the lower triangle. The row number corresponds to the assignments of variables, and
        // the col number corresponds to terms of the polynomial in the following order:
        // X_1, X_2, X_1 X_2, X_3, X_1 X_3, X_2 X_3, X_1 X_2 X_3...
        for (size_t i = 1; i < max_int; ++ i)
            for (size_t j = 1; j <= i; ++ j)
                // (i|j)==i : the 1-bits integer i includes all the 1-bits of j
                ENTRY(a, i, j) = ((i | j) == i) ? 1.0 : 0.0;
#undef ENTRY

        // assign the vector b
        double * x = new double[max_int];
        for (size_t i = 0; i < max_int; ++ i)
            x[i] = getWeight(value_t(s, i));

        cblas_dtrsv(CblasColMajor, CblasLower, CblasNoTrans, CblasUnit, max_int, a, max_int, x, 1);
        delete[] a;

        // Insert all the coefficients into the polynomial.
        for (size_t i = 0; i < max_int; ++ i)
        {
            typename Polynomial::key_type k;

            for (size_t j = 0; j < s; ++ j)
                if ((((size_t) 1u) << j) & i)
                    k.insert(getVariable(j));

            p[k] += x[i];
        }

        delete[] x;
    }
};

/** \brief A WCSP instance.
 *
 * This class describes a WCSP instance. It consists a set of constraints.
 *
 * \tparam VarIdType The type of variable IDs. It must be an integer type and defaults to \p
 * intmax_t.
 *
 * \tparam WeightType The type of weights. It must be a numeric type and defaults to \p double.
 *
 * \tparam ConstraintValueType The type of values of all variables in the constraints in the WCSP
 * instance. It should usually be a bitset type and defaults to \p boost::dynamic_bitset<>.
 */
template <class VarIdType = intmax_t, class WeightType = double,
          class ConstraintValueType = boost::dynamic_bitset<>,
          class NonBooleanValueType = size_t>
class WCSPInstance
{
public:
    /** Alias of \p VarIdType. */
    typedef VarIdType variable_id_t;
    /** Alias of \p WeightType. */
    typedef WeightType weight_t;
    /** Alias of \p ConstraintValueType. */
    typedef ConstraintValueType constraint_value_t;
    /** Alias of \p NonBooleanValueType. */
    typedef NonBooleanValueType non_boolean_value_t;

public:
    /** Alias of the Constraint type in the WCSP instance, i.e., \p
     * Constraint<variable_id_t,weight_t,constraint_value_t>.
     */
    typedef Constraint<variable_id_t, weight_t, constraint_value_t> constraint_t;

private:
    std::vector<constraint_t> constraints;

    // map from non-Boolean variables to their Boolean variable representations
    std::vector<std::vector<variable_id_t> > nonBooleanVariables;

public:

    /** \brief Get the mapping from original variables to Boolean variables.
     *
     * \return The mapping.
     */
    inline const std::vector<std::vector<variable_id_t> >& getNonBooleanVariables()
        const noexcept
    {
        return nonBooleanVariables;
    }

    /** \brief Display the mapping from original variables to Boolean variables.
     */
    inline void displayNonBooleanVariableMapping() const noexcept
    {
        std:: cout << "--- Non-Boolean Variable Mapping BEGINS ---" << std::endl;
        for (auto i = 0; i < nonBooleanVariables.size(); ++ i)
        {
            std::cout << i << '\t';
            for (auto j : nonBooleanVariables[i])
            {
                std::cout << j << ' ';
            }
            std::cout << std::endl;
        }
        std:: cout << "--- Non-Boolean Variable Mapping ENDS ---" << std::endl;
    }

    /** \brief Get the list of constraints.
     *
     * \return A list of \p constraint_t objects.
     */
    inline const std::vector<constraint_t>& getConstraints() const noexcept
    {
        return constraints;
    }

    /** \brief Get the <tt>i</tt>'th constraint.
     *
     * \param[in] i The index of the Constraint object to get.
     *
     * \return The <tt>i</tt>'th constraint.
     */
    inline const constraint_t& getConstraint(size_t i) const noexcept
    {
        return constraints.at(i);
    }

    /** \brief Compute the total weight corresponding to an assignment of values to all variables.
     *
     * \param[in] assignments The assignment of values to all variables.
     *
     * \return The computed total weight.
     */
    inline weight_t computeTotalWeight(
        const std::map<variable_id_t, non_boolean_value_t>& assignments) const noexcept
    {
        weight_t tw = 0;

        for (const auto& c : constraints)
        {
            std::vector<non_boolean_value_t> val(c.getNonBooleanVariables().size());
            for (size_t i = 0; i < c.getNonBooleanVariables().size(); ++ i)
            {
                try
                {
                    val[i] = assignments.at(c.getNonBooleanVariables().at(i));
                } catch (std::out_of_range& e)
                {
                    // Here, it means the variable assignments does not hold
                    // c.getNonBooleanVariables().at(i) -- the CCG does not contain that variable.
                    // This may well be that the variable itself is a "dummy" variable -- the value
                    // of it does not affect the total weight. In this case, we always assign zero
                    // (or anything else) to it.
                    val[i] = 0;
                }
            }

            tw += c.getWeight(val);
        }

        return tw;
    }

public:
    /** \brief Load a problem instance from a stream.
     *
     * \param[in] f The input stream.
     */
    void load(std::istream& f);

    /** \brief Load the problem in DIMACS format. The format specification is at
     * http://graphmod.ics.uci.edu/group/WCSP_file_format
     *
     * \param[in] f The input stream.
     */
    void loadDimacs(std::istream& f);

    /** \brief Load the problem in UAI format. The format specification is at
     * http://www.hlt.utdallas.edu/~vgogate/uai14-competition/modelformat.html
     *
     * \param[in] f The input stream.
     */
    void loadUAI(std::istream& f);

    /** List of supported input file formats.
     */
    enum class Format
    {
        DIMACS,
        UAI
    };

    /** \brief Construct a WCSP instance from an input stream in a given format.
     *
     * \param[in] f The input stream.
     *
     * \param[in] format The format of \p f.
     */
    WCSPInstance(std::istream& f, Format format)
    {
        switch (format)
        {
        case Format::DIMACS:
            loadDimacs(f);
            break;
        case Format::UAI:
            loadUAI(f);
            break;
        }
    }

    /** \brief Convert this WCSP instance to a human-readable string.
     *
     * \return The human-readable string.
     */
    std::string toString() const noexcept
    {
        std::stringstream ss;

        ss << '{' << std::endl;
        for (auto& c : constraints)
        {
            ss << c;
        }

        return ss.str();
    }

    /** \brief Solve the WCSP instance using the min-sum message passing algorithm.
     *
     * \param[in] delta The threshold for convergence determination. That is, the algorithm
     * terminates iff all messages change within \p delta compared with the previous iteration.
     *
     * \return A solution to the WCSP instance.
     */
    std::map<variable_id_t, bool> solveUsingMessagePassing(weight_t delta) const noexcept
    {
        std::map<variable_id_t, std::set<const constraint_t*> > constraint_list_for_v;

        // Initialize all messages.
        std::map<std::pair<variable_id_t, const constraint_t*>, std::array<weight_t, 2> > msgs_v_to_c;
        std::map<std::pair<const constraint_t*, variable_id_t>, std::array<weight_t, 2> > msgs_c_to_v;
        for (const auto& c : constraints)
        {
            for (auto v : c.getVariables())
            {
                constraint_list_for_v[v].insert(&c);
                msgs_v_to_c[std::make_pair(v, &c)] = {0, 0};
                msgs_c_to_v[std::make_pair(&c, v)] = {0, 0};
            }
        }

        bool converged = false;
        uintmax_t num_iterations = 0;
        do
        {
            if (RunningTime::GetInstance().isTimeOut())  // time's up
                break;

            ++ num_iterations;

            converged = true;

            auto msgs_v_to_c0 = msgs_v_to_c;
            auto msgs_c_to_v0 = msgs_c_to_v;

            // Update all the messages from variables to constraints.
            for (auto& it : msgs_v_to_c)
            {
                auto v = it.first.first;
                auto c = it.first.second;

                std::array<weight_t, 2> m = {0, 0};

                for (auto nc : constraint_list_for_v.at(v))
                {
                    if (nc != c)
                    {
                        const auto m_to_v = msgs_c_to_v0.find(std::make_pair(nc, v))->second;

                        m[0] += m_to_v.at(0);
                        m[1] += m_to_v.at(1);
                    }
                }

                it.second = m;

                // is it now convergent?
                if (converged)
                {
                    auto old_m = msgs_v_to_c0.at(it.first);

                    if (std::abs(old_m.at(0) - m.at(0)) > delta ||
                        std::abs(old_m.at(1) - m.at(1)) > delta)
                        converged = false;
                }
            }

            // Update all the messages from constraints to variables.
            for (auto& it : msgs_c_to_v)
            {
                auto c = it.first.first;
                auto v = it.first.second;

                std::vector<std::array<weight_t, 2> > m_to_c;
                m_to_c.reserve(c->getVariables().size());
                std::vector<variable_id_t> v_ids;
                v_ids.reserve(c->getVariables().size());
                size_t self_index;   // index of the variable itself
                for (size_t i = 0; i < c->getVariables().size(); ++ i)
                {
                    auto nv = c->getVariable(i);

                    m_to_c.push_back(msgs_v_to_c0.at(std::make_pair(nv, c)));
                    v_ids.push_back(nv);

                    if (nv == v)
                        self_index = i;
                }

                std::array<weight_t, 2> m = {
                    std::numeric_limits<weight_t>::max(), std::numeric_limits<weight_t>::max()
                };

                // TODO: Change the iteration which does not have an upper limit of number of bits.
                for (unsigned long i = 0; i < (1ul << c->getVariables().size()); ++ i)
                {
                    constraint_value_t values(c->getVariables().size(), i);

                    weight_t sum = c->getWeight(values);

                    for (size_t j = 0; j < c->getVariables().size(); ++ j)
                        if (c->getVariable(j) != v)
                            sum += msgs_v_to_c0.at(
                                std::make_pair(c->getVariable(j), c))[values[j] ? 1 : 0];

                    size_t index_update = values[self_index] ? 1 : 0;

                    if (m[index_update] > sum)
                        m[index_update] = sum;
                }

                auto m_min = m[0] < m[1] ? m[0] : m[1];
                m[0] -= m_min;
                m[1] -= m_min;

                it.second = m;

                // is it now convergent?
                if (converged)
                {
                    auto old_m = msgs_c_to_v0.at(it.first);

                    if (std::abs(old_m.at(0) - m.at(0)) > delta ||
                        std::abs(old_m.at(1) - m.at(1)) > delta)
                        converged = false;
                }
            }
        } while (!converged);

        std::cout << "Number of iterations: " << num_iterations << std::endl;

        // compute the variable values
        std::map<variable_id_t, std::array<weight_t, 2> > assignments_w;

        converged = true;
        for (const auto& c : constraints)
            for (auto v : c.getVariables())
            {
                assignments_w[v][0] += msgs_c_to_v[std::make_pair(&c, v)][0];
                assignments_w[v][1] += msgs_c_to_v[std::make_pair(&c, v)][1];
                if (isinf(assignments_w[v][0]) || isinf(assignments_w[v][1]))
                    converged = false;
            }

        std::map<variable_id_t, bool> assignments;
        for (const auto& a : assignments_w)
            assignments[a.first] = a.second[0] > a.second[1] ? a.second[1] : a.second[0];

        if (!converged)
            std::cout << "*** Message passing not converged! ***" << std::endl;

        return assignments;
    }

    /** \brief Solve the WCSP instance using linear programming.
     *
     * \param[in] lps The linear program solver to be used.
     *
     * \return A solution to the WCSP instance.
     */
    std::map<variable_id_t, non_boolean_value_t> solveUsingLinearProgramming(
        LinearProgramSolver& lps) const noexcept
    {
        lps.reset();
        lps.setTimeLimit(RunningTime::GetInstance().getTimeLimit().count());

        lps.setObjectiveType(LinearProgramSolver::ObjectiveType::MIN);

        auto constraints = this->getConstraints();

        // Every variable needs a unary constraint. This is the indices of all unary constraints.
        std::vector<size_t> unary_indices(this->getNonBooleanVariables().size(),
                                                 std::numeric_limits<size_t>::max());
        size_t num_unary = 0;
        for (size_t i = 0; i < constraints.size(); ++ i)
        {
            auto& vs = constraints.at(i).getVariables();
            if (vs.size() == 1)  // unary constraint
            {
                unary_indices[vs.at(0)] = i;
                ++ num_unary;
            }
        }
        constraints.reserve(constraints.size() + unary_indices.size() - num_unary);
        for (variable_id_t i = 0; i < unary_indices.size(); ++ i)
        {
            // no unary constraint for this var
            if (unary_indices.at(i) == std::numeric_limits<size_t>::max())
            {
                constraint_t cons;
                cons.setNonBooleanVariables(std::vector<variable_id_t>{i});
                constraints.push_back(std::move(cons));
                unary_indices[i] = constraints.size() - 1;
            }
        }

        std::vector<std::vector<LinearProgramSolver::variable_id_t> > lp_variables(
            constraints.size());

        // add LP variables and constraints
        for (size_t i = 0; i < constraints.size(); ++ i)
        {
            size_t num_values = 1;
            // Cartesian product of all values
            for (const auto& nbv : constraints.at(i).getNonBooleanVariables())
                num_values *= nonBooleanVariables[nbv].size() + 1;
            lp_variables[i].reserve(num_values);
            for (size_t j = 0; j < num_values; ++ j)  // add all variables
            {
                std::vector<non_boolean_value_t> val;
                val.reserve(constraints.at(i).getNonBooleanVariables().size());

                size_t j0 = j;
                for (const auto& nbv : constraints.at(i).getNonBooleanVariables())
                {
                    size_t d = nonBooleanVariables[nbv].size() + 1;
                    val.push_back(j0 % d);
                    j0 /= d;
                }

                lp_variables[i].push_back(lps.addVariable(constraints.at(i).getWeight(val)));
            }

            // Only one value in a WCSP constraint can take effect.
            lps.addConstraint(lp_variables[i], std::vector<double>(num_values, 1.0), 1.0,
                              LinearProgramSolver::ConstraintType::EQUAL);
        }

        // Add constraints that make sure LP variables that represent overlapped WCSP variables are
        // consistent.
        for (size_t i = 0; i < lp_variables.size(); ++ i)
        {
            for (size_t j : unary_indices)
            {
                if (j <= i)  // Don't do a pair twice.
                    continue;

                // Get the overlap map.
                std::vector<std::pair<size_t, size_t> > overlapped_variables;

                // Whether a variable is overlapping?
                std::vector<bool> vs_overlap_p[2];
                vs_overlap_p[0].resize(constraints.at(i).getNonBooleanVariables().size());
                vs_overlap_p[1].resize(constraints.at(j).getNonBooleanVariables().size());
                std::array<std::vector<size_t>, 2> domain_sizes;
                domain_sizes[0].reserve(constraints.at(i).getNonBooleanVariables().size());
                domain_sizes[1].reserve(constraints.at(j).getNonBooleanVariables().size());
                std::vector<size_t> overlapped_domain_sizes;

                for (size_t z = 0; z < 2; ++ z)
                {
                    size_t c = z ? j : i;
                    for (size_t k = 0; k < constraints.at(c).getNonBooleanVariables().size();
                         ++ k)
                        domain_sizes[z].push_back(
                            nonBooleanVariables[
                                constraints.at(c).getNonBooleanVariables()[k]].size() + 1);
                }

                for (size_t k0 = 0; k0 < constraints.at(i).getNonBooleanVariables().size(); ++ k0)
                    for (size_t k1 = 0; k1 < constraints.at(j).getNonBooleanVariables().size(); ++ k1)
                    {
                        if (constraints.at(i).getNonBooleanVariables()[k0] ==
                            constraints.at(j).getNonBooleanVariables()[k1])
                        {
                            overlapped_variables.push_back(std::make_pair(k0, k1));
                            overlapped_domain_sizes.push_back(
                                nonBooleanVariables[
                                    constraints.at(i).getNonBooleanVariables()[k0]].size() + 1
                                );
                            vs_overlap_p[0][k0] = true;
                            vs_overlap_p[1][k1] = true;
                            break;
                        }
                    }

                std::vector<non_boolean_value_t> vs[2];
                vs[0].resize(constraints.at(i).getNonBooleanVariables().size());
                vs[1].resize(constraints.at(j).getNonBooleanVariables().size());

                if (overlapped_domain_sizes.empty())  // no overlapping
                    continue;
                // Cartesian product of all values of overlapped variables
                size_t num_values = std::accumulate(overlapped_domain_sizes.begin(),
                                                    overlapped_domain_sizes.end(),
                                                    1, std::multiplies<size_t>());

                for (size_t k = 0; k < num_values; ++ k)
                {
                    size_t k0 = k;
                    for (const auto& ov : overlapped_variables)
                    {
                        size_t d = nonBooleanVariables[
                            constraints.at(i).getNonBooleanVariables()[ov.first]].size() + 1;
                        non_boolean_value_t val = k0 % d;
                        k0 /= d;
                        vs[0][ov.first] = val;
                        vs[1][ov.second] = val;
                    }

                    // non-overlapped variables
                    std::array<std::vector<non_boolean_value_t>, 2> vs_non_overlapped;
                    vs_non_overlapped[0].resize((vs[0].size() - overlapped_variables.size()));
                    vs_non_overlapped[1].resize((vs[1].size() - overlapped_variables.size()));

                    std::vector<LinearProgramSolver::variable_id_t> lp_vs;  // LP variables
                    std::vector<double> coefs;

                    for (size_t z = 0; z < 2; ++ z)
                    {
                        size_t l;
                        do {
                            for (l = 0; l < vs[z].size(); ++ l)
                            {
                                if (vs_overlap_p[z][l])
                                    continue;

                                ++ vs[z][l];
                                if (vs[z][l] >= domain_sizes[z][l])
                                    vs[z][l] = 0;
                                else
                                    break;
                            }

                            size_t index = 0;  // index corresponding to the variable values vs[z]
                            size_t cur_base = 1;
                            for (size_t m = 0; m < vs[z].size(); ++ m)
                            {
                                index += cur_base * vs[z][m];
                                cur_base *= domain_sizes[z][m];
                            }

                            if (z == 0)
                            {
                                lp_vs.push_back(lp_variables[i][index]);
                                coefs.push_back(1.0);
                            }
                            else
                            {
                                lp_vs.push_back(lp_variables[j][index]);
                                coefs.push_back(-1.0);
                            }
                        } while (l != vs[z].size());
                    }

                    lps.addConstraint(lp_vs, coefs, 0.0,
                                      LinearProgramSolver::ConstraintType::EQUAL);
                }
            }
        }

        std::vector<double> assignments;
        lps.solve(assignments);

        // Compute the solutions from assignments
        std::map<variable_id_t, non_boolean_value_t> solution;
        for (size_t i : unary_indices)
            for (size_t j = 0; j < lp_variables[i].size(); ++ j)
            {
                if (assignments[lp_variables[i][j]] > 0.99)
                {
                    solution[constraints.at(i).getNonBooleanVariables().at(0)] = j;
                    break;
                }
            }

        return solution;
    }
};

template <class VarIdType, class WeightType, class ConstraintValueType, class NonBooleanValueType>
void WCSPInstance<VarIdType, WeightType, ConstraintValueType, NonBooleanValueType>::loadDimacs(
    std::istream& f)
{
    std::string line;
    std::string tmp;
    size_t max_domain_size;
    size_t nv, nc; // number of variables, number of constraints

    // first line: problem_name number_of_variables max_domain_size number_of_constraints
    // global_upper_bound
    std::getline(f, line);
    std::istringstream ss(line);
    ss >> tmp; // name
    ss >> nv;
    ss >> max_domain_size; // max domain size
    ss >> nc;
    ss >> tmp; // ignore global upper bound

    // domain size of all variables
    std::getline(f, line);
    ss.clear();
    ss.str(line);
    variable_id_t cur_var_id = 0;
    nonBooleanVariables.clear();
    nonBooleanVariables.reserve(nv);
    for (size_t i = 0; i < nv; ++ i)
    {
        size_t domain_size;
        ss >> domain_size;
        // Boolean variables corresponding to the current variable
        std::vector<variable_id_t> vs(domain_size-1);
        std::iota(vs.begin(), vs.end(), cur_var_id);
        cur_var_id += vs.size();
        nonBooleanVariables.push_back(std::move(vs));
    }

    constraints.clear();
    constraints.resize(nc);

    // iterate over constraints
    for (size_t i = 0; i < nc; ++ i)
    {
        size_t arity;
        weight_t default_cost;
        size_t ntuples; // number of tuples not having the default cost
        std::getline(f, line);
        ss.clear();
        ss.str(line);

        ss >> arity;

        // load the variables in the constraint
        std::vector<variable_id_t> non_bool_variables;
        non_bool_variables.reserve(arity);
        // load the corresponding Boolean variables in the constraint
        std::vector<variable_id_t> variables;
        variables.reserve(arity*max_domain_size);
        for (size_t j = 0; j < arity; ++ j)
        {
            variable_id_t vid;
            ss >> vid;
            non_bool_variables.push_back(vid);
            const auto& nbv = nonBooleanVariables[vid];
            variables.insert(variables.end(), nbv.begin(), nbv.end());
        }
        constraints[i].setVariables(std::move(variables));
        constraints[i].setNonBooleanVariables(std::move(non_bool_variables));
        ss >> default_cost;
        // TODO: More efficient handling
        if (default_cost > std::abs(1e-6))   // non-zero default cost
        {
            constraint_value_t values;
            values.resize(constraints[i].getVariables().size());
            unsigned long te = (1u << constraints[i].getVariables().size());
            for (unsigned long l = 0; l < te; ++ l)
            {
                for (unsigned long k = 0; k < values.size(); ++ k)
                    values.set(k, (l & (1u << k)) ? 1 : 0);
                constraints[i].setWeight(values, default_cost);
            }
        }
        ss >> ntuples;

        // load the entries in the constraints
        for (size_t j = 0; j < ntuples; ++ j)
        {
            weight_t cost;
            constraint_value_t values;
            values.resize(constraints[i].getVariables().size());
            values.set();
            std::vector<non_boolean_value_t> non_boolean_values(arity);
            std::getline(f, line);
            ss.clear();
            ss.str(line);
            for (size_t k = 0, cur_bit = 0; k < arity; ++ k)
            {
                int val;
                ss >> val;
                non_boolean_values[k] = val;
                size_t bvs = nonBooleanVariables[constraints[i].getNonBooleanVariables()[k]].size();
                if (bvs == 1)
                    values.set(cur_bit ++, val ? true : false);
                else
                {
                    if (val != 0)
                        values.set(cur_bit + val - 1, false);
                    cur_bit += bvs;
                }
            }
            ss >> cost;
            constraints[i].setWeight(std::move(non_boolean_values), cost);
            constraints[i].setWeight(std::move(values), cost);
        }
    }
}

template <class VarIdType, class WeightType, class ConstraintValueType, class NonBooleanValueType>
void WCSPInstance<VarIdType, WeightType, ConstraintValueType, NonBooleanValueType>::loadUAI(
    std::istream& f)
{
    std::string line;
    std::string tmp;
    size_t nv, nc; // number of variables, number of constraints

    // first line: MARKOV, just ignore it
    std::getline(f, tmp);

    // second line: number of variables
    {
        std::getline(f, line);
        std::istringstream ss(line);
        ss >> nv; // number of variables
    }

    // third line: arity
    {
        std::getline(f, line);
        std::istringstream ss(line);
        variable_id_t cur_var_id = 0;
        nonBooleanVariables.clear();
        nonBooleanVariables.reserve(nv);
        for (size_t i = 0; i < nv; ++ i)
        {
            size_t domain_size;
            ss >> domain_size;
            // Boolean varaibles corresponding to the current variable
            std::vector<variable_id_t> vs(domain_size-1);
            std::iota(vs.begin(), vs.end(), cur_var_id);
            cur_var_id += vs.size();
            nonBooleanVariables.push_back(std::move(vs));
        }
    }

    // fourth line: number of constraints
    {
        std::getline(f, line);
        std::istringstream ss(line);
        ss >> nc;
        constraints.clear();
        constraints.resize(nc);
    }

    // iterate over constraints
    for (size_t i = 0; i < nc; ++ i)
    {
        size_t arity;
        std::getline(f, line);
        std::istringstream ss(line);

        ss >> arity;

        // Load the variables in the constraint. We load it reversely because UAI format arrange
        // their constraint value reversely as we do.
        std::vector<variable_id_t> variables;
        std::vector<variable_id_t> non_bool_variables(arity);

        for (size_t j = 0; j < arity; ++ j)
        {
            variable_id_t vid;
            ss >> vid;
            non_bool_variables[arity-j-1] = vid;
            variables.insert(variables.end(),
                             nonBooleanVariables[vid].rbegin(),
                             nonBooleanVariables[vid].rend());
        }
        std::reverse(variables.begin(), variables.end());

        constraints[i].setVariables(std::move(variables));
        constraints[i].setNonBooleanVariables(std::move(non_bool_variables));
    }

    // iterate over constraints
    for (size_t i = 0; i < nc; ++ i)
    {
        size_t ntuples;
        f >> ntuples;  // number of entries

        // load the entries in the constraints
        std::vector<weight_t> costs(ntuples);
        for (size_t j = 0; j < ntuples; ++ j)
            f >> costs[j];

        // normalization constant
        weight_t sum(std::accumulate(costs.begin(), costs.end(), weight_t()));

        // set the value of each tuple
        for (size_t j = 0; j < ntuples; ++ j)
        {
            costs[j] = -std::log(costs[j] / sum);
            if (!std::isfinite(costs[j]))
                costs[j] = 1e6;
            constraint_value_t values;
            std::vector<non_boolean_value_t> non_boolean_values;
            non_boolean_values.reserve(constraints[i].getNonBooleanVariables().size());
            size_t j0 = j;
            for (const auto& nbv : constraints[i].getNonBooleanVariables())
            {
                auto d = nonBooleanVariables[nbv].size() + 1;
                size_t cur_val = j0 % d;
                non_boolean_values.push_back(cur_val);

                if (d == 2)
                    values.push_back(cur_val ? true : false);
                else
                {
                    for (size_t k = 1; k < d; ++ k)
                    {
                        if (cur_val == k)
                            values.push_back(false);
                        else
                            values.push_back(true);
                    }
                }
                j0 /= d;
            }
            constraints[i].setWeight(std::move(values), costs[j]);
            constraints[i].setWeight(std::move(non_boolean_values), costs[j]);
        }
    }
}

/** \brief Write a Constraint object to a stream in a human-readable form.
 *
 * \param[in] o The stream to write to.
 *
 * \param[in] c The constraint object to be write to \p o.
 *
 * \return The stream \p o.
 */
template <class ...T>
std::ostream& operator << (std::ostream& o, const Constraint<T...>& c)
{
    boost::property_tree::ptree t;
    c.toPropertyTree(t);
    boost::property_tree::write_json(o, t, true);
    return o;
}

#endif /* WCSPINSTANCE_H_ */


/**
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_spline.h>
#include "aXe_grism.h"
#include "aXe_utils.h"
#include "spce_PET.h"
#include "inout_aper.h"
//#include "trace_conf.h"
//#include "aper_conf.h"
//#include "spc_sex.h"
//#include "disp_conf.h"
//#include "spc_wl_calib.h"
//#include "spce_binning.h"
//#include "spc_spc.h"
//#include "spce_pgp.h"
//#include "spce_output.h"
//#include "spc_FITScards.h"
//#include "spc_flatfield.h"
#include "fringe_conf.h"
#include "fringe_utils.h"

#define AXE_IMAGE_PATH "AXE_IMAGE_PATH"
#define AXE_OUTPUT_PATH "AXE_OUTPUT_PATH"
#define AXE_CONFIG_PATH "AXE_CONFIG_PATH"

int
main (int argc, char *argv[])
{
  char *opt;
  char aper_file[MAXCHAR];
  char aper_file_path[MAXCHAR];

  char conf_file[MAXCHAR];
  char conf_file_path[MAXCHAR];

  char fconf_file[MAXCHAR];
  char fconf_file_path[MAXCHAR];

  char grism_file[MAXCHAR];
  char grism_file_path[MAXCHAR];

  char FF_file[MAXCHAR];
  //char FF_file_path[MAXCHAR];

  char OPET_file[MAXCHAR];
  char OPET_file_path[MAXCHAR];

  char BPET_file[MAXCHAR];
  char BPET_file_path[MAXCHAR];

  aperture_conf *conf=NULL;
  fringe_conf   *fconf=NULL;

  object **oblist;
  observation *obs=NULL;

  ap_pixel *OPET=NULL;
  ap_pixel *BPET=NULL;

  fitsfile *OPET_ptr=NULL;
  fitsfile *BPET_ptr=NULL;

  beam act_beam;

  int index;
  int i;
  int f_status = 0;
  int bckmode = 0;
  int oaperID, obeamID;
  int baperID, bbeamID;

  if ((argc < 4) || (opt = get_online_option ("help", argc, argv)))
    {
      fprintf (stdout,
	       "aXe_FRINGECORR Version %s:\n",RELEASE);
      exit (1);
    }

  fprintf (stdout, "aXe_FRINGECORR: Starting...\n\n");

  // copy the first parameter to the grism filename
  index = 0;
  strcpy (grism_file, argv[++index]);
  build_path (AXE_IMAGE_PATH, grism_file, grism_file_path);

  // copy the next parameter to the aXe configuration file
  strcpy (conf_file, argv[++index]);
  build_path (AXE_CONFIG_PATH, conf_file, conf_file_path);

  // copy the next parameter to the fringe configuration file
  strcpy (fconf_file, argv[++index]);
  build_path (AXE_CONFIG_PATH, fconf_file, fconf_file_path);

  // Determine if we are using the special bck mode
  // In this mode, file names are handled diferently
  if ((opt = get_online_option("bck", argc, argv)))
    bckmode = 1;

  // load the aXe configuration file
  conf = get_aperture_descriptor (conf_file_path);

  // load the fringe configuration file
  fconf = load_fringe_conf(fconf_file_path);

  // Determine where the various extensions are in the FITS file
  get_extension_numbers(grism_file_path, conf,conf->optkey1,conf->optval1);

  // Build aperture file name
  replace_file_extension (grism_file, aper_file, ".fits",
			  ".OAF", conf->science_numext);
  build_path (AXE_OUTPUT_PATH, aper_file, aper_file_path);

  // Build object PET file name
  replace_file_extension (grism_file, OPET_file, ".fits",
			  ".PET.fits", conf->science_numext);
  build_path (AXE_OUTPUT_PATH, OPET_file, OPET_file_path);

  // build the background PET file name
  // if necessary
  if (bckmode)
    {
      replace_file_extension (grism_file, BPET_file, ".fits",
			      ".BCK.PET.fits", conf->science_numext);
      build_path (AXE_OUTPUT_PATH, BPET_file, BPET_file_path);
  }

  // Give a short feedback on the filenames
  // of the various input
  fprintf (stdout, "aXe_FRINGECORR: Grism image file name:          %s\n",
	   grism_file_path);
  fprintf (stdout, "aXe_FRINGECORR: Aperture file name:             %s\n",
	   aper_file_path);
  fprintf (stdout, "aXe_FRINGECORR: aXe configuration file name:    %s\n",
	   conf_file_path);
  fprintf (stdout, "aXe_FRINGECORR: Fringe configuration file name: %s\n",
	   fconf_file_path);
  fprintf (stdout, "aXe_FRINGECORR: Object PET file name:           %s\n",
	   OPET_file_path);
  if (bckmode)
    fprintf (stdout, "aXe_FRINGECORR: Background PET file name:       %s\n",
	     BPET_file_path);

  // check whether all necessary information
  // is in place, provide defaults
  check_fringe_conf(fconf);

  fprintf (stdout, "aXe_FRINGECORR: Loading object aperture list...");
  fflush(stdout);
  oblist = file_to_object_list_seq (aper_file_path, obs);
  fprintf (stdout,"%d objects loaded.\n",object_list_size(oblist));

  // Open the OPET file for reading/writing
  fits_open_file (&OPET_ptr, OPET_file_path, READWRITE, &f_status);
  if (f_status)
    {
      ffrprt (stdout, f_status);
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		   "aXe_FRINGECORR: Could not open file: %s\n",
		   OPET_file_path);
    }

  // if necessary, open the background PET
  if (bckmode)
    {
      fits_open_file (&BPET_ptr, BPET_file_path, READWRITE, &f_status);
      if (f_status)
	{
	  ffrprt (stdout, f_status);
	  aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		       "aXe_FRINGECORR: Could not open file: %s\n",
		       BPET_file_path);
	}
    }

  i = 0;
  if ((oblist!=NULL) && (strcmp(FF_file,"None")))
    {
      while (1)
	{
	  // Get the PET for this object
	  OPET = get_ALL_from_next_in_PET(OPET_ptr, &oaperID, &obeamID);
	  if ((oaperID==-1) && (obeamID==-1))
	    break;

	  // Get the PET for this object
	  if (bckmode)
	    {
	      BPET = get_ALL_from_next_in_PET(BPET_ptr, &baperID, &bbeamID);
	      if ((baperID != oaperID) ||  (bbeamID != obeamID))
		{
		  fprintf(stderr, "%i, %i, %i, %i\n", oaperID, baperID, obeamID, bbeamID);
		  aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
			       "aXe_FRINGECORR: The beam order in the\n"
			       "object PET %s and the background PET\n"
			       "%s does not coincide!\n",
			       OPET_file_path, BPET_file_path);
		}
	    }

	  // check whether there is content in the PET;
	  // skip it if empty
	  if (OPET==NULL)
	    continue;

	  // refer about the actual beam in process
	  fprintf (stdout, "aXe_FRINGECORR: BEAM %d%c",oaperID, BEAM(obeamID));

	  // get the beam information from the OAF
	  act_beam = find_beam_in_object_list(oblist, oaperID, obeamID);
	  fprintf (stdout, " Done. \n");

	  fringe_correct_PET(fconf, act_beam, OPET, BPET);

	  // update PET table with the new FF info
	  {
	    char ID[60];
	    sprintf (ID, "%d%c", oaperID, BEAM (act_beam.ID));
	    add_ALL_to_PET (OPET, ID, OPET_ptr,1);

	    if (bckmode)
	      add_ALL_to_PET (BPET, ID, BPET_ptr,1);
            }

	  // free the memory of the object pixels
	  if (OPET!=NULL)
	    {
	      free(OPET);
	      OPET=NULL;
	    }

	  // free the memory of the background pixels
	  if (BPET!=NULL)
	    {
	      free(BPET);
	      BPET=NULL;
	    }
	}
    }

  // free the object list
  if (oblist!=NULL)
    free_oblist (oblist);

  // close the object PET fits-file
  fits_close_file (OPET_ptr, &f_status);
  if (f_status)
    {
      ffrprt (stderr, f_status);
      aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		   "aXe_PETFF: " "Error closing PET: %s\n",
		   OPET_file_path);
    }

  // if necessary, close the object PET fits-file
  if (bckmode)
    {
      fits_close_file (BPET_ptr, &f_status);
      if (f_status)
	{
	  ffrprt (stderr, f_status);
	  aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		       "aXe_PETFF: " "Error closing PET: %s\n",
		       BPET_file_path);
	}
    }

  fprintf (stdout, "aXe_FRINGECORR: Done...\n\n");
  exit (0);
}


#pragma once

#include <gsl\gsl>
#include <winrt\Windows.Foundation.h>
#include <d3d11.h>
#include "DrawableGameComponent.h"
#include "MatrixHelper.h"
#include "PointLight.h"

namespace Library
{
	class ProxyModel;
}

namespace Rendering
{
	class PointLightMaterial;

	class PointLightDemo final : public Library::DrawableGameComponent
	{
	public:
		PointLightDemo(Library::Game& game, const std::shared_ptr<Library::Camera>& camera);
		PointLightDemo(const PointLightDemo&) = delete;
		PointLightDemo(PointLightDemo&&) = default;
		PointLightDemo& operator=(const PointLightDemo&) = default;
		PointLightDemo& operator=(PointLightDemo&&) = default;
		~PointLightDemo();

		bool AnimationEnabled() const;
		void SetAnimationEnabled(bool enabled);
		void ToggleAnimation();

		float AmbientLightIntensity() const;
		void SetAmbientLightIntensity(float intensity);

		float PointLightIntensity() const;
		void SetPointLightIntensity(float intensity);

		const DirectX::XMFLOAT3& LightPosition() const;
		const DirectX::XMVECTOR LightPositionVector() const;
		void SetLightPosition(const DirectX::XMFLOAT3& position);
		void SetLightPosition(DirectX::FXMVECTOR position);

		float LightRadius() const;
		void SetLightRadius(float radius);

		float SpecularIntensity() const;
		void SetSpecularIntensity(float intensity);

		float SpecularPower() const;
		void SetSpecularPower(float power);

		virtual void Initialize() override;
		virtual void Update(const Library::GameTime& gameTime) override;
		virtual void Draw(const Library::GameTime& gameTime) override;

	private:
		inline static const float RotationRate{ DirectX::XM_PI };

		std::shared_ptr<PointLightMaterial> mMaterial;
		DirectX::XMFLOAT4X4 mWorldMatrix{ Library::MatrixHelper::Identity };
		winrt::com_ptr<ID3D11Buffer> mVertexBuffer;
		winrt::com_ptr<ID3D11Buffer> mIndexBuffer;
		std::uint32_t mIndexCount{ 0 };
		Library::PointLight mPointLight;
		std::unique_ptr<Library::ProxyModel> mProxyModel;
		float mModelRotationAngle{ 0.0f };
		bool mAnimationEnabled{ true };
		bool mUpdateMaterial{ true };
	};
}


/** \file
 * This file contains an implementation of a MPMC queue by Dmitry Vyukov. It is
 * largely the same as the original source except for: minor formatting
 * changes, movement to a namespace, use of alignas(X) instead of pad variables,
 * and use of dyn_array from the guideline support library instead of
 * `new char[size]`.
 */

/*  Multi-producer/multi-consumer bounded queue.
 *  http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
 *
 *  Copyright (c) 2010-2011, Dmitry Vyukov. 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.
 *
 *  THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "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 DMITRY VYUKOV 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.
 *
 *  The views and conclusions contained in the software and documentation are
 *  those of the authors and should not be interpreted as representing official
 *  policies, either expressed or implied, of Dmitry Vyukov.
 */

#include <cassert>
#include <atomic>

#include <gsl_p/dyn_array.h>

namespace dvyukov {

    /**
     * A 'lockless' bounded multi-producer, multi-consumer queue
     *
     * Has the caveat that the queue can *appear* empty even if there are
     * returned items within it as a single thread can block progression
     * of the queue.
     */
    template<typename T>
    class mpmc_bounded_queue {
    public:
        /**
         * Constructs a bounded multi-producer, multi-consumer queue
         *
         * Note: Due to the algorithm used, buffer_size must be a power
         *       of two and must be greater than or equal to two.
         *
         * @param buffer_size Number of spaces available in the queue.
         */
        mpmc_bounded_queue(size_t buffer_size)
                : buffer_(buffer_size),
                  buffer_mask_(buffer_size - 1) {

            assert((buffer_size >= 2) &&
                   ((buffer_size & (buffer_size - 1)) == 0));
            for (size_t i = 0; i != buffer_size; i += 1) {
                buffer_[i].sequence_.store(i, std::memory_order_relaxed);
            }
            enqueue_pos_.store(0, std::memory_order_relaxed);
            dequeue_pos_.store(0, std::memory_order_relaxed);
        }

        /**
         * Enqueues an item into the queue
         *
         * @param data Argument to place into the array
         * @return false if the queue was full (and enqueing failed),
         *         true otherwise
         */
        bool enqueue(const T& data) {
            cell_t *cell;
            size_t pos = enqueue_pos_.load(std::memory_order_relaxed);
            for (; ;) {
                cell = &buffer_[pos & buffer_mask_];
                size_t seq = cell->sequence_.load(std::memory_order_acquire);
                intptr_t dif = (intptr_t) seq - (intptr_t) pos;
                if (dif == 0) {
                    if (enqueue_pos_.compare_exchange_weak(pos, pos + 1,
                                                           std::memory_order_relaxed)) {
                        break;
                    }
                } else if (dif < 0) {
                    return false;
                } else {
                    pos = enqueue_pos_.load(std::memory_order_relaxed);
                }
            }

            cell->data_ = data;
            cell->sequence_.store(pos + 1, std::memory_order_release);

            return true;
        }

        /**
         * Dequeues an item from the queue
         *
         * @param[out] data Reference to place item into
         * @return false if the queue was empty (and dequeuing failed),
         *         true if successful
         */
        bool dequeue(T& data) {
            cell_t *cell;
            size_t pos = dequeue_pos_.load(std::memory_order_relaxed);
            for (; ;) {
                cell = &buffer_[pos & buffer_mask_];
                size_t seq = cell->sequence_.load(std::memory_order_acquire);
                intptr_t dif = (intptr_t) seq - (intptr_t) (pos + 1);
                if (dif == 0) {
                    if (dequeue_pos_.compare_exchange_weak(pos, pos + 1,
                                                           std::memory_order_relaxed)) {
                        break;
                    }
                } else if (dif < 0) {
                    return false;
                } else {
                    pos = dequeue_pos_.load(std::memory_order_relaxed);
                }
            }

            data = cell->data_;
            cell->sequence_.store(pos + buffer_mask_ + 1,
                                  std::memory_order_release);

            return true;
        }

    private:
        struct cell_t {
            std::atomic<size_t> sequence_;
            T data_;
        };

        static const size_t cacheline_size = 64;
        typedef char cacheline_pad_t [cacheline_size];

        cacheline_pad_t pad0_;
        gsl_p::dyn_array<cell_t> buffer_;
        const size_t buffer_mask_;
        cacheline_pad_t pad1_;
        std::atomic<size_t> enqueue_pos_;
        cacheline_pad_t pad2_;
        std::atomic<size_t> dequeue_pos_;
        cacheline_pad_t pad3_;

        mpmc_bounded_queue(mpmc_bounded_queue const &);

        void operator=(mpmc_bounded_queue const &);
    };
}


/*
Copyright 2010-2011, D. E. Shaw Research.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
  notice, this list of conditions, and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions, and the following disclaimer in the
  documentation and/or other materials provided with the distribution.

* Neither the name of D. E. Shaw Research 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
OWNER 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 <gsl/gsl_randist.h>
#include <stdio.h>
#include "Random123/philox.h"
#include "Random123/threefry.h"
#include "Random123/conventional/gsl_cbrng.h"
#include <assert.h>

/* Exercise the GSL_CBRNG macro */

GSL_CBRNG(cbrng, threefry4x64); /* creates gsl_rng_cbrng */

int main(int argc, char **argv){
    int i;
    gsl_rng *r;
    gsl_rng *rcopy;
    unsigned long save, x;
    unsigned long saved[5];
    double sum = 0.;
    (void)argc; (void)argv; /* unused */

    r = gsl_rng_alloc(gsl_rng_cbrng);
    assert (gsl_rng_min(r) == 0);
    assert (gsl_rng_max(r) == 0xffffffffUL); // Not necessarily ~0UL
    assert (gsl_rng_size(r) > 0);

    printf("%s\nulongs from %s in initial state\n", argv[0], gsl_rng_name(r));
    for (i = 0; i < 5; i++) {
	x = gsl_rng_get(r);
        saved[i] = x;
	printf("%d: 0x%lx\n", i, x);
	assert(x != 0);
    }
    printf("uniforms from %s\n", gsl_rng_name(r));
    for (i = 0; i < 5; i++) {
        double z = gsl_rng_uniform(r);
        sum += z;
        printf("%d: %.4g\n", i, z);
    }
    assert( sum < 0.9*5 && sum > 0.1*5 && (long)"sum must be reasonably close  to 0.5*number of trials");
    save = gsl_rng_get(r);

    gsl_rng_set(r, 0xdeadbeef); /* set a non-zero seed */
    printf("ulongs from %s after seed\n", gsl_rng_name(r));
    for (i = 0; i < 5; i++) {
	x = gsl_rng_get(r);
	printf("%d: 0x%lx\n", i, x);
	assert(x != 0);
    }
    /* make a copy of the total state */
    rcopy = gsl_rng_alloc(gsl_rng_cbrng);
    gsl_rng_memcpy(rcopy, r);
    printf("uniforms from %s\n", gsl_rng_name(r));
    sum = 0.;
    for (i = 0; i < 5; i++) {
        double x = gsl_rng_uniform(r);
        double y = gsl_rng_uniform(rcopy);
	printf("%d: %.4g\n", i, x);
        sum += x;
        assert(x == y);
    }
    assert(gsl_rng_get(r) != save);
    assert( sum < 0.9*5 && sum > 0.1*5 && (long)"sum must be reasonably close  to 0.5*number of trials");

    /* gsl_rng_set(*, 0) is supposed to recover the default seed */
    gsl_rng_set(r, 0);
    printf("ulongs from %s after restore to initial\n", gsl_rng_name(r));
    for (i = 0; i < 5; i++) {
	x = gsl_rng_get(r);
        assert( x == saved[i] );
	printf("%d: 0x%lx\n", i, x);
	assert(x != 0);
    }
    printf("uniforms from %s\n", gsl_rng_name(r));
    for (i = 0; i < 5; i++) {
	printf("%d: %.4g\n", i, gsl_rng_uniform(r));
    }
    assert(gsl_rng_get(r) == save);

    gsl_rng_free (r);
    return 0;
}


#ifndef KEXPR_H
#define KEXPR_H
#pragma warning( disable : 991)

#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_complex.h>
#include <plplot/plplot.h>
#include <stdint.h>
#include <stdio.h>
#include "stack.h"
#include "utf8.h"
#include "gdate.h"
#include <setjmp.h>

#if defined(_MSC_VER) || defined(_WIN32)
#include <Windows.h>
#endif

#include "api.h"

#ifdef _DEBUG
void ke_validate_parameter_qte(sml_t* sml, token_t *p, int nb_param, char * function_name);
void ke_validate_parameter_vtype(sml_t* sml, token_t * p, int vtype, char * param_name, char * function_name);
void ke_validate_parameter_ttype(sml_t* sml, token_t * p, int ttype, char * function_name);
void ke_validate_parameter_not_null(sml_t* sml, token_t * p, void * ptr, char * param_name, char * function_name);
void ke_validate_parameter_int_gt_zero(sml_t* sml, token_t * p, char * param_name, char * function_name);
#endif // _DEBUG

void ke_set_jump(sml_t *sml, int info);
sml_t * ke_create_sml();
void ke_free_sml(sml_t *sml);
void ke_init_memory_count(sml_t *sml);
char *ke_mystrndup(sml_t *sml, char *src, size_t n);
utf8 *ke_mystr(sml_t *sml, utf8 *src, size_t n);
token_t * ke_get_tok(sml_t *sml);
token_t * ke_get_tokidx(sml_t *sml, int idx);
fncp ke_function(sml_t *sml, char * name);
typedef int(*DLLPROC)();
void ke_import(sml_t *sml, char * s);
void ke_load_dll(sml_t *sml, char * p);
void ke_free_val(sml_t *sml);
void ke_free_tokens(sml_t *sml);
int ke_fill_list(sml_t *sml, kexpr_t *ke);
void ke_set_int(sml_t* sml, token_t *tokp, int64_t y);
void ke_set_real(sml_t* sml, token_t *tokp, double x);
void ke_set_vector(sml_t * sml, token_t *tokp, gsl_vector * vecp);
void ke_set_vector_int(sml_t * sml, token_t *tokp, gsl_vector_int * vecp);
void ke_set_date(sml_t * sml, token_t *tokp, GDate_t * datep);
void ke_set_matrix(sml_t *sml, token_t *tokp, gsl_matrix * matp);
void ke_set_complex(sml_t *sml, token_t *tokp, gsl_complex complex);
static void ke_set_str_internal(sml_t* sml, token_t *tokp, char * tmp);
void ke_set_str(sml_t* sml, token_t *tokp, char *x);
void ke_set_str_direct(sml_t *sml, token_t * e, char *x);
void ke_set_record(sml_t *sml, token_t *source, token_t *dest);
void ke_set_image(sml_t *sml, token_t *source, token_t *dest);
void ke_set_file(sml_t *sml, token_t *source, token_t *dest);
void ke_set_buffer(sml_t *sml, token_t *source, token_t *dest);
void ke_print_one_stack(token_t * tokp);
void ke_print_one(sml_t *sml, token_t * tokp);
void ke_print_stack(sml_t* sml, token_t *tokp, int top);
void ke_print_range(kexpr_t *kexpr, int starti, int endi);
void ke_print_error_one(sml_t * sml, kexpr_t *kexpr, char * name, token_t * e, int itok);
void ke_print(sml_t* sml, kexpr_t *kexpr);
void ke_fill_hash(sml_t *sml);
token_t* ke_get_val_index(sml_t *sml, int i);
void ke_set_val_index(sml_t *sml, int i, token_t *tokp);
void ke_set_val(sml_t* sml, token_t* e, token_t *q);
void ke_push_stack(sml_t * sml, token_t * tokp, int *top);
void ke_free_dll(sml_t * sml);
void ke_destroy(sml_t *sml, kexpr_t *kexpr);
void ke_free_hash(sml_t *sml);
void ke_free(sml_t* sml, kexpr_t *kexpr);
int peekfor(sml_t* sml);
int popfor(sml_t* sml);
void pushfor(sml_t* sml, int val);

#endif


#ifndef OPENMC_TALLIES_FILTER_CELL_INSTANCE_H
#define OPENMC_TALLIES_FILTER_CELL_INSTANCE_H

#include <cstdint>
#include <unordered_map>
#include <vector>

#include <gsl/gsl>

#include "openmc/cell.h"
#include "openmc/tallies/filter.h"

namespace openmc {

//==============================================================================
//! Specifies cell instances that tally events reside in.
//==============================================================================

class CellInstanceFilter : public Filter {
public:
  //----------------------------------------------------------------------------
  // Constructors, destructors

  CellInstanceFilter() = default;
  CellInstanceFilter(gsl::span<CellInstance> instances);
  ~CellInstanceFilter() = default;

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

  std::string type() const override {return "cellinstance";}

  void from_xml(pugi::xml_node node) override;

  void get_all_bins(const Particle* p, int estimator, FilterMatch& match)
  const override;

  void to_statepoint(hid_t filter_group) const override;

  std::string text_label(int bin) const override;

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

  const std::vector<CellInstance>& cell_instances() const { return cell_instances_; }

  void set_cell_instances(gsl::span<CellInstance> instances);

private:
  //----------------------------------------------------------------------------
  // Data members

  //! The indices of the cells binned by this filter.
  std::vector<CellInstance> cell_instances_;

  //! A map from cell/instance indices to filter bin indices.
  std::unordered_map<CellInstance, gsl::index, CellInstanceHash> map_;
};

} // namespace openmc

#endif // OPENMC_TALLIES_FILTER_CELL_INSTANCE_H


//
// Created by Tobias on 10/2/2020.
//

#ifndef SD3D_ORBITCAMERACONTROLLER_H
#define SD3D_ORBITCAMERACONTROLLER_H

#include <GLFW/glfw3.h>
#include <gsl-lite/gsl-lite.hpp>

#pragma warning(push, 0)

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#pragma warning(pop)

#include "../graphics/Camera.h"

class OrbitCameraController {
public:
	struct OrbitCamSettings {
		glm::vec3 target{};

		float dist{};
		float minDist{};
		float maxDist{};
		float scrollSpeed{};

		float speed{};
		float deceleration{};

		OrbitCamSettings(const glm::vec3 &target, float dist, float speed,
						 float deceleration = 0.05f) :
			OrbitCamSettings{target, dist,  dist,        dist,
							 0.0f,   speed, deceleration} {}

		OrbitCamSettings(const glm::vec3 &target, float dist, float minDist,
						 float maxDist, float scrollSpeed, float speed,
						 float deceleration = 0.05f) :
			target{target},
			dist{dist},
			minDist{minDist},
			maxDist{maxDist},
			scrollSpeed{scrollSpeed},
			speed{speed},
			deceleration{deceleration} {}
	};

private:
	// split mouse vector in 2 directions
	// x -> theta
	// y -> phi

	glm::vec3 m_target{};

	float m_dist{};
	float m_minDist{};
	float m_maxDist{};
	float m_scrollSpeed{};

	float m_yaw{-90.0f};
	float m_pitch{};

	glm::vec2 m_moveVec{};

	float m_deceleration{0.05f};
	float m_speed{};

	Camera m_cam;

	// for settings window
private:
	float m_fovTemp;

	float m_fovSaved;
	OrbitCamSettings m_settingsSaved;

public:
	OrbitCameraController(Camera &&cam, const OrbitCamSettings &settings) :
		m_target{settings.target},
		m_dist{settings.dist},
		m_minDist{settings.minDist},
		m_maxDist{settings.maxDist},
		m_scrollSpeed{settings.scrollSpeed},
		m_deceleration{settings.deceleration},
		m_speed{settings.speed},
		m_cam{cam},
		m_fovTemp{gsl::narrow_cast<float>(m_cam.fov())},
		m_fovSaved{m_fovTemp},
		m_settingsSaved{settings} {}

	void update(float deltaTime);

	[[nodiscard]] const Camera &ccam() const { return m_cam; }
	[[nodiscard]] Camera &cam() { return m_cam; };

	void rotate(double dX, double dY);

	void zoom(double dScroll);

	[[nodiscard]] float min_dist() const { return m_minDist; }

	[[nodiscard]] float max_dist() const { return m_maxDist; }

	void set_min_dist(float val) { m_minDist = val; }

	void set_max_dist(float val) { m_maxDist = val; }

	void settings_gui(bool &show, glm::vec3 &clearCol);

	void reset_settings() {
		m_target = m_settingsSaved.target;
		m_dist = m_settingsSaved.dist;
		m_minDist = m_settingsSaved.minDist;
		m_maxDist = m_settingsSaved.maxDist;
		m_scrollSpeed = m_settingsSaved.scrollSpeed;
		m_deceleration = m_settingsSaved.deceleration;
		m_speed = m_settingsSaved.speed;

		m_fovTemp = m_fovSaved;
		m_cam.set_fov(m_fovTemp);
	}
};

#endif // SD3D_ORBITCAMERACONTROLLER_H


/* vector/gsl_vector_ulong.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 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 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.
 */

#ifndef __GSL_VECTOR_ULONG_H__
#define __GSL_VECTOR_ULONG_H__

#include <stdlib.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_block_ulong.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;
  unsigned long *data;
  gsl_block_ulong *block;
  int owner;
}
gsl_vector_ulong;

typedef struct
{
  gsl_vector_ulong vector;
} _gsl_vector_ulong_view;

typedef _gsl_vector_ulong_view gsl_vector_ulong_view;

typedef struct
{
  gsl_vector_ulong vector;
} _gsl_vector_ulong_const_view;

typedef const _gsl_vector_ulong_const_view gsl_vector_ulong_const_view;

/* Allocation */

gsl_vector_ulong *gsl_vector_ulong_alloc (const size_t n);
gsl_vector_ulong *gsl_vector_ulong_calloc (const size_t n);

gsl_vector_ulong *gsl_vector_ulong_alloc_from_block (gsl_block_ulong * b,
                                                     const size_t offset,
                                                     const size_t n,
                                                     const size_t stride);

gsl_vector_ulong *gsl_vector_ulong_alloc_from_vector (gsl_vector_ulong * v,
                                                      const size_t offset,
                                                      const size_t n,
                                                      const size_t stride);

void gsl_vector_ulong_free (gsl_vector_ulong * v);

/* Views */

_gsl_vector_ulong_view
gsl_vector_ulong_view_array (unsigned long *v, size_t n);

_gsl_vector_ulong_view
gsl_vector_ulong_view_array_with_stride (unsigned long *base,
                                         size_t stride,
                                         size_t n);

_gsl_vector_ulong_const_view
gsl_vector_ulong_const_view_array (const unsigned long *v, size_t n);

_gsl_vector_ulong_const_view
gsl_vector_ulong_const_view_array_with_stride (const unsigned long *base,
                                               size_t stride,
                                               size_t n);

_gsl_vector_ulong_view
gsl_vector_ulong_subvector (gsl_vector_ulong *v,
                            size_t i,
                            size_t n);

_gsl_vector_ulong_view
gsl_vector_ulong_subvector_with_stride (gsl_vector_ulong *v,
                                        size_t i,
                                        size_t stride,
                                        size_t n);

_gsl_vector_ulong_const_view
gsl_vector_ulong_const_subvector (const gsl_vector_ulong *v,
                                  size_t i,
                                  size_t n);

_gsl_vector_ulong_const_view
gsl_vector_ulong_const_subvector_with_stride (const gsl_vector_ulong *v,
                                              size_t i,
                                              size_t stride,
                                              size_t n);

/* Operations */

unsigned long gsl_vector_ulong_get (const gsl_vector_ulong * v, const size_t i);
void gsl_vector_ulong_set (gsl_vector_ulong * v, const size_t i, unsigned long x);

unsigned long *gsl_vector_ulong_ptr (gsl_vector_ulong * v, const size_t i);
const unsigned long *gsl_vector_ulong_const_ptr (const gsl_vector_ulong * v, const size_t i);

void gsl_vector_ulong_set_zero (gsl_vector_ulong * v);
void gsl_vector_ulong_set_all (gsl_vector_ulong * v, unsigned long x);
int gsl_vector_ulong_set_basis (gsl_vector_ulong * v, size_t i);

int gsl_vector_ulong_fread (FILE * stream, gsl_vector_ulong * v);
int gsl_vector_ulong_fwrite (FILE * stream, const gsl_vector_ulong * v);
int gsl_vector_ulong_fscanf (FILE * stream, gsl_vector_ulong * v);
int gsl_vector_ulong_fprintf (FILE * stream, const gsl_vector_ulong * v,
			      const char *format);

int gsl_vector_ulong_memcpy (gsl_vector_ulong * dest, const gsl_vector_ulong * src);

int gsl_vector_ulong_reverse (gsl_vector_ulong * v);

int gsl_vector_ulong_swap (gsl_vector_ulong * v, gsl_vector_ulong * w);
int gsl_vector_ulong_swap_elements (gsl_vector_ulong * v, const size_t i, const size_t j);

unsigned long gsl_vector_ulong_max (const gsl_vector_ulong * v);
unsigned long gsl_vector_ulong_min (const gsl_vector_ulong * v);
void gsl_vector_ulong_minmax (const gsl_vector_ulong * v, unsigned long * min_out, unsigned long * max_out);

size_t gsl_vector_ulong_max_index (const gsl_vector_ulong * v);
size_t gsl_vector_ulong_min_index (const gsl_vector_ulong * v);
void gsl_vector_ulong_minmax_index (const gsl_vector_ulong * v, size_t * imin, size_t * imax);

int gsl_vector_ulong_add (gsl_vector_ulong * a, const gsl_vector_ulong * b);
int gsl_vector_ulong_sub (gsl_vector_ulong * a, const gsl_vector_ulong * b);
int gsl_vector_ulong_mul (gsl_vector_ulong * a, const gsl_vector_ulong * b);
int gsl_vector_ulong_div (gsl_vector_ulong * a, const gsl_vector_ulong * b);
int gsl_vector_ulong_scale (gsl_vector_ulong * a, const double x);
int gsl_vector_ulong_add_constant (gsl_vector_ulong * a, const double x);

int gsl_vector_ulong_isnull (const gsl_vector_ulong * v);

extern int gsl_check_range;

#ifdef HAVE_INLINE

extern inline
unsigned long
gsl_vector_ulong_get (const gsl_vector_ulong * v, const size_t i)
{
#ifndef GSL_RANGE_CHECK_OFF
  if (i >= v->size)
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return v->data[i * v->stride];
}

extern inline
void
gsl_vector_ulong_set (gsl_vector_ulong * v, const size_t i, unsigned long x)
{
#ifndef GSL_RANGE_CHECK_OFF
  if (i >= v->size)
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  v->data[i * v->stride] = x;
}

extern inline
unsigned long *
gsl_vector_ulong_ptr (gsl_vector_ulong * v, const size_t i)
{
#ifndef GSL_RANGE_CHECK_OFF
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (unsigned long *) (v->data + i * v->stride);
}

extern inline
const unsigned long *
gsl_vector_ulong_const_ptr (const gsl_vector_ulong * v, const size_t i)
{
#ifndef GSL_RANGE_CHECK_OFF
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (const unsigned long *) (v->data + i * v->stride);
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_ULONG_H__ */


#ifndef PRK_PETSC_H_
#define PRK_PETSC_H_

#define PRK_PETSC_USE_MPI 1

#ifdef PRK_PETSC_USE_MPI
#include <mpi.h>
#endif

#include <petsc.h>
#include <petscsys.h>
#include <petscvec.h>
#include <petscmat.h>

#endif // PRK_PETSC_H_


#pragma once
#include "nkg_point.h"
#include "nkg_rect.h"
#include "nk_essential.h"
#include "utility/types.h"
#include "utility/c++std/string_view.h"
#include <nuklear.h>
#include <gsl/gsl>

namespace cws80 {

// drawing
void im_draw_point(nk_command_buffer *out, f32 x, f32 y, nk_color color);
void im_fill_polygon(nk_command_buffer *out, gsl::span<const im_pointf> points, nk_color color);
void im_draw_image(nk_command_buffer *out, im_rectf rect, const im_texture &tex,
                   nk_color color);
void im_draw_tiled_image(nk_command_buffer *out, im_rectf rect,
                         const im_texture &tex, nk_color color);
void im_draw_title_box(nk_context *ctx, im_rectf bounds, cxx::string_view title,
                       im_rectf *content_bounds = nullptr);
void im_draw_title_box_alt(nk_context *ctx, im_rectf bounds, cxx::string_view title);
void im_draw_led_screen(nk_context *ctx, im_rectf bounds, cxx::string_view text,
                        im_pointf textoff);

// drawing in screen coordinates
void ims_stroke_line(nk_context *ctx, f32 x0, f32 y0, f32 x1, f32 y1,
                     f32 line_thickness, nk_color color);
void ims_stroke_rect(nk_context *ctx, im_rectf rect, f32 rounding,
                     f32 line_thickness, nk_color color);
void ims_fill_rect(nk_context *ctx, im_rectf rect, f32 rounding, nk_color color);
void ims_fill_polygon(nk_context *ctx, gsl::span<const im_pointf> points, nk_color color);
void ims_draw_image(nk_context *ctx, im_rectf rect, const im_texture &tex, nk_color color);
void ims_draw_tiled_image(nk_context *ctx, im_rectf rect, const im_texture &tex,
                          nk_color color);
void ims_draw_text(nk_context *ctx, im_rectf rect, const char *text, int len,
                   const nk_user_font *fnt, nk_color bg, nk_color fg);
void ims_push_scissor(nk_context *ctx, im_rectf rect);
//
void ims_draw_title_box(nk_context *ctx, im_rectf bounds, cxx::string_view title,
                        im_rectf *content_bounds = nullptr);
void ims_draw_title_box_alt(nk_context *ctx, im_rectf bounds, cxx::string_view title);
void ims_draw_led_screen(nk_context *ctx, im_rectf bounds,
                         cxx::string_view text, im_pointf textoff);

// text
f32 im_text_width(const nk_user_font *fnt, cxx::string_view text);

}  // namespace cws80


#pragma once

#include <thrust/iterator/counting_iterator.h>
#include <thrust/iterator/transform_iterator.h>

#include <cuda/runtime_api.hpp>

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

#include <cub/cub.cuh>

#include <thrustshift/math.h>
#include <thrustshift/not-a-vector.h>

namespace cub {

template <typename _UnsignedBits, typename T>
struct BaseTraits<SIGNED_INTEGER,
                  false,
                  false,
                  _UnsignedBits,
                  thrustshift::AbsView<T>> {
	typedef _UnsignedBits UnsignedBits;

	static const Category CATEGORY = SIGNED_INTEGER;
	static const UnsignedBits HIGH_BIT = UnsignedBits(1)
	                                     << ((sizeof(UnsignedBits) * 8) - 1);
	static const UnsignedBits LOWEST_KEY = HIGH_BIT;
	static const UnsignedBits MAX_KEY = UnsignedBits(-1) ^ HIGH_BIT;

	enum {
		PRIMITIVE = true,
		NULL_TYPE = false,
	};

	static __device__ __forceinline__ UnsignedBits TwiddleIn(UnsignedBits key) {
		T val = key;
		if (val >= 0) {
			return val;
		}
		UnsignedBits new_val = std::abs(val);
		return new_val | HIGH_BIT;
	};

	static __device__ __forceinline__ UnsignedBits
	TwiddleOut(UnsignedBits key) {
		if (key & HIGH_BIT) {
			auto del_high_bit = key ^ HIGH_BIT;
			T x = -T(del_high_bit);
			return x;
		}
		return key;
	};

	static __host__ __device__ __forceinline__ T Max() {
		return std::numeric_limits<T>::max();
	}

	static __host__ __device__ __forceinline__ T Lowest() {
		return std::numeric_limits<T>::lowest();
	}
};

template <typename _UnsignedBits, typename T>
struct BaseTraits<FLOATING_POINT,
                  false,
                  false,
                  _UnsignedBits,
                  thrustshift::AbsView<T>> {
	typedef _UnsignedBits UnsignedBits;

	static const Category CATEGORY = FLOATING_POINT;
	static const UnsignedBits HIGH_BIT = UnsignedBits(1)
	                                     << ((sizeof(UnsignedBits) * 8) - 1);
	static const UnsignedBits LOWEST_KEY = UnsignedBits(-1);
	static const UnsignedBits MAX_KEY = UnsignedBits(-1) ^ HIGH_BIT;

	enum {
		PRIMITIVE = true,
		NULL_TYPE = false,
	};

	static __device__ __forceinline__ UnsignedBits TwiddleIn(UnsignedBits key) {
		return key;
	};

	static __device__ __forceinline__ UnsignedBits
	TwiddleOut(UnsignedBits key) {
		return key;
	};

	static __host__ __device__ __forceinline__ T Max() {
		return FpLimits<T>::Max();
	}

	static __host__ __device__ __forceinline__ T Lowest() {
		return FpLimits<T>::Lowest();
	}
};

template <>
struct NumericTraits<thrustshift::AbsView<int>>
    : BaseTraits<SIGNED_INTEGER,
                 false,
                 false,
                 unsigned int,
                 thrustshift::AbsView<int>> {};
template <>
struct NumericTraits<thrustshift::AbsView<long int>>
    : BaseTraits<SIGNED_INTEGER,
                 false,
                 false,
                 long unsigned int,
                 thrustshift::AbsView<long int>> {};
template <>
struct NumericTraits<thrustshift::AbsView<long long int>>
    : BaseTraits<SIGNED_INTEGER,
                 false,
                 false,
                 long long unsigned int,
                 thrustshift::AbsView<long long int>> {};
template <>
struct NumericTraits<thrustshift::AbsView<float>>
    : BaseTraits<FLOATING_POINT,
                 false,
                 false,
                 unsigned int,
                 thrustshift::AbsView<float>> {};
template <>
struct NumericTraits<thrustshift::AbsView<double>>
    : BaseTraits<FLOATING_POINT,
                 false,
                 false,
                 long long unsigned int,
                 thrustshift::AbsView<double>> {};

} // namespace cub

namespace thrustshift {

namespace async {

/*! \brief Batched sort of keys with values.
 *
 *  Uses CUB's radix sort.
 */
template <class KeyInRange,
          class KeyOutRange,
          class ValueInRange,
          class ValueOutRange,
          class MemoryResource>
void sort_batched_descending(cuda::stream_t& stream,
                             KeyInRange&& keys_in,
                             KeyOutRange&& keys_out,
                             ValueInRange&& values_in,
                             ValueOutRange&& values_out,
                             std::size_t batch_len,
                             MemoryResource& delayed_memory_resource) {

	const std::size_t N = keys_in.size();

	gsl_Expects(N % batch_len == 0);
	gsl_Expects(batch_len > 0);
	gsl_Expects(keys_out.size() == N);
	gsl_Expects(values_in.size() == N);
	gsl_Expects(values_out.size() == N);

	auto cit = thrust::make_counting_iterator(0);
	auto tit = thrust::make_transform_iterator(
	    cit, [batch_len] __device__(int i) { return i * batch_len; });

	const std::size_t num_batches = N / batch_len;

	size_t tmp_bytes_size = 0;
	void* tmp_ptr = nullptr;

	using KeyT = typename std::remove_reference<KeyOutRange>::type::value_type;

	auto exec = [&] {
		cuda::throw_if_error(cub::DeviceSegmentedRadixSort::SortPairsDescending(
		    tmp_ptr,
		    tmp_bytes_size,
		    keys_in.data(),
		    keys_out.data(),
		    values_in.data(),
		    values_out.data(),
		    gsl_lite::narrow<int>(N),
		    gsl_lite::narrow<int>(num_batches),
		    tit,
		    tit + 1,
		    0, // default value by CUB
		    sizeof(KeyT) * 8, // default value by CUB
		    stream.handle()));
	};
	exec();
	auto tmp =
	    make_not_a_vector<uint8_t>(tmp_bytes_size, delayed_memory_resource);
	tmp_ptr = tmp.to_span().data();
	exec();
}

/*! \brief Batched sort of keys with respect to their absolute values.
 *
 *  Example:
 *
 *  ```
 *  batch_len = 5
 *  keys_in = {-8, 7, 10, -6, 5}
 *  // sort_batched_abs
 *  keys_out = {5, -6, 7, -8, 10}
 *  ```
 *
 *  ```
 *  batch_len = 3
 *  keys_in = {-8, 7, 10,   -6, 5, 4}
 *  // sort_batched_abs
 *  keys_out = {7, -8, 10,   4, 5, -6}
 *  ```
 */
template <class KeyInRange,
          class KeyOutRange,
          class ValueInRange,
          class ValueOutRange,
          class MemoryResource>
void sort_batched_abs(cuda::stream_t& stream,
                      KeyInRange&& keys_in,
                      KeyOutRange&& keys_out,
                      ValueInRange&& values_in,
                      ValueOutRange&& values_out,
                      std::size_t batch_len,
                      MemoryResource& delayed_memory_resource) {

	const std::size_t N = keys_in.size();

	gsl_Expects(N % batch_len == 0);
	gsl_Expects(batch_len > 0);
	gsl_Expects(keys_out.size() == N);
	gsl_Expects(values_in.size() == N);
	gsl_Expects(values_out.size() == N);

	using KeyT = typename std::remove_reference<KeyOutRange>::type::value_type;
	using AbsT = AbsView<KeyT>;

	auto cit = thrust::make_counting_iterator(0);
	auto tit = thrust::make_transform_iterator(
	    cit, [batch_len] __device__(int i) { return i * batch_len; });

	const std::size_t num_batches = N / batch_len;

	size_t tmp_bytes_size = 0;
	void* tmp_ptr = nullptr;

	auto exec = [&] {
		cuda::throw_if_error(cub::DeviceSegmentedRadixSort::SortPairs(
		    tmp_ptr,
		    tmp_bytes_size,
		    reinterpret_cast<const AbsT*>(keys_in.data()),
		    reinterpret_cast<AbsT*>(keys_out.data()),
		    values_in.data(),
		    values_out.data(),
		    gsl_lite::narrow<int>(N),
		    gsl_lite::narrow<int>(num_batches),
		    tit,
		    tit + 1,
		    0, // first key bit for comparison
		    sizeof(KeyT) * 8 - 1, // highest bit is used to save sign
		    stream.handle()));
	};
	exec();
	auto tmp =
	    make_not_a_vector<uint8_t>(tmp_bytes_size, delayed_memory_resource);
	tmp_ptr = tmp.to_span().data();
	exec();
}

} // namespace async

} // namespace thrustshift