#ifndef __CPP_META_REPLACE_TYPE_HPP__
#define __CPP_META_REPLACE_TYPE_HPP__

#include <boost/type_traits.hpp>

/**
 * Write a ternary meta-function replace_type<c,x,y> that takes an arbitrary
 * compound type c as its first parameter, and replaces all occurrences of a
 * type x within c with y:
 *
 * typedef replace_type<void*, void, int>::type t1; // int*
 * typedef replace_type<
 *   int const*[10]
 *   , int const
 *   , long
 *   >::type t2; // long* [10]
 * typedef replace_type<
 *   char& (*)(char&)
 *   , char&
 *   , long&
 *   >::type t3; // long& (*)(long&)
 */

namespace detail {

template <
    typename T,
    size_t N,
    typename = typename boost::enable_if_c<
        !boost::is_abstract<T>::value &&
        !boost::is_reference<T>::value &&
        !boost::is_function<T>::value &&
        !boost::is_void<T>::value &&
        (N > 0)
    >::type
>
struct add_extent
{
    using type = T[N];
};

template <typename C, typename X, typename Y>
struct replace_type_impl;

template <typename C, typename Y>
struct replace_type_impl<C, C, Y>
{
    using type = Y;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<C[N], C, Y>
{
    using type = typename add_extent<Y, N>::type;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<const C[N], C, Y>
{
    using type = typename add_extent<typename boost::add_const<Y>::type, N>::type;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<volatile C[N], C, Y>
{
    using type = typename add_extent<typename boost::add_volatile<Y>::type, N>::type;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<const volatile C[N], C, Y>
{
    using type = typename add_extent<typename boost::add_cv<Y>::type, N>::type;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<const volatile C[N], const C, Y>
{
    using type = typename add_extent<typename boost::add_volatile<Y>::type, N>::type;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<const volatile C[N], volatile C, Y>
{
    using type = typename add_extent<typename boost::add_const<Y>::type, N>::type;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<C*[N], C, Y>
{
    using type = typename add_extent<typename boost::add_pointer<Y>::type, N>::type;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<const C*[N], C, Y>
{
    using type = typename add_extent<
        typename boost::add_pointer<
            typename boost::add_const<Y>::type
        >::type,
        N
    >::type;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<volatile C*[N], C, Y>
{
    using type = typename add_extent<
        typename boost::add_pointer<
            typename boost::add_volatile<Y>::type
        >::type,
        N
    >::type;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<const volatile C*[N], C, Y>
{
    using type = typename add_extent<
        typename boost::add_pointer<
            typename boost::add_cv<Y>::type
        >::type,
        N
    >::type;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<const volatile C*[N], const C, Y>
{
    using type = typename add_extent<
        typename boost::add_pointer<
            typename boost::add_volatile<Y>::type
        >::type,
        N
    >::type;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<const volatile C*[N], volatile C, Y>
{
    using type = typename add_extent<
        typename boost::add_pointer<
            typename boost::add_const<Y>::type
        >::type,
        N
    >::type;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<C* const[N], C, Y>
{
    using type = typename add_extent<
        typename boost::add_const<
            typename boost::add_pointer<Y>::type
        >::type,
        N
    >::type;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<C* volatile[N], C, Y>
{
    using type = typename add_extent<
        typename boost::add_volatile<
            typename boost::add_pointer<Y>::type
        >::type,
        N
    >::type;
};

template <typename C, typename Y, size_t N>
struct replace_type_impl<C* const volatile[N], C, Y>
{
    using type = typename add_extent<
        typename boost::add_cv<
            typename boost::add_pointer<Y>::type
        >::type,
        N
    >::type;
};

template <typename C, typename Y>
struct replace_type_impl<const C, C, Y>
{
    using type = typename boost::add_const<Y>::type;
};

template <typename C, typename Y>
struct replace_type_impl<volatile C, C, Y>
{
    using type = typename boost::add_volatile<Y>::type;
};

template <typename C, typename Y>
struct replace_type_impl<const volatile C, C, Y>
{
    using type = typename boost::add_cv<Y>::type;
};

template <typename C, typename Y>
struct replace_type_impl<const volatile C, const C, Y>
{
    using type = typename boost::add_volatile<Y>::type;
};

template <typename C, typename Y>
struct replace_type_impl<const volatile C, volatile C, Y>
{
    using type = typename boost::add_const<Y>::type;
};

template <typename C, typename X, typename Y>
struct replace_type
{
    using type = typename replace_type_impl<C, X, Y>::type;
};

template <typename C, typename X, typename Y>
struct replace_type<C*, X, Y>
{
    using type = typename boost::add_pointer<
        typename replace_type_impl<C, X, Y>::type
    >::type;
};

template <typename C, typename X, typename Y>
struct replace_type<C* const, X, Y>
{
    using type = typename boost::add_const<
        typename boost::add_pointer<
            typename replace_type_impl<C, X, Y>::type
        >::type
    >::type;
};

template <typename C, typename X, typename Y>
struct replace_type<C* volatile, X, Y>
{
    using type = typename boost::add_volatile<
        typename boost::add_pointer<
            typename replace_type_impl<C, X, Y>::type
        >::type
    >::type;
};

template <typename C, typename X, typename Y>
struct replace_type<C* const volatile, X, Y>
{
    using type = typename boost::add_cv<
        typename boost::add_pointer<
            typename replace_type_impl<C, X, Y>::type
        >::type
    >::type;
};

template <typename C, typename X, typename Y>
struct replace_type<C*, X*, Y>
{
    using type = typename replace_type_impl<C, X, Y>::type;
};

template <typename C, typename X, typename Y>
struct replace_type<C* const, X*, Y>
{
    using type = typename boost::add_const<
        typename replace_type_impl<C, X, Y>::type
    >::type;
};

template <typename C, typename X, typename Y>
struct replace_type<C* volatile, X*, Y>
{
    using type = typename boost::add_volatile<
        typename replace_type_impl<C, X, Y>::type
    >::type;
};

template <typename C, typename X, typename Y>
struct replace_type<C* const volatile, X*, Y>
{
    using type = typename boost::add_cv<
        typename replace_type_impl<C, X, Y>::type
    >::type;
};

template <typename C, typename X, typename Y>
struct replace_type<C&, X, Y>
{
    using type = typename boost::add_reference<
        typename replace_type_impl<C, X, Y>::type
    >::type;
};

template <typename C, typename X, typename Y>
struct replace_type<C&, X&, Y>
{
    using type = typename replace_type_impl<C, X, Y>::type;
};

template <typename C, typename X, typename Y>
struct replace_type<C*&, X, Y>
{
    using type = typename boost::add_reference<
        typename boost::add_pointer<
            typename replace_type_impl<C, X, Y>::type
        >::type
    >::type;
};

template <typename C, typename X, typename Y>
struct replace_type<C*&, X*, Y>
{
    using type = typename boost::add_reference<
        typename replace_type_impl<C, X, Y>::type
    >::type;
};

template <typename C, typename X, typename Y>
struct replace_type<C*&, X*&, Y>
{
    using type = typename replace_type_impl<C, X, Y>::type;
};

template <typename R, typename X, typename Y>
struct replace_type<R(*)(), X, Y>
{
    using type = typename replace_type<R, X, Y>::type (*)();
};

template <typename R, typename A0, typename X, typename Y>
struct replace_type<R(*)(A0), X, Y>
{
    using type = typename replace_type<R, X, Y>::type (*)(typename replace_type<A0, X, Y>::type);
};

template <typename R, typename A0, typename A1, typename X, typename Y>
struct replace_type<R(*)(A0, A1), X, Y>
{
    using type = typename replace_type<R, X, Y>::type (*)(
        typename replace_type<A0, X, Y>::type,
        typename replace_type<A1, X, Y>::type);
};

template <typename R, typename A0, typename A1, typename A2, typename X, typename Y>
struct replace_type<R(*)(A0, A1, A2), X, Y>
{
    using type = typename replace_type<R, X, Y>::type (*)(
        typename replace_type<A0, X, Y>::type,
        typename replace_type<A1, X, Y>::type,
        typename replace_type<A2, X, Y>::type);
};

template <typename R, typename A0, typename A1, typename A2, typename A3, typename X, typename Y>
struct replace_type<R(*)(A0, A1, A2, A3), X, Y>
{
    using type = typename replace_type<R, X, Y>::type (*)(
        typename replace_type<A0, X, Y>::type,
        typename replace_type<A1, X, Y>::type,
        typename replace_type<A2, X, Y>::type,
        typename replace_type<A3, X, Y>::type);
};

template <typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename X, typename Y>
struct replace_type<R(*)(A0, A1, A2, A3, A4), X, Y>
{
    using type = typename replace_type<R, X, Y>::type (*)(
        typename replace_type<A0, X, Y>::type,
        typename replace_type<A1, X, Y>::type,
        typename replace_type<A2, X, Y>::type,
        typename replace_type<A3, X, Y>::type,
        typename replace_type<A4, X, Y>::type);
};

template <typename R, typename A0, typename A1, typename A2, typename A3, typename A4,
          typename A5, typename X, typename Y>
struct replace_type<R(*)(A0, A1, A2, A3, A4, A5), X, Y>
{
    using type = typename replace_type<R, X, Y>::type (*)(
        typename replace_type<A0, X, Y>::type,
        typename replace_type<A1, X, Y>::type,
        typename replace_type<A2, X, Y>::type,
        typename replace_type<A3, X, Y>::type,
        typename replace_type<A4, X, Y>::type,
        typename replace_type<A5, X, Y>::type);
};

template <typename R, typename A0, typename A1, typename A2, typename A3, typename A4,
          typename A5, typename A6, typename X, typename Y>
struct replace_type<R(*)(A0, A1, A2, A3, A4, A5, A6), X, Y>
{
    using type = typename replace_type<R, X, Y>::type (*)(
        typename replace_type<A0, X, Y>::type,
        typename replace_type<A1, X, Y>::type,
        typename replace_type<A2, X, Y>::type,
        typename replace_type<A3, X, Y>::type,
        typename replace_type<A4, X, Y>::type,
        typename replace_type<A5, X, Y>::type,
        typename replace_type<A6, X, Y>::type);
};

template <typename R, typename A0, typename A1, typename A2, typename A3, typename A4,
          typename A5, typename A6, typename A7, typename X, typename Y>
struct replace_type<R(*)(A0, A1, A2, A3, A4, A5, A6, A7), X, Y>
{
    using type = typename replace_type<R, X, Y>::type (*)(
        typename replace_type<A0, X, Y>::type,
        typename replace_type<A1, X, Y>::type,
        typename replace_type<A2, X, Y>::type,
        typename replace_type<A3, X, Y>::type,
        typename replace_type<A4, X, Y>::type,
        typename replace_type<A5, X, Y>::type,
        typename replace_type<A6, X, Y>::type,
        typename replace_type<A7, X, Y>::type);
};

template <typename R, typename A0, typename A1, typename A2, typename A3, typename A4,
          typename A5, typename A6, typename A7, typename A8, typename X, typename Y>
struct replace_type<R(*)(A0, A1, A2, A3, A4, A5, A6, A7, A8), X, Y>
{
    using type = typename replace_type<R, X, Y>::type (*)(
        typename replace_type<A0, X, Y>::type,
        typename replace_type<A1, X, Y>::type,
        typename replace_type<A2, X, Y>::type,
        typename replace_type<A3, X, Y>::type,
        typename replace_type<A4, X, Y>::type,
        typename replace_type<A5, X, Y>::type,
        typename replace_type<A6, X, Y>::type,
        typename replace_type<A7, X, Y>::type,
        typename replace_type<A8, X, Y>::type);
};

template <typename R, typename A0, typename A1, typename A2, typename A3, typename A4,
          typename A5, typename A6, typename A7, typename A8, typename A9, typename X, typename Y>
struct replace_type<R(*)(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9), X, Y>
{
    using type = typename replace_type<R, X, Y>::type (*)(
        typename replace_type<A0, X, Y>::type,
        typename replace_type<A1, X, Y>::type,
        typename replace_type<A2, X, Y>::type,
        typename replace_type<A3, X, Y>::type,
        typename replace_type<A4, X, Y>::type,
        typename replace_type<A5, X, Y>::type,
        typename replace_type<A6, X, Y>::type,
        typename replace_type<A7, X, Y>::type,
        typename replace_type<A8, X, Y>::type,
        typename replace_type<A9, X, Y>::type);
};

} // namespace

template <typename C, typename X, typename Y>
struct replace_type : public detail::replace_type<C, X, Y> {};

#endif  // __CPP_META_REPLACE_TYPE_HPP__


#include "../include/common.hpp"
#include <boost/mpi.hpp>
#include <cstddef>
#include <algorithm>
#include <cmath>

namespace mpi_pi::common {

Division::Division(const boost::mpi::communicator &comm, std::size_t total) noexcept {
  const auto rank = static_cast<std::size_t>(comm.rank());
  const auto size = static_cast<std::size_t>(comm.size());

  const auto basic_local_count = static_cast<std::size_t>(std::ceil(static_cast<double>(total) / size));
  this->first = basic_local_count * rank;
  this->last = std::min(total, basic_local_count * (rank + 1));
}

std::size_t Division::count() const noexcept { return this->last - this->first; }

} // namespace mpi_pi::common


// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "boundary_facets.h"
#include "face_occurrences.h"
#include "list_to_matrix.h"
#include "matrix_to_list.h"
#include "sort.h"
#include "unique_rows.h"
#include "accumarray.h"
#include "slice_mask.h"

#include <Eigen/Core>

#include <map>
#include <iostream>

template <
  typename DerivedT,
  typename DerivedF,
  typename DerivedJ,
  typename DerivedK>
IGL_INLINE void igl::boundary_facets(
  const Eigen::MatrixBase<DerivedT>& T,
  Eigen::PlainObjectBase<DerivedF>& F,
  Eigen::PlainObjectBase<DerivedJ>& J,
  Eigen::PlainObjectBase<DerivedK>& K)
{
  const int simplex_size = T.cols();
  // Handle boring base case
  if(T.rows() == 0)
  {
    F.resize(0,simplex_size-1);
    J.resize(0,1);
    K.resize(0,1);
    return;
  }
  // Get a list of all facets
  DerivedF allF(T.rows()*simplex_size,simplex_size-1);
  // Gather faces (e.g., loop over tets)
  for(int i = 0; i< (int)T.rows();i++)
  {
    switch(simplex_size)
    {
      case 4:
        // get face in correct order
        allF(i*simplex_size+0,0) = T(i,1);
        allF(i*simplex_size+0,1) = T(i,3);
        allF(i*simplex_size+0,2) = T(i,2);
        // get face in correct order
        allF(i*simplex_size+1,0) = T(i,0);
        allF(i*simplex_size+1,1) = T(i,2);
        allF(i*simplex_size+1,2) = T(i,3);
        // get face in correct order
        allF(i*simplex_size+2,0) = T(i,0);
        allF(i*simplex_size+2,1) = T(i,3);
        allF(i*simplex_size+2,2) = T(i,1);
        // get face in correct order
        allF(i*simplex_size+3,0) = T(i,0);
        allF(i*simplex_size+3,1) = T(i,1);
        allF(i*simplex_size+3,2) = T(i,2);
        break;
      case 3:
        allF(i*simplex_size+0,0) = T(i,1);
        allF(i*simplex_size+0,1) = T(i,2);
        allF(i*simplex_size+1,0) = T(i,2);
        allF(i*simplex_size+1,1) = T(i,0);
        allF(i*simplex_size+2,0) = T(i,0);
        allF(i*simplex_size+2,1) = T(i,1);
        break;
    }
  }
  DerivedF sortedF;
  igl::sort(allF,2,true,sortedF);
  Eigen::VectorXi m,n;
  {
    DerivedF _1;
    igl::unique_rows(sortedF,_1,m,n);
  }
  Eigen::VectorXi C;
  igl::accumarray(n,1,C);
  const int ones = (C.array()==1).count();
  // Resize output to fit number of non-twos
  F.resize(ones, allF.cols());
  J.resize(F.rows(),1);
  K.resize(F.rows(),1);
  int k = 0;
  for(int c = 0;c< (int)C.size();c++)
  {
    if(C(c) == 1)
    {
      const int i = m(c);
      assert(k<(int)F.rows());
      F.row(k) = allF.row(i);
      J(k) = i/simplex_size;
      K(k) = i%simplex_size;
      k++;
    }
  }
  assert(k==(int)F.rows());
}

template <typename DerivedT, typename DerivedF>
IGL_INLINE void igl::boundary_facets(
  const Eigen::MatrixBase<DerivedT>& T,
  Eigen::PlainObjectBase<DerivedF>& F)
{
  Eigen::VectorXi J,K;
  return boundary_facets(T,F,J,K);
}

template <typename DerivedT, typename Ret>
Ret igl::boundary_facets(
  const Eigen::MatrixBase<DerivedT>& T)
{
  Ret F;
  igl::boundary_facets(T,F);
  return F;
}

template <typename IntegerT, typename IntegerF>
IGL_INLINE void igl::boundary_facets(
  const std::vector<std::vector<IntegerT> > & T,
  std::vector<std::vector<IntegerF> > & F)
{
  // Kept for legacy reasons. Could probably just delete.
  using namespace std;

  if(T.size() == 0)
  {
    F.clear();
    return;
  }

  int simplex_size = T[0].size();
  // Get a list of all faces
  vector<vector<IntegerF> > allF(
    T.size()*simplex_size,
    vector<IntegerF>(simplex_size-1));

  // Gather faces, loop over tets
  for(int i = 0; i< (int)T.size();i++)
  {
    assert((int)T[i].size() == simplex_size);
    switch(simplex_size)
    {
      case 4:
        // get face in correct order
        allF[i*simplex_size+0][0] = T[i][1];
        allF[i*simplex_size+0][1] = T[i][3];
        allF[i*simplex_size+0][2] = T[i][2];
        // get face in correct order
        allF[i*simplex_size+1][0] = T[i][0];
        allF[i*simplex_size+1][1] = T[i][2];
        allF[i*simplex_size+1][2] = T[i][3];
        // get face in correct order
        allF[i*simplex_size+2][0] = T[i][0];
        allF[i*simplex_size+2][1] = T[i][3];
        allF[i*simplex_size+2][2] = T[i][1];
        // get face in correct order
        allF[i*simplex_size+3][0] = T[i][0];
        allF[i*simplex_size+3][1] = T[i][1];
        allF[i*simplex_size+3][2] = T[i][2];
        break;
      case 3:
        allF[i*simplex_size+0][0] = T[i][1];
        allF[i*simplex_size+0][1] = T[i][2];
        allF[i*simplex_size+1][0] = T[i][2];
        allF[i*simplex_size+1][1] = T[i][0];
        allF[i*simplex_size+2][0] = T[i][0];
        allF[i*simplex_size+2][1] = T[i][1];
        break;
    }
  }

  // Counts
  vector<int> C;
  face_occurrences(allF,C);

  // Q: Why not just count the number of ones?
  // A: because we are including non-manifold edges as boundary edges
  int twos = (int) count(C.begin(),C.end(),2);
  //int ones = (int) count(C.begin(),C.end(),1);
  // Resize output to fit number of ones
  F.resize(allF.size() - twos);
  //F.resize(ones);
  int k = 0;
  for(int i = 0;i< (int)allF.size();i++)
  {
    if(C[i] != 2)
    {
      assert(k<(int)F.size());
      F[k] = allF[i];
      k++;
    }
  }
  assert(k==(int)F.size());
  //if(k != F.size())
  //{
  //  printf("%d =? %d\n",k,F.size());
  //}

}

#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
// generated by autoexplicit.sh
template void igl::boundary_facets<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
// generated by autoexplicit.sh
template void igl::boundary_facets<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<unsigned int, -1, 3, 1, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, 3, 1, -1, 3> >&);
// generated by autoexplicit.sh
template void igl::boundary_facets<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 3, 0, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> >&);
template void igl::boundary_facets<int, int>(std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > > const&, std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >&);
//template Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > igl::boundary_facets(Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&);
template Eigen::Matrix<int, -1, -1, 0, -1, -1> igl::boundary_facets<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&);
template void igl::boundary_facets<Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 3, 1, -1, 3> >(Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> >&);
#endif


/* Copyright (C) 2012-2017 IBM Corp.
 * This program is 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. See accompanying LICENSE file.
 */
#include <NTL/BasicThreadPool.h>

#include "recryption.h"
#include "EncryptedArray.h"
#include "EvalMap.h"
#include "powerful.h"
#include "CtPtrs.h"
#include "intraSlot.h"

/************* Some local functions *************/
static void x2iInSlots(ZZX& poly, long i,
		       vector<ZZX>& xVec, const EncryptedArray& ea);

// Make every entry of vec divisible by p^e by adding/subtracting multiples
// of p^r and q, while keeping the added multiples small.
template<class VecInt>
long makeDivisible(VecInt& vec, long p2e, long p2r, long q, double alpha);
static inline double pow(long a, long b) {return pow(double(a), double(b));}

RecryptData::~RecryptData()
{
  if (alMod!=NULL)     delete alMod;
  if (ea!=NULL)        delete ea;
  if (firstMap!=NULL)  delete firstMap;
  if (secondMap!=NULL) delete secondMap;
  if (p2dConv!=NULL)   delete p2dConv;
}

/** Computing the recryption parameters
 *
 * To get the smallest possible value of e-e', the params need to satisfy:
 *  (p^e +1)/4 =>
 *       max { (t+1)( 1+ (alpha/2)*(p^e/p^{ceil(log_p(t+2))}) ) + noise      }
 *           { (t+1)( 1+ ((1-alpha)/2)*(p^e/p^{ceil(log_p(t+2))}) +p^r/2) +1 },
 *
 * where noise is taken to be twice the mod-switching additive term, namely
 * noise = p^r *sqrt((t+1)*phi(m)/3). Denoting rho=(t+1)/p^{ceil(log_p(t+2))}
 * (and ignoring fome +1 terms), this is equivalent to:
 *
 *   p^e > max { 4(t+noise)/(1-2*alpha*rho), 2(t+1)p^r/(1-2(1-alpha)rho) }.
 *
 * We first compute the optimal value for alpha (which must be in [0,1]),
 * that makes the two terms in the max{...} as close as possible, and
 * then compute the smallest value of e satisfying this constraint.
 *
 * If this value is too big then we try again with e-e' one larger,
 * which means that rho is a factor of p smaller.
 */

// Some convenience functions
static double lowerBound1(long p, long r, long ePrime, long t,
			  double alpha, double noise)
{
  return (t+1)*(1+ alpha*pow(p,r+ePrime-1)/2)+noise;
}
static double lowerBound2(long p, long r, long ePrime, long t, double alpha)
{
  return (t+1)*(1+ (1-alpha)*pow(p,r+ePrime-1)/2 + pow(p,r)/2)+1;
}

static void setAlphaE(double& alpha, long& e, double rho, double gamma,
		      double noise, double logp, long p2r, long t)
{
  alpha = (1 +gamma*(2*rho-1))/(2*rho*(1+gamma));
  if (alpha<0) alpha=0;
  else if (alpha>1) alpha=1;

  if (alpha<1) {
    double ratio = 4*(t+noise)/(1-2*alpha*rho);
    e = floor(1+ log(ratio)/logp);
  }
  else
    e = floor(1+ log(2*(t+1)*p2r)/logp);
}

bool RecryptData::operator==(const RecryptData& other) const
{
  if (mvec != other.mvec) return false;
  if (hwt != other.hwt) return false;
  if (conservative != other.conservative) return false;

  return true;
}

// The main method
void RecryptData::init(const FHEcontext& context, const Vec<long>& mvec_,
		       long t, bool consFlag, bool build_cache_, bool minimal)
{
  if (alMod != NULL) { // were we called for a second time?
    cerr << "@Warning: multiple calls to RecryptData::init\n";
    return;
  }
  assert(computeProd(mvec_) == (long)context.zMStar.getM()); // sanity check

  // Record the arguments to this function
  mvec = mvec_;
  conservative = consFlag;
  build_cache = build_cache_;

  if (t <= 0) t = defSkHwt+1; // recryption key Hwt
  hwt = t;
  long p = context.zMStar.getP();
  long phim = context.zMStar.getPhiM();
  long r = context.alMod.getR();
  long p2r = context.alMod.getPPowR();
  double logp = log((double)p);

  double noise = p2r * sqrt((t+1)*phim/3.0);
  double gamma = 2*(t+noise)/((t+1)*p2r); // ratio between numerators

  long logT = ceil(log((double)(t+2))/logp); // ceil(log_p(t+2))
  double rho = (t+1)/pow(p,logT);

  if (!conservative) {   // try alpha, e with this "aggresive" setting
    setAlphaE(alpha, e, rho, gamma, noise, logp, p2r, t);
    ePrime = e -r +1 -logT;

    // If e is too large, try again with rho/p instead of rho
    long bound = (1L << (context.bitsPerLevel-1)); // halfSizePrime/2
    if (pow(p,e) > bound) { // try the conservative setting instead
      cerr << "* p^e="<<pow(p,e)<<" is too big (bound="<<bound<<")\n";
      conservative = true;
    }
  }
  if (conservative) { // set alpha, e with a "conservative" rho/p
    setAlphaE(alpha, e, rho/p, gamma, noise, logp, p2r, t);
    ePrime = e -r -logT;
  }

  // Compute highest key-Hamming-weight that still works (not more than 256)
  double qOver4 = (pow(p,e)+1)/4;
  for (t-=10; qOver4>=lowerBound2(p,r,ePrime,t,alpha)
	 &&  qOver4>=lowerBound1(p,r,ePrime,t,alpha,noise) && t<257; t++);
  skHwt = t-1;

  // First part of Bootstrapping works wrt plaintext space p^{r'}
  alMod = new PAlgebraMod(context.zMStar, e-ePrime+r);
  ea = new EncryptedArray(context, *alMod);
         // Polynomial defaults to F0, PAlgebraMod explicitly given

  p2dConv = new PowerfulDCRT(context, mvec);

  // Initialize the linear polynomial for unpacking the slots
  zz_pBak bak; bak.save(); ea->getAlMod().restoreContext();
  long nslots = ea->size();
  long d = ea->getDegree();

  const Mat<zz_p>& CBi=ea->getDerived(PA_zz_p()).getNormalBasisMatrixInverse();

  vector<ZZX> LM;
  LM.resize(d);
  for (long i = 0; i < d; i++) // prepare the linear polynomial
    LM[i] = rep(CBi[i][0]);

  vector<ZZX> C;
  ea->buildLinPolyCoeffs(C, LM); // "build" the linear polynomial

  unpackSlotEncoding.resize(d);  // encode the coefficients

  for (long j = 0; j < d; j++) {
    vector<ZZX> v(nslots);
    for (long k = 0; k < nslots; k++) v[k] = C[j];
    ea->encode(unpackSlotEncoding[j], v);
  }
  firstMap = new EvalMap(*ea, minimal, mvec, true, build_cache);
  secondMap = new EvalMap(*context.ea, minimal, mvec, false, build_cache);
}

/********************************************************************/
/********************************************************************/

#ifdef DEBUG_PRINTOUT
#include "debugging.h"
long printFlag = FLAG_PRINT_VEC;
#endif

// Extract digits from fully packed slots
void extractDigitsPacked(Ctxt& ctxt, long botHigh, long r, long ePrime,
			 const vector<ZZX>& unpackSlotEncoding);

// bootstrap a ciphertext to reduce noise
void FHEPubKey::reCrypt(Ctxt &ctxt)
{
  FHE_TIMER_START;

  // Some sanity checks for dummy ciphertext
  long ptxtSpace = ctxt.getPtxtSpace();
  if (ctxt.isEmpty()) return;
  if (ctxt.parts.size()==1 && ctxt.parts[0].skHandle.isOne()) {
    // Dummy encryption, just ensure that it is reduced mod p
    ZZX poly = to_ZZX(ctxt.parts[0]);
    for (long i=0; i<poly.rep.length(); i++)
      poly[i] = to_ZZ( rem(poly[i],ptxtSpace) );
    poly.normalize();
    ctxt.DummyEncrypt(poly);
    return;
  }

  assert(recryptKeyID>=0); // check that we have bootstrapping data

  long p = getContext().zMStar.getP();
  long r = getContext().alMod.getR();
  long p2r = getContext().alMod.getPPowR();

  // the bootstrapping key is encrypted relative to plaintext space p^{e-e'+r}.
  long e = getContext().rcData.e;
  long ePrime = getContext().rcData.ePrime;
  long p2ePrime = power_long(p,ePrime);
  long q = power_long(p,e)+1;
  assert(e>=r);

#ifdef DEBUG_PRINTOUT
  cerr << "reCrypt: p="<<p<<", r="<<r<<", e="<<e<<" ePrime="<<ePrime
       << ", q="<<q<<endl;
#endif

  // can only bootstrap ciphertext with plaintext-space dividing p^r
  assert(p2r % ptxtSpace == 0);

  FHE_NTIMER_START(preProcess);

  // Make sure that this ciphertxt is in canonical form
  if (!ctxt.inCanonicalForm()) ctxt.reLinearize();

  // Mod-switch down if needed
  IndexSet s = ctxt.getPrimeSet() / getContext().specialPrimes; // set minus
  if (s.card()>2) { // leave only bottom two primes
    long frst = s.first();
    long scnd = s.next(frst);
    IndexSet s2(frst,scnd);
    s.retain(s2); // retain only first two primes
  }
  ctxt.modDownToSet(s);

  // key-switch to the bootstrapping key
  ctxt.reLinearize(recryptKeyID);

  // "raw mod-switch" to the bootstrapping mosulus q=p^e+1.
  vector<ZZX> zzParts; // the mod-switched parts, in ZZX format
  double noise = ctxt.rawModSwitch(zzParts, q);
  noise = sqrt(noise);

  // Add multiples of p2r and q to make the zzParts divisible by p^{e'}
  long maxU=0;
  for (long i=0; i<(long)zzParts.size(); i++) {
    // make divisible by p^{e'}
    long newMax = makeDivisible(zzParts[i].rep, p2ePrime, p2r, q,
				getContext().rcData.alpha);
    zzParts[i].normalize();   // normalize after working directly on the rep
    if (maxU < newMax)  maxU = newMax;
  }

  // Check that the estimated noise is still low
  if (noise + maxU*p2r*(skHwts[recryptKeyID]+1) > q/2)
    cerr << " * noise/q after makeDivisible = "
	 << ((noise + maxU*p2r*(skHwts[recryptKeyID]+1))/q) << endl;

  for (long i=0; i<(long)zzParts.size(); i++)
    zzParts[i] /= p2ePrime;   // divide by p^{e'}

  // Multiply the post-processed cipehrtext by the encrypted sKey
#ifdef DEBUG_PRINTOUT
  cerr << "+ Before recryption ";
  decryptAndPrint(cerr, recryptEkey, *dbgKey, *dbgEa, printFlag);
#endif

  double p0size = to_double(coeffsL2Norm(zzParts[0]));
  double p1size = to_double(coeffsL2Norm(zzParts[1]));
  ctxt = recryptEkey;
  ctxt.multByConstant(zzParts[1], p1size*p1size);
  ctxt.addConstant(zzParts[0], p0size*p0size);

#ifdef DEBUG_PRINTOUT
  cerr << "+ Before linearTrans1 ";
  decryptAndPrint(cerr, ctxt, *dbgKey, *dbgEa, printFlag);
#endif
  FHE_NTIMER_STOP(preProcess);

  // Move the powerful-basis coefficients to the plaintext slots
  FHE_NTIMER_START(LinearTransform1);
  ctxt.getContext().rcData.firstMap->apply(ctxt);
  FHE_NTIMER_STOP(LinearTransform1);

#ifdef DEBUG_PRINTOUT
  cerr << "+ After linearTrans1 ";
  decryptAndPrint(cerr, ctxt, *dbgKey, *dbgEa, printFlag);
#endif

  // Extract the digits e-e'+r-1,...,e-e' (from fully packed slots)
  extractDigitsPacked(ctxt, e-ePrime, r, ePrime,
		      context.rcData.unpackSlotEncoding);

#ifdef DEBUG_PRINTOUT
  cerr << "+ Before linearTrans2 ";
  decryptAndPrint(cerr, ctxt, *dbgKey, *dbgEa, printFlag);
#endif

  // Move the slots back to powerful-basis coefficients
  FHE_NTIMER_START(LinearTransform2);
  ctxt.getContext().rcData.secondMap->apply(ctxt);
  FHE_NTIMER_STOP(LinearTransform2);
}

/*********************************************************************/
/*********************************************************************/

// Return in poly a polynomial with X^i encoded in all the slots
static void x2iInSlots(ZZX& poly, long i,
		       vector<ZZX>& xVec, const EncryptedArray& ea)
{
  xVec.resize(ea.size());
  ZZX x2i = ZZX(i,1);
  for (long j=0; j<(long)xVec.size(); j++) xVec[j] = x2i;
  ea.encode(poly, xVec);
}

// Make every entry of vec divisible by p^e by adding/subtracting multiples
// of p^r and q, while keeping the added multiples small. Specifically, for
// e>=2, an integer z can be made divisible by p^e via
//   z' = z + u * p^r + v*p^r * q,
// with
//   |u|<=ceil(alpha p^{r(e-1)}/2) and |v|<=0.5+floor(beta p^{r(e-1)}/2),
// for any alpha+beta=1. We assume that r<e and that q-1 is divisible by p^e.
// Returns the largest absolute values of the u's and the new entries.
//
// This code is more general than we need, for bootstrapping we will always
// have e>r.
template<class VecInt>
long makeDivisible(VecInt& vec, long p2e, long p2r, long q, double alpha)
{
  assert(((p2e % p2r == 0) && (q % p2e == 1)) ||
	 ((p2r % p2e == 0) && (q % p2r == 1)));

  long maxU =0;
  ZZ maxZ;
  for (long i=0; i<vec.length(); i++) {
    ZZ z, z2; conv(z, vec[i]);
    long u=0, v=0;

    long zMod1=0, zMod2=0;
    if (p2r < p2e && alpha>0) {
      zMod1 = rem(z,p2r);
      if (zMod1 > p2r/2) zMod1 -= p2r; // map to the symmetric interval

      // make z divisible by p^r by adding a multiple of q
      z2 = z - to_ZZ(zMod1)*q;
      zMod2 = rem(z2,p2e); // z mod p^e, still divisible by p^r
      if (zMod2 > p2e/2) zMod2 -= p2e; // map to the symmetric interval
      zMod2 /= -p2r; // now z+ p^r*zMod2=0 (mod p^e) and |zMod2|<=p^{r(e-1)}/2

      u = ceil(alpha * zMod2);
      v = zMod2 - u; // = floor((1-alpha) * zMod2)
      z = z2 + u*p2r + to_ZZ(q)*v*p2r;
    }
    else { // r >= e or alpha==0, use only mulitples of q
      zMod1 = rem(z,p2e);
      if (zMod1 > p2e/2) zMod1 -= p2e; // map to the symmetric interval
      z -= to_ZZ(zMod1) * q;
    }
    if (abs(u) > maxU) maxU = abs(u);
    if (abs(z) > maxZ) maxZ = abs(z);

    if (rem(z,p2e) != 0) { // sanity check
      cerr << "**error: original z["<<i<<"]=" << vec[i]
	   << std::dec << ", p^r="<<p2r << ", p^e="<<p2e << endl;
      cerr << "z' = z - "<<zMod1<<"*q = "<< z2<<endl;
      cerr << "z''=z' +" <<u<<"*p^r +"<<v<<"*p^r*q = "<<z<<endl;
      exit(1);
    }
    conv(vec[i], z); // convert back to native format
  }
  return maxU;
}
// explicit instantiation for vec_ZZ and vec_long
template long makeDivisible<vec_ZZ>(vec_ZZ& v, long p2e,
				    long p2r, long q, double alpha);
// template long makeDivisible<vec_long>(vec_long& v, long p2e,
// 				      long p2r, long q, double alpha);

#ifdef FHE_BOOT_THREADS

// Extract digits from fully packed slots, multithreaded version
void extractDigitsPacked(Ctxt& ctxt, long botHigh, long r, long ePrime,
			 const vector<ZZX>& unpackSlotEncoding)
{
  FHE_TIMER_START;

  // Step 1: unpack the slots of ctxt
  FHE_NTIMER_START(unpack);
  ctxt.cleanUp();

  // Apply the d automorphisms and store them in scratch area
  long d = ctxt.getContext().zMStar.getOrdP();

  vector<Ctxt> unpacked(d, Ctxt(ZeroCtxtLike, ctxt));
  { // explicit scope to force all temporaries to be released
    vector< shared_ptr<DoubleCRT> > coeff_vector;
    coeff_vector.resize(d);

    FHE_NTIMER_START(unpack1);
    for (long i = 0; i < d; i++)
      coeff_vector[i] = shared_ptr<DoubleCRT>(new
        DoubleCRT(unpackSlotEncoding[i], ctxt.getContext(), ctxt.getPrimeSet()) );
    FHE_NTIMER_STOP(unpack1);

    FHE_NTIMER_START(unpack2);
    vector<Ctxt> frob(d, Ctxt(ZeroCtxtLike, ctxt));

    NTL_EXEC_RANGE(d, first, last)
    // FIXME: implement using hoisting!
        for (long j = first; j < last; j++) { // process jth Frobenius
          frob[j] = ctxt;
          frob[j].frobeniusAutomorph(j);
          frob[j].cleanUp();
          // FIXME: not clear if we should call cleanUp here
        }
    NTL_EXEC_RANGE_END

    FHE_NTIMER_STOP(unpack2);

    FHE_NTIMER_START(unpack3);
    Ctxt tmp1(ZeroCtxtLike, ctxt);
    for (long i = 0; i < d; i++) {
      for (long j = 0; j < d; j++) {
        tmp1 = frob[j];
        tmp1.multByConstant(*coeff_vector[mcMod(i+j, d)]);
        unpacked[i] += tmp1;
      }
    }
    FHE_NTIMER_STOP(unpack3);
  }
  FHE_NTIMER_STOP(unpack);

  // Step 2: extract the digits top-1,...,0 from the slots of unpacked[i]
  long p = ctxt.getContext().zMStar.getP();
  long p2r = power_long(p,r);
  long topHigh = botHigh + r-1;

#ifdef DEBUG_PRINTOUT
  cerr << "+ After unpack ";
  decryptAndPrint(cerr, unpacked[0], *dbgKey, *dbgEa, printFlag);
  cerr << "    extracting "<<(topHigh+1)<<" digits\n";
#endif

  if (p==2 && r>2)
    topHigh--; // For p==2 we sometime get a bit for free

  FHE_NTIMER_START(extractDigits);

  NTL_EXEC_RANGE(d, first, last)
      for (long i = first; i < last; i++) {
        vector<Ctxt> scratch;

        if (topHigh<=0) { // extracting LSB = no-op
          scratch.assign(1,unpacked[i]);
        } else {          // extract digits topHigh...0, store them in scratch
          extractDigits(scratch, unpacked[i], topHigh+1);
        }

        // set upacked[i] = -\sum_{j=botHigh}^{topHigh} scratch[j] * p^{j-botHigh}
        if (topHigh >= (long)scratch.size()) {
          topHigh = scratch.size() -1;
          cerr << " @ suspect: not enough digits in extractDigitsPacked\n";
        }

        unpacked[i] = scratch[topHigh];
        for (long j=topHigh-1; j>=botHigh; --j) {
          unpacked[i].multByP();
          unpacked[i] += scratch[j];
        }
        if (p==2 && botHigh>0) {   // For p==2, subtract also the previous bit
          //cerr << scratch.size() << " " <<  botHigh-1 << "\n";
          unpacked.at(i) += scratch.at(botHigh-1);
        }
        unpacked[i].negate();

        if (r>ePrime) {          // Add in digits from the bottom part, if any
          long topLow = r-1 - ePrime;
          Ctxt tmp = scratch[topLow];
          for (long j=topLow-1; j>=0; --j) {
    	tmp.multByP();
    	tmp += scratch[j];
          }
          if (ePrime>0)
    	tmp.multByP(ePrime); // multiply by p^e'
          unpacked[i] += tmp;
        }
        unpacked[i].reducePtxtSpace(p2r); // Our plaintext space is now mod p^r
      }
  NTL_EXEC_RANGE_END

  FHE_NTIMER_STOP(extractDigits);

#ifdef DEBUG_PRINTOUT
  cerr << "+ Before repack ";
  decryptAndPrint(cerr, unpacked[0], *dbgKey, *dbgEa, printFlag);
#endif

  // Step 3: re-pack the slots
  FHE_NTIMER_START(repack);
  const EncryptedArray& ea2 = *ctxt.getContext().ea;
  ZZX xInSlots;
  vector<ZZX> xVec(ea2.size());
  ctxt = unpacked[0];
  for (long i=1; i<d; i++) {
    x2iInSlots(xInSlots, i, xVec, ea2);
    unpacked[i].multByConstant(xInSlots);
    ctxt += unpacked[i];
  }
  FHE_NTIMER_STOP(repack);
#ifdef DEBUG_PRINTOUT
  cerr << "+ After repack ";
  decryptAndPrint(cerr, ctxt, *dbgKey, *dbgEa, printFlag);
#endif
}

#else

// Extract digits from fully packed slots
void extractDigitsPacked(Ctxt& ctxt, long botHigh, long r, long ePrime,
			 const vector<ZZX>& unpackSlotEncoding)
{
  FHE_TIMER_START;

  // Step 1: unpack the slots of ctxt
  FHE_NTIMER_START(unpack);
  ctxt.cleanUp();

  // Apply the d automorphisms and store them in scratch area
  long d = ctxt.getContext().zMStar.getOrdP();

  vector<Ctxt> scratch; // used below
  vector<Ctxt> unpacked(d, Ctxt(ZeroCtxtLike, ctxt));
  { // explicit scope to force all temporaries to be released
    vector< shared_ptr<DoubleCRT> > coeff_vector;
    coeff_vector.resize(d);
    for (long i = 0; i < d; i++)
      coeff_vector[i] = shared_ptr<DoubleCRT>(new
        DoubleCRT(unpackSlotEncoding[i], ctxt.getContext(), ctxt.getPrimeSet()) );
    Ctxt tmp1(ZeroCtxtLike, ctxt);
    Ctxt tmp2(ZeroCtxtLike, ctxt);

    // FIXME: implement using hoisting!
    for (long j = 0; j < d; j++) { // process jth Frobenius
      tmp1 = ctxt;
      tmp1.frobeniusAutomorph(j);
      tmp1.cleanUp();
      // FIXME: not clear if we should call cleanUp here

      for (long i = 0; i < d; i++) {
        tmp2 = tmp1;
        tmp2.multByConstant(*coeff_vector[mcMod(i+j, d)]);
        unpacked[i] += tmp2;
      }
    }
  }
  FHE_NTIMER_STOP(unpack);

  // Step 2: extract the digits top-1,...,0 from the slots of unpacked[i]
  long p = ctxt.getContext().zMStar.getP();
  long p2r = power_long(p,r);
  long topHigh = botHigh + r-1;

#ifdef DEBUG_PRINTOUT
  cerr << "+ After unpack ";
  decryptAndPrint(cerr, unpacked[0], *dbgKey, *dbgEa, printFlag);
  cerr << "    extracting "<<(topHigh+1)<<" digits\n";
#endif

  if (p==2 && r>2)
    topHigh--; // For p==2 we sometime get a bit for free

  FHE_NTIMER_START(extractDigits);
  for (long i=0; i<(long)unpacked.size(); i++) {
    if (topHigh<=0) { // extracting LSB = no-op
      scratch.assign(1,unpacked[i]);
    } else {          // extract digits topHigh...0, store them in scratch
      extractDigits(scratch, unpacked[i], topHigh+1);
    }

    // set upacked[i] = -\sum_{j=botHigh}^{topHigh} scratch[j] * p^{j-botHigh}
    if (topHigh >= (long)scratch.size()) {
      topHigh = scratch.size() -1;
      cerr << " @ suspect: not enough digits in extractDigitsPacked\n";
    }

    unpacked[i] = scratch[topHigh];
    for (long j=topHigh-1; j>=botHigh; --j) {
      unpacked[i].multByP();
      unpacked[i] += scratch[j];
    }
    if (p==2 && botHigh>0)   // For p==2, subtract also the previous bit
      unpacked[i] += scratch[botHigh-1];
    unpacked[i].negate();

    if (r>ePrime) {          // Add in digits from the bottom part, if any
      long topLow = r-1 - ePrime;
      Ctxt tmp = scratch[topLow];
      for (long j=topLow-1; j>=0; --j) {
	tmp.multByP();
	tmp += scratch[j];
      }
      if (ePrime>0)
	tmp.multByP(ePrime); // multiply by p^e'
      unpacked[i] += tmp;
    }
    unpacked[i].reducePtxtSpace(p2r); // Our plaintext space is now mod p^r
  }
  FHE_NTIMER_STOP(extractDigits);

#ifdef DEBUG_PRINTOUT
  cerr << "+ Before repack ";
  decryptAndPrint(cerr, unpacked[0], *dbgKey, *dbgEa, printFlag);
#endif

  // Step 3: re-pack the slots
  FHE_NTIMER_START(repack);
  const EncryptedArray& ea2 = *ctxt.getContext().ea;
  ZZX xInSlots;
  vector<ZZX> xVec(ea2.size());
  ctxt = unpacked[0];
  for (long i=1; i<d; i++) {
    x2iInSlots(xInSlots, i, xVec, ea2);
    unpacked[i].multByConstant(xInSlots);
    ctxt += unpacked[i];
  }
  FHE_NTIMER_STOP(repack);
}

#endif

// Use packed bootstrapping, so we can bootstrap all in just one go.
void packedRecrypt(const CtPtrs& cPtrs,
                   const std::vector<zzX>& unpackConsts,
                   const EncryptedArray& ea)
{
  FHEPubKey& pKey = (FHEPubKey&)cPtrs[0]->getPubKey();

  // Allocate temporary ciphertexts for the recryption
  int nPacked = divc(cPtrs.size(), ea.getDegree()); // ceil(totoalNum/d)
  std::vector<Ctxt> cts(nPacked, Ctxt(pKey));

  repack(CtPtrs_vectorCt(cts), cPtrs, ea);  // pack ciphertexts
  //  cout << "@"<< lsize(cts)<<std::flush;
  for (Ctxt& c: cts) {     // then recrypt them
    c.reducePtxtSpace(2);  // we only have recryption data for binary ctxt
#ifdef DEBUG_PRINTOUT
    ZZX ptxt;
    decryptAndPrint((cout<<"  before recryption "), c, *dbgKey, *dbgEa);
    dbgKey->Decrypt(ptxt, c);
    c.DummyEncrypt(ptxt);
    decryptAndPrint((cout<<"  after recryption "), c, *dbgKey, *dbgEa);
#else
    pKey.reCrypt(c);
#endif
  }
  unpack(cPtrs, CtPtrs_vectorCt(cts), ea, unpackConsts);
}

// recrypt all ctxt at level < belowLvl
void packedRecrypt(const CtPtrs& array,
                   const std::vector<zzX>& unpackConsts,
                   const EncryptedArray& ea, long belowLvl)
{
  std::vector<Ctxt*> v;
  for (long i=0; i<array.size(); i++)
    if ( array.isSet(i) && !array[i]->isEmpty()
         && array[i]->findBaseLevel()<belowLvl )
      v.push_back(array[i]);
  packedRecrypt(CtPtrs_vectorPt(v), unpackConsts, ea);
}
void packedRecrypt(const CtPtrMat& m,
                   const std::vector<zzX>& unpackConsts,
                   const EncryptedArray& ea, long belowLvl)
{
  std::vector<Ctxt*> v;
  for (long i=0; i<m.size(); i++)
    for (long j=0; j<m[i].size(); j++)
      if ( m[i].isSet(j) && !m[i][j]->isEmpty()
           && m[i][j]->findBaseLevel()<belowLvl )
        v.push_back(m[i][j]);
  packedRecrypt(CtPtrs_vectorPt(v), unpackConsts, ea);
}


/**
 * \file boost/numeric/ublasx/matrix_diagonal.hpp
 *
 * \brief Diagonal view of a matrix.
 *
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 *
 * <hr/>
 *
 * Copyright (c) 2009, Marco Guazzone
 *
 * Distributed under 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)
 */

#ifndef BOOST_NUMERIC_UBLASX_PROXY_MATRIX_DIAGONAL_HPP
#define BOOST_NUMERIC_UBLASX_PROXY_MATRIX_DIAGONAL_HPP

#include <algorithm>
#include <boost/mpl/if.hpp>
#include <boost/numeric/ublas/detail/config.hpp>
#include <boost/numeric/ublas/detail/temporary.hpp>
#include <boost/numeric/ublas/detail/vector_assign.hpp>
#include <boost/numeric/ublas/expression_types.hpp>
#include <boost/numeric/ublas/exception.hpp>
#include <boost/numeric/ublas/fwd.hpp>
#include <boost/numeric/ublas/traits.hpp>
//#include <boost/numeric/ublasx/traits/array_type.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_const.hpp>

namespace boost { namespace numeric { namespace ublasx {

using namespace ::boost::numeric::ublas;

/**
 * \brief Matrix based diagonal vector class
 * \tparam MatrixT A model of MatrixExpression.
 *
 * This class provides a view of a specific diagonal of a matrix of type
 * \a MatrixT.
 * The diagonal of the matrix is chosen during construction through the
 * parameter \c k passed to the constructor.
 * The parameter \c k has the following meaning:
 * - \c k = 0, the main diagonal is extracted;
 * - \c k > 0, the \c k-th diagonal above the main diagonal is extracted;
 * - \c k < 0, the \c k-th diagonal under the main diagonal is extracted;
 * .
 * Model of VectorExpression.
 *
 * \author Marco Guazzone, marco.guazzone@gmail.com
 */
template <typename MatrixT>
class matrix_diagonal: public vector_expression< matrix_diagonal<MatrixT> >
{
	public: class const_iterator;
	public: class iterator;

	private: typedef matrix_diagonal<MatrixT> self_type;
#ifdef BOOST_UBLAS_ENABLE_PROXY_SHORTCUTS
	public: using vector_expression<self_type>::operator();
#endif // BOOST_UBLAS_ENABLE_PROXY_SHORTCUTS
	public: typedef MatrixT matrix_type;
	public: typedef typename matrix_traits<MatrixT>::size_type size_type;
	public: typedef typename matrix_traits<MatrixT>::difference_type difference_type;
	public: typedef typename matrix_traits<MatrixT>::value_type value_type;
//	public: typedef typename array_type<MatrixT>::type array_type; // FIXME: not in matrix_traits
	public: typedef typename matrix_traits<MatrixT>::const_reference const_reference;
	public: typedef typename ::boost::mpl::if_<
									::boost::is_const<MatrixT>,
									typename matrix_traits<MatrixT>::const_reference,
									typename matrix_traits<MatrixT>::reference
							>::type reference;
	public: typedef typename ::boost::mpl::if_<
									::boost::is_const<MatrixT>,
									typename matrix_traits<MatrixT>::const_closure_type,
									typename matrix_traits<MatrixT>::closure_type
							>::type matrix_closure_type;
	public: typedef const self_type const_closure_type;
	public: typedef self_type closure_type;
	public: typedef typename storage_restrict_traits<
								typename matrix_traits<MatrixT>::storage_category,
								dense_proxy_tag
							>::storage_category storage_category;
	// Iterator types
	private: typedef typename matrix_traits<MatrixT>::const_iterator1 const_subiterator1_type;
	private: typedef typename matrix_traits<MatrixT>::const_iterator2 const_subiterator2_type;
	private: typedef typename ::boost::mpl::if_<
									::boost::is_const<MatrixT>,
									typename matrix_traits<MatrixT>::const_iterator1,
									typename matrix_traits<MatrixT>::iterator1
							>::type subiterator1_type;
	private: typedef typename ::boost::mpl::if_<
									::boost::is_const<MatrixT>,
									typename matrix_traits<MatrixT>::const_iterator2,
									typename matrix_traits<MatrixT>::iterator2
							>::type subiterator2_type;
	// Reverse iterator
	public: typedef reverse_iterator_base<const_iterator> const_reverse_iterator;
	public: typedef reverse_iterator_base<iterator> reverse_iterator;

	//@{ Construction and destruction

	public: BOOST_UBLAS_INLINE
		matrix_diagonal(matrix_type& data, difference_type k)
			: data_(data),
			  k_(k),
			  r_(k < 0 ? -k : 0),
			  c_(k > 0 ?  k : 0)
	{
		// Early checking of preconditions here.
		// BOOST_UBLAS_CHECK(r_ < data_.size1() && c_ < data_.size2(), bad_index());
	}

	public: BOOST_UBLAS_INLINE
		matrix_diagonal(matrix_closure_type const& data, difference_type k, int)
			: data_(data),
			  k_(k),
			  r_(k < 0 ? -k : 0),
			  c_(k > 0 ?  k : 0)
	{
		// Early checking of preconditions here.
		// BOOST_UBLAS_CHECK(r_ < data_.size1() && c_ < data_.size2(), bad_index());
	}

//	public: template <typename ExprT>
//		BOOST_UBLAS_INLINE
//		matrix_diagonal(matrix_expression<ExprT>& me, difference_type k)
//			: data_(me()),
//			  k_(k),
//			  r_(k < 0 ? -k : 0),
//			  c_(k > 0 ?  k : 0)
//	{
//		// Early checking of preconditions here.
//		// BOOST_UBLAS_CHECK(r_ < data_.size1() && c_ < data_.size2(), bad_index());
//	}

	//@} Construction and destruction

	//@{ Accessors

	public: BOOST_UBLAS_INLINE
		size_type size() const
	{
		if (k_ > 0)
		{
			return ::std::min(data_.size1(), data_.size2() - c_);
		}
		return ::std::min(data_.size1() - r_, data_.size2());
	}

	public: BOOST_UBLAS_INLINE
		difference_type offset() const
	{
		return k_;
	}

	//@} Accessors

	//@{ Storage accessors

	public: BOOST_UBLAS_INLINE
		matrix_closure_type const& data() const
	{
		return data_;
	}

	public: BOOST_UBLAS_INLINE
		matrix_closure_type& data()
	{
		return data_;
	}

	//@} Storage accessors

	//@{ Element access

#ifndef BOOST_UBLASX_PROXY_CONST_MEMBER
	public: BOOST_UBLAS_INLINE
		const_reference operator()(size_type j) const
	{
		return data_(j+r_, j+c_);
	}

	public: BOOST_UBLAS_INLINE
		reference operator()(size_type j)
	{
		return data_(j+r_, j+c_);
	}

	public: BOOST_UBLAS_INLINE
		const_reference operator[](size_type j) const
	{
		return (*this)(j);
	}

	public: BOOST_UBLAS_INLINE
		reference operator[](size_type j)
	{
		return (*this)(j);
	}
#else
	public: BOOST_UBLAS_INLINE
		reference operator()(size_type j) const
	{
		return data_(j+r_, j+c_);
	}

	public: BOOST_UBLAS_INLINE
		reference operator[](size_type j) const
	{
		return (*this)(j);
	}
#endif // BOOST_UBLASX_PROXY_CONST_MEMBER

	//@} Element access

	//@{ Assignment

	public: BOOST_UBLAS_INLINE
		matrix_diagonal& operator=(matrix_diagonal const& md)
	{
		// ISSUE need a temporary, proxy can be overlaping alias
		vector_assign<scalar_assign>(
			*this,
			typename vector_temporary_traits<matrix_type>::type(md)
		);
		return *this;
	}

	public: BOOST_UBLAS_INLINE
		matrix_diagonal& assign_temporary(matrix_diagonal& md)
	{
		// assign elements, proxied container remains the same
		vector_assign<scalar_assign>(*this, md);
		return *this;
	}

	public: template<class AE>
		BOOST_UBLAS_INLINE
		matrix_diagonal& operator=(vector_expression<AE> const& ae)
	{
		vector_assign<scalar_assign>(
			*this,
			typename vector_temporary_traits<matrix_type>::type(ae)
		);
		return *this;
	}

	public: template<class AE>
	BOOST_UBLAS_INLINE
	matrix_diagonal& assign(vector_expression<AE> const& ae)
	{
		vector_assign<scalar_assign>(*this, ae);
		return *this;
	}

	public: template<class AE>
		BOOST_UBLAS_INLINE
		matrix_diagonal& operator+=(vector_expression<AE> const& ae)
	{
		vector_assign<scalar_assign>(
			*this,
			typename vector_temporary_traits<matrix_type>::type(*this + ae)
		);
		return *this;
	}

	public: template<class AE>
		BOOST_UBLAS_INLINE
		matrix_diagonal& plus_assign(vector_expression<AE> const& ae)
	{
		vector_assign<scalar_plus_assign>(*this, ae);
		return *this;
	}

	public: template<class AE>
		BOOST_UBLAS_INLINE
		matrix_diagonal& operator-=(vector_expression<AE> const& ae)
	{
		vector_assign<scalar_assign>(
			*this,
			typename vector_temporary_traits<matrix_type>::type(*this - ae)
		);
		return *this;
	}

	public: template<class AE>
		BOOST_UBLAS_INLINE
		matrix_diagonal &minus_assign(vector_expression<AE> const& ae)
	{
		vector_assign<scalar_minus_assign>(*this, ae);
		return *this;
	}

	public: template<class AT>
		BOOST_UBLAS_INLINE
		matrix_diagonal &operator*=(AT const& at)
	{
		vector_assign_scalar<scalar_multiplies_assign>(*this, at);
		return *this;
	}

	public: template<class AT>
		BOOST_UBLAS_INLINE
		matrix_diagonal &operator/=(AT const& at)
	{
		vector_assign_scalar<scalar_divides_assign>(*this, at);
		return *this;
	}

	//@} Assignment

	//@{ Closure comparison

	public: BOOST_UBLAS_INLINE
		bool same_closure(matrix_diagonal const& mr) const
	{
		return this->data_.same_closure(mr.data_);
	}

	//@} Closure comparison

	//@{ Comparison

	public: BOOST_UBLAS_INLINE
		bool operator==(matrix_diagonal const& mr) const
	{
		return this->data_ == mr.data_ && offset() == mr.offset();
	}

	//@} Comparison

	//@{ Swapping

	public: BOOST_UBLAS_INLINE
		void swap(matrix_diagonal mr) //FIXME: why not pass-by-reference?
	{
		if (this != &mr)
		{
			BOOST_UBLAS_CHECK(size() == mr.size(), bad_size());
			// Sparse ranges may be nonconformant now.
			// std::swap_ranges (begin(), end(), mr.begin());
			vector_swap<scalar_swap>(*this, mr);
		}
	}

	public: BOOST_UBLAS_INLINE
		friend
		void swap(matrix_diagonal mr1, matrix_diagonal mr2) //FIXME: why not pass-by-reference?
	{
		mr1.swap(mr2);
	}

	//@} Swapping

	//@{ Iterators

	public: BOOST_UBLAS_INLINE
		const_iterator find(size_type j) const
	{
		const_subiterator1_type it1(data_.find1(2, j+r_, c_));
		const_subiterator2_type it2(data_.find2(1, r_, j+c_));

		return const_iterator(*this, it1, it2);
	}

	public: BOOST_UBLAS_INLINE
		iterator find(size_type j)
	{
		subiterator1_type it1(data_.find1(2, j+r_, c_));
		subiterator2_type it2(data_.find2(1, r_, j+c_));

		return iterator(*this, it1, it2);
	}

	public: BOOST_UBLAS_INLINE
		const_iterator begin() const
	{
		return find(0);
	}

	public: BOOST_UBLAS_INLINE
		const_iterator end() const
	{
		return find(size());
	}

	public: BOOST_UBLAS_INLINE
		iterator begin()
	{
		return find(0);
	}

	public: BOOST_UBLAS_INLINE
		iterator end()
	{
		return find(size());
	}

	public: BOOST_UBLAS_INLINE
		const_reverse_iterator rbegin() const
	{
		return const_reverse_iterator(end());
	}

	public: BOOST_UBLAS_INLINE
		const_reverse_iterator rend() const
	{
		return const_reverse_iterator(begin());
	}

	public: BOOST_UBLAS_INLINE
		reverse_iterator rbegin()
	{
		return reverse_iterator(end());
	}

	public: BOOST_UBLAS_INLINE
		reverse_iterator rend()
	{
		return reverse_iterator(begin());
	}

	public: class const_iterator: 	public container_const_reference<matrix_diagonal>,
									public iterator_base_traits<typename const_subiterator1_type::iterator_category>::template iterator_base<const_iterator, value_type>::type
	{
		public: typedef typename const_subiterator1_type::value_type value_type;
		public: typedef typename const_subiterator1_type::difference_type difference_type;
		public: typedef typename const_subiterator1_type::reference reference;
		public: typedef typename const_subiterator1_type::pointer pointer;

		// Iterators cannot be different
		BOOST_STATIC_ASSERT((
			::boost::is_same<
					typename MatrixT::const_iterator1::iterator_category,
					typename MatrixT::const_iterator2::iterator_category
			>::value
		));

		// Construction and destruction
		public: BOOST_UBLAS_INLINE
			const_iterator()
			: container_const_reference<self_type>(),
			  it1_(),
			  it2_()
		{
		}

		public: BOOST_UBLAS_INLINE
			const_iterator(self_type const& mr, const_subiterator1_type const& it1, const_subiterator2_type const& it2)
			: container_const_reference<self_type>(mr),
			  it1_(it1),
			  it2_(it2)
		{
		}

		public: BOOST_UBLAS_INLINE
			const_iterator(typename self_type::iterator const& it)  // ISSUE self_type:: stops VC8 using std::iterator here
			: container_const_reference<self_type>(it()),
			  it1_(it.it1_),
			  it2_(it.it2_)
		{
		}

		// Arithmetic
		public: BOOST_UBLAS_INLINE
			const_iterator& operator++()
		{
			++it1_;
			++it2_;
			return *this;
		}

		public: BOOST_UBLAS_INLINE
			const_iterator& operator--()
		{
			--it1_;
			--it2_;
			return *this;
		}

		public: BOOST_UBLAS_INLINE
			const_iterator& operator+=(difference_type n)
		{
			it1_ += n;
			it1_ += n;
			return *this;
		}

		public: BOOST_UBLAS_INLINE
			const_iterator& operator-=(difference_type n)
		{
			it1_ -= n;
			it2_ -= n;
			return *this;
		}

		public: BOOST_UBLAS_INLINE
			difference_type operator-(const_iterator const& it) const
		{
			BOOST_UBLAS_CHECK((*this)().same_closure (it()), external_logic());
			return BOOST_UBLAS_SAME(it1_ - it.it1_, it2_ - it.it2_);
		}

		// Dereference
		public: BOOST_UBLAS_INLINE
			const_reference operator*() const
		{
			return (*this)().data_(it1_.index1(), it2_.index2());
		}

		public: BOOST_UBLAS_INLINE
			const_reference operator[](difference_type n) const
		{
			return *(*this + n);
		}

		// Index
		public: BOOST_UBLAS_INLINE
			size_type index() const
		{
			if (it1_.index1() > it2_.index2())
			{
				return it2_.index2();
			}

			return it1_.index1();
		}

		// Assignment
		public: BOOST_UBLAS_INLINE
			const_iterator& operator=(const_iterator const& it)
		{
			container_const_reference<self_type>::assign(&it());
			it1_ = it.it1_;
			it2_ = it.it2_;
			return *this;
		}

		// Comparison
		public: BOOST_UBLAS_INLINE
			bool operator==(const_iterator const& it) const
		{
			BOOST_UBLAS_CHECK((*this)().same_closure(it()), external_logic());
			return it1_ == it.it1_ && it2_ == it.it2_;
		}

		public: BOOST_UBLAS_INLINE
			bool operator<(const_iterator const& it) const
		{
			BOOST_UBLAS_CHECK((*this)().same_closure(it()), external_logic());
			return it1_ < it.it1_ && it2_ < it.it2_;
		}

		private: const_subiterator1_type it1_;
		private: const_subiterator2_type it2_;
	};

	public: class iterator: 	public container_reference<matrix_diagonal>,
								public iterator_base_traits<typename subiterator1_type::iterator_category>::template iterator_base<iterator, value_type>::type
	{
		public: typedef typename subiterator1_type::value_type value_type;
		public: typedef typename subiterator1_type::difference_type difference_type;
		public: typedef typename subiterator1_type::reference reference;
		public: typedef typename subiterator1_type::pointer pointer;

		// Iterators cannot be different
		BOOST_STATIC_ASSERT((
			::boost::is_same<
					typename MatrixT::iterator1::iterator_category,
					typename MatrixT::iterator2::iterator_category
			>::value
		));

		// Construction and destruction
		public: BOOST_UBLAS_INLINE
			iterator ()
			: container_reference<self_type>(),
			  it1_(),
			  it2_()
		{
		}

		public: BOOST_UBLAS_INLINE
			iterator(self_type& mr, subiterator1_type const& it1, subiterator2_type const& it2)
			: container_reference<self_type>(mr),
			  it1_(it1),
			  it2_(it2)
		{
		}

		// Arithmetic
		public: BOOST_UBLAS_INLINE
			iterator &operator++()
		{
			++it1_;
			++it2_;
			return *this;
		}

		public: BOOST_UBLAS_INLINE
			iterator& operator--()
		{
			--it1_;
			--it2_;
			return *this;
		}

		public: BOOST_UBLAS_INLINE
			iterator& operator+=(difference_type n)
		{
			it1_ += n;
			it2_ += n;
			return *this;
		}

		public: BOOST_UBLAS_INLINE
			iterator& operator-=(difference_type n)
		{
			it1_ -= n;
			it2_ -= n;
			return *this;
		}

		public: BOOST_UBLAS_INLINE
			difference_type operator-(iterator const& it) const
		{
			BOOST_UBLAS_CHECK((*this)().same_closure(it()), external_logic());
			return BOOST_UBLAS_SAME(it1_ - it.it1_, it2_ - it.it2_);
		}

		// Dereference
		public: BOOST_UBLAS_INLINE
			reference operator*() const
		{
			return (*this)().data_(it1_.index1(), it2_.index2());
		}

		public: BOOST_UBLAS_INLINE
			reference operator[](difference_type n) const
		{
			return *(*this + n);
		}

		// Index
		public: BOOST_UBLAS_INLINE
			size_type index() const
		{
			return BOOST_UBLAS_SAME(it1_.index1(),  it2_.index2());
		}

		// Assignment
		public: BOOST_UBLAS_INLINE
			iterator& operator=(iterator const& it)
		{
			container_reference<self_type>::assign(&it());
			it1_ = it.it1_;
			it2_ = it.it2_;
			return *this;
		}

		// Comparison
		public: BOOST_UBLAS_INLINE
			bool operator==(iterator const& it) const
		{
			BOOST_UBLAS_CHECK((*this)().same_closure(it()), external_logic());
			return it1_ == it.it1_ && it2_ == it.it2_;
		}

		public: BOOST_UBLAS_INLINE
			bool operator<(iterator const& it) const
		{
			BOOST_UBLAS_CHECK((*this)().same_closure(it()), external_logic());
			return it1_ < it.it1_ && it2_ < it.it2_;
		}

		private: subiterator1_type it1_;
		private: subiterator2_type it2_;
		private: friend class const_iterator; // See the const_interator(iterator const&) constructor
	};

	//@} Iterators

	//@{ Data members

	private: matrix_closure_type data_; ///< The underlying matrix.
	private: difference_type k_; ///< Offset from the main diagonal.
	private: size_type r_; ///< Offset from the row of the main diagonal.
	private: size_type c_; ///< Offset from the column of the main diagonal.

	//@} Data members
};

} // Namespace ublasx

namespace ublas {

// Specialize temporary traits

template <typename MatrixT>
struct vector_temporary_traits< ::boost::numeric::ublasx::matrix_diagonal<MatrixT> >: public vector_temporary_traits<MatrixT> {};

template <typename  MatrixT>
struct vector_temporary_traits< const ::boost::numeric::ublasx::matrix_diagonal<MatrixT> >: public vector_temporary_traits<MatrixT> {};

} // Namespace ublas

}} // Namespace boost::numeric

#endif // BOOST_NUMERIC_UBLASX_PROXY_MATRIX_DIAGONAL_HPP


/*=============================================================================
    Copyright (c) 2001-2014 Joel de Guzman

    Distributed under 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)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
//  Yet another calculator example! This time, we will compile to a simple
//  virtual machine. This is actually one of the very first Spirit example
//  circa 2000. Now, it's ported to Spirit2 (and X3).
//
//  [ JDG Sometime 2000 ]       pre-boost
//  [ JDG September 18, 2002 ]  spirit1
//  [ JDG April 8, 2007 ]       spirit2
//  [ JDG February 18, 2011 ]   Pure attributes. No semantic actions.
//  [ JDG April 9, 2014 ]       Spirit X3 (from qi calc6)
//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Uncomment this if you want to enable debugging
//#define BOOST_SPIRIT_X3_DEBUG

#if defined(_MSC_VER)
# pragma warning(disable: 4345)
#endif

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include <iostream>
#include <string>
#include <list>
#include <numeric>

namespace x3 = boost::spirit::x3;

namespace client { namespace ast
{
    ///////////////////////////////////////////////////////////////////////////
    //  The AST
    ///////////////////////////////////////////////////////////////////////////
    struct nil {};
    struct signed_;
    struct expression;

    struct operand : x3::variant<
            nil
          , unsigned int
          , x3::forward_ast<signed_>
          , x3::forward_ast<expression>
        >
    {
        using base_type::base_type;
        using base_type::operator=;
    };

    struct signed_
    {
        char sign;
        operand operand_;
    };

    struct operation
    {
        char operator_;
        operand operand_;
    };

    struct expression
    {
        operand first;
        std::list<operation> rest;
    };

    // print function for debugging
    inline std::ostream& operator<<(std::ostream& out, nil) { out << "nil"; return out; }
}}

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::signed_,
    (char, sign)
    (client::ast::operand, operand_)
)

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::operation,
    (char, operator_)
    (client::ast::operand, operand_)
)

BOOST_FUSION_ADAPT_STRUCT(
    client::ast::expression,
    (client::ast::operand, first)
    (std::list<client::ast::operation>, rest)
)

namespace client
{
    ///////////////////////////////////////////////////////////////////////////
    //  The Virtual Machine
    ///////////////////////////////////////////////////////////////////////////
    enum byte_code
    {
        op_neg,     //  negate the top stack entry
        op_add,     //  add top two stack entries
        op_sub,     //  subtract top two stack entries
        op_mul,     //  multiply top two stack entries
        op_div,     //  divide top two stack entries
        op_int,     //  push constant integer into the stack
    };

    class vmachine
    {
    public:

        vmachine(unsigned stackSize = 4096)
          : stack(stackSize)
          , stack_ptr(stack.begin())
        {
        }

        int top() const { return stack_ptr[-1]; };
        void execute(std::vector<int> const& code);

    private:

        std::vector<int> stack;
        std::vector<int>::iterator stack_ptr;
    };

    void vmachine::execute(std::vector<int> const& code)
    {
        std::vector<int>::const_iterator pc = code.begin();
        stack_ptr = stack.begin();

        while (pc != code.end())
        {
            switch (*pc++)
            {
                case op_neg:
                    stack_ptr[-1] = -stack_ptr[-1];
                    break;

                case op_add:
                    --stack_ptr;
                    stack_ptr[-1] += stack_ptr[0];
                    break;

                case op_sub:
                    --stack_ptr;
                    stack_ptr[-1] -= stack_ptr[0];
                    break;

                case op_mul:
                    --stack_ptr;
                    stack_ptr[-1] *= stack_ptr[0];
                    break;

                case op_div:
                    --stack_ptr;
                    stack_ptr[-1] /= stack_ptr[0];
                    break;

                case op_int:
                    *stack_ptr++ = *pc++;
                    break;
            }
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    //  The Compiler
    ///////////////////////////////////////////////////////////////////////////
    struct compiler
    {
        typedef void result_type;

        std::vector<int>& code;
        compiler(std::vector<int>& code)
          : code(code) {}

        void operator()(ast::nil) const { BOOST_ASSERT(0); }
        void operator()(unsigned int n) const
        {
            code.push_back(op_int);
            code.push_back(n);
        }

        void operator()(ast::operation const& x) const
        {
            boost::apply_visitor(*this, x.operand_);
            switch (x.operator_)
            {
                case '+': code.push_back(op_add); break;
                case '-': code.push_back(op_sub); break;
                case '*': code.push_back(op_mul); break;
                case '/': code.push_back(op_div); break;
                default: BOOST_ASSERT(0); break;
            }
        }

        void operator()(ast::signed_ const& x) const
        {
            boost::apply_visitor(*this, x.operand_);
            switch (x.sign)
            {
                case '-': code.push_back(op_neg); break;
                case '+': break;
                default: BOOST_ASSERT(0); break;
            }
        }

        void operator()(ast::expression const& x) const
        {
            boost::apply_visitor(*this, x.first);
            for (ast::operation const& oper : x.rest)
            {
                (*this)(oper);
            }
        }
    };

    ///////////////////////////////////////////////////////////////////////////////
    //  The calculator grammar
    ///////////////////////////////////////////////////////////////////////////////
    namespace calculator_grammar
    {
        using x3::uint_;
        using x3::char_;

        struct expression_class;
        struct term_class;
        struct factor_class;

        x3::rule<expression_class, ast::expression> const expression("expression");
        x3::rule<term_class, ast::expression> const term("term");
        x3::rule<factor_class, ast::operand> const factor("factor");

        auto const expression_def =
            term
            >> *(   (char_('+') > term)
                |   (char_('-') > term)
                )
            ;

        auto const term_def =
            factor
            >> *(   (char_('*') > factor)
                |   (char_('/') > factor)
                )
            ;

        auto const factor_def =
                uint_
            |   '(' > expression > ')'
            |   (char_('-') > factor)
            |   (char_('+') > factor)
            ;

        BOOST_SPIRIT_DEFINE(
            expression = expression_def
          , term = term_def
          , factor = factor_def
        );

        struct expression_class
        {
            //  Our error handler
            template <typename Iterator, typename Exception, typename Context>
            x3::error_handler_result
            on_error(Iterator&, Iterator const& last, Exception const& x, Context const& context)
            {
                std::cout
                    << "Error! Expecting: "
                    << x.which()
                    << " here: \""
                    << std::string(x.where(), last)
                    << "\""
                    << std::endl
                    ;
                return x3::error_handler_result::fail;
            }
        };

        auto calculator = expression;
    }

    using calculator_grammar::calculator;
}

///////////////////////////////////////////////////////////////////////////////
//  Main program
///////////////////////////////////////////////////////////////////////////////
int
main()
{
    std::cout << "/////////////////////////////////////////////////////////\n\n";
    std::cout << "Expression parser...\n\n";
    std::cout << "/////////////////////////////////////////////////////////\n\n";
    std::cout << "Type an expression...or [q or Q] to quit\n\n";

    typedef std::string::const_iterator iterator_type;
    typedef client::ast::expression ast_expression;
    typedef client::compiler compiler;

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

        client::vmachine mach;              // Our virtual machine
        std::vector<int> code;              // Our VM code
        auto& calc = client::calculator;    // Our grammar
        ast_expression expression;          // Our program (AST)
        compiler compile(code);             // Compiles the program

        std::string::const_iterator iter = str.begin();
        std::string::const_iterator end = str.end();
        boost::spirit::x3::ascii::space_type space;
        bool r = phrase_parse(iter, end, calc, space, expression);

        if (r && iter == end)
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded\n";
            compile(expression);
            mach.execute(code);
            std::cout << "\nResult: " << mach.top() << std::endl;
            std::cout << "-------------------------\n";
        }
        else
        {
            std::string rest(iter, end);
            std::cout << "-------------------------\n";
            std::cout << "Parsing failed\n";
            std::cout << "-------------------------\n";
        }
    }

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


/********************************************************************************
 * Copyright 2015 The Robotics Group, The Maersk Mc-Kinney Moller Institute,
 * Faculty of Engineering, University of Southern Denmark
 *
 * 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 RWLIBS_MATHEMATICA_RAWARRAY_HPP_
#define RWLIBS_MATHEMATICA_RAWARRAY_HPP_

/**
 * @file RawArray.hpp
 *
 * \copydoc rwlibs::mathematica::RawArray
 */

#include "List.hpp"
#include "Mathematica.hpp"

#include <rw/core/Ptr.hpp>
#include <rw/core/macros.hpp>

#include <boost/multi_array.hpp>

namespace rwlibs { namespace mathematica {
    //! @addtogroup mathematica

    //! @{
    //! @brief Utility for the RawArray type.
    class RawArrayUtil
    {
      public:
        /**
         * @brief Detect the dimensions of an array given as an expression.
         * @param exp [in] the expression.
         * @return the dimensions.
         */
        static std::size_t detectDimensions (const Mathematica::FunctionBase& exp);

        /**
         * @brief Get the type of array.
         * @return a symbol with the data type.
         */
        template< typename T > static Mathematica::Symbol::Ptr getType ()
        {
            RW_THROW ("Could not determine type for RawArray.");
        }

      private:
        RawArrayUtil (){};
        virtual ~RawArrayUtil (){};
    };

    /**
     * @brief Construct Byte symbol from unsigned char.
     * @return Byte symbol.
     */
    template<> inline Mathematica::Symbol::Ptr RawArrayUtil::getType< unsigned char > ()
    {
        return rw::core::ownedPtr (new Mathematica::Symbol ("Byte"));
    }

    //! @brief Representation of a N-dimensional %Mathematica array with fixed depth.
    template< typename T, std::size_t Dim > class RawArray : public Mathematica::Array< T >
    {
      public:
        //! @brief Smart pointer type.
        typedef rw::core::Ptr< RawArray< T, Dim > > Ptr;

        //! @brief The underlying array type.
        typedef boost::multi_array< T, Dim > ArrayType;

        /**
         * @brief Construct new array.
         * @param array [in] the array.
         */
        RawArray (const ArrayType& array) : _array (array), _size (new int[Dim])
        {
            for (std::size_t i = 0; i < Dim; i++) {
                _size[i] = _array.shape ()[i];
            }
            _type = RawArrayUtil::getType< T > ();
        }

        /**
         * @brief Construct new array with specific shape.
         * @param shape [in] the shape.
         */
        RawArray (boost::array< typename ArrayType::index, Dim > shape) :
            _array (shape), _size (new int[Dim])
        {
            for (std::size_t i = 0; i < Dim; i++) {
                _size[i] = shape[i];
            }
            _type = RawArrayUtil::getType< T > ();
        }

        /**
         * @brief Construct new array with specific shape.
         * @param shape [in] the shape.
         */
        RawArray (const std::vector< std::size_t > shape) : _size (new int[Dim])
        {
            for (std::size_t i = 0; i < Dim; i++) {
                _size[i] = shape[i];
            }
            boost::array< typename ArrayType::index, Dim > bshape;
            typename boost::array< typename ArrayType::index, Dim >::size_type i;
            for (i = 0; i < Dim; i++) {
                bshape[i] = shape[i];
            }
            _array.resize (bshape);

            _type = RawArrayUtil::getType< T > ();
            // boost::array<typename ArrayType::index,Dim> cur;
            //_list = createList(_array,0,cur,size());
        }

        /**
         * @brief Construct RawArray from native Mathematica array.
         * @param data [in] the data.
         * @param shape [in] the shape.
         */
        RawArray (const T* const data, const int* const shape) : _size (new int[Dim])
        {
            for (std::size_t i = 0; i < Dim; i++) {
                _size[i] = shape[i];
            }
            boost::array< typename ArrayType::index, Dim > bshape;
            typename boost::array< typename ArrayType::index, Dim >::size_type i;
            int elements = 0;
            for (i = 0; i < Dim; i++) {
                bshape[i] = shape[i];
                if (i == 0)
                    elements = shape[0];
                else
                    elements *= shape[i];
            }
            _array.resize (bshape);
            std::copy (data, data + elements, _array.data ());

            _type = RawArrayUtil::getType< T > ();
        }

        //! @brief Destructor.
        virtual ~RawArray () { delete[] _size; }

        /**
         * @brief Get the underlying array.
         * @return a reference to the array.
         */
        const ArrayType& getArray () const { return _array; }

        /**
         * @brief Set a value.
         * @param indexes [in] the indices.
         * @param value [in] the value to set.
         */
        void set (std::vector< std::size_t > indexes, T value)
        {
            RW_ASSERT (indexes.size () == Dim);
            boost::array< typename ArrayType::index, Dim > cur;
            for (std::size_t i = 0; i < Dim; i++)
                cur[i] = indexes[i];
            _array (cur) = value;
        }

        /**
         * @brief Construct a new array from an expression.
         * @param exp [in] the expression to parse.
         * @return a new array.
         */
        static RawArray< T, Dim >::Ptr fromExpression (const Mathematica::FunctionBase& exp)
        {
            if (exp.getName () != "RawArray")
                RW_THROW ("Expected function with name RawArray, not " << exp.getName () << ".");
            const std::list< rw::core::Ptr< const Mathematica::Expression > > args =
                exp.getArguments ();
            if (args.size () != 2)
                RW_THROW ("Expected two arguments for RawArray, not " << args.size () << ".");

            // Find the shape
            boost::array< typename ArrayType::index, Dim > shape;
            rw::core::Ptr< const Mathematica::Expression > arrayArg = args.back ();
            rw::core::Ptr< const Mathematica::FunctionBase > fct;
            bool cont                                                              = true;
            typename boost::array< typename ArrayType::index, Dim >::size_type dim = 0;
            while (cont) {
                cont = false;
                fct  = arrayArg.cast< const Mathematica::FunctionBase > ();
                if (!fct.isNull ()) {
                    const std::size_t args = fct->getArguments ().size ();
                    if (args > 0) {
                        shape[dim] = args;
                        dim++;
                        arrayArg = fct->getArguments ().front ();
                        cont     = true;
                    }
                }
            }
            if (dim != Dim) {
                RW_THROW ("Dim appear to be wrong.");
            }

            arrayArg = args.back ();
            fct      = arrayArg.cast< const Mathematica::FunctionBase > ();
            const rw::core::Ptr< const List > list = List::fromExpression (*fct);

            RawArray< T, Dim >::Ptr array = rw::core::ownedPtr (new RawArray< T, Dim > (shape));
            const std::vector< std::size_t > size = array->getSize ();
            boost::array< typename ArrayType::index, Dim > cur;
            setValues (array->_array, list, 0, cur, size);
            return array;
        }

        /**
         * @brief Set the values of an array recursively.
         * @param array [in/out] the array.
         * @param list  [in] the list at the current level.
         * @param level [in] the current level.
         * @param cur [in] the current indices.
         * @param size [in] the shape of the array.
         */
        static void setValues (ArrayType& array, rw::core::Ptr< const List > list,
                               std::size_t level,
                               boost::array< typename ArrayType::index, Dim > cur,
                               const std::vector< std::size_t >& size)
        {
            const std::list< rw::core::Ptr< const Mathematica::Expression > >& args =
                list->getArguments ();
            std::list< rw::core::Ptr< const Mathematica::Expression > >::const_iterator it =
                args.begin ();
            if (level == Dim - 1) {
                for (std::size_t i = 0; i < size[level]; i++) {
                    cur[level]                                               = i;
                    const rw::core::Ptr< const Mathematica::Expression > exp = *it;
                    const rw::core::Ptr< const Mathematica::Integer > integer =
                        exp.cast< const Mathematica::Integer > ();
                    if (integer.isNull ())
                        RW_THROW ("Expected integer at this level of the array.");
                    array (cur) = (T) integer->value ();
                    it++;
                }
            }
            else {
                for (std::size_t i = 0; i < size[level]; i++) {
                    cur[level]                                               = i;
                    const rw::core::Ptr< const Mathematica::Expression > exp = *it;
                    const rw::core::Ptr< const Mathematica::FunctionBase > fct =
                        exp.cast< const Mathematica::FunctionBase > ();
                    if (fct.isNull ())
                        RW_THROW ("Expected function at this level of the array.");
                    if (fct->getName () != "List")
                        RW_THROW ("Expected function with name \"List\", instead got \""
                                  << fct->getName () << "\".");
                    const rw::core::Ptr< const List > listChild = fct.cast< const List > ();
                    RW_ASSERT (!listChild.isNull ());
                    setValues (array, listChild, level + 1, cur, size);
                    it++;
                }
            }
        }

        /**
         * @brief Create a List expression from an array.
         * @param array [in] the array.
         * @param level [in] the current level.
         * @param cur [in] the current indices.
         * @param size [in] the shape of the array.
         * @return a list.
         */
        static List::Ptr createList (const ArrayType& array, std::size_t level,
                                     boost::array< typename ArrayType::index, Dim > cur,
                                     const std::vector< std::size_t >& size)
        {
            const List::Ptr list = rw::core::ownedPtr (new List ());
            if (level == Dim - 1) {
                for (std::size_t i = 0; i < size[level]; i++) {
                    cur[level] = i;
                    list->add (rw::core::ownedPtr (new Mathematica::Integer (array (cur))));
                }
            }
            else {
                for (std::size_t i = 0; i < size[level]; i++) {
                    cur[level] = i;
                    list->add (createList (array, level + 1, cur, size));
                }
            }
            return list;
        }

        /**
         * @brief Get the shape.
         * @return a list of sizes for each dimension.
         */
        std::vector< std::size_t > getSize () const
        {
            std::vector< std::size_t > shape;
            for (std::size_t i = 0; i < Dim; i++)
                shape.push_back (_array.shape ()[i]);
            return shape;
        }

        //! @copydoc Mathematica::Expression::out
        virtual void out (std::ostream& stream) const
        {
            stream << "RawArray[" << _type->getName () << ", << ";
            for (std::size_t i = 0; i < Dim; i++) {
                stream << _array.shape ()[i];
                if (i != Dim - 1)
                    stream << "x";
            }
            stream << " array >>]";
        }

        //! @copydoc Mathematica::Expression::clone
        virtual Mathematica::Expression::Ptr clone () const
        {
            return rw::core::ownedPtr (new RawArray< T, Dim > (_array));
        }

        //! @copydoc Mathematica::Array::size
        virtual const int* size () const { return _size; }

        //! @copydoc Mathematica::Array::data
        virtual const T* data () const { return _array.data (); }

        //! @copydoc Mathematica::Array::dimensions
        virtual int dimensions () const { return Dim; }

      private:
        ArrayType _array;
        int* _size;
        Mathematica::Symbol::Ptr _type;
    };

    //! @brief Value for the size of a RawArray when size is not known at compile time.
    const int Dynamic = -1;

    //! @brief Representation of a N-dimensional %Mathematica array with dynamic depth.
    template< typename T > class RawArray< T, Dynamic > : public Mathematica::Array< T >
    {
      public:
        //! @brief Smart pointer type.
        typedef rw::core::Ptr< RawArray< T, Dynamic > > Ptr;

        /**
         * @brief Construct new array with a dynamic dimensionality.
         * @param data [in] the data.
         * @param dims [in] the size of each dimension.
         * @param depth [in] the number of dimensions.
         */
        RawArray (const T* const data, const int* const dims, const int depth) :
            _data (NULL), _dims (new int[depth]), _depth (depth)
        {
            int size = 0;
            if (depth > 0) {
                size = dims[0];
                for (int i = 1; i < depth; i++) {
                    size *= dims[i];
                }
            }
            _data = new unsigned char[size];
            std::copy (data, data + size, _data);
            std::copy (dims, dims + depth, _dims);
            _type = RawArrayUtil::getType< T > ();
        }

        //! @brief Destructor.
        virtual ~RawArray ()
        {
            delete[] _data;
            delete[] _dims;
        }

        //! @copydoc Mathematica::Expression::out
        void out (std::ostream& stream) const
        {
            stream << "RawArray[" << _type->getName () << ", << ";
            for (int i = 0; i < _depth; i++) {
                stream << _dims[i];
                if (i != _depth - 1)
                    stream << "x";
            }
            stream << " array >>]";
        }

        //! @copydoc Mathematica::Expression::clone
        Mathematica::Expression::Ptr clone () const
        {
            return rw::core::ownedPtr (new RawArray< T, Dynamic > (_data, _dims, _depth));
        }

        //! @copydoc Mathematica::Array::size
        const int* size () const { return _dims; }

        //! @copydoc Mathematica::Array::data
        const T* data () const { return _data; }

        //! @copydoc Mathematica::Array::dimensions
        int dimensions () const { return _depth; }

      private:
        T* _data;
        int* _dims;
        const int _depth;
        Mathematica::Symbol::Ptr _type;
    };

    //! @}
}}     // namespace rwlibs::mathematica
#endif /* RWLIBS_MATHEMATICA_RAWARRAY_HPP_ */