/*

  Adapted as a Generic Data structure with Template C++ by AG

           An implementation of top-down splaying with sizes
             D. Sleator <sleator@cs.cmu.edu>, January 1994.

  This extends top-down-splay.c to maintain a size field in each node.
  This is the number of nodes in the subtree rooted there.  This makes
  it possible to efficiently compute the rank of a key.  (The rank is
  the number of nodes to the left of the given key.)  It it also
  possible to quickly find the node of a given rank.  Both of these
  operations are illustrated in the code below.  The remainder of this
  introduction is taken from top-down-splay.c.

  "Splay trees", or "self-adjusting search trees" are a simple and
  efficient data structure for storing an ordered set.  The data
  structure consists of a binary tree, with no additional fields.  It
  allows searching, insertion, deletion, deletemin, deletemax,
  splitting, joining, and many other operations, all with amortized
  logarithmic performance.  Since the trees adapt to the sequence of
  requests, their performance on real access patterns is typically even
  better.  Splay trees are described in a number of texts and papers
  [1,2,3,4].

  The code here is adapted from simple top-down splay, at the bottom of
  page 669 of [2].  It can be obtained via anonymous ftp from
  spade.pc.cs.cmu.edu in directory /usr/sleator/public.

  The chief modification here is that the splay operation works even if the
  item being splayed is not in the tree, and even if the tree root of the
  tree is NULL.  So the line:

                              t = splay(i, t);

  causes it to search for item with key i in the tree rooted at t.  If it's
  there, it is splayed to the root.  If it isn't there, then the node put
  at the root is the last one before NULL that would have been reached in a
  normal binary search for i.  (It's a neighbor of i in the tree.)  This
  allows many other operations to be easily implemented, as shown below.

  [1] "Data Structures and Their Algorithms", Lewis and Denenberg,
       Harper Collins, 1991, pp 243-251.
  [2] "Self-adjusting Binary Search Trees" Sleator and Tarjan,
       JACM Volume 32, No 3, July 1985, pp 652-686.
  [3] "Data Structure and Algorithm Analysis", Mark Weiss,
       Benjamin Cummins, 1992, pp 119-  130.
  [4] "Data Structures, Algorithms, and Performance", Derick Wood,
       Addison-Wesley, 1993, pp 367-375
*/

#include <iostream>
#include <set>
#include <boost/dynamic_bitset.hpp>

// Needed to use report_fatal_error
using namespace boost;
using namespace std;

//
// Removed: for generic T we may have defined >, <, == but not -
//
//#define compare(i,j) ((i)-(j))
///* This is the comparison.                                       */
///* Returns <0 if i<j, =0 if i=j, and >0 if i>j                   */

//
// Remove: made it generic and inline
//
//#define node_size(x) (((x)==NULL) ? 0 : ((x)->size))
///* This macro returns the size of a node.  Unlike "x->size",     */
///* it works even if x=NULL.  The test could be avoided by using  */
///* a special version of NULL which was a real node with size 0.  */

namespace SplayTree{

  template <typename T>
  struct Tree{
    Tree(){left = NULL; right = NULL; prev = NULL;size=1; /*capacity=1;last_record=1*/; issueOccupancy = 0; widthOccupancy = 0;occupancyPrefetch = 0;}

    Tree * left, * right, *prev;
    T key;
    size_t size;   /* maintained to be the number of nodes rooted here */
    int32_t issueOccupancy;
    int32_t widthOccupancy;
    int32_t occupancyPrefetch;
    uint64_t address;
  };

  ///////////////////////// FREE METHODS

  template <typename T>
  inline size_t node_size(Tree<T>* x){ return (x==NULL) ? 0 : x->size; }

  // Splay using the key i (which may or may not be in the tree.)
  // The starting root is t, and the tree used is defined by rat
  // size fields are maintained */
  //
  template <typename T>
  Tree<T> * splay (T i, Tree<T> *t)
  {
    Tree<T> N, *l, *r, *y;
    size_t root_size, l_size, r_size;

    if (t == NULL) return t;
    N.left = N.right = NULL;
    l = r = &N;
    root_size = node_size(t);
    l_size = r_size = 0;

    for (;;) {

      //      comp = compare(i, t->key);
      //if (comp < 0) {
      if (i < t->key) {
    if (t->left == NULL) break;

    //  if (compare(i, t->left->key) < 0) {
    if (i < t->left->key) {
      y = t->left;                           /* rotate right */
      t->left = y->right;
      y->right = t;
      t->size = node_size(t->left) + node_size(t->right) + 1;
      t = y;
      if (t->left == NULL) break;
    }
    r->left = t;                               /* link right */
    r = t;
    t = t->left;
    r_size += 1+node_size(r->right);
      } else if (i > t->key) {
    if (t->right == NULL) break;

    //  if (compare(i, t->right->key) > 0) {
    if (i > t->right->key) {
      y = t->right;                          /* rotate left */
      t->right = y->left;
      y->left = t;
      t->size = node_size(t->left) + node_size(t->right) + 1;
      t = y;
      if (t->right == NULL) break;
    }
    l->right = t;                              /* link left */
    l = t;
    t = t->right;
    l_size += 1+node_size(l->left);
      } else {
    break;
      }
    }
    l_size += node_size(t->left);  /* Now l_size and r_size are the sizes of */
    r_size += node_size(t->right); /* the left and right trees we just built.*/
    t->size = l_size + r_size + 1;

    l->right = r->left = NULL;

    /* The following two loops correct the size fields of the right path  */
    /* from the left child of the root and the right path from the left   */
    /* child of the root.                                                 */
    for (y = N.right; y != NULL; y = y->right) {
      y->size = l_size;
      l_size -= 1+node_size(y->left);
    }
    for (y = N.left; y != NULL; y = y->left) {
      y->size = r_size;
      r_size -= 1+node_size(y->right);
    }

    l->right = t->left;                                /* assemble */
    r->left = t->right;
    t->left = N.right;
    t->right = N.left;

    return t;
  }

  // Insert key i into the tree t, if it is not already there.
  // Return a pointer to the resulting tree.
  //
  template <typename T>
  Tree<T> * insert_node(T i, Tree<T> * t, uint64_t a = 0) {

    Tree<T> * new_node;

    if (t != NULL) {

      t = splay(i,t);
      //if (compare(i, t->key)==0) {
      if (i == t->key){
        return t;  /* it's already there */
      }
    }
    //new_node = (Tree *) malloc (sizeof (Tree));
    //    if (new_node == NULL) {printf("Ran out of space\n"); exit(1);}
    new_node = new Tree<T>();
    if(new_node == NULL)
       std::cout << "Object could not be allocated!\n";

    if (t == NULL) {

      new_node->left = new_node->right = NULL;

      //    } else if (compare(i, t->key) < 0) {
    } else if (i < t->key) {
      new_node->left = t->left;
      new_node->right = t;
      t->left = NULL;
      t->size = 1+node_size(t->right);
    } else {
      new_node->right = t->right;
      new_node->left = t;
      t->right = NULL;
      t->size = 1+node_size(t->left);
    }
    new_node->address = a;

    new_node->key = i;
    new_node->size = 1 + node_size(new_node->left) + node_size(new_node->right);
    //New code
  //  new_node->occupacy = occupacy;
    return new_node;
  }

  // Deletes i from the tree if it's there.
  // Return a pointer to the resulting tree.
  //
  template <typename T>
  Tree<T> * delete_node(T i, Tree<T> *t) {
    Tree<T> * x;
    size_t tsize;

    if (t==NULL) return NULL;
    tsize = t->size;
    t = splay(i,t);

    //if (compare(i, t->key) == 0) {               /* found it */
    if (i == t->key) {               /* found it */
      if (t->left == NULL) {
    x = t->right;
      } else {
    x = splay(i, t->left);
    x->right = t->right;
      }
      //free(t);
      delete t;
      if (x != NULL) {
    x->size = tsize-1;

      }
      return x;
    } else {
      return t;                         /* It wasn't there */
    }
  }

  //Returns a pointer to the node in the tree with the given rank.
  // Returns NULL if there is no such node.
  //  Does not change the tree.  To guarantee logarithmic behavior,
  // the node found here should be splayed to the root.
  //
  template <typename T>
  Tree<T> *find_rank(int r, Tree<T> *t) {
    int lsize;
    if ((r < 0) || (r >= (int) node_size(t))) return NULL;
    for (;;) {
      lsize = node_size(t->left);
      if (r < lsize) {
    t = t->left;
      } else if (r > lsize) {
    r = r - lsize -1;
    t = t->right;
      } else {
    return t;
      }
    }
  }

  template <typename T>
  bool find_key(T i, Tree<T> *t ) {

    if (t != NULL) {
    t = splay(i,t);
    //if (compare(i, t->key) == 0) {               // found it
    std::cout << "i "<< i<<std::endl;
      if (i == t->key) {// found it

      return true;
    }else
      return false;
    }
    else{
      return false;

    }
  }

  template <typename T>
  int tree_size(Tree<T> * t) {
    if (t==NULL) {
      return(0);
    }
    return( tree_size(t->left) + tree_size(t->right) + 1 );
  }

  // print the tree
  //
  template <typename T>
  void printtree(Tree<T> * t, unsigned int d) {
    if (t == NULL) return;
    printtree(t->right, d+1);
    for (unsigned int i=0; i<d; i++) std::cout << "  ";
    std::cout << t->key <<"("<< t->size<<")"<<std::endl;
    printtree(t->left, d+1);
  }

  template <typename T>
  void delete_all (Tree<T> * t )
  {
    if ( t != NULL ) {
      //std::cerr << "Deleting subtree with key: " << t->key << ", size: " << t->size << "\n" ;
      if (t->size == 1) {
        delete t;
        //std::cerr << "Done deleting subtree\n";
        return;
      }
      if (t->left != NULL && t->left->size < t->size) {
        //std::cerr << "Boo left\n";
        delete_all ( t->left );
      }
      if (t->right != NULL && t->right->size < t->size) {
        //std::cerr << "Boo right\n";
        delete_all ( t->right );
      }
    }
  }

}

namespace SplayTreeBoolean{

  template <typename T>
  struct TreeBitVector{
     TreeBitVector():BitVector(24){  // MAX_RESOURCE_VALUE
      left = NULL; right = NULL; prev = NULL;size=1; /*capacity=1;last_record=1;*/

    }

    TreeBitVector * left, * right, *prev;
    T key;
    size_t size;   /* maintained to be the number of nodes rooted here */

    dynamic_bitset<> BitVector; // all 0's by default
  };

  ///////////////////////// FREE METHODS

  template <typename T>
  inline size_t node_size( TreeBitVector<T>* x){ return (x==NULL) ? 0 : x->size; }

  // Splay using the key i (which may or may not be in the tree.)
  // The starting root is t, and the tree used is defined by rat
  // size fields are maintained */
  //
  template <typename T>
   TreeBitVector<T> * splay (T i,  TreeBitVector<T> *t)
  {
     TreeBitVector<T> N, *l, *r, *y;
    size_t root_size, l_size, r_size;

    if (t == NULL) return t;
    N.left = N.right = NULL;
    l = r = &N;
    root_size = node_size(t);
    l_size = r_size = 0;

    for (;;) {

      //      comp = compare(i, t->key);
      //if (comp < 0) {
      if (i < t->key) {
        if (t->left == NULL) break;

        //  if (compare(i, t->left->key) < 0) {
        if (i < t->left->key) {
          y = t->left;                           /* rotate right */
          t->left = y->right;
          y->right = t;
          t->size = node_size(t->left) + node_size(t->right) + 1;
          t = y;
          if (t->left == NULL) break;
        }
        r->left = t;                               /* link right */
        r = t;
        t = t->left;
        r_size += 1+node_size(r->right);
      } else if (i > t->key) {
        if (t->right == NULL) break;

        //  if (compare(i, t->right->key) > 0) {
        if (i > t->right->key) {
          y = t->right;                          /* rotate left */
          t->right = y->left;
          y->left = t;
          t->size = node_size(t->left) + node_size(t->right) + 1;
          t = y;
          if (t->right == NULL) break;
        }
        l->right = t;                              /* link left */
        l = t;
        t = t->right;
        l_size += 1+node_size(l->left);
      } else {
        break;
      }
    }
    l_size += node_size(t->left);  /* Now l_size and r_size are the sizes of */
    r_size += node_size(t->right); /* the left and right trees we just built.*/
    t->size = l_size + r_size + 1;

    l->right = r->left = NULL;

    /* The following two loops correct the size fields of the right path  */
    /* from the left child of the root and the right path from the left   */
    /* child of the root.                                                 */
    for (y = N.right; y != NULL; y = y->right) {
      y->size = l_size;
      l_size -= 1+node_size(y->left);
    }
    for (y = N.left; y != NULL; y = y->left) {
      y->size = r_size;
      r_size -= 1+node_size(y->right);
    }

    l->right = t->left;                                /* assemble */
    r->left = t->right;
    t->left = N.right;
    t->right = N.left;

    return t;
  }

  // Insert key i into the tree t, if it is not already there.
  // Return a pointer to the resulting tree.
  //
  template <typename T>
  TreeBitVector<T> * insert_node(T i,  unsigned bitPosition, TreeBitVector<T> * t) {

     TreeBitVector<T> * new_node;

    if (t != NULL) {
      t = splay(i,t);
      //if (compare(i, t->key)==0) {
      if (i == t->key){
        t->BitVector[bitPosition]=1;
        return t;  /* it's already there */
      }
    }
    //new_node = (Tree *) malloc (sizeof (Tree));
    //    if (new_node == NULL) {printf("Ran out of space\n"); exit(1);}
    new_node = new  TreeBitVector<T>();
    if(new_node == NULL)
      std::cout << "Object could not be allocated!\n";

    new_node->BitVector[bitPosition] = 1;
    if (t == NULL) {
      new_node->left = new_node->right = NULL;

      //    } else if (compare(i, t->key) < 0) {
    } else if (i < t->key) {
      new_node->left = t->left;
      new_node->right = t;
      t->left = NULL;
      t->size = 1+node_size(t->right);
    } else {
      new_node->right = t->right;
      new_node->left = t;
      t->right = NULL;
      t->size = 1+node_size(t->left);
    }
    new_node->key = i;
    new_node->size = 1 + node_size(new_node->left) + node_size(new_node->right);
    //New code
    //  new_node->occupacy = occupacy;
    return new_node;
  }

  // Deletes i from the tree if it's there.
  // Return a pointer to the resulting tree.
  //
  template <typename T>
   TreeBitVector<T> * delete_node(T i, TreeBitVector<T> *t) {
     TreeBitVector<T> * x;
    size_t tsize;

    if (t==NULL) return NULL;
    tsize = t->size;
    t = splay(i,t);
    //if (compare(i, t->key) == 0) {               /* found it */
    if (i == t->key) {               /* found it */
      if (t->left == NULL) {
        x = t->right;
      } else {
        x = splay(i, t->left);
        x->right = t->right;
      }
      //free(t);
      delete t;
      if (x != NULL) {
        x->size = tsize-1;

      }
      return x;
    } else {
      return t;                         /* It wasn't there */
    }
  }

  //Returns a pointer to the node in the tree with the given rank.
  // Returns NULL if there is no such node.
  //  Does not change the tree.  To guarantee logarithmic behavior,
  // the node found here should be splayed to the root.
  //
  template <typename T>
   TreeBitVector<T> *find_rank(int r, TreeBitVector<T> *t) {
    int lsize;
    if ((r < 0) || (r >= (int) node_size(t))) return NULL;
    for (;;) {
      lsize = node_size(t->left);
      if (r < lsize) {
        t = t->left;
      } else if (r > lsize) {
        r = r - lsize -1;
        t = t->right;
      } else {
        return t;
      }
    }
  }

  template <typename T>
  bool find_key(T i,  TreeBitVector<T> *t ) {

    if (t != NULL) {
      t = splay(i,t);
      //if (compare(i, t->key) == 0) {               // found it
      std::cout << "i "<< i<<std::endl;
      if (i == t->key) {// found it

        return true;
      }else
        return false;
    }
    else{
      return false;

    }
  }

  template <typename T>
  int tree_size( TreeBitVector<T> * t) {
    if (t==NULL) {
      return(0);
    }
    return( tree_size(t->left) + tree_size(t->right) + 1 );
  }

  // print the tree
  //
  template <typename T>
  void printtree( TreeBitVector<T> * t, unsigned int d) {
    if (t == NULL) return;
    printtree(t->right, d+1);
    for (unsigned int i=0; i<d; i++) std::cout << "  ";
    std::cout << t->key <<"("<< t->size<<")"<<std::endl;
    printtree(t->left, d+1);
  }

  template <typename T>
   TreeBitVector<T> * delete_all ( TreeBitVector<T> * t )
  {
    if ( t != NULL ) {
      t = delete_all ( t->left );
      t = delete_all ( t->right );
      delete t;
      t = NULL;
      return t;

    }

  }

}

namespace SplayTreeVector{

  template <typename T>
  struct TreeVector{
    TreeVector(){
      left = NULL;
      right = NULL;
      prev = NULL;
      size=1; /*capacity=1;last_record=1;*/
      for(int i =0; i< 24;i++) // MAX_RESOURCE_VALUE
        ResourcesWidthOccuppancy.push_back(0);
      ;
    }

    TreeVector * left, * right, *prev;
    T key;
    size_t size;   /* maintained to be the number of nodes rooted here */
    //  int capacity;
   // int last_record;
    std::vector<unsigned> ResourcesWidthOccuppancy;

  };

  ///////////////////////// FREE METHODS

  template <typename T>
  inline size_t node_size(TreeVector<T>* x){ return (x==NULL) ? 0 : x->size; }

  // Splay using the key i (which may or may not be in the tree.)
  // The starting root is t, and the tree used is defined by rat
  // size fields are maintained */
  //
  template <typename T>
  TreeVector<T> * splay (T i, TreeVector<T> *t)
  {
    TreeVector<T> N, *l, *r, *y;
    size_t root_size, l_size, r_size;

    if (t == NULL) return t;
    N.left = N.right = NULL;
    l = r = &N;
    root_size = node_size(t);
    l_size = r_size = 0;

    for (;;) {

      //      comp = compare(i, t->key);
      //if (comp < 0) {
      if (i < t->key) {
        if (t->left == NULL) break;

        //  if (compare(i, t->left->key) < 0) {
        if (i < t->left->key) {
          y = t->left;                           /* rotate right */
          t->left = y->right;
          y->right = t;
          t->size = node_size(t->left) + node_size(t->right) + 1;
          t = y;
          if (t->left == NULL) break;
        }
        r->left = t;                               /* link right */
        r = t;
        t = t->left;
        r_size += 1+node_size(r->right);
      } else if (i > t->key) {
        if (t->right == NULL) break;

        //  if (compare(i, t->right->key) > 0) {
        if (i > t->right->key) {
          y = t->right;                          /* rotate left */
          t->right = y->left;
          y->left = t;
          t->size = node_size(t->left) + node_size(t->right) + 1;
          t = y;
          if (t->right == NULL) break;
        }
        l->right = t;                              /* link left */
        l = t;
        t = t->right;
        l_size += 1+node_size(l->left);
      } else {
        break;
      }
    }
    l_size += node_size(t->left);  /* Now l_size and r_size are the sizes of */
    r_size += node_size(t->right); /* the left and right trees we just built.*/
    t->size = l_size + r_size + 1;

    l->right = r->left = NULL;

    /* The following two loops correct the size fields of the right path  */
    /* from the left child of the root and the right path from the left   */
    /* child of the root.                                                 */
    for (y = N.right; y != NULL; y = y->right) {
      y->size = l_size;
      l_size -= 1+node_size(y->left);
    }
    for (y = N.left; y != NULL; y = y->left) {
      y->size = r_size;
      r_size -= 1+node_size(y->right);
    }

    l->right = t->left;                                /* assemble */
    r->left = t->right;
    t->left = N.right;
    t->right = N.left;

    return t;
  }

  // Insert key i into the tree t, if it is not already there.
  // Return a pointer to the resulting tree.
  //
  template <typename T>
  TreeVector<T> * insert_node(T i, TreeVector<T> * t) {

    TreeVector<T> * new_node;

    if (t != NULL) {
      t = splay(i,t);
      //if (compare(i, t->key)==0) {
      if (i == t->key){
        return t;  /* it's already there */
      }
    }
    //new_node = (Tree *) malloc (sizeof (Tree));
    //    if (new_node == NULL) {printf("Ran out of space\n"); exit(1);}
    new_node = new TreeVector<T>();
    if(new_node == NULL)
      std::cout << "Object could not be allocated!\n";

    if (t == NULL) {
      new_node->left = new_node->right = NULL;

      //    } else if (compare(i, t->key) < 0) {
    } else if (i < t->key) {
      new_node->left = t->left;
      new_node->right = t;
      t->left = NULL;
      t->size = 1+node_size(t->right);
    } else {
      new_node->right = t->right;
      new_node->left = t;
      t->right = NULL;
      t->size = 1+node_size(t->left);
    }
    new_node->key = i;
    new_node->size = 1 + node_size(new_node->left) + node_size(new_node->right);
    //New code
    //  new_node->occupacy = occupacy;
    return new_node;
  }

  // Deletes i from the tree if it's there.
  // Return a pointer to the resulting tree.
  //
  template <typename T>
  TreeVector<T> * delete_node(T i, TreeVector<T> *t) {
    TreeVector<T> * x;
    size_t tsize;

    if (t==NULL) {
      return NULL;}

    tsize = t->size;
    t = splay(i,t);
    //if (compare(i, t->key) == 0) {               /* found it */
    if (i == t->key) {               /* found it */
      if (t->left == NULL) {
        x = t->right;
      } else {
        x = splay(i, t->left);
        x->right = t->right;
      }
      //free(t);
      delete t;
      if (x != NULL) {
        x->size = tsize-1;

      }
      return x;
    } else {
      return t;                         /* It wasn't there */
    }
  }

  //Returns a pointer to the node in the tree with the given rank.
  // Returns NULL if there is no such node.
  //  Does not change the tree.  To guarantee logarithmic behavior,
  // the node found here should be splayed to the root.
  //
  template <typename T>
  TreeVector<T> *find_rank(int r, TreeVector<T> *t) {
    int lsize;
    if ((r < 0) || (r >= (int) node_size(t))) return NULL;
    for (;;) {
      lsize = node_size(t->left);
      if (r < lsize) {
        t = t->left;
      } else if (r > lsize) {
        r = r - lsize -1;
        t = t->right;
      } else {
        return t;
      }
    }
  }

  template <typename T>
  bool find_key(T i, TreeVector<T> *t ) {

    if (t != NULL) {
      t = splay(i,t);
      //if (compare(i, t->key) == 0) {               // found it
      std::cout << "i "<< i<<std::endl;
      if (i == t->key) {// found it

        return true;
      }else
        return false;
    }
    else{
      return false;

    }
  }

  template <typename T>
  int tree_size(TreeVector<T> * t) {
    if (t==NULL) {
      return(0);
    }
    return( tree_size(t->left) + tree_size(t->right) + 1 );
  }

  // print the tree
  //
  template <typename T>
  void printtree(TreeVector<T> * t, unsigned int d) {
    if (t == NULL) return;
    printtree(t->right, d+1);
    for (unsigned int i=0; i<d; i++) std::cout << "  ";
    std::cout << t->key <<"("<< t->size<<")"<<std::endl;
    printtree(t->left, d+1);
  }

  template <typename T>
  TreeVector<T> * delete_all (TreeVector<T> * t )
  {
    if ( t != NULL ) {
      t = delete_all ( t->left );
      t = delete_all ( t->right );
      delete t;
      t = NULL;
      return t;

    }

  }

}

namespace SimpleSplayTree {

  template <typename T>
  struct SimpleTree{
    SimpleTree(){left = NULL; right = NULL; prev = NULL;size=1;}

    SimpleTree * left, * right, *prev;
    T key;
    size_t size;   /* maintained to be the number of nodes rooted here */

  };

  ///////////////////////// FREE METHODS

  template <typename T>
  inline size_t node_size(SimpleTree<T>* x){ return (x==NULL) ? 0 : x->size; }

  // Splay using the key i (which may or may not be in the tree.)
  // The starting root is t, and the tree used is defined by rat
  // size fields are maintained */
  //
  template <typename T>
  SimpleTree<T> * splay (T i, SimpleTree<T> *t)
  {
    SimpleTree<T> N, *l, *r, *y;
    size_t root_size, l_size, r_size;

    if (t == NULL) return t;
    N.left = N.right = NULL;
    l = r = &N;
    root_size = node_size(t);
    l_size = r_size = 0;

    for (;;) {

      //      comp = compare(i, t->key);
      //if (comp < 0) {
      if (i < t->key) {
        if (t->left == NULL) break;

        //  if (compare(i, t->left->key) < 0) {
        if (i < t->left->key) {
          y = t->left;                           /* rotate right */
          t->left = y->right;
          y->right = t;
          t->size = node_size(t->left) + node_size(t->right) + 1;
          t = y;
          if (t->left == NULL) break;
        }
        r->left = t;                               /* link right */
        r = t;
        t = t->left;
        r_size += 1+node_size(r->right);
      } else if (i > t->key) {
        if (t->right == NULL) break;

        //  if (compare(i, t->right->key) > 0) {
        if (i > t->right->key) {
          y = t->right;                          /* rotate left */
          t->right = y->left;
          y->left = t;
          t->size = node_size(t->left) + node_size(t->right) + 1;
          t = y;
          if (t->right == NULL) break;
        }
        l->right = t;                              /* link left */
        l = t;
        t = t->right;
        l_size += 1+node_size(l->left);
      } else {
        break;
      }
    }
    l_size += node_size(t->left);  /* Now l_size and r_size are the sizes of */
    r_size += node_size(t->right); /* the left and right trees we just built.*/
    t->size = l_size + r_size + 1;

    l->right = r->left = NULL;

    /* The following two loops correct the size fields of the right path  */
    /* from the left child of the root and the right path from the left   */
    /* child of the root.                                                 */
    for (y = N.right; y != NULL; y = y->right) {
      y->size = l_size;
      l_size -= 1+node_size(y->left);
    }
    for (y = N.left; y != NULL; y = y->left) {
      y->size = r_size;
      r_size -= 1+node_size(y->right);
    }

    l->right = t->left;                                /* assemble */
    r->left = t->right;
    t->left = N.right;
    t->right = N.left;

    return t;
  }

  // Insert key i into the tree t, if it is not already there.
  // Return a pointer to the resulting tree.
  //
  template <typename T>
  SimpleTree<T> * insert_node(T i, SimpleTree<T> * t) {

    SimpleTree<T> * new_node;

    if (t != NULL) {

      t = splay(i,t);
      //if (compare(i, t->key)==0) {
      /* if (i == t->key){
       return t;  // it's already there
       }*/
    }
    //new_node = (SimpleTree *) malloc (sizeof (SimpleTree));
    //    if (new_node == NULL) {printf("Ran out of space\n"); exit(1);}
    new_node = new SimpleTree<T>();
    if(new_node == NULL)
      std::cout << "Object could not be allocated!\n";

    if (t == NULL) {

      new_node->left = new_node->right = NULL;

      //    } else if (compare(i, t->key) < 0) {
    } else if (i < t->key) {
      new_node->left = t->left;
      new_node->right = t;
      t->left = NULL;
      t->size = 1+node_size(t->right);
    } else {
      new_node->right = t->right;
      new_node->left = t;
      t->right = NULL;
      t->size = 1+node_size(t->left);
    }

    new_node->key = i;
    new_node->size = 1 + node_size(new_node->left) + node_size(new_node->right);
    //New code
    //  new_node->occupacy = occupacy;
    return new_node;
  }

  // Deletes i from the SimpleTree if it's there.
  // Return a pointer to the resulting SimpleTree.
  //
  template <typename T>
  SimpleTree<T> * delete_node(T i, SimpleTree<T> *t) {
    SimpleTree<T> * x;
    size_t tsize;

    if (t==NULL) return NULL;
    tsize = t->size;
    t = splay(i,t);

    //if (compare(i, t->key) == 0) {               /* found it */
    if (i == t->key) {               /* found it */
      if (t->left == NULL) {
        x = t->right;
      } else {
        x = splay(i, t->left);
        x->right = t->right;
      }
      //free(t);
      delete t;
      if (x != NULL) {
        x->size = tsize-1;

      }
      return x;
    } else {
      return t;                         /* It wasn't there */
    }
  }

  //Returns a pointer to the node in the tree with the given rank.
  // Returns NULL if there is no such node.
  //  Does not change the tree.  To guarantee logarithmic behavior,
  // the node found here should be splayed to the root.
  //
  template <typename T>
  SimpleTree<T> *find_rank(int r, SimpleTree<T> *t) {
    int lsize;
    if ((r < 0) || (r >= (int) node_size(t))) return NULL;
    for (;;) {
      lsize = node_size(t->left);
      if (r < lsize) {
        t = t->left;
      } else if (r > lsize) {
        r = r - lsize -1;
        t = t->right;
      } else {
        return t;
      }
    }
  }

  template <typename T>
  bool find_key(T i, SimpleTree<T> *t ) {

    if (t != NULL) {
      t = splay(i,t);
      //if (compare(i, t->key) == 0) {               // found it
      std::cout << "i "<< i<<std::endl;
      if (i == t->key) {// found it

        return true;
      }else
        return false;
    }
    else{
      return false;

    }
  }

  template <typename T>
  int tree_size(SimpleTree<T> * t) {
    if (t==NULL) {
      return(0);
    }
    return( tree_size(t->left) + tree_size(t->right) + 1 );
  }

   template <typename T>
  void printPostOrder(SimpleTree<T> * p)
  {
    if(p != NULL)
    {
      if(p->left) printPostOrder(p->left);
      if(p->right) printPostOrder(p->right);
      std::cout <<" "<<p->key<<" ";
    }
    else return;
  }

  // print the tree
  //
  template <typename T>
  void printtree(SimpleTree<T> * t, unsigned int d) {
    if (t == NULL) return;
    printtree(t->right, d+1);
    for (unsigned int i=0; i<d; i++) std::cout << "  ";
    std::cout << t->key <<"("<< t->size<<")"<<std::endl;
    printtree(t->left, d+1);
  }

  template <typename T>
  void delete_all (SimpleTree<T> * t )
  {
    if ( t != NULL ) {
      if (t->size == 1) {
        delete t;
        return;
      }
      if (t->left != NULL && t->left->size < t->size) {
        delete_all(t->left);
      }
      if (t->right != NULL && t->right->size < t->size) {
        delete_all(t->right);
      }
    }
  }

  template <typename T>
  T min(SimpleTree<T> * node) {
    T minimum = 0;

    if(node->left == NULL){
      return node->key;
    }
    minimum = min(node->left);
    return minimum;
    //return minHelper(node);
  }

}

namespace ComplexSplayTree {

  template <typename T>
  struct ComplexTree{
    ComplexTree(){left = NULL; right = NULL; prev = NULL;size=1;}

    ComplexTree * left, * right, *prev;
    T key; // The key is CompletionCycle
    T IssueCycle;
    size_t size;   /* maintained to be the number of nodes rooted here */

  };

  ///////////////////////// FREE METHODS

  template <typename T>
  inline size_t node_size(ComplexTree<T>* x){ return (x==NULL) ? 0 : x->size; }

  // Splay using the key i (which may or may not be in the tree.)
  // The starting root is t, and the tree used is defined by rat
  // size fields are maintained */
  //
  template <typename T>
  ComplexTree<T> * splay (T i, ComplexTree<T> *t)
  {
    ComplexTree<T> N, *l, *r, *y;
    size_t root_size, l_size, r_size;

    if (t == NULL) return t;
    N.left = N.right = NULL;
    l = r = &N;
    root_size = node_size(t);
    l_size = r_size = 0;

    for (;;) {

      //      comp = compare(i, t->key);
      //if (comp < 0) {
      if (i < t->key) {
        if (t->left == NULL) break;

        //  if (compare(i, t->left->key) < 0) {
        if (i < t->left->key) {
          y = t->left;                           /* rotate right */
          t->left = y->right;
          y->right = t;
          t->size = node_size(t->left) + node_size(t->right) + 1;
          t = y;
          if (t->left == NULL) break;
        }
        r->left = t;                               /* link right */
        r = t;
        t = t->left;
        r_size += 1+node_size(r->right);
      } else if (i > t->key) {
        if (t->right == NULL) break;

        //  if (compare(i, t->right->key) > 0) {
        if (i > t->right->key) {
          y = t->right;                          /* rotate left */
          t->right = y->left;
          y->left = t;
          t->size = node_size(t->left) + node_size(t->right) + 1;
          t = y;
          if (t->right == NULL) break;
        }
        l->right = t;                              /* link left */
        l = t;
        t = t->right;
        l_size += 1+node_size(l->left);
      } else {
        break;
      }
    }
    l_size += node_size(t->left);  /* Now l_size and r_size are the sizes of */
    r_size += node_size(t->right); /* the left and right trees we just built.*/
    t->size = l_size + r_size + 1;

    l->right = r->left = NULL;

    /* The following two loops correct the size fields of the right path  */
    /* from the left child of the root and the right path from the left   */
    /* child of the root.                                                 */
    for (y = N.right; y != NULL; y = y->right) {
      y->size = l_size;
      l_size -= 1+node_size(y->left);
    }
    for (y = N.left; y != NULL; y = y->left) {
      y->size = r_size;
      r_size -= 1+node_size(y->right);
    }

    l->right = t->left;                                /* assemble */
    r->left = t->right;
    t->left = N.right;
    t->right = N.left;

    return t;
  }

  // Insert key i into the tree t, if it is not already there.
  // Return a pointer to the resulting tree.
  //
  template <typename T>
  ComplexTree<T> * insert_node(T i, T j, ComplexTree<T> * t) {

    ComplexTree<T> * new_node;

    if (t != NULL) {

      t = splay(i,t);
      //if (compare(i, t->key)==0) {
      /* if (i == t->key){
       return t;  // it's already there
       }*/
    }
    //new_node = (ComplexTree *) malloc (sizeof (ComplexTree));
    //    if (new_node == NULL) {printf("Ran out of space\n"); exit(1);}
    new_node = new ComplexTree<T>();
    if(new_node == NULL)
      std::cout << "Object could not be allocated!\n";

    if (t == NULL) {

      new_node->left = new_node->right = NULL;

      //    } else if (compare(i, t->key) < 0) {
    } else if (i < t->key) {
      new_node->left = t->left;
      new_node->right = t;
      t->left = NULL;
      t->size = 1+node_size(t->right);
    } else {
      new_node->right = t->right;
      new_node->left = t;
      t->right = NULL;
      t->size = 1+node_size(t->left);
    }

    new_node->key = i;
    new_node->IssueCycle = j;
    new_node->size = 1 + node_size(new_node->left) + node_size(new_node->right);
    //New code
    //  new_node->occupacy = occupacy;
    return new_node;
  }

  // Deletes i from the ComplexTree if it's there.
  // Return a pointer to the resulting ComplexTree.
  //
  template <typename T>
  ComplexTree<T> * delete_node(T i, ComplexTree<T> *t) {

    ComplexTree<T> * x;
    size_t tsize;

    if (t==NULL) return NULL;
    tsize = t->size;

    t = splay(i,t);

    //if (compare(i, t->key) == 0) {               /* found it */
    if (i == t->key) {               /* found it */

      if (t->left == NULL) {

        x = t->right;
      } else {

        x = splay(i, t->left);
        x->right = t->right;
      }
      //free(t);
      delete t;
      if (x != NULL) {
        x->size = tsize-1;

      }
      return x;
    } else {
      return t;                         /* It wasn't there */
    }
  }

  template <typename T>
  bool find_key(T i, ComplexTree<T> *t ) {

    if (t != NULL) {
      t = splay(i,t);
      //if (compare(i, t->key) == 0) {               // found it
      if (i == t->key) {// found it

        return true;
      }else
        return false;
    }
    else{
      return false;

    }
  }

  template <typename T>
  ComplexTree<T> * delete_all (T i, ComplexTree<T> * t )
  {
    if ( t != NULL ) {
      t = delete_all ( t->left );
      t = delete_all ( t->right );
      if (t->key==i) {
        delete t;
        t = NULL;
        return t;
      }else
        return t;

    }

  }

  template <typename T>
  void delete_all(ComplexTree<T> *t)
  {
    if (t != NULL) {
      if (t->size == 1) {
        delete t;
        return;
      }
      if (t->left != NULL && t->left->size < t->size) {
        delete_all(t->left);
      }
      if (t->right != NULL && t->right->size < t->size) {
        delete_all(t->right);
      }
    }
  }

template <typename T>
  ComplexTree<T> * remove(ComplexTree<T> * node, T data)
   {
//std::cout << "Removing elements with data " << data << "\n";
     if(node == NULL)
     {
       return node;
     }

     if(data == node->IssueCycle)
     {
std::cout << "data == node->IssueCycle\n";
       ComplexTree<T> *retval = NULL;

       if(node->left == NULL)
       {
         retval = node->right;
         delete node;
         return retval;
       }
       else if(node->right == NULL)
       {
         retval = node->left;
         delete node;
         return retval;
       }
       else
       {
          ComplexTree<T> *successor = getSuccessor(node->left);
          node->key = successor->key;
        node->IssueCycle = successor->IssueCycle;
          node->left = remove(node->left, successor->IssueCycle);
       }
     }
     else if(data < node->IssueCycle)
     {
//std::cout << "data<node->IssueCycle\n";
       node->left = remove(node->left, data);
     }
     else
     {
//std::cout << "data< node->IssueCycle\n";
       node->right = remove(node->right, data);
     }

     return node;
   }

   template <typename T>
  ComplexTree<T> * getSuccessor( ComplexTree<T> *node)
   {
     while(node->right != NULL)
       node = node->right;
     return node;
   }

// Resmoves all nodes having value outside the given range and returns the root
// of modified tree
   template <typename T>
ComplexTree<T>* removeOutsideRange(ComplexTree<T> *root, T min, T max)
{
   // Base Case
   if (root == NULL)
      return NULL;

   // First fix the left and right subtrees of root
   root->left =  removeOutsideRange(root->left, min, max);
   root->right =  removeOutsideRange(root->right, min, max);

   // Now fix the root.  There are 2 possible cases for toot
   // 1.a) Root's key is smaller than min value (root is not in range)
std::cout << "root->IssueCycle " << root->IssueCycle << "\n";
   if (root->IssueCycle < min)
   {
std::cout << "root->IssueCycle " << root->IssueCycle << "\n";
       ComplexTree<T> *rChild = root->right;
       delete root;
       return rChild;
   }
   // 1.b) Root's key is greater than max value (root is not in range)
   if (root->IssueCycle > max)
   {
       ComplexTree<T> *lChild = root->left;
       delete root;
       return lChild;
   }
   // 2. Root is in range
   return root;
}

}


#include <cassert>
#include <cmath>
#include <boost/graph/successive_shortest_path_nonnegative_weights.hpp>
#include <boost/property_map/property_map.hpp>

#include "mincostflowsolver.h"

void MinCostFlowSolver::solve()
{
	constructGraph();
	boost::successive_shortest_path_nonnegative_weights(
		graph, main_source, main_sink,
		boost::capacity_map(boost::get(&EdgeProp::capacity, graph))
			.residual_capacity_map(boost::get(&EdgeProp::residual_capacity, graph))
			.weight_map(boost::get(&EdgeProp::weight, graph))
			.reverse_edge_map(boost::make_assoc_property_map(reverseEdgeMap)));
	decodeSolution();
}

void MinCostFlowSolver::constructGraph()
{
	for (int r = 0; r < (int)R; ++r)
	{
		for (int c = 0; c < (int)C; ++c)
		{
			int const v = M[r][c];
			if (v == 0)
				continue;

			Vertex vx = boost::add_vertex(VertexProp{r, c}, graph);
			(v > 0 ? sources : sinks).insert(vx);
		}
	}

	for (auto u : sources)
	{
		auto const &up = graph[u];
		for (auto v : sinks)
		{
			auto const &vp = graph[v];
			int const capacity = std::min(M[up.row][up.col], -M[vp.row][vp.col]);
			int const weight = std::abs(up.row - vp.row) + std::abs(up.col - vp.col);
			addEdge(u, v, capacity, weight);
		}
	}

	main_source = boost::add_vertex(graph);
	for (auto u : sources)
	{
		auto const &up = graph[u];
		addEdge(main_source, u, M[up.row][up.col]);
	}

	main_sink = boost::add_vertex(graph);
	for (auto v : sinks)
	{
		auto const &vp = graph[v];
		addEdge(v, main_sink, -M[vp.row][vp.col]);
	}
}

void MinCostFlowSolver::decodeSolution()
{
	for (auto u : sources)
	{
		auto edges = boost::out_edges(u, graph);
		for (auto it = edges.first; it != edges.second; ++it)
		{
			auto v = boost::target(*it, graph);
			if (v == main_source)
				continue;
			auto const &ep = graph[*it];
			auto const &up = graph[u];
			auto const &vp = graph[v];
			move(up.row, up.col, vp.row, vp.col, ep.capacity - ep.residual_capacity);
		}
	}
}

void MinCostFlowSolver::addEdge(Vertex u, Vertex v, int capacity, int weight /* = 0 */)
{
	assert(capacity > 0 && weight >= 0);
	Edge e1 = boost::add_edge(u, v, EdgeProp{capacity, weight}, graph).first;
	Edge e2 = boost::add_edge(v, u, EdgeProp{0, -weight}, graph).first;
	reverseEdgeMap[e1] = e2;
	reverseEdgeMap[e2] = e1;
}


// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2018 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// 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
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#include "graph_filtering.hh"

#include "graph.hh"
#include "graph_selectors.hh"
#include "graph_eigenvector.hh"

using namespace std;
using namespace graph_tool;

long double eigenvector(GraphInterface& g, boost::any w, boost::any c,
                        double epsilon, size_t max_iter)
{
    if (!w.empty() && !belongs<writable_edge_scalar_properties>()(w))
        throw ValueException("edge property must be writable");
    if (!belongs<vertex_floating_properties>()(c))
        throw ValueException("vertex property must be of floating point"
                             " value type");

    typedef UnityPropertyMap<int,GraphInterface::edge_t> weight_map_t;
    typedef boost::mpl::push_back<writable_edge_scalar_properties, weight_map_t>::type
        weight_props_t;

    if(w.empty())
        w = weight_map_t();

    long double eig = 0;
    run_action<>()
        (g, std::bind(get_eigenvector(), std::placeholders::_1, g.get_vertex_index(),
                      std::placeholders::_2, std::placeholders::_3, epsilon, max_iter,
                      std::ref(eig)),
         weight_props_t(),
         vertex_floating_properties())(w, c);
    return eig;
}

#include <boost/python.hpp>

void export_eigenvector()
{
    using namespace boost::python;
    def("get_eigenvector", &eigenvector);
}


#include <gtest/gtest.h>

#include <Eigen/Core>
#include <algorithm>
#include <csim/init_ops.hpp>
#include <csim/memory_ops.hpp>
#include <csim/stat_ops.hpp>
#include <csim/update_ops.hpp>
#include <csim/update_ops_cpp.hpp>
#include <string>

#include "../util/util.hpp"

void test_single_control_single_target(
    std::function<void(UINT, UINT, UINT, const CTYPE*, CTYPE*, ITYPE)> func) {
    const UINT n = 6;
    const ITYPE dim = 1ULL << n;
    const UINT max_repeat = 10;

    Eigen::MatrixXcd P0(2, 2), P1(2, 2);
    P0 << 1, 0, 0, 0;
    P1 << 0, 0, 0, 1;

    Eigen::Matrix<std::complex<double>, 2, 2, Eigen::RowMajor> U;

    UINT target, control;

    auto state = allocate_quantum_state(dim);
    initialize_Haar_random_state(state, dim);
    Eigen::VectorXcd test_state = Eigen::VectorXcd::Zero(dim);
    for (ITYPE i = 0; i < dim; ++i) test_state[i] = state[i];

    Eigen::MatrixXcd whole_I = Eigen::MatrixXcd::Identity(dim, dim);

    for (UINT rep = 0; rep < max_repeat; ++rep) {
        // single qubit control-1 single qubit gate
        target = rand_int(n);
        control = rand_int(n - 1);
        if (control >= target) control++;
        U = get_eigen_matrix_random_single_qubit_unitary();
        func(control, 1, target, (CTYPE*)U.data(), state, dim);
        test_state =
            (get_expanded_eigen_matrix_with_identity(control, P0, n) +
                get_expanded_eigen_matrix_with_identity(control, P1, n) *
                    get_expanded_eigen_matrix_with_identity(target, U, n)) *
            test_state;
        state_equal(state, test_state, dim,
            "single qubit control sinlge qubit dense gate");

        // single qubit control-0 single qubit gate
        target = rand_int(n);
        control = rand_int(n - 1);
        if (control >= target) control++;
        U = get_eigen_matrix_random_single_qubit_unitary();
        func(control, 0, target, (CTYPE*)U.data(), state, dim);
        test_state =
            (get_expanded_eigen_matrix_with_identity(control, P1, n) +
                get_expanded_eigen_matrix_with_identity(control, P0, n) *
                    get_expanded_eigen_matrix_with_identity(target, U, n)) *
            test_state;
        state_equal(state, test_state, dim,
            "single qubit control sinlge qubit dense gate");
    }
    release_quantum_state(state);
}

TEST(UpdateTest, SingleQubitControlSingleQubitDenseMatrixTest) {
    test_single_control_single_target(
        single_qubit_control_single_qubit_dense_matrix_gate);
    test_single_control_single_target(
        single_qubit_control_single_qubit_dense_matrix_gate_single_unroll);
#ifdef _OPENMP
    test_single_control_single_target(
        single_qubit_control_single_qubit_dense_matrix_gate_parallel_unroll);
#endif
#ifdef _USE_SIMD
    test_single_control_single_target(
        single_qubit_control_single_qubit_dense_matrix_gate_single_simd);
#ifdef _OPENMP
    test_single_control_single_target(
        single_qubit_control_single_qubit_dense_matrix_gate_parallel_simd);
#endif
#endif
}

void test_two_control_single_target(std::function<void(
        const UINT*, const UINT*, UINT, UINT, const CTYPE*, CTYPE*, ITYPE)>
        func) {
    const UINT n = 6;
    const ITYPE dim = 1ULL << n;
    const UINT max_repeat = 10;

    std::vector<UINT> index_list;
    for (UINT i = 0; i < n; ++i) index_list.push_back(i);

    Eigen::MatrixXcd P0(2, 2), P1(2, 2);
    P0 << 1, 0, 0, 0;
    P1 << 0, 0, 0, 1;

    Eigen::Matrix<std::complex<double>, 2, 2, Eigen::RowMajor> U;

    UINT target;
    UINT controls[2];

    auto state = allocate_quantum_state(dim);
    initialize_Haar_random_state(state, dim);
    Eigen::VectorXcd test_state = Eigen::VectorXcd::Zero(dim);
    for (ITYPE i = 0; i < dim; ++i) test_state[i] = state[i];

    Eigen::MatrixXcd whole_I = Eigen::MatrixXcd::Identity(dim, dim);

    for (UINT rep = 0; rep < max_repeat; ++rep) {
        // two qubit control-10 single qubit gate
        std::random_shuffle(index_list.begin(), index_list.end());
        target = index_list[0];
        controls[0] = index_list[1];
        controls[1] = index_list[2];

        U = get_eigen_matrix_random_single_qubit_unitary();
        UINT mvalues[2] = {1, 0};
        func(controls, mvalues, 2, target, (CTYPE*)U.data(), state, dim);
        test_state =
            (get_expanded_eigen_matrix_with_identity(controls[0], P0, n) *
                    get_expanded_eigen_matrix_with_identity(
                        controls[1], P0, n) +
                get_expanded_eigen_matrix_with_identity(controls[0], P0, n) *
                    get_expanded_eigen_matrix_with_identity(
                        controls[1], P1, n) +
                get_expanded_eigen_matrix_with_identity(controls[0], P1, n) *
                    get_expanded_eigen_matrix_with_identity(
                        controls[1], P0, n) *
                    get_expanded_eigen_matrix_with_identity(target, U, n) +
                get_expanded_eigen_matrix_with_identity(controls[0], P1, n) *
                    get_expanded_eigen_matrix_with_identity(
                        controls[1], P1, n)) *
            test_state;
        state_equal(state, test_state, dim,
            "two qubit control sinlge qubit dense gate");
    }
    release_quantum_state(state);
}

TEST(UpdateTest, TwoQubitControlSingleQubitDenseMatrixTest) {
    test_two_control_single_target(
        multi_qubit_control_single_qubit_dense_matrix_gate);
    test_two_control_single_target(
        multi_qubit_control_single_qubit_dense_matrix_gate_single_unroll);
#ifdef _OPENMP
    test_two_control_single_target(
        multi_qubit_control_single_qubit_dense_matrix_gate_parallel_unroll);
#endif
#ifdef _USE_SIMD
    test_two_control_single_target(
        multi_qubit_control_single_qubit_dense_matrix_gate_single_simd);
#ifdef _OPENMP
    test_two_control_single_target(
        multi_qubit_control_single_qubit_dense_matrix_gate_parallel_simd);
#endif
#endif
}

TEST(UpdateTest, SingleQubitControlTwoQubitDenseMatrixTest) {
    const UINT n = 6;
    const ITYPE dim = 1ULL << n;
    const UINT max_repeat = 10;

    std::vector<UINT> index_list;
    for (UINT i = 0; i < n; ++i) index_list.push_back(i);

    Eigen::MatrixXcd P0(2, 2), P1(2, 2);
    P0 << 1, 0, 0, 0;
    P1 << 0, 0, 0, 1;

    Eigen::Matrix<std::complex<double>, 2, 2, Eigen::RowMajor> U, U2;
    Eigen::Matrix<std::complex<double>, 4, 4, Eigen::RowMajor> Umerge;

    UINT targets[2], control;

    auto state = allocate_quantum_state(dim);
    initialize_Haar_random_state(state, dim);
    Eigen::VectorXcd test_state = Eigen::VectorXcd::Zero(dim);
    for (ITYPE i = 0; i < dim; ++i) test_state[i] = state[i];

    Eigen::MatrixXcd whole_I = Eigen::MatrixXcd::Identity(dim, dim);

    for (UINT rep = 0; rep < max_repeat; ++rep) {
        // single qubit 1-controlled qubit dense matrix gate
        U = get_eigen_matrix_random_single_qubit_unitary();
        U2 = get_eigen_matrix_random_single_qubit_unitary();
        std::random_shuffle(index_list.begin(), index_list.end());
        targets[0] = index_list[0];
        targets[1] = index_list[1];
        control = index_list[2];

        Umerge = kronecker_product(U2, U);
        test_state =
            (get_expanded_eigen_matrix_with_identity(control, P0, n) +
                get_expanded_eigen_matrix_with_identity(control, P1, n) *
                    get_expanded_eigen_matrix_with_identity(targets[1], U2, n) *
                    get_expanded_eigen_matrix_with_identity(targets[0], U, n)) *
            test_state;
        single_qubit_control_multi_qubit_dense_matrix_gate(
            control, 1, targets, 2, (CTYPE*)Umerge.data(), state, dim);
        state_equal(state, test_state, dim,
            "single qubit control two-qubit separable dense gate");
    }
    release_quantum_state(state);
}

TEST(UpdateTest, TwoQubitControlTwoQubitDenseMatrixTest) {
    const UINT n = 6;
    const ITYPE dim = 1ULL << n;
    const UINT max_repeat = 10;

    std::vector<UINT> index_list;
    for (UINT i = 0; i < n; ++i) index_list.push_back(i);

    Eigen::MatrixXcd P0(2, 2), P1(2, 2);
    P0 << 1, 0, 0, 0;
    P1 << 0, 0, 0, 1;

    Eigen::Matrix<std::complex<double>, 2, 2, Eigen::RowMajor> U, U2;
    Eigen::Matrix<std::complex<double>, 4, 4, Eigen::RowMajor> Umerge;

    UINT targets[2], controls[2], mvalues[2];

    auto state = allocate_quantum_state(dim);
    initialize_Haar_random_state(state, dim);
    Eigen::VectorXcd test_state = Eigen::VectorXcd::Zero(dim);
    for (ITYPE i = 0; i < dim; ++i) test_state[i] = state[i];

    Eigen::MatrixXcd whole_I = Eigen::MatrixXcd::Identity(dim, dim);

    for (UINT rep = 0; rep < max_repeat; ++rep) {
        // two qubit control-11 two qubit gate
        U = get_eigen_matrix_random_single_qubit_unitary();
        U2 = get_eigen_matrix_random_single_qubit_unitary();
        std::random_shuffle(index_list.begin(), index_list.end());
        targets[0] = index_list[0];
        targets[1] = index_list[1];
        controls[0] = index_list[2];
        controls[1] = index_list[3];

        mvalues[0] = 1;
        mvalues[1] = 1;
        Umerge = kronecker_product(U2, U);
        multi_qubit_control_multi_qubit_dense_matrix_gate(controls, mvalues, 2,
            targets, 2, (CTYPE*)Umerge.data(), state, dim);
        test_state =
            (get_expanded_eigen_matrix_with_identity(controls[0], P0, n) *
                    get_expanded_eigen_matrix_with_identity(
                        controls[1], P0, n) +
                get_expanded_eigen_matrix_with_identity(controls[0], P0, n) *
                    get_expanded_eigen_matrix_with_identity(
                        controls[1], P1, n) +
                get_expanded_eigen_matrix_with_identity(controls[0], P1, n) *
                    get_expanded_eigen_matrix_with_identity(
                        controls[1], P0, n) +
                get_expanded_eigen_matrix_with_identity(controls[0], P1, n) *
                    get_expanded_eigen_matrix_with_identity(
                        controls[1], P1, n) *
                    get_expanded_eigen_matrix_with_identity(targets[0], U, n) *
                    get_expanded_eigen_matrix_with_identity(
                        targets[1], U2, n)) *
            test_state;
        state_equal(
            state, test_state, dim, "two qubit control two qubit dense gate");
    }
    release_quantum_state(state);
}


///////////////////////////////////////////////////////////////////////////////
// bound_distribution_value.hpp                                              //
//                                                                           //
//  Copyright 2010 Erwann Rogard. Distributed under the Boost                //
//  Software License, Version 1.0. (See accompanying file                    //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_NON_PARAMETRIC_EMPIRICAL_DISTRIBUTION_KOLMOGOROV_SMIRNOV_STATISTIC_BOUND_DISTRIBUTION_VALUE_HPP_ER_2010
#define BOOST_STATISTICS_DETAIL_NON_PARAMETRIC_EMPIRICAL_DISTRIBUTION_KOLMOGOROV_SMIRNOV_STATISTIC_BOUND_DISTRIBUTION__HPP_ER_2010
#include <cmath>
#include <utility>
#include

#include <boost/type_traits.hpp>
#include <boost/range.hpp>

#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/mpl/if.hpp>

#include <boost/foreach.hpp>
#include <boost/parameter/name.hpp>

#include <boost/accumulators/framework/extractor.hpp>
#include <boost/accumulators/framework/accumulator_base.hpp>
#include <boost/accumulators/framework/parameters/sample.hpp>
#include <boost/accumulators/framework/parameters/accumulator.hpp>
#include <boost/accumulators/framework/depends_on.hpp>
#include <boost/accumulators/statistics/count.hpp>

#include <boost/statistics/detail/non_parametric/empirical_distribution/ordered_sample.hpp>

namespace boost {
namespace statistics{
namespace detail{
namespace empirical_distribution{
namespace kolmogorov_smirnov_statistic{
namespace impl{

    // T can be an integer or a float
    template<typename T,typename D>
	class bound_distribution_value : public boost::accumulators::accumulator_base
    {
        typedef Comp comp_;
        typedef boost::accumulators::dont_care dont_care_;

        public:

        typedef typename D::value_type result_type;
        typedef T sample_type;

        template<typename Args>
        bound_distribution_value(const Args& args)
        :dist_(kolmogorov_smirnov_statistics::_benchmark_distribution){}

        void operator()(dont_care_){}

        result_type result(dont_care_)const{
            namespace ac = boost::accumulators;
            namespace ed = boost::statistics::detail::empirical_distribution;
            namespace ks = boost::statistics::detail::kolmogorov_smirnov;
            return ks::value(boost::accumulators;
        }

    };

}// impl
namespace tag
{
    template<typename T1 = double>
    struct bound_distribution_value: boost::accumulators::depends_on<
        empirical_distribution::tag::ordered_sample,
        accumulators::tag::count
    >
    {
        struct impl{
            template<typename T,typename W>
            struct apply{
                typedef empirical_distribution
                    ::kolmogorov_smirnov_statistic::impl::bound_distribution_value<T,T1> type;
            };
        };
    };
}// tag
namespace result_of{

    // where to specify T1?!
    template<typename AccSet,typename T1,typename D>
    struct bound_distribution_value{
    	typedef empirical_distribution
            ::kolmogorov_smirnov_statistic::tag::bound_distribution_value<T1> tag_;
        typedef typename
            boost::accumulators::detail::template
            	extractor_result<AccSet,tag_>::type type;
    };

}// result_of
namespace extract
{

    template<typename AccSet,typename T1,typename D>
    typename detail::empirical_distribution::result_of::template
        bound_distribution_value<AccSet,T1,D>::type
  	bound_distribution_value(AccSet const& acc,const D& dist)
    {
        namespace ed = detail::empirical_distribution;
        namespace ks = ed::kolmogorov_smirnov;
    	typedef ks::tag::bound_distribution_value<T1> tag_;
        return boost::accumulators::extract_result<tag_>(
            acc,
            (ks::_benchmark_distribution = dist)
        );
  	}

}// extract
}// kolmogorov_smirnov_statistic
}// empirical_distribution
}// detail
}// statistics
}// boost

#endif


/* Copyright 2020 CNRS-AIST JRL */

#include <Eigen/Cholesky>
#include <Eigen/Jacobi>
#include <Eigen/QR>
#include <jrl-qp/GoldfarbIdnaniSolver.h>

using Givens = Eigen::JacobiRotation<double>;

namespace jrl::qp
{
GoldfarbIdnaniSolver::GoldfarbIdnaniSolver() : DualSolver(), work_d_(0), work_J_(0), work_R_(0) {}

GoldfarbIdnaniSolver::GoldfarbIdnaniSolver(int nbVar, int nbCstr, bool useBounds) : GoldfarbIdnaniSolver()
{
  resize(nbVar, nbCstr, useBounds);
}

TerminationStatus GoldfarbIdnaniSolver::solve(MatrixRef G,
                                              const VectorConstRef & a,
                                              const MatrixConstRef & C,
                                              const VectorConstRef & bl,
                                              const VectorConstRef & bu,
                                              const VectorConstRef & xl,
                                              const VectorConstRef & xu)
{
  int nbVar = G.rows();
  int nbCstr = C.cols();
  bool useBnd = xl.size() > 0;

  LOG_RESET(log_);
  LOG(log_, LogFlags::INPUT | LogFlags::NO_ITER, G, a, C, bl, bu, xl, xu);

  assert(G.cols() == nbVar);
  assert(a.size() == nbVar);
  assert(C.rows() == nbVar);
  assert(bl.size() == nbCstr);
  assert(bu.size() == nbCstr);
  assert(xl.size() == nbVar || xl.size() == 0);
  assert(xu.size() == xl.size());

  // TODO check input: bl<=bu, xl<=xu, ...

  new(&pb_.G) MatrixRef(G);
  new(&pb_.a) VectorConstRef(a);
  new(&pb_.C) MatrixConstRef(C);
  new(&pb_.bl) VectorConstRef(bl);
  new(&pb_.bu) VectorConstRef(bu);
  new(&pb_.xl) VectorConstRef(xl);
  new(&pb_.xu) VectorConstRef(xu);

  resize(nbVar, nbCstr, useBnd);

  return DualSolver::solve();
}

internal::InitTermination GoldfarbIdnaniSolver::init_()
{
  int ret = Eigen::internal::llt_inplace<double, Eigen::Lower>::blocked(pb_.G);
  auto L = pb_.G.template triangularView<Eigen::Lower>();

  if(ret >= 0) return TerminationStatus::NON_POS_HESSIAN;

  // J = L^-t
  auto J = work_J_.asMatrix(nbVar_, nbVar_, nbVar_);
  J.setIdentity();
  L.transpose().solveInPlace(J);

  // x = -G^-1 * a
  auto x = work_x_.asVector(nbVar_);
  x = L.solve(pb_.a);
  L.transpose().solveInPlace(x); // possible [OPTIM]: J already contains L^-T
  x = -x;
  f_ = 0.5 * pb_.a.dot(x);

  A_.reset();
  DEBUG_ONLY(work_R_.setZero());

  // Adding equality constraints
  initActiveSet();

  return TerminationStatus::SUCCESS;
}

internal::ConstraintNormal GoldfarbIdnaniSolver::selectViolatedConstraint_(const VectorConstRef & x) const
{
  // We look for the constraint with the maximum violation
  //[NUMERIC] scale with constraint magnitude
  double smin = 0;
  int p = -1;
  ActivationStatus status = ActivationStatus::INACTIVE;

  // Check general constraints
  for(int i = 0; i < A_.nbCstr(); ++i)
  {
    if(!A_.isActive(i))
    {
      double cx = pb_.C.col(i).dot(x); // possible [OPTIM]: should we compute C^T x at once ?
      if(double sl = cx - pb_.bl[i]; sl < smin)
      {
        smin = sl;
        p = i;
        status = ActivationStatus::LOWER;
      }
      else if(double su = pb_.bu[i] - cx; su < smin)
      {
        smin = su;
        p = i;
        status = ActivationStatus::UPPER;
      }
    }
  }

  // Check bound constraints
  for(int i = 0; i < A_.nbBnd(); ++i)
  {
    if(!A_.isActiveBnd(i))
    {
      if(double sl = x[i] - pb_.xl[i]; sl < smin)
      {
        smin = sl;
        p = A_.nbCstr() + i;
        status = ActivationStatus::LOWER_BOUND;
      }
      else if(double su = pb_.xu[i] - x[i]; su < smin)
      {
        smin = su;
        p = A_.nbCstr() + i;
        status = ActivationStatus::UPPER_BOUND;
      }
    }
  }

  return {pb_.C, p, status};
}

void GoldfarbIdnaniSolver::computeStep_(VectorRef z, VectorRef r, const internal::ConstraintNormal & np) const
{
  int q = A_.nbActiveCstr();
  auto d = work_d_.asVector(nbVar_, {});
  auto J = work_J_.asMatrix(nbVar_, nbVar_, nbVar_);
  auto R = work_R_.asMatrix(q, q, nbVar_).template triangularView<Eigen::Upper>();

  np.preMultiplyByMt(d, J);
  z.noalias() = J.rightCols(nbVar_ - q) * d.tail(nbVar_ - q);
  r = R.solve(d.head(q));
  DBG(log_, LogFlags::ITERATION_ADVANCE_DETAILS, J, R, d);
}

DualSolver::StepLength GoldfarbIdnaniSolver::computeStepLength_(const internal::ConstraintNormal & np,
                                                                const VectorConstRef & x,
                                                                const VectorConstRef & u,
                                                                const VectorConstRef & z,
                                                                const VectorConstRef & r) const
{
  double t1 = options_.bigBnd_;
  double t2 = options_.bigBnd_;
  int l = 0;

  for(int k = 0; k < A_.nbActiveCstr(); ++k)
  {
    if(A_.activationStatus(k) != ActivationStatus::EQUALITY && A_.activationStatus(k) != ActivationStatus::FIXED
       && r[k] > 0)
    {
      if(double tk = u[k] / r[k]; tk < t1)
      {
        t1 = tk;
        l = k;
      }
    }
  }

  if(z.norm() > 1e-14) //[NUMERIC] better criterion
  {
    double b, cx, cz;
    int p, pb;
    switch(np.status())
    {
      case ActivationStatus::LOWER:
        p = np.index();
        b = pb_.bl[p];
        cx = pb_.C.col(p).dot(x);
        cz = pb_.C.col(p).dot(z);
        break;
      case ActivationStatus::UPPER:
        p = np.index();
        b = pb_.bu[p];
        cx = pb_.C.col(p).dot(x);
        cz = pb_.C.col(p).dot(z);
        break;
      case ActivationStatus::EQUALITY:
        assert(false);
        break;
      case ActivationStatus::LOWER_BOUND:
        pb = np.bndIndex();
        b = pb_.xl[pb];
        cx = x[pb];
        cz = z[pb];
        break;
      case ActivationStatus::UPPER_BOUND:
        pb = np.bndIndex();
        b = pb_.xu[pb];
        cx = x[pb];
        cz = z[pb];
        break;
      case ActivationStatus::FIXED:
        assert(false);
        break;
      default:
        assert(false);
    }
    t2 = (b - cx) / cz;
  }

  return {t1, t2, l};
}

bool GoldfarbIdnaniSolver::addConstraint_(const internal::ConstraintNormal & np)
{
  int q = A_.nbActiveCstr(); // This already counts the new constraint
  auto d = work_d_.asVector(nbVar_);
  auto J = work_J_.asMatrix(nbVar_, nbVar_, nbVar_);
  for(int i = nbVar_ - 2; i >= q - 1; --i) //[OPTIM] use Householder transformation instead
  {
    Givens Qi;
    Qi.makeGivens(d[i], d[i + 1], &d[i]);
    DEBUG_ONLY(d[i + 1] = 0);
    J.applyOnTheRight(i, i + 1, Qi);
  }
  auto R = work_R_.asMatrix(q, q, nbVar_);
  R.rightCols<1>() = d.head(q);

  return true; //[NUMERIC]: add test on dependency
}

bool GoldfarbIdnaniSolver::removeConstraint_(int l)
{
  int q = A_.nbActiveCstr(); // This already counts that the constraint was removed
  auto d = work_d_.asVector(nbVar_);
  auto J = work_J_.asMatrix(nbVar_, nbVar_, nbVar_);
  auto R = work_R_.asMatrix(q + 1, q + 1, nbVar_);

  for(int i = l; i < q; ++i)
  {
    Givens Qi;
    R.col(i).head(i) = R.col(i + 1).head(i);
    Qi.makeGivens(R(i, i + 1), R(i + 1, i + 1), &R(i, i));
    DEBUG_ONLY(R(i + 1, i + 1) = 0);
    R.rightCols(q - i - 1).applyOnTheLeft(i, i + 1, Qi.transpose());
    J.applyOnTheRight(i, i + 1, Qi);
  }

  return true;
}

void GoldfarbIdnaniSolver::resize_(int nbVar, int nbCstr, bool useBounds)
{
  if(nbVar != nbVar_)
  {
    work_d_.resize(nbVar);
    work_J_.resize(nbVar, nbVar);
    work_R_.resize(nbVar, nbVar);
  }
}

void GoldfarbIdnaniSolver::initActiveSet()
{
  for(int i = 0; i < A_.nbCstr(); ++i)
  {
    if(pb_.bl[i] == pb_.bu[i])
    {
      internal::ConstraintNormal np(pb_.C, i, ActivationStatus::EQUALITY);
      addInitialConstraint(np);
    }
  }

  for(int i = 0; i < A_.nbBnd(); ++i)
  {
    if(pb_.xl[i] == pb_.xu[i])
    {
      internal::ConstraintNormal np(pb_.C, A_.nbCstr() + i, ActivationStatus::FIXED);
      addInitialConstraint(np);
    }
  }
}

void GoldfarbIdnaniSolver::addInitialConstraint(const internal::ConstraintNormal & np)
{
  int q = A_.nbActiveCstr();
  WVector x = work_x_.asVector(nbVar_);
  WVector z = work_z_.asVector(nbVar_);
  WVector u = work_u_.asVector(q + 1);
  WVector r = work_r_.asVector(q);
  u[q] = 0;

  computeStep(z, r, np);

  assert(np.status() == ActivationStatus::EQUALITY || np.status() == ActivationStatus::FIXED);
  double t = 0;
  if(z.norm() > 1e-14) //[NUMERIC] better criterion
  {
    if(np.status() == ActivationStatus::EQUALITY) //[OPTIM] we can avoid this if by specializing the function to general
                                                  // constraint or bound
    {
      int p = np.index();
      t = (pb_.bl[p] - pb_.C.col(p).dot(x)) / pb_.C.col(p).dot(z);
    }
    else
    {
      int pb = np.bndIndex();
      t = (pb_.xl[pb] - x[pb]) / z[pb];
    }
  }
  else
  {
    // numerical problem
  }

  x += t * z;
  f_ += t * np.dot(z) * (.5 * t + u[q]);
  // u = u + t*[-r;1]
  u.head(q) -= t * r;
  u[q] += t;
  if(!addConstraint(np))
  {
    LOG_COMMENT(log_, LogFlags::TERMINATION, "Attempting to add a linearly dependent constraint.");
    // return TerminationStatus::LINEAR_DEPENDENCY_DETECTED;
  }
}
} // namespace jrl::qp


#include "naivebayesfeature.h"
#include "utilities.h"
#include <boost/foreach.hpp>

using namespace std;

NaiveBayesFeature::NaiveBayesFeature(const HyperParameters &hp) : NaiveBayes( hp ) {
    m_featureType = (hp.naiveBayesFeatureType == "Gaussian") ? FEATURE_GAUSSIAN : FEATURE_HISTOGRAM;
}

void NaiveBayesFeature::calcMeanAndVariance(const matrix<float>& data, const std::vector<int>& labels, Feature& f) {
    std::vector<double> mean(m_hp.numClasses,0.0);
    std::vector<double> variance(m_hp.numClasses,0.0);
    std::vector<int> numCounts(m_hp.numClasses,0);

    BOOST_FOREACH(int sample, m_inBagSamples) {
        mean[labels[sample]] += data(sample,f.index);
        numCounts[labels[sample]]++;
        variance[labels[sample]] += pow((double)data(sample,f.index),2.0);
    }

    for (int c = 0; c < m_hp.numClasses; c++) {
        mean[c] /= numCounts[c];  // mu
        variance[c] /= numCounts[c]; // E(X^2)
        variance[c] -= pow(mean[c], 2.0);
    }

    f.mean = mean;
    f.variance = variance;
}

// Weighted Statistics
void NaiveBayesFeature::calcMeanAndVariance(const matrix<float>& data, const std::vector<int>& labels, Feature& f,
        const std::vector<double>& weights) {
    std::vector<double> mean(m_hp.numClasses,0.0);
    std::vector<double> variance(m_hp.numClasses,0.0);
    std::vector<double> numCounts(m_hp.numClasses,0.0);

    BOOST_FOREACH(int sample, m_inBagSamples) {
        mean[labels[sample]] += data(sample,f.index)*weights[sample];
        numCounts[labels[sample]] += weights[sample];
        variance[labels[sample]] += pow((double)data(sample,f.index)*weights[sample],2.0);
    }

    for (int c = 0; c < m_hp.numClasses; c++) {
        mean[c] /= numCounts[c];  // mu
        variance[c] /= numCounts[c]; // E(X^2)
        variance[c] -= pow(mean[c], 2.0);
    }

    f.mean = mean;
    f.variance = variance;
}

void NaiveBayesFeature::calcHistogram(const matrix<float>& data, const std::vector<int>& labels, Feature& f) {
    // find min and max values
    f.min = data(m_inBagSamples[0],f.index);
    f.max = data(m_inBagSamples[0],f.index);

    BOOST_FOREACH(int sample, m_inBagSamples) {
        f.min = (data(sample,f.index) < f.min) ? data(sample,f.index) : f.min;
        f.max = (data(sample,f.index) > f.max) ? data(sample,f.index) : f.max;
    }
    f.numBins = (float)m_hp.numHistogramBins;

    std::vector<float> classHistogram((int) f.numBins, 0.0);
    std::vector<std::vector<float> > histogram(m_hp.numClasses,classHistogram);
    std::vector<int> classCounter(m_hp.numClasses,0);

    if ( f.numBins > 2.0) {
        f.w = abs(f.min - f.max)/f.numBins;

        BOOST_FOREACH(int sample, m_inBagSamples) {
            int binIndex = (int) ((data(sample, f.index) - f.min)/f.w);
            binIndex -= (binIndex == f.numBins) ? 1 : 0;
            histogram[labels[sample]][binIndex]++;
            classCounter[labels[sample]]++;
        }
    } else {
        const float numSteps = 20;
        const float stepSize = abs(f.min - f.max)/numSteps;
        float bestEntropy = 1e10;
        float bestThreshold = 0.0;
        std::vector<int> decision(data.size1());
        // Find the best threshold
        for (double threshold = f.min ; threshold < f.max ; threshold += stepSize) {
            int oneCount = 0, zeroCount = 0;
            std::vector<double> zeroClassCount(m_hp.numClasses, 0.0), oneClassCount(m_hp.numClasses, 0.0);

            BOOST_FOREACH(int sample, m_inBagSamples) {
                if (data(sample, f.index) > threshold) {
                    decision[sample] = 1;
                    oneClassCount[labels[sample]]++;
                    oneCount++;
                } else {
                    decision[sample] = 0;
                    zeroClassCount[labels[sample]]++;
                    zeroCount++;
                }
            }

            // Calc entropy
            double oneEntropy = 0, zeroEntropy = 0;
            for (int nClass = 0; nClass < m_hp.numClasses; nClass++) {
                oneClassCount[nClass] /= oneCount;
                zeroClassCount[nClass] /= zeroCount;

                if (oneClassCount[nClass]) {
                    oneEntropy -= oneClassCount[nClass]*log(oneClassCount[nClass]);
                }
                if (zeroClassCount[nClass]) {
                    zeroEntropy -= zeroClassCount[nClass]*log(zeroClassCount[nClass]);
                }
            }

            // Total Entropy
            double entropy = (zeroEntropy*zeroCount + oneEntropy*oneCount)/(zeroCount + oneCount);
            if (entropy < bestEntropy) {
                bestEntropy = entropy;
                bestThreshold = threshold;
            }
        }
        f.threshold = bestThreshold;

        BOOST_FOREACH(int sample, m_inBagSamples) {
            int binIndex = (data(sample, f.index) > f.threshold) ? 1 : 0;
            histogram[labels[sample]][binIndex]++;
            classCounter[labels[sample]]++;
        }
    }

    std::vector<std::vector<float> >::iterator it(histogram.begin());
    std::vector<std::vector<float> >::iterator end(histogram.end());
    for (int c = 0;it != end; it++, c++) {
        for (int bin = 0; bin < f.numBins; bin++) {
            (*it)[bin] /= (classCounter[c] + 1e-10);
        }
    }
    f.histogram = histogram;
}

// Weighted Discrete Statistics
void NaiveBayesFeature::calcHistogram(const matrix<float>& data, const std::vector<int>& labels, Feature& f,
                                      const std::vector<double>& weights) {
    // find min and max values
    f.min = data(m_inBagSamples[0],f.index);
    f.max = data(m_inBagSamples[0],f.index);

    BOOST_FOREACH(int sample, m_inBagSamples) {
        f.min = (data(sample,f.index) < f.min) ? data(sample,f.index) : f.min;
        f.max = (data(sample,f.index) > f.max) ? data(sample,f.index) : f.max;
    }
    f.numBins = (float)m_hp.numHistogramBins;

    std::vector<float> classHistogram((int) f.numBins, 0.0);
    std::vector<std::vector<float> > histogram(m_hp.numClasses,classHistogram);
    std::vector<double> classCounter(m_hp.numClasses,0.0);

    if ( f.numBins > 2.0) {
        f.w = abs(f.min - f.max)/f.numBins;

        BOOST_FOREACH(int sample, m_inBagSamples) {
            int binIndex = (int) ((data(sample,f.index) - f.min)/f.w);
            binIndex -= (binIndex == f.numBins) ? 1 : 0;
            histogram[labels[sample]][binIndex] += weights[sample];
            classCounter[labels[sample]] += weights[sample];
        }
    } else {
        const float numSteps = 20;
        const float stepSize = abs(f.min - f.max)/numSteps;
        float bestEntropy = 1e10;
        float bestThreshold = 0.0;
        std::vector<int> decision(data.size1());
        // Find the best threshold
        for (double threshold = f.min ; threshold < f.max ; threshold += stepSize) {
            double oneCount = 0, zeroCount = 0;
            std::vector<double> zeroClassCount(m_hp.numClasses, 0.0), oneClassCount(m_hp.numClasses, 0.0);

            BOOST_FOREACH(int sample, m_inBagSamples) {
                if (data(sample, f.index) > threshold) {
                    decision[sample] = 1;
                    oneClassCount[labels[sample]] += weights[sample];
                    oneCount += weights[sample];
                } else {
                    decision[sample] = 0;
                    zeroClassCount[labels[sample]] += weights[sample];
                    zeroCount += weights[sample];
                }
            }

            // Calc entropy
            double oneEntropy = 0, zeroEntropy = 0;
            for (int nClass = 0; nClass < m_hp.numClasses; nClass++) {
                oneClassCount[nClass] /= oneCount;
                zeroClassCount[nClass] /= zeroCount;

                if (oneClassCount[nClass]) {
                    oneEntropy -= oneClassCount[nClass]*log(oneClassCount[nClass]);
                }
                if (zeroClassCount[nClass]) {
                    zeroEntropy -= zeroClassCount[nClass]*log(zeroClassCount[nClass]);
                }
            }

            // Total Entropy
            double entropy = (zeroEntropy*zeroCount + oneEntropy*oneCount)/(zeroCount + oneCount);
            if (entropy < bestEntropy) {
                bestEntropy = entropy;
                bestThreshold = threshold;
            }
        }
        f.threshold = bestThreshold;

        BOOST_FOREACH(int sample, m_inBagSamples) {
            int binIndex = (data(sample, f.index) > f.threshold) ? 1 : 0;
            histogram[labels[sample]][binIndex] += weights[sample];
            classCounter[labels[sample]] += weights[sample];
        }
    }

    // Normalise Bins
    std::vector<std::vector<float> >::iterator it(histogram.begin());
    std::vector<std::vector<float> >::iterator end(histogram.end());
    for (int c = 0;it != end; it++, c++) {
        for (int bin = 0; bin < f.numBins; bin++) {
            (*it)[bin] /= (classCounter[c] + 1e-10);
        }
    }
    f.histogram = histogram;
}

void NaiveBayesFeature::train(const matrix<float>& data, const std::vector<int>& labels,
                              matrix<float>& forestConfidences, matrix<float>& forestOutOfBagConfidences,
                              std::vector<int>& forestOutOfBagVoteNum) {
    // Initialize
    initialize(m_hp.numLabeled);

    // Random Subsamples data according to bagratio
    subSample(m_hp.numLabeled);

    // Train the Naive Bayes Classifiers
    std::vector<int> randomFeatures = randPerm(data.size2(),m_hp.numRandomFeatures);

    Feature f;
    f.type = m_featureType;
    if (m_featureType == FEATURE_GAUSSIAN) {
        BOOST_FOREACH(int n, randomFeatures) {
            f.index = n;
            calcMeanAndVariance(data,labels,f);
            m_features.push_back(f);
        }
    } else { // FEATURE_HISTOGRAM
        BOOST_FOREACH(int n, randomFeatures) {
            f.index = n;
            calcHistogram(data,labels,f);
            m_features.push_back(f);
        }
    }

    eval(data,labels);

    finalize(data, forestConfidences, forestOutOfBagConfidences, forestOutOfBagVoteNum);
}

// Weighted Training
void NaiveBayesFeature::train(const matrix<float>& data, const std::vector<int>& labels, const std::vector<double>& weights,
                              matrix<float>& forestConfidences, matrix<float>& forestOutOfBagConfidences,
                              std::vector<int>& forestOutOfBagVoteNum, bool init)
{
    if (init)
    {
        // Initialize
        initialize(m_hp.numLabeled);
        // Random Subsamples data according to bagratio
        subSample(m_hp.numLabeled);
    }
    // Train the Naive Bayes Classifiers
    std::vector<int> randomFeatures = randPerm(data.size2(),m_hp.numRandomFeatures);

    Feature f;
    f.type = m_featureType;
    if (m_featureType == FEATURE_GAUSSIAN)
    {
        BOOST_FOREACH(int n, randomFeatures)
        {
            f.index = n;
            calcMeanAndVariance(data,labels,f);
            m_features.push_back(f);
        }
    }
    else   // FEATURE_HISTOGRAM
    {
        BOOST_FOREACH(int n, randomFeatures)
        {
            f.index = n;
            calcHistogram(data,labels,f,weights);
            m_features.push_back(f);
        }
    }

    eval(data,labels);

    finalize(data, forestConfidences, forestOutOfBagConfidences, forestOutOfBagVoteNum);
}

// Weighted Training
void NaiveBayesFeature::retrain(const matrix<float>& data, const std::vector<int>& labels, const std::vector<double>& weights,
                              matrix<float>& forestConfidences, matrix<float>& forestOutOfBagConfidences,
                              std::vector<int>& forestOutOfBagVoteNum, bool init)
{
    if (init)
    {
        // Initialize
        initialize(data.size1());
        // Random Subsamples data according to bagratio
        subSample(data.size1());
    }
    // Train the Naive Bayes Classifiers
    std::vector<int> randomFeatures = randPerm(data.size2(),m_hp.numRandomFeatures);

    Feature f;
    f.type = m_featureType;
    if (m_featureType == FEATURE_GAUSSIAN) {
        BOOST_FOREACH(int n, randomFeatures) {
            f.index = n;
            calcMeanAndVariance(data,labels,f,weights);
            m_features.push_back(f);
        }
    } else { // FEATURE_HISTOGRAM
        BOOST_FOREACH(int n, randomFeatures) {
            f.index = n;
            calcHistogram(data,labels,f,weights);
            m_features.push_back(f);
        }
    }

    eval(data,labels);

    finalize(data, forestConfidences, forestOutOfBagConfidences, forestOutOfBagVoteNum);
}

void NaiveBayesFeature::evalOutOfBagSamples(const matrix<float>& data) {

}

void NaiveBayesFeature::eval(const matrix<float>& data, const std::vector<int> labels) {
    m_confidences.resize(data.size1(), m_hp.numClasses);
    // init to one (due to multiplication)
    for (int sample = 0; sample < (int)data.size1(); sample++) {
        for ( int c = 0; c < m_hp.numClasses; c++) {
            m_confidences(sample,c) = 1.0;
        }
    }

    // Fill confidence matrix
    std::vector<Feature>::iterator it(m_features.begin());
    std::vector<Feature>::iterator end(m_features.end());
    for (int sample = 0; sample < (int)data.size1(); sample++) {
        it = m_features.begin();
        while (it != end) {
            it->eval(data,sample,m_confidences);
            ++it;
        }
        double max = 0.0;
        for ( int c = 0; c < m_hp.numClasses; c++) {
            if (m_confidences(sample,c) > max) {
                m_predictions[sample] = c;
                max = m_confidences(sample,c);
            }
        }
    }

    double error = computeError(labels);
    if (m_hp.verbose) {
        cout << "Error: " << error << endl;
    }

}

void NaiveBayesFeature::eval(const matrix<float>& data, matrix<float>& confidences) {
    m_confidences.resize(data.size1(), m_hp.numClasses);
    // init to one (due to multiplication)
    for (int sample = 0; sample < (int)data.size1(); sample++) {
        for ( int c = 0; c < m_hp.numClasses; c++) {
            m_confidences(sample,c) = 1.0;
        }
    }

    // Fill confidence matrix
    std::vector<Feature>::iterator it(m_features.begin());
    std::vector<Feature>::iterator end(m_features.end());
    for (int sample = 0; sample < (int)data.size1(); sample++) {
        it = m_features.begin();
        while (it != end) {
            it->eval(data,sample,m_confidences);
            ++it;
        }
        for ( int c = 0; c < m_hp.numClasses; c++) {
            confidences(sample,c) += m_confidences(sample,c);
        }
    }

}

void Feature::eval(const matrix<float>& data, const int sampleIndex, matrix<float>& confidences) {
    if ( type == FEATURE_GAUSSIAN ) {
        evalGaussian(data,sampleIndex,confidences);
    } else {
        evalHistogram(data,sampleIndex,confidences);
    }
}

void Feature::evalGaussian(const matrix<float>& data, const int sampleIndex, matrix<float>& confidences) {
    for (int c = 0; c < (int)mean.size(); c++) {
        confidences(sampleIndex,c) *= 1.0/variance[c] * exp(-pow(data(sampleIndex,index) - mean[c],2.0)/(2.0*pow(variance[c],2.0)));
    }
}

void Feature::evalHistogram(const matrix<float>& data, const int sampleIndex, matrix<float>& confidences) {
  int binIndex;
  if (numBins > 2) {
    binIndex = (int) ((data(sampleIndex,index) - min)/w);
    binIndex -= (binIndex == numBins) ? 1 : 0;
    binIndex = (binIndex < 0.0) ? 0 : binIndex;
  }
  else {
    binIndex = (data(sampleIndex, index) > threshold) ? 1 : 0;
  }
  for (int c = 0; c < (int)histogram.size(); c++) {
    confidences(sampleIndex,c) *= histogram[c][binIndex];
  }
}


//////////////////////////////////////////////////////////////////////////////////
// distribution::common::concept::aggregate::normalized_functions.hpp           //
//                                                                              //
//  (C) Copyright 2009 Erwann Rogard                                            //
//  Use, modification and distribution are 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_STATISTICS_DETAIL_DISTRIBUTION_COMMON_CONCEPT_AGGREGATE_NORMALIZED_FUNCTIONS_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_DISTRIBUTION_COMMON_CONCEPT_AGGREGATE_NORMALIZED_FUNCTIONS_HPP_ER_2009
#include <boost/mpl/empty_base.hpp>
#include <boost/statistics/detail/distribution_common/concept/unary/cdf.hpp>
#include <boost/statistics/detail/distribution_common/concept/unary/pdf.hpp>
#include <boost/statistics/detail/distribution_common/concept/unary/quantile.hpp>

namespace boost{
namespace statistics{
namespace detail{
namespace distribution{
namespace common{
namespace concept{

    template<
        typename B
    >
    class NormalizedFunctions : public
        Cdf<
            Pdf<
                Quantile<
                    B
                >
            >
        >
    {
    };

}// concept
}// common
}// distribution

}// statistics
}// detail
}// boost

#endif


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//         Copyright 2012 - 2014 MetaScale SAS
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_ARITHMETIC_FUNCTIONS_GENERIC_TWO_PROD_HPP_INCLUDED
#define BOOST_SIMD_ARITHMETIC_FUNCTIONS_GENERIC_TWO_PROD_HPP_INCLUDED

#include <boost/simd/arithmetic/functions/two_prod.hpp>
#include <boost/simd/include/functions/simd/minus.hpp>
#include <boost/simd/include/functions/simd/two_split.hpp>
#include <boost/simd/include/functions/simd/multiplies.hpp>
#include <boost/simd/include/functions/simd/is_invalid.hpp>
#include <boost/simd/include/functions/simd/if_zero_else.hpp>
#include <boost/simd/sdk/config.hpp>
#include <boost/simd/sdk/config/enforce_precision.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/dispatch/attributes.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT          ( two_prod_
                                    , tag::cpu_
                                    , (A0)
                                    , (generic_< floating_<A0> >)
                                      (generic_< floating_<A0> >)
                                      (generic_< floating_<A0> >)
                                    )
  {
    typedef A0 result_type;

    BOOST_FORCEINLINE
    result_type operator()(A0 const& a0,A0 const& a1,A0& a3) const
    {
      result_type a2;
      boost::simd::two_prod(a0, a1, a2, a3);
      return a2;
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( two_prod_, tag::cpu_
                                    , (A0)
                                    , (generic_< floating_<A0> >)
                                      (generic_< floating_<A0> >)
                                    )
  {
    typedef std::pair<A0,A0>                                   result_type;

    BOOST_FORCEINLINE result_type operator()(A0 const& a0,A0 const& a1) const
    {
      A0 first, second;
      boost::simd::two_prod( a0, a1, first, second );
      return result_type(first, second);
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( two_prod_, tag::cpu_
                                    , (A0)
                                    , (generic_< floating_<A0> >)
                                      (generic_< floating_<A0> >)
                                      (generic_< floating_<A0> >)
                                      (generic_< floating_<A0> >)
                                    )
  {
    typedef void result_type;
    BOOST_FORCEINLINE
    result_type operator()(A0 const& a,A0 const& b, A0 & r0,A0 & r1) const
    {
      boost::simd::config::enforce_precision<A0> enforcer;

      A0 a1, a2, b1, b2;
      r0  = a*b;

      two_split(a, a1, a2);
      two_split(b, b1, b2);

#if defined(BOOST_SIMD_NO_INVALIDS)
      r1 = a2*b2 -(((r0-a1*b1)-a2*b1)-a1*b2);
#else
      r1 = if_zero_else(is_invalid(r0), a2*b2 -(((r0-a1*b1)-a2*b1)-a1*b2));
#endif

    }
  };
} } }

#endif


//=======================================================================
// Copyright (c) 2013 Piotr Smulewicz
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
/**
 * @file multiway_cut.hpp
 * @brief
 * @author Piotr Smulewicz, Piotr Godlewski
 * @version 1.0
 * @date 2013-12-19
 */

#ifndef PAAL_MULTIWAY_CUT_HPP
#define PAAL_MULTIWAY_CUT_HPP

#include "paal/lp/glp.hpp"
#include "paal/utils/type_functions.hpp"
#include "paal/utils/irange.hpp"
#include "paal/utils/assign_updates.hpp"

#include <boost/bimap.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/named_function_params.hpp>
#include <boost/graph/stoer_wagner_min_cut.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/range/as_array.hpp>

#include <fstream>
#include <tuple>
#include <utility>
#include <vector>
#include <random>

namespace paal {
namespace detail {

inline int vertices_column_index(int vertex, int dimentions, int column) {
    return vertex * dimentions + column;
}

template <class Graph>
using CostType = typename boost::property_traits<
    puretype(get(boost::edge_weight, std::declval<Graph>()))>::value_type;

template <typename LP> class multiway_cut_lp {
  public:
    /// Initialize the cut LP.
    template <typename Graph, typename IndexMap, typename WeightMap,
              typename ColorMap>
    void init(const Graph &graph, int k, const IndexMap &index_map,
              const WeightMap &weight_map, const ColorMap &color_map) {

        m_lp.set_lp_name("Multiway Cut");
        m_lp.set_optimization_type(lp::MINIMIZE);

        add_variables(graph, k, weight_map);
        add_constraints(graph, k, index_map, color_map);
    }

  private:
    // adding variables
    // returns the number of variables
    template <typename Graph, typename WeightMap>
    void add_variables(const Graph &graph, int k, const WeightMap &weight_map) {
        for (auto e : boost::as_array(edges(graph))) {
            for (int i = 0; i < k; ++i) {
                auto col_idx = m_lp.add_column(get(weight_map, e));
                edges_column.push_back(col_idx);
            }
        }
        for (unsigned vertex = 0; vertex <= num_vertices(graph); ++vertex) {
            for (int i = 0; i < k; ++i) {
                auto col_idx = m_lp.add_column(0);
                vertices_column.push_back(col_idx);
            }
        }
    }

    template <typename Graph, typename IndexMap, typename ColorMap>
    void add_constraints(const Graph &graph, int k, const IndexMap &index_map,
                         const ColorMap &color_map) {
        int db_index = 0;
        for (auto edge : boost::as_array(edges(graph))) {
            auto sour = get(index_map, source(edge, graph));
            auto targ = get(index_map, target(edge, graph));
            for (auto i : irange(k)) {
                for (auto j : irange(2)) {
                    auto x_e =
                        edges_column[vertices_column_index(db_index, k, i)];
                    auto x_src =
                        vertices_column[vertices_column_index(sour, k, i)];
                    auto x_trg =
                        vertices_column[vertices_column_index(targ, k, i)];
                    m_lp.add_row(
                        x_e + (j * 2 - 1) * x_src + (1 - 2 * j) * x_trg >= 0);
                }
            }
            ++db_index;
        }
        db_index = 0;
        for (auto vertex : boost::as_array(vertices(graph))) {
            auto col = get(color_map, vertex);
            if (col != 0) {
                auto x_col = vertices_column[
                    vertices_column_index(db_index, k, col - 1)];
                m_lp.add_row(x_col == 1);
            }
            lp::linear_expression expr;
            for (auto i : irange(k)) {
                expr += vertices_column[vertices_column_index(db_index, k, i)];
            }
            m_lp.add_row(std::move(expr) == 1);
            ++db_index;
        }
    }

  public:
    LP m_lp;
    std::vector<lp::col_id> edges_column;
    std::vector<lp::col_id> vertices_column;
};

template <typename Graph, typename VertexIndexMap, typename EdgeWeightMap,
          typename Dist, typename Rand, typename LP>
auto make_cut(const Graph &graph, int k, const VertexIndexMap &index_map,
              const EdgeWeightMap &weight_map, Dist &dist, Rand &&random_engine,
              multiway_cut_lp<LP> &mc_lp, std::vector<int> &vertex_to_part)
    ->detail::CostType<Graph> {
    double cut_cost = 0;
    std::vector<double> random_radiuses;
    dist.reset();
    for (int i = 0; i < k; ++i) {
        random_radiuses.push_back(dist(random_engine));
    }
    vertex_to_part.resize(num_vertices(graph));
    auto get_column = [&](int vertex, int dimension) {
        return mc_lp.m_lp.get_col_value(
            mc_lp.vertices_column[vertices_column_index(vertex, k, dimension)]);
    };

    for (auto vertex : boost::as_array(vertices(graph))) {
        for (int dimension = 0; dimension < k; ++dimension)
            if (1.0 - get_column(get(index_map, vertex), dimension) <
                    random_radiuses[dimension] ||
                dimension == k - 1) {
                // because each vertex have sum of coordinates equal 1,
                // 1.0-get_column(vertex,dimension) is proportional to distance
                // to vertex correspond to dimension
                vertex_to_part[get(index_map, vertex)] = dimension;
                break;
            }
    }
    for (auto edge : boost::as_array(edges(graph))) {
        if (vertex_to_part[get(index_map, source(edge, graph))] !=
            vertex_to_part[get(index_map, target(edge, graph))])
            cut_cost += get(weight_map, edge);
    }
    return cut_cost;
}

template <typename Rand = std::default_random_engine,
          typename Distribution = std::uniform_real_distribution<double>,
          typename LP = lp::glp, typename Graph, typename OutputIterator,
          typename VertexIndexMap, typename EdgeWeightMap,
          typename VertexColorMap>
auto multiway_cut_dispatch(const Graph &graph, OutputIterator result,
                           Rand &&random_engine, int iterations,
                           VertexIndexMap index_map, EdgeWeightMap weight_map,
                           VertexColorMap color_map)
    ->typename boost::property_traits<EdgeWeightMap>::value_type {
    using CostType = detail::CostType<Graph>;
    Distribution dis(0, 1);
    int terminals = 0;
    for (auto vertex : boost::as_array(vertices(graph))) {
        assign_max(terminals, get(color_map, vertex));
    }
    detail::multiway_cut_lp<LP> multiway_cut_lp;
    multiway_cut_lp.init(graph, terminals, index_map, weight_map, color_map);
    multiway_cut_lp.m_lp.solve_simplex(lp::DUAL);
    CostType cut_cost = std::numeric_limits<CostType>::max();
    std::vector<int> best_solution;
    std::vector<int> solution;
    for (int i = 0; i < iterations; ++i) {
        solution.clear();
        int res = detail::make_cut(graph, terminals, index_map, weight_map, dis,
                                   random_engine, multiway_cut_lp, solution);
        if (res < cut_cost) {
            swap(solution, best_solution);
            cut_cost = res;
        }
    }
    for (auto v : boost::as_array(vertices(graph))) {
        *result = std::make_pair(v, best_solution[get(index_map, v)]);
        ++result;
    }
    return cut_cost;
}

template <typename Rand = std::default_random_engine,
          typename Distribution = std::uniform_real_distribution<double>,
          typename LP = lp::glp, typename Graph, typename OutputIterator,
          typename VertexIndexMap, typename EdgeWeightMap,
          typename VertexColorMap>
auto multiway_cut_dispatch(const Graph &graph, OutputIterator result,
                           Rand &&random_engine, boost::param_not_found,
                           VertexIndexMap index_map, EdgeWeightMap weight_map,
                           VertexColorMap color_map)
    ->typename boost::property_traits<EdgeWeightMap>::value_type {
    int vertices = num_vertices(graph);
    const static int MIN_NUMBER_OF_REPEATS = 100;
    auto number_of_repeats =
        vertices * vertices +
        MIN_NUMBER_OF_REPEATS; // This variable is not supported by any proof
    return multiway_cut_dispatch(graph, result, random_engine,
                                 number_of_repeats, index_map, weight_map,
                                 color_map);
}

} //!detail

/**
 * @brief this is solve multiway_cut problem
 * and return cut_cost
 * example:
 *  \snippet multiway_cut_example.cpp  Multiway Cut Example
 * @param Graph graph
 * @param OutputIterator result pairs of vertex descriptor and number form (1,2,
 * ... ,k) id of part
 * @param random_engine
 * @param params
 * @tparam Graph
 * @tparam OutputIterator
 * @tparam Rand random engine
 * @tparam Distribution used to chose random radius
 * @tparam LP
 * @tparam P
 * @tparam T
 * @tparam R
 */
template <typename Rand = std::default_random_engine,
          typename Distribution = std::uniform_real_distribution<double>,
          typename LP = lp::glp, typename Graph, typename OutputIterator,
          typename P, typename T, typename R>
auto multiway_cut(const Graph &g, OutputIterator out,
                  const boost::bgl_named_params<P, T, R> &params,
                  Rand &&random_engine = std::default_random_engine(5426u))
    ->typename boost::property_traits<puretype(
          boost::choose_const_pmap(get_param(params, boost::edge_weight), g,
                                   boost::edge_weight))>::value_type {
    return detail::multiway_cut_dispatch(
        g, out, random_engine, get_param(params, boost::iterations_t()),
        boost::choose_const_pmap(get_param(params, boost::vertex_index), g,
                                 boost::vertex_index),
        boost::choose_const_pmap(get_param(params, boost::edge_weight), g,
                                 boost::edge_weight),
        boost::choose_const_pmap(get_param(params, boost::vertex_color), g,
                                 boost::vertex_color));
}

/**
 * @brief this is solve multiway_cut problem
 * and return cut_cost
 * example:
 *  \snippet multiway_cut_example.cpp  Multiway Cut Example
 *
 * example file is  multiway_cut_example.cpp
 * @param Graph graph
 * @param int repeats number of sets of radius
 * @param OutputIterator result pairs of vertex descriptor and number form (1,2,
* ... ,k) id of part
 * @tparam Rand random engine
 * @tparam Distribution used to chose random radius
 * @tparam LP
 * @tparam Graph
 * @tparam OutputIterator
 */
template <typename Rand = std::default_random_engine,
          typename Distribution = std::uniform_real_distribution<double>,
          typename LP = lp::glp, typename Graph, class OutputIterator>
auto multiway_cut(const Graph &graph, OutputIterator result,
                  Rand random_engine = std::default_random_engine(5426u))
    ->detail::CostType<Graph> {
    return multiway_cut(graph, result, boost::no_named_parameters(),
                        random_engine);
}

}      //!paal
#endif // PAAL_MULTIWAY_CUT_HPP


#include "dSFMTsearch.hpp"
#include "Annihilate.h"
#include <errno.h>
#include <stdlib.h>
#include <getopt.h>
#include <errno.h>
#include <NTL/GF2X.h>
#include <NTL/GF2XFactoring.h>
#include <NTL/vector.h>
#include <MTToolBox/period.hpp>
#include <MTToolBox/AlgorithmReducibleRecursionSearch.hpp>
#include <MTToolBox/AlgorithmCalculateParity.hpp>
#include "calc_parity.h"

using namespace MTToolBox;
using namespace NTL;
using namespace std;

w128_t calc_parity(dSFMT& dsfmt, const GF2X& irreducible)
{
    AlgorithmCalculateParity<w128_t, dSFMT> cp;
    cp.searchParity(dsfmt, irreducible);
    return dsfmt.getParityValue();
}