// Demonstration of representing/analyzing circuits as MNA matrices
// to accompany "Analyzing On-Chip Interconnect with Modern C++"
// Author: Jeff Trull <edaskel@att.net>

/*
Copyright (c) 2014 Jeffrey E. Trull

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include <iostream>
#include <boost/numeric/odeint.hpp>

#include "ckt_matrix.h"

// Create my standard 2-signal coupling testcase
struct coupling_circuit_t {
    coupling_circuit_t() {
        // MNA
        // MNA - we will have 10 state variables:
        // 8 for node voltages (vagg, n1, n2, n3, vvic, n5, n6, n7)
        // 2 for input currents (iagg, ivic)
        using namespace Eigen;
        typedef Matrix<double, 10, 10> state_matrix_t;
        state_matrix_t G, C;   // conductance and time derivative matrices
        G = state_matrix_t::Zero();
        C = state_matrix_t::Zero();

        double const kohm = 1000;
        double const ff   = 1e-15;
        double rdrv       = 0.1*kohm;
        double pi_r       = 1.0*kohm;
        double pi_c       = 100*ff;
        double coupl_c    = 100*ff;
        double rcvr_c     = 20*ff;

        stamp_i(G, 0, 8);                // aggressor driver current
        stamp(G, 0, 1, 1.0 / rdrv);      // aggressor driver impedance
        stamp(C, 1,    pi_c / 2);        // begin first "pi"
        stamp(G, 1, 2, 1.0 / pi_r);
        stamp(C, 2,    pi_c / 2);        // central node
        stamp(C, 2,    pi_c / 2);        // second "pi"
        stamp(G, 2, 3, 1.0 / pi_r);
        stamp(C, 3,    pi_c / 2);
        stamp(C, 3,    rcvr_c);          // aggressor receiver

        stamp_i(G, 4, 9);                // victim driver current
        stamp(G, 4, 5, 1.0 / rdrv);      // victim driver impedance
        stamp(C, 5,    pi_c / 2);        // begin first "pi"
        stamp(G, 5, 6, 1.0 / pi_r);
        stamp(C, 6,    pi_c / 2);        // central node
        stamp(C, 6,    pi_c / 2);        // second "pi"
        stamp(G, 6, 7, 1.0 / pi_r);
        stamp(C, 7,    pi_c / 2);
        stamp(C, 7,    rcvr_c);          // victim receiver

        stamp(C, 2, 6, coupl_c);         // coupling cap

        // We also have 2 inputs, two outputs
        typedef Matrix<double, 10, 2> io_matrix_t;
        io_matrix_t B = io_matrix_t::Zero();
        B(8, 0) = -1;                    // connect input 0 to vagg
        B(9, 1) = -1;                    // connect input 1 to vvic
        io_matrix_t L = io_matrix_t::Zero();
        L(3, 0) = 1;                     // connect agg rcvr to output 0
        L(7, 1) = 1;                     // connect vic rcvr to output 1

        // cross-check: compute moments
        Matrix<double, 2, 2> E = Matrix<double, 2, 2>::Zero();   // feedthrough term we don't have
        auto block_moments = moments(G, C, B, L, E, 2);
        std::cerr << "moment 0=\n" << block_moments[0] << std::endl;
        std::cerr << "moment 1=\n" << block_moments[1] << std::endl;

        // Now regularize.  Results are of dynamic (not initially known) size
        MatrixXd Greg, Creg;             // regularized versions of C and G
        Matrix<double, Dynamic, 2> Breg, Lreg;
        std::tie(Greg, Creg, Breg, Lreg) = regularize(G, C, B, L);

        // Finally, put in a form suitable for simulation, by transforming
        // C*dX/dt = -G*X + B*u
        // into
        // dX/dt   = -C.inv()*G*X + C.inv()*B*u
        // Wikipedia says Cholesky decomposition "roughly twice as efficient" as LU:
        assert(canLDLTDecompose(Creg));            // make sure we can use it
        drift_  = Creg.ldlt().solve(-1.0 * Greg);  // -Creg.inv()*Greg
        input_  = Creg.ldlt().solve(Breg);         // Creg.inv()*Breg
        output_ = Lreg.transpose();

    }

    // perform dX/dt calculation for ODEInt
    typedef std::vector<double> state_t;
    void operator()(state_t const& x, state_t& dxdt, double) const {
        using namespace Eigen;
        // need to wrap std::vector state types for Eigen to use
        Map<const Matrix<double, Dynamic, 1>> xvec(x.data(), x.size());
        Map<Matrix<double, Dynamic, 1>>       result(dxdt.data(), x.size());

        // simulating step function at time 0 for simplicity
        Matrix<double, 2, 1> u; u << 1.0, 0.0;   // aggressor voltage 1V, victim quiescent
        result = drift_ * xvec + input_ * u;
    }

    // turns internal state into output by applying transformed L matrix
    std::vector<double> state2output(state_t const& x) const {
        using namespace Eigen;
        std::vector<double> result(2);
        Map<const Matrix<double, Dynamic, 1> > xvec(x.data(), x.size());
        Map<Matrix<double, 2, 1> >             ovec(result.data());
        ovec = output_ * xvec;
        return result;
    }

    size_t statecnt() const {
        return drift_.rows();
    }

private:
    Eigen::Matrix<double, Eigen::Dynamic, 2>              input_;
    Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> drift_;
    Eigen::Matrix<double, 2, Eigen::Dynamic>             output_;
};

int main() {
    using namespace std;

    // instantiate circuit
    coupling_circuit_t coupling_test;

    // simulate
    typedef coupling_circuit_t::state_t state_t;
    state_t         x(coupling_test.statecnt(), 0.0);   // initial conditions = all zero

    using boost::numeric::odeint::integrate;

    integrate( coupling_test, x, 0.0, 1e-9, 1e-12,
               [&](state_t const& x, double t) {
                   auto outputs = coupling_test.state2output(x);
                   cout << t << " " << outputs[0] << " " << outputs[1] << endl;
               });
}


/*
 * Copyright 2015 Christoph Jud (christoph.jud@unibas.ch)
 *
 * 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.
 *
 */

#include <iostream>
#include <memory>
#include <ctime>
#include <chrono>

#include <boost/random.hpp>
#include <Eigen/SVD>

#include "LAPACKUtils.h"
#include "GaussianProcess.h"

using namespace gpr;

template<class T>
void Test1(unsigned N, bool cout=false){
    /*
     * Test 1: invert general matrix
     * - compare Eigen inversion and LAPACK inversion
     */
    std::cout << "Test 1: Eigen vs. LAPACK... (general matrix) " << std::endl;
    std::chrono::time_point<std::chrono::system_clock> start;

    typedef GaussianProcess<T> GaussianProcessType;
    typedef typename GaussianProcessType::MatrixType MatrixType;

    // generate random matrix
    static boost::minstd_rand randgen(static_cast<unsigned>(time(0)));
    static boost::normal_distribution<> dist(0, 1);
    static boost::variate_generator<boost::minstd_rand, boost::normal_distribution<> > r(randgen, dist);

    // generate double precision random matrix
    MatrixType m(N, N);
    for (unsigned i =0; i < N ; i++) {
        for (unsigned j = 0; j < N; j++) {
            m(i,j) = r();
        }
    }

    if(cout){
        std::cout << "matrix: " << std::endl;
        std::cout << m << std::endl;
    }

    // Eigen inversion (LU)
    std::cout << " - eigen... " << std::flush;
    start = std::chrono::system_clock::now();
    MatrixType m_inv = m.inverse();
    std::chrono::duration<double> elapsed_seconds = std::chrono::system_clock::now()-start;
    std::cout << "elapsed time: (sec) " << elapsed_seconds.count() << std::endl;
    if(cout) std::cout << m_inv << std::endl;

    // LAPACK inversion (LU)
    std::cout << " - lapack... " << std::flush;
    start = std::chrono::system_clock::now();
    MatrixType lu_inv = lapack::lu_invert<T>(m);
    elapsed_seconds = std::chrono::system_clock::now()-start;
    std::cout << "elapsed time: (sec) " << elapsed_seconds.count() << std::endl;
    if(cout) std::cout << lu_inv << std::endl;

    MatrixType lapack_identity = (m * lu_inv);
    MatrixType lu_identity = (m * m_inv);
    MatrixType identity = MatrixType::Identity(m.cols(), m.cols());

    T lapack_error = 0;
    T lu_error = 0;
    // generate double precision random matrix
    for (unsigned i =0; i < N ; i++) {
        for (unsigned j = 0; j < N; j++) {
            lu_error += std::fabs(identity(i,j)-lu_identity(i,j));
            lapack_error += std::fabs(identity(i,j)-lapack_identity(i,j));
        }
    }

    std::cout << " - [passed] error: eigen " << lu_error << ", lapack " << lapack_error << std::endl;
}

template<class T>
void Test2(unsigned N, bool cout=false){
    /*
     * Test 2: invert general matrix
     * - compare Eigen inversion and LAPACK inversion
     */
    std::cout << "Test 2: Eigen vs. LAPACK... (symmetric matrix) " << std::endl;
    std::chrono::time_point<std::chrono::system_clock> start;

    typedef GaussianProcess<T> GaussianProcessType;
    typedef typename GaussianProcessType::MatrixType MatrixType;
    typedef typename GaussianProcessType::VectorType VectorType;

    // generate random matrix
    static boost::minstd_rand randgen(static_cast<unsigned>(time(0)));
    static boost::normal_distribution<> dist(0, 1);
    static boost::variate_generator<boost::minstd_rand, boost::normal_distribution<> > r(randgen, dist);

    // generate double precision random matrix
    MatrixType m(N, N);
    for (unsigned i =0; i < N ; i++) {
        for (unsigned j = 0; j < N; j++) {
            m(i,j) = r();
        }
    }

    m = m.transpose()*m; // make it inverse and positive definite
    for(unsigned i=0; i<N; i++){
        m(i,i) += 0.01;
    }

    if(cout){
        std::cout << "matrix: " << std::endl;
        std::cout << m << std::endl;
    }

    // Eigen inversion for symmetric positive definite matrices
    std::cout << " - eigen... " << std::flush;
    start = std::chrono::system_clock::now();

    Eigen::SelfAdjointEigenSolver<MatrixType> es;
    es.compute(m);
    VectorType eigenValues = es.eigenvalues().reverse();
    MatrixType eigenVectors = es.eigenvectors().rowwise().reverse();
    if((eigenValues.real().array() < 0).any()){
        throw std::string("there are negative eigenvalues.");
        std::cout.flush();
    }
    MatrixType m_inv = eigenVectors * VectorType(1/eigenValues.array()).asDiagonal() * eigenVectors.transpose();
    std::chrono::duration<double> elapsed_seconds = std::chrono::system_clock::now()-start;
    std::cout << "elapsed time: (sec) " << elapsed_seconds.count() << std::endl;
    if(cout) std::cout << m_inv << std::endl;

    // LAPACK inversion (cholesky)
    std::cout << " - lapack... " << std::flush;
    start = std::chrono::system_clock::now();
    MatrixType chol_inv = lapack::chol_invert<T>(m);
    elapsed_seconds = std::chrono::system_clock::now()-start;
    std::cout << "elapsed time: (sec) " << elapsed_seconds.count() << std::endl;
    if(cout) std::cout << chol_inv << std::endl;

    MatrixType chol_identity = (m * chol_inv);
    MatrixType lu_identity = (m * m_inv);
    MatrixType identity = MatrixType::Identity(m.cols(), m.cols());

    T chol_error = 0;
    T lu_error = 0;
    // generate double precision random matrix
    for (unsigned i =0; i < N ; i++) {
        for (unsigned j = 0; j < N; j++) {
            //error += std::fabs(m_inv(i,j)-chol_inv(i,j));
            chol_error += std::fabs(chol_identity(i,j)-identity(i,j));
            lu_error += std::fabs(lu_identity(i,j)-identity(i,j));
        }
    }
    std::cout << " - [passed] error: eigen " << lu_error << ", lapack " << chol_error << std::endl;
}

int main (int argc, char *argv[]){

    unsigned n = 1000;
    bool cout = false;
    try{
        std::cout << "LAPACK inversion test: (float)" << std::endl;
        Test1<float>(n/4, cout);
        Test2<float>(n/4, cout);

        std::cout << "LAPACK inversion test: (double)" << std::endl;
        Test1<double>(n, cout);
        Test2<double>(n, cout);
    }
    catch(std::string& s){
        std::cout << " [failed] - " << s << std::endl;
        return -1;
    }

    return 0;
}


///1
// ALGOLAB BGL Tutorial 3
// Flow example demonstrating
// - breadth first search (BFS) on the residual graph

// Compile and run with one of the following:
// g++ -std=c++11 -O2 bgl_residual_bfs.cpp -o bgl_residual_bfs ./bgl_residual_bfs
// g++ -std=c++11 -O2 -I path/to/boost_1_58_0 bgl_residual_bfs.cpp -o bgl_residual_bfs; ./bgl_residual_bfs

// Includes
// ========
// STL includes
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
// BGL includes
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/push_relabel_max_flow.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/graph/strong_components.hpp>

// BGL graph definitions
// =====================
// Graph Type with nested interior edge properties for Flow Algorithms
typedef  boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS> traits;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::no_property,
  boost::property<boost::edge_capacity_t, long,
    boost::property<boost::edge_residual_capacity_t, long,
      boost::property<boost::edge_reverse_t, traits::edge_descriptor> > > >  graph;
// Interior Property Maps
typedef traits::vertex_descriptor vertex_desc;

typedef  boost::graph_traits<graph>::edge_descriptor      edge_desc;
typedef  boost::graph_traits<graph>::out_edge_iterator      out_edge_it;

// Custom Edge Adder Class, that holds the references
// to the graph, capacity map and reverse edge map
// ===================================================
class edge_adder {
 graph &G;

 public:
  explicit edge_adder(graph &G) : G(G) {}

  void add_edge(int from, int to, long capacity) {
    auto c_map = boost::get(boost::edge_capacity, G);
    auto r_map = boost::get(boost::edge_reverse, G);
    const edge_desc e = boost::add_edge(from, to, G).first;
    const edge_desc rev_e = boost::add_edge(to, from, G).first;
    c_map[e] = capacity;
    c_map[rev_e] = 0; // reverse edge has no capacity!
    r_map[e] = rev_e;
    r_map[rev_e] = e;
  }
};

// Main
void testcase() {
  // build graph
  int n, m;
  std::cin >> n >> m;
  graph G(n);
  edge_adder adder(G);
  // auto c_map = boost::get(boost::edge_capacity, G);
  // auto rc_map = boost::get(boost::edge_residual_capacity, G);
  const vertex_desc v_source = boost::add_vertex(G);
  const vertex_desc v_sink = boost::add_vertex(G);

  std::vector<int> b(n);
  long sum_b = 0;
  for(int i = 0; i < n; i++) {
    std::cin >> b[i];
    if(b[i] > 0) {
      sum_b += b[i];
      adder.add_edge(v_source, i, b[i]);
    }
    else {
      adder.add_edge(i, v_sink, -b[i]);
    }
  }

  for(int k = 0; k < m; k++) {
    int i, j, c;
    std::cin >> i >> j >> c;
    adder.add_edge(i, j, c);
  }

  long flow = boost::push_relabel_max_flow(G, v_source, v_sink);
  // if(sum_dfs > 0) {
  if(flow < sum_b) {
    std::cout << "yes" << std::endl;
    return;
  }
  std::cout << "no" << std::endl;
  // std::cout << flow << " " << sum_b << " " << sum_abs_b << "\n";
  // std::cerr << std::endl;
  // // Retrieve the capacity map and reverse capacity map
  // const auto c_map = boost::get(boost::edge_capacity, G);
  // const auto rc_map = boost::get(boost::edge_residual_capacity, G);

  // // Iterate over all the edges to print the flow along them
  // auto edge_iters = boost::edges(G);
  // for (auto edge_it = edge_iters.first; edge_it != edge_iters.second; ++edge_it) {
  //   const edge_desc edge = *edge_it;
  //   const long flow_through_edge = c_map[edge] - rc_map[edge];
  //   std::cerr << "edge from " << boost::source(edge, G) << " to " << boost::target(edge, G)
  //             << " cmap " << c_map[edge] << " rcmap " << rc_map[edge] << " and " << flow_through_edge
  //             << " units of flow (negative for reverse direction). \n";
  // }
}

int main() {
  std::ios_base::sync_with_stdio(false);
  std::size_t t;
  for (std::cin >> t; t > 0; --t) testcase();
  return 0;
}


// License: The Unlicense (https://unlicense.org)
#pragma once

// TODO(tybl): Replace Eigen with liblynel
#include <Eigen/Dense>

#include <cmath>
#include <limits>

namespace tybl::stats {

template <typename Type>
class distribution {
  std::size_t m_count     { 0UL };
  double m_mean           { 0.0 };
  double m_sum_of_squares { 0.0 };
  Type m_maximum          { std::numeric_limits<Type>::lowest() };
  Type m_minimum          { std::numeric_limits<Type>::max() };
public:

  // TODO(tybl): Unintuitive use of operator+=, replace with regular function
  constexpr auto operator+=(Type x) -> distribution& {
    m_count += 1;
    double delta = static_cast<double>(x) - m_mean;
    m_mean += delta / static_cast<double>(m_count);
    m_sum_of_squares += delta * (static_cast<double>(x) - m_mean);
    m_maximum = std::max(x, m_maximum);
    m_minimum = std::min(x, m_minimum);
    return *this;
  }

  [[nodiscard]] constexpr auto count() const -> std::size_t {
    return m_count;
  }

  [[nodiscard]] constexpr auto maximum() const -> Type {
    return m_maximum;
  }

  [[nodiscard]] constexpr auto minimum() const -> Type {
    return m_minimum;
  }

  [[nodiscard]] constexpr auto mean() const -> double {
    return m_mean;
  }

  [[nodiscard]] auto pop_stddev() const -> double {
    return std::sqrt(pop_var());
  }

  [[nodiscard]] auto samp_stddev() const -> double {
    return std::sqrt(samp_var());
  }

  [[nodiscard]] auto pop_var() const -> double {
    return (0 < m_count) ? (m_sum_of_squares / static_cast<double>(m_count))
                         : std::numeric_limits<double>::quiet_NaN();
  }

  [[nodiscard]] auto samp_var() const -> double {
    return (1 < m_count) ? (m_sum_of_squares / static_cast<double>(m_count - 1))
                         : std::numeric_limits<double>::quiet_NaN();
  }

}; // class distribution

// TODO(tybl): Class name is not descriptive
template <size_t N>
class dist {
  static_assert(0 < N, "");

  // types:
  using value_type = double;
  using vector_type = Eigen::Matrix<value_type, N, 1>;
  using matrix_type = Eigen::Matrix<value_type, N, N>;
  using size_type = std::size_t;

  // member variables:
  size_type m_count { 0UL };
  vector_type m_means { vector_type::Zero() };
  matrix_type m_covars { matrix_type::Zero() };

public:

  constexpr auto insert(Eigen::Matrix<double, N, 1> const& xs) -> void {
    m_count += 1;
    vector_type deltas = xs - m_means;
    m_means += deltas / m_count;
    matrix_type covar_deltas = (xs - m_means) * deltas.transpose() - m_covars;
    m_covars += covar_deltas / m_count;
  }

  constexpr auto means() const -> vector_type const& {
    return m_means;
  }

  constexpr auto covariance() const -> matrix_type const& {
    return m_covars;
  }

}; // class dist

} // namespace tybl::stats


#define ARMA_DONT_USE_WRAPPER

#include <armadillo>
#include <cstdio>
#include <mpi.h>
#include <string>

#define ALLREDUCE(X) MPI_Allreduce(MPI_IN_PLACE, X.memptr(), X.n_elem, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD)

class Shaq
{
  public:
    Shaq()
    {
      MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    };

    void ranshaq(int seed, arma::uword m_local, arma::uword n)
    {
      int size;

      MPI_Comm_size(MPI_COMM_WORLD, &size);

      Data.resize(m_local, n);
      arma::arma_rng::set_seed(seed + rank);
      Data.randn();

      nrows = m_local*size;
      ncols = n;
    };

    void center()
    {
      arma::rowvec colmeans = sum(Data, 0);
      ALLREDUCE(colmeans);
      colmeans /= (double) nrows;
      Data.each_row() -= colmeans;
    };

    arma::mat Data;
    arma::uword nrows;
    arma::uword ncols;
    int rank;
};

// shows the first and last singular values, computed via covariance matrix
static arma::vec princomp(Shaq &X)
{
  X.center();
  arma::mat Cov = X.Data.t() * X.Data;
  Cov /= X.nrows - 1;
  ALLREDUCE(Cov);

  arma::vec d;
  eig_sym(d, Cov);

  return sqrt(d);
}

static void get_dims(int argc, char **argv, arma::uword *m_local, arma::uword *n)
{

  if (argc != 3)
  {
    int rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    if (rank == 0)
      fprintf(stderr, "ERROR incorrect number of arguments: usage is 'mpirun -np n princomp num_local_rows num_global_cols\n");

    exit(-1);
  }

  *m_local = (arma::uword) std::stoi(argv[1]);
  *n = (arma::uword) std::stoi(argv[2]);
}

int main(int argc, char **argv)
{
  arma::uword m_local, n;
  MPI_Init(NULL, NULL);

  get_dims(argc, argv, &m_local, &n);

  Shaq X;
  X.ranshaq(1234, m_local, n);

  arma::vec d = princomp(X);

  if (X.rank == 0)
    printf("%f %f\n", d[n-1], d[0]);

  MPI_Finalize();
  return 0;
}


//
//  minhash.cpp
//
//
//  Created by Roberto Perdisci on 1/7/17.
//  Copyright © 2017 Roberto Perdisci. All rights reserved.
//

#include "minhash.hpp"

#include <iostream>
#include <cstdint>
#include <cassert>
#include <boost/functional/hash.hpp>
#include "xxHash/xxhash.h"

namespace rp {

using std::string;
using std::vector;
using std::set;

MinHash::MinHash(const unsigned sig_len, const unsigned seed) {
    this->sig_len = sig_len;
    this->seed = seed;
    rand_eng.seed(seed);
}

vector<uint32_t> MinHash::random_uint32_universal_hash(const uint64_t x) {

    assert(x > 0 && x < UINT64_MAX);

    static const unsigned w = sizeof(uint64_t)*8;
    static const unsigned M = sizeof(uint32_t)*8;

    vector<uint32_t> hv;
    hv.push_back(static_cast<uint32_t>(x));

    for(int i=0; i<sig_len; i++) {
        if(a.size()<sig_len) {
            a.push_back(urandom_64(rand_eng));
            b.push_back(urandom_32(rand_eng));
        }

        uint32_t h = static_cast<uint32_t>((a[i]*x+b[i]) >> (w-M));
        hv.push_back(h);
    }

    return hv;
}

vector<uint32_t> MinHash::minhash_universal(const std::map<string, bool>& s_set) {

    vector<uint32_t> mh_sig(sig_len,UINT32_MAX);
    for(auto s : s_set) {
        uint64_t xxh = static_cast<uint64_t>(XXH64(s.first.data(), s.first.size(),seed));
        vector<uint32_t> rh = random_uint32_universal_hash(xxh);
        for(int i=0; i<sig_len; i++) {
            if(rh[i] < mh_sig[i])
                mh_sig[i] = rh[i];
        }
    }

    return std::move(mh_sig);
}

uint32_t MinHash::shash32(const string s, const unsigned seed) {
    return static_cast<uint32_t>(XXH32(s.data(),s.size(),seed));
}

vector<uint32_t> MinHash::minhash_xor(const set<string>& s_set) {

    static boost::hash<std::string> boost_hash_fn;

    static std::default_random_engine rand_eng(seed);
    static std::uniform_int_distribution<uint32_t> srandom;

    static vector<uint32_t> rn;

    vector<uint32_t> mh_sig(sig_len,std::numeric_limits<uint32_t>::max());
    for(string s : s_set) {
        std::size_t boosth = boost_hash_fn(s);
        for(int i=0; i<sig_len; i++) {
            if(rn.size()<sig_len)
                rn.push_back(srandom(rand_eng));
            uint32_t h = boosth^rn[i];
            if(h < mh_sig[i]) {
                mh_sig[i] = h;
            }
        }
    }

    return mh_sig;
}

} // namespace rp


#pragma once
#include <iostream>
#include <vector>
#include <armadillo>
#include <thread>

namespace arm_simu{

	template<typename T>
	struct KahanSumParam{
		T sum=0;
		T c=0;
		T y=0;
		T t=0;
	};

	template<typename T>
	std::ostream& operator<<(std::ostream& cout,KahanSumParam<T> param){
		cout<<"KahanSumParam[sum:"<<param.sum<<", c:"<<param.c<<", y:"<<param.y<<", t:"<<param.t<<"]";
		return cout;
	}

	class KahanSum{

		private:
			static void ThreadWorkerSummation(double* input,const int size,arma::vec* out,const int index,bool use_kahansum=true){
				if (use_kahansum){
					double sum=0;
					double c=0;
					for (unsigned long long i=0;i<size;i++){
						double y=input[i]-c;
						double t=sum+y;
						c=(t-sum)-y;
						sum=t;
					}
					(*out)[index]=sum;
				}else{
					double sum=0;
					for (int i=0;i<size;i++){
						sum+=input[i];
					}
					(*out)[index]=sum;
				}
			}

		public:
			//full Summation use_kahansum=true(for kahansummation) and false for normal summation @param arma::vec
			template<typename T>
			static T Summation(arma::vec input,bool use_kahansum=true){
				if (use_kahansum){
					T sum=0;
					T c=0;
					for (unsigned long long i=0;i<input.size();i++){
						T y=input[i]-c;
						T t=sum+y;
						c=(t-sum)-y;
						sum=t;
					}
					return sum;
				}else{
					T sum=0;
					for (int i=0;i<input.size();i++){
						sum+=input[i];
					}
					return sum;
				}
				return 0;
			}

			static void Summation(arma::mat input,arma::vec* result,bool use_kahansum=true){
				arma::uword ncols=input.n_cols;
				arma::uword nrows=input.n_rows;

				if (ncols<=2000){

					std::thread* thread_list=new std::thread[ncols];
					for (int i=0;i<ncols;i++){
						double* ptr=input.colptr(i);
						thread_list[i]=std::thread(ThreadWorkerSummation,input.colptr(i),nrows,result,i,use_kahansum);
					}

					for (int i=0;i<ncols;i++){
						thread_list[i].join();
					}

					delete[] thread_list;
				}else{
					throw std::runtime_error("columns must be in range of [0,2000]");
				}
			}

			//full Summation use_kahansum=true(for kahansummation) and false for normal summation @param std::vec
			template<typename T>
			static T Summation(std::vector<T> input,bool use_kahansum=true){
				if (use_kahansum){
					T sum=0;
					T c=0;
					for (unsigned long long i=0;i<input.size();i++){
						T y=input[i]-c;
						T t=sum+y;
						c=(t-sum)-y;
						sum=t;
					}
					return sum;
				}else{
					T sum=0;
					for (int i=0;i<input.size();i++){
						sum+=input[i];
					}
					return sum;
				}
				return 0;
			}
			//Step kahansummation
			template<typename T>
			static T StepSummation(T i_input,KahanSumParam<T>& param){
				param.y=i_input-param.c;
				param.t=param.sum+param.y;
				param.c=(param.t-param.sum)-param.y;
				param.sum=param.t;
				return param.sum;
			}

			//Reseting to recompute stepsummation
			template<typename T>
			static void ResetStepSummation(KahanSumParam<T>& param){
				param.sum=0;
				param.y=0;
				param.c=0;
				param.t=0;
			}

	};
}


/*******************************************************************************
 * Abstract domain described in Section 4 from the paper "An Abstract
 * Domain of Uninterpreted Functions" by Gange, Navas, Schachte,
 * Sondergaard, and Stuckey published in VMCAI'16.
 *
 * Each program variable is mapped to a syntactic term (aka
 * uninterpreted function). The join is antiunification and the meet
 * is a pseudo-meet based on the classical congruence closure
 * algorithm. The domain is suitable to infer equalities between
 * variables.
 ******************************************************************************/

#pragma once

#include <crab/domains/abstract_domain.hpp>
#include <crab/domains/abstract_domain_specialized_traits.hpp>
#include <crab/domains/backward_assign_operations.hpp>
#include <crab/domains/term/term_expr.hpp>
#include <crab/domains/term/term_operators.hpp>
#include <crab/support/debug.hpp>
#include <crab/support/stats.hpp>

#include <algorithm>
#include <map>
#include <set>
#include <utility>
#include <vector>

#include <boost/container/flat_map.hpp>
#include <boost/container/flat_set.hpp>
#include <boost/optional.hpp>

namespace crab {
namespace domains {

// TODO: factorize code. uf_domain and term_domain share a lot of
// code.
template <typename Number, typename VariableName>
class uf_domain final
    : public abstract_domain_api<uf_domain<Number, VariableName>> {

  using uf_domain_t = uf_domain<Number, VariableName>;
  using abstract_domain_t = abstract_domain_api<uf_domain_t>;

public:
  using typename abstract_domain_t::disjunctive_linear_constraint_system_t;
  using typename abstract_domain_t::interval_t;
  using typename abstract_domain_t::linear_constraint_system_t;
  using typename abstract_domain_t::linear_constraint_t;
  using typename abstract_domain_t::linear_expression_t;
  using typename abstract_domain_t::reference_constraint_t;
  using typename abstract_domain_t::variable_or_constant_t;
  using typename abstract_domain_t::variable_or_constant_vector_t;
  using typename abstract_domain_t::variable_t;
  using typename abstract_domain_t::variable_vector_t;
  using number_t = Number;
  using varname_t = VariableName;

private:
  using ttbl_t = term::term_table<number_t, term::term_operator_t>;
  using term_id_t = typename ttbl_t::term_id_t;
  using term_t = typename ttbl_t::term_t;
  using const_term_t = typename ttbl_t::const_term_t;
  using var_term_t = typename ttbl_t::var_term_t;
  using ftor_term_t = typename ttbl_t::ftor_term_t;

  using var_map_t = boost::container::flat_map<variable_t, term_id_t>;
  using var_set_t = std::set<variable_t>;
  using rev_var_map_t = boost::container::flat_map<term_id_t, var_set_t>;
  using linterm_t = typename linear_expression_t::component_t;

  bool m_is_bottom;
  ttbl_t m_ttbl;
  var_map_t m_var_map;
  rev_var_map_t m_rev_var_map;

  uf_domain(bool is_top) : m_is_bottom(!is_top) {}

  uf_domain(ttbl_t &&tbl, var_map_t &&vm, rev_var_map_t &&rvm)
      : m_is_bottom(false), m_ttbl(std::move(tbl)), m_var_map(std::move(vm)),
        m_rev_var_map(std::move(rvm)) {
    check_terms(__LINE__);
  }

  void check_terms(int line) const {
    CRAB_LOG(
        "uf-check-terms",
        for (auto const &p
             : m_var_map) {
          if (!(p.second < m_ttbl.size())) {
            CRAB_ERROR("term_equiv.hpp at line=", line, ": ",
                       "term id is not the table term");
          }
        }

        for (auto kv
             : m_rev_var_map) {
          for (auto v : kv.second) {
            auto it = m_var_map.find(v);
            if (it->second != kv.first) {
              CRAB_ERROR("term_equiv.hpp at line=", line, ": ", v,
                         " is mapped to t", it->second,
                         " but the reverse map says that should be t",
                         kv.first);
            }
          }
        });
  }

  void deref(term_id_t t) {
    std::vector<term_id_t> forgotten /*unused*/;
    m_ttbl.deref(t, forgotten);
  }

  /* Begin manipulate the reverse variable map */
  void add_rev_var_map(rev_var_map_t &rvmap, term_id_t t, variable_t v) const {
    auto it = rvmap.find(t);
    if (it != rvmap.end()) {
      it->second.insert(v);
    } else {
      var_set_t varset;
      varset.insert(v);
      rvmap.insert(std::make_pair(t, varset));
    }
  }

  void remove_rev_var_map(term_id_t t, const variable_t &v) {
    auto it = m_rev_var_map.find(t);
    if (it != m_rev_var_map.end()) {
      it->second.erase(v);
      if (it->second.empty()) {
        m_rev_var_map.erase(it);
      }
    }
  }
  /* End manipulate the reverse variable map */

  const term_t &get_term(term_id_t t) const {
    const term_t *ptr_t = m_ttbl.get_term_ptr(t);
    assert(ptr_t);
    return *ptr_t;
  }

  void rebind_var(const variable_t &x, term_id_t tx) {
    m_ttbl.add_ref(tx);

    auto it(m_var_map.find(x));
    if (it != m_var_map.end()) {
      remove_rev_var_map((*it).second, x);
      deref((*it).second);
      m_var_map.erase(it);
    }
    m_var_map.insert(std::make_pair(x, tx));
    add_rev_var_map(m_rev_var_map, tx, x);
  }

  term_id_t term_of_const(const number_t &n) {
    boost::optional<term_id_t> opt_tn(m_ttbl.find_const(n));
    if (opt_tn) {
      return *opt_tn;
    } else {
      return m_ttbl.make_const(n);
    }
  }

  term_id_t term_of_var(variable_t v, var_map_t &var_map,
                        rev_var_map_t &rvar_map, ttbl_t &ttbl) {
    auto it(var_map.find(v));
    if (it != var_map.end()) {
      // assert((*it).first == v);
      assert(ttbl.size() > (*it).second);
      return (*it).second;
    } else {
      // Allocate a fresh term
      term_id_t id(ttbl.fresh_var());
      var_map[v] = id;
      add_rev_var_map(rvar_map, id, v);
      ttbl.add_ref(id);
      return id;
    }
  }

  term_id_t term_of_var(variable_t v) {
    return term_of_var(v, m_var_map, m_rev_var_map, m_ttbl);
  }

  term_id_t term_of_linterm(linterm_t term) {
    if (term.first == 1) {
      return term_of_var(term.second);
    } else {
      return build_term(term::conv2termop(OP_MULTIPLICATION),
                        term_of_const(term.first), term_of_var(term.second));
    }
  }

  term_id_t build_term(term::term_operator_t op, term_id_t tx) {
    std::vector<term_id_t> ids = {tx};
    return build_term(op, ids);
  }

  term_id_t build_term(term::term_operator_t op, term_id_t tx, term_id_t ty) {
    std::vector<term_id_t> ids = {tx,ty};
    return build_term(op, ids);
  }

  term_id_t build_term(term::term_operator_t op, const std::vector<term_id_t> &ids) {
    boost::optional<term_id_t> eopt(m_ttbl.find_ftor(op, ids));
    if (eopt) {
      return *eopt;
    } else {
      term_id_t tx = m_ttbl.apply_ftor(op, ids);
      return tx;
    }
  }

  term_id_t build_linexpr(const linear_expression_t &e) {
    number_t cst = e.constant();
    typename linear_expression_t::const_iterator it(e.begin());
    if (it == e.end()) {
      return term_of_const(cst);
    }

    term_id_t t;
    if (cst == 0) {
      t = term_of_linterm(*it);
      ++it;
    } else {
      t = term_of_const(cst);
    }
    for (; it != e.end(); ++it) {
      t = build_term(term::conv2termop(OP_ADDITION), t, term_of_linterm(*it));
    }

    return t;
  }

  boost::optional<std::pair<variable_t, variable_t>>
  get_eq_or_diseq(linear_constraint_t cst) {
    if (cst.is_equality() || cst.is_disequation()) {
      if (cst.size() == 2 && cst.constant() == 0) {
        auto it = cst.begin();
        auto nx = it->first;
        auto vx = it->second;
        ++it;
        assert(it != cst.end());
        auto ny = it->first;
        auto vy = it->second;
        if (nx == (ny * -1)) {
          return std::make_pair(vx, vy);
        }
      }
    }
    return boost::optional<std::pair<variable_t, variable_t>>();
  }

  // helper for pseudo-meet: choose one non-var term from the
  // equivalence class associated with t.
  template <typename Range>
  boost::optional<term_id_t> choose_non_var(ttbl_t &ttbl,
                                            const Range &terms) const {
    std::vector<term_id_t> non_var_terms(terms.size());
    auto it = std::copy_if(terms.begin(), terms.end(), non_var_terms.begin(),
                           [&ttbl](term_id_t t) {
                             term_t *t_ptr = ttbl.get_term_ptr(t);
                             return (t_ptr && t_ptr->kind() == term::TERM_APP);
                           });
    non_var_terms.resize(std::distance(non_var_terms.begin(), it));
    if (non_var_terms.empty()) {
      return boost::optional<term_id_t>();
    } else {
      // TODO: the heuristics as described in the VMCAI'16 paper
      // that chooses the one that has more references each class.
      return *(non_var_terms.begin());
    }
  }

  // helper for pseudo-meet
  term_id_t build_dag_term(ttbl_t &ttbl, int t,
                           term::congruence_closure_solver<ttbl_t> &solver,
                           ttbl_t &out_ttbl, std::vector<int> &stack,
                           std::map<int, term_id_t> &cache) const {

    // already processed
    auto it = cache.find(t);
    if (it != cache.end()) {
      CRAB_LOG("uf-meet", crab::outs() << "build_dag_term. Found in cache: ";
               crab::outs() << "t" << t << " --> "
                            << "t" << it->second << "\n";);
      return it->second;
    }

    // break the cycle with a fresh variable
    if (std::find(stack.begin(), stack.end(), t) != stack.end()) {
      term_id_t v = out_ttbl.fresh_var();
      CRAB_LOG("uf-meet", crab::outs() << "build_dag_term. Detected cycle: ";
               crab::outs() << "t" << t << " --> "
                            << "t" << v << "\n";);
      return v;
    }

    stack.push_back(t);
    auto membs = solver.get_members(t);
    boost::optional<term_id_t> f = choose_non_var(ttbl, membs);

    if (!f) {
      // no concrete definition exists return a fresh variable
      term_id_t v = out_ttbl.fresh_var();
      auto res = cache.insert(std::make_pair(t, v));
      stack.pop_back();
      CRAB_LOG("uf-meet", crab::outs()
                              << "build_dag_term. No concrete definition: ";
               crab::outs() << "t" << t << " --> "
                            << "t" << (res.first)->second << "\n";);
      return (res.first)->second;
    } else {
      // traverse recursively the term
      term_t *f_ptr = ttbl.get_term_ptr(*f);
      CRAB_LOG("uf-meet",
               crab::outs()
                   << "build_dag_term. Traversing recursively the term "
                   << "t" << *f << ":";
               crab::outs() << *f_ptr << "\n";);
      const std::vector<term_id_t> &args(term::term_args(f_ptr));
      std::vector<term_id_t> res_args;
      res_args.reserve(args.size());
      for (term_id_t c : args) {
        res_args.push_back(build_dag_term(ttbl, solver.get_class(c), solver,
                                          out_ttbl, stack, cache));
      }
      auto res = cache.insert(std::make_pair(
          t, out_ttbl.apply_ftor(term::term_ftor(f_ptr), res_args)));
      stack.pop_back();
      CRAB_LOG("uf-meet", crab::outs()
                              << "build_dag_term. Finished recursive case: ";
               crab::outs() << "t" << t << " --> "
                            << "t" << (res.first)->second << "\n";);
      return (res.first)->second;
    }
  }

  void print_term(const term_t &t, crab_os &o) const {
    if (t.kind() == term::TERM_CONST) {
      const const_term_t *ct = static_cast<const const_term_t *>(&t);
      o << ct->val;
    } else if (t.kind() == term::TERM_VAR) {
      const var_term_t *vt = static_cast<const var_term_t *>(&t);
      o << "$VAR_" << vt->var;
    } else {
      assert(t.kind() == term::TERM_APP && "term should be a function");
      const ftor_term_t *ft = static_cast<const ftor_term_t *>(&t);
      o << ft->ftor << "(";
      for (unsigned i = 0, sz = ft->args.size(); i < sz;) {
        print_term(get_term(ft->args[i]), o);
        ++i;
        if (i < sz) {
          o << ",";
        }
      }
      o << ")";
    }
  }

public:
  uf_domain_t make_top() const override { return uf_domain_t(true); }

  uf_domain_t make_bottom() const override { return uf_domain_t(false); }

  void set_to_top() override {
    uf_domain abs(true);
    std::swap(*this, abs);
  }

  void set_to_bottom() override {
    uf_domain abs(false);
    std::swap(*this, abs);
  }

  uf_domain() : m_is_bottom(false) {}

  uf_domain(const uf_domain_t &o)
      : m_is_bottom(o.m_is_bottom), m_ttbl(o.m_ttbl), m_var_map(o.m_var_map),
        m_rev_var_map(o.m_rev_var_map) {
    crab::CrabStats::count(domain_name() + ".count.copy");
    crab::ScopedCrabStats __st__(domain_name() + ".copy");
    check_terms(__LINE__);
  }

  uf_domain_t &operator=(const uf_domain_t &o) {
    crab::CrabStats::count(domain_name() + ".count.copy");
    crab::ScopedCrabStats __st__(domain_name() + ".copy");

    o.check_terms(__LINE__);
    if (this != &o) {
      m_is_bottom = o.m_is_bottom;
      m_ttbl = o.m_ttbl;
      m_var_map = o.m_var_map;
      m_rev_var_map = o.m_rev_var_map;
    }
    check_terms(__LINE__);
    return *this;
  }

  bool is_bottom() const override { return m_is_bottom; }

  bool is_top() const override { return !m_var_map.size() && !is_bottom(); }

  // Lattice operations
  bool operator<=(const uf_domain_t &o) const override {
    crab::CrabStats::count(domain_name() + ".count.leq");
    crab::ScopedCrabStats __st__(domain_name() + ".leq");

    if (is_bottom()) {
      return true;
    } else if (o.is_bottom()) {
      return false;
    } else {
      // FIXME: avoid this copy
      uf_domain_t left(*this);
      uf_domain_t right(o);
      typename ttbl_t::term_map_t gen_map /*unused*/;

      // Build up the mapping of right onto left, variable by variable.
      // Assumption: the set of variables in left & right are common.
      for (auto p : left.m_var_map) {
        if (!left.m_ttbl.map_leq(right.m_ttbl, left.term_of_var(p.first),
                                 right.term_of_var(p.first), gen_map))
          return false;
      }
      return true;
    }
  }

  void operator|=(const uf_domain_t &o) override {
    crab::CrabStats::count(domain_name() + ".count.join");
    crab::ScopedCrabStats __st__(domain_name() + ".join");

    if (is_bottom() || o.is_top()) {
      *this = o;
    } else if (o.is_bottom() || is_top()) {
      return;
    } else {
      uf_domain_t right(o);
      ttbl_t out_tbl;
      var_map_t out_vmap;
      rev_var_map_t out_rvmap;
      typename ttbl_t::gener_map_t gener_map /*unused*/;

      for (auto p : m_var_map) {
        const variable_t &v = p.first;
        term_id_t tx = p.second;
        term_id_t ty(right.term_of_var(v));
        term_id_t tz =
            m_ttbl.generalize(right.m_ttbl, tx, ty, out_tbl, gener_map);
        assert(tz < out_tbl.size());
        out_vmap[v] = tz;
        add_rev_var_map(out_rvmap, tz, v);
      }

      for (auto p : out_vmap) {
        out_tbl.add_ref(p.second);
      }

      m_is_bottom = false;
      std::swap(m_ttbl, out_tbl);
      std::swap(m_var_map, out_vmap);
      std::swap(m_rev_var_map, out_rvmap);
    }
  }

  uf_domain_t operator|(const uf_domain_t &o) const override {
    crab::CrabStats::count(domain_name() + ".count.join");
    crab::ScopedCrabStats __st__(domain_name() + ".join");

    if (is_bottom() || o.is_top()) {
      return o;
    } else if (o.is_bottom() || is_top()) {
      return *this;
    } else {
      // FIXME: avoid this copy
      uf_domain_t left(*this);
      uf_domain_t right(o);
      ttbl_t out_tbl;
      typename ttbl_t::gener_map_t gener_map /*unused*/;
      var_map_t out_vmap;
      rev_var_map_t out_rvmap;

      // For each program variable in state, compute a generalization.
      for (auto p : left.m_var_map) {
        const variable_t &v = p.first;
        term_id_t tx = p.second;
        term_id_t ty = right.term_of_var(v);
        term_id_t tz =
            left.m_ttbl.generalize(right.m_ttbl, tx, ty, out_tbl, gener_map);
        assert(tz < out_tbl.size());
        out_vmap[v] = tz;
        add_rev_var_map(out_rvmap, tz, v);
      }

      for (auto p : out_vmap) {
        out_tbl.add_ref(p.second);
      }

      uf_domain_t res(std::move(out_tbl), std::move(out_vmap),
                      std::move(out_rvmap));

      CRAB_LOG("uf", crab::outs() << "============ JOIN ==================";
               crab::outs() << *this << "\n----------------";
               crab::outs() << o << "\n----------------";
               crab::outs() << res << "\n================"
                            << "\n");

      return res;
    }
  }

  uf_domain_t operator||(const uf_domain_t &other) const override {
    return *this | other;
  }

  uf_domain_t widening_thresholds(
      const uf_domain_t &other,
      const iterators::thresholds<number_t> &ts) const override {
    return *this | other;
  }

  // Meet
  uf_domain_t operator&(const uf_domain_t &o) const override {
    crab::CrabStats::count(domain_name() + ".count.meet");
    crab::ScopedCrabStats __st__(domain_name() + ".meet");

    if (is_bottom() || o.is_top()) {
      return *this;
    } else if (is_top() || o.is_bottom()) {
      return o;
    } else {
      ttbl_t out_ttbl(m_ttbl);
      std::map<term_id_t, term_id_t> copy_map;
      std::vector<int> stack;
      std::map<int, term_id_t> cache;
      var_map_t out_vmap;
      rev_var_map_t out_rvmap;

      // bring all terms to one ttbl
      for (auto p : o.m_var_map) {
        term_id_t tx = p.second;
        out_ttbl.copy_term(o.m_ttbl, tx, copy_map);
      }

      // build unifications between terms from this and o
      std::vector<std::pair<term_id_t, term_id_t>> eqs;
      for (auto p : m_var_map) {
        variable_t v(p.first);
        auto it = o.m_var_map.find(v);
        if (it != o.m_var_map.end()) {
          term_id_t tx = p.second;
          eqs.push_back(std::make_pair(tx, copy_map[it->second]));
        }
      }

      // compute equivalence classes
      term::congruence_closure_solver<ttbl_t> solver(&out_ttbl);
      solver.run(eqs);

      // new map from variable to an acyclic term
      for (auto p : m_var_map) {
        const variable_t &v = p.first;
        term_id_t t_old = p.second;
        term_id_t t_new = build_dag_term(out_ttbl, solver.get_class(t_old),
                                         solver, out_ttbl, stack, cache);
        out_vmap[v] = t_new;
        add_rev_var_map(out_rvmap, t_new, v);
      }
      for (auto p : o.m_var_map) {
        variable_t v(p.first);
        if (out_vmap.find(v) != out_vmap.end())
          continue;
        term_id_t t_old(copy_map[p.second]);
        term_id_t t_new = build_dag_term(out_ttbl, solver.get_class(t_old),
                                         solver, out_ttbl, stack, cache);
        out_vmap[v] = t_new;
        add_rev_var_map(out_rvmap, t_new, v);
      }

      for (auto p : out_vmap) {
        out_ttbl.add_ref(p.second);
      }

      uf_domain_t res(std::move(out_ttbl), std::move(out_vmap),
                      std::move(out_rvmap));

      CRAB_LOG("uf", crab::outs() << "============ MEET ==================";
               crab::outs() << *this << "\n----------------";
               crab::outs() << o << "\n----------------";
               crab::outs() << res << "\n================"
                            << "\n");
      return res;
    }
  }

  uf_domain_t operator&&(const uf_domain_t &o) const override {
    return *this & o;
  }

  // Remove a variable from the scope
  void operator-=(const variable_t &v) override {
    crab::CrabStats::count(domain_name() + ".count.forget");
    crab::ScopedCrabStats __st__(domain_name() + ".forget");

    auto it(m_var_map.find(v));
    if (it != m_var_map.end()) {
      term_id_t t = (*it).second;
      m_var_map.erase(it);
      remove_rev_var_map(t, v);
      deref(t);
    }
    CRAB_LOG("uf", crab::outs()
                       << "After removing " << v << ": " << *this << "\n";);
  }

  void assign(const variable_t &x, const linear_expression_t &e) override {
    crab::CrabStats::count(domain_name() + ".count.assign");
    crab::ScopedCrabStats __st__(domain_name() + ".assign");

    if (!is_bottom()) {
      term_id_t tx(build_linexpr(e));
      rebind_var(x, tx);
      check_terms(__LINE__);
      CRAB_LOG("uf", crab::outs() << "*** Assign " << x << ":=" << e << ":"
                                  << *this << "\n");
    }
  }

  // Apply operations to variables.

  // x = y op z
  void apply(arith_operation_t op, const variable_t &x, const variable_t &y,
             const variable_t &z) override {
    crab::CrabStats::count(domain_name() + ".count.apply");
    crab::ScopedCrabStats __st__(domain_name() + ".apply");
    check_terms(__LINE__);

    if (!is_bottom()) {
      term_id_t tx(
          build_term(term::conv2termop(op), term_of_var(y), term_of_var(z)));
      rebind_var(x, tx);
      check_terms(__LINE__);
      CRAB_LOG("uf", crab::outs() << "*** " << x << ":=" << y << " " << op
                                  << " " << z << ":" << *this << "\n");
    }
  }

  // x = y op k
  void apply(arith_operation_t op, const variable_t &x, const variable_t &y,
             number_t k) override {
    crab::CrabStats::count(domain_name() + ".count.apply");
    crab::ScopedCrabStats __st__(domain_name() + ".apply");

    if (!is_bottom()) {
      term_id_t tx(
          build_term(term::conv2termop(op), term_of_var(y), term_of_const(k)));
      rebind_var(x, tx);
      check_terms(__LINE__);
      CRAB_LOG("uf", crab::outs() << "*** " << x << ":=" << y << " " << op
                                  << " " << k << ":" << *this << "\n");
    }
  }

  void backward_assign(const variable_t &x, const linear_expression_t &e,
                       const uf_domain_t &inv) override {
    crab::CrabStats::count(domain_name() + ".count.backward_assign");
    crab::ScopedCrabStats __st__(domain_name() + ".backward_assign");
    if (!is_bottom()) {
      CRAB_WARN("backward_assign not implemented by ", domain_name());
    }
  }

  void backward_apply(arith_operation_t op, const variable_t &x,
                      const variable_t &y, number_t z,
                      const uf_domain_t &inv) override {
    crab::CrabStats::count(domain_name() + ".count.backward_apply");
    crab::ScopedCrabStats __st__(domain_name() + ".backward_apply");
    if (!is_bottom()) {
      CRAB_WARN("backward_apply not implemented by ", domain_name());
    }
  }

  void backward_apply(arith_operation_t op, const variable_t &x,
                      const variable_t &y, const variable_t &z,
                      const uf_domain_t &inv) override {
    crab::CrabStats::count(domain_name() + ".count.backward_apply");
    crab::ScopedCrabStats __st__(domain_name() + ".backward_apply");
    if (!is_bottom()) {
      CRAB_WARN("backward_apply not implemented by ", domain_name());
    }
  }

  void operator+=(const linear_constraint_t &cst) {
    crab::CrabStats::count(domain_name() + ".count.add_constraints");
    crab::ScopedCrabStats __st__(domain_name() + ".add_constraints");

    CRAB_LOG("uf", crab::outs()
                       << "*** Before assume " << cst << ":" << *this << "\n");

    if (is_bottom()) {
      return;
    }

    using pair_var_t = std::pair<variable_t, variable_t>;

    if (boost::optional<pair_var_t> eq = get_eq_or_diseq(cst)) {
      term_id_t tx(term_of_var((*eq).first));
      term_id_t ty(term_of_var((*eq).second));
      if (cst.is_disequation()) {
        if (tx == ty) {
          set_to_bottom();
          CRAB_LOG("uf", crab::outs() << "*** After assume " << cst << ":"
                                      << *this << "\n");
          return;
        }
      } else {
        // not bother if they are already equal
        if (tx == ty) {
          return;
        }

        std::vector<int> stack;
        std::map<int, term_id_t> cache;
        // congruence closure to compute equivalence classes
        term::congruence_closure_solver<ttbl_t> solver(&m_ttbl);
        std::vector<std::pair<term_id_t, term_id_t>> eqs = {{tx, ty}};
        solver.run(eqs);

        // new map from variable to an acyclic term
        for (auto p : m_var_map) {
          const variable_t &v = p.first;
          term_id_t t_old(term_of_var(v));
          term_id_t t_new = build_dag_term(m_ttbl, solver.get_class(t_old),
                                           solver, m_ttbl, stack, cache);
          rebind_var(v, t_new);
        }
      }
    }

    CRAB_LOG("uf", crab::outs()
                       << "*** After assume " << cst << ":" << *this << "\n");
  }

  void operator+=(const linear_constraint_system_t &csts) override {
    for (auto cst : csts) {
      this->operator+=(cst);
    }
  }

  interval_t operator[](const variable_t &x) override {
    crab::CrabStats::count(domain_name() + ".count.to_intervals");
    crab::ScopedCrabStats __st__(domain_name() + ".to_intervals");
    if (is_bottom()) {
      return interval_t::bottom();
    } else {
      return interval_t::top();
    }
  }

  void apply(int_conv_operation_t /*op*/, const variable_t &dst,
             const variable_t &src) override {
    // since reasoning about infinite precision we simply assign and
    // ignore the widths.
    assign(dst, src);
  }

  void apply(bitwise_operation_t op, const variable_t &x, const variable_t &y,
             const variable_t &z) override {
    crab::CrabStats::count(domain_name() + ".count.apply");
    crab::ScopedCrabStats __st__(domain_name() + ".apply");

    if (!is_bottom()) {
      term_id_t tx(
          build_term(term::conv2termop(op), term_of_var(y), term_of_var(z)));
      rebind_var(x, tx);
      check_terms(__LINE__);
      CRAB_LOG("uf", crab::outs() << "*** " << x << ":=" << y << " " << op
                                  << " " << z << ":" << *this << "\n");
    }
  }

  void apply(bitwise_operation_t op, const variable_t &x, const variable_t &y,
             number_t k) override {
    crab::CrabStats::count(domain_name() + ".count.apply");
    crab::ScopedCrabStats __st__(domain_name() + ".apply");

    if (!is_bottom()) {
      term_id_t tx(
          build_term(term::conv2termop(op), term_of_var(y), term_of_const(k)));
      rebind_var(x, tx);
      check_terms(__LINE__);
      CRAB_LOG("uf", crab::outs() << "*** " << x << ":=" << y << " " << op
                                  << " " << k << ":" << *this << "\n");
    }
  }

  /* Array operations */

  virtual void array_init(const variable_t & /*a*/,
                          const linear_expression_t & /*elem_size*/,
                          const linear_expression_t & /*lb_idx*/,
                          const linear_expression_t & /*ub_idx*/,
                          const linear_expression_t & /*val*/) override {
    // do nothing
  }

  virtual void array_load(const variable_t &lhs, const variable_t &a,
                          const linear_expression_t & /*elem_size*/,
                          const linear_expression_t &i) override {
    crab::CrabStats::count(domain_name() + ".count.array_read");
    crab::ScopedCrabStats __st__(domain_name() + ".array_read");

    if (!is_bottom()) {
      /**
       *  We treat the array load as an uninterpreted function
       *  lhs := array_load(a, i) -->  lhs := f(a,i)
       */
      term_id_t t_uf(
          build_term(term::TERM_OP_FUNCTION, term_of_var(a), build_linexpr(i)));
      rebind_var(lhs, t_uf);
      check_terms(__LINE__);
      CRAB_LOG("uf", crab::outs() << lhs << ":=" << a << "[" << i << "]  -- "
                                  << *this << "\n";);
    }
  }

  virtual void array_store(const variable_t &a,
                           const linear_expression_t & /*elem_size*/,
                           const linear_expression_t &i,
                           const linear_expression_t &val,
                           bool /*is_strong_update*/) override {
    // do nothing
  }

  virtual void array_store_range(const variable_t &a,
                                 const linear_expression_t &elem_size,
                                 const linear_expression_t &i,
                                 const linear_expression_t &j,
                                 const linear_expression_t &v) override {
    // do nothing
  }

  virtual void array_assign(const variable_t &lhs,
                            const variable_t &rhs) override {
    // do nothing
  }

  // backward array operations
  void backward_array_init(const variable_t &a,
                           const linear_expression_t &elem_size,
                           const linear_expression_t &lb_idx,
                           const linear_expression_t &ub_idx,
                           const linear_expression_t &val,
                           const uf_domain_t &invariant) override {
    CRAB_WARN(domain_name(), " does not implement backward operations");
  }
  void backward_array_load(const variable_t &lhs, const variable_t &a,
                           const linear_expression_t &elem_size,
                           const linear_expression_t &i,
                           const uf_domain_t &invariant) override {
    *this -= lhs;
    CRAB_WARN(domain_name(), " does not implement backward operations");
  }
  void backward_array_store(const variable_t &a,
                            const linear_expression_t &elem_size,
                            const linear_expression_t &i,
                            const linear_expression_t &v, bool is_strong_update,
                            const uf_domain_t &invariant) override {
    CRAB_WARN(domain_name(), " does not implement backward operations");
  }
  void backward_array_store_range(const variable_t &a,
                                  const linear_expression_t &elem_size,
                                  const linear_expression_t &i,
                                  const linear_expression_t &j,
                                  const linear_expression_t &v,
                                  const uf_domain_t &invariant) override {
    CRAB_WARN(domain_name(), " does not implement backward operations");
  }
  void backward_array_assign(const variable_t &lhs, const variable_t &rhs,
                             const uf_domain_t &invariant) override {
    CRAB_WARN(domain_name(), " does not implement backward operations");
  }

  DEFAULT_SELECT(uf_domain_t)

  // boolean operators
  virtual void assign_bool_cst(const variable_t &lhs,
                               const linear_constraint_t &rhs) override {
    // TODO
    operator-=(lhs);
  }

  virtual void assign_bool_ref_cst(const variable_t &lhs,
                                   const reference_constraint_t &rhs) override {
    // TODO
    operator-=(lhs);
  }

  virtual void assign_bool_var(const variable_t &lhs, const variable_t &rhs,
                               bool is_not_rhs) override {
    crab::CrabStats::count(domain_name() + ".count.assign_bool_var");
    crab::ScopedCrabStats __st__(domain_name() + ".assign_bool_var");

    if (!is_bottom()) {
      check_terms(__LINE__);
      if (is_not_rhs) {
        term_id_t tx(build_term(term::TERM_OP_NOT, term_of_var(rhs)));
        rebind_var(lhs, tx);
      } else {
        term_id_t tx(term_of_var(rhs));
        rebind_var(lhs, tx);
      }
      check_terms(__LINE__);

      CRAB_LOG(
          "uf", crab::outs() << "*** " << lhs << ":="; if (is_not_rhs) {
            crab::outs() << "not(" << rhs << ")";
          } else { crab::outs() << rhs; } crab::outs() << ":"
                                                       << *this << "\n");
    }
  }

  virtual void apply_binary_bool(bool_operation_t op, const variable_t &x,
                                 const variable_t &y,
                                 const variable_t &z) override {
    crab::CrabStats::count(domain_name() + ".count.apply_binary_bool");
    crab::ScopedCrabStats __st__(domain_name() + ".apply_binary_bool");

    if (!is_bottom()) {
      check_terms(__LINE__);
      term_id_t tx(
          build_term(term::conv2termop(op), term_of_var(y), term_of_var(z)));
      rebind_var(x, tx);
      check_terms(__LINE__);
      CRAB_LOG("uf", crab::outs() << "*** " << x << ":=" << y << " " << op
                                  << " " << z << ":" << *this << "\n");
    }
  }

  virtual void assume_bool(const variable_t &v, bool is_negated) override {
    // do nothing
  }

  void select_bool(const variable_t &lhs, const variable_t &cond,
                   const variable_t &b1, const variable_t &b2) override {
    operator-=(lhs);
  }

  // backward boolean operators
  virtual void backward_assign_bool_cst(const variable_t &lhs,
                                        const linear_constraint_t &rhs,
                                        const uf_domain_t &inv) override {
    CRAB_WARN(domain_name(), " does not implement backward operations");
  }

  virtual void backward_assign_bool_ref_cst(const variable_t &lhs,
                                            const reference_constraint_t &rhs,
                                            const uf_domain_t &inv) override {
    CRAB_WARN(domain_name(), " does not implement backward operations");
  }

  virtual void backward_assign_bool_var(const variable_t &lhs,
                                        const variable_t &rhs, bool is_not_rhs,
                                        const uf_domain_t &inv) override {
    CRAB_WARN(domain_name(), " does not implement backward operations");
  }

  virtual void backward_apply_binary_bool(bool_operation_t op,
                                          const variable_t &x,
                                          const variable_t &y,
                                          const variable_t &z,
                                          const uf_domain_t &inv) override {
    CRAB_WARN(domain_name(), " does not implement backward operations");
  }

  // Region operations
  virtual void region_init(const variable_t &reg) override {
    // do nothing
  }

  virtual void region_copy(const variable_t &lhs_reg,
                           const variable_t &rhs_reg) override {
    // do nothing
  }

  virtual void region_cast(const variable_t &src_reg,
                           const variable_t &dst_reg) override {
    // do nothing
  }

  virtual void ref_make(const variable_t &ref, const variable_t &reg,
                        const variable_or_constant_t &size,
                        const allocation_site &as) override {
    // do nothing
  }

  virtual void ref_free(const variable_t &reg, const variable_t &ref) override {
    // do nothing
  }

  virtual void ref_load(const variable_t &ref, const variable_t &reg,
                        const variable_t &res) override {
    crab::CrabStats::count(domain_name() + ".count.ref_load");
    crab::ScopedCrabStats __st__(domain_name() + ".ref_load");

    if (!is_bottom()) {
      /**
       *  We treat the load as an uninterpreted function:
       *  res := ref_load(reg, ref) -->  res := f(reg,ref)
       */
      term_id_t t_uf(build_term(term::TERM_OP_FUNCTION, term_of_var(reg),
                                build_linexpr(ref)));
      rebind_var(res, t_uf);
      check_terms(__LINE__);
      CRAB_LOG("uf", crab::outs() << res << ":=ref_load(" << reg << "," << ref
                                  << ")  -- " << *this << "\n";);
    }
  }

  virtual void ref_store(const variable_t &ref, const variable_t &reg,
                         const variable_or_constant_t &val) override {
    // do nothing
  }

  virtual void ref_gep(const variable_t &ref1, const variable_t &reg1,
                       const variable_t &ref2, const variable_t &reg2,
                       const linear_expression_t &offset) override {
    // do nothing
  }

  virtual void
  ref_load_from_array(const variable_t &lhs, const variable_t &ref,
                      const variable_t &region,
                      const linear_expression_t &index,
                      const linear_expression_t &elem_size) override {
    // TODO
    // do nothing
  }

  virtual void ref_store_to_array(const variable_t &ref,
                                  const variable_t &region,
                                  const linear_expression_t &index,
                                  const linear_expression_t &elem_size,
                                  const linear_expression_t &val) override {
    // do nothing
  }

  virtual void ref_assume(const reference_constraint_t &cst) override {
    // do nothing
  }

  void ref_to_int(const variable_t &reg, const variable_t &ref_var,
                  const variable_t &int_var) override {
    // do nothing
  }

  void int_to_ref(const variable_t &int_var, const variable_t &reg,
                  const variable_t &ref_var) override {
    // do nothing
  }

  void select_ref(const variable_t &lhs_ref, const variable_t &lhs_rgn,
                  const variable_t &cond, const variable_or_constant_t &ref1,
                  const boost::optional<variable_t> &rgn1,
                  const variable_or_constant_t &ref2,
                  const boost::optional<variable_t> &rgn2) override {
    // do nothing
  }

  boolean_value is_null_ref(const variable_t &ref) override {
    // do nothing
    return boolean_value();
  }
  bool
  get_allocation_sites(const variable_t &ref,
                       std::vector<allocation_site> &alloc_sites) override {
    // do nothing
    return false;
  }

  bool get_tags(const variable_t &rgn, const variable_t &ref,
                std::vector<uint64_t> &tags) override {
    // do nothing
    return false;
  }

  // Miscellaneous
  void rename(const variable_vector_t &from,
              const variable_vector_t &to) override {
    crab::CrabStats::count(domain_name() + ".count.rename");
    crab::ScopedCrabStats __st__(domain_name() + ".rename");

    if (is_top() || is_bottom()) {
      return;
    }

    CRAB_LOG(
        "uf", crab::outs() << "Renaming {"; for (auto v
                                                 : from) {
          crab::outs() << v << ";";
        } crab::outs() << "} with ";
        for (auto v
             : to) { crab::outs() << v << ";"; } crab::outs()
        << "}:\n";
        crab::outs() << *this << "\n";);

    auto error_if_found = [this](const variable_t &v) {
      auto it = m_var_map.find(v);
      if (it != m_var_map.end()) {
        CRAB_ERROR(domain_name() + "::rename assumes that ", v,
                   " does not exist");
      }
    };

    for (unsigned i = 0, sz = from.size(); i < sz; ++i) {
      const variable_t &v = from[i];
      const variable_t &new_v = to[i];
      if (v == new_v) { // nothing to rename
        continue;
      }

      error_if_found(new_v);

      auto it = m_var_map.find(v);
      if (it != m_var_map.end()) {
        term_id_t id = it->second;
        m_var_map.erase(it);
        m_var_map.insert(std::make_pair(new_v, id));
        remove_rev_var_map(id, v);
        add_rev_var_map(m_rev_var_map, id, new_v);
      }
    }
    CRAB_LOG("uf", crab::outs() << "RESULT=" << *this << "\n");
  }

  void forget(const variable_vector_t &variables) override {
    if (is_bottom() || is_top()) {
      return;
    }

    for (auto v : variables) {
      *this -= v;
    }
  }

  void project(const variable_vector_t &variables) override {
    crab::CrabStats::count(domain_name() + ".count.project");
    crab::ScopedCrabStats __st__(domain_name() + ".project");

    if (is_bottom() || is_top()) {
      return;
    }

    if (variables.empty()) {
      set_to_top();
      return;
    }

    std::set<variable_t> s1, s2;
    variable_vector_t s3;
    for (auto p : m_var_map) {
      s1.insert(p.first);
    }
    s2.insert(variables.begin(), variables.end());
    std::set_difference(s1.begin(), s1.end(), s2.begin(), s2.end(),
                        std::back_inserter(s3));
    forget(s3);
  }

  void expand(const variable_t &x, const variable_t &y) override {
    crab::CrabStats::count(domain_name() + ".count.expand");
    crab::ScopedCrabStats __st__(domain_name() + ".expand");

    if (is_bottom() || is_top()) {
      return;
    }

    linear_expression_t e(x);
    term_id_t tx(build_linexpr(e));
    rebind_var(y, tx);
    check_terms(__LINE__);
  }

  /* begin intrinsics operations */
  void intrinsic(std::string name, const variable_or_constant_vector_t &inputs,
                 const variable_vector_t &outputs) override {
    CRAB_WARN("Intrinsics ", name, " not implemented by ", domain_name());
  }

  void backward_intrinsic(std::string name,
                          const variable_or_constant_vector_t &inputs,
                          const variable_vector_t &outputs,
                          const uf_domain_t &invariant) override {
    CRAB_WARN("Intrinsics ", name, " not implemented by ", domain_name());
  }
  /* end intrinsics operations */

  void normalize() override {}
  void minimize() override {}

  // Output function
  void write(crab_os &o) const override {
    crab::CrabStats::count(domain_name() + ".count.write");
    crab::ScopedCrabStats __st__(domain_name() + ".write");

    if (is_bottom()) {
      o << "_|_";
      return;
    }
    if (m_var_map.empty()) {
      o << "{}";
      return;
    }

    bool first = true;
    o << "{";
    for (auto p : m_var_map) {
      if (first) {
        first = false;
      } else {
        o << ", ";
      }
      o << p.first << " -> ";
      print_term(get_term(p.second), o);
    }
    o << "}";

    CRAB_LOG("ufo-print-ttbl",
             /// For debugging purposes
             o << " ttbl={" << m_ttbl << "}\n";);
  }

  linear_constraint_system_t to_linear_constraint_system() const override {
    crab::CrabStats::count(domain_name() +
                           ".count.to_linear_constraint_system");
    crab::ScopedCrabStats __st__(domain_name() +
                                 ".to_linear_constraint_system");

    linear_constraint_system_t out_csts;
    if (is_bottom()) {
      out_csts += linear_constraint_t::get_false();
    } else if (!is_top()) {
      // Extract equalities

      // Seen equalities to avoid adding twice the same.
      std::set<std::pair<variable_t, variable_t>> seen;
      for (auto &kv : m_var_map) {
        const variable_t &x = kv.first;
        term_id_t tx = kv.second;
        auto it = m_rev_var_map.find(tx);
        if (it == m_rev_var_map.end()) {
          // this shouldn't happen
          continue;
        }
        for (auto var : it->second) {
          if (var.index() != x.index()) {
            if (seen.count(std::make_pair(var, x)) <= 0) {
              seen.insert(std::make_pair(x, var));
              out_csts += linear_constraint_t(linear_expression_t(x) ==
                                              linear_expression_t(var));
            }
          }
        }
      }
    }
    return out_csts;
  }

  disjunctive_linear_constraint_system_t
  to_disjunctive_linear_constraint_system() const override {
    auto lin_csts = to_linear_constraint_system();
    if (lin_csts.is_false()) {
      return disjunctive_linear_constraint_system_t(true /*is_false*/);
    } else if (lin_csts.is_true()) {
      return disjunctive_linear_constraint_system_t(false /*is_false*/);
    } else {
      return disjunctive_linear_constraint_system_t(lin_csts);
    }
  }

  std::string domain_name() const override { return "UFDomain"; }
}; // class uf_domain

template <typename Number, typename VariableName>
struct abstract_domain_traits<uf_domain<Number, VariableName>> {
  using number_t = Number;
  using varname_t = VariableName;
}; // end uf_domain

} // namespace domains
} // namespace crab


/*=============================================================================
    Copyright (c) 2001-2003 Daniel Nuffer
    http://spirit.sourceforge.net/

    Use, modification and distribution is subject to the Boost Software
    License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
    http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
//  Demonstrates parse trees. This is discussed in the
//  "Trees" chapter in the Spirit User's Guide.
//
///////////////////////////////////////////////////////////////////////////////
#define BOOST_SPIRIT_DUMP_PARSETREE_AS_XML

#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_parse_tree.hpp>
#include <boost/assert.hpp>

#include <iostream>
#include <stack>
#include <functional>
#include <string>

#ifdef BOOST_SPIRIT_DUMP_PARSETREE_AS_XML
#include <boost/spirit/include/classic_tree_to_xml.hpp>
#include <map>
#endif

////////////////////////////////////////////////////////////////////////////
// This example shows how to use a parse tree
using namespace std;
using namespace BOOST_SPIRIT_CLASSIC_NS;

// Here's some typedefs to simplify things
typedef char const*         iterator_t;
typedef tree_match<iterator_t> parse_tree_match_t;
typedef parse_tree_match_t::const_tree_iterator iter_t;

typedef pt_match_policy<iterator_t> match_policy_t;
typedef scanner_policies<iteration_policy, match_policy_t, action_policy> scanner_policy_t;
typedef scanner<iterator_t, scanner_policy_t> scanner_t;
typedef rule<scanner_t> rule_t;

//  grammar rules
rule_t expression, term, factor, integer;

////////////////////////////////////////////////////////////////////////////
// Here's the function prototypes that we'll use.  One function for each
// grammar rule.
long evaluate(const tree_parse_info<>& info);
long eval_expression(iter_t const& i);
long eval_term(iter_t const& i);
long eval_factor(iter_t const& i);
long eval_integer(iter_t const& i);

long evaluate(const tree_parse_info<>& info)
{
    return eval_expression(info.trees.begin());
}

// i should be pointing to a node created by the expression rule
long eval_expression(iter_t const& i)
{
    parser_id id = i->value.id();
    BOOST_ASSERT(id == expression.id()); // check the id

    // first child points to a term, so call eval_term on it
    iter_t chi = i->children.begin();
    long lhs = eval_term(chi);
    for (++chi; chi != i->children.end(); ++chi)
    {
        // next node points to the operator.  The text of the operator is
        // stored in value (a vector<char>)
        char op = *(chi->value.begin());
        ++chi;
        long rhs = eval_term(chi);
        if (op == '+')
            lhs += rhs;
        else if (op == '-')
            lhs -= rhs;
        else
            BOOST_ASSERT(0);
    }
    return lhs;
}

long eval_term(iter_t const& i)
{
    parser_id id = i->value.id();
    BOOST_ASSERT(id == term.id());

    iter_t chi = i->children.begin();
    long lhs = eval_factor(chi);
    for (++chi; chi != i->children.end(); ++chi)
    {
        char op = *(chi->value.begin());
        ++chi;
        long rhs = eval_factor(chi);
        if (op == '*')
            lhs *= rhs;
        else if (op == '/')
            lhs /= rhs;
        else
            BOOST_ASSERT(0);
    }
    return lhs;
}

long eval_factor(iter_t const& i)
{
    parser_id id = i->value.id();
    BOOST_ASSERT(id == factor.id());

    iter_t chi = i->children.begin();
    id = chi->value.id();
    if (id == integer.id())
        return eval_integer(chi->children.begin());
    else if (*(chi->value.begin()) == '(')
    {
        ++chi;
        return eval_expression(chi);
    }
    else if (*(chi->value.begin()) == '-')
    {
        ++chi;
        return -eval_factor(chi);
    }
    else
    {
        BOOST_ASSERT(0);
        return 0;
    }
}

long eval_integer(iter_t const& i)
{
    // extract integer (not always delimited by '\0')
    string integer(i->value.begin(), i->value.end());

    return strtol(integer.c_str(), 0, 10);
}

////////////////////////////////////////////////////////////////////////////
int
main()
{

    //  Start grammar definition
    integer     =   lexeme_d[ token_node_d[ (!ch_p('-') >> +digit_p) ] ];
    factor      =   integer
                |   '(' >> expression >> ')'
                |   ('-' >> factor);
    term        =   factor >>
                    *(  ('*' >> factor)
                      | ('/' >> factor)
                    );
    expression  =   term >>
                    *(  ('+' >> term)
                      | ('-' >> term)
                    );
    //  End grammar definition

    cout << "/////////////////////////////////////////////////////////\n\n";
    cout << "\t\tThe simplest working calculator...\n\n";
    cout << "/////////////////////////////////////////////////////////\n\n";
    cout << "Type an expression...or [q or Q] to quit\n\n";

    string str;
    while (getline(cin, str))
    {
        if (str.empty() || str[0] == 'q' || str[0] == 'Q')
            break;

        const char* first = str.c_str();

        tree_parse_info<> info = pt_parse(first, expression);

        if (info.full)
        {
#if defined(BOOST_SPIRIT_DUMP_PARSETREE_AS_XML)
            // dump parse tree as XML
            std::map<parser_id, std::string> rule_names;
            rule_names[integer.id()] = "integer";
            rule_names[factor.id()] = "factor";
            rule_names[term.id()] = "term";
            rule_names[expression.id()] = "expression";
            tree_to_xml(cout, info.trees, first, rule_names);
#endif

            // print the result
            cout << "parsing succeeded\n";
            cout << "result = " << evaluate(info) << "\n\n";
        }
        else
        {
            cout << "parsing failed\n";
        }
    }

    cout << "Bye... :-) \n\n";
    return 0;
}


#include <iostream>
#include <string>
#include <vector>
#include <cctype>
#include <sstream>
#include <memory>

using namespace std;

#include <boost/lexical_cast.hpp>

struct Token {
    enum Type {
        integer, plus, minus, lparen, rparen
    } type;
    string text;

    explicit Token(Type type, const string &text) :
            type{type}, text{text} {}

    friend ostream &operator<<(ostream &os, const Token &obj) {
        return os << "`" << obj.text << "`";
    }
};

vector<Token> lex(const string &input) {
    vector<Token> result;

    for (int i = 0; i < input.size(); ++i) {
        switch (input[i]) {
            case '+':
                result.push_back(Token{Token::plus, "+"});
                break;
            case '-':
                result.push_back(Token{Token::minus, "-"});
                break;
            case '(':
                result.push_back(Token{Token::lparen, "("});
                break;
            case ')':
                result.push_back(Token{Token::rparen, ")"});
                break;
            default:
                // number
                ostringstream buffer;
                buffer << input[i];
                for (int j = i + 1; j < input.size(); ++j) {
                    if (isdigit(input[j])) {
                        buffer << input[j];
                        ++i;
                    } else {
                        result.push_back(Token{Token::integer, buffer.str()});
                        break;
                    }
                }
        }
    }

    return result;
}

// parsing =====================================================

struct Element {
    virtual ~Element() = default;

    virtual int eval() const = 0;
};

struct Integer : Element {
    int value;

    explicit Integer(const int value)
            : value(value) {
    }

    int eval() const override { return value; }
};

struct BinaryOperation : Element {
    enum Type {
        addition, subtraction
    } type;
    shared_ptr<Element> lhs, rhs;

    int eval() const override {
        if (type == addition)
            return lhs->eval() + rhs->eval();
        return lhs->eval() - rhs->eval();
    }
};

shared_ptr<Element> parse(const vector<Token> &tokens) {
    auto result = make_unique<BinaryOperation>();
    bool have_lhs = false;
    for (size_t i = 0; i < tokens.size(); i++) {
        auto token = tokens[i];
        switch (token.type) {
            case Token::integer: {
                int value = boost::lexical_cast<int>(token.text);
                auto integer = make_shared<Integer>(value);
                if (!have_lhs) {
                    result->lhs = integer;
                    have_lhs = true;
                } else result->rhs = integer;
            }
                break;
            case Token::plus:
                result->type = BinaryOperation::addition;
                break;
            case Token::minus:
                result->type = BinaryOperation::subtraction;
                break;
            case Token::lparen: {
                int j = i;
                for (; j < tokens.size(); ++j)
                    if (tokens[j].type == Token::rparen)
                        break; // found it!

                vector<Token> subexpression(&tokens[i + 1], &tokens[j]);
                auto element = parse(subexpression);
                if (!have_lhs) {
                    result->lhs = element;
                    have_lhs = true;
                } else result->rhs = element;
                i = j; // advance
            }
                break;
        }
    }
    return result;
}

int main() {
    string input{"(13-4)-(12+1)"}; // see if you can make nested braces work
    auto tokens = lex(input);

    // let's see the tokens
    for (auto &t : tokens)
        cout << t << "   ";
    cout << endl;

    try {
        auto parsed = parse(tokens);
        cout << input << " = " << parsed->eval() << endl;
    }
    catch (const exception &e) {
        cout << e.what() << endl;
    }

    return 0;
}