/*******************************************************************************
 *
 * Standard domain of intervals.
 *
 * Author: Arnaud J. Venet (arnaud.j.venet@nasa.gov)
 *
 * Contributors: Alexandre C. D. Wimmers (alexandre.c.wimmers@nasa.gov)
 *
 * Notices:
 *
 * Copyright (c) 2011 United States Government as represented by the
 * Administrator of the National Aeronautics and Space Administration.
 * All Rights Reserved.
 *
 * Disclaimers:
 *
 * No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF
 * ANY KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED
 * TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS,
 * ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
 * OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL BE
 * ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, WILL CONFORM TO
 * THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN
 * ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS,
 * RESULTING DESIGNS, HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS
 * RESULTING FROM USE OF THE SUBJECT SOFTWARE.  FURTHER, GOVERNMENT AGENCY
 * DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING THIRD-PARTY SOFTWARE,
 * IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS."
 *
 * Waiver and Indemnity:  RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST
 * THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL
 * AS ANY PRIOR RECIPIENT.  IF RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS
 * IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES OR LOSSES ARISING FROM SUCH
 * USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING FROM,
 * RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD
 * HARMLESS THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS,
 * AS WELL AS ANY PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY LAW.
 * RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE IMMEDIATE,
 * UNILATERAL TERMINATION OF THIS AGREEMENT.
 *
 ******************************************************************************/

#pragma once

#include <boost/optional.hpp>
#include <crab/common/types.hpp>
#include <crab/common/stats.hpp>
#include <crab/common/bignums.hpp>
#include <crab/domains/linear_constraints.hpp>
#include <crab/domains/linear_interval_solver.hpp>
#include <crab/domains/separate_domains.hpp>
#include <crab/domains/operators_api.hpp>
#include <crab/domains/backward_assign_operations.hpp>

namespace ikos {

  template< typename Number >
  class bound;

  template< typename Number >
  class bound: public writeable {

    template < typename Any > friend class bound;

  public:
    typedef bound< Number > bound_t;

  private:
    bool _is_infinite;
    Number _n;

  private:
    bound();

    bound(bool is_infinite, Number n): _is_infinite(is_infinite), _n(n) {
      if (is_infinite){
        if (n > 0)
          this->_n = 1;
        else
          this->_n = -1;
      }
    }

  public:
    static bound_t min(bound_t x, bound_t y) {
      return (x.operator<=(y) ? x : y);
    }

    static bound_t min(bound_t x, bound_t y, bound_t z) {
      return min(x, min(y, z));
    }

    static bound_t min(bound_t x, bound_t y, bound_t z, bound_t t) {
      return min(x, min(y, z, t));
    }

    static bound_t max(bound_t x, bound_t y) {
      return (x.operator<=(y) ? y : x);
    }

    static bound_t max(bound_t x, bound_t y, bound_t z) {
      return max(x, max(y, z));
    }

    static bound_t max(bound_t x, bound_t y, bound_t z, bound_t t) {
      return max(x, max(y, z, t));
    }

    static bound_t plus_infinity() {
      return bound_t(true, 1);
    }

    static bound_t minus_infinity() {
      return bound_t(true, -1);
    }

  public:
    bound(int n): _is_infinite(false), _n(n) { }

    bound(std::string s): _n(1) {
      if (s == "+oo") {
        this->_is_infinite = true;
      } else if (s == "-oo") {
        this->_is_infinite = true;
        this->_n = -1;
      } else {
        this->_is_infinite = false;
        this->_n = Number(s);
      }
    }

    bound(Number n): _is_infinite(false), _n(n) { }

    bound(const bound_t& o): writeable(), _is_infinite(o._is_infinite), _n(o._n) { }

    bound_t& operator=(const bound_t &o){
      if (this != &o) {
        this->_is_infinite = o._is_infinite;
        this->_n = o._n;
      }
      return *this;
    }

    bool is_infinite() const {
      return this->_is_infinite;
    }

    bool is_finite() const {
      return !this->_is_infinite;
    }

    bool is_plus_infinity() const {
      return (this->is_infinite() && this->_n > 0);
    }

    bool is_minus_infinity() const {
      return (this->is_infinite() && this->_n < 0);
    }

    bound_t operator-() const {
      return bound_t(this->_is_infinite, -this->_n);
    }

    bound_t operator+(bound_t x) const {
      if (this->is_finite() && x.is_finite()) {
        return bound_t(this->_n + x._n);
      } else if (this->is_finite() && x.is_infinite()) {
        return x;
      } else if (this->is_infinite() && x.is_finite()) {
        return *this;
      } else if (this->_n == x._n) {
        return *this;
      } else {
        CRAB_ERROR("Bound: undefined operation -oo + +oo");
      }
    }

    bound_t& operator+=(bound_t x)  {
      return this->operator=(this->operator+(x));
    }

    bound_t operator-(bound_t x) const {
      return this->operator+(x.operator-());
    }

    bound_t& operator-=(bound_t x)  {
      return this->operator=(this->operator-(x));
    }

    bound_t operator*(bound_t x) const {
      if (x._n == 0)
        return x;
      else if (this->_n == 0)
        return *this;
      else
        return bound_t(this->_is_infinite || x._is_infinite, this->_n * x._n);
    }

    bound_t& operator*=(bound_t x)  {
      return this->operator=(this->operator*(x));
    }

    bound_t operator/(bound_t x) const {
      if (x._n == 0) {
        CRAB_ERROR("Bound: division by zero");
      } else if (this->is_finite() && x.is_finite()) {
        return bound_t(false, _n / x._n);
      } else if (this->is_finite() && x.is_infinite()) {
        if (this->_n > 0) {
          return x;
        } else if (this->_n == 0) {
          return *this;
        } else {
          return x.operator-();
        }
      } else if (this->is_infinite() && x.is_finite()) {
        if (x._n > 0) {
          return *this;
        } else {
          return this->operator-();
        }
      } else {
        return bound_t(true, this->_n * x._n);
      }
    }

    bound_t& operator/=(bound_t x) {
      return this->operator=(this->operator/(x));
    }

    bool operator<(bound_t x) const {
      return !this->operator>=(x);
    }

    bool operator>(bound_t x) const {
      return !this->operator<=(x);
    }

    bool operator==(bound_t x) const {
      return (this->_is_infinite == x._is_infinite && this->_n == x._n);
    }

    bool operator!=(bound_t x) const {
      return !this->operator==(x);
    }

    /*	operator<= and operator>= use a somewhat optimized implementation.
     *	results include up to 20% improvements in performance in the octagon domain
     *	over a more naive implementation.
     */
    bool operator<=(bound_t x) const {
      if(this->_is_infinite xor x._is_infinite){
        if(this->_is_infinite){
          return this->_n < 0;
        }
        return x._n > 0;
      }
      return this->_n <= x._n;
    }

    bool operator>=(bound_t x) const {
      if(this->_is_infinite xor x._is_infinite){
        if(this->_is_infinite){
          return this->_n > 0;
        }
        return x._n < 0;
      }
      return this->_n >= x._n;
    }

    bound_t abs() const {
      if (this->operator>=(0)) {
        return *this;
      } else {
        return this->operator-();
      }
    }

    boost::optional< Number > number() const {
      if (this->is_infinite()) {
        return boost::optional< Number >();
      } else {
        return boost::optional< Number >(this->_n);
      }
    }

    void write(crab::crab_os& o) {
      if (this->is_plus_infinity()) {
        o << "+oo";
      } else if (this->is_minus_infinity()) {
        o << "-oo";
      } else {
        o << this->_n;
      }
    }

  }; // class bound

  typedef bound< z_number > z_bound;
  typedef bound< q_number > q_bound;

  namespace bounds_impl {
    // Conversion between z_bound and q_bound
    // template<class B1, class B2>
    // inline void convert_bounds(B1 b1, B2& b2);

    inline void convert_bounds(z_bound b1, z_bound &b2)
    { std::swap (b1,b2); }
    inline void convert_bounds(q_bound b1, q_bound &b2)
    { std::swap (b1,b2); }
    inline void convert_bounds(z_bound b1, q_bound &b2)
    {
      if (b1.is_plus_infinity())
	b2 = q_bound::plus_infinity();
      else if (b1.is_minus_infinity())
	b2 = q_bound::minus_infinity();
      else
	b2 = q_bound (q_number(*b1.number()));
    }
    inline void convert_bounds(q_bound b1, z_bound &b2)
    {
      if (b1.is_plus_infinity())
	b2 = z_bound::plus_infinity();
      else if (b1.is_minus_infinity())
	b2 = z_bound::minus_infinity();
      else
	b2 = z_bound ((*(b1.number())).round_to_lower ());
    }
  }

  template< typename Number >
  class interval;

  template< typename Number >
  class interval: public writeable {

  public:
    typedef bound< Number > bound_t;
    typedef interval< Number > interval_t;

  private:
    bound_t _lb;
    bound_t _ub;

  public:
    static interval_t top() {
      return interval_t(bound_t::minus_infinity(), bound_t::plus_infinity());
    }

    static interval_t bottom() {
      return interval_t();
    }

  private:
    interval(): _lb(0), _ub(-1) { }

    static Number abs(Number x) { return x < 0 ? -x : x; }

    static Number max(Number x, Number y) { return x.operator<=(y) ? y : x; }

    static Number min(Number x, Number y) { return x.operator<(y) ? x : y; }

  public:
    interval(bound_t lb, bound_t ub): _lb(lb), _ub(ub) {
      if (lb > ub) {
        this->_lb = 0;
        this->_ub = -1;
      }
    }

    interval(bound_t b): _lb(b), _ub(b) {
      if (b.is_infinite()) {
        this->_lb = 0;
        this->_ub = -1;
      }
    }

    interval(Number n): _lb(n), _ub(n) { }

    interval(std::string b): _lb(b), _ub(b) {
      if (this->_lb.is_infinite()) {
        this->_lb = 0;
        this->_ub = -1;
      }
    }

    interval(const interval_t& i): writeable(), _lb(i._lb), _ub(i._ub) { }

    interval_t& operator=(interval_t i){
      this->_lb = i._lb;
      this->_ub = i._ub;
      return *this;
    }

    bound_t lb() const {
      return this->_lb;
    }

    bound_t ub() const {
      return this->_ub;
    }

    bool is_bottom() const {
      return (this->_lb > this->_ub);
    }

    bool is_top() const {
      return (this->_lb.is_infinite() && this->_ub.is_infinite());
    }

    interval_t lower_half_line() const {
      return interval_t(bound_t::minus_infinity(), this->_ub);
    }

    interval_t upper_half_line() const {
      return interval_t(this->_lb, bound_t::plus_infinity());
    }

    bool operator==(interval_t x) const {
      if (is_bottom()) {
        return x.is_bottom();
      } else {
        return (this->_lb == x._lb) && (this->_ub == x._ub);
      }
    }

    bool operator!=(interval_t x) const {
      return !this->operator==(x);
    }

    bool operator<=(interval_t x) const {
      if (this->is_bottom()) {
        return true;
      } else if (x.is_bottom()) {
        return false;
      } else {
        return (x._lb <= this->_lb) && (this->_ub <= x._ub);
      }
    }

    interval_t operator|(interval_t x) const {
      if (this->is_bottom()) {
        return x;
      } else if (x.is_bottom()) {
        return *this;
      } else {
	return interval_t(bound_t::min(this->_lb, x._lb),
                            bound_t::max(this->_ub, x._ub));
      }
    }

    interval_t operator&(interval_t x) const {
      if (this->is_bottom() || x.is_bottom()) {
        return this->bottom();
      } else {
        return interval_t(bound_t::max(this->_lb, x._lb),
                          bound_t::min(this->_ub, x._ub));
      }
    }

    interval_t operator||(interval_t x) const {
      if (this->is_bottom()) {
	return x;
      } else if (x.is_bottom()) {
	return *this;
      } else {
        return interval_t(x._lb < this->_lb ?
                          bound_t::minus_infinity() :
                          this->_lb,
                          this->_ub < x._ub ?
                          bound_t::plus_infinity() :
                          this->_ub);
      }
    }

    template<typename Thresholds>
    interval_t widening_thresholds (interval_t x, const Thresholds &ts) {
      if (this->is_bottom()) {
	return x;
      } else if (x.is_bottom()) {
	return *this;
      } else {
        bound_t lb = (x._lb < this->_lb ? ts.get_prev (x._lb) : this->_lb);
        bound_t ub = (this->_ub < x._ub ? ts.get_next (x._ub) :  this->_ub);
        return interval_t(lb, ub);
      }
    }

    interval_t operator&&(interval_t x) const {
      if (this->is_bottom() || x.is_bottom()) {
        return this->bottom();
      } else {
        return interval_t(this->_lb.is_infinite() && x._lb.is_finite() ?
                          x._lb : this->_lb,
                          this->_ub.is_infinite() && x._ub.is_finite() ?
                          x._ub : this->_ub);
      }
    }

    interval_t operator+(interval_t x) const {
      if (this->is_bottom() || x.is_bottom()) {
        return this->bottom();
      } else {
	return interval_t(this->_lb + x._lb, this->_ub + x._ub);
      }
    }

    interval_t& operator+=(interval_t x) {
      return this->operator=(this->operator+(x));
    }

    interval_t operator-() const {
      if (this->is_bottom()) {
        return this->bottom();
      } else {
        return interval_t(-this->_ub, -this->_lb);
      }
    }

    interval_t operator-(interval_t x) const {
      if (this->is_bottom() || x.is_bottom()) {
        return this->bottom();
      } else {
        return interval_t(this->_lb - x._ub, this->_ub - x._lb);
      }
    }

    interval_t& operator-=(interval_t x) {
      return this->operator=(this->operator-(x));
    }

    interval_t operator*(interval_t x) const {
      if (this->is_bottom() || x.is_bottom()) {
        return this->bottom();
      } else {
        bound_t ll = this->_lb * x._lb;
        bound_t lu = this->_lb * x._ub;
        bound_t ul = this->_ub * x._lb;
        bound_t uu = this->_ub * x._ub;
        return interval_t(bound_t::min(ll, lu, ul, uu),
                          bound_t::max(ll, lu, ul, uu));
      }
    }

    interval_t& operator*=(interval_t x) {
      return this->operator=(this->operator*(x));
    }

    interval_t operator/(interval_t x) const;

    interval_t& operator/=(interval_t x) {
      return this->operator=(this->operator/(x));
    }

    boost::optional< Number > singleton() const {
      if (!this->is_bottom() && this->_lb == this->_ub) {
        return this->_lb.number();
      } else {
        return boost::optional< Number >();
      }
    }

    bool operator[](Number n) const {
      if (this->is_bottom()) {
        return false;
      } else {
        bound_t b(n);
        return (this->_lb <= b) && (b <= this->_ub);
      }
    }

    void write(crab::crab_os& o) {
      if (is_bottom()) {
        o << "_|_";
      } else {
        o << "[" << _lb << ", " << _ub << "]";
      }
    }

    // division and remainder operations

    interval_t UDiv(interval_t x ) const {
      if (this->is_bottom() || x.is_bottom()) {
        return this->bottom();
      } else {
        return this->top();
      }
    }

    interval_t SRem(interval_t x)  const {
      if (this->is_bottom() || x.is_bottom()) {
        return this->bottom();
      } else {
        return this->top();
      }
    }

    interval_t URem(interval_t x)  const {
      if (this->is_bottom() || x.is_bottom()) {
        return this->bottom();
      } else {
        return this->top();
      }
    }

    // bitwise operations
    interval_t And(interval_t x) const {
      if (this->is_bottom() || x.is_bottom()) {
        return this->bottom();
      } else {
        return this->top();
      }
    }

    interval_t Or(interval_t x)  const {
      if (this->is_bottom() || x.is_bottom()) {
        return this->bottom();
      } else {
        return this->top();
      }
    }

    interval_t Xor(interval_t x) const { return this->Or(x); }

    interval_t Shl(interval_t x) const {
      if (this->is_bottom() || x.is_bottom()) {
        return this->bottom();
      } else {
        return this->top();
      }
    }

    interval_t LShr(interval_t  x) const {
      if (this->is_bottom() || x.is_bottom()) {
        return this->bottom();
      } else {
        return this->top();
      }
    }

    interval_t AShr(interval_t  x) const {
      if (this->is_bottom() || x.is_bottom()) {
        return this->bottom();
      } else {
        return this->top();
      }
    }

  };//  class interval

  template<>
  inline interval< q_number > interval< q_number >::
  operator/(interval< q_number > x) const {
    if (this->is_bottom() || x.is_bottom()) {
      return this->bottom();
    } else {
      boost::optional< q_number > d = x.singleton();
      if (d && *d == 0) {
        // [_, _] / 0 = _|_
        return this->bottom();
      } else if (x[0]) {
        boost::optional< q_number > n = this->singleton();
        if (n && *n == 0) {
          // 0 / [_, _] = 0
          return interval_t(q_number(0));
        } else {
          return this->top();
        }
      } else {
        bound_t ll = this->_lb / x._lb;
        bound_t lu = this->_lb / x._ub;
        bound_t ul = this->_ub / x._lb;
        bound_t uu = this->_ub / x._ub;
        return interval_t(bound_t::min(ll, lu, ul, uu),
                          bound_t::max(ll, lu, ul, uu));
      }
    }
  }

  template<>
  inline interval< z_number > interval< z_number >::
  operator/(interval< z_number > x) const {
    if (this->is_bottom() || x.is_bottom()) {
      return this->bottom();
    } else {
      // Divisor is a singleton:
      //   the linear interval solver can perform many divisions where
      //   the divisor is a singleton interval. We optimize for this case.
      if (boost::optional<z_number> n = x.singleton()) {
	z_number c = *n;
	if (c == 1) {
	  return *this;
	} else if (c > 0) {
	  return interval_t(_lb / c, _ub / c);
	} else if (c < 0) {
	  return interval_t(_ub / c, _lb / c);
	} else {}
      }
      // Divisor is not a singleton
      typedef interval< z_number > z_interval;
      if (x[0]) {
        z_interval l(x._lb, z_bound(-1));
        z_interval u(z_bound(1), x._ub);
        return (this->operator/(l) | this->operator/(u));
      } else if (this->operator[](0)) {
        z_interval l(this->_lb, z_bound(-1));
        z_interval u(z_bound(1), this->_ub);
        return ((l / x) | (u / x) | z_interval(z_number(0)));
      } else {
        // Neither the dividend nor the divisor contains 0
        z_interval a = (this->_ub < 0) ?
            (*this + ((x._ub < 0) ?
                      (x + z_interval(z_number(1))) :
                      (z_interval(z_number(1)) - x))) : *this;
	bound_t ll = a._lb / x._lb;
	bound_t lu = a._lb / x._ub;
	bound_t ul = a._ub / x._lb;
	bound_t uu = a._ub / x._ub;
	return interval_t(bound_t::min(ll, lu, ul, uu),
			  bound_t::max(ll, lu, ul, uu));
      }
    }
  }

  template <>
  inline interval< z_number > interval< z_number >::
  SRem(interval< z_number > x) const {
    // note that the sign of the divisor does not matter

    if (this->is_bottom() || x.is_bottom()) {
      return this->bottom();
    } else if (this->singleton() && x.singleton()) {
      z_number dividend = *this->singleton();
      z_number divisor = *x.singleton();

      if (divisor == 0) {
        return this->bottom();
      }

      return interval_t(dividend % divisor);
    } else if (x.ub().is_finite() && x.lb().is_finite()) {
      z_number max_divisor = max(abs(*x.lb().number()),
                                 abs(*x.ub().number()));

      if (max_divisor == 0) {
        return this->bottom();
      }

      if (this->lb() < 0) {
        if (this->ub() > 0) {
          return interval_t(-(max_divisor - 1), max_divisor - 1);
        } else {
          return interval_t(-(max_divisor - 1), 0);
        }
      } else {
        return interval_t(0, max_divisor - 1);
      }
    } else {
      return this->top();
    }
  }

  template <>
  inline interval< z_number > interval< z_number >::
  URem(interval< z_number > x) const {

    if (this->is_bottom() || x.is_bottom()) {
      return this->bottom();
    } else if (this->singleton() && x.singleton()) {
      z_number dividend = *this->singleton();
      z_number divisor = *x.singleton();

      if (divisor < 0) {
        return this->top();
      } else if (divisor == 0) {
        return this->bottom();
      } else if (dividend < 0) {
        // dividend is treated as an unsigned integer.
        // we would need the size to be more precise
        return interval_t(0, divisor - 1);
      } else {
        return interval_t(dividend % divisor);
      }
    } else if (x.ub().is_finite() && x.lb().is_finite()) {
      z_number max_divisor = *x.ub().number();

      if (x.lb() < 0 || x.ub() < 0) {
        return this->top();
      } else if (max_divisor == 0) {
        return this->bottom();
      }

      return interval_t(0, max_divisor - 1);
    } else {
      return this->top();
    }
  }

  template <>
  inline interval< z_number > interval< z_number >::
  And(interval< z_number > x) const {
    if (this->is_bottom() || x.is_bottom()) {
      return this->bottom();
    } else {
      boost::optional< z_number > left_op = this->singleton();
      boost::optional< z_number > right_op = x.singleton();

      if (left_op && right_op) {
        return interval_t((*left_op) & (*right_op));
      } else if (this->lb() >= 0 && x.lb() >= 0) {
        return interval_t(0, bound_t::min(this->ub(), x.ub()));
      } else {
        return this->top();
      }
    }
  }

  template <>
  inline interval< z_number > interval< z_number >::
  Or(interval< z_number > x) const {
    if (this->is_bottom() || x.is_bottom()) {
      return this->bottom();
    } else {
      boost::optional< z_number > left_op = this->singleton();
      boost::optional< z_number > right_op = x.singleton();

      if (left_op && right_op) {
        return interval_t((*left_op) | (*right_op));
      } else if (this->lb() >= 0 && x.lb() >= 0) {
        boost::optional< z_number > left_ub = this->ub().number();
        boost::optional< z_number > right_ub = x.ub().number();

        if (left_ub && right_ub) {
          z_number m = (*left_ub > *right_ub ? *left_ub : *right_ub);
          return interval_t(0, m.fill_ones());
        } else {
          return interval_t(0, bound_t::plus_infinity());
        }
      } else {
        return this->top();
      }
    }
  }

  template <>
  inline interval< z_number > interval< z_number >::
  Xor(interval< z_number > x)  const {
    if (this->is_bottom() || x.is_bottom()) {
      return this->bottom();
    } else {
      boost::optional< z_number > left_op = this->singleton();
      boost::optional< z_number > right_op = x.singleton();

      if (left_op && right_op) {
        return interval_t((*left_op) ^ (*right_op));
      } else {
        return this->Or(x);
      }
    }
  }

  template <>
  inline interval< z_number > interval< z_number >::
  Shl(interval< z_number > x) const {
    if (this->is_bottom() || x.is_bottom()) {
      return this->bottom();
    } else {
      if (boost::optional<z_number> shift = x.singleton()) {
	z_number k = *shift;
	if (k < 0) {
	  //CRAB_ERROR("lshr shift operand cannot be negative");
	  return this->top();
	}
	// Some crazy linux drivers generate shl instructions with
	// huge shifts.  We limit the number of times the loop is run
	// to avoid wasting too much time on it.
	if (k <= 128) {
	  z_number factor = 1;
	  for (int i = 0; k > i ; i++) {
	    factor *= 2;
	  }
	  return (*this) * factor;
	}
      }
      return this->top();
    }
  }

  template <>
  inline interval< z_number > interval< z_number >::
  AShr(interval< z_number > x) const {
    if (this->is_bottom() || x.is_bottom()) {
      return this->bottom();
    } else {
      if (boost::optional<z_number> shift = x.singleton()) {
	z_number k = *shift;
	if (k < 0) {
	  //CRAB_ERROR("ashr shift operand cannot be negative");
	  return this->top();
	}
	// Some crazy linux drivers generate ashr instructions with
	// huge shifts.  We limit the number of times the loop is run
	// to avoid wasting too much time on it.
	if (k <= 128) {
	  z_number factor = 1;
	  for (int i = 0; k > i; i++) {
	    factor *= 2;
	  }
	  return (*this) / factor;
	}
      }
      return this->top();
    }
  }

  template <>
  inline interval< z_number > interval< z_number >::
  LShr(interval< z_number > x) const {
    if (this->is_bottom() || x.is_bottom()) {
      return this->bottom();
    } else {
      if (boost::optional< z_number > shift = x.singleton()) {
	z_number k = *shift;
	if (k < 0) {
	  //CRAB_ERROR("lshr shift operand cannot be negative");
	  return this->top();
	}
	// Some crazy linux drivers generate lshr instructions with
	// huge shifts.  We limit the number of times the loop is run
	// to avoid wasting too much time on it.
	if (k <= 128) {
	  if (this->lb() >= 0 && this->ub().is_finite() && shift) {
	    z_number lb = *this->lb().number();
	    z_number ub = *this->ub().number();
	    return interval< z_number >(lb >> k, ub >> k);
	  }
	}
      }
      return this->top();
    }
  }

  template< typename Number >
  inline interval< Number > operator+(Number c, interval< Number > x) {
    return interval< Number >(c) + x;
  }

  template< typename Number >
  inline interval< Number > operator+(interval< Number > x, Number c) {
    return x + interval< Number >(c);
  }

  template< typename Number >
  inline interval< Number > operator*(Number c, interval< Number > x) {
    return interval< Number >(c) * x;
  }

  template< typename Number >
  inline interval< Number > operator*(interval< Number > x, Number c) {
    return x * interval< Number >(c);
  }

  template< typename Number >
  inline interval< Number > operator/(Number c, interval< Number > x) {
    return interval< Number >(c) / x;
  }

  template< typename Number >
  inline interval< Number > operator/(interval< Number > x, Number c) {
    return x / interval< Number >(c);
  }

  template< typename Number >
  inline interval< Number > operator-(Number c, interval< Number > x) {
    return interval< Number >(c) - x;
  }

  template< typename Number >
  inline interval< Number > operator-(interval< Number > x, Number c) {
    return x - interval< Number >(c);
  }

  template < typename Number >
  inline crab::crab_os& operator<<(crab::crab_os& o, interval< Number > i) {
    i.write(o);
    return o;
  }

  typedef interval< z_number > z_interval;
  typedef interval< q_number > q_interval;

  namespace linear_interval_solver_impl {

    template<>
    inline z_interval trim_interval(z_interval i, z_interval j) {
      if (boost::optional<z_number> c = j.singleton()) {
	if (i.lb() == *c) {
	  return z_interval(*c + 1, i.ub());
	} else if (i.ub() == *c) {
	  return z_interval(i.lb(), *c - 1);
	} else {
	}
      }
      return i;
    }

    template<>
    inline q_interval trim_interval(q_interval i, q_interval /* j */) {
      // No refinement possible for disequations over rational numbers
      return i;
    }

    template<>
    inline z_interval lower_half_line(z_interval i, bool /*is_signed*/) {
      return i.lower_half_line();
    }

    template<>
    inline q_interval lower_half_line(q_interval i, bool /*is_signed*/) {
      return i.lower_half_line();
    }

    template<>
    inline z_interval upper_half_line(z_interval i, bool /*is_signed*/) {
      return i.upper_half_line();
    }

    template<>
    inline q_interval upper_half_line(q_interval i, bool /*is_signed*/) {
      return i.upper_half_line();
    }
  } // namespace linear_interval_solver_impl

  template<typename Number, typename VariableName, std::size_t max_reduction_cycles = 10>
  class interval_domain:
    public crab::domains::
    abstract_domain<Number, VariableName,
		    interval_domain<Number,VariableName,max_reduction_cycles> >  {
  public:
    typedef interval_domain<Number, VariableName, max_reduction_cycles> interval_domain_t;
    typedef crab::domains::
    abstract_domain<Number,VariableName,interval_domain_t> abstract_domain_t;
    using typename abstract_domain_t::linear_expression_t;
    using typename abstract_domain_t::linear_constraint_t;
    using typename abstract_domain_t::linear_constraint_system_t;
    using typename abstract_domain_t::variable_t;
    using typename abstract_domain_t::number_t;
    using typename abstract_domain_t::varname_t;
    typedef interval<Number> interval_t;

  private:
    typedef separate_domain<variable_t, interval_t> separate_domain_t;
    typedef linear_interval_solver<Number, VariableName, separate_domain_t> solver_t;

  public:
    typedef typename separate_domain_t::iterator iterator;

  private:
    separate_domain_t _env;

    interval_domain(separate_domain_t env): _env(env) { }

  public:
    static interval_domain_t top() {
      return interval_domain(separate_domain_t::top());
    }

    static interval_domain_t bottom() {
      return interval_domain(separate_domain_t::bottom());
    }

  public:
    interval_domain(): _env(separate_domain_t::top()) { }

    interval_domain(const interval_domain_t& e):
      _env(e._env) {
      crab::CrabStats::count (getDomainName() + ".count.copy");
      crab::ScopedCrabStats __st__(getDomainName() + ".copy");
    }

    interval_domain_t& operator=(const interval_domain_t& o) {
      crab::CrabStats::count (getDomainName() + ".count.copy");
      crab::ScopedCrabStats __st__(getDomainName() + ".copy");
      if (this != &o)
        this->_env = o._env;
      return *this;
    }

    iterator begin() {
      return this->_env.begin();
    }

    iterator end() {
      return this->_env.end();
    }

    bool is_bottom() {
      return this->_env.is_bottom();
    }

    bool is_top() {
      return this->_env.is_top();
    }

    bool operator<=(interval_domain_t e) {
      crab::CrabStats::count (getDomainName() + ".count.leq");
      crab::ScopedCrabStats __st__(getDomainName() + ".leq");
      return (this->_env <= e._env);
    }

    void operator|=(interval_domain_t e) {
      crab::CrabStats::count (getDomainName() + ".count.join");
      crab::ScopedCrabStats __st__(getDomainName() + ".join");
      this->_env = this->_env | e._env;
    }

    interval_domain_t operator|(interval_domain_t e) {
      crab::CrabStats::count (getDomainName() + ".count.join");
      crab::ScopedCrabStats __st__(getDomainName() + ".join");
      return (this->_env | e._env);
    }

    interval_domain_t operator&(interval_domain_t e) {
      crab::CrabStats::count (getDomainName() + ".count.meet");
      crab::ScopedCrabStats __st__(getDomainName() + ".meet");
      return (this->_env & e._env);
    }

    interval_domain_t operator||(interval_domain_t e) {
      crab::CrabStats::count (getDomainName() + ".count.widening");
      crab::ScopedCrabStats __st__(getDomainName() + ".widening");
      return (this->_env || e._env);
    }

    template<typename Thresholds>
    interval_domain_t widening_thresholds (interval_domain_t e, const Thresholds &ts) {
      crab::CrabStats::count (getDomainName() + ".count.widening");
      crab::ScopedCrabStats __st__(getDomainName() + ".widening");
      return this->_env.widening_thresholds (e._env, ts);
    }

    interval_domain_t operator&&(interval_domain_t e) {
      crab::CrabStats::count (getDomainName() + ".count.narrowing");
      crab::ScopedCrabStats __st__(getDomainName() + ".narrowing");
      return (this->_env && e._env);
    }

    void set(variable_t v, interval_t i) {
      crab::CrabStats::count (getDomainName() + ".count.assign");
      crab::ScopedCrabStats __st__(getDomainName() + ".assign");
      this->_env.set(v, i);
    }

    void set(variable_t v, Number n) {
      crab::CrabStats::count (getDomainName() + ".count.assign");
      crab::ScopedCrabStats __st__(getDomainName() + ".assign");
      this->_env.set(v, interval_t(n));
    }

    void operator-=(variable_t v) {
      crab::CrabStats::count (getDomainName() + ".count.forget");
      crab::ScopedCrabStats __st__(getDomainName() + ".forget");
      this->_env -= v;
    }

    interval_t operator[](variable_t v) {
      return this->_env[v];
    }

    interval_t operator[](linear_expression_t expr) {
      interval_t r(expr.constant());
      for (typename linear_expression_t::iterator it = expr.begin();
           it != expr.end(); ++it) {
	interval_t c(it->first);
	r += c * this->_env[it->second];
      }
      return r;
    }

    void operator+=(linear_constraint_system_t csts) {
      crab::CrabStats::count (getDomainName() + ".count.add_constraints");
      crab::ScopedCrabStats __st__(getDomainName() + ".add_constraints");
      this->add(csts);
    }

    void add(linear_constraint_system_t csts,
             std::size_t threshold = max_reduction_cycles) {
      if (!this->is_bottom()) {
	// XXX: filter out unsigned linear inequalities
	linear_constraint_system_t signed_csts;
	for (auto const& c: csts) {
	  if (c.is_inequality() && c.is_unsigned()) {
	    CRAB_WARN("unsigned inequality skipped");
	    continue;
	  }
	  signed_csts += c;
	}
	solver_t solver(signed_csts, threshold);
	solver.run(this->_env);
      }
    }

    interval_domain_t operator+(linear_constraint_system_t csts) {
      interval_domain_t e(this->_env);
      e += csts;
      return e;
    }

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

      if (boost::optional<variable_t> v = e.get_variable ()) {
        this->_env.set(x, this->_env [(*v)]);
      }
      else {
        interval_t r = e.constant();
        for (typename linear_expression_t::iterator it = e.begin();
             it != e.end(); ++it) {
	r += it->first * this->_env[it->second];
        }
        this->_env.set(x, r);
      }
    }

    void apply(operation_t op, variable_t x, variable_t y, variable_t z) {
      crab::CrabStats::count (getDomainName() + ".count.apply");
      crab::ScopedCrabStats __st__(getDomainName() + ".apply");

      interval_t yi = this->_env[y];
      interval_t zi = this->_env[z];
      interval_t xi = interval_t::bottom();

      switch (op) {
      case OP_ADDITION: {
	xi = yi + zi;
	break;
      }
      case OP_SUBTRACTION: {
	xi = yi - zi;
	break;
      }
      case OP_MULTIPLICATION: {
	xi = yi * zi;
	break;
      }
      case OP_DIVISION: {
	xi = yi / zi;
	break;
      }
      }
      this->_env.set(x, xi);
    }

    void apply(operation_t op, variable_t x, variable_t y, Number k) {
      crab::CrabStats::count (getDomainName() + ".count.apply");
      crab::ScopedCrabStats __st__(getDomainName() + ".apply");

      interval_t yi = this->_env[y];
      interval_t zi(k);
      interval_t xi = interval_t::bottom();

      switch (op) {
      case OP_ADDITION: {
	xi = yi + zi;
	break;
      }
      case OP_SUBTRACTION: {
	xi = yi - zi;
	break;
      }
      case OP_MULTIPLICATION: {
	xi = yi * zi;
	break;
      }
      case OP_DIVISION: {
	xi = yi / zi;
	break;
      }
      }
      this->_env.set(x, xi);
    }

    void backward_assign (variable_t x, linear_expression_t e,
			  interval_domain_t inv) {
      crab::domains::BackwardAssignOps<interval_domain_t>::
	assign (*this, x, e, inv);
    }

    void backward_apply (operation_t op,
			 variable_t x, variable_t y, Number z,
			 interval_domain_t inv) {
      crab::domains::BackwardAssignOps<interval_domain_t>::
	apply(*this, op, x, y, z, inv);
    }

    void backward_apply(operation_t op,
			variable_t x, variable_t y, variable_t z,
			interval_domain_t inv) {
      crab::domains::BackwardAssignOps<interval_domain_t>::
	apply(*this, op, x, y, z, inv);
    }

    // cast_operators_api

    void apply(crab::domains::int_conv_operation_t /*op*/,
	       variable_t dst, variable_t src){
      // ignore the widths
      assign(dst, src);
    }

    // bitwise_operators_api

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

      interval_t yi = this->_env[y];
      interval_t zi = this->_env[z];
      interval_t xi = interval_t::bottom();

      switch (op) {
        case OP_AND: {
	xi = yi.And(zi);
	break;
        }
        case OP_OR: {
	xi = yi.Or(zi);
	break;
        }
        case OP_XOR: {
	xi = yi.Xor(zi);
	break;
        }
        case OP_SHL: {
	xi = yi.Shl(zi);
	break;
        }
        case OP_LSHR: {
          xi = yi.LShr(zi);
          break;
        }
        case OP_ASHR: {
	xi = yi.AShr(zi);
	break;
        }
        default:
          CRAB_ERROR("unreachable");
      }
      this->_env.set(x, xi);
    }

    void apply(bitwise_operation_t op, variable_t x, variable_t y, Number k){
      crab::CrabStats::count (getDomainName() + ".count.apply");
      crab::ScopedCrabStats __st__(getDomainName() + ".apply");

      interval_t yi = this->_env[y];
      interval_t zi(k);
      interval_t xi = interval_t::bottom();
      switch (op) {
        case OP_AND: {
	xi = yi.And(zi);
	break;
        }
        case OP_OR: {
	xi = yi.Or(zi);
	break;
        }
        case OP_XOR: {
	xi = yi.Xor(zi);
	break;
        }
        case OP_SHL: {
	xi = yi.Shl(zi);
	break;
        }
        case OP_LSHR: {
          xi = yi.LShr(zi);
          break;
        }
        case OP_ASHR: {
	xi = yi.AShr(zi);
	break;
        }
        default:
          CRAB_ERROR("unreachable");
      }
      this->_env.set(x, xi);
    }

    // division_operators_api

    void apply(div_operation_t op, variable_t x, variable_t y, variable_t z){
      crab::CrabStats::count (getDomainName() + ".count.apply");
      crab::ScopedCrabStats __st__(getDomainName() + ".apply");

      interval_t yi = this->_env[y];
      interval_t zi = this->_env[z];
      interval_t xi = interval_t::bottom();

      switch (op) {
        case OP_SDIV: {
	xi = yi / zi;
	break;
        }
        case OP_UDIV: {
	xi = yi.UDiv(zi);
	break;
        }
        case OP_SREM: {
	xi = yi.SRem(zi);
	break;
        }
        case OP_UREM: {
	xi = yi.URem(zi);
	break;
        }
        default:
          CRAB_ERROR("unreachable");
      }
      this->_env.set(x, xi);

    }

    void apply(div_operation_t op, variable_t x, variable_t y, Number k){
      crab::CrabStats::count (getDomainName() + ".count.apply");
      crab::ScopedCrabStats __st__(getDomainName() + ".apply");

      interval_t yi = this->_env[y];
      interval_t zi(k);
      interval_t xi = interval_t::bottom();
      switch (op) {
        case OP_SDIV: {
	xi = yi / zi;
	break;
        }
        case OP_UDIV: {
	xi = yi.UDiv(zi);
	break;
        }
        case OP_SREM: {
	xi = yi.SRem(zi);
	break;
        }
        case OP_UREM: {
	xi = yi.URem(zi);
	break;
        }
        default:
          CRAB_ERROR("unreachable");
      }
      this->_env.set(x, xi);
    }

    void write(crab::crab_os& o) {
      this->_env.write(o);
    }

    linear_constraint_system_t to_linear_constraint_system ()
    {
      linear_constraint_system_t csts;

      if (this->is_bottom()) {
        csts += linear_constraint_t::get_false();
        return csts;
      }

      for (iterator it = this->_env.begin(); it != this->_env.end(); ++it)
      {
        variable_t v = it->first;
        interval_t   i = it->second;
        boost::optional<Number> lb = i.lb().number();
        boost::optional<Number> ub = i.ub().number();
        if (lb) csts += linear_constraint_t(v >= *lb);
        if (ub) csts += linear_constraint_t(v <= *ub);
      }
      return csts;
    }

    static std::string getDomainName () {
      return "Intervals";
    }

  }; // class interval_domain

} // namespace ikos


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2019 Tinko Bartels, Berlin, Germany.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_STRATEGIES_CARTESIAN_ACCELERATED_SHULL_HPP
#define BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_STRATEGIES_CARTESIAN_ACCELERATED_SHULL_HPP

#include <boost/geometry/extensions/triangulation/strategies/delaunay_triangulation.hpp>
#include <boost/geometry/extensions/triangulation/strategies/cartesian/detail/accelerated_shull.hpp>
#include <boost/geometry/extensions/triangulation/strategies/delaunay_triangulation.hpp>
#include <boost/geometry/extensions/triangulation/strategies/cartesian/in_circle_robust.hpp>
#include <boost/geometry/strategies/cartesian/side_by_triangle.hpp>

#include <boost/geometry/core/coordinate_system.hpp>
#include <boost/geometry/core/tag.hpp>
#include <boost/geometry/core/tags.hpp>

namespace boost { namespace geometry { namespace strategy
{

namespace delaunay_triangulation
{

struct accelerated_shull
{
    template
    <
        typename PointContainer,
        typename Triangulation,
        typename SideStrategy = strategy::side::side_by_triangle<>,
        typename InCircleStrategy = strategy::in_circle::in_circle_robust<>
    >
    static inline void apply(PointContainer const & in,
                             Triangulation& out,
                             bool legalize = true)
    {
        detail::accelerated_shull::apply<
            PointContainer,
            Triangulation,
            SideStrategy,
            InCircleStrategy>(in, out, legalize);
    }
};

namespace services
{
template <>
struct default_strategy<point_tag, cartesian_tag, 2>
{
    typedef accelerated_shull type;
};
}

}}}} // namespace boost::geometry::strategy::delaunay:triangulation

#endif // BOOST_GEOMETRY_EXTENSIONS_TRIANGULATION_STRATEGIES_CARTESIAN_ACCELERATED_SHULL_HPP


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
// Copyright (c) 2008-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_ARITHMETIC_ARITHMETIC_HPP
#define BOOST_GEOMETRY_ARITHMETIC_ARITHMETIC_HPP

#include <functional>

#include <boost/call_traits.hpp>
#include <boost/concept/requires.hpp>

#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/geometries/concepts/point_concept.hpp>
#include <boost/geometry/util/for_each_coordinate.hpp>
#include <boost/geometry/util/select_most_precise.hpp>

namespace geofeatures_boost {} namespace boost = geofeatures_boost; namespace geofeatures_boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail
{

template <typename Point>
struct param
{
    typedef typename geofeatures_boost::call_traits
        <
            typename coordinate_type<Point>::type
        >::param_type type;
};

template <typename Value, template <typename> class Function>
struct value_operation
{
    Value m_value;

    inline value_operation(Value const &value)
        : m_value(value)
    {}

    template <typename PointDst, std::size_t Index>
    inline void apply(PointDst& point_dst) const
    {
        set<Index>(point_dst,
               Function
                <
                    typename geometry::select_most_precise
                        <
                            Value,
                            typename geometry::coordinate_type<PointDst>::type
                        >::type
                >()(get<Index>(point_dst), m_value));
    }
};

template <typename PointSrc, template <typename> class Function>
struct point_operation
{
    PointSrc const& m_point_src;

    inline point_operation(PointSrc const& point)
        : m_point_src(point)
    {}

    template <typename PointDst, std::size_t Index>
    inline void apply(PointDst& point_dst) const
    {
        set<Index>(point_dst,
               Function
                <
                    typename geometry::select_most_precise
                        <
                            typename geometry::coordinate_type<PointSrc>::type,
                            typename geometry::coordinate_type<PointDst>::type
                        >::type
                >()(get<Index>(point_dst), get<Index>(m_point_src)));
    }
};

template <typename Value>
struct value_assignment
{
    Value m_value;

    inline value_assignment(Value const &value)
        : m_value(value)
    {}

    template <typename PointDst, std::size_t Index>
    inline void apply(PointDst& point_dst) const
    {
        set<Index>(point_dst, m_value);
    }
};

template <typename PointSrc>
struct point_assignment
{
    PointSrc const& m_point_src;

    inline point_assignment(PointSrc const& point)
        : m_point_src(point)
    {}

    template <typename PointDst, std::size_t Index>
    inline void apply(PointDst& point_dst) const
    {
        set<Index>(point_dst, get<Index>(m_point_src));
    }
};

} // namespace detail
#endif // DOXYGEN_NO_DETAIL

/*!
    \brief Adds the same value to each coordinate of a point
    \ingroup arithmetic
    \details
    \tparam Point \tparam_point
    \param p point
    \param value value to add
 */
template <typename Point>
inline void add_value(Point& p, typename detail::param<Point>::type value)
{
    BOOST_CONCEPT_ASSERT( (concept::Point<Point>) );

    for_each_coordinate(p,
                        detail::value_operation
                            <
                                typename coordinate_type<Point>::type,
                                std::plus
                            >(value));
}

/*!
    \brief Adds a point to another
    \ingroup arithmetic
    \details The coordinates of the second point will be added to those of the first point.
             The second point is not modified.
    \tparam Point1 \tparam_point
    \tparam Point2 \tparam_point
    \param p1 first point
    \param p2 second point
 */
template <typename Point1, typename Point2>
inline void add_point(Point1& p1, Point2 const& p2)
{
    BOOST_CONCEPT_ASSERT( (concept::Point<Point1>) );
    BOOST_CONCEPT_ASSERT( (concept::ConstPoint<Point2>) );

    for_each_coordinate(p1, detail::point_operation<Point2, std::plus>(p2));
}

/*!
    \brief Subtracts the same value to each coordinate of a point
    \ingroup arithmetic
    \details
    \tparam Point \tparam_point
    \param p point
    \param value value to subtract
 */
template <typename Point>
inline void subtract_value(Point& p, typename detail::param<Point>::type value)
{
    BOOST_CONCEPT_ASSERT( (concept::Point<Point>) );

    for_each_coordinate(p,
                        detail::value_operation
                            <
                                typename coordinate_type<Point>::type,
                                std::minus
                            >(value));
}

/*!
    \brief Subtracts a point to another
    \ingroup arithmetic
    \details The coordinates of the second point will be subtracted to those of the first point.
             The second point is not modified.
    \tparam Point1 \tparam_point
    \tparam Point2 \tparam_point
    \param p1 first point
    \param p2 second point
 */
template <typename Point1, typename Point2>
inline void subtract_point(Point1& p1, Point2 const& p2)
{
    BOOST_CONCEPT_ASSERT( (concept::Point<Point1>) );
    BOOST_CONCEPT_ASSERT( (concept::ConstPoint<Point2>) );

    for_each_coordinate(p1, detail::point_operation<Point2, std::minus>(p2));
}

/*!
    \brief Multiplies each coordinate of a point by the same value
    \ingroup arithmetic
    \details
    \tparam Point \tparam_point
    \param p point
    \param value value to multiply by
 */
template <typename Point>
inline void multiply_value(Point& p, typename detail::param<Point>::type value)
{
    BOOST_CONCEPT_ASSERT( (concept::Point<Point>) );

    for_each_coordinate(p,
                        detail::value_operation
                            <
                                typename coordinate_type<Point>::type,
                                std::multiplies
                            >(value));
}

/*!
    \brief Multiplies a point by another
    \ingroup arithmetic
    \details The coordinates of the first point will be multiplied by those of the second point.
             The second point is not modified.
    \tparam Point1 \tparam_point
    \tparam Point2 \tparam_point
    \param p1 first point
    \param p2 second point
    \note This is *not* a dot, cross or wedge product. It is a mere field-by-field multiplication.
 */
template <typename Point1, typename Point2>
inline void multiply_point(Point1& p1, Point2 const& p2)
{
    BOOST_CONCEPT_ASSERT( (concept::Point<Point1>) );
    BOOST_CONCEPT_ASSERT( (concept::ConstPoint<Point2>) );

    for_each_coordinate(p1, detail::point_operation<Point2, std::multiplies>(p2));
}

/*!
    \brief Divides each coordinate of the same point by a value
    \ingroup arithmetic
    \details
    \tparam Point \tparam_point
    \param p point
    \param value value to divide by
 */
template <typename Point>
inline void divide_value(Point& p, typename detail::param<Point>::type value)
{
    BOOST_CONCEPT_ASSERT( (concept::Point<Point>) );

    for_each_coordinate(p,
                        detail::value_operation
                            <
                                typename coordinate_type<Point>::type,
                                std::divides
                            >(value));
}

/*!
    \brief Divides a point by another
    \ingroup arithmetic
    \details The coordinates of the first point will be divided by those of the second point.
             The second point is not modified.
    \tparam Point1 \tparam_point
    \tparam Point2 \tparam_point
    \param p1 first point
    \param p2 second point
 */
template <typename Point1, typename Point2>
inline void divide_point(Point1& p1, Point2 const& p2)
{
    BOOST_CONCEPT_ASSERT( (concept::Point<Point1>) );
    BOOST_CONCEPT_ASSERT( (concept::ConstPoint<Point2>) );

    for_each_coordinate(p1, detail::point_operation<Point2, std::divides>(p2));
}

/*!
    \brief Assign each coordinate of a point the same value
    \ingroup arithmetic
    \details
    \tparam Point \tparam_point
    \param p point
    \param value value to assign
 */
template <typename Point>
inline void assign_value(Point& p, typename detail::param<Point>::type value)
{
    BOOST_CONCEPT_ASSERT( (concept::Point<Point>) );

    for_each_coordinate(p,
                        detail::value_assignment
                            <
                                typename coordinate_type<Point>::type
                            >(value));
}

/*!
    \brief Assign a point with another
    \ingroup arithmetic
    \details The coordinates of the first point will be assigned those of the second point.
             The second point is not modified.
    \tparam Point1 \tparam_point
    \tparam Point2 \tparam_point
    \param p1 first point
    \param p2 second point
 */
template <typename Point1, typename Point2>
inline void assign_point(Point1& p1, Point2 const& p2)
{
    BOOST_CONCEPT_ASSERT( (concept::Point<Point1>) );
    BOOST_CONCEPT_ASSERT( (concept::ConstPoint<Point2>) );

    for_each_coordinate(p1, detail::point_assignment<Point2>(p2));
}

}} // namespace geofeatures_boost::geometry

#endif // BOOST_GEOMETRY_ARITHMETIC_ARITHMETIC_HPP


/*
 * multi_expo.cpp
 *
 *  Created on: 02.07.2012
 *      Author: stephaniebayer
 */

#include "multi_expo.h"
#include "G_q.h"
#include "ElGammal.h";
#include "Pedersen.h"
#include "Cipher_elg.h"
#include <NTL/ZZ.h>
NTL_CLIENT
#include<vector>
using namespace std;
#include <iostream>
#include <time.h>
#include <fstream>

extern G_q G;
extern G_q H;
extern ElGammal El;
extern Pedersen Ped;

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

}

multi_expo::~multi_expo() {
	// TODO Auto-generated destructor stub
}

vector<vector<int>*>* multi_expo::to_binary(int win){

	vector<vector<int>* >* ret;
	vector<int>* temp;
	long e,i,j;
	double two= 2;

	e = pow(two, win);
	ret = new vector<vector<int>* >(e);
	for (i = 0; i<e; i++){
		temp = new vector<int>(win);
		for (j=0; j<win; j++){
			temp->at(j) =bit(i,j);
		}
		ret->at(i)=temp;
	}
	return ret;

}

long multi_expo::to_long(vector<int>* bit_r){

	long  t, length;
	double two,i;
	two = 2;

	length =bit_r->size();
	t=0;
	for(i = 0; i<length; i++ ){
		t = t+bit_r->at(i)*pow(two,i);
	}
	return t;
}

void multi_expo::to_long(long& t,vector<int>* bit_r){

	long  length;
	double two,i;
	two = 2;

	length =bit_r->size();
	t=0;
	for(i = 0; i<length; i++ ){
		t = t+bit_r->at(i)*pow(two,i);
	}
}

vector<long>* multi_expo::to_basis(ZZ e, long num_b, int omega){
	long i, j, l, t;
	vector<int>* bit_r;
	vector<long>* basis;
	bit_r = new vector<int>(omega);
	t= num_b/omega +1;
	basis = new vector<long>(t);

	j=0;
	l=0;

	for(i=0; i<num_b; i++){
		bit_r->at(j)=bit(e,i);
		j++;
		if(j==omega){
			to_long(basis->at(l),bit_r);
			j=0;
			l++;
		}
		else if(i == num_b-1){
			for(j = j; j<omega; j++){
				bit_r->at(j)= 0;
				to_long(basis->at(l),bit_r);
			}
		}
	}
	delete bit_r;
	return basis;

}

vector<vector<vector<long>* >* >* multi_expo::to_basis_vec(vector<vector<ZZ>* >* a, long num_b, int omega){
	vector<vector<vector<long>* >* >* basis_vec=0;
	vector<vector<long>* >* basis_row=0;
	vector<long>* basis = 0;
	long i, j, m,n;
	m=a->size();
	n=a->at(0)->size();
	basis_vec = new vector<vector<vector<long>* >* >(m+1);

	for (i = 0; i<m; i++){

		basis_row = new vector<vector<long>* >(n);
		for (j = 0; j<n; j++){
			basis = to_basis(a->at(i)->at(j), num_b, omega);
			basis_row->at(j)= basis;
		}
		basis_vec->at(i+1) = basis_row;
	}
	delete basis;
	return basis_vec;
}

ZZ multi_expo::expo_mult(const vector<ZZ>* e, ZZ ran, int omega_expo, vector<Mod_p>* gen){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;
	long length;// num_b;
	ZZ prod, p, temp_1, temp_2, mod;
	double two;
	long num_b;
	length = e->size();
	mod = G.get_mod();
	num_b = NumBits(G.get_ord());
	l = num_b/omega_expo +1;
	basis_vec = new vector<vector<long>* >(length+1);
	basis_vec->at(0) = to_basis(ran, num_b, omega_expo);
	for(i = 0; i<length; i++){
		basis_vec->at(i+1) = to_basis(e->at(i), num_b, omega_expo);
	}
	prod = 1;
	two = 2;
	p=1;
	t = pow(two, omega_expo)-1;
	for(i=l-1; i>0; i--){

		p=1;
		for(j = 0; j<length+1; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,gen->at(j).get_val(),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p=1;
			for(j = 0; j<length+1; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,gen->at(j).get_val(),mod);
				}
			}
			MulMod(temp_1,temp_1,p,mod);
			MulMod(temp_2,temp_1,temp_2,mod);

		}
		MulMod(prod , prod,temp_2,mod);
		for(k =0; k<omega_expo; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p=1;
	for(j = 0; j<length+1; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,gen->at(j).get_val(),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p=1;
		for(j = 0; j<length+1; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,gen->at(j).get_val(),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod,prod,temp_2,mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
	return prod;
}

void multi_expo::expo_mult(ZZ& prod, const vector<ZZ>* e, ZZ ran, int omega_expo, vector<Mod_p>* gen){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;
	long length;// num_b;
	ZZ p, temp_1, temp_2, mod;
	double two;
	long num_b;
	length = e->size();
	mod = G.get_mod();
	num_b = NumBits(G.get_ord());
	l = num_b/omega_expo +1;
	basis_vec = new vector<vector<long>* >(length+1);
	basis_vec->at(0) = to_basis(ran, num_b, omega_expo);
	for(i = 0; i<length; i++){
		basis_vec->at(i+1) = to_basis(e->at(i), num_b, omega_expo);
	}
	prod = 1;
	two = 2;
	p=1;
	t = pow(two, omega_expo)-1;
	length = length +1;
	for(i=l-1; i>0; i--){

		p=1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,gen->at(j).get_val(),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p=1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,gen->at(j).get_val(),mod);
				}
			}
			MulMod(temp_1,temp_1,p,mod);
			MulMod(temp_2,temp_1,temp_2,mod);

		}
		MulMod(prod , prod,temp_2,mod);
		for(k =0; k<omega_expo; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p=1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,gen->at(j).get_val(),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p=1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,gen->at(j).get_val(),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod,prod,temp_2,mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
}

Cipher_elg multi_expo::expo_mult(const vector<Cipher_elg>* a, vector<ZZ>* e, int omega ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;
	long length;
	ZZ prod_u, p_u, temp_1_u, temp_2_u,prod_v, p_v, temp_1_v, temp_2_v, mod;
	Cipher_elg prod;
	double two;
	long num_b;

	length = e->size();
	mod = H.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;

	vector<ZZ> a_u(length);
	vector<ZZ> a_v(length);
	basis_vec = new vector<vector<long>* >(length);
	for(i = 0; i<length; i++){
		basis_vec->at(i) = to_basis(e->at(i), num_b,omega);
		a_u.at(i)=a->at(i).get_u();
		a_v.at(i)=a->at(i).get_v();
	}
	prod_u = 1;
	prod_v= 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p_u= 1;
		p_v= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j), mod);
			}
		}
		temp_1_u = p_u;
		temp_2_u = p_u;
		temp_1_v = p_v;
		temp_2_v = p_v;
		for(k = t-1; k>0; k--){
			p_u= 1;
			p_v = 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p_u,p_u,a_u.at(j),mod);
					MulMod(p_v,p_v,a_v.at(j),mod);
				}
			}
			MulMod(temp_1_u,temp_1_u,p_u,mod);
			MulMod(temp_2_u,temp_1_u,temp_2_u,mod);
			MulMod(temp_1_v, temp_1_v,p_v,mod);
			MulMod(temp_2_v,temp_1_v,temp_2_v,mod);

		}
		MulMod(prod_u,prod_u,temp_2_u,mod);
		MulMod(prod_v,prod_v,temp_2_v,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod_u,prod_u,mod);
			SqrMod(prod_v,prod_v,mod);
		}
	}
	p_u= 1;
	p_v= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p_u,p_u,a_u.at(j),mod);
			MulMod(p_v,p_v,a_v.at(j),mod);
		}
	}
	temp_1_u = p_u;
	temp_2_u = p_u;
	temp_1_v = p_v;
	temp_2_v = p_v;
	for(k = t-1; k>0; k--){
		p_u= 1;
		p_v = 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j),mod);
			}
		}
		MulMod(temp_1_u,temp_1_u,p_u,mod);
		MulMod(temp_2_u,temp_1_u,temp_2_u,mod);
		MulMod(temp_1_v,temp_1_v,p_v,mod);
		MulMod(temp_2_v,temp_1_v,temp_2_v,mod);
	}
	MulMod(prod_u,prod_u,temp_2_u,mod);
	MulMod(prod_v,prod_v,temp_2_v,mod);
	prod = Cipher_elg(prod_u, prod_v, mod);

	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
	return prod;
}

void multi_expo::expo_mult(Cipher_elg& prod, const vector<Cipher_elg>* a, vector<ZZ>* e, int omega ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;
	ZZ ord = H.get_ord();
	long length;
	ZZ prod_u, p_u, temp_1_u, temp_2_u,prod_v, p_v, temp_1_v, temp_2_v, mod,temp;
	double two;
	long num_b;

	length = a->size();
	mod = H.get_mod();
	num_b = NumBits(ord);
	l = num_b/omega +1;

	vector<ZZ> a_u(length);
	vector<ZZ> a_v(length);
	basis_vec = new vector<vector<long>* >(length);
	for(i = 0; i<length; i++){
		basis_vec->at(i) = to_basis(e->at(i), num_b,omega);
		a_u.at(i)=a->at(i).get_u();
		a_v.at(i)=a->at(i).get_v();
	}
	prod_u = 1;
	prod_v= 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p_u= 1;
		p_v= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j), mod);
			}
		}
		temp_1_u = p_u;
		temp_2_u = p_u;
		temp_1_v = p_v;
		temp_2_v = p_v;
		for(k = t-1; k>0; k--){
			p_u= 1;
			p_v = 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p_u,p_u,a_u.at(j),mod);
					MulMod(p_v,p_v,a_v.at(j),mod);
				}
			}
			MulMod(temp_1_u ,temp_1_u,p_u,mod);
			MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
			MulMod(temp_1_v ,temp_1_v,p_v,mod);
			MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);

		}
		prod_u = MulMod(prod_u,temp_2_u,mod);
		prod_v = MulMod(prod_v,temp_2_v,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod_u,prod_u,mod);
			SqrMod(prod_v,prod_v,mod);
		}
	}
	p_u= 1;
	p_v= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p_u,p_u,a_u.at(j),mod);
			MulMod(p_v,p_v,a_v.at(j),mod);
		}
	}
	temp_1_u = p_u;
	temp_2_u = p_u;
	temp_1_v = p_v;
	temp_2_v = p_v;
	for(k = t-1; k>0; k--){
		p_u= 1;
		p_v = 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j),mod);
			}
		}
		MulMod(temp_1_u ,temp_1_u,p_u,mod);
		MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
		MulMod(temp_1_v, temp_1_v,p_v,mod);
		MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);
	}
	MulMod(prod_u,prod_u,temp_2_u,mod);
	MulMod(prod_v,prod_v,temp_2_v,mod);
	prod = Cipher_elg(prod_u, prod_v, mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
}

ZZ multi_expo::expo_mult(const vector<ZZ>* a, vector<vector<ZZ>*>* e, int omega, long pos ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;

	long length;
	ZZ prod, p, temp_1, temp_2, mod;

	double two;
	long num_b;

	length = e->size();
	mod = H.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;

	basis_vec = new vector<vector<long>* >(length);

	for(i = 0; i<length; i++){
		basis_vec->at(i) = to_basis(e->at(i)->at(pos), num_b,omega);
	}
	prod = 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,a->at(j),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p= 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,a->at(j),mod);
				}
			}
			temp_1 = MulMod(temp_1,p,mod);
			temp_2 = MulMod(temp_1,temp_2,mod);

		}
		prod = MulMod(prod,temp_2,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,a->at(j),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,a->at(j),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod,prod,temp_2,mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
	return prod;
}

void multi_expo::expo_mult(ZZ& prod, const vector<ZZ>* a, vector<vector<ZZ>*>* e, int omega, long pos ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;

	long length;
	ZZ  p, temp_1, temp_2, mod;

	double two;
	long num_b;

	length = e->size();
	mod = H.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;

	basis_vec = new vector<vector<long>* >(length);

	for(i = 0; i<length; i++){
		basis_vec->at(i) = to_basis(e->at(i)->at(pos), num_b,omega);
	}
	prod = 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,a->at(j),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p= 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,a->at(j),mod);
				}
			}
			temp_1 = MulMod(temp_1,p,mod);
			temp_2 = MulMod(temp_1,temp_2,mod);

		}
		prod = MulMod(prod,temp_2,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,a->at(j),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,a->at(j),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod,prod,temp_2,mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
}

ZZ multi_expo::expo_mult(const vector<vector<vector<ZZ>* >*>* a, vector<vector<ZZ>*>* e, int omega, long pos, long pos_2 ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;

	long length;
	ZZ prod, p, temp_1, temp_2, mod;

	double two;
	long num_b;

	length = e->size();
	mod = H.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;
	basis_vec = new vector<vector<long>* >(length);

	for(i = 0; i<length; i++){
		basis_vec->at(i) = to_basis(e->at(i)->at(pos), num_b,omega);
	}

	prod = 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p= 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
				}
			}
			MulMod(temp_1,temp_1,p,mod);
			MulMod(temp_2,temp_1,temp_2,mod);

		}
		MulMod(prod,prod,temp_2,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod,prod,temp_2,mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
	return prod;
}

void multi_expo::expo_mult(ZZ& prod, const vector<vector<vector<ZZ>* >*>* a, vector<vector<ZZ>*>* e, int omega, long pos, long pos_2 ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;

	long length;
	ZZ  p, temp_1, temp_2, mod;

	double two;
	long num_b;

	length = e->size();
	mod = H.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;
	basis_vec = new vector<vector<long>* >(length);

	for(i = 0; i<length; i++){
		basis_vec->at(i) = to_basis(e->at(i)->at(pos), num_b,omega);
	}

	prod = 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p= 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
				}
			}
			MulMod(temp_1,temp_1,p,mod);
			MulMod(temp_2,temp_1,temp_2,mod);

		}
		MulMod(prod,prod,temp_2,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,a->at(j)->at(pos_2)->at(pos),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod,prod,temp_2,mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
}

Cipher_elg multi_expo::expo_mult(const vector<Cipher_elg>* a, vector<vector<long>*>* basis_vec, int omega ){
	long i, j, k, l,t;
	long length;
	ZZ prod_u, p_u, temp_1_u, temp_2_u,prod_v, p_v, temp_1_v, temp_2_v, mod;
	Cipher_elg prod;
	double two;
	long num_b;

	length = a->size();
	mod = H.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;

	vector<ZZ> a_u(length);
	vector<ZZ> a_v(length);

	for(i = 0; i<length; i++){
		a_u.at(i)=a->at(i).get_u();
		a_v.at(i)=a->at(i).get_v();
	}
	prod_u = 1;
	prod_v= 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p_u= 1;
		p_v= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j), mod);
			}
		}
		temp_1_u = p_u;
		temp_2_u = p_u;
		temp_1_v = p_v;
		temp_2_v = p_v;
		for(k = t-1; k>0; k--){
			p_u= 1;
			p_v = 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p_u,p_u,a_u.at(j),mod);
					MulMod(p_v,p_v,a_v.at(j),mod);
				}
			}
			MulMod(temp_1_u ,temp_1_u,p_u,mod);
			MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
			MulMod(temp_1_v,temp_1_v,p_v,mod);
			MulMod(temp_2_v, temp_1_v,temp_2_v,mod);

		}
		MulMod(prod_u,prod_u,temp_2_u,mod);
		MulMod(prod_v,prod_v,temp_2_v,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod_u,prod_u,mod);
			SqrMod(prod_v,prod_v,mod);
		}
	}
	p_u= 1;
	p_v= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p_u,p_u,a_u.at(j),mod);
			MulMod(p_v,p_v,a_v.at(j),mod);
		}
	}
	temp_1_u = p_u;
	temp_2_u = p_u;
	temp_1_v = p_v;
	temp_2_v = p_v;
	for(k = t-1; k>0; k--){
		p_u= 1;
		p_v = 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j),mod);
			}
		}
		MulMod(temp_1_u ,temp_1_u,p_u,mod);
		MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
		MulMod(temp_1_v, temp_1_v,p_v,mod);
		MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);
	}
	MulMod(prod_u, prod_u,temp_2_u,mod);
	MulMod(prod_v,prod_v,temp_2_v,mod);
	prod = Cipher_elg(prod_u, prod_v, mod);

	return prod;
}

void multi_expo::expo_mult(Cipher_elg& prod, const vector<Cipher_elg>* a, vector<vector<long>*>* basis_vec, int omega ){
	long i, j, k, l,t;
	long length;
	ZZ prod_u, p_u, temp_1_u, temp_2_u,prod_v, p_v, temp_1_v, temp_2_v, mod;
	double two;
	long num_b;

	length = a->size();
	mod = H.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;

	vector<ZZ> a_u(length);
	vector<ZZ> a_v(length);

	for(i = 0; i<length; i++){
		a_u.at(i)=a->at(i).get_u();
		a_v.at(i)=a->at(i).get_v();
	}
	prod_u = 1;
	prod_v= 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p_u= 1;
		p_v= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j), mod);
			}
		}
		temp_1_u = p_u;
		temp_2_u = p_u;
		temp_1_v = p_v;
		temp_2_v = p_v;
		for(k = t-1; k>0; k--){
			p_u= 1;
			p_v = 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p_u,p_u,a_u.at(j),mod);
					MulMod(p_v,p_v,a_v.at(j),mod);
				}
			}
			MulMod(temp_1_u ,temp_1_u,p_u,mod);
			MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
			MulMod(temp_1_v,temp_1_v,p_v,mod);
			MulMod(temp_2_v, temp_1_v,temp_2_v,mod);

		}
		MulMod(prod_u,prod_u,temp_2_u,mod);
		MulMod(prod_v,prod_v,temp_2_v,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod_u,prod_u,mod);
			SqrMod(prod_v,prod_v,mod);
		}
	}
	p_u= 1;
	p_v= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p_u,p_u,a_u.at(j),mod);
			MulMod(p_v,p_v,a_v.at(j),mod);
		}
	}
	temp_1_u = p_u;
	temp_2_u = p_u;
	temp_1_v = p_v;
	temp_2_v = p_v;
	for(k = t-1; k>0; k--){
		p_u= 1;
		p_v = 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j),mod);
			}
		}
		MulMod(temp_1_u ,temp_1_u,p_u,mod);
		MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
		MulMod(temp_1_v, temp_1_v,p_v,mod);
		MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);
	}
	MulMod(prod_u, prod_u,temp_2_u,mod);
	MulMod(prod_v,prod_v,temp_2_v,mod);
	prod = Cipher_elg(prod_u, prod_v, mod);

}

Cipher_elg multi_expo::expo_mult(const vector<Cipher_elg>* a, ZZ f, vector<ZZ>* e, int omega ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;
	ZZ ord = H.get_ord();
	long length;
	ZZ prod_u, p_u, temp_1_u, temp_2_u,prod_v, p_v, temp_1_v, temp_2_v, mod,temp;
	Cipher_elg prod;
	double two;
	long num_b;

	length = e->size();
	mod = H.get_mod();
	num_b = NumBits(ord);
	l = num_b/omega +1;

	vector<ZZ> a_u(length);
	vector<ZZ> a_v(length);
	basis_vec = new vector<vector<long>* >(length);
	for(i = 0; i<length; i++){
		temp = MulMod(f,e->at(i),ord);
		basis_vec->at(i) = to_basis(temp, num_b,omega);
		a_u.at(i)=a->at(i).get_u();
		a_v.at(i)=a->at(i).get_v();
	}
	prod_u = 1;
	prod_v= 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p_u= 1;
		p_v= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j), mod);
			}
		}
		temp_1_u = p_u;
		temp_2_u = p_u;
		temp_1_v = p_v;
		temp_2_v = p_v;
		for(k = t-1; k>0; k--){
			p_u= 1;
			p_v = 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p_u,p_u,a_u.at(j),mod);
					MulMod(p_v,p_v,a_v.at(j),mod);
				}
			}
			MulMod(temp_1_u ,temp_1_u,p_u,mod);
			MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
			MulMod(temp_1_v ,temp_1_v,p_v,mod);
			MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);

		}
		prod_u = MulMod(prod_u,temp_2_u,mod);
		prod_v = MulMod(prod_v,temp_2_v,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod_u,prod_u,mod);
			SqrMod(prod_v,prod_v,mod);
		}
	}
	p_u= 1;
	p_v= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p_u,p_u,a_u.at(j),mod);
			MulMod(p_v,p_v,a_v.at(j),mod);
		}
	}
	temp_1_u = p_u;
	temp_2_u = p_u;
	temp_1_v = p_v;
	temp_2_v = p_v;
	for(k = t-1; k>0; k--){
		p_u= 1;
		p_v = 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j),mod);
			}
		}
		MulMod(temp_1_u ,temp_1_u,p_u,mod);
		MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
		MulMod(temp_1_v, temp_1_v,p_v,mod);
		MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);
	}
	MulMod(prod_u,prod_u,temp_2_u,mod);
	MulMod(prod_v,prod_v,temp_2_v,mod);
	prod = Cipher_elg(prod_u, prod_v, mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	}
	delete basis_vec;
	return prod;
}

void multi_expo::expo_mult(Cipher_elg& prod, const vector<Cipher_elg>* a, ZZ f, vector<ZZ>* e, int omega ){
	long i, j, k, l,t;
	vector<vector<long>* >* basis_vec;
	ZZ ord = H.get_ord();
	long length;
	ZZ prod_u, p_u, temp_1_u, temp_2_u,prod_v, p_v, temp_1_v, temp_2_v, mod,temp;
	double two;
	long num_b;

	length = e->size();
	mod = H.get_mod();
	num_b = NumBits(ord);
	l = num_b/omega +1;
	vector<ZZ> a_u(length);
	vector<ZZ> a_v(length);
	basis_vec = new vector<vector<long>* >(length);
	for(i = 0; i<length; i++){
		temp = MulMod(f,e->at(i),ord);
		basis_vec->at(i) = to_basis(temp, num_b,omega);
		a_u.at(i)=a->at(i).get_u();
		a_v.at(i)=a->at(i).get_v();
	}
	prod_u = 1;
	prod_v= 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p_u= 1;
		p_v= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j), mod);
			}
		}
		temp_1_u = p_u;
		temp_2_u = p_u;
		temp_1_v = p_v;
		temp_2_v = p_v;
		for(k = t-1; k>0; k--){
			p_u= 1;
			p_v = 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p_u,p_u,a_u.at(j),mod);
					MulMod(p_v,p_v,a_v.at(j),mod);
				}
			}
			MulMod(temp_1_u ,temp_1_u,p_u,mod);
			MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
			MulMod(temp_1_v ,temp_1_v,p_v,mod);
			MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);

		}
		prod_u = MulMod(prod_u,temp_2_u,mod);
		prod_v = MulMod(prod_v,temp_2_v,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod_u,prod_u,mod);
			SqrMod(prod_v,prod_v,mod);
		}
	}
	p_u= 1;
	p_v= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p_u,p_u,a_u.at(j),mod);
			MulMod(p_v,p_v,a_v.at(j),mod);
		}
	}
	temp_1_u = p_u;
	temp_2_u = p_u;
	temp_1_v = p_v;
	temp_2_v = p_v;
	for(k = t-1; k>0; k--){
		p_u= 1;
		p_v = 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p_u,p_u,a_u.at(j),mod);
				MulMod(p_v,p_v,a_v.at(j),mod);
			}
		}
		MulMod(temp_1_u ,temp_1_u,p_u,mod);
		MulMod(temp_2_u ,temp_1_u,temp_2_u,mod);
		MulMod(temp_1_v, temp_1_v,p_v,mod);
		MulMod(temp_2_v ,temp_1_v,temp_2_v,mod);
	}
	MulMod(prod_u,prod_u,temp_2_u,mod);
	MulMod(prod_v,prod_v,temp_2_v,mod);
	prod = Cipher_elg(prod_u, prod_v, mod);
	j = basis_vec->size();
	for(i=0; i<j; i++){
		delete basis_vec->at(i);
		basis_vec->at(i)=0;
	};
	delete basis_vec;
}

Cipher_elg multi_expo::expo_mult(const vector<vector<Cipher_elg>*>* a, vector<ZZ>* s1, vector<ZZ>* s2, int omega ){
	Cipher_elg prod;
	long i,l;
	l = a->size();
	prod = expo_mult(a->at(0), s1->at(0), s2, omega);
	for(i = 1; i<l; i++){
		prod = prod*expo_mult(a->at(i), s1->at(i), s2, omega);
	}
	return prod;

}

void  multi_expo::expo_mult(Cipher_elg& prod, const vector<vector<Cipher_elg>*>* a, vector<ZZ>* s1, vector<ZZ>* s2, int omega ){
	Cipher_elg temp;
	long i,l;
	l = a->size();
	expo_mult(prod,a->at(0), s1->at(0), s2, omega);
	for(i = 1; i<l; i++){
		expo_mult(temp,a->at(i), s1->at(i), s2, omega);
		Cipher_elg::mult(prod,prod,temp);
	}
}

Mod_p multi_expo::expo_mult(const vector<Mod_p>* a, vector<vector<long>*>* basis_vec, int omega ){
	long i, j, k, l,t;

	long length;
	ZZ prod, p, temp_1, temp_2, mod;
	Mod_p pro;
	double two;
	long num_b;

	length = a->size();
	mod = G.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;

	vector<ZZ> a_temp(length);

	for(i = 0; i<length; i++){
		a_temp.at(i)=a->at(i).get_val();
	}
	prod = 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,a_temp.at(j),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p= 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,a_temp.at(j),mod);
				}
			}
			MulMod(temp_1,temp_1,p,mod);
			MulMod(temp_2,temp_1,temp_2,mod);

		}
		prod = MulMod(prod,temp_2,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,a_temp.at(j),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,a_temp.at(j),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod, prod,temp_2,mod);
	pro = Mod_p(prod,  mod);
	return pro;
}

void multi_expo::expo_mult(Mod_p& pro, const vector<Mod_p>* a, vector<vector<long>*>* basis_vec, int omega ){
	long i, j, k, l,t;
	long length;
	ZZ prod, p, temp_1, temp_2, mod;
	//Mod_p pro;
	double two;
	long num_b;

	length = a->size();
	mod = G.get_mod();
	num_b = NumBits(H.get_ord());
	l = num_b/omega +1;

	vector<ZZ> a_temp(length);

	for(i = 0; i<length; i++){
		a_temp.at(i)=a->at(i).get_val();
	}
	prod = 1;
	two = 2;
	t = pow(two, omega)-1;
	for(i=l-1; i>0; i--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(i)==t){
				MulMod(p,p,a_temp.at(j),mod);
			}
		}
		temp_1 = p;
		temp_2 = p;
		for(k = t-1; k>0; k--){
			p= 1;
			for(j = 0; j<length; j++){
				if(basis_vec->at(j)->at(i)==k){
					MulMod(p,p,a_temp.at(j),mod);
				}
			}
			MulMod(temp_1,temp_1,p,mod);
			MulMod(temp_2,temp_1,temp_2,mod);

		}
		prod = MulMod(prod,temp_2,mod);
		for(k =0; k<omega; k++){
			SqrMod(prod,prod,mod);
		}
	}
	p= 1;
	for(j = 0; j<length; j++){
		if(basis_vec->at(j)->at(0)==t){
			MulMod(p,p,a_temp.at(j),mod);
		}
	}
	temp_1 = p;
	temp_2 = p;
	for(k = t-1; k>0; k--){
		p= 1;
		for(j = 0; j<length; j++){
			if(basis_vec->at(j)->at(0)==k){
				MulMod(p,p,a_temp.at(j),mod);
			}
		}
		MulMod(temp_1,temp_1,p,mod);
		MulMod(temp_2,temp_1,temp_2,mod);
	}
	MulMod(prod, prod,temp_2,mod);
	pro = Mod_p(prod,  mod);
}

vector<vector<ZZ>* >* multi_expo::calc_Yk(vector<ZZ>* y, int win){
	vector<vector<ZZ>* >* ret;
	vector<ZZ>* temp;
	long h,t, i,j,k,e;
	double two=2;
	ZZ prod, mod, tem;
	mod = H.get_mod();
	vector<vector<int>* >* binary;

	h= y->size()/win;
	e = pow(two, win);
	binary = to_binary(win);

	if((unsigned) h*win ==y->size()){
		ret = new vector<vector<ZZ>* >(h);
		for(i =0; i<h; i++){
			temp = new vector<ZZ>(e);
			for(j=0; j<e; j++){
				prod = 1;
				for(k=0; k<win; k++){
					PowerMod(tem,y->at(i*win+k), binary->at(j)->at(k), mod);
					MulMod(prod,prod,tem,mod);
				}
				temp->at(j)=prod;
			}
			ret->at(i)=temp;
		}
	}
	else{
		ret = new vector<vector<ZZ>* >(h+1);
		t= y->size()-h*win;
		for(i =0; i<h; i++){
			temp = new vector<ZZ>(e);
			for(j=0; j<e; j++){
				prod = 1;
				for(k=0; k<win; k++){
					PowerMod(tem,y->at(i*win+k), binary->at(j)->at(k), mod);
					MulMod(prod,prod,tem,mod);
				}
				temp->at(j)=prod;
			}
			ret->at(i)=temp;
		}
		temp = new vector<ZZ>(e);
		for(j=0; j<e; j++){
			prod = 1;
			for(k=0; k<t; k++){
				PowerMod(tem,y->at(h*win+k), binary->at(j)->at(k), mod);
				MulMod(prod,prod,tem,mod);
			}
			temp->at(j)=prod;
		}
		ret->at(h)=temp;

	}
	for (i = 0; i<e; i++){
		delete binary->at(i);
		binary->at(i)= 0;
	}
	delete binary;
	return ret;

}

vector<vector<Mod_p>* >* multi_expo::calc_Yk(vector<Mod_p>* y, int win){
	vector<vector<Mod_p>* >* ret;
	vector<Mod_p>* temp;
	long h,t, i,j,k,e;
	double two=2;
	Mod_p prod, tem;
	vector<vector<int>* >* binary;

	h= y->size()/win;
	e = pow(two, win);
	binary = to_binary(win);

	if((unsigned)h*win == y->size()){
		ret = new vector<vector<Mod_p>* >(h);
		for(i =0; i<h; i++){
			temp = new vector<Mod_p>(e);
			for(j=0; j<e; j++){
				prod = Mod_p(1,G.get_mod());
				for(k=0; k<win; k++){
					Mod_p::expo(tem,y->at(i*win+k), binary->at(j)->at(k));
					Mod_p::mult(prod,prod,tem);
				}
				temp->at(j)=prod;
			}
			ret->at(i)=temp;
		}
	}
	else{
		ret = new vector<vector<Mod_p>* >(h+1);
		t=y->size()-h*win;
		for(i =0; i<h; i++){
			temp = new vector<Mod_p>(e);
			for(j=0; j<e; j++){
				prod = Mod_p(1,G.get_mod());
				for(k=0; k<win; k++){
					Mod_p::expo(tem,y->at(i*win+k), binary->at(j)->at(k));
					Mod_p::mult(prod,prod, tem);
				}
				temp->at(j)=prod;
			}
			ret->at(i)=temp;
		}
		temp = new vector<Mod_p>(e);
		for(j=0; j<e; j++){
			prod =  Mod_p(1,G.get_mod());;
			for(k=0; k<t; k++){
				Mod_p::expo(tem,y->at(h*win+k), binary->at(j)->at(k));
				Mod_p::mult(prod,prod,tem);
			}
			temp->at(j)=prod;
		}
		ret->at(h)=temp;

	}
	j = binary->size();
	for(i=0; i<j; i++){
		delete binary->at(i);
		binary->at(i)=0;
	}
	for (i = 0; i<e; i++){
		delete binary->at(i);
		binary->at(i)= 0;
	}
	delete binary;
	return ret;

}
vector<vector<ZZ>* >* multi_expo::calc_Yk(vector<vector<vector<ZZ>*>*>* y, int win, long pos, long pos_2){
	vector<vector<ZZ>* >* ret;
	vector<ZZ>* temp;
	long h,t, i,j,k,e;
	double two=2;
	ZZ prod, mod;
	mod = H.get_mod();
	vector<vector<int>* >* binary;

	h= y->size()/win;
	e = pow(two, win);
	binary = to_binary(win);

	if((unsigned)h*win ==y->size()){
		ret = new vector<vector<ZZ>* >(h);
		for(i =0; i<h; i++){
			temp = new vector<ZZ>(e);
			for(j=0; j<e; j++){
				prod = 1;
				for(k=0; k<win; k++){
					MulMod(prod,prod,PowerMod(y->at(i*win+k)->at(pos_2)->at(pos), binary->at(j)->at(k), mod),mod);
				}
				temp->at(j)=prod;
			}
			ret->at(i)=temp;
		}
	}
	else{
		ret = new vector<vector<ZZ>*>(h+1);
		t= y->size()-h*win;
		for(i =0; i<h; i++){
			temp = new vector<ZZ>(e);
			for(j=0; j<e; j++){
				prod = 1;
				for(k=0; k<win; k++){
					MulMod(prod,prod,PowerMod(y->at(i*win+k)->at(pos_2)->at(pos), binary->at(j)->at(k), mod),mod);
				}
				temp->at(j)=prod;
			}
			ret->at(i)=temp;
		}
		temp = new vector<ZZ>(e);
		for(j=0; j<e; j++){
			prod = 1;
			for(k=0; k<t; k++){
				MulMod(prod,prod,PowerMod(y->at(h*win+k)->at(pos_2)->at(pos), binary->at(j)->at(k), mod),mod);
			}
			temp->at(j)=prod;
		}
		ret->at(h)=temp;

	}
	for (i = 0; i<e; i++){
			delete binary->at(i);
			binary->at(i)= 0;
		}
	delete binary;
	return ret;

}

ZZ multi_expo::multi_expo_LL(vector<ZZ>* y, vector<vector<ZZ>*>* e, int win, long pos){
	vector<vector<ZZ>*>* Yk;
	ZZ ret;
	double two = 2;
	int expo, tem, i,j,k,h;

	ZZ mod = H.get_mod();
	long t = NumBits(H.get_ord());
	h= y->size()/win;
	Yk = calc_Yk(y,win);
	ret = 1;

	if((unsigned)h*win == y->size()){
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		for(k=t-2;k>=0; k--){
			SqrMod(ret, ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
		}
	}
	else{
		tem= y->size()-h*win;
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		expo=0;
		for(j=h*win; j<h*win+tem; j++){
			expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-h*win);
		}
		MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		for(k=t-2;k>=0; k--){
			SqrMod(ret, ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
			expo=0;
			for(j=h*win; j<h*win+tem; j++){
				expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-h*win);
			}
			MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		}
	}
	j = Yk->size();
	for(i=0; i<j; i++){
		delete Yk->at(i);
		Yk->at(i)=0;
	}
	delete Yk;
	return ret;

}

void multi_expo::multi_expo_LL(ZZ& ret, vector<ZZ>* y, vector<vector<ZZ>*>* e, int win, long pos){
	vector<vector<ZZ>*>* Yk;
	//ZZ ret;
	double two = 2;
	int expo, tem, i,j,k,h;

	ZZ mod = H.get_mod();
	long t = NumBits(H.get_ord());
	h= y->size()/win;
	Yk = calc_Yk(y,win);
	ret = 1;

	if((unsigned)h*win == y->size()){
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		for(k=t-2;k>=0; k--){
			SqrMod(ret, ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
		}
	}
	else{
		tem= y->size()-h*win;
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		expo=0;
		for(j=h*win; j<h*win+tem; j++){
			expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-h*win);
		}
		MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		for(k=t-2;k>=0; k--){
			SqrMod(ret, ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
			expo=0;
			for(j=h*win; j<h*win+tem; j++){
				expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-h*win);
			}
			MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		}
	}
	j = Yk->size();
	for(i=0; i<j; i++){
		delete Yk->at(i);
		Yk->at(i)=0;
	}
	delete Yk;

}

ZZ multi_expo::multi_expo_LL(vector<vector<vector<ZZ>*>*>* y, vector<vector<ZZ>*>* e, int win, long pos, long pos_2){
	vector<vector<ZZ>*>* Yk;
	ZZ ret;
	double two = 2;
	int expo, tem, i,j,k,h;

	ZZ mod = H.get_mod();
	long t = NumBits(H.get_ord());
	h= y->size()/win;
	Yk = calc_Yk(y,win, pos, pos_2);

	ret = 1;

	if((unsigned)h*win == y->size()){
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		for(k=t-2;k>=0; k--){
			SqrMod(ret,ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret, ret,Yk->at(i)->at(expo),mod);
			}
		}
	}
	else{
		tem= y->size()-h*win;
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		expo=0;
		for(j=h*win; j<h*win+tem; j++){
			expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-h*win);
		}
		MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		for(k=t-2;k>=0; k--){
			SqrMod(ret,ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
			expo=0;
			for(j=h*win; j<h*win+tem; j++){
				expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-h*win);
			}
			MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		}
	}
	j = Yk->size();
	for(i=0; i<j; i++){
		delete Yk->at(i);
		Yk->at(i)=0;
	}
	delete Yk;
	return ret;

}

void multi_expo::multi_expo_LL(ZZ& ret, vector<vector<vector<ZZ>*>*>* y, vector<vector<ZZ>*>* e, int win, long pos, long pos_2){
	vector<vector<ZZ>*>* Yk;
	//ZZ ret;
	double two = 2;
	int expo, tem, i,j,k,h;

	ZZ mod = H.get_mod();
	long t = NumBits(H.get_ord());
	h= y->size()/win;
	Yk = calc_Yk(y,win, pos, pos_2);

	ret = 1;

	if((unsigned)h*win == y->size()){
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		for(k=t-2;k>=0; k--){
			SqrMod(ret,ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret, ret,Yk->at(i)->at(expo),mod);
			}
		}
	}
	else{
		tem= y->size()-h*win;
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		expo=0;
		for(j=h*win; j<h*win+tem; j++){
			expo = expo + bit(e->at(j)->at(pos),t-1)*pow(two,j-h*win);
		}
		MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		for(k=t-2;k>=0; k--){
			SqrMod(ret,ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
			expo=0;
			for(j=h*win; j<h*win+tem; j++){
				expo = expo+ bit(e->at(j)->at(pos),k)*pow(two,j-h*win);
			}
			MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		}
	}
	j = Yk->size();
	for(i=0; i<j; i++){
		delete Yk->at(i);
		Yk->at(i)=0;
	}
	delete Yk;
}

Mod_p multi_expo::multi_expo_LL(vector<Mod_p>* y, vector<ZZ>* e, int win){
	vector<vector<Mod_p>*>* Yk;
	Mod_p ret;
	double two = 2;
	int expo, i,j,k,h,t,tem;
	t = NumBits(G.get_ord());

	h= y->size()/win;
	Yk = calc_Yk(y,win);
	ret = Mod_p(1,G.get_mod());
	if((unsigned)win*h==y->size()){
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j),t-1)*pow(two,j-i*win);
			}
			Mod_p::mult(ret ,ret,Yk->at(i)->at(expo));
		}

		for(k=t-2;k>=0; k--){
			ret = ret*ret;
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j),k)*pow(two,j-i*win);
				}
				Mod_p::mult(ret ,ret,Yk->at(i)->at(expo));
			}
		}
	}
	else{
		tem=y->size()-h*win;
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j),t-1)*pow(two,j-i*win);
			}
			Mod_p::mult(ret ,ret,Yk->at(i)->at(expo));
		}
		expo=0;
		for(j=h*win; j<h*win+tem; j++){
			expo = expo + bit(e->at(j),t-1)*pow(two,j-h*win);
		}
		Mod_p::mult(ret ,ret,Yk->at(h)->at(expo));

		for(k=t-2;k>=0; k--){
			ret = ret*ret;
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j),k)*pow(two,j-i*win);
				}
				Mod_p::mult(ret,ret,Yk->at(i)->at(expo));
			}
			expo=0;
			for(j=h*win; j<h*win+tem; j++){
				expo = expo + bit(e->at(j),k)*pow(two,j-h*win);
			}
			Mod_p::mult(ret ,ret,Yk->at(h)->at(expo));
		}

	}
	j = Yk->size();
	for(i=0; i<j; i++){
		delete Yk->at(i);
		Yk->at(i)=0;
	}
	delete Yk;
	return ret;
}

void multi_expo::multi_expo_LL(Mod_p& ret, vector<Mod_p>* y, vector<ZZ>* e, int win){
	vector<vector<Mod_p>*>* Yk;

	double two = 2;
	int expo, i,j,k,h,t,tem;
	long length;

	t = NumBits(G.get_ord());
/*	if(e->size() < y->size()){
		length = e->size();
		h= length/win;
	} else {*/
		length = y->size();
		h = length/win;
	//}
	Yk = calc_Yk(y,win);
	ret = Mod_p(1,G.get_mod());

	if(win*h==length){
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j),t-1)*pow(two,j-i*win);
			}
			Mod_p::mult(ret ,ret,Yk->at(i)->at(expo));
		}

		for(k=t-2;k>=0; k--){
			ret = ret*ret;
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j),k)*pow(two,j-i*win);
				}
				Mod_p::mult(ret ,ret,Yk->at(i)->at(expo));
			}
		}
	}
	else{
		tem=length-h*win;
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j),t-1)*pow(two,j-i*win);
			}
			Mod_p::mult(ret ,ret,Yk->at(i)->at(expo));
		}
		expo=0;
		for(j=h*win; j<h*win+tem; j++){
			expo = expo + bit(e->at(j),t-1)*pow(two,j-h*win);
		}
		Mod_p::mult(ret ,ret,Yk->at(h)->at(expo));

		for(k=t-2;k>=0; k--){
			ret = ret*ret;
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j),k)*pow(two,j-i*win);
				}
				Mod_p::mult(ret,ret,Yk->at(i)->at(expo));
			}
			expo=0;
			for(j=h*win; j<h*win+tem; j++){
				expo = expo + bit(e->at(j),k)*pow(two,j-h*win);
			}
			Mod_p::mult(ret ,ret,Yk->at(h)->at(expo));
		}

	}
	j = Yk->size();
	for(i=0; i<j; i++){
		delete Yk->at(i);
		Yk->at(i)=0;
	}
	delete Yk;
}

void multi_expo::multi_expo_LL(ZZ& ret, vector<ZZ>* y, vector<ZZ>* e, int win){
	vector<vector<ZZ>*>* Yk;

	double two = 2;
	int expo, tem, i,j,k,h;

	ZZ mod = H.get_mod();
	long t = NumBits(H.get_ord());
	h= y->size()/win;
	Yk = calc_Yk(y,win);
	ret = 1;

	if((unsigned)h*win == y->size()){
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		for(k=t-2;k>=0; k--){
			SqrMod(ret, ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
		}
	}
	else{
		tem= y->size()-h*win;
		for(i=0; i<h; i++){
			expo=0;
			for(j=i*win; j<(i+1)*win; j++){
				expo = expo + bit(e->at(j),t-1)*pow(two,j-i*win);
			}
			MulMod(ret,ret,Yk->at(i)->at(expo),mod);
		}
		expo=0;
		for(j=h*win; j<h*win+tem; j++){
			expo = expo + bit(e->at(j),t-1)*pow(two,j-h*win);
		}
		MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		for(k=t-2;k>=0; k--){
			SqrMod(ret, ret,mod);
			for(i=0; i<h; i++){
				expo=0;
				for(j=i*win; j<(i+1)*win; j++){
					expo = expo+ bit(e->at(j),k)*pow(two,j-i*win);
				}
				MulMod(ret,ret,Yk->at(i)->at(expo),mod);
			}
			expo=0;
			for(j=h*win; j<h*win+tem; j++){
				expo = expo+ bit(e->at(j),k)*pow(two,j-h*win);
			}
			MulMod(ret,ret,Yk->at(h)->at(expo),mod);
		}
	}
	j = Yk->size();
	for(i=0; i<j; i++){
		delete Yk->at(i);
		Yk->at(i)=0;
	}
	delete Yk;

}

void multi_expo::multi_expo_LL(Cipher_elg& ret, Cipher_elg c1, Cipher_elg c2, Cipher_elg c3, Cipher_elg c4, vector<ZZ>* e, int win){

	ZZ ret_u, ret_v;
	vector<ZZ>* c_u;
	vector<ZZ>* c_v;

	c_u = new vector<ZZ>(4);
	c_u->at(0) = c1.get_u();
	c_u->at(1) = c2.get_u();
	c_u ->at(2) = c3.get_u();
	c_u->at(3) = c4.get_u();

	c_v = new vector<ZZ>(4);
	c_v->at(0) = c1.get_v();
	c_v->at(1) = c2.get_v();
	c_v ->at(2) = c3.get_v();
	c_v->at(3) = c4.get_v();

	multi_expo_LL(ret_u, c_u, e, win);
	multi_expo_LL(ret_v, c_v, e, win);
	delete c_u;
	delete c_v;
	ret = Cipher_elg(ret_u, ret_v, H.get_mod());

}

vector<int>* multi_expo::to_basis_sw(ZZ e, long num_b, int omega_sw){

	long i, j, t, te;
	vector<int>* temp;
	vector<int>* basis;
	temp = new vector<int>(omega_sw);
	basis = new vector<int>(num_b);

	i = num_b-1;
	while(i>=omega_sw){
		if (bit(e,i)==0){
			basis->at(i)=0;
			i=i-1;
		}
		else{
			for(t= i-omega_sw+1; t < i+1; t++){
				if(bit(e,t)==1){
					te = i-t;
					for(j = 0; j<= te; j++){
						temp->at(j)=bit(e, t+j);
					}
					for(j =te+1; j<omega_sw; j++){
						temp->at(j) =0;
					}
					break;
				}
			}
			basis->at(t)= to_long(temp);
			for(j = t+1; j<=i; j++){
				basis->at(j)=0;
			}
			i=t-1;
		}
	}
	while(i>=0){
		if (bit(e,i)==0){
			basis->at(i)=0;
			i=i-1;
		}
		else{
			for(t= 0; t < i+1; t++){
				if(bit(e,t)==1){
					te = i-t;
					for(j = 0; j<= te; j++){
						temp->at(j)=bit(e, t+j);
					}
					for(j =te+1; j<omega_sw; j++){
						temp->at(j) =0;
					}
					break;
				}
			}
			basis->at(t)= to_long(temp);
			for(j = t+1; j<=i; j++){
				basis->at(j)=0;
			}
			i=t-1;
		}

	}
	delete temp;
	return basis;
}

ZZ multi_expo::multi_expo_sw( ZZ e_1, ZZ e_2, int omega_sw,  vector<vector<ZZ>* >* gen_prec){
	long i;
	int t_1, t_2;
	ZZ prod, p;
	vector<int>* E_1;
	vector<int>* E_2;
	long num_b;
	ZZ mod;
	mod = G.get_mod();
	num_b = NumBits(G.get_ord());
	E_1 = to_basis_sw(e_1, num_b, omega_sw);
	E_2 = to_basis_sw(e_2, num_b, omega_sw);
	prod = 1;
	t_1 = 0;
	t_2 = 0;
	t_1 = (E_1->at(num_b-1)+1)/2;
	t_2 = (E_2->at(num_b-1)+1)/2;
	if ( t_1> 0){
		MulMod(prod,prod,gen_prec->at(0)->at(t_1-1),mod);
	}
	if ( t_2> 0){
		MulMod(prod,prod,gen_prec->at(1)->at(t_2-1),mod);
	}
	for(i = num_b-2; i>=0; i--){
		t_1 = (E_1->at(i)+1)/2;
		t_2 = (E_2->at(i)+1)/2;
		SqrMod(prod,prod, mod);
		if ( t_1> 0){
			MulMod(prod,prod,gen_prec->at(0)->at(t_1-1),mod);
		}
		if ( t_2> 0){
			MulMod(prod,prod,gen_prec->at(1)->at(t_2-1),mod);
		}
	}
	delete E_1;
	delete E_2;
	 return prod;
}

void multi_expo::multi_expo_sw(ZZ& prod, ZZ e_1, ZZ e_2, int omega_sw, vector<vector<ZZ>* >* gen_prec){
	long i;
	int t_1, t_2;
	ZZ p;
	vector<int>* E_1;
	vector<int>* E_2;
	long num_b;
	ZZ mod;
	mod = G.get_mod();
	num_b = NumBits(G.get_ord());
	E_1 = to_basis_sw(e_1, num_b, omega_sw);
	E_2 = to_basis_sw(e_2, num_b, omega_sw);
	prod = 1;
	t_1 = 0;
	t_2 = 0;
	t_1 = (E_1->at(num_b-1)+1)/2;
	t_2 = (E_2->at(num_b-1)+1)/2;
	if ( t_1> 0){
		MulMod(prod,prod,gen_prec->at(0)->at(t_1-1),mod);
	}
	if ( t_2> 0){
		MulMod(prod,prod,gen_prec->at(1)->at(t_2-1),mod);
	}
	for(i = num_b-2; i>=0; i--){
		t_1 = (E_1->at(i)+1)/2;
		t_2 = (E_2->at(i)+1)/2;
		SqrMod(prod,prod, mod);
		if ( t_1> 0){
			MulMod(prod,prod,gen_prec->at(0)->at(t_1-1),mod);
		}
		if ( t_2> 0){
			MulMod(prod,prod,gen_prec->at(1)->at(t_2-1),mod);
		}
	}
	delete E_1;
	delete E_2;
	// return prod;
}