/* ScaFES
 * Copyright (c) 2011-2015, ZIH, TU Dresden, Federal Republic of Germany.
 * For details, see the files COPYING and LICENSE in the base directory
 * of the package.
 */

/**
 *  @file ScaFES_Ntuple.hpp
 *  @brief Contains the class template Ntuple.
 */
#ifndef SCAFES_NTUPLE_HPP_
#define SCAFES_NTUPLE_HPP_

#include "ScaFES_Config.hpp"

//#include <cstdint>
#include <iomanip>
#include <iostream>
#include <ios>
#include <vector>
#include <cmath>
#include <type_traits>
#include <stdexcept>

#ifdef SCAFES_HAVE_BOOST
#include <boost/version.hpp>
#endif

#ifdef SCAFES_HAVE_BOOST_SERIALIZATION
namespace boost
{
namespace serialization
{
    class access;
}
}
#include <boost/serialization/version.hpp>
#if BOOST_VERSION < 105900
   #include <boost/serialization/pfto.hpp>
#endif
#endif

namespace ScaFES
{

/*******************************************************************************
 ******************************************************************************/
/** \class Ntuple
 * @brief The class template \c Ntuple represents a
 * \c NN dimensional vector of a given type \c TT.
 *
 * All important unary and binary operators are implemented resp. overloaded
 * and behave as expected. Especially, many operators work componentwise.
 *
 * Usually, the memory for the vector elements must be allocated dynamically,
 * because the number of elements is known at run time. This is related
 * with additional effort for creating and deleting the elements on the
 * heap.
 * Due to the nontype template parameter \c NN the number of vector elements
 * is known at compile time. Thus, it is possible to allocate the memory
 * for the vector elements statically.
 *
 * Advantages:
 * - Automatic compiler SIMD vectorizations can be better applied.
 * - Using the class template \c Ntuple with three elements
 * instead of a class containing explicitly three elements does not affect
 * the performance, but will give more flexibility in solving other than
 * three-dimensional problems.
 *
 * \code
 * // Constructors.
 * ScaFES::Ntuple<double,3> a(1.1, 2.2, 3.3);  // a = (1.1, 2.2, 3.3)
 * ScaFES::Ntuple<double,3> b(6.0, 6.0, 4.0);  // b = (3.0, 3.0, 2.0)
 * ScaFES::Ntuple<double,3> c = a + 2 * b;     // c = (7.1, 8.2, 7.3)
 *
 * // Arithmetic operators.
 * a += 2.0;                                   // a = (3.1, 4.2, 5.3)
 * c = b * b;                                  // c = (9.0, 9.0, 4.0)
 *
 * // Relational operators
 * a < b;                                      // false: 3.3 > 2.0
 * b *= 2.0;                                   // b = (6.0, 6.0, 4.0)
 * b > a;                                      // true
 * \endcode
 */
template <typename TT, std::size_t NN = 3> class Ntuple
{

public:
    /*----------------------------------------------------------------------
    | TYPE DEFINITIONS.
    ----------------------------------------------------------------------*/
    /** Re-export typename TT STL-like. */
    typedef TT value_type;

    /*----------------------------------------------------------------------
    | FRIEND CLASSES.
    ----------------------------------------------------------------------*/
#ifdef SCAFES_HAVE_BOOST_SERIALIZATION
    friend class boost::serialization::access;
#endif

    /*----------------------------------------------------------------------
    | LIFE CYCLE METHODS.
    ----------------------------------------------------------------------*/
    /** Creates the default constructor.
     *  All elements are initialized by zero.*/
    Ntuple<TT, NN>();

    /** Creates a special constructor for twodimensional vectors:
     *  The elements are initialized by given values. */
    Ntuple<TT, NN>(const TT& a, const TT& b);

    /** Creates a special constructor for threedimensional vectors:
     *  The elements are initialized by given values. */
    Ntuple<TT, NN>(const TT& a, const TT& b, const TT& c);

    /** Creates own constructor:
     *  All elements are initalized by the same value. */
    Ntuple<TT, NN>(const TT& scal);

    /** Creates own constructor: Conversion method.
     *  All elements are set by a given STL vector. */
    Ntuple<TT, NN>(const std::vector<TT>& vec);

    /** Creates copy constructor.
     * \remarks Use copy-and-swap idiom.
     */
    Ntuple<TT, NN>(const Ntuple<TT, NN>& rhs);

    /** Creates copy assignment operator.
     * All elements of the rhs. vector will be assigned. */
    Ntuple<TT, NN>& operator=(Ntuple<TT, NN> rhs);

    /** Creates the destructor. */
    ~Ntuple() {};

    /*----------------------------------------------------------------------
    | GETTER METHODS.
    ----------------------------------------------------------------------*/
    /** Returns the dimension (= number of elements) of the n-tuple. */
    size_t dim() const;

    /** Returns a reference to the element at idx \c ii in the n-tuple.
     * \remarks With range check!
     */
    const TT& elem(const std::size_t& ii) const;

    /** Returns the element at idx \c ii in the n-tuple.
     * \remarks Without range check! */
    const TT& operator[](const std::size_t& ii) const;

    /*----------------------------------------------------------------------
    | SETTER METHODS.
    ----------------------------------------------------------------------*/
    /** Returns a reference to the element at idx ii in the Ntuple.
     * \remarks With range check!
     */
    TT& elem(const std::size_t& ii);

    /** Returns a reference to the element at idx \c ii in the n-tuple.
     * \remarks Without range check! */
    TT& operator[](const std::size_t& ii);

    /*----------------------------------------------------------------------
    | ARITHMETIC METHODS.
    ----------------------------------------------------------------------*/
    /** Creates an assignment operator.
     * All elements will be assigned to a given scalar.*/
    ScaFES::Ntuple<TT, NN>& operator=(const TT& sca);

    /** Multiplies this n-tuple by a given scalar (elementwise). */
    template <typename CT> ScaFES::Ntuple<TT, NN>& operator*=(const CT& sca);

    /** Divides this n-tuple by a given scalar (elementwise). */
    template <typename CT> ScaFES::Ntuple<TT, NN>& operator/=(const CT& sca);

    /** Adds a given rhs n-tuple to this n-tuple. */
    ScaFES::Ntuple<TT, NN>& operator+=(const ScaFES::Ntuple<TT, NN>& rhs);

    /** Subtracts a given rhs n-tuple from this n-tuple. */
    ScaFES::Ntuple<TT, NN>& operator-=(const ScaFES::Ntuple<TT, NN>& rhs);

    /** Multiplies this n-tuple by a given rhs n-tuple. */
    ScaFES::Ntuple<TT, NN>& operator*=(const ScaFES::Ntuple<TT, NN>& rhs);

    /** Divides this n-tuple by a given rhs n-tuple. */
    ScaFES::Ntuple<TT, NN>& operator/=(const ScaFES::Ntuple<TT, NN>& rhs);

    /*----------------------------------------------------------------------
    | COMPARISON METHODS.
    ----------------------------------------------------------------------*/
    /** Compares elementwise if this n-tuple is smaller than a a given rhs
     *  n-tuple. */
    bool operator<(const ScaFES::Ntuple<TT, NN>& rhs) const;

    /** Compares elementwise if this n-tuple is greater or equal
     *  than a a given rhs n-tuple. */
    bool operator>=(const ScaFES::Ntuple<TT, NN>& rhs) const;

    /** Compares elementwise if this n-tuple is smaller or equal
     *  than a a given rhs n-tuple. */
    bool operator<=(const ScaFES::Ntuple<TT, NN>& rhs) const;

    /** Compares elementwise if this n-tuple is greater than a a given rhs
     *  n-tuple. */
    bool operator>(const ScaFES::Ntuple<TT, NN>& rhs) const;

    /** Compares elementwise if this n-tuple is equal to a given rhs
     *  n-tuple. */
    bool operator==(const ScaFES::Ntuple<TT, NN>& rhs) const;

    /** Compares elementwise if this n-tuple is unequal to a given rhs
     *  n-tuple. */
    bool operator!=(const ScaFES::Ntuple<TT, NN>& rhs) const;

    /*----------------------------------------------------------------------
    | WORK METHODS.
    ----------------------------------------------------------------------*/
    /** Returns the index of the element with maximal value. */
    std::size_t idxMaxElem() const;

    /** Computes the dimension of the n-tuple,
     * i.e., all elements will be multiplied. */
    TT size() const;

    /** Computes the 1-norm. */
    double norm1() const;

#ifdef SCAFES_HAVE_BOOST_SERIALIZATION
    /** Serializes this class. */
    template <class Archive>
    void serialize(Archive& ar, unsigned int const version);
#endif

    /*----------------------------------------------------------------------
    | FREE METHODS WHICH ARE FRIENDS OF THIS CLASS.
    ----------------------------------------------------------------------*/
    /** Overloads the output operator \c operator<<.
     * An n-tuple is printed this way: '(a_0, a_1,..., a_{n-1})' */
    template <typename ST, std::size_t MM>
    friend std::ostream& operator<<(std::ostream& output,
                                    const ScaFES::Ntuple<ST, MM>& t);

    /** Method to swap members of two ScaFES::Ntuples. */
    template <typename ST, std::size_t MM>
    friend void swap(ScaFES::Ntuple<ST, MM>& first,
                     ScaFES::Ntuple<ST, MM>& second);

    /*----------------------------------------------------------------------
    | CONSTANTS.
    ----------------------------------------------------------------------*/
    /** Dimension of n-tuple. */
    static const std::size_t DIM = NN;

private:
    /*----------------------------------------------------------------------
    | MEMBER VARIABLES.
    ----------------------------------------------------------------------*/
    /** Static array for storing the elements of an n-tuple. */
    TT mElem[NN];

}; // End of class //

/*******************************************************************************
 * FREE METHODS.
 ******************************************************************************/
/** Swaps two objects of the class \c ScaFES::Ntuple. */
template <typename TT, std::size_t NN>
void swap(ScaFES::Ntuple<TT, NN>& first, ScaFES::Ntuple<TT, NN>& second);
/*----------------------------------------------------------------------------*/
/** Preprares writing an object of the class \c ScaFES::Ntuple to output. */
template <typename TT, std::size_t NN>
std::ostream& operator<<(std::ostream& output, const ScaFES::Ntuple<TT, NN>& t);

/*******************************************************************************
 * LIFE CYCLE METHODS.
 ******************************************************************************/
template <typename TT, std::size_t NN> inline Ntuple<TT, NN>::Ntuple()
{
    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        this->mElem[iii] = static_cast<TT>(0);
    }
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline Ntuple<TT, NN>::Ntuple(const TT& scal)
{
    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        this->mElem[iii] = scal;
    }
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline Ntuple<TT, NN>::Ntuple(const TT& a, const TT& b)
{
    static_assert((NN == 2), "Constructor is valid in case NN=2, only.");
    this->mElem[0] = a;
    this->mElem[1] = b;
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline Ntuple<TT, NN>::Ntuple(const TT& a, const TT& b, const TT& c)
{
    static_assert((NN == 3), "Constructor is valid in case NN=3, only.");
    this->mElem[0] = a;
    this->mElem[1] = b;
    this->mElem[2] = c;
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline Ntuple<TT, NN>::Ntuple(const std::vector<TT>& vec)
{
    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        this->mElem[iii] = vec.at(iii);
    }
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline Ntuple<TT, NN>::Ntuple(const ScaFES::Ntuple<TT, NN>& rhs)
{
    for (std::size_t iii = 0; iii < rhs.dim(); ++iii)
    {
        this->mElem[iii] = rhs.elem(iii);
    }
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline ScaFES::Ntuple<TT, NN>& Ntuple<TT, NN>::
operator=(ScaFES::Ntuple<TT, NN> rhs)
{
    swap(*this, rhs);
    return *this;
}

/*******************************************************************************
 * GETTER METHODS.
 ******************************************************************************/
template <typename TT, std::size_t NN>
inline std::size_t Ntuple<TT, NN>::dim() const
{
    return NN;
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline const TT& ScaFES::Ntuple<TT, NN>::elem(const std::size_t& idx) const
{
    return this->mElem[idx];
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline const TT& ScaFES::Ntuple<TT, NN>::
operator[](const std::size_t& idx) const
{
    return this->mElem[idx];
}

/*******************************************************************************
 * SETTER METHODS.
 ******************************************************************************/
template <typename TT, std::size_t NN>
inline TT& Ntuple<TT, NN>::elem(const std::size_t& idx)
{
    return this->mElem[idx];
    // const_cast<TT&>(static_cast<const
    // ScaFES::Ntuple<TT,NN>&>(*this).elem(idx));
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline TT& Ntuple<TT, NN>::operator[](const std::size_t& idx)
{
    return this->mElem[idx];
    // const_cast<TT&>(static_cast<const ScaFES::Ntuple<TT,NN>&>(*this)[idx]);
}

/*******************************************************************************
 * COMPARISON METHODS.
 ******************************************************************************/
template <typename TT, std::size_t NN>
inline bool Ntuple<TT, NN>::operator<(const ScaFES::Ntuple<TT, NN>& rhs) const
{
    bool res = true;

    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        if (!(this->mElem[iii] < rhs.elem(iii)))
        {
            res = false;
            break;
        }
    }

    return res;
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline bool Ntuple<TT, NN>::operator<=(const ScaFES::Ntuple<TT, NN>& rhs) const
{
    bool res = true;

    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        if (this->mElem[iii] > rhs.elem(iii))
        {
            res = false;
            break;
        }
    }

    return res;
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline bool Ntuple<TT, NN>::operator>=(const ScaFES::Ntuple<TT, NN>& rhs) const
{
    bool res = true;

    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        if ((this->mElem[iii] < rhs.elem(iii)))
        {
            res = false;
            break;
        }
    }

    return res;
    // return !(*this < rhs); Does not work because we are working elementwise!
}

/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline bool Ntuple<TT, NN>::operator>(const ScaFES::Ntuple<TT, NN>& rhs) const
{
    bool res = true;

    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        if (!(this->mElem[iii] > rhs.elem(iii)))
        {
            res = false;
            break;
        }
    }

    return res;
    // return !(*this <= rhs); Does not work because we are working elementwise!
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline bool Ntuple<TT, NN>::operator==(const ScaFES::Ntuple<TT, NN>& rhs) const
{
    bool res = true;

    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        if (!(::fabs(this->mElem[iii] - rhs.elem(iii)) < 2.2e-15))
        {
            res = false;
            break;
        }
    }

    return res;
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline bool Ntuple<TT, NN>::operator!=(const ScaFES::Ntuple<TT, NN>& rhs) const
{
    return !(*this == rhs);
}

/*******************************************************************************
 * WORK METHODS.
 ******************************************************************************/
template <typename TT, std::size_t NN>
inline std::size_t Ntuple<TT, NN>::idxMaxElem() const
{
    std::size_t idx = 0;
    TT maxElem = this->mElem[0];

    for (std::size_t iii = 1; iii < NN; ++iii)
    {
        if (maxElem < this->mElem[iii])
        {
            maxElem = this->mElem[iii];
            idx = iii;
        }
    }

    return idx;
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN> inline TT Ntuple<TT, NN>::size() const
{
    TT prod = this->mElem[0];

    for (std::size_t iii = 1; iii < NN; ++iii)
    {
        prod *= this->mElem[iii];
    }

    return prod;
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline double Ntuple<TT, NN>::norm1() const
{
    double res = ::fabs(this->mElem[0]);

    for (std::size_t iii = 1; iii < NN; ++iii)
    {
        res += ::fabs(this->mElem[iii]);
    }

    return res;
}
/*----------------------------------------------------------------------------*/
#ifdef SCAFES_HAVE_BOOST_SERIALIZATION
template <typename TT, std::size_t NN>
template <class Archive>
inline void Ntuple<TT, NN>::serialize(Archive& ar, unsigned int const version)
{
    if (1 <= version)
    {
        ar&(this->mElem);
    }
}
#endif

/*******************************************************************************
 * ARITHMETIC METHODS.
 ******************************************************************************/
template <typename TT, std::size_t NN>
inline ScaFES::Ntuple<TT, NN>& Ntuple<TT, NN>::operator=(const TT& sca)
{
    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        this->mElem[iii] = static_cast<TT>(sca);
    }

    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
template <typename CT>
inline ScaFES::Ntuple<TT, NN>& Ntuple<TT, NN>::operator*=(const CT& sca)
{
    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        this->mElem[iii] *= static_cast<TT>(sca);
    }

    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
template <typename CT>
inline ScaFES::Ntuple<TT, NN>& Ntuple<TT, NN>::operator/=(const CT& sca)
{
    if (::fabs(sca) < 2.2e-12)
    {
        throw std::runtime_error("Given scalar is very near to zero.");
    }
    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        this->mElem[iii] /= static_cast<TT>(sca);
    }

    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline ScaFES::Ntuple<TT, NN>& Ntuple<TT, NN>::
operator+=(const ScaFES::Ntuple<TT, NN>& rhs)
{
    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        this->mElem[iii] += rhs.elem(iii);
    }

    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline ScaFES::Ntuple<TT, NN>& Ntuple<TT, NN>::
operator-=(const ScaFES::Ntuple<TT, NN>& rhs)
{
    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        this->mElem[iii] -= rhs.elem(iii);
    }

    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline ScaFES::Ntuple<TT, NN>& Ntuple<TT, NN>::
operator*=(const ScaFES::Ntuple<TT, NN>& rhs)
{
    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        this->mElem[iii] *= rhs.elem(iii);
    }

    return *this;
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline ScaFES::Ntuple<TT, NN>& Ntuple<TT, NN>::
operator/=(const ScaFES::Ntuple<TT, NN>& rhs)
{
    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        if (::fabs(rhs.elem(iii)) < 2.2e-12)
        {
            throw std::runtime_error("Element of rhs is very near to zero.");
        }
    }
    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        this->mElem[iii] /= rhs.elem(iii);
    }

    return *this;
}

/*******************************************************************************
* FREE METHODS WHICH ARE FRIENDS OF THIS CLASS.
 ******************************************************************************/
template <typename TT, std::size_t NN>
inline void swap(ScaFES::Ntuple<TT, NN>& first, ScaFES::Ntuple<TT, NN>& second)
{
    TT tmp;

    for (std::size_t iii = 0; iii < second.dim(); ++iii)
    {
        tmp = second.mElem[iii];
        second.mElem[iii] = first.mElem[iii];
        first.mElem[iii] = tmp;
    }
}
/*----------------------------------------------------------------------------*/
template <typename TT, std::size_t NN>
inline std::ostream& operator<<(std::ostream& output,
                                const ScaFES::Ntuple<TT, NN>& t)
{
    output << "[";
    for (std::size_t iii = 0; iii < NN; ++iii)
    {
        output << ::std::setw(4) << ::std::right << t.elem(iii);

        if (NN - 1 > iii)
        {
            output << "; ";
        }
    }

    output << "]";
    return output;
}

} // End of namespace. //

/*******************************************************************************
 ******************************************************************************/
#ifdef SCAFES_HAVE_BOOST_SERIALIZATION
namespace boost
{
namespace serialization
{
    /** Designed to set the boost serialization version of a class template. */
    template <typename TT, std::size_t NN>
    struct version<ScaFES::Ntuple<TT, NN>>
    {
        /** Sets the version number for serialization. */
        BOOST_STATIC_CONSTANT(unsigned long int, value = 2);
    };
}
}
#endif

#endif


#include <NTL/LLL.h>

NTL_CLIENT

long SubsetSumSolution(const vec_ZZ& z)
{
   long n = z.length()-3;
   long j;

   if (z(n+1) != 0) return 0;
   if (z(n+2) != -1 && z(n+2) != 1) return 0;
   for (j = 1; j <= n; j++)
      if (z(j) != -1 && z(j) != 1) return 0;

   return 1;
}

int main()
{
   RR::SetPrecision(150);
   long n, b, size;

   cerr << "n: ";
   cin >> n;

   cerr << "b: ";
   cin >> b;

   cerr << "size: ";
   cin >> size;

   cerr << "prune: ";
   long prune;
   cin >> prune;

   ZZ seed;
   cerr << "seed: ";
   cin >> seed;

   if (seed != 0)
      SetSeed(seed);

   char alg;
   cerr << "alg [fqQxr]: ";
   cin >> alg;

   double TotalTime = 0;
   long TotalSucc = 0;

   long iter;

   for (iter = 1; iter <= 20; iter++) {
      vec_ZZ a;
      a.SetLength(n);

      ZZ bound;

      LeftShift(bound, to_ZZ(1), b);

      long i;
      for (i = 1; i <= n; i++) {
         RandomBnd(a(i), bound);
         a(i) += 1;
      }

      ZZ S;

      do {
         RandomLen(S, n+1);
      } while (weight(S) != n/2+1);

      ZZ s;
      clear(s);
      for (i = 1; i <= n; i++)
         if (bit(S, i-1))
            s += a(i);

      mat_ZZ B(INIT_SIZE, n+1, n+3);

      for (i = 1; i <= n; i++) {
         B(i, i) = 2;
         B(i, n+1) = a(i) * n;
         B(i, n+3) = n;
      }

      for (i = 1; i <= n; i++)
         B(n+1, i) = 1;

      B(n+1, n+1) = s * n;
      B(n+1, n+2) = 1;
      B(n+1, n+3) = n;
      B(n+1, n+3) *= n/2;

      swap(B(1), B(n+1));

      for (i = 2; i <= n; i++) {
         long j = RandomBnd(n-i+2) + i;
         swap(B(i), B(j));
      }

      double t;

      LLLStatusInterval = 10;

      t = GetTime();
      switch (alg) {
      case 'f':
         BKZ_FP(B, 0.99, size, prune, SubsetSumSolution);
         break;
      case 'q':
         BKZ_QP(B, 0.99, size, prune, SubsetSumSolution);
         break;
      case 'Q':
         BKZ_QP1(B, 0.99, size, prune, SubsetSumSolution);
         break;
      case 'x':
         BKZ_XD(B, 0.99, size, prune, SubsetSumSolution);
         break;
      case 'r':
         BKZ_RR(B, 0.99, size, prune, SubsetSumSolution);
         break;
      default:
         Error("invalid algorithm");
      }

      t = GetTime()-t;

      long succ = 0;
      for (i = 1; i <= n+1; i++)
         if (SubsetSumSolution(B(i)))
            succ = 1;

      TotalTime += t;
      TotalSucc += succ;

      if (succ)
         cerr << "+";
      else
         cerr << "-";
   }

   cerr << "\n";

   cerr << "number of success: " << TotalSucc << "\n";
   cerr << "average time: " << TotalTime/20 << "\n";

   return 0;
}


#ifndef STAN_MATH_PRIM_MAT_FUN_LOG_SUM_EXP_HPP
#define STAN_MATH_PRIM_MAT_FUN_LOG_SUM_EXP_HPP

#include <stan/math/prim/scal/fun/log1p.hpp>
#include <stan/math/prim/mat/fun/Eigen.hpp>
#include <boost/math/tools/promotion.hpp>
#include <limits>
#include <vector>

namespace stan {
namespace math {

/**
 * Return the log of the sum of the exponentiated values of the specified
 * matrix of values.  The matrix may be a full matrix, a vector,
 * or a row vector.
 *
 * The function is defined as follows to prevent overflow in exponential
 * calculations.
 *
 * \f$\log \sum_{n=1}^N \exp(x_n) = \max(x) + \log \sum_{n=1}^N \exp(x_n -
 * \max(x))\f$.
 *
 * @param[in] x Matrix of specified values
 * @return The log of the sum of the exponentiated vector values.
 */
template <int R, int C>
double log_sum_exp(const Eigen::Matrix<double, R, C>& x) {
  using std::exp;
  using std::log;
  using std::numeric_limits;
  double max = -numeric_limits<double>::infinity();
  for (int i = 0; i < x.size(); i++)
    if (x(i) > max)
      max = x(i);

  double sum = 0.0;
  for (int i = 0; i < x.size(); i++)
    if (x(i) != -numeric_limits<double>::infinity())
      sum += exp(x(i) - max);

  return max + log(sum);
}

}  // namespace math
}  // namespace stan

#endif


/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2014 Jose Aparicio

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

#ifndef quantlib_binomial_loss_model_hpp
#define quantlib_binomial_loss_model_hpp

#include <ql/handle.hpp>
#include <ql/experimental/credit/basket.hpp>
#include <ql/experimental/credit/defaultlossmodel.hpp>
#include <ql/experimental/credit/constantlosslatentmodel.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/bind.hpp>
#include <algorithm>
#include <numeric>

namespace QuantLib {

    /*! Binomial Defaultable Basket Loss Model\par
    Models the portfolio loss distribution by approximatting it to an adjusted
    binomial. Fits the two moments of the loss distribution through an adapted
    binomial approximation. This simple model allows for portfolio inhomogeneity
    with no excesive cost over the LHP.\par
    See:\par
    Approximating Independent Loss Distributions with an Adjusted Binomial
    Distribution , Dominic O'Kane, 2007 EDHEC RISK AND ASSET MANAGEMENT
    RESEARCH CENTRE \par
    Modelling single name and multi-name credit derivatives Chapter
    18.5.2, Dominic O'Kane, Wiley Finance, 2008 \par
    The version presented here is adaptated to the multifactorial case
    by computing a conditional binomial approximation; notice that the Binomial
    is stable. This way the model can be used also in risk management models
    rather than only in pricing. The copula is also left
    undefined/arbitrary. \par
    LLM: Loss Latent Model template parameter able to model default and
    loss.\par
    The model is allowed and arbitrary copula, although initially designed for
    a Gaussian setup. If these exotic versions were not allowed the template
    parameter can then be dropped but the use of random recoveries should be
    added in some other way.

    \todo untested/wip for the random recovery models.
    \todo integrate with the previously computed probability inversions of
    the cumulative functions.
    */
    template<class LLM>
    class BinomialLossModel : public DefaultLossModel {
    public:
        typedef typename LLM::copulaType copulaType;
        BinomialLossModel(
            const boost::shared_ptr<LLM>& copula)
        : copula_(copula) { }
    private:
        void resetModel() {
            /* say there are defaults and these havent settled... and this is
            the engine to compute them.... is this the wrong place?:*/
            attachAmount_ = basket_->remainingAttachmentAmount();
            detachAmount_ = basket_->remainingDetachmentAmount();

            copula_->resetBasket(basket_.currentLink());// forces interface
        }
    protected:
        /*! Returns the probability of the default loss values given by the
            method lossPoints.
        */
        Disposable<std::vector<Real> >
            expectedDistribution(const Date& date) const {
            // precal date conditional magnitudes:
            std::vector<Real> notionals = basket_->remainingNotionals(date);
            std::vector<Probability> invProbs =
                basket_->remainingProbabilities(date);
            for(Size iName=0; iName<invProbs.size(); iName++)
                invProbs[iName] =
                    copula_->inverseCumulativeY(invProbs[iName], iName);

            return copula_->integratedExpectedValue(
                boost::function<Disposable<std::vector<Real> > (
                  const std::vector<Real>& v1)>(
                    boost::bind(
                        &BinomialLossModel<LLM>::lossProbability,
                        this,
                        boost::cref(date), //d,
                        boost::cref(notionals),
                        boost::cref(invProbs),
                        _1)
                    )
                );
        }
        //! attainable loss points this model provides
        Disposable<std::vector<Real> > lossPoints(const Date&) const;
        //! Returns the cumulative full loss distribution
        Disposable<std::map<Real, Probability> >
            lossDistribution(const Date& d) const;
        //! Loss level for this percentile
        Real percentile(const Date& d, Real percentile) const;
        Real expectedShortfall(const Date&d, Real percentile) const;
        Real expectedTrancheLoss(const Date& d) const;
    protected:
        // Model internal workings ----------------
        //! Average loss per credit.
        Real averageLoss(const Date&, const std::vector<Real>& reminingNots,
            const std::vector<Real>&) const;
        Real condTrancheLoss(const Date&, const std::vector<Real>& lossVals,
            const std::vector<Real>& bsktNots,
            const std::vector<Probability>& uncondDefProbs,
            const std::vector<Real>&) const;
        // expected as in time-value, not average, see literature
        Disposable<std::vector<Real> >
            expConditionalLgd(const Date& d,
                               const std::vector<Real>& mktFactors) const
        {
            std::vector<Real> condLgds;
            const std::vector<Size>& evalDateLives = basket_->liveList();
            for(Size i=0; i<evalDateLives.size(); i++)
                condLgds.push_back(1.-copula_->conditionalRecovery(d,
                    evalDateLives[i], mktFactors));
            return condLgds;
        }

        //! Loss probability density conditional on the market factor value.
        // Heres where the burden of the algorithm setup lies.
        Disposable<std::vector<Real> >
            lossProbability(
                const Date& date,
                // expected exposures at the passed date, no wrong way means
                //  no dependence of the exposure with the mkt factor
                const std::vector<Real>& bsktNots,
                const std::vector<Real>& uncondDefProbInv,
                            const std::vector<Real>&  mktFactor) const;
    protected:
        const boost::shared_ptr<LLM> copula_;

        // cached arguments:
        // remaining basket magnitudes:
        mutable Real attachAmount_, detachAmount_;
    };

    //-------------------------------------------------------------------------

    /* The algorithm to compute the prob. of n defaults in the basket is
        recursive. For this reason theres no sense in returning the prob
        distribution of a given number of defaults.
    */
    template< class LLM>
    Disposable<std::vector<Real> > BinomialLossModel<LLM>::lossProbability(
        const Date& date,
        const std::vector<Real>& bsktNots,
        const std::vector<Real>& uncondDefProbInv,
        const std::vector<Real>& mktFactors) const
    {   // the model as it is does not model the exposures conditional to the
        //   mkt factr, otherwise this needs revision
        /// model does not take the unconditional rr
        Size bsktSize = basket_->remainingSize();
        /* The conditional loss per unit notional of each name at time 'date'
            The spot recovery model is returning for all i's:
            \frac{\int_0^t  [1-rr_i(\tau; \xi)] P_{def-i}(0, \tau; \xi) d\tau}
                 {P_{def-i}(0,t;\xi)}
            and the constant recovery model is simply returning:
            1-RR_i
        */
        // conditional fractional LGD expected as given by the recovery model
        //   for the ramaining(live) names at the current eval date.
        std::vector<Real> fractionalEL = expConditionalLgd(date, mktFactors);
        std::vector<Real> lgdsLeft;
        std::transform(fractionalEL.begin(), fractionalEL.end(),
            bsktNots.begin(), std::back_inserter(lgdsLeft),
            std::multiplies<Real>());
        Real avgLgd =
            std::accumulate(lgdsLeft.begin(), lgdsLeft.end(), Real(0.)) /
                bsktSize;

        std::vector<Probability> condDefProb(bsktSize, 0.);
        for(Size j=0; j<bsktSize; j++)//transform
            condDefProb[j] =
                copula_->conditionalDefaultProbabilityInvP(uncondDefProbInv[j],
                    j, mktFactors);
        // of full portfolio:
        Real avgProb = avgLgd <= QL_EPSILON ? 0. : // only if all are 0
                std::inner_product(condDefProb.begin(),
                    condDefProb.end(), lgdsLeft.begin(), 0.)
                / (avgLgd * bsktSize);
        // model parameters:
        Real m = avgProb * bsktSize;
        Real floorAveProb = std::min(Real(bsktSize-1), std::floor(Real(m)));
        Real ceilAveProb = floorAveProb + 1.;
        // nu_A
        Real varianceBinom = avgProb * (1. - avgProb)/bsktSize;
        // nu_E
        std::vector<Probability> oneMinusDefProb;//: 1.-condDefProb[j]
        std::transform(condDefProb.begin(), condDefProb.end(),
            std::back_inserter(oneMinusDefProb),
            std::bind1st(std::minus<Real>(), 1.));

        //breaks condDefProb and lgdsLeft to spare memory
        std::transform(condDefProb.begin(), condDefProb.end(),
            oneMinusDefProb.begin(), condDefProb.begin(),
            std::multiplies<Real>());
        std::transform(lgdsLeft.begin(), lgdsLeft.end(),
            lgdsLeft.begin(), lgdsLeft.begin(), std::multiplies<Real>());
        Real variance = std::inner_product(condDefProb.begin(),
            condDefProb.end(), lgdsLeft.begin(), 0.);

        variance = avgLgd <= QL_EPSILON ? 0. :
            variance / (bsktSize * bsktSize * avgLgd * avgLgd );
        Real sumAves = -std::pow(ceilAveProb-m, 2)
            - (std::pow(floorAveProb-m, 2) - std::pow(ceilAveProb,2.))
                * (ceilAveProb-m);
        Real alpha = (variance * bsktSize + sumAves)
            / (varianceBinom * bsktSize + sumAves);
        // Full distribution:
        // ....DO SOMETHING CHEAPER at least go up to the loss tranche limit.
        std::vector<Probability> lossProbDensity(bsktSize+1, 0.);
        if(avgProb >= 1.-QL_EPSILON) {
           lossProbDensity[bsktSize] = 1.;
        }else if(avgProb <= QL_EPSILON) {
           lossProbDensity[0] = 1.;
        }else{
            /* FIX ME: With high default probabilities one only gets tiny values
            at the end and the sum of probabilities in the
            conditional distribution does not add up to one. It might be due to
            the fact that recursion should be done in the other direction as
            pointed out in the book. This is numerical.
            */
            Probability probsRatio = avgProb/(1.-avgProb);
            lossProbDensity[0] = std::pow(1.-avgProb,
                static_cast<Real>(bsktSize));
            for(Size i=1; i<bsktSize+1; i++) // recursive to avoid factorial
                lossProbDensity[i] = lossProbDensity[i-1] * probsRatio
                    * (bsktSize-i+1.)/i;
            // redistribute probability:
            for(Size i=0; i<bsktSize+1; i++)
                lossProbDensity[i] *= alpha;
            // adjust average
            Real epsilon = (1.-alpha)*(ceilAveProb-m);
            Real epsilonPlus = 1.-alpha-epsilon;
            lossProbDensity[static_cast<Size>(floorAveProb)] += epsilon;
            lossProbDensity[static_cast<Size>(ceilAveProb)]  += epsilonPlus;
        }
        return lossProbDensity;
    }

    //-------------------------------------------------------------------------

    template< class LLM>
    Real BinomialLossModel<LLM>::averageLoss(
        const Date& d,
        const std::vector<Real>& reminingNots,
        const std::vector<Real>& mktFctrs) const
    {
        Size bsktSize = basket_->remainingSize();
        /* The conditional loss per unit notional of each name at time 'date'
            The spot recovery model is returning for all i's:
            \frac{\int_0^t  [1-rr_i(\tau; \xi)] P_{def-i}(0, \tau; \xi) d\tau}
                 {P_{def-i}(0,t;\xi)}
            and the constant recovery model is simply returning:
            1-RR_i
        */
        std::vector<Real> fractionalEL = expConditionalLgd(d, mktFctrs);
        Real notBskt = std::accumulate(reminingNots.begin(),
            reminingNots.end(), Real(0.));
        std::vector<Real> lgdsLeft;
        std::transform(fractionalEL.begin(), fractionalEL.end(),
            reminingNots.begin(), std::back_inserter(lgdsLeft),
            boost::lambda::_1 * boost::lambda::_2 / notBskt);
        return std::accumulate(lgdsLeft.begin(), lgdsLeft.end(), Real(0.))
            / bsktSize;
    }

    template< class LLM>
    Disposable<std::vector<Real> >
        BinomialLossModel<LLM>::lossPoints(const Date& d) const
    {
        std::vector<Real> notionals = basket_->remainingNotionals(d);

        Real aveLossFrct = copula_->integratedExpectedValue(
            boost::function<Real (const std::vector<Real>& v1)>(
                boost::bind(
                    &BinomialLossModel<LLM>::averageLoss,
                    this,
                    boost::cref(d),
                    boost::cref(notionals),
                    _1)
                )
            );

        std::vector<Real> data;
        Size dataSize = basket_->remainingSize() + 1;
        data.reserve(dataSize);
        // use std::algorithm
        Real outsNot = basket_->remainingNotional(d);
        for(Size i=0; i<dataSize; i++)
            data.push_back(i * aveLossFrct * outsNot);
        return data;
    }

    template< class LLM>
    Real BinomialLossModel<LLM>::condTrancheLoss(
        const Date& d,
        const std::vector<Real>& lossVals,
        const std::vector<Real>& bsktNots,
        const std::vector<Real>& uncondDefProbsInv,
        const std::vector<Real>& mkf) const {

        std::vector<Real> condLProb =
            lossProbability(d, bsktNots, uncondDefProbsInv, mkf);
        // \to do: move to a do-while over attach to detach
        Real suma = 0.;
        for(Size i=0; i<lossVals.size(); i++) {
            suma += condLProb[i] *
                std::min(std::max(lossVals[i]
                 - attachAmount_, 0.), detachAmount_ - attachAmount_);
        }
        return suma;
    }

    template< class LLM>
    Real BinomialLossModel<LLM>::expectedTrancheLoss(const Date& d) const {
        std::vector<Real> lossVals  = lossPoints(d);
        std::vector<Real> notionals = basket_->remainingNotionals(d);
        std::vector<Probability> invProbs =
            basket_->remainingProbabilities(d);
        for(Size iName=0; iName<invProbs.size(); iName++)
            invProbs[iName] =
                copula_->inverseCumulativeY(invProbs[iName], iName);

        return copula_->integratedExpectedValue(
            boost::function<Real (const std::vector<Real>& v1)>(
                boost::bind(&BinomialLossModel<LLM>::condTrancheLoss,
                            this,
                            boost::cref(d),
                            boost::cref(lossVals),
                            boost::cref(notionals),
                            boost::cref(invProbs),
                            _1))
            );
    }

    template< class LLM>
    Disposable<std::map<Real, Probability> >
        BinomialLossModel<LLM>::lossDistribution(const Date& d) const
    {
        std::map<Real, Probability> distrib;
        std::vector<Real> lossPts = lossPoints(d);
        std::vector<Real> values  = expectedDistribution(d);
        Real sum = 0.;
        for(Size i=0; i<lossPts.size(); i++) {
            distrib.insert(std::make_pair(lossPts[i],
                //capped, some situations giving a very small probability over 1
                std::min(sum+values[i],1.)
                ));
            sum+= values[i];
        }
        return distrib;
    }

    template< class LLM>
    Real BinomialLossModel<LLM>::percentile(const Date& d, Real perc) const {
        std::map<Real, Probability> dist = lossDistribution(d);
        // \todo: Use some of the library interpolators instead
        if(// included in test below-> (dist.begin()->second >=1.) ||
            (dist.begin()->second >= perc))return dist.begin()->first;

        // deterministic case (e.g. date requested is todays date)
        if(dist.size() == 1) return dist.begin()->first;

        if(perc == 1.) return dist.rbegin()->first;
        if(perc == 0.) return dist.begin()->first;
        std::map<Real, Probability>::const_iterator itdist = dist.begin();
        while(itdist->second <= perc) itdist++;
        Real valPlus = itdist->second;
        Real xPlus   = itdist->first;
        itdist--;//we r never 1st or last, because of tests above
        Real valMin  = itdist->second;
        Real xMin    = itdist->first;

        Real portfLoss = xPlus-(xPlus-xMin)*(valPlus-perc)/(valPlus-valMin);

        return
            std::min(std::max(portfLoss - attachAmount_, 0.),
                detachAmount_ - attachAmount_);
    }

    template< class LLM>
    Real BinomialLossModel<LLM>::expectedShortfall(const Date&d,
        Real perctl) const
    {
        //taken from recursive since we have the distribution in both cases.
        if(d == Settings::instance().evaluationDate()) return 0.;
            std::map<Real, Probability> distrib = lossDistribution(d);

            std::map<Real, Probability>::iterator
                itNxt, itDist = distrib.begin();
            for(; itDist != distrib.end(); itDist++)
                if(itDist->second >= perctl) break;
            itNxt = itDist; itDist--;

            // \todo: I could linearly triangulate the exact point and get
            //    extra precission on the first(broken) period.
            if(itNxt != distrib.end()) {
                Real lossNxt = std::min(std::max(itNxt->first - attachAmount_,
                    0.), detachAmount_ - attachAmount_);
                Real lossHere = std::min(std::max(itDist->first - attachAmount_,
                    0.), detachAmount_ - attachAmount_);

                Real val =  lossNxt - (itNxt->second - perctl) *
                    (lossNxt - lossHere) / (itNxt->second - itDist->second);
                Real suma = (itNxt->second - perctl) * (lossNxt + val) * .5;
                itDist++;itNxt++;
                do{
                    lossNxt = std::min(std::max(itNxt->first - attachAmount_,
                        0.), detachAmount_ - attachAmount_);
                    lossHere = std::min(std::max(itDist->first - attachAmount_,
                        0.), detachAmount_ - attachAmount_);
                    suma += .5 * (lossHere + lossNxt)
                        * (itNxt->second - itDist->second);
                    itDist++;itNxt++;
                }while(itNxt != distrib.end());
                return suma / (1.-perctl);
            }
            QL_FAIL("Binomial model fails to calculate ESF.");
    }

    // The standard use:
    typedef BinomialLossModel<GaussianConstantLossLM> GaussianBinomialLossModel;
    typedef BinomialLossModel<TConstantLossLM> TBinomialLossModel;

}

#endif


#pragma once
#include <numbers>
#include <cmath>
#include <Eigen/Dense>
#include "Material.hpp"
#include "../../utils/random.hpp"

class MaterialDiffuse : public Material
{
public:
	MaterialDiffuse(Eigen::Vector3f k_d) : Material(MaterialType::Diffuse), k_d(k_d) {};

    Eigen::Vector3f calc_brdf(Eigen::Vector3f normal, Eigen::Vector3f in, Eigen::Vector3f out) const override
    {
        // calculate the contribution of diffuse model
        auto cosalpha = normal.dot(out);
        if (cosalpha <= 0.0f)
            return { 0.0f, 0.0f, 0.0f };

        auto diffuse = k_d / std::numbers::pi;
        return diffuse;
    };

    Eigen::Vector3f sample(Eigen::Vector3f normal, Eigen::Vector3f in) const override
    {
        // uniform sample on the hemisphere
        auto x_1 = get_random_float();
        auto x_2 = get_random_float();
        auto z = std::abs(1.0f - 2.0f * x_1);
        auto r = std::sqrt(1.0f - z * z);
        auto phi = static_cast<float>(2 * std::numbers::pi_v<float> * x_2);
        Eigen::Vector3f local{ r * std::cos(phi), r * std::sin(phi), z };
        return to_world(local, normal);
    };

    Eigen::Vector3f at(double u, double v) const override { return k_d; };

protected:
    Eigen::Vector3f k_d;

    Eigen::Vector3f to_world(const Eigen::Vector3f& a, const Eigen::Vector3f& N) const {
        Eigen::Vector3f C;
        if (std::abs(N.x()) > std::abs(N.y()))
        {
            auto lenInv = 1.0f / std::sqrt(N.x() * N.x() + N.z() * N.z());
            C = Eigen::Vector3f(N.z() * lenInv, 0.0f, -N.x() * lenInv);
        }
        else
        {
            auto lenInv = 1.0f / std::sqrt(N.y() * N.y() + N.z() * N.z());
            C = Eigen::Vector3f(0.0f, N.z() * lenInv, -N.y() * lenInv);
        }
        auto B = C.cross(N);
        return a.x() * B + a.y() * C + a.z() * N;
    }
};


//=============================================================================
//
//  CLASS NConstraintInterfaceAD
//
//=============================================================================

#ifndef COMISO_NCONSTRAINTINTERFACEAD_HH
#define COMISO_NCONSTRAINTINTERFACEAD_HH

//== COMPILE-TIME PACKAGE REQUIREMENTS ========================================
#include <CoMISo/Config/config.hh>
#if COMISO_ADOLC_AVAILABLE
#if COMISO_EIGEN3_AVAILABLE

//== INCLUDES =================================================================

#include <CoMISo/Config/CoMISoDefines.hh>
#include "SuperSparseMatrixT.hh"

#include <boost/shared_array.hpp>

#include <adolc/adolc.h>
#include <adolc/adouble.h>
#include <adolc/drivers/drivers.h>
#include <adolc/sparse/sparsedrivers.h>
#include <adolc/taping.h>

#include "NConstraintInterface.hh"
#include "NProblemInterfaceAD.hpp"

#include "TapeIDSingleton.hh"

#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET
#include <Eigen/Sparse>

//== FORWARDDECLARATIONS ======================================================

//== NAMESPACES ===============================================================

namespace COMISO {

//== CLASS DEFINITION =========================================================

/** \class NProblemInterfaceAD NProblemInterfaceAD.hpp

    The problem interface using automatic differentiation.
 */
class COMISODLLEXPORT NConstraintInterfaceAD : public NConstraintInterface {
public:

    // Define Sparse Datatypes
    typedef NConstraintInterface::SVectorNC SVectorNC;
    typedef NConstraintInterface::SMatrixNC SMatrixNC;

    typedef NConstraintInterface::ConstraintType ConstraintType;

    /// Default constructor
    NConstraintInterfaceAD(NProblemInterfaceAD& _problem, int _n_unknowns,
                           const ConstraintType _type = NC_EQUAL, double _eps = 1e-6) :
        NConstraintInterface(_type, _eps),
        problem_(_problem),
        n_unknowns_(_n_unknowns),
        type_(_type),
        function_evaluated_(false),
        use_tape_(true),
        constant_hessian_evaluated_(false),
        tape_(static_cast<short int>(TapeIDSingleton::Instance()->requestId())) {

        for(size_t i = 0; i < 11; ++i) tape_stats_[i] = 0;
    }

    /// Destructor
    virtual ~NConstraintInterfaceAD() {
        TapeIDSingleton::Instance()->releaseId(static_cast<size_t>(tape_));
    }

    /**
     * \brief Only override this function
     */
    virtual adouble evaluate(const adouble* _x) = 0;

public:

    virtual int n_unknowns() {
        return n_unknowns_;
    }

    virtual double eval_constraint(const double* _x) {

        double y = 0.0;

        if(!function_evaluated_ || !use_tape_) {

            adouble y_d = 0.0;

            boost::shared_array<adouble> x_d_ptr = problem_.x_d_ptr();

            trace_on(tape_); // Start taping

            // Fill data vector
            for(int i = 0; i < n_unknowns_; ++i) {
                x_d_ptr[i] <<= _x[i];
            }

            // Call virtual function to compute
            // functional value
            y_d = evaluate(x_d_ptr.get());

            y_d >>= y;

            trace_off();

#ifdef ADOLC_STATS
            tapestats(tape_, tape_stats_);
            std::cout << "Status values for tape " << tape_ << std::endl;
            std::cout << "===============================================" << std::endl;
            std::cout << "Number of independent variables:\t" << tape_stats_[0] << std::endl;
            std::cout << "Number of dependent variables:\t\t" << tape_stats_[1] << std::endl;
            std::cout << "Max. number of live active variables:\t" << tape_stats_[2] << std::endl;
            std::cout << "Size of value stack:\t\t\t" << tape_stats_[3] << std::endl;
            std::cout << "Buffer size:\t\t\t\t" << tape_stats_[4] << std::endl;
            std::cout << "Total number of operations recorded:\t" << tape_stats_[5] << std::endl;
            std::cout << "Other stats [6]:\t\t\t" << tape_stats_[6] << std::endl;
            std::cout << "Other stats [7]:\t\t\t" << tape_stats_[7] << std::endl;
            std::cout << "Other stats [8]:\t\t\t" << tape_stats_[8] << std::endl;
            std::cout << "Other stats [9]:\t\t\t" << tape_stats_[9] << std::endl;
            std::cout << "Other stats [10]:\t\t\t" << tape_stats_[10] << std::endl;
            std::cout << "===============================================" << std::endl;
#endif

            function_evaluated_ = true;

        } else {

            double ay[1] = {0.0};

            int ec = function(tape_, 1, n_unknowns_, const_cast<double*>(_x), ay);

#ifdef ADOLC_RET_CODES
            std::cout << "Info: function() returned code " << ec << std::endl;
#endif

            y = ay[0];
        }

        return y;
    }

    virtual void eval_gradient(const double* _x, SVectorNC& _g) {

        if(!function_evaluated_ || !use_tape_) {
            // Evaluate original functional
            eval_constraint(_x);
        }

        boost::shared_array<double> grad_p = problem_.grad_ptr();

        _g.resize(n_unknowns_);
        _g.setZero();

        int ec = gradient(tape_, n_unknowns_, _x, grad_p.get());

        if(ec < 0) {
            // Retape function if return code indicates discontinuity
            function_evaluated_ = false;
#ifdef ADOLC_RET_CODES
            std::cout << __FUNCTION__ << " invokes retaping of function due to discontinuity! Return code: " << ec << std::endl;
#endif
            eval_constraint(_x);
            ec = gradient(tape_, n_unknowns_, _x, grad_p.get());
        }

#ifdef ADOLC_RET_CODES
        std::cout << "Info: gradient() returned code " << ec << std::endl;
#endif

        for(int i = 0; i < n_unknowns_; ++i) {
            _g.coeffRef(i) += grad_p[i];
        }
    }

    virtual void eval_hessian(const double* _x, SMatrixNC& _H) {

        _H.resize(n_unknowns_, n_unknowns_);

        if(constant_hessian() && constant_hessian_evaluated_) {
            _H = constant_hessian_;
            return;
        }

        if(!function_evaluated_ || !use_tape_) {
            // Evaluate original functional
            eval_constraint(_x);
        }

        if(sparse_hessian()) {

            int nz = 0;
            int opt[2] = {0, 0};

            unsigned int* r_ind = NULL;
            unsigned int* c_ind = NULL;
            double* val = NULL;

            int ec = sparse_hess(tape_, n_unknowns_, 0, _x, &nz, &r_ind, &c_ind, &val, opt);
            if(ec < 0) {
                // Retape function if return code indicates discontinuity
                function_evaluated_ = false;
#ifdef ADOLC_RET_CODES
                std::cout << __FUNCTION__ << " invokes retaping of function due to discontinuity! Return code: " << ec << std::endl;
#endif
                eval_constraint(_x);
                ec = sparse_hess(tape_, n_unknowns_, 0, _x, &nz, &r_ind, &c_ind, &val, opt);
            }

            assert(nz >= 0);
            assert(r_ind != NULL);
            assert(c_ind != NULL);
            assert(val != NULL);

#ifdef ADOLC_RET_CODES
            std::cout << "Info: sparse_hessian() returned code " << ec << std::endl;
#endif

            for(int i = 0; i < nz; ++i) {

                _H(r_ind[i], c_ind[i]) += val[i];
            }

            if(constant_hessian()) {
                constant_hessian_ = _H;
                constant_hessian_evaluated_ = true;
            }

            delete[] r_ind;
            delete[] c_ind;
            delete[] val;

        } else {

            double** h_ptr = problem_.dense_hessian_ptr();

            int ec = hessian(tape_, n_unknowns_, const_cast<double*>(_x), h_ptr);

            if(ec < 0) {
                // Retape function if return code indicates discontinuity
                function_evaluated_ = false;
#ifdef ADOLC_RET_CODES
                std::cout << __FUNCTION__ << " invokes retaping of function due to discontinuity! Return code: " << ec << std::endl;
#endif
                eval_constraint(_x);
                ec = hessian(tape_, n_unknowns_, const_cast<double*>(_x), h_ptr);
            }

#ifdef ADOLC_RET_CODES
            std::cout << "Info: hessian() returned code " << ec << std::endl;
#endif

            for(int i = 0; i < n_unknowns_; ++i) {
                for(int j = 0; j <= i; ++j) {

                    _H(i, j) += h_ptr[i][j];

                    if(i != j) {
                        _H(j, i) += h_ptr[i][j];
                    }
                }
            }

            if(constant_hessian()) {
                constant_hessian_ = _H;
                constant_hessian_evaluated_ = true;
            }
        }
    }

    /** \brief Use tape
     * Set this to false if the energy functional
     * is discontinuous (so that the operator tree
     * has to be re-established at each evaluation)
     */
    bool use_tape() const {
        return use_tape_;
    }

    void use_tape(bool _b) {
        use_tape_ = _b;
    }

    /**
     * \brief Indicate whether the hessian is sparse.
     * If so, the computations (as well as the memory
     * consumption) can be performed more efficiently.
     */
    virtual bool sparse_hessian() {
        return false;
    }

private:

    // Reference to associated objective function
    NProblemInterfaceAD& problem_;

    // Number of unknowns
    int n_unknowns_;

    // Constraint type
    ConstraintType type_;

    size_t tape_stats_[11];

    bool function_evaluated_;
    bool use_tape_;

    SMatrixNC constant_hessian_;
    bool constant_hessian_evaluated_;

    const short int tape_;
};

//=============================================================================
}// namespace COMISO
//=============================================================================
#endif // COMISO_ADOLC_AVAILABLE
//=============================================================================
#endif // COMISO_EIGEN3_AVAILABLE
//=============================================================================
#endif // ACG_NCONSTRAINTINTERFACEAD_HH defined
//=============================================================================


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

// #define MTL_VERBOSE_TEST

#include <iostream>

#include <boost/numeric/mtl/mtl.hpp>
#include <boost/numeric/itl/itl.hpp>

const int size = 3, N = size * size;

int max_iter= 0;

template <typename Matrix, typename Solver>
void test1(const char* solver_name, const char* matrix_name)
{
    mtl::io::tout << solver_name << " on " << matrix_name << std::endl;;

    Matrix                             A;
    laplacian_setup(A, size, size);

    mtl::dense_vector<double>          x(N, 1.0), b(N);
    b = A * x;
    x= 0;

    Solver s(A);
    s.iteration_ref().set_max_iterations(N+1);

#ifdef MTL_VERBOSE_TEST
    s.iteration_ref().set_quite(false);
    s.iteration_ref().set_cycle(1);
#else
    s.iteration_ref().suppress_resume(true);
#endif

    s(x, b);
    int i= s.iteration().iterations();
    if (i > max_iter)
	max_iter= i;
    // MTL_THROW_IF(size == 3 && i > 9, mtl::runtime_error("Too many iterations in solver"));
    if (size == 3 && i > 9)
	std::cout << "Solver \"" << solver_name << "\" converges slowly!\n";
}

// same without iter
template <typename Matrix, typename Solver>
void test1a(const char* solver_name, const char* matrix_name)
{
    mtl::io::tout << solver_name << " on " << matrix_name << std::endl;;

    Matrix                             A;
    laplacian_setup(A, size, size);

    mtl::dense_vector<double>          x(N, 1.0), b(N);
    b = A * x;
    x= 0;

    Solver s(A);
    s.step(x, b);
}

template <typename Matrix>
int test2(const char* matrix_name)
{
    test1<Matrix, itl::cg_solver<Matrix> >("CG", matrix_name);
    test1<Matrix, itl::cg_solver<Matrix, itl::pc::ilu_0<Matrix> > >("CG with ILU_0", matrix_name);
    test1<Matrix, itl::cgs_solver<Matrix> >("CGS", matrix_name);
    test1<Matrix, itl::cgs_solver<Matrix, itl::pc::ilu_0<Matrix> > >("CGS with ILU_0", matrix_name);

    test1<Matrix, itl::bicg_solver<Matrix> >("BiCG", matrix_name);
    test1<Matrix, itl::bicg_solver<Matrix, itl::pc::ilu_0<Matrix> > >("BiCG with ILU_0", matrix_name);
    test1<Matrix, itl::bicgstab_solver<Matrix> >("BiCGStab", matrix_name);
    test1<Matrix, itl::bicgstab_solver<Matrix, itl::pc::ilu_0<Matrix> > >("BiCGStab with ILU_0", matrix_name);
    test1<Matrix, itl::bicgstab_2_solver<Matrix> >("BiCGStab(2)", matrix_name);
    test1<Matrix, itl::bicgstab_2_solver<Matrix, itl::pc::ilu_0<Matrix> > >("BiCGStab(2) with ILU_0", matrix_name);
    test1<Matrix, itl::bicgstab_ell_solver<Matrix> >("BiCGStab(ell)", matrix_name);
    test1<Matrix, itl::bicgstab_ell_solver<Matrix, itl::pc::ilu_0<Matrix> > >("BiCGStab(ell) with ILU_0", matrix_name);
    test1<Matrix, itl::gmres_solver<Matrix> >("GMRES", matrix_name);
    test1<Matrix, itl::gmres_solver<Matrix, itl::pc::ilu_0<Matrix> > >("GMRES with ILU_0", matrix_name);
    test1<Matrix, itl::gmres_solver<Matrix, itl::pc::ilu_0<Matrix>, itl::pc::ilu_0<Matrix> > >("GMRES with ILU_0 from left and right", matrix_name);

    test1<Matrix, itl::qmr_solver<Matrix> >("QMR", matrix_name);
    test1<Matrix, itl::qmr_solver<Matrix, itl::pc::ilu_0<Matrix> > >("QMR with ILU_0", matrix_name);
    test1<Matrix, itl::tfqmr_solver<Matrix> >("TFQMR", matrix_name);
    test1<Matrix, itl::tfqmr_solver<Matrix, itl::pc::ilu_0<Matrix> > >("TFQMR with ILU_0", matrix_name);

    test1<Matrix, itl::idr_s_solver<Matrix> >("IDR(s)", matrix_name);
    test1<Matrix, itl::idr_s_solver<Matrix, itl::pc::ilu_0<Matrix> > >("IDR(s) with ILU_0", matrix_name);

    typedef itl::cg_solver<Matrix>                           cg1_type;
    typedef itl::cg_solver<Matrix, itl::pc::ilu_0<Matrix> >  cg2_type;

    test1a<Matrix, itl::repeating_solver<cg1_type, 3, true> >("3 iterations CG", matrix_name);
    test1a<Matrix, itl::repeating_solver<cg2_type, 3, true> >("3 iterations CG with ILU_0", matrix_name);

    return 0;
}

int main()
{
    test2<mtl::compressed2D<double> >("Compressed2D");
    //test<mtl::sparse_banded<double> >();
    // std::cout << "Largest iteration number is " << max_iter << '\n';

    return 0;
}


//==================================================================================================
/*!
  @file

  @copyright 2015 NumScale SAS
  @copyright 2015 J.T.Lapreste

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_DETAIL_GENERIC_D_EXPO_REDUCTION_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_DETAIL_GENERIC_D_EXPO_REDUCTION_HPP_INCLUDED

#include <boost/simd/function/horn.hpp>
#include <boost/simd/function/horn1.hpp>
#include <boost/simd/arch/common/detail/tags.hpp>
#include <boost/simd/constant/invlog10_2.hpp>
#include <boost/simd/constant/invlog_2.hpp>
#include <boost/simd/constant/log10_2hi.hpp>
#include <boost/simd/constant/log10_2lo.hpp>
#include <boost/simd/constant/log_10.hpp>
#include <boost/simd/constant/log_2.hpp>
#include <boost/simd/constant/log_2hi.hpp>
#include <boost/simd/constant/log_2lo.hpp>
#include <boost/simd/constant/maxlog.hpp>
#include <boost/simd/constant/maxlog10.hpp>
#include <boost/simd/constant/maxlog2.hpp>
#include <boost/simd/constant/minlog.hpp>
#include <boost/simd/constant/minlog10.hpp>
#include <boost/simd/constant/minlog2.hpp>
#include <boost/simd/constant/two.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/function/simd/is_greater_equal.hpp>
#include <boost/simd/function/simd/is_less_equal.hpp>
#include <boost/simd/function/simd/fma.hpp>
#include <boost/simd/function/simd/fnms.hpp>
#include <boost/simd/function/simd/inc.hpp>
#include <boost/simd/function/simd/oneplus.hpp>
#include <boost/simd/function/simd/oneminus.hpp>
#include <boost/simd/function/simd/round2even.hpp>
#include <boost/simd/function/simd/sqr.hpp>
#include <boost/simd/logical.hpp>
#include <boost/dispatch/meta/scalar_of.hpp>

namespace boost { namespace simd
{
  namespace detail
  {
    namespace bd =  boost::dispatch;
    namespace bs =  boost::simd;

    template < class A0> struct exp_reduction < A0, bs::tag::exp_, double>
    {
      static BOOST_FORCEINLINE auto isgemaxlog(A0 const& a0) BOOST_NOEXCEPT
        -> decltype(is_greater_equal(a0, Maxlog<A0>()))
      {
        return is_greater_equal(a0, Maxlog<A0>());
      }

      static BOOST_FORCEINLINE auto isleminlog(A0 const& a0) BOOST_NOEXCEPT
        -> decltype(is_less_equal(a0, Minlog<A0>()))
      {
        return is_less_equal(a0, Minlog<A0>());
      }

      static BOOST_FORCEINLINE A0 reduce( A0 const& a0
                                        , A0& hi, A0& lo, A0& x) BOOST_NOEXCEPT
      {
        A0 k = round2even(Invlog_2<A0>()*a0);
        hi = fnms(k, Log_2hi<A0>(), a0); //a0-k*L
        lo = k*Log_2lo<A0>();
        x  = hi-lo;
        return k;
      }

      static BOOST_FORCEINLINE A0 approx(A0 x) BOOST_NOEXCEPT
      {
        A0 const t = sqr(x);
        return fnms(t,
                    horn<A0
                         , 0x3fc555555555553eull
                         , 0xbf66c16c16bebd93ull
                         , 0x3f11566aaf25de2cull
                         , 0xbebbbd41c5d26bf1ull
                         , 0x3e66376972bea4d0ull
                    >(t), x); //x-h*t
    }

      static BOOST_FORCEINLINE A0 finalize(A0 x, A0 c, A0 hi, A0 lo) BOOST_NOEXCEPT
      {
        return One<A0>()-(((lo-(x*c)/(Two<A0>()-c))-hi));
      }

    };

    template < class A0 > struct exp_reduction < A0, bs::tag::exp2_, double>
    {
      static BOOST_FORCEINLINE auto isgemaxlog(A0 const& a0) BOOST_NOEXCEPT
        -> decltype(is_greater_equal(a0, Maxlog2<A0>()))
      {
        return is_greater_equal(a0, Maxlog2<A0>());
      }

      static BOOST_FORCEINLINE auto isleminlog(A0 const& a0) BOOST_NOEXCEPT
        -> decltype(is_less_equal(a0, Minlog2<A0>()))
      {
        return is_less_equal(a0, Minlog2<A0>());
      }

      static BOOST_FORCEINLINE A0 reduce(A0 const& a0, A0, A0, A0& x) BOOST_NOEXCEPT
      {
        A0 k = round2even(a0);
        x = (a0 - k)*Log_2<A0>();
        return k;
      }

      static BOOST_FORCEINLINE A0 approx(A0 x) BOOST_NOEXCEPT
      {
        const A0 t =  sqr(x);
        return fnms(t,
                    horn<A0
                         , 0x3fc555555555553eull
                         , 0xbf66c16c16bebd93ull
                         , 0x3f11566aaf25de2cull
                         , 0xbebbbd41c5d26bf1ull
                         , 0x3e66376972bea4d0ull
                    > (t), x); //x-h*t
      }

      static BOOST_FORCEINLINE A0 finalize( A0 x, A0 c, A0, A0& ) BOOST_NOEXCEPT
      {
        return oneminus(((-(x*c)/(Two<A0>()-c))-x));
      }
    };

    template < class A0 > struct exp_reduction < A0, bs::tag::exp10_, double>
    {
      static BOOST_FORCEINLINE auto isgemaxlog(A0 const& a0) BOOST_NOEXCEPT
        -> decltype(is_greater_equal(a0, Maxlog10<A0>()))
      {
        return is_greater_equal(a0, Maxlog10<A0>());
      }

      static BOOST_FORCEINLINE auto isleminlog(A0 const& a0) BOOST_NOEXCEPT
        -> decltype(is_less_equal(a0, Minlog10<A0>()))
      {
        return is_less_equal(a0, Minlog10<A0>());
      }

      static BOOST_FORCEINLINE A0 reduce(A0 const& a0, A0&, A0&, A0& x) BOOST_NOEXCEPT
      {
        A0 k  = round2even(Invlog10_2<A0>()*a0);
        x = fnms(k, Log10_2hi<A0>(), a0);
        x = fnms(k, Log10_2lo<A0>(), x);
        return k;
      }

      static BOOST_FORCEINLINE A0 approx(A0 x) BOOST_NOEXCEPT
      {
        A0 xx = sqr(x);
        A0 px = x*horn<A0,
                       0x40a2b4798e134a01ull,
                       0x40796b7a050349e4ull,
                       0x40277d9474c55934ull,
                       0x3fa4fd75f3062dd4ull
                       > (xx);
        A0 x2 =  px/(horn1<A0,
                          0x40a03f37650df6e2ull,
                          0x4093e05eefd67782ull,
                          0x405545fdce51ca08ull
                     //   0x3ff0000000000000ull
                          > (xx)-px);
        return oneplus(x2+x2);
      }

      static BOOST_FORCEINLINE A0 finalize(A0, A0 c, A0,  A0 ) BOOST_NOEXCEPT
      {
        return c;
      }
    };
  }
} }
#endif


/*
BSD 3-Clause License
Copyright (c) 2020, Juan S. Campos
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*
This code calculates the partial and augmented relaxations as described in
"Partial Lasserre relaxation for sparse Max-Cut" by Campos, J.S., Misener R., and Parpas, P. 2020.
*/

#include <iostream>
#include "fusion.h"
#include <fstream>
#include <string>
#include <sstream>
#include <stdio.h>
#include <typeinfo>
#include <chrono>
#include <math.h>
#include <algorithm>
#include <vector>
#include <iomanip>
#include <stdlib.h>
#include <time.h>
#include <boost/functional/hash.hpp>
#include <unordered_map>
#define BILLION 1e9

using namespace std;
using namespace mosek::fusion;
using namespace monty;

struct timespec my_start;

void read_matrix(const char *, Matrix::t&);
void read_matrix_sparse_format(const char *, Matrix::t&);

template <typename Container>
struct container_hash
{
	size_t operator()(Container const& c) const {
		return boost::hash_range(c.begin(), c.end());
	}
};

int main(int argc, char ** argv)
{
 	 // Order of arguments: max_size_cliques (r), no_subsets to add (p), type of heuristic (H1 to H5), name  of the instance
	char aux_string[300];
	// Read weight matrix W
	// First row must contain the number of nodes and edges
	Matrix::t W;
	sprintf(aux_string,"graphs/%s.txt",argv[4]);
	read_matrix_sparse_format(aux_string, W);

	int n = W->numColumns();
	int max_size_clique = atoi(argv[1]);// 0,2,3 ...
	int no_subsets = atoi(argv[2]);// 0,1,2,3 ...
	char heuristic [300];
	sprintf(heuristic,"%s",argv[3]);// H1, H2, ..., H5

	int total_cliques;
	double time_creating_mosek = 0;
	double time_solving_SDP = 0;
	double time_creating_cliques = 0;
	double time_generating_LB = 0;
	double time_total = 0;

	auto start = std::chrono::high_resolution_clock::now();
	auto start_creating = std::chrono::high_resolution_clock::now();

    // Read maximal cliques and subset generated from matlab
    Matrix::t cliques;
    Matrix::t first_second_order;

   	sprintf(aux_string,"clique_%s_%d_%d_%s.txt",argv[4], max_size_clique, no_subsets,heuristic);
	read_matrix(aux_string, cliques);
    sprintf(aux_string,"clique_aux_%s_%d_%d_%s.txt",argv[4], max_size_clique, no_subsets,heuristic);
	read_matrix(aux_string, first_second_order);
	auto finish = std::chrono::high_resolution_clock::now();
	std::chrono::duration<double> elapsed = finish - start;
	cout<<"time reading W, cliques, and first_second_order: "<<elapsed.count()<<endl;

	start = std::chrono::high_resolution_clock::now();
	total_cliques = (cliques->numRows());

	vector <int> size_cliques(total_cliques);
	vector <vector<int>> idx_cliques(total_cliques);
	vector <int> size_blk(total_cliques);
	int total_var_all_blocks = 0;
	for (int i = 0; i < total_cliques; i++)
    {
		int aux_count = 0;

	    for(int j=0; j < n; j++)
	    {
			if(cliques->get(i,j)>0)
			{
				aux_count = aux_count + 1;
				idx_cliques[i].push_back(j);
			}
		}
		size_cliques[i] = aux_count;
		if((first_second_order->get(i,0) == 1) | (aux_count == 1))
		{
			size_blk[i] = aux_count + 1;
		}
		else
		{
			size_blk[i] = (aux_count + 2)*(aux_count + 1)/2 - aux_count;
		}
		total_var_all_blocks = total_var_all_blocks + (size_blk[i]*size_blk[i]);
    }
    finish = std::chrono::high_resolution_clock::now();
	elapsed = finish - start;
	cout<<"time creating size_cliques and idx_cliques: "<<elapsed.count()<<endl;

    // Generate unordered_map containing the monomials needed for the cliques
    start = std::chrono::high_resolution_clock::now();
    vector <int> alpha (4,0);
    unordered_map<vector<int>, int, container_hash<vector<int>>> map;

    for(int i=0; i< n; i++)
    {
		alpha[0] = i;
		map.insert({alpha,i});
	}
	int value_map = n;

    for (int i = 0; i < total_cliques; i++)
    {
		int degree = 2;
		if(first_second_order->get(i,0) == 2)
		{
			degree = 4;
		}

		for(int j=2; j<=degree; j++)
		{
			if(j <= size_cliques[i])
			{
				vector <int> perm(size_cliques[i], 0);
                fill_n(perm.begin(), j, 1);
                int count_aux = 0;
                do
                {
                    for (int k = 0; k < size_cliques[i]; k++)
                    {
                        if (perm[k] == 1)
                        {
                            alpha[count_aux] = idx_cliques[i][k];
                            count_aux = count_aux + 1;
                        }
                    }
                    count_aux = 0;
                    if(map.find(alpha) == map.end())
                    {
                        map.insert({alpha,value_map});
                        value_map = value_map + 1;
                    }
                    fill(alpha.begin(), alpha.end(),0);
                } while (prev_permutation(perm.begin(), perm.end()));
			}
		}
	}

	int total_y = map.size();
	finish = std::chrono::high_resolution_clock::now();
	elapsed = finish - start;
	cout<<"time creating unordered_map: "<<elapsed.count()<<endl;

    // Generating SDP problem: max b*y, st. Aty - C = S, S>=0

    // Creating mosek model
    Model::t M = new Model("sdo1");
    // Creating SDP blocks and lifted monomials variable and calculating norm_C
    double norm_C = 0;
    Variable::t S[total_cliques];
    for(int i=0; i<total_cliques; i++)
    {
		sprintf(aux_string,"S_%d",i);
	    S[i] = M->variable(aux_string, Domain::inPSDCone(size_blk[i]));
	    norm_C = max(norm_C, sqrt(double(size_blk[i])));
	}

	norm_C = (max(1.0,norm_C));

	// Creating b
	auto bsubi = new_array_ptr<int, 1>(n*(n-1)*0.5 + n);
	auto bsubj = new_array_ptr<int, 1>(n*(n-1)*0.5 + n);
	auto bcof  = new_array_ptr<double, 1>(n*(n-1)*0.5 + n);
	double obj_constant = 0;

	fill(alpha.begin(), alpha.end(),0);
	double norm_b = 0;
	vector<double> pert(n);
	srand(time(NULL));
	double perturbation = 0;//////////////////////////////
	for(int i = 0; i < n; i++)
	{
		pert[i] = perturbation*(double(rand())/RAND_MAX);
		norm_b = norm_b + pert[i];

		for(int j = i+1; j < n; j++)
		{
			norm_b = 0.25*4*W->get(i,j)*W->get(i,j) + norm_b;
		}
	}
	norm_b = max(1.0,sqrt(norm_b));
	int aux_count = 0;
	for(int i = 0; i < n; i++)
	{
		alpha[0] = i;
		alpha[1] = 0;
		(*bsubi)[aux_count] = map[alpha];
		(*bsubj)[aux_count] = 0;
		(*bcof)[aux_count] = pert[i]/norm_b;
		aux_count = aux_count + 1;
		for(int j = i+1; j < n; j++)
		{
			alpha[0] = i;
			alpha[1] = j;
			(*bsubi)[aux_count] = map[alpha];
			(*bsubj)[aux_count] = 0;
			(*bcof)[aux_count] = -0.25*2*(W->get(i,j))/norm_b;
			obj_constant = obj_constant + W->get(i,j);
			aux_count = aux_count + 1;
		}
	}
	obj_constant = 2*obj_constant*0.25;
	Matrix::t b = Matrix::sparse(total_y, 1, bsubi, bsubj, bcof);

	// Generating A
    start = std::chrono::high_resolution_clock::now();
    int total_constraints = total_var_all_blocks;

	Expression::t Ax_b;
	int count_const;
	vector<double> norm_A(total_cliques);
	for(int i=0; i < total_cliques; i++)
    {
		int size_vec_blk_i = size_blk[i]*size_blk[i];
		int size_blk_i = size_blk[i];
		int size_clique_i = size_cliques[i];
		int total_constraints = size_blk_i*(size_blk_i - 1);
		count_const = 0;
		auto msubi = new_array_ptr<int, 1>(total_constraints);
	    auto msubj = new_array_ptr<int, 1>(total_constraints);
	    auto mcof  = new_array_ptr<double, 1>(total_constraints);
	    norm_A[i] = max(1.0, sqrt(double(total_constraints)));

		fill(alpha.begin(), alpha.end(),0);
		double const_coef = 1/norm_A[i];
		for(int j = 1; j <= size_clique_i; j++)
		{
			alpha[0] = idx_cliques[i][j-1];
			(*msubi)[count_const] = j;
			(*msubj)[count_const] = map[alpha];
			(*mcof)[count_const] = const_coef;
			(*msubi)[count_const+1] = j*(size_blk_i);
			(*msubj)[count_const+1] = map[alpha];
			(*mcof)[count_const+1] = const_coef;
			count_const = count_const + 2;
		}

		for(int j = 0; j < size_clique_i; j++)
		{
			for(int k = j + 1; k < size_cliques[i]; k++)
			{
				alpha[0] = idx_cliques[i][j];
				alpha[1] = idx_cliques[i][k];
				(*msubi)[count_const] = (j+1)*(size_blk[i]) + k + 1;
				(*msubj)[count_const] = map[alpha];
				(*mcof)[count_const] = const_coef;
				(*msubi)[count_const+1] = (k+1)*(size_blk[i]) + j + 1;
				(*msubj)[count_const+1] = map[alpha];
				(*mcof)[count_const+1] = const_coef;
				count_const = count_const + 2;
			}
		}

		if((first_second_order->get(i,0) == 2) & (size_clique_i > 1))
		{
			fill(alpha.begin(), alpha.end(),0);
			int count_aux = 1;
		    for(int k = 0; k < size_clique_i; k++)
		    {
				for(int l = k+1; l < size_clique_i; l++)
				{
			        alpha[0] = idx_cliques[i][k];
				    alpha[1] = idx_cliques[i][l];
				    (*msubi)[count_const] = (size_clique_i) + count_aux;
				    (*msubj)[count_const] = map[alpha];
				    (*mcof)[count_const] = const_coef;
				    (*msubi)[count_const+1] = ((size_clique_i+count_aux)*size_blk_i);
				    (*msubj)[count_const+1] = map[alpha];
				    (*mcof)[count_const+1] = const_coef;
				    count_const = count_const + 2;
				    count_aux = count_aux + 1;
			    }
			}

		    for(int j = 0; j < size_clique_i; j++)
		    {
				count_aux = 1;
			    for(int k = 0; k < size_clique_i-1; k++)
			    {
					for(int l = k+1; l < size_clique_i; l++)
					{
				        fill(alpha.begin(), alpha.end(),0);
				        if(j == k)
				        {
							alpha[0] = idx_cliques[i][l];
						}
						else if(j == l)
						{
							alpha[0] = idx_cliques[i][k];
						}
						else if(j<k)
						{
							alpha[0] = idx_cliques[i][j];
							alpha[1] = idx_cliques[i][k];
							alpha[2] = idx_cliques[i][l];
						}
						else if (j<l)
						{
							alpha[0] = idx_cliques[i][k];
							alpha[1] = idx_cliques[i][j];
							alpha[2] = idx_cliques[i][l];
						}
						else
						{
							alpha[0] = idx_cliques[i][k];
							alpha[1] = idx_cliques[i][l];
							alpha[2] = idx_cliques[i][j];
						}

				        (*msubi)[count_const] = (j+1)*size_blk_i + size_clique_i + count_aux;
				        (*msubj)[count_const] = map[alpha];
				        (*mcof)[count_const] = const_coef;
				        (*msubi)[count_const+1] = (size_clique_i + count_aux)*size_blk_i + j + 1;
				        (*msubj)[count_const+1] = map[alpha];
				        (*mcof)[count_const+1] = const_coef;
				        count_const = count_const + 2;
				        count_aux = count_aux + 1;
			        }
				}
		    }

		    count_aux = 0;
		    int count_aux2;
		    for(int j = 0; j < size_clique_i-1; j++)
		    {
				for(int k = j+1; k < size_clique_i; k++)
			    {
					count_aux = count_aux + 1;
					count_aux2 = 0;
					for(int l = 0; l < size_clique_i-1; l++)
					{
						for(int m = l+1; m < size_clique_i; m++)
					    {
							count_aux2 = count_aux2 + 1;
							if((j==l) & (k==m))
							{
								continue;
							}
							fill(alpha.begin(), alpha.end(),0);
				            if(j == l)
				            {
								if(k<m)
								{
								    alpha[0] = idx_cliques[i][k];
								    alpha[1] = idx_cliques[i][m];
								}
								else
								{
									alpha[0] = idx_cliques[i][m];
								    alpha[1] = idx_cliques[i][k];
								}
						    }
						    else if(k == m)
						    {
							    if(j<l)
								{
								    alpha[0] = idx_cliques[i][j];
								    alpha[1] = idx_cliques[i][l];
								}
								else
								{
									alpha[0] = idx_cliques[i][l];
								    alpha[1] = idx_cliques[i][j];
								}
						    }
						    else if(j == m)
						    {
							    alpha[0] = idx_cliques[i][l];
							    alpha[1] = idx_cliques[i][k];

						    }
						    else if(k == l)
						    {
							    alpha[0] = idx_cliques[i][j];
							    alpha[1] = idx_cliques[i][m];

						    }
						    else if(k<l)
						    {
							    alpha[0] = idx_cliques[i][j];
							    alpha[1] = idx_cliques[i][k];
							    alpha[2] = idx_cliques[i][l];
							    alpha[3] = idx_cliques[i][m];
						    }
						    else if(m<j)
						    {
							    alpha[0] = idx_cliques[i][l];
							    alpha[1] = idx_cliques[i][m];
							    alpha[2] = idx_cliques[i][j];
							    alpha[3] = idx_cliques[i][k];
						    }
						    else if((j<l) & (m<k))
						    {
							    alpha[0] = idx_cliques[i][j];
							    alpha[1] = idx_cliques[i][l];
							    alpha[2] = idx_cliques[i][m];
							    alpha[3] = idx_cliques[i][k];
						    }
						    else if((j<l) & (k<m))
						    {
							    alpha[0] = idx_cliques[i][j];
							    alpha[1] = idx_cliques[i][l];
							    alpha[2] = idx_cliques[i][k];
							    alpha[3] = idx_cliques[i][m];
						    }
						    else if((l<j) & (m<k))
						    {
							    alpha[0] = idx_cliques[i][l];
							    alpha[1] = idx_cliques[i][j];
							    alpha[2] = idx_cliques[i][m];
							    alpha[3] = idx_cliques[i][k];
						    }
						    else if((l<j) & (k<m))
						    {
							    alpha[0] = idx_cliques[i][l];
							    alpha[1] = idx_cliques[i][j];
							    alpha[2] = idx_cliques[i][k];
							    alpha[3] = idx_cliques[i][m];
						    }

				            (*msubi)[count_const] = (size_clique_i + count_aux)*size_blk_i + size_clique_i + count_aux2;
				            (*msubj)[count_const] = map[alpha];
				            (*mcof)[count_const] = const_coef;
				            count_const = count_const + 1;
						}
			        }
				}
		    }
	    }

	    auto msubid = new_array_ptr<int, 1>(size_blk_i);
	    auto msubjd = new_array_ptr<int, 1>(size_blk_i);
	    auto mcofd  = new_array_ptr<double, 1>(size_blk_i);
	    for (int j = 0; j < size_blk[i]; j++)
	    {
			(*msubid)[j] = (j*size_blk_i)+j;
			(*msubjd)[j] = 0;
			(*mcofd)[j] = 1.0/(norm_C*norm_A[i]);
		}

	    Matrix::t A = Matrix::sparse(total_y,size_vec_blk_i, msubj, msubi, mcof);

	    if (i==0)
	    {
			Ax_b = Expr::mul(A, Expr::reshape(Expr::mul(S[i],-1),size_vec_blk_i));
		}
		else
		{
	        Ax_b = Expr::add(Ax_b, Expr::mul(A, Expr::reshape(Expr::mul(S[i],-1),size_vec_blk_i)));
	    }

	}
	Ax_b = Expr::sub(Ax_b,b);
	Constraint::t con = M->constraint(Ax_b, Domain::equalsTo(0.0));
	cout<<"size map: "<<map.size()<<endl;
	cout<<"Total_constraints: "<<b->numRows()<<endl;

    finish = std::chrono::high_resolution_clock::now();
    elapsed = finish - start;
    cout<<"Time creating constraints: "<<elapsed.count()<<endl;

    // Creating objective function

    Expression::t obj = Expr::constTerm(0);
    for (int i=0;i<total_cliques;i++)
    {
		int size_blk_i = size_blk[i];
	    auto msubid = new_array_ptr<int, 1>(size_blk_i);
	    auto msubjd = new_array_ptr<int, 1>(size_blk_i);
	    auto mcofd  = new_array_ptr<double, 1>(size_blk_i);
	    for (int j = 0; j < size_blk_i; j++)
	    {
			(*msubid)[j] = j;
			(*msubjd)[j] = j;
			(*mcofd)[j] = 1.0/(norm_C*norm_A[i]);
		}

		Matrix::t D_ones = Matrix::sparse(size_blk_i,size_blk_i, msubid, msubjd, mcofd);
		obj = Expr::add(obj, Expr::dot(S[i],D_ones));

	}

    auto finish_creating = std::chrono::high_resolution_clock::now();
	std::chrono::duration<double> elapsed_creating = finish_creating - start_creating;
    //Solve SDP
  	M->setLogHandler([=](const std::string & msg) { std::cout << msg << std::flush; } );
	M->objective(ObjectiveSense::Minimize, obj);
	M->setSolverParam("optimizerMaxTime", 10800);

	auto start_solving_SDP = std::chrono::high_resolution_clock::now();

	M->solve();
	finish = std::chrono::high_resolution_clock::now();
	elapsed = finish - start_solving_SDP;
	time_solving_SDP = elapsed.count();

	std::cout << std::fixed;
    std::cout << std::setprecision(4);
	cout<<"time creating problem: "<<elapsed_creating.count()<<endl;
	cout<<"time solving SDP: "<<elapsed.count()<<endl;
	cout<<"Obj_value_partial_relaxation: "<<M->primalObjValue()*norm_b*norm_C + obj_constant<<endl;
	return 0;
}

void read_matrix(const char *filename, Matrix::t& result)
{
	int row, col;
	int i = 0,j = 0;
    ifstream fin(filename);
	if (!fin.is_open())
	{
		cout << "Error opening file: "<<filename;
		exit(1);
	}

	string line, aux;
	// First line must have in the first two positions the number of rows and columns
	getline(fin, line);
	stringstream stream(line);
	getline(stream,aux,',');
	row = stoi(aux);
	getline(stream,aux,',');
	col = stoi(aux);

	auto msubi = new_array_ptr<int, 1>(row*col);
	auto msubj = new_array_ptr<int, 1>(row*col);
	auto mcof  = new_array_ptr<double, 1>(row*col);
	int aux2 = 0;
	while (getline(fin, line))
   	{
		stringstream stream(line);
		while(stream.good())
		{
			getline(stream,aux,',');
			(*msubi)[aux2] = i;
			(*msubj)[aux2] = j;
			(*mcof)[aux2] = stod(aux);
			j++;
			aux2 = aux2 + 1;
			if (j == col)
			{
				i = i+1;
				j = 0;
			}
		}
	}
	result = Matrix::sparse(row, col, msubi, msubj, mcof);
}

void read_matrix_sparse_format(const char *filename, Matrix::t& result)
{
	// reads symmetric  matrix in sparse format given as
	// n nnz
	// i_1 j_1 val_1
	// i_2 j_2 val_2
	// ...
	// i1_nnz j1_nnz val_nnz
	// where n is the dimension of the matrix, and nnz the non-zero elements.
	// It gives only the upper triangular values.

	int row, col;
	string val, i, j;
    ifstream fin(filename);
	if (!fin.is_open())
	{
		cout << "Error opening file: "<<filename;
		exit(1);
	}

	string line, aux;
	// First line must have the dimension of the square matrix and the number of non-zeros
	getline(fin, line);
	stringstream stream(line);
	getline(stream,aux,' ');
	row = stoi(aux);
	col = row;
	getline(stream,aux,' ');
	int nnz = stoi(aux);
	auto msubi = new_array_ptr<int, 1>(2*nnz);
	auto msubj = new_array_ptr<int, 1>(2*nnz);
	auto mcof  = new_array_ptr<double, 1>(2*nnz);
	int aux2 = 0;
	while(fin >> i >> j >> val)
	{
		(*msubi)[aux2] = stod(i)-1;
		(*msubj)[aux2] = stod(j)-1;
		(*msubi)[aux2+1] = stod(j)-1;
		(*msubj)[aux2+1] = stod(i)-1;
		(*mcof) [aux2] = stod(val);
		(*mcof) [aux2+1] = stod(val);
		aux2 = aux2+2;
	}

	result = Matrix::sparse(row, col, msubi, msubj, mcof);
}