#include <NTL/ZZ_p.h>
#include <NTL/FFT.h>

NTL_START_IMPL

NTL_TLS_GLOBAL_DECL(SmartPtr<ZZ_pInfoT>, ZZ_pInfo_stg)
NTL_TLS_GLOBAL_DECL(SmartPtr<ZZ_pTmpSpaceT>, ZZ_pTmpSpace_stg)

NTL_CHEAP_THREAD_LOCAL ZZ_pInfoT *ZZ_pInfo = 0;
NTL_CHEAP_THREAD_LOCAL ZZ_pTmpSpaceT *ZZ_pTmpSpace = 0;
NTL_CHEAP_THREAD_LOCAL bool ZZ_pInstalled = false;

ZZ_pInfoT::ZZ_pInfoT(const ZZ& NewP)
{
   if (NewP <= 1) LogicError("ZZ_pContext: p must be > 1");

   p = NewP;
   size = p.size();

   ExtendedModulusSize = 2*size +
                 (NTL_BITS_PER_LONG + NTL_ZZ_NBITS - 1)/NTL_ZZ_NBITS;

}

// we use a lazy strategy for initializing and installing
// FFTInfo and TmpSpace related to a ZZ_p modulus.
// The routines GetFFTInfo and GetTmpSpace make sure this process
// is complete.

void ZZ_p::DoInstall()
{
   SmartPtr<ZZ_pTmpSpaceT> tmps;

   do { // NOTE: thread safe lazy init
      Lazy<ZZ_pFFTInfoT>::Builder builder(ZZ_pInfo->FFTInfo);
      if (!builder()) break;

      UniquePtr<ZZ_pFFTInfoT> FFTInfo;
      FFTInfo.make();

      ZZ B, M, M1, M2, M3;
      long n, i;
      long q, t;
      mulmod_t qinv;

      sqr(B, ZZ_pInfo->p);

      LeftShift(B, B, NTL_FFTMaxRoot+NTL_FFTFudge);

      // FIXME: the following is quadratic time...would
      // be nice to get a faster solution...
      // One could estimate the # of primes by summing logs,
      // then multiply using a tree-based multiply, then
      // adjust up or down...

      // Assuming IEEE floating point, the worst case estimate
      // for error guarantees a correct answer +/- 1 for
      // numprimes up to 2^25...for sure we won't be
      // using that many primes...we can certainly put in
      // a sanity check, though.

      // If I want a more accuaruate summation (with using Kahan,
      // which has some portability issues), I could represent
      // numbers as x = a + f, where a is integer and f is the fractional
      // part.  Summing in this representation introduces an *absolute*
      // error of 2 epsilon n, which is just as good as Kahan
      // for this application.

      // same strategy could also be used in the ZZX HomMul routine,
      // if we ever want to make that subquadratic

      set(M);
      n = 0;
      while (M <= B) {
         UseFFTPrime(n);
         q = GetFFTPrime(n);
         n++;
         mul(M, M, q);
      }

      FFTInfo->NumPrimes = n;
      FFTInfo->MaxRoot = CalcMaxRoot(q);

      double fn = double(n);

      // NOTE: the following checks is somewhat academic,
      // but the implementation relies on it

      if (8.0*fn*(fn+48) > NTL_FDOUBLE_PRECISION)
         ResourceError("modulus too big");

      FFTInfo->rem_struct.init(n, ZZ_pInfo->p, GetFFTPrime);
      FFTInfo->crt_struct.init(n, ZZ_pInfo->p, GetFFTPrime);

      if (!FFTInfo->crt_struct.special()) {
         FFTInfo->prime.SetLength(n);
         FFTInfo->prime_recip.SetLength(n);
         FFTInfo->u.SetLength(n);
         FFTInfo->uqinv.SetLength(n);

         // montgomery
         FFTInfo->reduce_struct.init(ZZ_pInfo->p, ZZ(n) << NTL_SP_NBITS);

         ZZ qq, rr;

         DivRem(qq, rr, M, ZZ_pInfo->p);

         NegateMod(FFTInfo->MinusMModP, rr, ZZ_pInfo->p);

         // montgomery
         FFTInfo->reduce_struct.adjust(FFTInfo->MinusMModP);

         for (i = 0; i < n; i++) {
            q = GetFFTPrime(i);
            qinv = GetFFTPrimeInv(i);

            long tt = rem(qq, q);

            mul(M2, ZZ_pInfo->p, tt);
            add(M2, M2, rr);
            div(M2, M2, q);  // = (M/q) rem p

            div(M1, M, q);
            t = rem(M1, q);
            t = InvMod(t, q);

            // montgomery
            FFTInfo->reduce_struct.adjust(M2);

            FFTInfo->crt_struct.insert(i, M2);

            FFTInfo->prime[i] = q;
            FFTInfo->prime_recip[i] = 1/double(q);
            FFTInfo->u[i] = t;
            FFTInfo->uqinv[i] = PrepMulModPrecon(FFTInfo->u[i], q, qinv);
         }

      }

      tmps = MakeSmart<ZZ_pTmpSpaceT>();
      tmps->crt_tmp_vec.fetch(FFTInfo->crt_struct);
      tmps->rem_tmp_vec.fetch(FFTInfo->rem_struct);

      builder.move(FFTInfo);
   } while (0);

   if (!tmps) {
      const ZZ_pFFTInfoT *FFTInfo = ZZ_pInfo->FFTInfo.get();
      tmps = MakeSmart<ZZ_pTmpSpaceT>();
      tmps->crt_tmp_vec.fetch(FFTInfo->crt_struct);
      tmps->rem_tmp_vec.fetch(FFTInfo->rem_struct);
   }

   NTL_TLS_GLOBAL_ACCESS(ZZ_pTmpSpace_stg);
   ZZ_pTmpSpace_stg = tmps;
   ZZ_pTmpSpace = ZZ_pTmpSpace_stg.get();
}

void ZZ_p::init(const ZZ& p)
{
   ZZ_pContext c(p);
   c.restore();
}

void ZZ_pContext::save()
{
   NTL_TLS_GLOBAL_ACCESS(ZZ_pInfo_stg);
   ptr = ZZ_pInfo_stg;
}

void ZZ_pContext::restore() const
{
   if (ZZ_pInfo == ptr.get()) return;
   // NOTE: this simple optimization could be useful in some situations,
   //    for example, a worker thread re-setting the current modulus
   //    in a multi-threaded build

   NTL_TLS_GLOBAL_ACCESS(ZZ_pInfo_stg);
   ZZ_pInfo_stg = ptr;
   ZZ_pInfo = ZZ_pInfo_stg.get();

   NTL_TLS_GLOBAL_ACCESS(ZZ_pTmpSpace_stg);
   ZZ_pTmpSpace_stg = 0;
   ZZ_pTmpSpace = 0;

   ZZ_pInstalled = false;
}

ZZ_pBak::~ZZ_pBak()
{
   if (MustRestore) c.restore();
}

void ZZ_pBak::save()
{
   c.save();
   MustRestore = true;
}

void ZZ_pBak::restore()
{
   c.restore();
   MustRestore = false;
}

const ZZ_p& ZZ_p::zero()
{
   static const ZZ_p z(INIT_NO_ALLOC); // GLOBAL (assumes C++11 thread-safe init)
   return z;
}

NTL_CHEAP_THREAD_LOCAL
ZZ_p::DivHandlerPtr ZZ_p::DivHandler = 0;

ZZ_p::ZZ_p(INIT_VAL_TYPE, const ZZ& a)  // NO_ALLOC
{
   conv(*this, a);
}

ZZ_p::ZZ_p(INIT_VAL_TYPE, long a) // NO_ALLOC
{
   conv(*this, a);
}

void conv(ZZ_p& x, long a)
{
   if (a == 0)
      clear(x);
   else if (a == 1)
      set(x);
   else {
      NTL_ZZRegister(y);

      conv(y, a);
      conv(x, y);
   }
}

istream& operator>>(istream& s, ZZ_p& x)
{
   NTL_ZZRegister(y);

   NTL_INPUT_CHECK_RET(s, s >> y);
   conv(x, y);

   return s;
}

void div(ZZ_p& x, const ZZ_p& a, const ZZ_p& b)
{
   NTL_ZZ_pRegister(T);

   inv(T, b);
   mul(x, a, T);
}

void inv(ZZ_p& x, const ZZ_p& a)
{
   NTL_ZZRegister(T);

   if (InvModStatus(T, a._ZZ_p__rep, ZZ_p::modulus())) {
      if (!IsZero(a._ZZ_p__rep) && ZZ_p::DivHandler)
         (*ZZ_p::DivHandler)(a);

      InvModError("ZZ_p: division by non-invertible element",
                   a._ZZ_p__rep, ZZ_p::modulus());
   }

   x._ZZ_p__rep = T;
}

long operator==(const ZZ_p& a, long b)
{
   if (b == 0)
      return IsZero(a);

   if (b == 1)
      return IsOne(a);

   NTL_ZZ_pRegister(T);
   conv(T, b);
   return a == T;
}

void add(ZZ_p& x, const ZZ_p& a, long b)
{
   NTL_ZZ_pRegister(T);
   conv(T, b);
   add(x, a, T);
}

void sub(ZZ_p& x, const ZZ_p& a, long b)
{
   NTL_ZZ_pRegister(T);
   conv(T, b);
   sub(x, a, T);
}

void sub(ZZ_p& x, long a, const ZZ_p& b)
{
   NTL_ZZ_pRegister(T);
   conv(T, a);
   sub(x, T, b);
}

void mul(ZZ_p& x, const ZZ_p& a, long b)
{
   NTL_ZZ_pRegister(T);
   conv(T, b);
   mul(x, a, T);
}

void div(ZZ_p& x, const ZZ_p& a, long b)
{
   NTL_ZZ_pRegister(T);
   conv(T, b);
   div(x, a, T);
}

void div(ZZ_p& x, long a, const ZZ_p& b)
{
   if (a == 1) {
      inv(x, b);
   }
   else {
      NTL_ZZ_pRegister(T);
      conv(T, a);
      div(x, T, b);
   }
}

NTL_END_IMPL


#ifndef PLANE_DETECTOR__
#define PLANE_DETECTOR__

#include <vector>
#include <random>
#include <queue>
#include "detected_plane.hpp"
#include <eigen3/Eigen/Geometry>
#include <eigen3/Eigen/Eigenvalues>
#include <boost/array.hpp>
#include <boost/thread.hpp>
#include <exception>
#include <opencv2/opencv.hpp>
#include <iostream>

#define __MIN_RANGE__ 0.3
#define __MAX_RANGE__ 10.0

struct UnknownDepthException:public std::exception {
  const char *what() const throw()
  {
    return "Unknown Depth Exception";
  }

};

enum PixelStatus {
  UNPROCESSED = -3, IN_R_PRIMA, IN_QUEUE
};

//! @class PlaneDetector
//! @brief Implements a fast plane detector on RGB-D images as presented in Poppinga IROS 2008
class PlaneDetector {
public:
  //! Recommended constructor
  PlaneDetector(double delta = 1.0, double epsilon = 1.0, double gamma = 10.0, int theta = 1000, double _std_dev = 0.03);

  //! @brief Detects the planes inside the image
  int detectPlanes(const cv::Mat &depth);

  inline std::vector<DetectedPlane> getPlanes() const {
    return _detected_planes;
  }

  void resetStatus();

  inline int getPos(int i, int j) const {
    return i + j * _width;
  }

  void setCameraParameters(const boost::array< double, 9 >& k_);

  inline bool isInitialized() {return _initialized;}

  inline bool setFloatImage(bool new_value) {_float_image = new_value;}

protected:
  double _delta, _epsilon, _gamma; // Dynamically reconfigurable
  int _theta;
  std::vector <DetectedPlane> _detected_planes; // Vector de IDs
  std::vector <int> _detected_ids; // Contiene todos los IDs que son considerados planos
  std::vector<int> _status_vec; // Relaciona los puntos de la imagen con una region
  std::queue<int> _q;
  int _available_pixels;
  bool _initialized;
  bool _downsample;
  double _std_dev;
  cv::Mat _image;

  std::vector<int> _curr_region; // Saves the region in coordinates --> i + j * _height
  DetectedPlane _curr_plane;

  // Internal stuff for update Matrices
  Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> _es;
  DetectedPlane _p;

  // Attributes of the image
  int _width, _height;
  bool _float_image;

  // Camera parameters
  Eigen::Matrix3d _K;
  float _kx, _cx, _ky , _cy; // Taken from camera parameters

  std::vector<Eigen::Vector3d> _color;

  int getRandomPixel(int region = (int)UNPROCESSED) const;
  //! @brief Gets the unprocessed nearest neighbor of the image
  int getNearestNeighbor(int index) const;
  //! @brief Gets the amount of available pixels in image
  int availablePixels() const;

  // For random numbers
  static std::default_random_engine generator;

  //! @brief Adds pixels to region and adds its nearest neighbors to the queue (modifying status_vec)
  void addPixelToRegion(int index, int _curr_region_id);

  Eigen::Vector3d get3DPoint(int i, int j) const;
  Eigen::Vector3d get3DPoint(int index) const;
  //! @brief Gets the depth of a depth image
  //! @return THe depth value or -1.0 if the data is not valid
  double getDepth(int i, int j) const;

  //! @brief updates s_g, m_k, p_k and MSE
  //! @retval true The conditions of step 8 of the matrix (see [1]) are met --> the matrices are updated
  bool  updateMatrices(const Eigen::Vector3d &v);

  //! @brief Initialize the color matrix
  void initializeColors();
};

std::default_random_engine PlaneDetector::generator;

PlaneDetector::PlaneDetector(double delta, double epsilon, double gamma, int theta, double _std_dev):
_delta(delta), _epsilon(epsilon), _gamma(gamma), _theta(theta),_initialized(false), _downsample(true),_std_dev(_std_dev)
{
  initializeColors();
}

void PlaneDetector::initializeColors() {
  // Different colors for planes
  Eigen::Vector3d v;
  v(0) = 0.0; v(1) = 1.0; v(2) = 0.0;
  _color.push_back(v);
  v(0) = 1.0; v(1) = 0.0; v(2) = 0.0;
  _color.push_back(v);
  v(0) = 0.0; v(1) = 0.0; v(2) = 1.0;
  _color.push_back(v);
  v(0) = 0.0; v(1) = 1.0; v(2) = 1.0;
  _color.push_back(v);
  v(0) = 1.0; v(1) = 0.0; v(2) = 1.0;
  _color.push_back(v);
  v(0) = 1.0; v(1) = 1.0; v(2) = 0.0;
  _color.push_back(v);

}

int PlaneDetector::detectPlanes(const cv::Mat& image)
{
  if (!_initialized) {
    // The camera parameters have to be set before detecting planes
    return 0;
  }
  // First: get parameters of the image (MUST BE AT THE FIRST)
  _width = image.cols;
  _height = image.rows;
  this->_image = image; // TODO: será lento?

  if (_downsample) {
    _width /= 2;
    _height /= 2;
  }

  // All internal status to zero or proper values
  resetStatus();
  _q.empty();
  int _curr_region_id = 0;

  _available_pixels = availablePixels();
  while (_available_pixels > _theta * 1.1)
  {
//     std::cout << "Available pixels: " << _available_pixels << std::endl;
    _curr_region.clear();
    // Initialization of the algorithm--> random point and nearest neighbor
    int candidate = getRandomPixel();
    int nearest = getNearestNeighbor(candidate);

    if (nearest > 0)
    {
      _status_vec[nearest] = _curr_region_id;
      addPixelToRegion(candidate, _curr_region_id);
      addPixelToRegion(nearest, _curr_region_id);

      // Initialize matrices and vectors
      Eigen::Vector3d r_1 = get3DPoint(candidate);
      Eigen::Vector3d r_2 = get3DPoint(nearest);
      _curr_plane.s_g = r_1 + r_2;
      _curr_plane.m_k = (r_1 - _curr_plane.s_g * 0.5)*(r_1 - _curr_plane.s_g * 0.5).transpose();
      _curr_plane.m_k += (r_2 - _curr_plane.s_g * 0.5)*(r_2 - _curr_plane.s_g * 0.5).transpose();
      _curr_plane.p_k = r_1 * r_1.transpose() + r_2*r_2.transpose();
      _curr_plane.n_points = 2;

      while (!_q.empty())
      {
        int new_point = _q.front();
        _q.pop();
        Eigen::Vector3d v = get3DPoint(new_point);
        if (updateMatrices(v)) {
          addPixelToRegion(new_point, _curr_region_id);
        } else {
          _available_pixels--;
        }
      }

      // The queue has been emptied --> clear possible QUEUE status and add the region to the detected planes if condition of step 12 (Algorithm 1)
      if (_curr_region.size() > _theta) {
        _curr_plane.makeDPositive();
        _curr_plane.calculateCovariance(_std_dev);
//         std::cout << "Detected plane: " << _curr_plane.toString() << std::endl;
        _detected_planes.push_back(_curr_plane);
        _detected_ids.push_back(_curr_region_id);
      }
      _curr_region_id++; // The next plane will have a new identificator
    } else {
      // No nearest neighbor available --> discard (to R_PRIMA)
      _status_vec[candidate] = (int)IN_R_PRIMA;
      _available_pixels--;
    }
  }

  return _detected_planes.size();
}

void PlaneDetector::resetStatus()
{
  if (_status_vec.size() != _width*_height) {
    _status_vec.resize(_width*_height);
  }
  for (size_t i = 0; i < _width; i++) {
    for (size_t j = 0; j < _height; j++) {
      if (getDepth(i, j) > 0.0) {
        _status_vec[getPos(i,j)] = (int)UNPROCESSED;
      } else {
        _status_vec[getPos(i,j)] = (int)IN_R_PRIMA;
      }
    }
  }

  _p.init();
  _curr_plane.init();

  _detected_planes.clear();
  _detected_ids.clear();
}

int PlaneDetector::getRandomPixel(int region) const
{
  std::uniform_int_distribution<int> distribution(0, _height * _width - 1);
  int ret_val;

  do {
    ret_val = distribution(generator);
  } while (_status_vec[ret_val] != region);

  return ret_val;
}

//! Gets the unprocessed nearest neighbor of a pixel in a window of size 9
int PlaneDetector::getNearestNeighbor(int index) const
{
  int near = -1;
  int aux;
  float min_dist = 1e10;

  Eigen::Vector3d v = get3DPoint(index);
  Eigen::Vector3d v_;
  for (int i = -1; i < 2; i++) {
    for (int j = -1; j < 2; j++) {
      if (i != 0 || j != 0) {
        aux = index + i + j * _width;

        if (aux > 0 && aux < _height * _width) { // Check bounds
          if (_status_vec[aux] == (int)UNPROCESSED) {
            v_ = get3DPoint(aux);
            double dist = (v - v_).norm();
            if (dist < min_dist) {
              near = aux;
              min_dist = dist;
            }
          }
        }
      }
    }
  }
  return near;
}

int PlaneDetector::availablePixels() const
{
  bool ret_val = false;
  int cont = 0;

  //TODO: Uncomment
  for (int i = 0; i < _width * _height /*&& cont < _theta*/; i++) {
    if (_status_vec[i] == (int)UNPROCESSED)
      cont++;
  }

//   std::cout << "availablePixels() --> cont = " << cont << std::endl;

  return cont;
}

void PlaneDetector::setCameraParameters(const boost::array< double, 9 >& k_)
{
  for (int i = 0; i < 3;i++)
  {
    for (int j = 0; j < 3; j++)
    {
      _K(i,j) = k_[i*3 + j];
    }
  }

  _kx = 1.0/_K(0,0);
  _cx = _K(0,2);
  _ky = 1.0/_K(1,1);
  _cy = _K(1,2);
  _initialized = true;
}

double PlaneDetector::getDepth(int i, int j) const
{
  double ret_val = -1.0;
  if (_downsample) {
    i *= 2;
    j *= 2;
  }

  if (i >= _image.cols || j >= _image.rows)
    return -1.0;
//   std::cout << "Data: " << cvbDepth->image.at<float>(i,j) << " ";
  if(_float_image)
    ret_val = _image.at<float>(j, i); /// CAUTION!!!! the indices in cv::Mat are row, cols (elsewhere cols, rows)
  else
    ret_val = _image.at<u_int16_t>(j, i)*0.001;
  if(ret_val < __MIN_RANGE__ || ret_val > __MAX_RANGE__)
  {
    ret_val = -1.0;
  }
  return ret_val;
}

Eigen::Vector3d PlaneDetector::get3DPoint(int i, int j) const
{
  Eigen::Vector3d pt;

  pt[2] = getDepth(i, j);
  if (_downsample) {
    i *= 2;
    j *= 2;
  }
  if(pt[2] > __MIN_RANGE__ && pt[2] < __MAX_RANGE__)
  {
    pt[0] = (float)(i - _cx) * pt[2] * _kx;
    pt[1] = (float)(j - _cy) * pt[2] * _ky;
  } else {
    throw UnknownDepthException();
  }

  return pt;
}

Eigen::Vector3d PlaneDetector::get3DPoint(int index) const
{
  return get3DPoint(index%_width, index/_width);
}

void PlaneDetector::addPixelToRegion(int index, int _curr_region_id)
{
  _status_vec[index] = _curr_region_id;
  _curr_region.push_back(index);
  Eigen::Vector3d v = get3DPoint(index);
  _available_pixels--;

  int neighbor = getNearestNeighbor(index);
  while (neighbor >= 0) {
    Eigen::Vector3d v_2 = get3DPoint(neighbor);
    if ( (v-v_2).norm() < _delta) { // First check --> the neighbor is sufficiently near to the point
      _status_vec[neighbor] = (int)IN_QUEUE;
      _q.push(neighbor);
      neighbor = getNearestNeighbor(index);
    } else {
      neighbor = -1;
    }
  }
}

bool PlaneDetector::updateMatrices(const Eigen::Vector3d& v)
{
  size_t k = _curr_region.size();
  double div_1 = 1.0 / (double)(k + 1);
  double div = 1.0 / (double)(k);

  _p.s_g = _curr_plane.s_g + v;
  _p.m_k = _curr_plane.m_k + v*v.transpose() - (_p.s_g * div_1)*_p.s_g.transpose() + (_curr_plane.s_g * div) * _curr_plane.s_g.transpose();
  _p.p_k = _curr_plane.p_k + v*v.transpose();

  // Calculate d and n (n is the eigenvector related to the lowest eigenvalue)

  _es.compute(_p.m_k);
  double min_eigenval = 1e10;
  int min_i;
  for (int i = 0; i < 3; i++) {
    double curr_eigen = fabs(_es.eigenvalues()(i));
    if (curr_eigen < min_eigenval) {
      min_eigenval = curr_eigen;
      min_i = i;
    }

  }
  _p.v = _es.eigenvectors().col(min_i);
  _p.d = div_1 * _p.v.dot(_p.s_g);

  // Update the MSE (Eq 3)
  _p.mse = div_1 * _p.v.transpose() * _p.p_k * _p.v - 2 * _p.v.dot(_p.s_g) * _p.d * div_1 + _p.d * _p.d;
  _p.n_points = k + 1;

  // Check if the new plane meets the constraint of algorithm 1 step 8. If so, update the values of the matrices of the class
  if (_p.mse < _epsilon && _p.distance(v) < _gamma)
  {
    // Meets the constraints --> Actualize the plane
    _p.r_g = _p.s_g * div_1;
    _curr_plane = _p;

    return true;
  }
  return false;
}

#endif


#include <iomanip>
#include <cassert>
#include "boost/format.hpp"

#include "amilab_messages.h"
#include "DefineClass.hpp"
#include "Variable.hpp"
#include <math.h>
#include <iostream>

#define NEW_SMARTPTR(type, var, value) \
  boost::shared_ptr<type> var(new type(value));

#define RETURN_VARPTR(type,  value) \
  boost::shared_ptr<type> newval(new type(value)); \
  return Variable<type>::ptr( new Variable<type>(newval));

#include <boost/numeric/conversion/cast.hpp>

std::vector<std::string> AMILabType<float>::conversion_types =
        {   "bool", "double",
            "long", "int", "short",
            "unsigned long", "unsigned short",
            "unsigned char" };

//------------------------------------------------------
//------- Variable<float>
//------------------------------------------------------

/// Copy contents to new variable
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::NewCopy() const
{
  float_ptr newval( new float(Value()));
  Variable<float>::ptr newvar(new Variable<float>(newval));
  return newvar;
}

// Arithmetic operators

/// +a
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator +()
{  RETURN_VARPTR(float,Value());}

/// prefix ++ operator ++a
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator ++()
{
  //std::cout << "**" << std::endl;
  RETURN_VARPTR(float,++RefValue());
}

/// postfix ++ operator a++
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator ++(int)
{
  //std::cout << "**" << std::endl;
  RETURN_VARPTR(float,RefValue()++);
}

/// -a
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator -()
{   RETURN_VARPTR(float,-Value());}

/// prefix -- operator --a
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator --()
{  RETURN_VARPTR(float,--RefValue()); }

/// postfix -- operator a--
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator --(int)
{  RETURN_VARPTR(float,RefValue()--);  }

/// a+b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator +(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RETURN_VARPTR(float,Value()+b->GetValueAsDouble());
  }
  else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

/// a+=b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator +=(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RefValue() += b->GetValueAsDouble();
  } else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

/// a-b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator -(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RETURN_VARPTR(float,Value()-b->GetValueAsDouble());
  }
  else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

/// a-=b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator -=(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RefValue() -= b->GetValueAsDouble();
  } else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

/// a*b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator *(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RETURN_VARPTR(float,Value()*b->GetValueAsDouble());
  }
  else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

/// a*=b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator *=(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RefValue() *= b->GetValueAsDouble();
  } else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

/// a/b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator /(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RETURN_VARPTR(float,Value()/b->GetValueAsDouble());
  }
  else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

/// a/=b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator /=(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RefValue() /= b->GetValueAsDouble();
  } else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

/// a%b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator %(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RETURN_VARPTR(float, ((int) round(Value())) % ((int) round(b->GetValueAsDouble())));
  } else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

/// a%=b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator %=(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RefValue() =  ((int) round(Value())) % ((int) round(b->GetValueAsDouble()));
  } else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

//  Comparison Operators

/// a<b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator <(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RETURN_VARPTR(bool,Value()<b->GetValueAsDouble());
  } else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

/// a<=b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator <=(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RETURN_VARPTR(bool,Value()<=b->GetValueAsDouble());
  } else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

/// a>b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator >(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RETURN_VARPTR(bool,Value()>b->GetValueAsDouble());
  } else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

/// a>=b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator >=(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RETURN_VARPTR(bool,Value()>=b->GetValueAsDouble());
  } else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

/// a!=b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator !=(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RETURN_VARPTR(bool,Value()!=b->GetValueAsDouble());
  } else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

/// a==b
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator ==(const BasicVariable::ptr& b)
{
  //std::cout << __func__ << std::endl;
  if (b->IsNumeric()) {
    RETURN_VARPTR(bool,Value()==b->GetValueAsDouble());
  } else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

// Logical operators

template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator !()
{
  RETURN_VARPTR(bool,!(Value()>0.5));
}

template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator &&(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RETURN_VARPTR(bool,(Value()>0.5)&& (bool) (b->GetValueAsDouble()>0.5));
  } else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::operator ||(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RETURN_VARPTR(bool,(Value()>0.5) || (bool) (b->GetValueAsDouble()>0.5));
  } else
    CLASS_ERROR("operation not defined");
  return this->NewReference();
}

// Mathematical functions
#define VAR_IMPL_FUNC(type,fname,func) \
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<type>::m_##fname() \
{ \
    RETURN_VARPTR(float, func(Value())); \
}

VAR_IMPL_FUNC(float,  sin,  sin)
VAR_IMPL_FUNC(float,  cos,  cos)
VAR_IMPL_FUNC(float,  tan,  tan)
VAR_IMPL_FUNC(float,  asin, asin)
VAR_IMPL_FUNC(float,  acos, acos)
VAR_IMPL_FUNC(float,  atan, atan)
VAR_IMPL_FUNC(float,  fabs, fabs)
VAR_IMPL_FUNC(float,  round,round)
VAR_IMPL_FUNC(float,  floor,floor)
VAR_IMPL_FUNC(float,  exp,  exp)
VAR_IMPL_FUNC(float,  log,  1.0/log(10.0)*log)
VAR_IMPL_FUNC(float,  ln,   log)
VAR_IMPL_FUNC(float,  norm, fabs)
VAR_IMPL_FUNC(float,  sqrt, sqrt)
//VAR_IMPL_FUNC(float,  pow,  pow)

//---------------------------------------------------
template<> AMI_DLLEXPORT
BasicVariable::ptr Variable<float>::TryCast(
    const std::string& type_string) const
{
  try
  {
    // cast to double
    if (type_string==AMILabType<double>::name_as_string()) {
      RETURN_VARPTR(double, boost::numeric_cast<double>(Value()));
    } else
    // cast to int
    if (type_string==AMILabType<int>::name_as_string()) {
      RETURN_VARPTR(int, boost::numeric_cast<int>(Value()));
    } else
    // cast to long
    if (type_string==AMILabType<long>::name_as_string()) {
      RETURN_VARPTR(long, boost::numeric_cast<long>(Value()));
    } else
    // cast to unsigned long
    if (type_string==AMILabType<unsigned long>::name_as_string()) {
      RETURN_VARPTR(unsigned long, boost::numeric_cast<unsigned long>(Value()));
    } else
    // cast to short
    if (type_string==AMILabType<short>::name_as_string()) {
      RETURN_VARPTR(short, boost::numeric_cast<short>(Value()));
    } else
    // cast to unsigned short
    if (type_string==AMILabType<unsigned short>::name_as_string()) {
      RETURN_VARPTR(unsigned short, boost::numeric_cast<unsigned short>(Value()));
    } else
    // cast to unsigned char
    if (type_string==AMILabType<unsigned char>::name_as_string()) {
      RETURN_VARPTR(unsigned char, boost::numeric_cast<unsigned char>(Value()));
    } else
    // cast to bool
    if (type_string==AMILabType<bool>::name_as_string()) {
      RETURN_VARPTR(bool, boost::numeric_cast<bool>(Value()));
    } else
    {
      // make default conversion to double??
      CLASS_ERROR((boost::format("No conversion available for variable %1% from float to %2%") % _name % type_string).str().c_str());
    }
  } catch (std::bad_cast &e)
  {
    CLASS_ERROR((boost::format("%1%, for variable %2% from float to %3%") % e.what() % _name % type_string).str().c_str());
    return BasicVariable::ptr();
  }
  return BasicVariable::ptr();
}

//
template<> AMI_DLLEXPORT BasicVariable::ptr Variable<float>::BasicCast(const int& type)
{
#define NUMCAST(amitype,type) \
      case amitype:  { RETURN_VARPTR(type, boost::numeric_cast<type>(Value())); }

  try
  {
    switch((WORDTYPE)type) {
      NUMCAST( WT_UNSIGNED_CHAR,  unsigned char )
      NUMCAST( WT_SIGNED_SHORT,   short         )
      NUMCAST( WT_UNSIGNED_SHORT, unsigned short)
      NUMCAST( WT_SIGNED_INT,     int           )
      NUMCAST( WT_SIGNED_LONG,    long          )
      NUMCAST( WT_FLOAT,          float         )
      NUMCAST( WT_DOUBLE,         double        )
      case WT_UNSIGNED_INT: { RETURN_VARPTR(float, (unsigned int) Value()); }
      default:
        CLASS_ERROR(( boost::format("Conversion to type %1% not available")%((WORDTYPE)type)).str().c_str());
    }
  } catch (std::bad_cast &e)
  {
    CLASS_ERROR((boost::format("%1%, for variable %2% from float to WORDTYPE %3%") % e.what() % _name % (WORDTYPE)type ).str().c_str());
    return BasicVariable::ptr();
  }

  RETURN_VARPTR(float, Value());

}

//
template<> AMI_DLLEXPORT
BasicVariable::ptr Variable<float>::TernaryCondition(const BasicVariable::ptr& v1, const BasicVariable::ptr&v2)
{

  if (Value()>0.5) {
    return v1->NewReference();
  } else {
    return v2->NewReference();
  }
  return NewReference();
}

#if !defined(ARRAY_SIZE)
    #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
#endif

template<> AMI_DLLEXPORT
  BasicVariable::ptr Variable<float>::operator[](const BasicVariable::ptr& v)
{
  if (v->IsNumeric()) {
    float* pointer = this->Pointer().get();
    //std::cout << "Size of array " << ARRAY_SIZE(pointer) << std::endl;
    // at the user own risk
    float res = pointer[(int)(v->GetValueAsDouble()+0.5)];
    return AMILabType<float>::CreateVar(res);
  } else
    CLASS_ERROR("operator[] only takes a numerical parameter");
  return NewReference();
}

template<> AMI_DLLEXPORT
BasicVariable::ptr Variable<float>::operator =(const BasicVariable::ptr& b)
{
  if (b->IsNumeric()) {
    RefValue() = b->GetValueAsDouble();
  } else
    CLASS_ERROR("operation not defined");
  return NewReference();
}


#ifndef STAN_MATH_TORSTEN_TWOCPT_EFFCPT_HPP
#define STAN_MATH_TORSTEN_TWOCPT_EFFCPT_HPP

#include <Eigen/Dense>
#include <stan/math/prim/err/check_greater_or_equal.hpp>
#include <stan/math/torsten/meta.hpp>
#include <stan/math/torsten/to_array_2d.hpp>
#include <stan/math/torsten/pmx_solve_cpt.hpp>
#include <stan/math/torsten/ev_solver.hpp>
#include <stan/math/torsten/ev_manager.hpp>
#include <stan/math/torsten/pmx_twocpt_effcpt_model.hpp>
#include <stan/math/torsten/pmx_population_check.hpp>
#include <stan/math/prim/meta/return_type.hpp>
#include <string>
#include <vector>

namespace torsten {

/**
 * Computes the predicted amounts in each compartment at each event
 * for a one-compartment coupled with effective-compartment
 * model with analytical solution.
 *
 * @tparam Ts types of parameters, see pmx_solve_cpt for
 *         details.
 * @return a matrix with predicted amount in each compartment
 *         at each event.
 *
 */
  template <typename... Ts>
  auto pmx_solve_twocpt_effcpt(Ts... args) {
    return PMXSolveCPT<PMXTwocptEffCptModel>::solve(args...);
  }

/**
 * Overload function to allow user to pass an std::vector for
 * pMatrix/bioavailability/tlag
 */
  template <typename T0, typename T1, typename T2, typename T3,
            typename T_par, typename T_biovar, typename T_tlag,
            typename = require_any_not_std_vector_t<T_par, T_biovar, T_tlag> >
  stan::matrix_return_t<T0, T1, T2, T3, T_par, T_biovar, T_tlag>
  pmx_solve_twocpt_effcpt(const std::vector<T0>& time,
                          const std::vector<T1>& amt,
                          const std::vector<T2>& rate,
                          const std::vector<T3>& ii,
                          const std::vector<int>& evid,
                          const std::vector<int>& cmt,
                          const std::vector<int>& addl,
                          const std::vector<int>& ss,
                          const std::vector<T_par>& pMatrix,
                          const std::vector<T_biovar>& biovar,
                          const std::vector<T_tlag>& tlag) {
    auto param_ = torsten::to_array_2d(pMatrix);
    auto biovar_ = torsten::to_array_2d(biovar);
    auto tlag_ = torsten::to_array_2d(tlag);

    return pmx_solve_twocpt_effcpt(time, amt, rate, ii, evid, cmt, addl, ss,
                                   param_, biovar_, tlag_);
  }

  /**
   * For population models, we follow the call signature
   * but add the arrays of the length of each individual's data.
   * The size of that vector is the size of
   * the population.
   */
template <typename T0, typename T1, typename T2, typename T3, typename T4,
          typename T5, typename T6>
Eigen::Matrix<typename  EventsManager<NONMENEventsRecord<T0, T1, T2, T3>, NonEventParameters<T0, T4, std::vector, std::tuple<T5, T6> > >::T_scalar, // NOLINT
              Eigen::Dynamic, Eigen::Dynamic>
pmx_solve_group_twocpt_effcpt(const std::vector<int>& len,
                       const std::vector<T0>& time,
                       const std::vector<T1>& amt,
                       const std::vector<T2>& rate,
                       const std::vector<T3>& ii,
                       const std::vector<int>& evid,
                       const std::vector<int>& cmt,
                       const std::vector<int>& addl,
                       const std::vector<int>& ss,
                       const std::vector<std::vector<T4> >& pMatrix,
                       const std::vector<std::vector<T5> >& biovar,
                       const std::vector<std::vector<T6> >& tlag) {
  using ER = NONMENEventsRecord<T0, T1, T2, T3>;
  using EM = EventsManager<ER, NonEventParameters<T0, T4, std::vector, std::tuple<T5, T6> >>;

  int nCmt = torsten::PMXTwocptEffCptModel<double>::Ncmt;
  ER events_rec(nCmt, len, time, amt, rate, ii, evid, cmt, addl, ss);

  static const char* caller("pmx_solve_group_twocpt_effcpt");
  torsten::pmx_population_check(len, time, amt, rate, ii, evid, cmt, addl, ss,
                                pMatrix, biovar, tlag, caller);

  using model_type = torsten::PMXTwocptEffCptModel<typename EM::T_par>;
   EventSolver<model_type, EM> pr;

  Eigen::Matrix<typename EM::T_scalar, -1, -1> pred(events_rec.total_num_event_times, nCmt);

  pr.pred(events_rec, pred, dsolve::PMXAnalyiticalIntegrator(), pMatrix, biovar, tlag);

  return pred;
}

}
#endif


/*
 * Prover_toom.cpp
 *
 *  Created on: 24.04.2011
 *      Author: stephaniebayer
 */

#include "Prover_toom.h"

#include<vector>
#include "Cipher_elg.h"
#include "G_q.h"
#include "Mod_p.h"
#include "Functions.h"
#include "ElGammal.h"
#include "multi_expo.h"
#include "func_pro.h"
#include <fstream>
#include <time.h>

#include <NTL/ZZ.h>
NTL_CLIENT

//extern G_q G;
extern G_q H;
extern Pedersen Ped;
extern ElGammal El;
extern long mu;
extern long mu_h;
extern long m_r;

Prover_toom::Prover_toom() {
	// TODO Auto-generated constructor stub

}

Prover_toom::Prover_toom(vector<vector<Cipher_elg>* >* Cin,vector<vector<ZZ>*>* Rin, vector<vector<vector<long>* >* >* piin, vector<long> num, ZZ gen){

	// set the dimensions of the row and columns according to the user input
	m = num[1]; //number of rows
	n = num[2]; //number of columns
	C = Cin; //sets the reencrypted chipertexts to the input
	R = Rin; //sets the random elements to the input
	pi = piin; // sets the permutation to the input
	omega_mulex = num[3]; //windowsize for sliding-window technique
	omega_sw = num[4]; //windowsize for multi-expo technique
	omega_LL = num[7]; //windowsize for multi-expo technique

	//Creates the matrices A
	A = new vector<vector<ZZ>* >(m);
	func_pro::set_A(A,pi, m, n);

	//Allocate the storage needed for the vectors
	chal_x6 = new vector<ZZ>(2*m); //x6, x6^2, ... challenges from round 6
	chal_y6 = new vector<ZZ>(n); //y6, y6^2, ... challenges form round 6
	chal_x8 = new vector<ZZ>(2*m +1); //x8, x8^2, ... challenges from round 8

	//Allocate the storage needed for the vectors
	c_A = new vector<Mod_p>(m+1); //commitments to the rows in A
	r_A = new vector<ZZ>(m+1); //random elements used for the commitments

	D = new vector<vector<ZZ>* >(m+1); //vector containing in the first row random values and in all others y*A(ij) + B(ij)-z
	D_h = new vector<vector<ZZ>* >(m); //Vector of the Hadamare products of the rows in D
	D_s = new vector<vector<ZZ>* >(m+1); //Shifted rows of D_h
	d = new vector<ZZ>(n); //containing random elements to proof product of D_hm
	Delta = new vector<ZZ>(n); //containing random elements to proof product of D_hm
	d_h = new vector<ZZ>(n); // vector containing the last row of D-h

	r_D_h = new vector<ZZ>(m);//random elements for commitments to D_h
	c_D_h = new vector<Mod_p>(m+2);//commitments to the rows in D_h
	C_small = new vector<vector<Cipher_elg>* >(m_r); //matrix of reduced ciphertexts

	B = new vector<vector<ZZ>* >(m);//matrix of permuted exponents, exponents are x2^i, i=1, ..N
	basis_B = new vector<vector<vector<long>* >* >(m); //basis for the multi-expo, containing the Bij
	B_small = new vector<vector<ZZ>* >(m_r); //matrix of reduced exponents
	B_0 = new vector<ZZ>(n); //vector containing random exponents
	basis_B0 = new vector<vector<long>* >(n); //basis for multi-expo, containing  the B0j
	r_B = new vector<ZZ>(m); //random elements used to commit to B
	r_B_small = new vector<ZZ>(m_r); //random elements for commitments to B_small
	c_B = new vector<Mod_p>(m); //vector of commitments to rows in T
	a = new vector<ZZ>(2*m); //elements used for reencryption in round 5
	r_a = new vector<ZZ>(2*m); //random elements to commit to elements in a
	c_a = new vector<Mod_p>(2*m); //commitments to elements a
	E = new vector<Cipher_elg>(2*m); //vector of the products of the diogonals of A^T generated in round 7
	rho_a = new vector<ZZ>(2*m); //contains random elements used for the reencryption in 7

	C_c = new vector<Cipher_elg>(mu_h);  //Ciphertexts to prove correctness of reduction
	c_a_c= new vector<Mod_p>(mu_h); //vector containing the commitments to value used for the reencryption of E_c
	a_c= new vector<ZZ>(mu_h); //vector containing the values used for reecnrcyption
	r_c= new vector<ZZ>(mu_h); //random elements used to commit to a_c
	rho_c = new vector<ZZ>(mu_h); //random elements used in the reencryption

	a = new vector<ZZ>(2*mu); //elements used for reencryption in round 5
	r_a = new vector<ZZ>(2*mu); //random elements to commit to elements in a
	c_a = new vector<Mod_p>(2*mu); //commitments to elements a
	E = new vector<Cipher_elg>(2*mu); //vector of the products of the diogonals of Y^T generated in round 9
	rho_a = new vector<ZZ>(2*mu); //contains random elements used for the reencryption in 9

	Dl = new vector<ZZ>(2*m+1); //bilinear_map(Y_pi, U, chal_t)
	r_Dl = new vector<ZZ>(2*m+1); //random elements to commit to the C_ls
	c_Dl = new vector<Mod_p>(2*m +1); //commitments to the C_ls

	d_bar = new vector<ZZ>(n);// chal_x8*D_h(m-1) +d
	Delta_bar = new vector<ZZ>(n);//chal_x8*d_h+Delta
	D_h_bar = new vector<ZZ>(n);//sum over the rows in D_h

	B_bar  = new vector<ZZ>(n); // sum over the rows in B multiplied by chal^i
	A_bar = new vector<ZZ>(n); //sum over the rows in A times the challenges
	D_s_bar = new vector<ZZ>(n); // sum over the rows in D_s times the challenges

}

//Destructor deletes all pointers and frees the storage
Prover_toom::~Prover_toom() {
	delete chal_x6;
	delete chal_y6;
	delete chal_x8;
	delete c_A;
	delete r_A;

	Functions::delete_vector(D);
	Functions::delete_vector(D_h);
	Functions::delete_vector(D_s);
	delete d;
	delete Delta;
	delete d_h;

	delete r_D_h;
	delete c_D_h;
	Functions::delete_vector(B);
	Functions::delete_vector(basis_B);
	delete B_0;
	Functions::delete_vector(basis_B0);
	delete r_B;
	delete r_B_small;
	delete c_B;
	delete a;
	delete r_a;
	delete c_a;
	delete rho_a;

	delete Dl;
	delete r_Dl;
	delete c_Dl;

	delete D_h_bar;
	delete d_bar;
	delete Delta_bar;
	delete B_bar;
	delete A_bar;
	delete D_s_bar;

	delete C_c;
	delete c_a_c; //vector containing the commitments to value used for the reencryption of E_low_up
	delete a_c; //vector containing the exponents
	delete r_c;
	delete rho_c;
	delete E;
}

//round_1 picks random elements and commits to the rows of A
string Prover_toom::round_1(){
	long i;
	string name;
	time_t rawtime;
	time ( &rawtime );

	name = "round_1 ";
	name = name + ctime(&rawtime);
	//calculates commitments to rows of A
	Functions::commit_op(A,r_A,c_A);

	ofstream ost(name.c_str());
	for (i=0; i<m; i++){
		ost << c_A->at(i)<< " ";
	}
	return name;
}

//round_3, permuted the exponents in s,  picks random elements and commits to values
string Prover_toom::round_3(string in_name){
	long i;
	ZZ x2;
	vector<vector<ZZ>* >* chal_x2 = new vector<vector<ZZ>* >(m);

	string name;
	time_t rawtime;
	time ( &rawtime );

	//reads in values of s
	ifstream ist(in_name.c_str());
	if(!ist) cout<<"Can't open "<< in_name;
	ist >> x2;

	//creates a matrix with entries x2,..., x2^N
	func_pro::set_x2(chal_x2,x2, m,n);

	//permutes chal_x2 according pi to create B
	func_pro::set_B_op(B, basis_B, chal_x2, pi , omega_mulex);

	//commits to the rows in B
	Functions::commit_op(B,r_B,c_B);

	name = "round_3 ";
	name = name + ctime(&rawtime);

	//write data in the file name
	ofstream ost(name.c_str());
	for (i=0; i<m;i++){
		ost << c_B->at(i) <<" ";
	}

	Functions::delete_vector(chal_x2);
	return name;
}

//round_5a calculates D and the commitments to the vectors chal_z, D_h
void Prover_toom::round_5a(){
	long i;
	ZZ temp, t; //temporary variables
	vector<ZZ>* r = new vector<ZZ>(n);
	vector<ZZ>* v_z = new vector<ZZ>(n); //row containing the challenge alpha
	ZZ ord = H.get_ord();
	time_t rawtime;
	time ( &rawtime );

	//calculate for each value in the first m rows in D: y* A_ij + A_ij -z
	func_pro::set_D(D, A,B, chal_z4, chal_y4);

	//Set the matrix D_h as the Hadamard product of the rows in D
	func_pro::set_D_h(D_h, D);

	for( i=0; i<n;i++){
		v_z->at(i) = chal_z4; //fills the vector alpha with the challenge alpha
		NegateMod(r->at(i),to_ZZ(1),ord);
	}

	//Sets the additional row in D to contain -1
	D->at(m) = r;
	//random number to commit to last row in A
	r_A->at(m) = 0;

	//calculate commitment to alpha
	Functions::commit_op(v_z, r_z, c_z);
	//calculate commitment to the rows in D_h
	Functions::commit_op(D_h,r_D_h,c_D_h);

	delete v_z;
}

void Prover_toom::round_5b(){
	func_pro::set_Rb(B, R, R_b);
	commit_ac();
	calculate_Cc(C,basis_B);

}

string Prover_toom::round_5(string in_name ){
	long i;
	string name;
	time_t rawtime;
	time ( &rawtime );

	//reads the values out of the file
	ifstream ist(in_name.c_str());
	if(!ist) cout<<"Can't open "<< in_name;
	ist>>chal_z4;
	ist >> chal_y4;

	round_5a();
	round_5b();
	//Set name of the output file and open stream
	name = "round_5 ";
	name = name + ctime(&rawtime);

	ofstream ost(name.c_str());
	//writes the commitments in the file
	ost<<  c_z<< "\n";
	for (i = 0; i<m ; i++){
		ost << c_D_h ->at(i)<< " ";
	}
	ost<<"\n";

	for(i=0; i<mu_h; i++){
		ost<<C_c->at(i)<<" ";
	}
	ost<<"\n";
	for(i=0; i<mu_h; i++){
		ost<<c_a_c->at(i)<<" ";
	}
	return name;
}

string Prover_toom::round_5_red(string in_name){
	long i;
	string name;
	time_t rawtime;
	time ( &rawtime );

	//reads the values out of the file
	ifstream ist(in_name.c_str());
	if(!ist) cout<<"Can't open "<< in_name;
	ist>>chal_z4;
	ist >> chal_y4;

	func_pro::set_Rb(B,R,R_b);
	commit_ac();

	calculate_Cc(C,basis_B);

	name = "round_5_red ";
	name = name + ctime(&rawtime);

	ofstream ost(name.c_str());
	for(i=0; i<mu_h; i++){
		ost<<C_c->at(i)<<" ";
	}
	ost<<"\n";
	for(i=0; i<mu_h; i++){
		ost<<c_a_c->at(i)<<" ";
	}
	return name;

}

string Prover_toom::round_5_red1(string in_name){
	long i;
	double tstart, tstop;
	string name;
	time_t rawtime;
	time ( &rawtime );

	x = new vector<ZZ>(mu_h);

	//reads the values out of the file
	ifstream ist(in_name.c_str());
	if(!ist) cout<<"Can't open "<< in_name;
	//reads challenges x
	for(i=0; i<mu_h; i++){
		ist>> x->at(i);
	}

	//Call of round 5a
	round_5a();

	//calculate F_c and Z_c for the first reduction
	calculate_ac_bar(x);
	calculate_r_ac_bar(x);

	//reduction from m rows to m_r rows
	tstart= (double)clock()/CLOCKS_PER_SEC;
		reduce_C(C, B, r_B, x, 4*m_r);
	tstop = (double)clock()/CLOCKS_PER_SEC;
	time_di = time_di+tstop-tstart;

	set_Rb1(x);
	commit_ac();
	calculate_Cc(C_small, B_small);

	delete x;

	//Set name of the output file and open stream
	name = "round_5 ";
	name = name + ctime(&rawtime);

	ofstream ost(name.c_str());
	//writes the commitments in the file
	ost<<  c_z<< "\n";
	for (i = 0; i<m ; i++){
		ost << c_D_h ->at(i)<< " ";
	}
	ost<<"\n";
	for(i=0; i<mu_h; i++){
		ost<<C_c->at(i)<<" ";
	}
	ost<<"\n";
	for(i=0; i<mu_h; i++){
		ost<<c_a_c->at(i)<<" ";
	}
	ost<<a_c_bar<<endl;
	ost<<r_ac_bar<<endl;
	return name;
}

void Prover_toom::round_7a(){

	//Set the rows in D_s as D_s(i) = chal_t_1^i+1*D_h(i) for i<m-1 and D_s(m-1) = sum(chal_x6^i+1 * D_s(i+1) and set last row of D_s to random values and also D(0)
	func_pro::set_D_s(D_s,D_h,D,chal_x6,r_Dl_bar);

	//calculate the values Dls as Dl(l) = sum(D(i)*D_s->at(i)*chal_y6) for j=n+i-l and commits to the values
	func_pro::commit_Dl_op(c_Dl,Dl, r_Dl, D, D_s, chal_y6);

	//commitments to D(0) and D_s(m)
	Functions::commit_op(D->at(0),r_D0,c_D0);
	Functions::commit_op(D_s->at(m), r_Dm, c_Dm);

	//commitments to prove that the product over the elements in D_h->at(m) is the desired product of n *y + x2n -z
	func_pro::commit_d_op(d,r_d,c_d);
	func_pro::commit_Delta_op(Delta, d, r_Delta, c_Delta);
	func_pro::commit_d_h_op(D_h,d_h,d,Delta, r_d_h, c_d_h);

}

void Prover_toom::round_7b(){
	calculate_ac_bar(chal_x6);
	calculate_r_ac_bar(chal_x6);

}

void  Prover_toom::round_7c(){
	double tstart, tstop;
	vector<Cipher_elg>* e = 0;

	tstart= (double)clock()/CLOCKS_PER_SEC;
	reduce_C(C, B, r_B, chal_x6, m_r);
	set_Rb1(chal_x6);
	tstop = (double)clock()/CLOCKS_PER_SEC;
	time_di = time_di+tstop-tstart;

	func_pro::commit_a_op(a, r_a, c_a);
	func_pro::commit_B0_op(B_0, basis_B0, r_B0, c_B0, omega_mulex);

	tstart = (double)clock()/CLOCKS_PER_SEC;
	e = calculate_e();
	tstop = (double)clock()/CLOCKS_PER_SEC;
	time_di = time_di+tstop-tstart;
	//cout<<"To calculate the di's took "<<time_di<<" sec."<<endl;

	calculate_E(e);

	delete e;
	Functions::delete_vector(C_small);
}

void  Prover_toom::round_7c_red(){
	vector<Cipher_elg>* e = 0;
	double tstart, tstop;
	vector<vector<Cipher_elg>* >* C_small_temp = 0;
	vector<vector<ZZ>* >* B_small_temp = 0;
	vector<ZZ>* r_B_small_temp = 0;

	tstart= (double)clock()/CLOCKS_PER_SEC;
		C_small_temp = copy_C();
		B_small_temp = copy_B();
		r_B_small_temp = copy_r_B();

		C_small = new vector<vector<Cipher_elg>* >(m_r);
		B_small = new vector<vector<ZZ>* >(m_r);
		r_B_small = new vector<ZZ>(m_r);

		reduce_C(C_small_temp, B_small_temp, r_B_small_temp, chal_x6, m_r);
		set_Rb1(chal_x6);
	tstop = (double)clock()/CLOCKS_PER_SEC;
	time_di = time_di+tstop-tstart;

	func_pro::commit_a_op(a,r_a,c_a);
	func_pro::commit_B0_op(B_0, basis_B0, r_B0, c_B0, omega_mulex);

	tstart = (double)clock()/CLOCKS_PER_SEC;
	e= calculate_e();
	tstop = (double)clock()/CLOCKS_PER_SEC;
	time_di = time_di+tstop-tstart;
	//cout<<"To calculate the di's took "<<time_di<<" sec."<<endl;

	calculate_E(e);

	Functions::delete_vector(C_small);
	Functions::delete_vector(C_small_temp);
	Functions::delete_vector(B_small_temp);
	delete r_B_small_temp;
	delete e;
}

string Prover_toom::round_7(string in_name){
	long i,l;
	string name;
	time_t rawtime;
	time ( &rawtime );
	//reads the values out of the file
	ifstream ist(in_name.c_str());
	if(!ist) cout<<"Can't open "<< in_name;
	//reads the vector t_1
	l=2*m;
	for (i = 0; i<l; i++){
		ist >> chal_x6->at(i);
	}
	//reads the vector t
	for (i = 0; i<n; i++){
		ist >> chal_y6->at(i);
	}

	round_7a();
	round_7b();
	round_7c();

	//Set name of the output file and open stream
	name = "round_7 ";
	name = name + ctime(&rawtime);

	ofstream ost(name.c_str());	for (i = 0; i<=l ; i++){
		ost << c_Dl ->at(i)<< " ";
	}
	ost << "\n";
	ost<<c_D0<<"\n";
	ost <<c_Dm<<"\n";
	ost<<c_d<<"\n";
	ost<<c_Delta<<"\n";
	ost<<c_d_h<<"\n";
	ost<<a_c_bar<<"\n";
	ost<<r_ac_bar<<"\n";
	for(i=0; i<8; i++){
		ost<<E->at(i)<<" ";
	}
	ost<<"\n";
	ost<<c_B0<<"\n";
	for(i=0; i<8; i++){
		ost<<c_a->at(i)<<" ";
	}
	ost<<"\n";

	return name;
}

string Prover_toom::round_7_red(string in_name){
	long i,l;
	string name;
	time_t rawtime;
	time ( &rawtime );
	//reads the values out of the file
	ifstream ist(in_name.c_str());
	if(!ist) cout<<"Can't open "<< in_name;
	//reads the vector t_1
	l=2*m;
	for (i = 0; i<l; i++){
		ist >> chal_x6->at(i);
	}
	//reads the vector t
	for (i = 0; i<n; i++){
		ist >> chal_y6->at(i);
	}

	round_7a();
	round_7b();
	round_7c_red();

	//Set name of the output file and open stream
	name = "round_7 ";
	name = name + ctime(&rawtime);

	ofstream ost(name.c_str());
	for (i = 0; i<=l ; i++){
		ost << c_Dl ->at(i)<< " ";
	}
	ost << "\n";
	ost<<c_D0<<"\n";
	ost<<c_Dm<<"\n";
	ost<<c_d<<"\n";
	ost<<c_Delta<<"\n";
	ost<<c_d_h<<"\n";

	ost<<a_c_bar<<"\n";
	ost<<r_ac_bar<<"\n";
	for(i=0; i<8; i++){
		ost<<E->at(i)<<" ";
	}
	ost<<"\n";
	ost<<c_B0<<"\n";
	for(i=0; i<8; i++){
		ost<<c_a->at(i)<<" ";
	}
	ost<<"\n";
	return name;
}

void Prover_toom::round_9a(){

	//Calculate D_h_bar = sum(chal^i*D_h(row(i)))
	func_pro::calculate_D_h_bar(D_h_bar,D_h,chal_x8);

	//calculate r_Dh_bar = sum(chal^i*r_Dh_bar(i)), opening to prove correctness of D_h
	func_pro::calculate_r_Dh_bar(r_D_h, chal_x8, r_Dh_bar);

	//calculate d_bar, r_d_bar, Delta_bar, r_Delta_bar, openings to prove product over elements in D_h->at(m-1)
	func_pro::calculate_dbar_rdbar(D_h, chal_x8, d_bar,d,r_D_h, r_d, r_d_bar);
	func_pro::calculate_Deltabar_rDeltabar(d_h, chal_x8, Delta_bar, Delta, r_d_h, r_Delta, r_Delta_bar);

}

void  Prover_toom::round_9b(){

	//A_bar and r_A_bar, openings to prove permutation in D
	func_pro::calculate_A_bar(D, A_bar, chal_x8);
	func_pro::calculate_r_A_bar(r_D0, r_A, r_B, chal_x8, r_z, chal_y4, r_A_bar);

	//D_s_bar and r_Ds_bar, openings to prove correctness of D_s
	func_pro::calculate_D_s_bar(D_s, D_s_bar, chal_x8);
	func_pro::calculate_r_Ds_bar(r_D_h, chal_x6, chal_x8, r_Ds_bar, r_Dm);

	//sum of the random values used to commit to the Dl's, to prover correctness of them
	func_pro::calculate_r_Dl_bar(r_Dl, chal_x8, r_Dl_bar);

}

void  Prover_toom::round_9c(){
	//calculate B_bar
	func_pro::calculate_B_bar(B_0, B_small,chal_x8, B_bar );
	Functions::delete_vector(B_small);

	//calculate r_B_bar
	func_pro::calculate_r_B_bar(r_B_small, chal_x8,r_B0, r_B_bar );

	//calculate a_bar
	func_pro::calculate_a_bar(a, chal_x8, a_bar);

	//calculate r_a_bar
	func_pro::calculate_r_a_bar(r_a, chal_x8, r_a_bar);

	//calculate rho_a_bar
	func_pro::calculate_rho_a_bar(rho_a, chal_x8, rho_bar);
}

string Prover_toom::round_9(string in_name){
	long i;
	long l = chal_x8->size();
	ZZ tem;
	string name;
	time_t rawtime;
	time ( &rawtime );

	//reads the values out of the file
	ifstream ist(in_name.c_str());
	if(!ist) cout<<"Can't open "<< in_name;
	//reads the vector e
	for (i = 0; i<l ; i++){
		ist >> chal_x8->at(i);
	}

	round_9a();
	round_9b();
	round_9c();

	//Set name of the output file and open stream
	name = "round_9 ";
	name = name + ctime(&rawtime);

	ofstream ost(name.c_str());

	for (i = 0; i<n; i++){
		ost << D_h_bar->at(i)<<" ";
	}
	ost <<"\n";

	ost<< r_Dh_bar;
	ost <<"\n";

	for(i=0; i<n;i++){
		ost<<d_bar->at(i)<<" ";
	}
	ost<<"\n";
	ost<< r_d_bar <<"\n";
	for(i=0; i<n; i++){
		ost<<Delta_bar->at(i)<<" ";
	}
	ost<<"\n";
	ost<<r_Delta_bar <<"\n";

	for (i = 0; i<n; i++){
		ost << A_bar->at(i)<<" ";
	}
	ost<<"\n";
	ost<<r_A_bar<<"\n";
	for(i=0; i<n; i++){
		ost<<D_s_bar->at(i)<<" ";
	}
	ost<<"\n";
	ost<<r_Ds_bar<<"\n";
	ost<<r_Dl_bar<<"\n";
	for (i = 0; i<n; i++){
		ost << B_bar->at(i)<<" ";
	}
	ost <<"\n";

	ost<< r_B_bar;
	ost <<"\n";

	ost<< a_bar;
	ost <<"\n";

	ost<< r_a_bar;
	ost <<"\n";

	ost<< rho_bar;
	ost <<"\n";

	return name;
}

void Prover_toom::commit_ac(){
	long i;
	ZZ ord = H.get_ord();

	for(i= 0; i<mu_h; i++){
		a_c->at(i) = RandomBnd(ord);
		r_c->at(i) = RandomBnd(ord);
		rho_c->at(i) = RandomBnd(ord);
	}
	a_c->at(mu-1) = to_ZZ(0);
	r_c->at(mu-1) = to_ZZ(0);
	NegateMod(rho_c->at(mu-1),R_b, ord);
	for(i= 0; i<mu_h; i++){
		c_a_c->at(i) = Ped.commit_sw(a_c->at(i),r_c->at(i));
	}

}

void Prover_toom::calculate_Cc(vector<vector<Cipher_elg>* >* C, vector<vector<vector<long>* >* >* B){
	long i, j, l,k;
	ZZ mod = H.get_mod();
	ZZ gen = H.get_gen().get_val();
	Cipher_elg temp, temp_1;
	ZZ t_1;
	double tstart, tstop;

	tstart = (double)clock()/CLOCKS_PER_SEC;
	for(k=0; k<mu_h; k++){
		temp = Cipher_elg(1,1,mod);
		for(i=0; i<mu; i++){
			j=k+1-mu+i;
			if(j>=0 & j<mu){
				for(l=0; l<m_r; l++){
					multi_expo::expo_mult(temp_1, C->at(4*l+i), B->at(4*l+j), omega_mulex);
					Cipher_elg::mult(temp, temp, temp_1);
				}
			}
		}
		PowerMod(t_1, gen, a_c->at(k), mod);
		temp_1 = El.encrypt(t_1, rho_c->at(k));
		Cipher_elg::mult(C_c->at(k),temp, temp_1);
	}
	tstop = (double)clock()/CLOCKS_PER_SEC;
	time_di=0;
	time_di = time_di + (tstop-tstart);
}

void Prover_toom::calculate_Cc(vector<vector<Cipher_elg>* >* C, vector<vector<ZZ>* >* B){
	long i, j, l,k;
	ZZ mod = H.get_mod();
	ZZ gen = H.get_gen().get_val();
	Cipher_elg temp, temp_1;
	ZZ t_1;
	double tstart, tstop;

	tstart = (double)clock()/CLOCKS_PER_SEC;
	for(k=0; k<mu_h; k++){
		temp = Cipher_elg(1,1,mod);
		for(i=0; i<mu; i++){
			j=k+1-mu+i;
			if(j>=0 & j<mu){
				for(l=0; l<m_r; l++){
					multi_expo::expo_mult(temp_1, C->at(4*l+i), B->at(4*l+j), omega_mulex);
					Cipher_elg::mult(temp, temp, temp_1);
				}
			}
		}
		PowerMod(t_1, gen, a_c->at(k), mod);
		temp_1 = El.encrypt(t_1, rho_c->at(k));
		Cipher_elg::mult(C_c->at(k),temp, temp_1);
	}
	tstop = (double)clock()/CLOCKS_PER_SEC;
	time_di=0;
	time_di = time_di + (tstop-tstart);
}

void Prover_toom::calculate_ac_bar(vector<ZZ>* x){
	long i;
	ZZ temp;
	ZZ ord = H.get_ord();

	a_c_bar = a_c->at(0);
	for(i=1; i<mu_h; i++){
		MulMod(temp, a_c->at(i), x->at(i-1), ord);
		AddMod(a_c_bar, a_c_bar, temp, ord);
	}
}

void Prover_toom::calculate_r_ac_bar(vector<ZZ>* x){
	long i;
	ZZ temp;
	ZZ ord = H.get_ord();

	r_ac_bar = r_c->at(0);
	for(i=1; i<mu_h; i++){
		MulMod(temp, r_c->at(i), x->at(i-1), ord);
		AddMod(r_ac_bar, r_ac_bar, temp, ord);
	}
}

void Prover_toom::reduce_C(vector<vector<Cipher_elg>*>* C, vector<vector<ZZ>* >* B, vector<ZZ>* r_B, vector<ZZ>* x, long length){
	long i, j;
	ZZ temp, temp_1;
	ZZ ord  = H.get_ord();
	double tstart, tstop;
	vector<Cipher_elg>* row_C=0;
	vector<ZZ>* row_B=0;
	vector<ZZ>* x_temp = new vector<ZZ>(4);

	tstart= (double)clock()/CLOCKS_PER_SEC;
	x_temp->at(3)=1;
	x_temp->at(2)= x->at(0);
	x_temp->at(1) = x->at(1);
	x_temp->at(0)= x->at(2);

	for(i=0; i<length;i++){
		row_C = new vector<Cipher_elg>(n);
		row_B = new vector<ZZ>(n);
		for(j=0; j<n; j++){
			multi_expo::multi_expo_LL(row_C->at(j), C->at(4*i)->at(j), C->at(4*i+1)->at(j),C->at(4*i+2)->at(j),C->at(4*i+3)->at(j), x_temp, omega_LL);
			temp = B->at(4*i)->at(j);
			MulMod(temp_1, B->at(4*i+1)->at(j), x_temp->at(2), ord);
			AddMod(temp, temp, temp_1, ord);
			MulMod(temp_1, B->at(4*i+2)->at(j), x_temp->at(1), ord);
			AddMod(temp, temp, temp_1, ord);
			MulMod(temp_1, B->at(4*i+3)->at(j), x_temp->at(0), ord);
			AddMod(temp, temp, temp_1, ord);
			row_B->at(j) = temp;
		}
		C_small->at(i)=row_C;
		B_small->at(i)=row_B;
		temp = r_B->at(4*i);
		MulMod(temp_1, r_B->at(4*i+1), x_temp->at(2), ord);
		AddMod(temp, temp, temp_1, ord);
		MulMod(temp_1, r_B->at(4*i+2), x_temp->at(1), ord);
		AddMod(temp, temp, temp_1, ord);
		MulMod(temp_1, r_B->at(4*i+3), x_temp->at(0), ord);
		AddMod(temp, temp, temp_1, ord);
		r_B_small->at(i) = temp;
	}
	tstop = (double)clock()/CLOCKS_PER_SEC;
	time_di = time_di + (tstop-tstart);
	delete x_temp;
}

void Prover_toom::set_Rb1(vector<ZZ>* x){
	long i;
	ZZ temp;
	ZZ ord = H.get_ord();

	R_b = rho_c->at(0);
	for(i=1; i<mu_h; i++){
		MulMod(temp, rho_c->at(i), x->at(i-1), ord);
		AddMod(R_b, R_b, temp, ord);
	}
}

vector<Cipher_elg>* Prover_toom::calculate_e(){
	long k,l;
	Cipher_elg temp;
	ZZ ord = H.get_ord();
	ZZ mod = H.get_mod();
	vector<Cipher_elg>* dt = 0;
	vector<Cipher_elg>* e = new vector<Cipher_elg>(2*m);

	dt = toom4_pow(C_small, B_small);

	multi_expo::expo_mult(e->at(0),C_small->at(mu-1), basis_B0, omega_mulex);
	for (k =1; k<mu; k++){
		multi_expo::expo_mult(temp , C_small->at(mu-k-1), basis_B0, omega_mulex);
		Cipher_elg::mult(e->at(k) ,temp,dt->at(2*mu-k-1));
	}
	l=2*mu;
	for (k = mu; k<l; k++){
		e->at(k) = dt->at(2*mu-k-1);
	}

	delete dt;
	return e;
}

void Prover_toom::calculate_E(vector<Cipher_elg>* e){
	long i,l;
	Mod_p t;
	Mod_p gen = H.get_gen();
	ZZ ord = H.get_ord();

	l=2*mu;
	for (i = 0; i<l; i++){
		rho_a->at(i)= RandomBnd(ord);
	}
	rho_a->at(mu)=R_b ;
	for (i = 0; i<l; i++){
		 t = gen.expo(a->at(i));
		 E->at(i) = El.encrypt(t,rho_a->at(i))*e->at(i);
	}
}

vector<vector<Cipher_elg>* >* Prover_toom::copy_C(){
	long i,j,l;
	vector<Cipher_elg>* row_C;
	l=mu*m_r;
	vector<vector<Cipher_elg>* >* C_small_temp = new vector<vector<Cipher_elg>* >(l);

	for(i=0; i<l; i++){
		row_C = new vector<Cipher_elg>(n);
		for(j=0; j<n; j++){
			row_C->at(j)= C_small->at(i)->at(j);
		}
		C_small_temp ->at(i)= row_C;
		delete C_small->at(i);
		C_small->at(i)=0;
	}
	delete C_small;
	C_small=0;

	return C_small_temp;
}

vector<vector<ZZ>* >* Prover_toom::copy_B(){
	long i, j;
	long l = mu*m_r;
	vector<vector<ZZ>* >* B_small_temp =new vector<vector<ZZ>* >(l);
	vector<ZZ>* row_B;

	for(i=0; i<l; i++){
		row_B = new vector<ZZ>(n);
		for(j=0; j<n; j++){
			row_B->at(j)=B_small->at(i)->at(j);
		}
		B_small_temp->at(i)=row_B;
		delete B_small->at(i);
		B_small->at(i)=0;
	}
	delete B_small;

	return B_small_temp;
}

vector<ZZ>* Prover_toom::copy_r_B(){
	long i;
	long l=mu*m_r;
	vector<ZZ>* r_B_small_temp = new vector<ZZ>(l);
	for(i=0; i<l; i++){
		r_B_small_temp->at(i)=r_B_small->at(i);
	}
	delete r_B_small;
	r_B_small=0;

	return r_B_small_temp;
}

vector<vector<ZZ>*>* Prover_toom::evulation(vector<vector<ZZ>*>* p){
	vector<vector<ZZ>*>* ret;
	vector<ZZ>* row;
	ZZ p0,p1,p2,p3,ord,temp,temp_1;
	long l,i;
	l= p->at(0)->size();
	ord = H.get_ord();
	ret = new vector<vector<ZZ>*>(l);

	for(i = 0; i<l; i++){
		row = new vector<ZZ>(7);
			AddMod(p0,p->at(2)->at(i), p->at(0)->at(i),ord);
			AddMod(p1 ,p->at(3)->at(i) , p->at(1)->at(i), ord);
			MulMod(temp, p->at(2)->at(i),2,ord);
			MulMod(temp_1, p->at(0)->at(i), 8, ord);
			AddMod(p2 , temp , temp_1,ord);
			MulMod(temp, p->at(1)->at(i), 4, ord);
			AddMod(p3 ,p->at(3)->at(i) , temp,ord);

			row->at(0) = p->at(3)->at(i);
			MulMod(temp_1, p->at(1)->at(i), 2,ord);
			AddMod(temp,temp_1 , p->at(0)->at(i), ord);
			MulMod(temp_1, p->at(2)->at(i),4, ord);
			AddMod(temp,temp,temp_1,ord);
			MulMod(temp_1, p->at(3)->at(i), 8, ord);
			AddMod(row->at(1), temp,temp_1, ord);
			AddMod(row->at(2) , p0,p1,ord);
			SubMod(row->at(3) , p0,p1,ord);
			AddMod(row ->at(4) , p2,p3,ord);
			SubMod(row ->at(5),p2,p3,ord);
			row->at(6) = p->at(0)->at(i);
			ret->at(i) = row;
	}
	return ret;
}

vector<vector<vector<ZZ>*>*>* Prover_toom::evulation_pow(vector<vector<Cipher_elg>*>* p){
	vector<vector<vector<ZZ>*>*>* ret;
	vector<vector<ZZ>* >* ret_u;
	vector<vector<ZZ>* >* ret_v;
	vector<ZZ>* row_u;
	vector<ZZ>* row_v;
	ZZ p0_u,p1_u,p2_u,p3_u,temp_u, temp_1_u;
	ZZ p0_v,p1_v,p2_v,p3_v,temp_v, temp_1_v;
	ZZ mod = H.get_mod();
	long l, i;
	l = p->at(0)->size();

	ret = new vector<vector<vector<ZZ>*>*>(2);
	ret_u = new vector<vector<ZZ>*>(l);
	ret_v = new vector<vector<ZZ>*>(l);

	for(i = 0; i<l; i++){
		row_u = new vector<ZZ>(7);
		row_v = new vector<ZZ>(7);
		MulMod(p0_u,p->at(1)->at(i).get_u(), p->at(3)->at(i).get_u(),mod);
		MulMod(p0_v,p->at(1)->at(i).get_v(), p->at(3)->at(i).get_v(),mod);
		MulMod(p1_u ,p->at(0) ->at(i).get_u(), p->at(2)->at(i).get_u(), mod);
		MulMod(p1_v ,p->at(0) ->at(i).get_v(), p->at(2)->at(i).get_v(), mod);
		PowerMod(temp_u, p->at(1)->at(i).get_u(), 2,mod);
		PowerMod(temp_v, p->at(1)->at(i).get_v(), 2,mod);
		PowerMod(temp_1_u, p->at(3)->at(i).get_u(), 8,mod);
		PowerMod(temp_1_v, p->at(3)->at(i).get_v(), 8,mod);
		MulMod(p2_u ,temp_u , temp_1_u,mod);
		MulMod(p2_v ,temp_v , temp_1_v,mod);
		PowerMod(temp_u, p->at(2)->at(i).get_u(), 4, mod);
		PowerMod(temp_v, p->at(2)->at(i).get_v(), 4, mod);
		MulMod(p3_u ,p->at(0)->at(i).get_u() , temp_u,mod);
		MulMod(p3_v ,p->at(0)->at(i).get_v() , temp_v,mod);

		row_u->at(0) = p->at(0)->at(i).get_u();
		PowerMod(temp_u, p->at(2)->at(i).get_u(), 2,mod);
		MulMod(temp_u,temp_u , p->at(3)->at(i).get_u(), mod);
		PowerMod(temp_1_u, p->at(1)->at(i).get_u(), 4,mod);
		MulMod(temp_u,temp_u,temp_1_u,mod);
		PowerMod(temp_1_u, p->at(0)->at(i).get_u(), 8, mod);
		MulMod(row_u->at(1), temp_u,temp_1_u,mod);
		MulMod(row_u->at(2) , p0_u,p1_u,mod);
		InvMod(temp_u, p1_u, mod);
		MulMod(row_u->at(3) , p0_u,temp_u,mod);
		MulMod(row_u ->at(4) , p2_u,p3_u,mod);
		InvMod(temp_u, p3_u, mod);
		MulMod(row_u ->at(5),p2_u,temp_u,mod);
		row_u->at(6) = p->at(3)->at(i).get_u();

		row_v->at(0) = p->at(0)->at(i).get_v();
		PowerMod(temp_v, p->at(2)->at(i).get_v(), 2,mod);
		MulMod(temp_v,temp_v , p->at(3)->at(i).get_v(), mod);
		PowerMod(temp_1_v, p->at(1)->at(i).get_v(), 4,mod);
		MulMod(temp_v,temp_v,temp_1_v,mod);
		PowerMod(temp_1_v, p->at(0)->at(i).get_v(), 8, mod);
		MulMod(row_v->at(1), temp_v,temp_1_v,mod);
		MulMod(row_v->at(2) , p0_v,p1_v,mod);
		InvMod(temp_v, p1_v, mod);
		MulMod(row_v->at(3) , p0_v,temp_v,mod);
		MulMod(row_v ->at(4) , p2_v,p3_v,mod);
		InvMod(temp_v, p3_v, mod);
		MulMod(row_v ->at(5),p2_v,temp_v,mod);
		row_v->at(6) = p->at(3)->at(i).get_v();

		ret_u->at(i) = row_u;
		ret_v->at(i) = row_v;
	}
	ret->at(0) = ret_u;
	ret->at(1) = ret_v;
	return ret;
}

vector<vector<vector<ZZ>*>*>* Prover_toom::point_pow(vector<vector<vector<ZZ>*>*>* points_p, vector<vector<ZZ>*>* points_q){
	long i,j,l;
	vector<vector<vector<ZZ>*>*>* ret;
	vector<vector<ZZ>*>* ret_u;
	vector<vector<ZZ>*>* ret_v;
	vector<ZZ>* row_u;
	vector<ZZ>* row_v;
	ZZ mod = H.get_mod();
	l = points_p->at(0)->size();

	ret = new vector<vector<vector<ZZ>*>*>(2);
	ret_u = new vector<vector<ZZ>*>(l);
	ret_v = new vector<vector<ZZ>*>(l);
	for(j = 0; j<l; j++){
		row_u = new vector<ZZ>(7);
		row_v = new vector<ZZ>(7);
		for(i=0; i<7; i++){
			PowerMod(row_u->at(i) , points_p->at(0)->at(j)->at(i), points_q->at(j)->at(i),mod);
			PowerMod(row_v->at(i) , points_p->at(1)->at(j)->at(i), points_q->at(j)->at(i),mod);

		}
		ret_u->at(j) = row_u;
		ret_v->at(j) = row_v;
	}
	ret->at(0)= ret_u;
	ret->at(1) = ret_v;
	for(i = 0; i< l ; i++){
		delete points_p->at(0)->at(i);
		points_p->at(0)->at(i)=0;
		delete points_p->at(1)->at(i);
		points_p->at(1)->at(i)=0;
	}
	delete points_p->at(0);
	delete points_p->at(1);
	delete points_p;
	for(i = 0; i< l ; i++){
		delete points_q->at(i);
		points_q->at(i)=0;
	}
	delete points_q;
	return ret;
}

vector<vector<ZZ>*>* Prover_toom::mult_points(vector<vector<vector<ZZ>* >*>* points){
	long i,l,j;
	vector<vector<ZZ>*>* ret = new vector<vector<ZZ>*>(2);
	vector<ZZ>* ret_u = new vector<ZZ>(7);
	vector<ZZ>* ret_v = new vector<ZZ>(7);
	l = points->at(0)->size();
	ZZ temp_u, temp_v;
	ZZ mod = H.get_mod();

	for(i = 0; i<7; i++){
		temp_u = 1;
		temp_v = 1;
		for(j = 0; j<l; j++){
			MulMod(temp_u, temp_u, points->at(0)->at(j)->at(i),mod);
			MulMod(temp_v, temp_v, points->at(1)->at(j)->at(i),mod);
		}
		ret_u->at(i) = temp_u;
		ret_v->at(i) = temp_v;
	}
	for(i = 0; i<l;i++){
		delete points->at(0)->at(i);
		points->at(0)->at(i) = 0;
		delete points->at(1)->at(i);
		points->at(1)->at(i) = 0;
	}
	delete points->at(0);
	points->at(0) =0;
	delete points->at(1);
	points->at(1) =0;
	delete points;
	ret->at(0) = ret_u;
	ret->at(1) = ret_v;
	return ret;
}

vector<ZZ>* Prover_toom::interpolation_pow(vector<ZZ>* points){
	vector<ZZ>* ret = new vector<ZZ>(7);
	ZZ r1,r2,r3,r4,r5,r6,r7,temp;
	ZZ ord = H.get_ord();
	ZZ mod = H.get_mod();

	r1 = points->at(0);
	r2 = points->at(1);
	r3 = points->at(2);
	r4 = points->at(3);
	r5 = points->at(4);
	r6 = points->at(5);
	r7 = points->at(6);

	MulMod(r2 ,r2, r5, mod);
	InvMod(temp,r5,mod);
	MulMod(r6 , r6,temp, mod);
	InvMod(temp, r3, mod);
	MulMod(r4 , r4,temp,mod);
	InvMod(temp, r1, mod);
	MulMod(r5,r5,temp,mod);
	PowerMod(temp, r7, 64,mod);
	InvMod(temp, temp, mod);
	MulMod(r5, r5,temp,mod);
	InvMod(temp,to_ZZ(2),ord);
	PowerMod(r4,r4,temp,mod);
	MulMod(r3, r3,r4,mod);
	PowerMod(temp, r5,2, mod);
	MulMod(r5 , temp , r6,mod);

	PowerMod(temp, r3, 65,mod);
	InvMod(temp, temp, mod);
	MulMod(r2 , r2,temp,mod);
	PowerMod(r4 ,r4,-1,mod);
	PowerMod(r6 , r6,-1,mod);
	InvMod(temp, r7, mod);
	MulMod(r3, r3, temp,mod);
	InvMod(temp, r1, mod);
	MulMod(r3 , r3,temp,mod);
	PowerMod(temp, r3, 45, mod);
	MulMod(r2 , r2,temp,mod);
	PowerMod(temp, r3, 8, mod);
	InvMod(temp, temp, mod);
	MulMod(r5 , r5,temp,mod);

	InvMod(temp,to_ZZ(24),ord);
	PowerMod(r5 , r5,temp, mod);
	InvMod(temp, r2, mod);
	MulMod(r6 , r6,temp,mod);
	PowerMod(temp, r4, 16, mod);
	InvMod(temp, temp, mod);
	MulMod(r2 , r2,temp,mod);
	InvMod(temp,to_ZZ(18), ord);
	PowerMod(r2 ,r2, temp, mod);
	InvMod(temp, r5, mod);
	MulMod(r3 , r3,temp,mod);
	InvMod(temp, r2, mod);
	MulMod(r4, r4,temp,mod);
	PowerMod(temp, r2, 30, mod);
	MulMod(r6 , r6,temp,mod);
	InvMod(temp, to_ZZ(60), ord);
	PowerMod(r6,  r6, temp, mod);
	InvMod(temp, r6, mod);
	MulMod(r2 , r2 ,temp,mod);

	ret->at(0)= r1;
	ret->at(1) = r2;
	ret->at(2) = r3;
	ret ->at(3) = r4;
	ret->at(4) = r5;
	ret->at(5) = r6;
	ret->at(6) = r7;

	return ret;
}

vector<Cipher_elg>* Prover_toom::toom4_pow(vector<vector<Cipher_elg>*>* p, vector<vector<ZZ>*>* q){
	vector<vector<vector<ZZ>*>*>* points_p;
	vector<vector<ZZ>*>* points_q;
	vector<vector<vector<ZZ>*>*>* points_temp;
	vector<vector<ZZ>*>* points;
	vector<ZZ>* ret_u;
	vector<ZZ>* ret_v;
	vector<Cipher_elg>* ret = new vector<Cipher_elg>(7);
	long i,l;
	ZZ mod = H.get_mod();
	points_p = evulation_pow(p);
	points_q = evulation(q);
	points_temp = point_pow(points_p, points_q);
	points = mult_points(points_temp);
	ret_u = interpolation_pow(points->at(0));
	ret_v = interpolation_pow(points->at(1));
	l = points->size();
	for(i = 0; i<l; i++){
		delete points->at(i);
		points->at(i)=0;
	}
	delete points;

	for(i = 0; i<7; i++){
		ret->at(i)= Cipher_elg(ret_u->at(i), ret_v->at(i), mod);
	}
	delete ret_u;
	delete ret_v;
	return ret;
}


/*!
@file
Internal header to break cyclic dependencies.

@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#ifndef BOOST_HANA_MONOID_DETAIL_MONOID_FWD_HPP
#define BOOST_HANA_MONOID_DETAIL_MONOID_FWD_HPP

#include <boost/hana/core/typeclass.hpp>
#include <boost/hana/detail/constexpr.hpp>

namespace boost { namespace hana {
    namespace monoid_detail { namespace operators { struct enable { }; }}

    //! @ingroup group-typeclasses
    //! The `Monoid` type class is used for data types with an associative
    //! binary operation that has an identity.
    //!
    //! The method names refer to the monoid of numbers under addition, but
    //! there are many other instances such as sequences under concatenation.
    //! Some datatypes can be viewed as a monoid in more than one way, e.g.
    //! both addition and multiplication on numbers.
    //!
    //! ### Laws
    //! For all objects `x`, `y` and `z` whose data type `M` is a `Monoid`,
    //! the following laws must be satisfied:
    //! @code
    //!     plus(zero<M>, x) == x                      // left zero
    //!     plus(x, zero<M>) == x                      // right zero
    //!     plus(x, plus(y, z)) == plus(plus(x, y), z) // associativity
    //! @endcode
    struct Monoid {
        BOOST_HANA_BINARY_TYPECLASS(Monoid);

        //! Minimal complete definition : `zero` and `plus`
        struct mcd { };

        struct laws;

        using operators = monoid_detail::operators::enable;
    };

    //! Associative operation on a `Monoid`.
    //! @relates Monoid
    //!
    //! ### Example
    //! @snippet example/monoid/plus.cpp main
    BOOST_HANA_CONSTEXPR_LAMBDA auto plus = [](auto x, auto y) {
        return Monoid::instance<
            datatype_t<decltype(x)>, datatype_t<decltype(y)>
        >::plus_impl(x, y);
    };

    //! Identity of `plus`.
    //! @relates Monoid
    //!
    //! Since `Monoid` is a binary type class and `zero` is a nullary method,
    //! `zero<M>` is dispatched to the type class instance for `M` and `M`,
    //! i.e. `Monoid::instance<M, M>`.
    //!
    //! @tparam M
    //! The data type (a `Monoid`) of the returned identity.
    //!
    //! ### Example
    //! @snippet example/monoid/zero.cpp main
    template <typename M>
    constexpr auto zero = Monoid::instance<M, M>::zero_impl();

    namespace monoid_detail { namespace operators {
        //! Equivalent to `plus`.
        //! @relates boost::hana::Monoid
        template <typename C1, typename C2>
        constexpr auto operator+(C1 c1, C2 c2)
        { return plus(c1, c2); }
    }}
}} // end namespace boost::hana

#endif // !BOOST_HANA_MONOID_DETAIL_MONOID_FWD_HPP


/*    Copyright (c) 2010-2015, Delft University of Technology
 *    All rights reserved.
 *
 *    Redistribution and use in source and binary forms, with or without modification, are
 *    permitted provided that the following conditions are met:
 *      - Redistributions of source code must retain the above copyright notice, this list of
 *        conditions and the following disclaimer.
 *      - Redistributions in binary form must reproduce the above copyright notice, this list of
 *        conditions and the following disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *      - Neither the name of the Delft University of Technology nor the names of its contributors
 *        may be used to endorse or promote products derived from this software without specific
 *        prior written permission.
 *
 *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
 *    OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *    COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *    AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 *    OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *    Changelog
 *      YYMMDD    Author            Comment
 *      110620    F.M. Engelen      File created.
 *      110721    J. Melman         Comments, variable names, and consistency modified.
 *      110722    F.M. Engelen      Removed setRelativePath function.
 *
 *    References
 *
 *    Notes
 *
 */

#include <iostream>

#include <boost/make_shared.hpp>

#include "Tudat/InputOutput/matrixTextFileReader.h"

#include "Tudat/Astrodynamics/Aerodynamics/tabulatedAtmosphere.h"

namespace tudat
{
namespace aerodynamics
{

//! Initialize atmosphere table reader.
void TabulatedAtmosphere::initialize( const std::string& atmosphereTableFile )
{
    // Locally store the atmosphere table file name.
    atmosphereTableFile_ = atmosphereTableFile;

    Eigen::MatrixXd containerOfAtmosphereTableFileData
            = input_output::readMatrixFromFile( atmosphereTableFile_, " \t", "%" );

    // Check whether data is present in the file.
    if ( containerOfAtmosphereTableFileData.rows( ) < 1
         || containerOfAtmosphereTableFileData.cols( ) < 1 )
    {
        std::cerr << "The atmosphere table file is empty." << std::endl;
        std::cerr << atmosphereTableFile_ << std::endl;
    }

    // Initialize vectors.
    altitudeData_.resize( containerOfAtmosphereTableFileData.rows( ) );
    densityData_.resize( containerOfAtmosphereTableFileData.rows( ) );
    pressureData_.resize( containerOfAtmosphereTableFileData.rows( ) );
    temperatureData_.resize( containerOfAtmosphereTableFileData.rows( ) );

    // Loop through all the strings stored in the container and store the data
    // in the right Eigen::VectorXd.
    for ( int i = 0; i < containerOfAtmosphereTableFileData.rows( ); i++  )
    {
        altitudeData_[ i ] = containerOfAtmosphereTableFileData( i, 0 );
        densityData_[ i ] = containerOfAtmosphereTableFileData( i, 1 );
        pressureData_[ i ] = containerOfAtmosphereTableFileData( i, 2 );
        temperatureData_[ i ] = containerOfAtmosphereTableFileData( i, 3 );
    }

    using namespace interpolators;

    cubicSplineInterpolationForDensity_
            = boost::make_shared< CubicSplineInterpolatorDouble >( altitudeData_, densityData_ );
    cubicSplineInterpolationForPressure_
            = boost::make_shared< CubicSplineInterpolatorDouble >( altitudeData_, pressureData_ );
    cubicSplineInterpolationForTemperature_
            = boost::make_shared< CubicSplineInterpolatorDouble >(
                altitudeData_, temperatureData_ );
}

} // namespace aerodynamics
} // namespace tudat