/*
  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_ */


#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


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

#ifndef UTILS_H_
#define UTILS_H_

#include <string>
#include <cmath>
#include <map>
#include <iostream>
#include <fstream>
#include <sstream>
#include <cassert>
#include <algorithm>
#include <gsl/gsl_sf_gamma.h>

////////////////////////////////////////////////////////////////////////////////
/////////////////   SOME USEFUL MACROS   ///////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#ifdef DEBUG
#define tracer if (0) ; else std::cerr
#define DBG if (1)
#else
#define tracer if (1) ; else std::cerr
#define DBG if (1) ; else
#endif

  // some syntactic sugar for implementing interfaces using (multiple) inheritance
#define interface class
#define implements public

  // A macro to disallow the copy constructor and operator= functions
  // This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
  TypeName(const TypeName&);               \
  void operator=(const TypeName&)

namespace gatsby { namespace libplump {

/**
 * Alias: std::vector<double>
 */
typedef std::vector<double> d_vec;

/**
 * Alias: std::vector<std::vector<double> >
 */
typedef std::vector<d_vec> d_vec_vec;

/**
 * Alias: std::vector<unsigned int>
 */
typedef std::vector<unsigned int> ui_vec;

/**
 * Alias: std::vector<std::vector<unsigned int> >
 */
typedef std::vector<ui_vec> ui_vec_vec;

/**
 * Compute log2(x) -- the log2 provided by GCC is somewhat weird.
 */
inline double log2(double x){
  static const double LOG2 = std::log(2.);
  return std::log(x)/LOG2;
}

/**
 * Read items of type E from a file and push them into a sequence of type
 * S (that has to support push_back(E item).
 *
 * Items are read from the file sizeof(E) bytes at a time. No error checking
 * is performed.
 *
 * @tparam E Type of element to read from file, elements will be read sizeof(E)
 *           bytes at a time.
 * @tparam S Type of sequence to push items into;
 *           must support push_back(E item)
 * @param fileName Name of the file to read from
 * @param seq Sequence to push items into
 * @param limit Maximum number of items to push (default: 0, no limit)
 */
template<typename E, class S>
  inline void pushFileToVec(const std::string& fileName, S& seq, int limit = 0){
    std::ifstream in;
    in.open(fileName.c_str(),std::ios::binary | std::ios_base::in);
    char buffer[sizeof(E)];
    in.read(buffer,sizeof(E));
    int j = 0;
    if (limit==0)
      limit = -1;
    while (!in.eof() && j!=limit){
      seq.push_back(*((E*)buffer));
      in.read(buffer,sizeof(E));
      j++;
    }
    in.close();
  }

/**
 * Push each character of string s individually into container seq of type S,
 * which has to support push_back(char c);
 *
 * @tparam S type of container to push into
 * @param s input string to push
 * @param seq output container to push into
 */
template<class S>
  inline void pushStringToVec(const std::string& s, S& seq){
    for (size_t i=0;i<s.length();i++) {
      seq.push_back(s[i]);
    }
  }

/**
 * Convert a map<T,T> where T is an integer type with
 * range 0...MAX to a vector v of length MAX+1 so that
 * v[x] = m[x] for x=1,...,MAX. v[x] = 0 if m.count(x) = 0.
 *
 * @tparam T integer type
 * @param m input map
 * @param v output vector
 * @returns pointer to histogram vector on the heap
 */
template<typename T>
  inline void mapHistogramToVectorHistogram(const std::map<T,T>& m, std::vector<T>& v) {
    T max = (*max_element(m.begin(), m.end())).first;
    v.clear();
    v.assign(max + 1,0);
    for (typename std::map<T,T>::iterator i = m.begin(); i != m.end(); ++i) {
      v[(*i).first] = (*i).second;
    }
    return v;
  }

/**
 * Count the number of times the elements k=0...max-1 occur in vec and
 * return the result in ret[k].
 */
template<typename T>
  inline std::vector<T> vec2hist(std::vector<T>& vec, T max) {
    std::vector<T> ret(max,0);
    for (unsigned int i = 0; i < vec.size(); ++i) {
      ++ret[vec[i]];
    }
    return ret;
  }

/**
 * Compute the mean of the elements in a sequence.
 *
 * @tparam T iteratable sequence type; must have a const_iterator member
 *           as well as begin() and end() methods.
 * @param in sequence to compute the mean of
 * @returns the mean of the elements in the sequence
 */
template<typename T>
  inline double mean(const T& in) {
    double mean = 0.0;
    size_t j = 0;
    for (typename T::const_iterator i = in.begin(); i != in.end(); ++i) {
      mean += (((double)*i)-mean)/(++j);
    }
    return mean;
  }

/**
 * Sum the elements in a sequence.
 *
 * @tparam T iteratable sequence type; must have a const_iterator member
 *           as well as begin() and end() methods.
 * @param in sequence to compute the sum of
 * @returns the sum of the elements in the sequence
 */
template<typename T>
  inline typename T::value_type sum(const T& in) {
    typename T::value_type sum = 0;
    for (typename T::const_iterator i = in.begin(); i != in.end(); ++i) {
      sum += *i;
    }
    return sum;
  }

inline void log2_vec(d_vec& in) {
  for (size_t i=0;i<in.size();i++){
    in[i] = log2(in[i]);
  }
}

inline void exp_vec(d_vec& in) {
  for (size_t i=0;i<in.size();i++){
    in[i] = exp(in[i]);
  }
}

/**
 * Multiply all elements of a vector by a constant.
 */
template<typename elem_t>
  inline void mult_vec(std::vector<elem_t>& in, elem_t mult) {
    for (size_t i=0;i<in.size();i++){
      in[i] = in[i] * mult;
    }
  }

/**
 * Add a constant to all elements of a vector.
 */
template<typename elem_t>
  inline void add_vec(std::vector<elem_t>& in, elem_t add) {
    for (size_t i=0;i<in.size();i++){
      in[i] = in[i] + add;
    }
  }

/**
 * Subtract max element fromall elements of a vector.
 */
template<typename elem_t>
  inline void subMax_vec(std::vector<elem_t>& in) {
    elem_t max = *std::max_element(in.begin(), in.end());
    // if (max == -INFINITY) {
    //   for (size_t i=0;i<in.size();i++){
    //     in[i] = 0;
    //   }
    // }
    for (size_t i=0;i<in.size();i++){
      in[i] = in[i] - max;
    }
  }

/**
 * Add two vectors elementwise.
 */
template<typename elem_t>
  inline void add_vec(std::vector<elem_t>& inout, const std::vector<elem_t>& add) {
    std::transform(inout.begin(), inout.end(), add.begin(), inout.begin(),
                  std::plus<elem_t>());
  }

/**
 * Elementwise multiplication. Results is returned in the first argument.
 */
template<typename elem_t>
  inline void mult_vec(std::vector<elem_t>& inout, std::vector<elem_t> in) {
    assert(inout.size() == in.size());
    for (size_t i=0;i<inout.size();i++){
      inout[i] *= in[i];
    }
  }

/**
 * Average the values in multiple vectors of the same length.
 **/
inline d_vec average(const d_vec_vec& ins) {
  d_vec out(ins[0].size(), 0);
  for (int j = 0; j < ins.size(); j++) {
    add_vec(out, ins[j]);
  }
  mult_vec(out, 1./ins.size());
  return out;
}

template<typename elem_t>
  inline double prob2loss(std::vector<elem_t> in) {
    double out = 0.0;
    for (size_t i=0;i<in.size();i++){
      out -= log2(in[i]);
    }
    return out/in.size();
  }

inline bool closeTo(double value, double target, double tol=10e-4) {
  return std::abs(value-target) < tol;
}

template<typename Iterable>
  inline std::string iterableToString(const Iterable input) {
    std::ostringstream output;
    for(typename Iterable::const_iterator it = input.begin(); it!=input.end();it++) {
      output << *it << ", ";
    }
    return output.str();
  }

template<typename Iterable>
  void iterableToCSVFile(const Iterable input, std::string fn) {
    std::ofstream output(fn.c_str());
    output.precision(10);
    for(typename Iterable::const_iterator it = input.begin(); it!=input.end();it++) {
      output << *it << ", ";
    }
  }

/**
 * computes log(exp(a) + exp(b)) while avoiding numerical
 * instabilities.
 */
inline double logsumexp(double a, double b) {
  // choose c to be the one that is largest in abs value
  double c = (a>b)?a:b;
  return (log(exp(a-c) + exp(b-c))) + c;
}

inline double sigmoid(double x) {
  return 1.0/(1.0 + exp(-x));
}

// logit = inverse sigmoid
inline double logit(double x) {
  return log(x) - log(1-x);
}

inline double logKramp(double base, double inc, double lim) {
  if (inc == 0)
      return lim*log(base);
  if (lim <= 0) {
    return 0;
  } else {
    return lim*log(inc) + gsl_sf_lnpoch(base/inc, lim);
  }
}

inline double kramp(double base, double inc, double lim) {
  return exp(logKramp(base, inc, lim));
}

static clock_t global_clock;

/*
 * Start timing
 */
inline void tic() {
  global_clock = clock();
}

/**
 * Return number of seconds since last tic()
 */
inline double toc() {
  return (clock() - global_clock)/(double)CLOCKS_PER_SEC;

}

inline std::string makeProgressBarString(double percentDone, int total=10) {
  std::ostringstream out;

  int numDone = (int)floor(total * percentDone);

  out << "[";
  for (int i=0; i<numDone; i++) {
    out << "=";
  }
  out << ">";
  for (int i=numDone; i<total; i++) {
    out << " ";
  }
  out << "]";
  return out.str();
}

}} // namespace gatsby::libplump

#endif /* UTILS_H_ */


/*	$Id$ */
/*
 * Copyright (c) 2014, 2015 Kristaps Dzonsons <kristaps@kcons.eu>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include <assert.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#ifdef __linux__
#include <bsd/stdlib.h> /* arc4random() */
#endif
#include <string.h>

#include <glib.h>
#include <gtk/gtk.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_multifit.h>
#include <gsl/gsl_histogram.h>
#include <kplot.h>

#include "extern.h"

enum	kmltype {
	KML_INIT = 0,
	KML_KML,
	KML_DOCUMENT,
	KML_FOLDER,
	KML_PLACEMARK,
	KML_POINT,
	KML_COORDINATES,
	KML_DESCRIPTION,
	KML__MAX
};

enum	kmlkey {
	KMLKEY_MEAN,
	KMLKEY_MEAN_PERCENT,
	KMLKEY_STDDEV,
	KMLKEY_POPULATION,
	KMLKEY__MAX
};

struct	kmlsave {
	FILE		*f;
	struct sim	*cursim;
	size_t		 curisland;
	gchar		*buf;
	gsize		 bufsz;
	gsize		 bufmax;
	int		 buffering;
};

struct	kmlparse {
	enum kmltype	 elem; /* current element */
	struct kmlplace	*cur; /* currently-parsed kmlplace */
	gchar		*buf; /* current parse text buffer */
	gsize	 	 bufsz; /* size in parse buffer */
	gsize	 	 bufmax; /* maximum sized buffer */
	gchar		*altbuf;
	gsize	 	 altbufsz;
	gsize	 	 altbufmax;
	GList		*places; /* parsed places */
	gchar		*ign; /* element we're currently ignoring */
	size_t		 ignstack; /* stack of "ign" while ignoring */
#define	KML_STACKSZ	 128
	enum kmltype	 stack[128];
	size_t		 stackpos;
};

static	const char *const kmlkeys[KMLKEY__MAX] = {
	"mean", /* KMLKEY_MEAN */
	"meanpct", /* KMLKEY_MEAN_PERCENT */
	"stddev", /* KMLKEY_STDDEV */
	"population", /* KMLKEY_POPULATION */
};

static	const char *const kmltypes[KML__MAX] = {
	NULL, /* KML_INIT */
	"kml", /* KML_KML */
	"Document", /* KML_DOCUMENT */
	"Folder", /* KML_FOLDER */
	"Placemark", /* KML_PLACEMARK */
	"Point", /* KML_POINT */
	"coordinates", /* KML_COORDINATES */
	"description", /* KML_DESCRIPTION */
};

static	const double DEG_TO_RAD = 0.017453292519943295769236907684886;
static	const double EARTH_RADIUS_IN_METERS = 6372797.560856;

static double
kml_dist(const struct kmlplace *from, const struct kmlplace *to)
{
	double	 latitudeArc, longitudeArc,
		 latitudeH, lontitudeH, tmp;

	latitudeArc = (from->lat - to->lat) * DEG_TO_RAD;
	longitudeArc = (from->lng - to->lng) * DEG_TO_RAD;
	latitudeH = sin(latitudeArc * 0.5);
	latitudeH *= latitudeH;
	lontitudeH = sin(longitudeArc * 0.5);
	lontitudeH *= lontitudeH;
	tmp = cos(from->lat * DEG_TO_RAD) *
		cos(to->lat * DEG_TO_RAD);

	return(EARTH_RADIUS_IN_METERS * 2.0 *
		asin(sqrt(latitudeH + tmp * lontitudeH)));
}

static void
kml_append(gchar **buf, gsize *bufsz,
	gsize *bufmax, const char *text, gsize sz)
{

	/* XXX: no check for overflow... */
	if (*bufsz + sz + 1 > *bufmax) {
		*bufmax = *bufsz + sz + 1024;
		*buf = g_realloc(*buf, *bufmax);
	}

	memcpy(*buf + *bufsz, text, sz);
	*bufsz += sz;
	(*buf)[*bufsz] = '\0';
	g_assert(*bufsz <= *bufmax);
}

static enum kmltype
kml_lookup(const gchar *name)
{
	enum kmltype	i;

	for (i = 0; i < KML__MAX; i++) {
		if (NULL == kmltypes[i])
			continue;
		if (0 == g_strcmp0(kmltypes[i], name))
			break;
	}

	return(i);
}

static void
kmlparse_free(gpointer dat)
{
	struct kmlplace	*place = dat;

	if (NULL == place)
		return;
	free(place);
}

void
kml_free(struct kml *kml)
{

	if (NULL == kml)
		return;

	g_list_free_full(kml->kmls, kmlparse_free);

	if (NULL != kml->file)
		g_mapped_file_unref(kml->file);
}

/*
 * Try to find the string "@@population=NN@@", which stipulates the
 * population for this particular island.
 * If we don't find it, just return--we'll use the default.
 * If we find a bad population, raise an error.
 */
static int
kml_placemark(const gchar *buf, struct kmlplace *place)
{
	const gchar	*cp;
	gchar		*ep;
	gsize		 keysz;
	gchar		 nbuf[22];

	keysz = strlen("@@population=");
	while (NULL != (cp = strstr(buf, "@@population="))) {
		buf = cp + keysz;
		if (NULL == (cp = strstr(buf, "@@")))
			break;
		if ((gsize)(cp - buf) >= sizeof(nbuf) - 1)
			return(0);
		memcpy(nbuf, buf, cp - buf);
		nbuf[cp - buf] = '\0';
		place->pop = g_ascii_strtoull(buf, &ep, 10);
		if (ERANGE == errno || EINVAL == errno || ep == buf)
			return(0);
		break;
	}

	return(1);
}

static void
kml_elem_end(GMarkupParseContext *ctx,
	const gchar *name, gpointer dat, GError **er)
{
	struct kmlparse	 *p = dat;
	enum kmltype	  t;
	gchar		**set;

	if (NULL != p->ign) {
		g_assert(p->ignstack > 0);
		if (0 == g_strcmp0(p->ign, name))
			p->ignstack--;
		if (p->ignstack > 0)
			return;
		g_free(p->ign);
		p->ign = NULL;
		return;
	}

	t = kml_lookup(name);
	g_assert(p->stackpos > 0);
	g_assert(t == p->stack[p->stackpos - 1]);
	p->stackpos--;

	switch (p->stack[p->stackpos]) {
	case (KML_PLACEMARK):
		/*
		 * if we're ending a Placemark, first check whether
		 * we've listed a population somewhere in any of the
		 * nested text segments.
		 * Also make sure that we have some coordinates (the
		 * default value was 360 for both, which of course isn't
		 * a valid coordinate).
		 */
		g_assert(NULL != p->cur);
		if ( ! kml_placemark(p->altbuf, p->cur)) {
			*er = g_error_new_literal
				(G_MARKUP_ERROR,
				 G_MARKUP_ERROR_INVALID_CONTENT,
				 "Cannot parse population");
			break;
		} else if (p->cur->lat > 180 || p->cur->lng > 180) {
			*er = g_error_new_literal
				(G_MARKUP_ERROR,
				 G_MARKUP_ERROR_INVALID_CONTENT,
				 "No coordinates for placemark");
			break;
		}
		p->places = g_list_append(p->places, p->cur);
		p->cur = NULL;
		break;
	case (KML_COORDINATES):
		/*
		 * Parse coordinates from the mix.
		 * Coordinates are longitude,latitude,altitude.
		 * Parse quickly and just make sure they're valid.
		 */
		set = g_strsplit(p->buf, ",", 3);
		p->cur->lng = g_ascii_strtod(set[0], NULL);
		if (ERANGE == errno)
			*er = g_error_new_literal
				(G_MARKUP_ERROR,
				 G_MARKUP_ERROR_INVALID_CONTENT,
				 "Cannot parse longitude");
		else if (p->cur->lng > 180.0 || p->cur->lng < -180.0)
			*er = g_error_new_literal
				(G_MARKUP_ERROR,
				 G_MARKUP_ERROR_INVALID_CONTENT,
				 "Invalid longitude");
		p->cur->lat = g_ascii_strtod(set[1], NULL);
		if (ERANGE == errno)
			*er = g_error_new_literal
				(G_MARKUP_ERROR,
				 G_MARKUP_ERROR_INVALID_CONTENT,
				 "Cannot parse latitude");
		else if (p->cur->lat > 90.0 || p->cur->lat < -90.0)
			*er = g_error_new_literal
				(G_MARKUP_ERROR,
				 G_MARKUP_ERROR_INVALID_CONTENT,
				 "Invalid latitude");
		g_strfreev(set);
		break;
	default:
		break;
	}
}

static void
kml_elem_start(GMarkupParseContext *ctx,
	const gchar *name, const gchar **attrn,
	const gchar **attrv, gpointer dat, GError **er)
{
	enum kmltype	 t;
	struct kmlparse	*p = dat;

	if (NULL != p->ign) {
		g_assert(p->ignstack > 0);
		if (0 == g_strcmp0(p->ign, name))
			p->ignstack++;
		return;
	}

	if (KML__MAX == (t = kml_lookup(name))) {
		assert(0 == p->ignstack);
		p->ign = g_strdup(name);
		p->ignstack = 1;
		return;
	}

	p->stack[p->stackpos++] = t;
	p->bufsz = 0;

	switch (p->stack[p->stackpos - 1]) {
	case (KML_PLACEMARK):
		/*
		 * If we're starting a Placemark, initialise ourselves
		 * with bad coorindates and a default population.
		 */
		if (NULL == p->cur) {
			p->cur = g_malloc0(sizeof(struct kmlplace));
			p->cur->lat = p->cur->lng = 360;
			p->cur->pop = 2;
			p->altbufsz = 0;
			break;
		}
		*er = g_error_new_literal
			(G_MARKUP_ERROR,
			 G_MARKUP_ERROR_INVALID_CONTENT,
			 "Nested placemarks not allowed.");
		break;
	default:
		break;
	}
}

static void
kml_text(GMarkupParseContext *ctx,
	const gchar *txt, gsize sz, gpointer dat, GError **er)
{
	struct kmlparse	*p = dat;

	if (NULL != p->cur)
		kml_append(&p->altbuf, &p->altbufsz,
			&p->altbufmax, txt, sz);

	/* No collection w/o element or while ignoring. */
	if (NULL != p->ign || 0 == p->stackpos)
		return;

	/* Each element for which we're going to collect text. */
	switch (p->stack[p->stackpos - 1]) {
	case (KML_COORDINATES):
		break;
	default:
		return;
	}

	kml_append(&p->buf, &p->bufsz, &p->bufmax, txt, sz);
}

static void
kml_error(GMarkupParseContext *ctx, GError *er, gpointer dat)
{

	g_warning("%s", er->message);
}

struct kml *
kml_torus(size_t islands, size_t islanders)
{
	struct kml	*kml;
	struct kmlplace	*p;
	size_t		 i;

	kml = g_malloc0(sizeof(struct kml));

	for (i = 0; i < islands; i++) {
		p = g_malloc0(sizeof(struct kmlplace));
		p->pop = islanders;
		p->lng = 360 * i / (double)islands - 180.0;
		p->lat = 0.0;
		kml->kmls = g_list_append(kml->kmls, p);
	}

	return(kml);
}

struct kml *
kml_rand(size_t islands, size_t islanders)
{
	struct kml	*kml;
	struct kmlplace	*p;
	size_t		 i;

	kml = g_malloc0(sizeof(struct kml));

	for (i = 0; i < islands; i++) {
		p = g_malloc0(sizeof(struct kmlplace));
		p->pop = islanders;
		p->lng = 360 * arc4random() / (double)UINT32_MAX - 180.0;
		p->lat = 180 * arc4random() / (double)UINT32_MAX - 90.0;
		kml->kmls = g_list_append(kml->kmls, p);
	}

	return(kml);
}

struct kml *
kml_parse(const gchar *file, GError **er)
{
	GMarkupParseContext	*ctx;
	GMarkupParser	  	 parse;
	GMappedFile		*f;
	int			 rc;
	struct kmlparse		 data;
	struct kml		*kml;

	if (NULL != er)
		*er = NULL;

	memset(&parse, 0, sizeof(GMarkupParser));
	memset(&data, 0, sizeof(struct kmlparse));

	data.elem = KML_INIT;

	parse.start_element = kml_elem_start;
	parse.end_element = kml_elem_end;
	parse.text = kml_text;
	parse.error = kml_error;

	if (NULL == (f = g_mapped_file_new(file, FALSE, er)))
		return(NULL);

	ctx = g_markup_parse_context_new
		(&parse, 0, &data, NULL);
	g_assert(NULL != ctx);
	rc = g_markup_parse_context_parse
		(ctx, g_mapped_file_get_contents(f),
		 g_mapped_file_get_length(f), er);

	g_markup_parse_context_free(ctx);
	g_free(data.buf);
	g_free(data.altbuf);
	g_free(data.ign);
	kmlparse_free(data.cur);

	if (0 == rc) {
		g_list_free_full(data.places, kmlparse_free);
		g_mapped_file_unref(f);
		return(NULL);
	} else if (NULL == data.places) {
		g_mapped_file_unref(f);
		return(NULL);
	}

	g_assert(NULL != data.places);
	kml = g_malloc0(sizeof(struct kml));
	kml->file = f;
	kml->kmls = data.places;
	return(kml);
}

double **
kml_migration_twonearest(GList *list, enum maptop map)
{
	double		**p;
	double		  dist, min;
	size_t		  i, j, len, minj, min2j;
	struct kmlplace	 *pl1, *pl2;

	len = (size_t)g_list_length(list);
	g_assert(len > 0);
	if (1 == len) {
		g_debug("Request for two nearest falling "
			"back to nearest");
		return(kml_migration_nearest(list, map));
	}

	p = g_malloc0_n(len, sizeof(double *));

	/*
	 * Special case the torus, which has a well-defined layout
	 * between nodes, such that the next ("right") and previous
	 * ("left") islands, wrapping around, get the migrants.
	 */
	if (MAPTOP_TORUS == map) {
		for (i = 0; i < len; i++) {
			p[i] = g_malloc0_n(len, sizeof(double));
			if (i < len - 1)
				p[i][i + 1] = 0.5;
			else
				p[i][0] = 0.5;
			if (i > 0)
				p[i][i - 1] = 0.5;
			else
				p[i][len - 1] = 0.5;
		}
		return(p);
	}

	for (i = 0; i < len; i++) {
		pl1 = g_list_nth_data(list, i);
		p[i] = g_malloc0_n(len, sizeof(double));
		for (minj = j = 0, min = DBL_MAX; j < len; j++) {
			if (i == j)
				continue;
			pl2 = g_list_nth_data(list, j);
			if ((dist = kml_dist(pl1, pl2)) < min) {
				min = dist;
				minj = j;
			}
		}
		p[i][minj] = 0.5;
		for (min2j = j = 0, min = DBL_MAX; j < len; j++) {
			if (i == j || j == minj)
				continue;
			pl2 = g_list_nth_data(list, j);
			if ((dist = kml_dist(pl1, pl2)) < min) {
				min = dist;
				min2j = j;
			}
		}
		p[i][min2j] = 0.5;
		g_assert(min2j != minj);
		g_assert(i != min2j);
	}

	return(p);
}

double **
kml_migration_nearest(GList *list, enum maptop map)
{
	double		**p;
	double		  dist, min;
	size_t		  i, j, len, minj;
	struct kmlplace	 *pl1, *pl2;

	len = (size_t)g_list_length(list);
	g_assert(len > 0);
	p = g_malloc0_n(len, sizeof(double *));

	/*
	 * Special case the torus, which has a well-defined layout
	 * between nodes, such that the next ("right") island, wrapping
	 * around, gets the migrant.
	 */
	if (MAPTOP_TORUS == map) {
		for (i = 0; i < len; i++) {
			p[i] = g_malloc0_n(len, sizeof(double));
			p[i][(i + 1) % len] = 1.0;
		}
		return(p);
	}

	for (i = 0; i < len; i++) {
		pl1 = g_list_nth_data(list, i);
		p[i] = g_malloc0_n(len, sizeof(double));
		for (minj = j = 0, min = DBL_MAX; j < len; j++) {
			if (i == j)
				continue;
			pl2 = g_list_nth_data(list, j);
			if ((dist = kml_dist(pl1, pl2)) < min) {
				min = dist;
				minj = j;
			}
		}
		p[i][minj] = 1.0;
	}

	return(p);
}

double **
kml_migration_distance(GList *list, enum maptop map)
{
	double		**p;
	double		  dist, sum;
	size_t		  i, j, len;
	struct kmlplace	 *pl1, *pl2;

	len = (size_t)g_list_length(list);
	g_assert(len > 0);
	p = g_malloc0_n(len, sizeof(double *));
	for (i = 0; i < len; i++) {
		pl1 = g_list_nth_data(list, i);
		p[i] = g_malloc0_n(len, sizeof(double));
		for (sum = 0.0, j = 0; j < len; j++) {
			if (i == j) {
				p[i][j] = 0.0;
				continue;
			}
			pl2 = g_list_nth_data(list, j);
			dist = kml_dist(pl1, pl2);
			p[i][j] = 1.0 / (dist * dist);
			sum += p[i][j];
		}
		for (j = 0; j < len; j++)
			if (i != j)
				p[i][j] = p[i][j] / sum;
	}

	return(p);
}


#include <stdio.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_odeiv.h>
#include <gsl/gsl_odeiv2.h>

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

#include <time.h>
#include <math.h>

#include <GL/glut.h>
#include <GL/gl.h>

#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>

#define SLICE	360

typedef struct _line_vertex line_vertex;
struct _line_vertex
{
	float x1;
	float y1;

	float x2;
	float y2;
};

float text_x = 64.0f, text_y = 58.0f;
const float TRANS_VAL = 100.0f;

uint32_t mouse_loc;
int x, y;

bool clicked = false;
int pos_x, pos_y;
int mov_x, mov_y;
int click_x, click_y;
int special_key;

bool run = false;

line_vertex vertices[9] = {
	{ 50.0f, 50.0f, 50.0f, 30.0f },
	{ 50.0f, 50.0f, 55.0f, 50.0f },
	{ 75.0f, 50.0f, 80.0f, 50.0f },
	{ 80.0f, 50.0f, 80.0f, 30.0f },
	{ 55.0f, 55.0f, 55.0f, 45.0f },
	{ 55.0f, 55.0f, 75.0f, 55.0f },
	{ 75.0f, 55.0f, 75.0f, 45.0f },
	{ 55.0f, 45.0f, 75.0f, 45.0f }
};

line_vertex pick_vertices[9] = {
	{ 45.0f, 25.0f, 45.0f, 65.0f },
	{ 45.0f, 25.0f, 85.0f, 25.0f },
	{ 45.0f, 65.0f, 85.0f, 65.0f },
	{ 85.0f, 25.0f, 85.0f, 65.0f }
};

line_vertex power_vertices[5] = {
	{ 40.0f, 50.0f, 40.0f, 60.0f },
	{ 50.0f, 50.0f, 30.0f, 50.0f },
	{ 45.0f, 45.0f, 35.0f, 45.0f },
	{ 40.0f, 45.0f, 40.0f, 35.0f }
};

line_vertex ground_vertices[5] = {
	{ 40.0f, 50.0f, 40.0f, 60.0f },
	{ 50.0f, 50.0f, 30.0f, 50.0f },
	{ 47.5f, 47.5f, 32.5f, 47.5f },
	{ 45.0f, 45.0f, 35.0f, 45.0f }
};

int *jac;

int rhs (double t, const double y[], double f[], void *params_ptr);
//int jacobian (double t, const double y[], double *dfdy, double dfdt[], void *params_ptr);

void calc_differential_eq(void);
void draw_differential_eq_plot(void);
void draw_lattice(void);
void draw_lattice2(void);
void draw_volt_plot(void);

void reshape(int w, int h);

typedef struct _xy_plot xy_plot;
struct _xy_plot
{
	double x;
	double y;
};

//xy_plot plot_res[1001];
xy_plot plot_res[100000001];
//double volt_res[100000001];

float resistance;
float inductance;
float voltage;

int sub_window;
int sub_window2;

void sim_reshape(int w, int h)
{
    GLfloat n_range = 100.0f;

    if(h == 0)
        h = 1;

    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

	// glOrtho(표기하고자 하는 최소 x값, 최대 x값, 화면상 나타는 최소 y, 최대 y, z는 관계없음)
	// 즉 내가 보고자 하는 x, y, z의 범위를 지정할 수 있음
	glOrtho(-0.000005, 0.000005, -0.7, 0.7, -1, 1);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void simulate(void)
{
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glColor3f(1, 0, 0);

    glBegin(GL_LINE_LOOP);
    	glVertex3f(100.0, 0.0, 0.0);
        glVertex3f(-100.0, 0.0, 0.0);
    glEnd();

    glColor3f(0.0, 1.0, 0.0);

    glBegin(GL_LINE_LOOP);
        glVertex3f(0.0, 100.0, 0.0);
        glVertex3f(0.0, -100.0, 0.0);
    glEnd();

    draw_differential_eq_plot();
    draw_lattice();

    glutSwapBuffers();
}

void sim_reshape2(int w, int h)
{
    GLfloat n_range = 100.0f;

    if(h == 0)
        h = 1;

    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

	glOrtho(-0.000005, 0.000005, -2000000, 500000, -1, 1);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void simulate2(void)
{
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glColor3f(1, 0, 0);

    glBegin(GL_LINE_LOOP);
    	glVertex3f(100.0, 0.0, 0.0);
        glVertex3f(-100.0, 0.0, 0.0);
    glEnd();

    glColor3f(0.0, 1.0, 0.0);

    glBegin(GL_LINE_LOOP);
        glVertex3f(0.0, 500000.0, 0.0);
        glVertex3f(0.0, -2000000.0, 0.0);
    glEnd();

    draw_volt_plot();
    draw_lattice2();

    glutSwapBuffers();
}

void keyboard_handler(unsigned char key, int x, int y)
{
	int pid;
	int i, status;

	// fork -> execve
	// OpenGL -> GPU
#if 0
	char *argv[5] = {"print_simulation"};
	char *envp[] = {0};
	char volt[64] = {0};
	char coil[64] = {0};
	char resist[64] = {0};
#endif

	switch(key)
	{
		case 's':
			printf("Simulation Preparation\n");
			printf("저항값 입력: ");
			scanf("%f", &resistance);
			printf("인덕턴스값 입력: ");
			scanf("%f", &inductance);
			printf("입력 전원값 입력: ");
			scanf("%f", &voltage);

			printf("R = %f, L = %f, V = %f\n", resistance, inductance, voltage);

			calc_differential_eq();
			run = true;
			break;
		case 'r':
			printf("Run Simulation\n");

#if 0
			pid = fork();

			if (pid > 0)
			{
				printf("wait for simulation finished\n");
				wait(&status);
			}
			else if (pid == 0)
			{
				execve("./print_simulation", argv, envp);
			}
			else
			{
				perror("fork() ");
				exit(-1);
			}
#endif

			sub_window = glutCreateWindow("Current i(t)");

			glutDisplayFunc(simulate);
			glutReshapeFunc(sim_reshape);

			sub_window2 = glutCreateWindow("Voltage V(t)");

			glutDisplayFunc(simulate2);
			glutReshapeFunc(sim_reshape2);
			break;
		case 't':
			printf("Termination\n");
			glutDestroyWindow(sub_window);
			glutDestroyWindow(sub_window2);
			break;
		case 27:
			exit(1);
			break;
	}
}

void on_mouse(int button, int state, int x, int y)
{
	special_key = glutGetModifiers();

	if (button == GLUT_LEFT_BUTTON & state == GLUT_DOWN)
	{
		if (clicked == false)
		{
			printf("Click - Down\n");
			click_x = x;
			click_y = y;
			clicked = true;

			printf("click_x = %d, click_y = %d\n", click_x, click_y);
		}
	}

	if (button == GLUT_LEFT_BUTTON & state == GLUT_UP)
	{
		printf("Click - Up\n");
		clicked = false;
	}

	int window_width = glutGet(GLUT_WINDOW_WIDTH);
	int window_height = glutGet(GLUT_WINDOW_HEIGHT);

	GLbyte color[4];
	GLfloat depth;
	GLuint index;

	glReadPixels(x, window_height - y - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, color);
	glReadPixels(x, window_height - y - 1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
	glReadPixels(x, window_height - y - 1, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_INT, &index);

	printf("Clicked on pixel %d, %d, color %02hhx%02hhx%02hhx%02hhx, depth %f, stencil index %u\n",
			x, y, color[0], color[1], color[2], color[3], depth, index);

}

void drag_mouse(int x, int y)
{
	pos_x = x;
	pos_y = y;

	special_key = glutGetModifiers();

	if (clicked == true)
	{
		printf("clicked!\n");

		mov_x = (click_x - pos_x) / 3;
		mov_y = (click_y - pos_y) / 2.5;

		click_x = pos_x;
		click_y = pos_y;

		printf("mov_x = %d, mov_y = %d\n", mov_x, mov_y);

		int i;

		for (i = 0; i < 8; i++)
		{
			vertices[i].x1 -= mov_x;
			vertices[i].x2 -= mov_x;
			vertices[i].y1 += mov_y;
			vertices[i].y2 += mov_y;
		}

		for (i = 0; i < 4; i++)
		{
			pick_vertices[i].x1 -= mov_x;
			pick_vertices[i].x2 -= mov_x;
			pick_vertices[i].y1 += mov_y;
			pick_vertices[i].y2 += mov_y;
		}

		for (i = 0; i < 4; i++)
		{
			power_vertices[i].x1 -= mov_x;
			power_vertices[i].x2 -= mov_x;
			power_vertices[i].y1 += mov_y;
			power_vertices[i].y2 += mov_y;
		}

		for (i = 0; i < 4; i++)
		{
			ground_vertices[i].x1 -= mov_x;
			ground_vertices[i].x2 -= mov_x;
			ground_vertices[i].y1 += mov_y;
			ground_vertices[i].y2 += mov_y;
		}

		text_x -= mov_x;
		text_y += mov_y;

		glutPostRedisplay();
	}
}

void drawString (char *s)
{
	unsigned int i;

	for (i = 0; i < strlen (s); i++)
		glutBitmapCharacter (GLUT_BITMAP_HELVETICA_10, s[i]);
}

void drawStringBig (char *s)
{
	unsigned int i;

	for (i = 0; i < strlen (s); i++)
		glutBitmapCharacter (GLUT_BITMAP_HELVETICA_18, s[i]);
}

void draw_outline(void)
{
	int i;

	glColor3f(0, 1, 0);

	glBegin(GL_LINES);
		for (i = 0; i < 8; i++)
		{
			glVertex2f(pick_vertices[i].x1, pick_vertices[i].y1);
			glVertex2f(pick_vertices[i].x2, pick_vertices[i].y2);
		}
	glEnd();
}

void draw_inductance_outline(void)
{
	int i;

	glColor3f(0, 1, 0);

	glBegin(GL_LINES);
		for (i = 0; i < 4; i++)
		{
			glVertex2f(pick_vertices[i].x1 + 50.0f, pick_vertices[i].y1);
			glVertex2f(pick_vertices[i].x2 + 50.0f, pick_vertices[i].y2);
		}
	glEnd();
}

void draw_resistance(void)
{
	int i;
	static char label[100];

	glColor3f(1, 0, 1);

	glBegin(GL_LINES);
		for (i = 0; i < 8; i++)
		{
			glVertex2f(vertices[i].x1, vertices[i].y1);
			glVertex2f(vertices[i].x2, vertices[i].y2);
		}
	glEnd();

	sprintf (label, "R");
	glRasterPos2f (text_x, text_y);
	drawStringBig (label);
}

void draw_inductance(void)
{
	int i;
	static char label[100];

	glColor3f(1, 0, 1);

	glBegin(GL_LINES);
		for (i = 0; i < 8; i++)
		{
			glVertex2f(vertices[i].x1 + 50.0f, vertices[i].y1);
			glVertex2f(vertices[i].x2 + 50.0f, vertices[i].y2);
		}
	glEnd();

	sprintf (label, "L");
	glRasterPos2f (text_x + 50.0f, text_y);
	drawStringBig (label);
}

void draw_power_outline(void)
{
	int i;

	glColor3f(0, 1, 0);

	glBegin(GL_LINES);
		for (i = 0; i < 4; i++)
		{
			glVertex2f(pick_vertices[i].x1 - 75.0f, pick_vertices[i].y1 + 20.0f);
			glVertex2f(pick_vertices[i].x2 - 75.0f, pick_vertices[i].y2 + 20.0f);
		}
	glEnd();
}

void draw_power(void)
{
	int i;
	static char label[100];

	glColor3f(1, 0, 1);

	glBegin(GL_LINES);
		for (i = 0; i < 4; i++)
		{
			glVertex2f(power_vertices[i].x1 - 50.0f, power_vertices[i].y1 + 15.0f);
			glVertex2f(power_vertices[i].x2 - 50.0f, power_vertices[i].y2 + 15.0f);
		}
	glEnd();

	sprintf (label, "V");
	glRasterPos2f (text_x - 70.0f, text_y + 20.0f);
	drawStringBig (label);
}

void draw_ground_outline(void)
{
	int i;

	glColor3f(0, 1, 0);

	glBegin(GL_LINES);
		for (i = 0; i < 4; i++)
		{
			glVertex2f(pick_vertices[i].x1 - 75.0f, pick_vertices[i].y1 - 20.0f);
			glVertex2f(pick_vertices[i].x2 - 75.0f, pick_vertices[i].y2 - 20.0f);
		}
	glEnd();
}

void draw_ground(void)
{
	int i;
	static char label[100];

	glColor3f(1, 0, 1);

	glBegin(GL_LINES);
		for (i = 0; i < 4; i++)
		{
			glVertex2f(ground_vertices[i].x1 - 50.0f, ground_vertices[i].y1 - 30.0f);
			glVertex2f(ground_vertices[i].x2 - 50.0f, ground_vertices[i].y2 - 30.0f);
		}
	glEnd();

	sprintf (label, "GND");
	glRasterPos2f (text_x - 70.0f, text_y - 30.0f);
	drawStringBig (label);
}

void display(void)
{
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	glColor3f(1, 0, 0);

	glBegin(GL_LINE_LOOP);
	glVertex3f(100.0, 0.0, 0.0);
        glVertex3f(-100.0, 0.0, 0.0);
        glEnd();

        glColor3f(0.0, 1.0, 0.0);

        glBegin(GL_LINE_LOOP);
        glVertex3f(0.0, 100.0, 0.0);
        glVertex3f(0.0, -100.0, 0.0);
    glEnd();

	draw_resistance();
	draw_outline();

	draw_inductance();
	draw_inductance_outline();

	draw_power();
	draw_power_outline();

	draw_ground();
	draw_ground_outline();

#if 0
	if (run)
	{
		draw_differential_eq_plot();
	}

	draw_lattice();
#endif

	glutSwapBuffers();
}

void reshape(int w, int h)
{
    GLfloat n_range = 100.0f;

    if(h == 0)
        h = 1;

    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

#if 0
    if(w <= h)
        glOrtho(-n_range, n_range, -n_range * h / w, n_range * h / w, -n_range, n_range);
    else
        glOrtho(-n_range * w / h, n_range * w / h, -n_range, n_range, -n_range, n_range);
#endif

	//glOrtho(-10, 10, -0.5, 0.5, -1, 1);
	glOrtho(-100, 100, -100, 100, 1.0, -1.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int calc_vertices_num(void)
{
	double tmin = 0.0, tmax = 10.0, delta_t = 0.01;
	return (tmax - tmin) / delta_t;
}

void calc_differential_eq(void)
{
    int dim = 1;

	gsl_odeiv2_system sys = {rhs, NULL, dim, NULL};
	gsl_odeiv2_driver * d = gsl_odeiv2_driver_alloc_y_new (&sys, gsl_odeiv2_step_rkf45, 1e-6, 1e-6, 0.0);

	int cnt = 0;

	double i;
    double x0 = 0.0,  xf = 10.0;
    double x = x0;
    //double y[1] = { 0.5 };
    double y[1] = { 0.0 };
	double delta = 0.0000001;

	for (i = 0; i <= xf; i += delta)
    {
        double xi = x0 + i * (xf-x0) / xf;
        int status = gsl_odeiv2_driver_apply (d, &x, xi, y);

        if (status != GSL_SUCCESS)
        {
            printf ("error, return value=%d\n", status);
            break;
        }

		plot_res[cnt].x = x;
		plot_res[cnt].y = y[0];

		//volt_res[cnt] = y[0] * (resistance / inductance);

		if (cnt < 1001)
		{
       		printf ("%.8e %.8e\n", plot_res[cnt].x, plot_res[cnt].y);
		}

		cnt++;
    }

	printf("Finish Calc\n");

    gsl_odeiv2_driver_free (d);

#if 0
    const gsl_odeiv_step_type *type_ptr = gsl_odeiv_step_rkf45;

    gsl_odeiv_step *step_ptr = gsl_odeiv_step_alloc (type_ptr, dimension);
    gsl_odeiv_control *control_ptr = gsl_odeiv_control_y_new (eps_abs, eps_rel);
    gsl_odeiv_evolve *evolve_ptr = gsl_odeiv_evolve_alloc (dimension);

    gsl_odeiv_system my_system;	/* structure with the rhs function, etc. */

    double mu = 10;		/* parameter for the diffeq */
    double y[2];			/* current solution vector */

    double t, t_next;		/* current and next independent variable */
    double tmin, tmax, delta_t;	/* range of t and step size for output */

    double h = 1e-6;		/* starting step size for ode solver */

	int cnt = 0;
	int vertex_num = calc_vertices_num();

    my_system.function = rhs;	/* the right-hand-side functions dy[i]/dt */
    my_system.jacobian = NULL;
    my_system.dimension = dimension;	/* number of diffeq's */
    my_system.params = NULL;

    tmin = 0.;			/* starting t value */
    tmax = 10.;			/* final t value */
    delta_t = 0.01;

    y[1] = 0.5;			/* initial x value */

    t = tmin;             /* initialize t */

    for (t_next = tmin + delta_t; t_next <= tmax; t_next += delta_t)
    {
        while (t < t_next)	/* evolve from t to t_next */
        {
            gsl_odeiv_evolve_apply (evolve_ptr, control_ptr, step_ptr,
                                    &my_system, &t, t_next, &h, y);
        }

		plot_res[cnt].x = t;
		plot_res[cnt].y = y[0];
        printf ("%.5e %.5e %.5e\n", t, plot_res[cnt].x, plot_res[cnt].y); /* print at t=t_next */

		cnt++;
    }

    gsl_odeiv_evolve_free (evolve_ptr);
    gsl_odeiv_control_free (control_ptr);
    gsl_odeiv_step_free (step_ptr);
#endif
}

void draw_lattice(void)
{
	int i;
	float lattice = 0.000001;
	char buf[64] = {0};

	glColor3f(1, 1, 0);
	glBegin(GL_LINES);
		for (i = 1; i <= 10; i++)
		{
			glVertex2f(i * 0.000001, -0.01);
			glVertex2f(i * 0.000001, 0.01);
		}
	glEnd();

	for (i = 1; i <= 10; i++)
	{
		sprintf(buf, "%f", lattice * i);
	    glRasterPos2f (lattice * i - 0.0000005, -0.05);
	    drawStringBig (buf);
	}

	glBegin(GL_LINES);
		for (i = 1; i <= 10; i++)
		{
			glVertex2f(-0.0000002, 0.1 * i);
			glVertex2f(0.0000002, 0.1 * i);
		}
	glEnd();

	for (i = 1; i <= 10; i++)
	{
		sprintf(buf, "%.1f", 0.1 * i);
	    glRasterPos2f (-0.0000004, 0.1 * i - 0.05);
	    drawStringBig (buf);
	}
}

void draw_differential_eq_plot(void)
{
	static char label[100];

	float x = 0, x2 = 0, y2, cx, cy;
	float tmp;
	int cache = 0;
	int i;

	glColor3f(0, 1, 1);
	glBegin(GL_LINES);
	//for(; ; t += step)
	for (i = 0; i < 100000000; i++)
	{
		x2 = plot_res[i].x;
		y2 = plot_res[i].y;

		if(cache)
		{
			glVertex2f(cx, cy);	// 이전값
			glVertex2f(x2, y2);	// 현재값
		}

		cache = 1;
		cx = x2;
		cy = y2;
		//printf("t = %f, y2 = %f\n", x2, y2);
	}
	glEnd();

	sprintf (label, "i(t)");
	// -0.000005
    glRasterPos2f (-0.0000045, 0.0000045);
    drawStringBig (label);
}

void draw_lattice2(void)
{
	int i;
	float lattice = 0.000001;
	char buf[64] = {0};

	glColor3f(1, 0, 0);
	glBegin(GL_LINES);
		for (i = 1; i <= 10; i++)
		{
			glVertex2f(i * 0.000001, -20000);
			glVertex2f(i * 0.000001, 20000);
		}
	glEnd();

	for (i = 1; i <= 10; i++)
	{
		sprintf(buf, "%f", lattice * i);
	    glRasterPos2f (lattice * i - 0.0000005, 50000);
	    drawStringBig (buf);
	}

	glBegin(GL_LINES);
		for (i = 1; i <= 10; i++)
		{
			glVertex2f(-0.0000002, -200000 * i);
			glVertex2f(0.0000002, -200000 * i);
		}
	glEnd();

	for (i = 1; i <= 10; i++)
	{
		sprintf(buf, "%d", -200000 * i);
	    glRasterPos2f (-0.0000015, -200000 * i);
	    drawStringBig (buf);
	}

}

void draw_volt_plot(void)
{
	static char label[100];

	float x = 0, x2 = 0, y2, cx, cy;
	float tmp;
	int cache = 0;
	int i;

	glColor3f(1, 1, 0);
	glBegin(GL_LINES);
	//for(; ; t += step)
	for (i = 0; i < 100000000; i++)
	{
		x2 = plot_res[i].x;
		y2 = plot_res[i].y * (-resistance / inductance);

		if(cache)
		{
			glVertex2f(cx, cy);	// 이전값
			glVertex2f(x2, y2);	// 현재값
		}

		cache = 1;
		cx = x2;
		cy = y2;
		//printf("t = %f, y2 = %f\n", x2, y2);
	}
	glEnd();

	sprintf (label, "V(t)");
	// -0.000005
    glRasterPos2f (-0.0000045, 0.000004);
    drawStringBig (label);
}

int main (int argc, char **argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE);
	glutInitWindowSize(800, 800);
	glutInitWindowPosition(0, 0);
	glutCreateWindow("Digital Signal Processing");

	printf("저항 1000, 인덕터: 0.0003, 전압: 5V\n");

	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutMouseFunc(on_mouse);
	glutMotionFunc(drag_mouse);
	// 새롭게 추가된 요것은 키보드 입력을 처리하는 콜백 등록 함수
	glutKeyboardFunc(keyboard_handler);
	glutMainLoop();

    return 0;
}

/*************************** rhs ****************************/
/*
   x' = v                  ==>  dy[0]/dt = f[0] = y[1]
   v' = -x + \mu v (1-x^2) ==>  dy[1]/dt = f[1] = -y[0] + mu*y[1]*(1-y[0]*y[0])

   x''(t) + x'(t) + x(t) = 0
   x''(t) = -x'(t) - x(t)

   x' = v            ===> dy[0]/dt = f[0] = y[1]
   v' = -v - x       ===> dy[1]/dt = f[1] = -y[1] - y[0]
*/

// https://www.wolframalpha.com/input/?i=y%27%27+%2B+y%27+%2B+y+%3D+0%2C+y%280%29+%3D+1%2C+y%27%280%29+%3D+0, y(0) = 1
// https://www.wolframalpha.com/input/?i=%28sqrt%283%29+*+sin%28sqrt%283%29+%2F+2%29+%2B+3+*+cos%28sqrt%283%29+%2F+2%29%29+%2F+%283+*+sqrt%28e%29%29, y(1)
// (sqrt(3) * sin(sqrt(3) / 2) + 3 * cos(sqrt(3) / 2)) / (3 * sqrt(e)), y(1) = 0.6597001...
int
rhs (double t, const double y[], double f[], void *params_ptr)
{
#if 0
	f[0] = y[1];
	f[1] = -y[1] - 9 * y[0];
#endif

	f[0] =  -(resistance / inductance) * y[0] + voltage / inductance;

    return GSL_SUCCESS;		/* GSL_SUCCESS defined in gsl/errno.h as 0 */
}

#if 0

주석: 현재 아래 상황은 방전 상황입니다.
      또한 현재 코드는 충전 상황입니다.
	  두 가지 버전으로 작성이 필요함.

V = iR + L * di /dt
초기조건 V_L(t) | (t=0) ===> V_L(0) = 0V

V * (1 / L) = i(t) * (R / L) + i`(t)

P(x) = R / L

mu = C * exp^(int P(x)dx)

mu = C * exp^(R/L)t

d (mu * i(t)) / dt = (R/L) exp^(R/L)t * i(t) + exp^(R/L)t * i`(t)

int d(mu * i(t)) / dt = mu * i(t) = c

exp^(R/L)t * i(t) = C

i(t) = C * exp^(-R/L)t

i(t) | (t=0) = 0.5A ===> C = 0.5

i(t) = 0.5 * exp^(-R/L)t
V(t) = L di / dt
     = L * 0.5 * (-R/L) * exp^(-R/L)t
	-(resistance / inductance)
#endif