/**

\file
\author Datta Ramadasan
//==============================================================================
//         Copyright 2015 INSTITUT PASCAL UMR 6602 CNRS/Univ. Clermont II
//
//          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 __LMA_OPT2_BA_CREATE_HESSIAN_HPP__
#define __LMA_OPT2_BA_CREATE_HESSIAN_HPP__

#include "make_type.hpp"

#include <libv/lma/ttt/mpl/for.hpp>
#include <libv/lma/lm/function/function.hpp>
#include <libv/lma/ttt/fusion/pair.hpp>
#include <libv/lma/ttt/mpl/naming.hpp>
#include <libv/lma/ttt/mpl/cat.hpp>
#include <boost/mpl/unique.hpp>
#include <boost/mpl/count_if.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/or.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/replace.hpp>
#include <boost/mpl/copy_if.hpp>
#include <boost/type_traits/is_same.hpp>

namespace lma
{

  // extract parameters
  template<class L1> using ListOfListOfParameters = mpl::transform<L1,Function<mpl::_1>>;

  template<class A, class B> struct MakePair
  {
    typedef bf::pair<A,B> type;
  };

  template<class Int_, class List, class Int, class Result> struct CreatePairs3_
  : mpl::push_back<
                  Result,
                  typename MakePair<typename mpl::at<List,Int_>::type,typename mpl::at<List,Int>::type>::type
                  > {};

  template<class Int> using CreatePairs3 = CreatePairs3_<Int,mpl::_1,mpl::_2,mpl::_3>;

  template<class List, class Int, class Result> struct CreatePairs2_ : For<Int::value+1,mpl::size<List>::value,List,CreatePairs3<Int>,Result> {};

  template<class L> using CreatePairs2 = CreatePairs2_<L,mpl::_2,mpl::_3>;

  template<class T> struct Unique : mpl::unique<T, boost::is_same<mpl::_1,mpl::_2> > {};// ne retire que les doublons contigus

  // L ne doit pas contenir de doublon
  template<class L> struct CreatePairs : For<0,mpl::size<L>::value,L,CreatePairs2<L>> {};

  // get cross parameters
  template<class L2> using CrossParameters = mpl::transform<typename mpl::transform<L2,Unique<mpl::_1>>::type,CreatePairs<mpl::_1>>;

  template<class T> struct Double {};
  template<class T> struct Single {};

  template<class T, class L> struct SingleOrDouble : mpl::if_c<(mpl::count_if<L,boost::is_same<mpl::_1,T>>::value>1),Double<T>,Single<T>> {};

  template<class L> struct DiagHessianFunctor : mpl::transform<typename Unique<L>::type,SingleOrDouble<mpl::_1,L>> {};

  template<class L2> using ToSingleOrDouble = mpl::transform<L2,DiagHessianFunctor<mpl::_1>>;

  template<class L3, class L4> using CatCrossAndDiag = mpl::transform<L4,L3,Cat<mpl::_1,mpl::_2>>;

  template<class L, class T> struct AddIf;

  template<class L, class A, class B> struct AddIf<L,bf::pair<A,B>>
  : mpl::if_<
              mpl::or_<
                      mpl::contains<L,bf::pair<A,B>>,
                      mpl::contains<L,bf::pair<B,A>>
                      >,
              L,
              typename mpl::push_back<L,bf::pair<A,B>>::type
            > {};

  template<class L, class T> struct AddIf<L,Single<T>>
  : mpl::if_<
              mpl::or_<
                        mpl::contains<L,Single<T>>,
                        mpl::contains<L,Double<T>>
                      >,
              L,
              typename mpl::push_front<L,Single<T>>::type
            > {};

  template<class L, class T> struct AddIf<L,Double<T>>
  :  mpl::if_<
              boost::mpl::contains<L,Single<T>>,
              typename mpl::replace<L,Single<T>,Double<T>>::type,
              typename mpl::push_front<L,Double<T>>::type
            > {};

  template<class List, class Int, class Result> struct ForEachParameters : AddIf<Result,typename mpl::at<List,Int>::type> {};

  template<class List, class Int, class Result> struct ForEachFunctor :
  For<0,mpl::size<typename mpl::at<List,Int>::type>::value,typename mpl::at<List,Int>::type,ForEachParameters<mpl::_1,mpl::_2,mpl::_3>,Result> {};

  template<class L> using TypesContainers = For<0,mpl::size<L>::value,L,ForEachFunctor<mpl::_1,mpl::_2,mpl::_3>>;

  template<class T, class Flt> struct ToTable;

  template<class A, class Flt> struct ToTable<Single<A>,Flt>// : MakeTupleTable<A,A,Flt> {
  {
    typedef bf::pair<bf::pair<A,A>,Table<A,A,Flt,Diagonal>> type;
  };

  template<class A, class Flt> struct ToTable<Double<A>,Flt>
  {
    typedef bf::pair<bf::pair<A,A>,Table<A,A,Flt,Symetric>> type;
  };

  template<class A, class B, class Flt> struct ToTable<bf::pair<A,B>,Flt> : MakeTupleTable<A,B,Flt> {};

  template<class L, class Flt> using Containers = mpl::transform<L,ToTable<mpl::_1,Flt>>;

  template<class Result, class A, class B> struct add_ { typedef Result type; };
  template<class Result, class A> struct add_<Result,A,Single<A>> : mpl::push_back<Result,Single<A>> {};
  template<class Result, class A> struct add_<Result,A,Double<A>> : mpl::push_back<Result,Double<A>> {};
  template<class Result, class A, class B> struct add_<Result,A,bf::pair<A,B>> : mpl::push_back<Result,bf::pair<A,B>> {};
  template<class Elt, class L, class Int, class Result> struct Ordering2 : add_<Result,Elt,typename mpl::at<L,Int>::type> {};

  template<class L, class Ordre, class Int, class Result> struct Ordering : For<0,mpl::size<L>::value,L,Ordering2<typename mpl::at<Ordre,Int>::type,mpl::_1,mpl::_2,mpl::_3>,Result> {};
  template<class L, class Ordre> struct Order : For<0,mpl::size<Ordre>::value,Ordre,Ordering<L,mpl::_1,mpl::_2,mpl::_3>> {};

  template<class Keys, class T> struct IsKeyUs : mpl::false_ { };
  template<class Keys, class Key, class P, class Q, class T, class Tag> struct IsKeyUs<Keys, bf::pair< Key, Table<P,Q,T,Tag> >>
    : mpl::and_< mpl::contains<Keys,P>, mpl::contains<Keys,Q> > { };

  template<class T> struct SymetricToDiagonal { typedef T type; };
  template<class Key, class P, class T> struct SymetricToDiagonal<bf::pair< Key, Table<P,P,T,Diagonal> >>  { typedef bf::pair< Key, Table<P,P,T,Symetric> > type; };

  template<class H, class KeyUs> struct ListS
  {
    typedef typename mpl::copy_if<H, IsKeyUs<KeyUs,mpl::_1>>::type type0;
    typedef typename mpl::transform<type0, SymetricToDiagonal<mpl::_1>>::type type;
  };

  template<class Bundle, class flt> struct ListH
  {
    typedef typename Bundle::ListFunction ListFunction;
    typedef typename Bundle::ListeParam ListeParam;
    typedef typename Bundle::ParamFonctor ParamFonctor;
    typedef ListFunction L1;

//       typedef typename CreateListParam<ListFunction,ParamFonctor>::type parameters;

      typedef typename ListOfListOfParameters<L1>::type L2_;
      typedef typename mpl::transform<L2_,ParamFonctor>::type L2;

      typedef typename CrossParameters<L2>::type L3;

      typedef typename ToSingleOrDouble<L2>::type L4;

      typedef typename CatCrossAndDiag<L3,L4>::type L5;

      typedef typename TypesContainers<L5>::type L6;

      typedef typename Order<L6,ListeParam>::type L7;

      typedef typename Containers<L7,flt>::type type;

      static void disp()
      {
        std::cout << " Functors : " << ttt::name<L1>() << std::endl;
        std::cout << " ListOfListOfParameters : " << ttt::name<L2>() << std::endl;
        std::cout << " ToSingleOrDouble : " << ttt::name<L4>() << std::endl;
        std::cout << std::endl;
        std::cout << " CrossAndDiag : " << ttt::name<L5>() << std::endl;
        std::cout << std::endl;
        std::cout << " Types : " << ttt::name<L6>() << std::endl;
        std::cout << " Order  : " << ttt::name<L7>() << std::endl;
        std::cout << " Containers  : " << ttt::name<type>() << std::endl;
//         std::cout << " Parmaeters " << ttt::name<ListeParam>() << std::endl;
      }
  };

}

namespace ttt
{
  template<class T> struct Name<lma::Single<T>> { static std::string name() { return std::string("Single<") + ttt::name<T>() + ">"; } };
  template<class T> struct Name<lma::Double<T>> { static std::string name() { return std::string("Double<") + ttt::name<T>() + ">"; } };
}

#endif


#include <exception>
#include <iostream>
#include <set>
#include <string>

#include <boost/format.hpp>
#include <boost/program_options.hpp>

#include <nit/eval/card.h>
#include <nit/eval/card_set.h>
#include <nit/eval/card_set_generators.h>
#include <nit/eval/make_evaluator.h>
#include <nit/eval/poker_hand_evaluator.h>
#include <nit/util/combinations.h>

#include "guard.h"

namespace po = boost::program_options;

namespace {

int runLut(int argc, char** argv) {
  // set up the program options,
  // handle the help case, and extract the values
  po::options_description desc("creates a lookup table for poker hands");
  // clang-format off
  desc.add_options()
      ("help,?", "produce help message")
      ("pocket-count,p", po::value<size_t>()->default_value(2),
       "number of pocket cards to use")
      ("board-count,b", po::value<size_t>()->default_value(3),
       "number of board cards to use")
      ("game,g", po::value<std::string>()->default_value("O"),
       "game to use for evaluation")
      ("ranks", "print the set of rank values");
  // clang-format on

  po::variables_map vm;
  try {
    po::store(po::parse_command_line(argc, argv, desc), vm);
  } catch (const std::exception& err) {
    std::cerr << "Option error: " << err.what() << "\n\n" << desc << std::endl;
    return 1;
  }
  po::notify(vm);

  // check for help
  if (vm.count("help") || argc == 1) {
    std::cout << desc << std::endl;
    return 1;
  }

  // extract the options
  size_t pocketCount = vm["pocket-count"].as<size_t>();
  size_t boardCount = vm["board-count"].as<size_t>();
  std::string game = vm["game"].as<std::string>();

  bool ranks = vm.count("ranks") > 0;
  // make the sets
  nit::Card::Grouping grouping = nit::Card::SUIT_CANONICAL;
  if (ranks)
    grouping = nit::Card::RANK;
  std::set<nit::CardSet> pockets = nit::createCardSet(pocketCount, grouping);
  std::set<nit::CardSet> boards = nit::createCardSet(boardCount, grouping);

  std::unique_ptr<nit::PokerHandEvaluator> evaluator = nit::makeEvaluator(game);
  for (const auto& pocket : pockets)
    for (const auto& board : boards) {
      if (ranks) {
        nit::PokerEvaluation eval = evaluator->evaluateRanks(pocket, board);
        // cout << boost::format("%s, %s\n") % pit->rankstr() %
        // bit->rankstr();

        // clang-format off
        std::cout << boost::format("%s, %s: [%4d,%4d] -> %s [%9d]\n") %
                         pocket.str() %
                         board.str() %
                         pocket.rankColex() %
                         board.rankColex() %
                         eval.str() %
                         eval.code();
        // clang-format on
      } else {
        board.canonize(pocket);
        if (pocket.intersects(board))
          continue;
        nit::PokerHandEvaluation eval = evaluator->evaluate(pocket, board);
        // clang-format off
        std::cout << boost::format("%s, %s: [%4d,%4d] -> %s [%9d]\n") %
                         pocket.str() %
                         board.str() %
                         pocket.colex() %
                         board.colex() %
                         eval.str() %
                         eval.high().code();
        // clang-format on
      }
    }

  return 0;
}

}  // namespace

int main(int argc, char** argv) {
  return guard([&argc, &argv] { return runLut(argc, argv); });
}


#include "Sampler.h"
#include "ConnSampler.h"
#include "SamplerMulti.h"
#include "ConnSamplerMulti.h"

#include <boost/program_options.hpp>
#include <random>
#include <string>
#include <limits>
#include <iostream>
#include <fstream>

namespace po = boost::program_options;
using namespace CDS;
using namespace std;

int main(int argc, char *argv[]) {

    try {

        // Set up program options

        po::options_description desc("Allowed options");
        desc.add_options()
            ("help,h", "produce help message")
            ("file,f",        po::value<string>(),                    "file containing degree sequence")
            ("degrees,d",   po::value<vector<deg_t>>()->multitoken(), "degree sequence")
            ("connected,c", po::bool_switch(),                        "generate connected graphs")
            ("multi,m",     po::bool_switch(),                        "generate loop-free multigraphs")
            ("alpha,a",     po::value<double>()->default_value(1.0),  "set parameter for the heuristic")
            ("count,n",     po::value<long>()->default_value(1L),     "how many graphs to generate")
            ("seed,s",      po::value<long>(),                        "set random seed")
        ;

        po::positional_options_description p;
        p.add("file", 1);

        po::variables_map vm;
        po::store(po::command_line_parser(argc, argv).
                  options(desc).positional(p).run(), vm);
        po::notify(vm);

        if (vm.count("help") || ! (vm.count("file") || vm.count("degrees"))) {
            if (! vm.count("help"))
                cerr << "Error: No degree sequence was given!\n";

            cout << "Usage:\n"
                 << argv[0] << " input_file\n"
                 << argv[0] << " --degrees d1 d2 d3\n\n"
                 << desc << "\n";

            if (vm.count("help"))
                return 0;
            else
                return 1;
        }

        if (vm.count("file") && vm.count("degrees")) {
            cerr << "Error: On the command line, provide either an input file, or an explicit degree sequence, but not both!\n";
            return 1;
        }

        // Read program options and degree sequence

        double alpha = vm["alpha"].as<double>();
        long n = vm["count"].as<long>();

        vector<deg_t> degrees;

        if (vm.count("file")) {
            ifstream dsfile(vm["file"].as<string>());

            if (! dsfile) {
                cerr << "Error: Could not open " << vm["file"].as<string>() << "!\n";
                return 1;
            }

            deg_t d;
            while (dsfile >> d)
                degrees.push_back(d);

            if (! dsfile.eof()) {
                cerr << "Error: Unexpected input in degree sequence file!\n";
                return 1;
            }
        } else {
            degrees = vm["degrees"].as<vector<deg_t>>();
        }

        // Set up random number generator

        mt19937 rng(random_device{}());
        if (vm.count("seed"))
            rng.seed(vm["seed"].as<long>());

        // Generate samples

        // Ensure that no precision is lost when printing weight values
        cout.precision(numeric_limits<double>::max_digits10);

        for (; n > 0; --n) {
            edgelist_t edges;
            double logprob;

            if (vm["multi"].as<bool>()) {
                if (vm["connected"].as<bool>()) {
                    DegreeSequenceMulti ds(degrees.begin(), degrees.end());
                    tie(edges, logprob) = sample_conn_multi(ds, alpha, rng);
                } else {
                    DegreeSequenceMulti ds(degrees.begin(), degrees.end());
                    tie(edges, logprob) = sample_multi(ds, alpha, rng);
                }
            } else {
                if (vm["connected"].as<bool>()) {
                    DegreeSequence ds(degrees.begin(), degrees.end());
                    tie(edges, logprob) = sample_conn(ds, alpha, rng);
                } else {
                    DegreeSequence ds(degrees.begin(), degrees.end());
                    tie(edges, logprob) = sample(ds, alpha, rng);
                }
            }

            cout << logprob << '\n';
            for (const auto &edge : edges) {
                // 'edges' uses 0-based indexing. Increment vertex names to output with 1-based indexing.
                cout << edge.first+1 << '\t' << edge.second+1 << '\n';
            }

            cout << "\n";
        }
    }
    catch(exception& e) {
        cerr << "Error: " << e.what() << "\n";
        return 1;
    }
    catch(...) {
        cerr << "Exception of unknown type!\n";
    }

    return 0;
}


/*
 dparallel_recursion: distributed parallel_recursion skeleton
 Copyright (C) 2015-2016 Carlos H. Gonzalez, Basilio B. Fraguela. Universidade da Coruna

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

///
/// \file     quicksort.cpp
/// \author   Carlos H. Gonzalez  <cgonzalezv@udc.es>
/// \author   Basilio B. Fraguela <basilio.fraguela@udc.es>
///

/*
 *  The initial input is only located in the root.
 *  The result is left distributed.
 */

#include <cstdlib>
#include <algorithm>
#include <sys/time.h>
#include <dparallel_recursion/dparallel_recursion.h>

//#include <boost/serialization/split_member.hpp>

using namespace dpr;

size_t n = 100000000;
int nthreads = 8;
int nprocs;
int tasks_per_thread = 1;

/*
#include <iostream>
 void print(int* a, int size) {
 for (int i = 0; i < size; i++) {
   std::cout << a[i] << std::endl;
 }
   std::cout << std::endl;
 }
*/

//namespace ser = boost::serialization;

struct range_t {

	int *begin;
	size_t size, i;

	range_t(int *begin_ = nullptr, size_t size_ = 0)
		: begin(begin_), size(size_)
                { }

	void partition() {
		int *array = begin;
		int *key0 = begin;
		size_t m = size / 2u;
		std::swap ( array[0], array[m] );

		i = 0;
		size_t j = size;
		// Partition interval [i+1,j-1] with key *key0.
		for (;;) {
			// Loop must terminate since array[l]==*key0.
			do {
				--j;
			} while ( *key0 > array[j] );
			do {
				if ( i == j ) goto partition;
				++i;
			} while ( array[i] > *key0 );
			if ( i == j ) goto partition;
			std::swap( array[i], array[j] );
		}
partition:
		// Put the partition key where it belongs
		std::swap( array[j], *key0 );
		// array[l..j) is less or equal to key.
		// array(j..r) is std::greater than or equal to key.
		// array[j] is equal to key
		i = j + 1;
	}

	range_t child(int nchild) {
		//return range_t(begin + nchild * i, nchild ? (size - i) : j);
		return nchild ? range_t(begin + i, size - i) : range_t(begin, i);
	}

        /*
	template<typename Archive>
	void save(Archive &ar, const unsigned int) const {
		ar & size;
		ar & ser::make_array(begin, size);
	}

	template<typename Archive>
	void load(Archive &ar, const unsigned int) {
		ar & size;

		if (!begin) {
			begin = (int *)malloc(sizeof(int) * size);
		}

		ar & ser::make_array(begin, size);
	}

	BOOST_SERIALIZATION_SPLIT_MEMBER()
        */

       template<class Archive>
       void serialize(Archive & ar, const unsigned int version)
       { }

       template<typename Archive>
       void gather_scatter(Archive &ar) {
                ar & begin & size;
       }
};

TRANSMIT_BY_CHUNKS(range_t);

struct QSinfo: public DInfo<range_t, 2> {

  QSinfo() : DInfo<range_t, 2>(nthreads * tasks_per_thread)
  {}

  static bool is_base(range_t &r) {
    //printf("%lu <= %lu (%d %d %d) = %d\n", r.size, (n / (4 * nthreads * nprocs * tasks_per_thread)), nthreads, nprocs, tasks_per_thread, (r.size <= (n / (4 * nthreads * nprocs * tasks_per_thread))));
    //return r.size <= (n / (4 * nthreads * nprocs * tasks_per_thread));
    return r.size <= 10000;
  }

  static range_t child(int i, range_t &r) {
    return r.child(i);
  }

  static float cost(const range_t &r) noexcept {
    return (float)r.size;
  }

};

struct QS : public EmptyBody<range_t, void> {

  static void base(range_t &r) {
    //printf(" sorts %lu elems\n", r.size);
    std::sort(r.begin, r.begin + r.size, std::greater<int> ());
  }

  static void pre_rec(range_t &r) {
    r.partition();
  }

};

int main(int argc, char **argv) {
	int rank, provided;
        struct timeval t0, t1, t;

        MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided);
        assert(provided >= MPI_THREAD_SERIALIZED);
        MPI_Comm_rank(MPI_COMM_WORLD, &rank);
        MPI_Comm_size(MPI_COMM_WORLD, &nprocs);

	if (getenv("OMP_NUM_THREADS"))
		nthreads = atoi(getenv("OMP_NUM_THREADS"));

	pr_init(nthreads);

	if (argc > 1)
		n = (size_t) strtoull(argv[1], NULL, 0);
	if (argc > 2)
		tasks_per_thread = atoi(argv[2]);

	int *data = (int *) malloc(n * sizeof(int)); //BBF: Actually only needed in root
        if (!rank) {
          srand(1234);
          for (size_t i=0; i<n; i++)
            data[i] = rand();
        }

        MPI_Barrier(MPI_COMM_WORLD); //So that we all measure from the same point
	gettimeofday(&t0, NULL);

        range_t tmp(data, n);
        QSinfo qsinfo;
	dparallel_recursion<void> (tmp, qsinfo, QS(), partitioner::automatic(), DistributedOutput|Scatter|UseCost);

        MPI_Barrier(MPI_COMM_WORLD); //So that we all measure up to the same point
	gettimeofday(&t1, NULL);
	timersub(&t1, &t0, &t);

        if(rank==0) {
                printf("Nprocs=%d threads=%d task_per_thread=%d\n", nprocs, nthreads, tasks_per_thread);
                printf("Problem size: %lu\n", n);
		printf("compute time: %f\n", t.tv_sec + t.tv_usec / 1000000.0);
        }
	MPI_Finalize();
	return 0;
}


// Copyright University of Warwick 2014
// 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)

// Authors:
// John Back

/*! \file LpcPoint.hh
    \brief File containing the declaration of the LpcPoint class
*/

/*! \class LpcPoint
    \brief Class that defines the scaled and unscaled co-ordinates of a point in the LpcAbsCurve
*/

#ifndef LPC_POINT_HH
#define LPC_POINT_HH

#include <Eigen/Dense>
#include <iostream>

class LpcPoint  {

public:

    //! Default, empty constructor
    LpcPoint();

    //! Constructor using an Eigen VectorXd containing the scaled co-ordinates
    /*!
      \param [in] index The lpc point index number
      \param [in] scaledCoords The Eigen vector containing the scaled co-ordinates
    */
    LpcPoint(int index, const Eigen::VectorXd& scaledCoords);

    //! Constructor using an Eigen VectorXd containing the scaled & unscaled co-ordinates
    /*!
      \param [in] index The lpc point index number
      \param [in] scaledCoords The Eigen vector containing the scaled co-ordinates
      \param [in] coords The Eigen vector containing the unscaled co-ordinates
    */
    LpcPoint(int index, const Eigen::VectorXd& scaledCoords,
	     const Eigen::VectorXd& coords);

    //! Copy constructor
    /*!
      \param [in] other The LpcPoint to copy
    */
    LpcPoint(const LpcPoint& other);

    //! Assignment operator using another LpcPoint
    /*!
      \param [in] other The LpcPoint used for assignment
    */
    LpcPoint& operator=(const LpcPoint& other);

    //! Destructor
    virtual ~LpcPoint();

    //! Find the unscaled co-ordinates using an Eigen vector
    /*!
      \param [in] theRange The scaling range as an Eigen vector
    */
    void unscale(const Eigen::VectorXd& theRange);

    // Accessor functions

    //! Get the index number of the lpc point
    /*!
      \return the index number of the lpc point
    */
    int getIndex() const {return index_;}

    //! Return the number of co-ordinate dimensions
    /*!
      \return the number of co-ordinate dimensions
    */
    int getNDimensions() const {return nDim_;}

    //! Return the scaled co-ordinates as an Eigen vector object
    /*!
      \return the scaled co-ordinates as an Eigen vector
    */
    Eigen::VectorXd getScaledCoords() const {return scaledCoords_;}

    //! Return the unscaled co-ordinates as an Eigen vector object
    /*!
      \return the unscaled co-ordinates as an Eigen vector
    */
    Eigen::VectorXd getCoords() const {return coords_;}

    //! Overload the ostream insertion operator to print out the LpcPoint info
    /*!
      \param [in] os The string stream
      \param [in] thePoint The LpcPoint object to print out
    */
    friend std::ostream& operator << (std::ostream& os, const LpcPoint& thePoint);

protected:

    //! The integer index number
    int index_;

    //! The number of co-ordinate dimensions
    int nDim_;

    //! The scaled co-ordinates
    Eigen::VectorXd scaledCoords_;

    //! The unscaled co-ordinates
    Eigen::VectorXd coords_;

private:

};

#endif


#ifndef VPYTON_PYTHON_VECTOR_ARRAY_HPP
#define VPYTHON_PYTHON_VECTOR_ARRAY_HPP

// This alternative to numpy is not currently being used
// but is retained in CVS for possible future use.
// In particular, information about array objects such
// as curve cannot currently be cached because we don't
// know when a numpy pos array has been changed.

// Copyright (c) 2003, 2004 Jonathan Brandmeyer.
// See the file license.txt for complete license terms.
// See the file authors.txt for a complete list of contributors.

#include <deque>
#include "util/vector.hpp"

#include <boost/python/numeric.hpp>

namespace cvisual { namespace python {

class scalar_array;

class vector_array
{
 private:
	std::deque<vector> data;
	friend class scalar_array;

 public:
	typedef std::deque<vector>::iterator iterator;
	typedef std::deque<vector>::const_iterator const_iterator;

	vector_array( int size = 0, vector fill = vector())
		: data( size, fill){}

	// Construct from a list of three-element tuples.
	explicit vector_array( const boost::python::list&);
	vector_array( const vector_array& v)
		: data( v.data) {}

	explicit vector_array( boost::python::numeric::array);

	inline iterator
	begin()
	{ return data.begin(); }

	inline const_iterator
	begin() const
	{ return data.begin(); }

	inline iterator
	end()
	{ return data.end(); }

	inline const_iterator
	end() const
	{ return data.end(); }

	// Append a single vector (or vector represented as a tuple) to the array.
	void append( const vector& v);
	void append( const vector_array& va);

	// Prepend a single vector (or vector represented as a tuple) the the array.
	void prepend( const vector& v);

	// Remove a single element from the beginning of the array
	void head_clip();
	// Remove i elemnts from the beginning of the array.
	void head_crop( int i);
	// Remove a single element from the end of the array.
	void tail_clip();
	// Remove i elements from the end of the array.
	void tail_crop( int i);

	// Scalar operations
	vector_array
	operator*( double s) const;

	vector_array
	operator*( const scalar_array& s) const;

	vector_array
	operator*( vector s) const;

	vector_array
	operator/( double s) const;

	vector_array
	operator/( const scalar_array& s) const;

	vector_array
	operator-() const;

	const vector_array&
	operator*=( double s);

	const vector_array&
	operator*=( const scalar_array& s);

	const vector_array&
	operator/=( double s);

	const vector_array&
	operator/=( const scalar_array& s);

	// Vector operations
	vector_array
	operator+( const vector& v) const;

	vector_array
	operator-( const vector& v) const;

	vector_array
	operator+( const vector_array& v) const;

	vector_array
	operator-( const vector_array& v) const;

	const vector_array&
	operator+=( const vector& v);

	const vector_array&
	operator-=( const vector& v);

	const vector_array&
	operator+=( const vector_array& v);

	const vector_array&
	operator-=( const vector_array& v);

	// Compounded operations
	vector_array
	cross( const vector& v);

	vector_array
	cross( const vector_array& v);

	vector_array
	norm() const;

	scalar_array
	dot( const vector& v);

	scalar_array
	dot( const vector_array& v);

	vector_array
	proj( const vector_array& v);

	vector_array
	proj( const vector& v);

	void
	rotate( const double& angle, vector axis = vector(0,0,0));

	scalar_array
	mag() const;

	scalar_array
	mag2() const;

	scalar_array
	comp( const vector& v);

	scalar_array
	comp( const vector_array& v);

	// Returns the vector at the specified position.
	// Use the non-checked version for C++, and the checked version for python.
	inline vector&
	operator[]( int i) { return data[i]; }

	inline const vector&
	operator[]( int i) const { return data[i]; }

	// Returns the number of elemnts in the array.
	inline int
	size() const { return data.size(); }

	inline bool
	empty() const { return data.empty(); }

	vector&
	py_getitem( int index);

	void
	py_setitem( int index, vector value);

	scalar_array get_x() const;
	scalar_array get_y() const;
	scalar_array get_z() const;

	void set_x( const scalar_array&);
	void set_y( const scalar_array&);
	void set_z( const scalar_array&);

	void set_x( boost::python::numeric::array);
	void set_y( boost::python::numeric::array);
	void set_z( boost::python::numeric::array);

	// Force every element to be this single value.
	void set_x( double x);
	void set_y( double y);
	void set_z( double z);

	void set_x( const boost::python::list&);
	void set_y( const boost::python::list&);
	void set_z( const boost::python::list&);

	vector
	sum() const;

	// Support expressions matching vector_array = vector - vector_array.
	vector_array
	lhs_sub( const vector& v) const;

	// Array relational comparisons.  These operators return a vector_array whose x,y,and z members
	// are all either 0.0 or 1.1.
	vector_array
	operator>=( const double&) const;

	vector_array
	operator>=( const scalar_array&) const;

	vector_array
	operator>=( const vector_array&) const;

	vector_array
	operator<=( const double&) const;

	vector_array
	operator<=( const scalar_array&) const;

	vector_array
	operator<=( const vector_array&) const;

	vector_array
	fabs() const;

	// Element-wise multiplication...
	vector_array
	operator*( const vector_array& v) const;

	boost::python::handle<PyObject>
	as_array() const;
};

void wrap_vector_array();

// Return an ordered list of collisions detected between a set of spheres
// whose centers are encoded in pos, and radii are encoded in radius.
// This algorithm runs in O(n*n) time.
// The returned list is an ordered list of tuple indexes into the array pos.
// e.g., [(2,4)] indicates a collision between the second and fourth spheres.
boost::python::list
sphere_collisions( const vector_array& pos, const scalar_array& radius);

boost::python::list
sphere_to_plane_collisions( const vector_array& pos, const scalar_array& radius
                          , vector normal, vector origin);

// Support several expressions with a vector_array on the rhs
// Note that all of the scalar_array op vector_array expressions are covered
// in class scalar_array.
inline vector_array
operator*( double s, const vector_array& v)
{
	return v * s;
}

inline vector_array
operator-( const vector& v, const vector_array& v_a)
{
	return v_a.lhs_sub( v);
}

inline vector_array
operator+( const vector& v, const vector_array& v_a)
{
	return v_a + v;
}

} } // !namespace cvisual::python

#endif // !VISUAL_VECTOR_ARRAY_H


/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
// This file is a part of ChASE.
// Copyright (c) 2015-2021, Simulation and Data Laboratory Quantum Materials,
//   Forschungszentrum Juelich GmbH, Germany. All rights reserved.
// License is 3-clause BSD:
// https://github.com/ChASE-library/ChASE

#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <cmath>
#include <time.h>
#include <iomanip>
#include <chrono>
#include <cstdlib>

#include "algorithm/performance.hpp"
#include "ChASE-MPI/chase_mpi.hpp"

#include "ChASE-MPI/impl/chase_mpidla_blaslapack_seq.hpp"
#include "ChASE-MPI/impl/chase_mpidla_blaslapack_seq_inplace.hpp"
#include "ChASE-MPI/impl/chase_mpidla_blaslapack.hpp"

#ifdef DRIVER_BUILD_MGPU
#include "ChASE-MPI/impl/chase_mpidla_mgpu.hpp"
#endif

#include "scalapack_templates.hpp"

const int i_zero = 0, i_one = 1;
const std::size_t sze_one = 1;

typedef std::size_t DESC[ 9 ];

namespace po = boost::program_options;
namespace bf = boost::filesystem;

using namespace::chase;
using namespace::chase::mpi;

template <typename T>
std::vector<T> generateEyeMat(std::size_t N){
  int rank;
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  if (rank == 0) std::cout << "]> Generating an Identity matrix as S" << std::endl;

  std::vector<T> S(N * N, T(0.0));
  for (std::size_t i = 0; i < N; i++) {
    S[i + i * N] = 1.0;
  }

  return S;
}

template <typename T>
std::vector<T> generateClementMat(std::size_t N){
  int rank;
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  if (rank == 0) std::cout << "]> Generating a Clement matrix as H" << std::endl;

  std::vector<T> C(N * N, T(0.0));
  for (auto i = 0; i < N; ++i) {
    C[i + N * i] = 0;
    if (i != N - 1) C[i + 1 + N * i] = std::sqrt(i * (N + 1 - i));
    if (i != N - 1) C[i + N * (i + 1)] = std::sqrt(i * (N + 1 - i));
  }

  return C;
}

//write eigenvalues: path_out/eigenvalues_index.bin
//write eigenvetors: path_out/eigenvectors_index.bin
template <typename T>
void writeMatrix(T *H, std::string path_out, std::string prefix,
		       std::size_t index, std::string suffix, std::size_t size)
{
  std::ostringstream problem(std::ostringstream::ate);
  problem << path_out << prefix << "_" << index << suffix;

  std::cout << "]> writing ";
  std::cout << prefix << " of size into ";
  std::cout << problem.str() << std::endl;

  auto outfile = std::fstream(problem.str().c_str(), std::ios::out | std::ios::binary);

  outfile.write((char*)&H[0], size * sizeof(T));

  outfile.close();

}

//read eigenvalues: path_in/eigenvalues_index.bin
//read eigenvetors: path_in/eigenvectors_index.bin
template <typename T>
void readMatrix(T *H, std::string path_in, std::string prefix,
                       std::size_t index, std::string suffix, std::size_t size)
{
  std::ostringstream problem(std::ostringstream::ate);
  problem << path_in << prefix << "_" << index << suffix;

  std::cout << "]> reading ";
  std::cout << prefix << " of size from ";
  std::cout << problem.str() << std::endl;

  auto infile = std::fstream(problem.str().c_str(), std::ios::binary);

  infile.write((char*)&H[0], size * sizeof(T));

  infile.close();

}

template <typename T>
void readMatrix(T* H, std::string path_in, std::string prefix,
                std::size_t index, std::string suffix, std::size_t size,
		std::size_t m, std::size_t mblocks, std::size_t nblocks,
            	std::size_t* r_offs, std::size_t* r_lens, std::size_t* r_offs_l,
		std::size_t* c_offs, std::size_t* c_lens, std::size_t* c_offs_l)
{

  std::size_t N = std::sqrt(size);
  std::ostringstream problem(std::ostringstream::ate);

  int rank;
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  problem << path_in << prefix << "  1 " << std::setw(2) << index << suffix;
  if (rank == 0) std::cout << "]> Loading " << problem.str() << std::endl;

  std::size_t file_size = bf::file_size(problem.str().c_str());

  try{
    if(size * sizeof(T) != file_size ){
      throw std::logic_error(std::string("The given file : ") +
                               problem.str() + std::string(" of size ") + std::to_string(file_size) +
                               std::string(" doesn't equals to the required size of matrix of size ") + std::to_string(size * sizeof(T)));
    }
  }
  catch(std::exception &e)
  {
    std::cerr << "Caught " << typeid( e ).name( ) << " : "<< e.what( ) << std::endl;
    return ;
  }

  std::ifstream input(problem.str().c_str(), std::ios::binary);

  if (!input.is_open()) {
    throw new std::logic_error(std::string("error reading file: ") +
                               problem.str());
  }

  for(std::size_t j = 0; j < nblocks; j++){
      for(std::size_t i = 0; i < mblocks; i++){
          for(std::size_t q = 0; q < c_lens[j]; q++){
	      input.seekg(((q + c_offs[j]) * N + r_offs[i])* sizeof(T));
	      input.read(reinterpret_cast<char*>(H + (q + c_offs_l[j]) * m + r_offs_l[i]), r_lens[i] * sizeof(T));
	  }
      }
  }
}

using T = std::complex<double>;

#ifdef DRIVER_BUILD_MGPU
typedef ChaseMpi<ChaseMpiDLAMultiGPU, T> CHASE;
#else
typedef ChaseMpi<ChaseMpiDLABlaslapack, T> CHASE;
#endif

struct ChASE_DriverProblemConfig {
  std::size_t N;    // Size of the Matrix
  std::size_t nev;  // Number of sought after eigenvalues
  std::size_t nex;  // Extra size of subspace
  std::size_t deg;  // initial degree
  std::size_t bgn;  // beginning of sequence
  std::size_t end;  // end of sequence

  double tol;     // desired tolerance
  bool sequence;  // handle this as a sequence?

  std::string path_in;    // path to the matrix input files
  std::string mode;       // Approx or Random mode
  std::string opt;        // enable optimisation of degree
  std::string arch;       // ??
  std::string path_eigp;  // TODO
  std::string path_out;
  std::string path_name;

  bool complex;
  bool isdouble;

  std::size_t mbsize;
  std::size_t nbsize;
  int dim0;
  int dim1;
  int irsrc;
  int icsrc;
  std::string major;
};

template <typename T>
int do_chase_gev(ChASE_DriverProblemConfig& conf) {

  T alpha = 0.5;
  std::size_t N = conf.N;
  std::size_t nev = conf.nev;
  std::size_t nex = conf.nex;
  std::size_t deg = conf.deg;
  std::size_t bgn = conf.bgn;
  std::size_t end = conf.end;

  double tol = conf.tol;
  bool sequence = conf.sequence;

  std::string path_in = conf.path_in;
  std::string mode = conf.mode;
  std::string opt = conf.opt;
  std::string arch;
  std::string path_eigp = conf.path_eigp;
  std::string path_out = conf.path_out;
  std::string path_name = conf.path_name;

  std::size_t mbsize = conf.mbsize;
  std::size_t nbsize = conf.nbsize;
  int dim0 = conf.dim0;
  int dim1 = conf.dim1;
  int irsrc = conf.irsrc;
  int icsrc = conf.irsrc;
  std::string major = conf.major;

  if(dim0 == 0 || dim1 == 0){
    int dims[2];
    dims[0] = dims[1] = 0;
    int gsize;
    MPI_Comm_size(MPI_COMM_WORLD, &gsize);
    MPI_Dims_create(gsize, 2, dims);
    dim0 = dims[0];
    dim1 = dims[1];
  }

  int rank, size;

  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &size);

  std::cout << std::setprecision(16);

  if (rank == 0){
    std::cout << "\nChASE example driver for a sequence of Generalized "
	<<"Eigenproblems."
	<< std::endl;
  }

  // Timing variables
  std::chrono::high_resolution_clock::time_point st, ed, st_loc;
  std::size_t prb_nb = end - bgn + 1;
  std::chrono::duration<double> elapsed[6][prb_nb];

  //Scalapack part
  //Initalize Scalapack environment
  int myproc, nprocs;
  blacs_pinfo( &myproc, &nprocs );

  int ictxt;
  int val;
  blacs_get( &ictxt, &i_zero, &val );

  blacs_gridinit( &ictxt, major.at(0), &dim0, &dim1 );
  int myrow, mycol;
  blacs_gridinfo( &ictxt, &dim0, &dim1, &myrow, &mycol);

  //get local size of matrix = N_loc_r x N_loc_c
  std::size_t N_loc_r, N_loc_c;

  N_loc_r = numroc( &N, &mbsize, &myrow, &irsrc, &dim0 );
  N_loc_c = numroc( &N, &nbsize, &mycol, &icsrc, &dim1 );

  //for column major matrix, the leading dimension
  std::size_t lld_loc = std::max(N_loc_r, (std::size_t)1);

  //construct scalapack matrix descriptor
  DESC   desc;
  int    info;

  descinit( desc, &N, &N, &mbsize, &nbsize, &irsrc, &irsrc, &ictxt, &lld_loc, &info );

  //ChASE part
  //eigenpairs of standard eigenproblem
  auto V__ = std::unique_ptr<T[]>(new T[N * (nev + nex)]);
  auto Lambda__ = std::unique_ptr<Base<T>[]>(new Base<T>[(nev + nex)]);

  T* V = V__.get();
  Base<T>* Lambda = Lambda__.get();

  //Setup ChASE environment for a standard eigenproblem
  CHASE single(new ChaseMpiProperties<T>(N, mbsize, nbsize, nev, nex, dim0,
	dim1, const_cast<char*>(major.c_str()), irsrc, icsrc, MPI_COMM_WORLD), V, Lambda);

  ChaseConfig<T>& config = single.GetConfig();
  config.SetTol(tol);
  config.SetDeg(deg);
  config.SetOpt(opt == "S");
  config.SetMaxIter(100);

  if (rank == 0)
    std::cout << "\n"
              << config;

  std::mt19937 gen(1337.0);
  std::normal_distribution<> d;

  T* matrix = single.GetMatrixPtr();

  // Using ChASE-MPI functionalities to get some additional information
  // on the block cyclic data layout which faciliates the implementation
  //local block number = mblocks x nblocks
  std::size_t mblocks = single.get_mblocks();
  std::size_t nblocks = single.get_nblocks();

  //local matrix size = m x n
  std::size_t m = single.get_m(); // should = N_loc_r
  std::size_t n = single.get_n(); // should = N_loc_c

  //global and local offset/length of each block of block-cyclic data
  std::size_t *r_offs, *c_offs, *r_lens, *c_lens, *r_offs_l, *c_offs_l;

  single.get_offs_lens(r_offs, r_lens, r_offs_l, c_offs, c_lens, c_offs_l);

  Base<T> scale; //for t_psy(he)gst in Scalapack

  // GEV: H * X = LAMBDA * S * X, in which H and S are local matrices
  T *H = new T [N_loc_r * N_loc_c];
  T *S = new T [N_loc_r * N_loc_c];

  //sequence of problem
  for(auto idx = bgn; idx <= end; ++idx){

    if (rank == 0) {
      std::cout << "]> Starting Problem #" << idx << "\n";
    }

    st = std::chrono::high_resolution_clock::now();
    st_loc = std::chrono::high_resolution_clock::now();

    // if the path of matrices are empty, generate H as a Clement matrix
    // and generate S as an Identity matrix
    if(path_in.empty())
    {
      //Generate Clement matrix
      std::vector<T> HH = generateClementMat<T>(N);
      //Generate an Identify matrix
      std::vector<T> SS = generateEyeMat<T>(N);
      //redistribute into HH and SS into block cyclic layout
      for(std::size_t j = 0; j < nblocks; j++){
        for(std::size_t i = 0; i < mblocks; i++){
          for(std::size_t q = 0; q < c_lens[j]; q++){
	    for(std::size_t p = 0; p < r_lens[i]; p++){
	      H[(q + c_offs_l[j]) * m + p + r_offs_l[i]] = HH[(q + c_offs[j]) * N + p + r_offs[i]];
              S[(q + c_offs_l[j]) * m + p + r_offs_l[i]] = SS[(q + c_offs[j]) * N + p + r_offs[i]];
	    }
	  }
        }
      }
    }else //if path of matrices are given, load them into H and S by parallel IO
    {
      //read matrix H from local
      readMatrix(H, path_in, "hmat", idx, ".bin", N * N, m, mblocks, nblocks,
         r_offs, r_lens, r_offs_l, c_offs, c_lens, c_offs_l);
      //read matrix S from local
      readMatrix(S, path_in, "smat", idx, ".bin", N * N, m, mblocks, nblocks,
         r_offs, r_lens, r_offs_l, c_offs, c_lens, c_offs_l);
    }

    ed = std::chrono::high_resolution_clock::now();
    elapsed[0][idx - 1] = std::chrono::duration_cast<std::chrono::duration<double>>(ed - st_loc);

    st_loc = std::chrono::high_resolution_clock::now();

    // Transform to standard problem using SCALAPACK
    // Cholesky Factorization of S = L * L^T, S is overwritten by L
    t_ppotrf<T>('U', N, S, sze_one, sze_one, desc);

    ed = std::chrono::high_resolution_clock::now();
    elapsed[1][idx - 1] = std::chrono::duration_cast<std::chrono::duration<double>>(ed - st_loc);

    st_loc = std::chrono::high_resolution_clock::now();

    // Reduce H * X = eig * S ( X to the standard from H' * X' = eig * X'
    // with H' = L^{-1} * H * (L^T)^{-1}
    t_psyhegst<T>(i_one, 'U', N, H, sze_one, sze_one, desc, S, sze_one, sze_one, desc, &scale);

    // ensure H' to be exact Symmetric/Hermtian by H' = 0.5*H'+0.5*transpose(H')
    T *tmpH = new T [m * n];
    std::memcpy(tmpH, H, m * n * sizeof(T));
    t_geadd<T>('T', N, N, alpha, tmpH, sze_one, sze_one, desc, alpha, H, sze_one, sze_one, desc);

    ed = std::chrono::high_resolution_clock::now();
    elapsed[2][idx - 1] = std::chrono::duration_cast<std::chrono::duration<double>>(ed - st_loc);

    // Copy H into single.matrix()
    std::memcpy(matrix, H, m * n * sizeof(T));

    // for the first problem, generate randomly the initial guess of V
    if (idx == bgn) {
      config.SetApprox(false);
      //random generated initial guess of V
      for (std::size_t i = 0; i < N * (nev + nex); ++i) {
        V[i] = T(d(gen), d(gen));
      }
    }else{
      //use the eigenpairs from last problem
      config.SetApprox(true);
    }

    PerformanceDecoratorChase<T> performanceDecorator(&single);

    MPI_Barrier(MPI_COMM_WORLD);

    st_loc = std::chrono::high_resolution_clock::now();

    // ChASE to solve the standard eigenproblem H' * X' = eig * X'
    chase::Solve(&performanceDecorator);

    MPI_Barrier(MPI_COMM_WORLD);

    ed = std::chrono::high_resolution_clock::now();
    elapsed[3][idx - 1] = std::chrono::duration_cast<std::chrono::duration<double>>(ed - st_loc);

    if (rank == 0) {
      std::cout << "\n]> Output of ChASE for Problem #" << idx << "\n";
      performanceDecorator.GetPerfData().print();
      Base<T>* resid = single.GetResid();
      std::cout << "]> Printing first 5 eigenvalues and residuals\n";
      std::cout
          << "| Index |       Eigenvalue      |         Residual      |\n"
          << "|-------|-----------------------|-----------------------|\n";
      std::size_t width = 20;
      std::cout << std::setprecision(12);
      std::cout << std::setfill(' ');
      std::cout << std::scientific;
      std::cout << std::right;
      for (auto i = 0; i < std::min(std::size_t(5), nev); ++i)
        std::cout << "|  " << std::setw(4) << i + 1 << " | " << std::setw(width)
                  << Lambda[i] << "  | " << std::setw(width) << resid[i]
                  << "  |\n";
      std::cout << "\n";
    }

    //Scalapack part
    //In ChASE, the eigenvectors V is stored rebundantly on each proc
    //In order to recover the generalized eigenvectors by Scalapack, it should be
    //redistributed into block-cyclic format. We re-use H to restore V.
    //this part is in parallel implicitly

    st_loc = std::chrono::high_resolution_clock::now();

    for(std::size_t j = 0; j < nblocks; j++){
      for(std::size_t i = 0; i < mblocks; i++){
        for(std::size_t q = 0; q < c_lens[j]; q++){
	  for(std::size_t p = 0; p < r_lens[i]; p++){
	    if((q + c_offs[j]) * N + p + r_offs[i] < (nev + nex) * N){
	      H[(q + c_offs_l[j]) * m + p + r_offs_l[i]] = V[(q + c_offs[j]) * N + p + r_offs[i]];
	    }
	  }
	}
      }
    }

    //Now the first (nev+nex) columns of H (a global view) is overwritten by V
    //Recover the genealized eigenvectors X by solving X' = L^T * X

    t_ptrtrs<T>('U','N','N', N, nev + nex, S, sze_one, sze_one, desc, H, sze_one, sze_one, desc);

    ed = std::chrono::high_resolution_clock::now();
    elapsed[4][idx - 1] = std::chrono::duration_cast<std::chrono::duration<double>>(ed - st_loc);
    elapsed[5][idx - 1] = std::chrono::duration_cast<std::chrono::duration<double>>(ed - st);

    //copy V_dist -> V;
    //restore eigenvectors from scalapack form to ChASE form

    if (rank == 0) {
      std::cout << "]> Finished Problem #" << idx << "\n";
      std::cout << "**********************"  << std::endl;
    }
  }

  MPI_Barrier(MPI_COMM_WORLD);

  if(rank == 0){
    std::cout << "\nSUMMARY : Time (s)" << "\n";
    std::cout
          << "---------------------------------------------------------------------------------------------------------------------------------------------------------------\n"
          << "|  Index|             Parallel IO|  Cholesky Factorization|    Transfer to Standard|            ChASE Solver|          Back Transform|                     All|\n"
          << "|-------|------------------------|------------------------|------------------------|------------------------|------------------------|------------------------|";
    std::cout << std::endl;

    std::cout << std::setprecision(6);
    std::cout << std::setfill(' ');
    std::cout << std::scientific;
    for(auto idx = bgn - 1; idx < end; ++idx){
      std::cout << "|" << std::setw(7) << idx + 1 << "|";
      for(auto i = 0; i < 6; i++){
	std::cout << std::setw(24) << elapsed[i][idx].count() << "|";
      }
      std::cout << std::endl;
    }

    std::cout
            << "---------------------------------------------------------------------------------------------------------------------------------------------------------------"
	    << std::endl;
  }

  return 0;
}

int main(int argc, char* argv[]) {

  MPI_Init(&argc, &argv);

  ChASE_DriverProblemConfig conf;

  po::options_description desc("ChASE Options");

  desc.add_options()(                                                     //
      "help,h",                                                           //
      "show this message"                                                 //
      )(                                                                  //
      "n", po::value<std::size_t>(&conf.N)->required(),                   //
      "Size of the Input Matrix"                                          //
      )(                                                                  //
      "double", po::value<bool>(&conf.isdouble)->default_value(true),     //
      "Is matrix complex double valued, false indicates the single type"  //
      )(                                                                  //
      "complex", po::value<bool>(&conf.complex)->default_value(true),     //
      "Matrix is complex valued"                                          //
      )(                                                                  //
      "nev", po::value<std::size_t>(&conf.nev)->required(),               //
      "Wanted Number of Eigenpairs"                                       //
      )(                                                                  //
      "nex", po::value<std::size_t>(&conf.nex)->default_value(25),        //
      "Extra Search Dimensions"                                           //
      )(                                                                  //
      "deg", po::value<std::size_t>(&conf.deg)->default_value(20),        //
      "Initial filtering degree"                                          //
      )(                                                                  //
      "bgn", po::value<std::size_t>(&conf.bgn)->default_value(1),         //
      "Start ell"                                                         //
      )(                                                                  //
      "end", po::value<std::size_t>(&conf.end)->default_value(1),         //
      "End ell"                                                           //
      )(                                                                  //
      "tol", po::value<double>(&conf.tol)->default_value(1e-10),          //
      "Tolerance for Eigenpair convergence"                               //
      )(                                                                  //
      "path_in", po::value<std::string>(&conf.path_in)->default_value(""),//
      "Path to the input matrix/matrices"                                 //
      )(                                                                  //
      "mode", po::value<std::string>(&conf.mode)->default_value("A"),     //
      "valid values are R(andom) or A(pproximate)"                        //
      )(                                                                  //
      "opt", po::value<std::string>(&conf.opt)->default_value("S"),       //
      "Optimi(S)e degree, or do (N)ot optimise"                           //
      )(                                                                  //
      "path_eigp", po::value<std::string>(&conf.path_eigp),               //
      "Path to approximate solutions, only required when mode"            //
      "is Approximate, otherwise not used"                                //
      )(                                                                  //
      "sequence", po::value<bool>(&conf.sequence)->default_value(false),  //
      "Treat as sequence of Problems. Previous ChASE solution is used,"   //
      "when available"                                                    //
      )(                                                                  //
      "mbsize", po::value<std::size_t>(&conf.mbsize)->default_value(50),  //
      "block size for the row"                                            //
      )(                                                                  //
      "nbsize", po::value<std::size_t>(&conf.nbsize)->default_value(50),  //
      "block size for the column"                                         //
      )(                                                                  //
      "dim0", po::value<int>(&conf.dim0)->default_value(0),               //
      "row number of MPI proc grid"                                       //
      )(                                                                  //
      "dim1", po::value<int>(&conf.dim1)->default_value(0),               //
      "column number of MPI proc grid"                                    //
      )(								  //
      "irsrc", po::value<int>(&conf.irsrc)->default_value(0),             //
      "The process row over which the first row of matrix is"             //
      "distributed."                                                      //
      )(                                                                  //
      "icsrc", po::value<int>(&conf.icsrc)->default_value(0),             //
      "The process column over which the first column of the array A is"  //
      "distributed."                                                      //
      )(                                                                  //
      "major", po::value<std::string>(&conf.major)->default_value("C"),    //
      "Major of MPI proc grid, valid values are R(ow) or C(olumn)"        //
      );

  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, desc), vm);

  // print help
  if (vm.count("help")) {
    std::cout << desc << std::endl;
    return 1;
  }

  po::notify(vm);
  conf.mode = toupper(conf.mode.at(0));
  conf.opt = toupper(conf.opt.at(0));

  if (conf.bgn > conf.end) {
    std::cout << "Begin must be smaller than End!" << std::endl;
    return -1;
  }

  if (conf.mode != "R" && conf.mode != "A") {
    std::cout << "Illegal value for mode: \"" << conf.mode << "\"" << std::endl
              << "Legal values are R or A" << std::endl;
    return -1;
  }

  if (conf.opt != "N" && conf.opt != "S") {
    std::cout << "Illegal value for opt: " << conf.opt << std::endl
              << "Legal values are N, S" << std::endl;
    return -1;
  }

  if (conf.path_eigp.empty() && conf.mode == "A") {
    std::cout << "eigp is required when mode is " << conf.mode << std::endl;
    return -1;
  }

  if (conf.isdouble) {
    do_chase_gev<std::complex<double>>(conf);
  } else {
    std::cout << "single not implemented\n";
  }

  MPI_Finalize();

}


//  Copyright John Maddock 2007.
//  Copyright Paul A. Bristow 2007
//  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_STATS_PARETO_HPP
#define BOOST_STATS_PARETO_HPP

// http://en.wikipedia.org/wiki/Pareto_distribution
// http://www.itl.nist.gov/div898/handbook/eda/section3/eda3661.htm
// Also:
// Weisstein, Eric W. "Pareto Distribution."
// From MathWorld--A Wolfram Web Resource.
// http://mathworld.wolfram.com/ParetoDistribution.html

#include <boost/math/distributions/fwd.hpp>
#include <boost/math/distributions/complement.hpp>
#include <boost/math/distributions/detail/common_error_handling.hpp>
#include <boost/math/special_functions/powm1.hpp>

#include <utility> // for BOOST_CURRENT_VALUE?

namespace boost
{
  namespace math
  {
    namespace detail
    { // Parameter checking.
      template <class RealType, class Policy>
      inline bool check_pareto_location(
        const char* function,
        RealType location,
        RealType* result, const Policy& pol)
      {
        if((boost::math::isfinite)(location))
        { // any > 0 finite value is OK.
          if (location > 0)
          {
            return true;
          }
          else
          {
            *result = policies::raise_domain_error<RealType>(
              function,
              "Location parameter is %1%, but must be > 0!", location, pol);
            return false;
          }
        }
        else
        { // Not finite.
          *result = policies::raise_domain_error<RealType>(
            function,
            "Location parameter is %1%, but must be finite!", location, pol);
          return false;
        }
      } // bool check_pareto_location

      template <class RealType, class Policy>
      inline bool check_pareto_shape(
        const char* function,
        RealType shape,
        RealType* result, const Policy& pol)
      {
        if((boost::math::isfinite)(shape))
        { // Any finite value > 0 is OK.
          if (shape > 0)
          {
            return true;
          }
          else
          {
            *result = policies::raise_domain_error<RealType>(
              function,
              "Shape parameter is %1%, but must be > 0!", shape, pol);
            return false;
          }
        }
        else
        { // Not finite.
          *result = policies::raise_domain_error<RealType>(
            function,
            "Shape parameter is %1%, but must be finite!", shape, pol);
          return false;
        }
      } // bool check_pareto_shape(

      template <class RealType, class Policy>
      inline bool check_pareto_x(
        const char* function,
        RealType const& x,
        RealType* result, const Policy& pol)
      {
        if((boost::math::isfinite)(x))
        { //
          if (x > 0)
          {
            return true;
          }
          else
          {
            *result = policies::raise_domain_error<RealType>(
              function,
              "x parameter is %1%, but must be > 0 !", x, pol);
            return false;
          }
        }
        else
        { // Not finite..
          *result = policies::raise_domain_error<RealType>(
            function,
            "x parameter is %1%, but must be finite!", x, pol);
          return false;
        }
      } // bool check_pareto_x

      template <class RealType, class Policy>
      inline bool check_pareto( // distribution parameters.
        const char* function,
        RealType location,
        RealType shape,
        RealType* result, const Policy& pol)
      {
        return check_pareto_location(function, location, result, pol)
           && check_pareto_shape(function, shape, result, pol);
      } // bool check_pareto(

    } // namespace detail

    template <class RealType = double, class Policy = policies::policy<> >
    class pareto_distribution
    {
    public:
      typedef RealType value_type;
      typedef Policy policy_type;

      pareto_distribution(RealType location = 1, RealType shape = 1)
        : m_location(location), m_shape(shape)
      { // Constructor.
        RealType result;
        detail::check_pareto("boost::math::pareto_distribution<%1%>::pareto_distribution", location, shape, &result, Policy());
      }

      RealType location()const
      { // AKA Xm and b
        return m_location;
      }

      RealType shape()const
      { // AKA k and a
        return m_shape;
      }
    private:
      // Data members:
      RealType m_location;  // distribution location (xm)
      RealType m_shape;  // distribution shape (k)
    };

    typedef pareto_distribution<double> pareto; // Convenience to allow pareto(2., 3.);

    template <class RealType, class Policy>
    inline const std::pair<RealType, RealType> range(const pareto_distribution<RealType, Policy>& /*dist*/)
    { // Range of permissible values for random variable x.
      using boost::math::tools::max_value;
      return std::pair<RealType, RealType>(0, max_value<RealType>()); // location zero to + infinity.
    } // range

    template <class RealType, class Policy>
    inline const std::pair<RealType, RealType> support(const pareto_distribution<RealType, Policy>& dist)
    { // Range of supported values for random variable x.
      // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero.
      using boost::math::tools::max_value;
      return std::pair<RealType, RealType>(dist.location(), max_value<RealType>() ); // location to + infinity.
    } // support

    template <class RealType, class Policy>
    inline RealType pdf(const pareto_distribution<RealType, Policy>& dist, const RealType& x)
    {
      BOOST_MATH_STD_USING  // for ADL of std function pow.
      static const char* function = "boost::math::pdf(const pareto_distribution<%1%>&, %1%)";
      RealType location = dist.location();
      RealType shape = dist.shape();
      RealType result;
      if(false == (detail::check_pareto_x(function, x, &result, Policy())
         && detail::check_pareto(function, location, shape, &result, Policy())))
         return result;
      if (x < location)
      { // regardless of shape, pdf is zero.
        return 0;
      }

      result = shape * pow(location, shape) / pow(x, shape+1);
      return result;
    } // pdf

    template <class RealType, class Policy>
    inline RealType cdf(const pareto_distribution<RealType, Policy>& dist, const RealType& x)
    {
      BOOST_MATH_STD_USING  // for ADL of std function pow.
      static const char* function = "boost::math::cdf(const pareto_distribution<%1%>&, %1%)";
      RealType location = dist.location();
      RealType shape = dist.shape();
      RealType result;

      if(false == (detail::check_pareto_x(function, x, &result, Policy())
         && detail::check_pareto(function, location, shape, &result, Policy())))
         return result;

      if (x <= location)
      { // regardless of shape, cdf is zero.
        return 0;
      }

      // result = RealType(1) - pow((location / x), shape);
      result = -boost::math::powm1(location/x, shape, Policy()); // should be more accurate.
      return result;
    } // cdf

    template <class RealType, class Policy>
    inline RealType quantile(const pareto_distribution<RealType, Policy>& dist, const RealType& p)
    {
      BOOST_MATH_STD_USING  // for ADL of std function pow.
      static const char* function = "boost::math::quantile(const pareto_distribution<%1%>&, %1%)";
      RealType result;
      RealType location = dist.location();
      RealType shape = dist.shape();
      if(false == (detail::check_probability(function, p, &result, Policy())
           && detail::check_pareto(function, location, shape, &result, Policy())))
      {
        return result;
      }
      if (p == 0)
      {
        return location; // x must be location (or less).
      }
      if (p == 1)
      {
        return tools::max_value<RealType>(); // x = + infinity.
      }
      result = location /
        (pow((1 - p), 1 / shape));
      // K. Krishnamoorthy,  ISBN 1-58488-635-8 eq 23.1.3
      return result;
    } // quantile

    template <class RealType, class Policy>
    inline RealType cdf(const complemented2_type<pareto_distribution<RealType, Policy>, RealType>& c)
    {
       BOOST_MATH_STD_USING  // for ADL of std function pow.
       static const char* function = "boost::math::cdf(const pareto_distribution<%1%>&, %1%)";
       RealType result;
       RealType x = c.param;
       RealType location = c.dist.location();
       RealType shape = c.dist.shape();
       if(false == (detail::check_pareto_x(function, x, &result, Policy())
           && detail::check_pareto(function, location, shape, &result, Policy())))
         return result;

       if (x <= location)
       { // regardless of shape, cdf is zero, and complement is unity.
         return 1;
       }
       result = pow((location/x), shape);

       return result;
    } // cdf complement

    template <class RealType, class Policy>
    inline RealType quantile(const complemented2_type<pareto_distribution<RealType, Policy>, RealType>& c)
    {
      BOOST_MATH_STD_USING  // for ADL of std function pow.
      static const char* function = "boost::math::quantile(const pareto_distribution<%1%>&, %1%)";
      RealType result;
      RealType q = c.param;
      RealType location = c.dist.location();
      RealType shape = c.dist.shape();
      if(false == (detail::check_probability(function, q, &result, Policy())
           && detail::check_pareto(function, location, shape, &result, Policy())))
      {
        return result;
      }
      if (q == 1)
      {
        return location; // x must be location (or less).
      }
      if (q == 0)
      {
        return tools::max_value<RealType>(); // x = + infinity.
      }
      result = location / (pow(q, 1 / shape));
      // K. Krishnamoorthy,  ISBN 1-58488-635-8 eq 23.1.3
      return result;
    } // quantile complement

    template <class RealType, class Policy>
    inline RealType mean(const pareto_distribution<RealType, Policy>& dist)
    {
      RealType result;
      static const char* function = "boost::math::mean(const pareto_distribution<%1%>&, %1%)";
      if(false == detail::check_pareto(function, dist.location(), dist.shape(), &result, Policy()))
      {
        return result;
      }
      if (dist.shape() > RealType(1))
      {
        return dist.shape() * dist.location() / (dist.shape() - 1);
      }
      else
      {
        using boost::math::tools::max_value;
        return max_value<RealType>(); // +infinity.
      }
    } // mean

    template <class RealType, class Policy>
    inline RealType mode(const pareto_distribution<RealType, Policy>& dist)
    {
      return dist.location();
    } // mode

    template <class RealType, class Policy>
    inline RealType median(const pareto_distribution<RealType, Policy>& dist)
    {
      RealType result;
      static const char* function = "boost::math::median(const pareto_distribution<%1%>&, %1%)";
      if(false == detail::check_pareto(function, dist.location(), dist.shape(), &result, Policy()))
      {
        return result;
      }
      BOOST_MATH_STD_USING
      return dist.location() * pow(RealType(2), (1/dist.shape()));
    } // median

    template <class RealType, class Policy>
    inline RealType variance(const pareto_distribution<RealType, Policy>& dist)
    {
      RealType result;
      RealType location = dist.location();
      RealType shape = dist.shape();
      static const char* function = "boost::math::variance(const pareto_distribution<%1%>&, %1%)";
      if(false == detail::check_pareto(function, location, shape, &result, Policy()))
      {
        return result;
      }
      if (shape > 2)
      {
        result = (location * location * shape) /
         ((shape - 1) *  (shape - 1) * (shape - 2));
      }
      else
      {
        result = policies::raise_domain_error<RealType>(
          function,
          "variance is undefined for shape <= 2, but got %1%.", dist.shape(), Policy());
      }
      return result;
    } // variance

    template <class RealType, class Policy>
    inline RealType skewness(const pareto_distribution<RealType, Policy>& dist)
    {
      BOOST_MATH_STD_USING
      RealType result;
      RealType shape = dist.shape();
      static const char* function = "boost::math::pdf(const pareto_distribution<%1%>&, %1%)";
      if(false == detail::check_pareto(function, dist.location(), shape, &result, Policy()))
      {
        return result;
      }
      if (shape > 3)
      {
        result = sqrt((shape - 2) / shape) *
          2 * (shape + 1) /
          (shape - 3);
      }
      else
      {
        result = policies::raise_domain_error<RealType>(
          function,
          "skewness is undefined for shape <= 3, but got %1%.", dist.shape(), Policy());
      }
      return result;
    } // skewness

    template <class RealType, class Policy>
    inline RealType kurtosis(const pareto_distribution<RealType, Policy>& dist)
    {
      RealType result;
      RealType shape = dist.shape();
      static const char* function = "boost::math::pdf(const pareto_distribution<%1%>&, %1%)";
      if(false == detail::check_pareto(function, dist.location(), shape, &result, Policy()))
      {
        return result;
      }
      if (shape > 4)
      {
        result = 3 * ((shape - 2) * (3 * shape * shape + shape + 2)) /
          (shape * (shape - 3) * (shape - 4));
      }
      else
      {
        result = policies::raise_domain_error<RealType>(
          function,
          "kurtosis_excess is undefined for shape <= 4, but got %1%.", shape, Policy());
      }
      return result;
    } // kurtosis

    template <class RealType, class Policy>
    inline RealType kurtosis_excess(const pareto_distribution<RealType, Policy>& dist)
    {
      RealType result;
      RealType shape = dist.shape();
      static const char* function = "boost::math::pdf(const pareto_distribution<%1%>&, %1%)";
      if(false == detail::check_pareto(function, dist.location(), shape, &result, Policy()))
      {
        return result;
      }
      if (shape > 4)
      {
        result = 6 * ((shape * shape * shape) + (shape * shape) - 6 * shape - 2) /
          (shape * (shape - 3) * (shape - 4));
      }
      else
      {
        result = policies::raise_domain_error<RealType>(
          function,
          "kurtosis_excess is undefined for shape <= 4, but got %1%.", dist.shape(), Policy());
      }
      return result;
    } // kurtosis_excess

    } // namespace math
  } // namespace boost

  // This include must be at the end, *after* the accessors
  // for this distribution have been defined, in order to
  // keep compilers that support two-phase lookup happy.
#include <boost/math/distributions/detail/derived_accessors.hpp>

#endif // BOOST_STATS_PARETO_HPP


#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost_adaptbx/tuple_conversion.h>

namespace {

  boost::tuple<int, double> exercise(int i) {
    return boost::make_tuple(2*i, 0.5*i);
  }

}

BOOST_PYTHON_MODULE(boost_tuple_ext)
{
  using namespace boost_adaptbx::tuple_conversion;
  using namespace boost::python;
  to_python<boost::tuple<int, double> >();
  def("exercise", exercise);
}


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

// Copyright (c) 2020 Barend Gehrels, Amsterdam, the Netherlands.

// This file was modified by Oracle on 2020.
// Modifications copyright (c) 2020 Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

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

#include "geometry_test_common.hpp"

#include <boost/geometry/algorithms/area.hpp>
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
#include <boost/geometry/algorithms/detail/buffer/piece_border.hpp>
#include <boost/geometry/util/range.hpp>

#include <boost/geometry/io/wkt/wkt.hpp>
#include <boost/geometry/geometries/geometries.hpp>

#include <boost/geometry/strategies/comparable_distance_result.hpp>
#include <boost/geometry/strategies/cartesian/distance_pythagoras.hpp>
#include <boost/geometry/strategies/cartesian/distance_projected_point.hpp>

// TEMP
#include <boost/geometry/strategies/cartesian.hpp>

#if defined(TEST_WITH_SVG)
#include <boost/geometry/io/svg/svg_mapper.hpp>
#include <boost/geometry/algorithms/detail/buffer/buffer_box.hpp>
#endif

static const std::string rectangle_offsetted = "POLYGON((0 0,0 1,0 2,0 3,1 3,2 3,3 3,3 2,3 1,3 0,2 0,1 0,0 0))";
static const std::string rectangle_original = "POLYGON((1 1,1 2,2 2,2 1,1 1))";

static const std::string diamond_offsetted = "POLYGON((3 0,2 1,1 2,0 3,1 4,2 5,3 6,4 5,5 4,6 3,5 2,4 1,3 0))";
static const std::string diamond_original = "POLYGON((3 2,2 3,3 4,4 3,3 2))";

template <typename Geometry>
inline std::string as_wkt(Geometry const& geometry)
{
    std::ostringstream out;
    out << bg::wkt(geometry);
    return out.str();
}

// Return the border. It needs to pass the ring because its address
// is preserved in the border. It also gets the original.
template <typename Border, typename Ring>
Border setup_piece_border(Ring& ring, Ring& original,
                          std::string const& offsetted_wkt,
                          std::string const& original_wkt,
                          char piece)
{
    // The test setup (here fore case "rectangle"; "diamond" has same layout).
    // Points on offsetted ring are numbered, points on original like this [0].
    // a, b, c: test pieces.
    //
    //           4       5
    //   3 +-----+-------+-----+ 6
    //     |     |       |     |
    //     |     |   a   |  b  |
    //     |     |       |     |
    //   2 +----[1]-----[2]----+ 7
    //     |     |       |     |
    //     |     |  ori  |  c  |
    //     |     |       |     |
    //   1 +----[0,4]---[3]----+ 8
    //     |     |       |     |
    //     |     |       |     |
    //     |     |       |     |
    //   0 +-----+-------+-----+ 9
    //    12    11       10

    bg::read_wkt(original_wkt, original);
    bg::read_wkt(offsetted_wkt, ring);

    Border border;

    switch(piece)
    {
        case 'a' :
            border.set_offsetted(ring, 4, 6);
            border.add_original_point(original[2]);
            border.add_original_point(original[1]);
            break;
        case 'b' :
            border.set_offsetted(ring, 5, 8);
            border.add_original_point(original[2]);
            break;
        case 'c' :
            border.set_offsetted(ring, 7, 9);
            border.add_original_point(original[3]);
            border.add_original_point(original[2]);
            break;
        default :
            return border;
    }

    typedef typename bg::point_type<Ring>::type point_type;

    bg::strategies::relate::cartesian<> strategy;

    border.get_properties_of_border(false, point_type(), strategy);

    border.get_properties_of_offsetted_ring_part(strategy);

    return border;
}

template <typename Point>
void test_rectangle_properties()
{
    typedef bg::model::ring<Point> ring_type;
    typedef bg::detail::buffer::piece_border<ring_type, Point> border_type;

    border_type empty;
    BOOST_CHECK_MESSAGE(empty.ring_or_original_empty(), "piece should be empty");

    ring_type offsetted, original;
    border_type border = setup_piece_border<border_type>(offsetted, original,
            rectangle_offsetted, rectangle_original, 'a');

    // Check get_full_ring functionality (used for testing, writing SVG)
    ring_type const outline = border.get_full_ring();
    std::string const outline_wkt = as_wkt(outline);

    std::string const expected = "POLYGON((1 3,2 3,2 2,1 2,1 3))";
    BOOST_CHECK_MESSAGE(outline_wkt == expected,
                        "expected: " << expected
                        << " detected: " << outline_wkt);

    BOOST_CHECK_MESSAGE(! border.ring_or_original_empty(),
                        "piece should not be empty");

    // Check border-properties functionality (envelope, radius)
    auto const area = bg::area(border.m_envelope);
    BOOST_CHECK_MESSAGE(area > 1.0 && area < 1.01,
                        "detected: " << area);

    // Check offsetted-ring-properties functionality (convexity, monotonicity)
    BOOST_CHECK_MESSAGE(border.m_is_convex == true,
                        "piece should be convex");
    BOOST_CHECK_MESSAGE(border.m_is_monotonic_increasing == true,
                        "piece should be monotonically increasing");
    BOOST_CHECK_MESSAGE(border.m_is_monotonic_decreasing == false,
                        "piece NOT should be monotonically decreasing");
}

template <typename Point, typename Border, typename Mapper>
void test_point(std::string const& wkt, bool expected, Border const& border,
                Mapper& mapper, std::string const& color)
{
    typename Border::state_type state;
    Point point;
    bg::read_wkt(wkt, point);
    border.point_on_piece(point, false, false, state);
    bool const on_piece = state.code() == 1;
    BOOST_CHECK_MESSAGE(on_piece == expected,
                        " expected: " << expected
                        << " detected: " << on_piece
                        << " wkt: " << wkt);

#ifdef TEST_WITH_SVG
    std::string style = "fill:" + color + ";stroke:rgb(0,0,0);stroke-width:1";
    mapper.map(point, style);

    // Mark on-piece as T or F
    std::ostringstream out;
    out << (on_piece ? "T" : "F");
    mapper.text(point, out.str(), "fill:rgb(0,0,0);font-family='Arial';font-size:9px;", 10, -10);
#else
    boost::ignore_unused(mapper, color);
#endif
}

#ifdef TEST_WITH_SVG
template <typename Mapper, typename Ring, typename Border>
void start_svg(Mapper& mapper, Ring const& original, Ring const& offsetted,
               Border const& border)
{
    typedef typename bg::point_type<Ring>::type point_type;
    typedef bg::model::box<point_type> box_type;
    box_type box = border.m_envelope;
    bg::detail::buffer::buffer_box(box, 1.0, box);
    mapper.add(box);
    mapper.add(offsetted);
    mapper.map(offsetted,
               "fill-opacity:0.5;fill:rgb(255,255,128);stroke:none");
    mapper.map(original,
               "fill-opacity:0.5;fill:rgb(51,51,153);stroke:none");
    mapper.map(border.get_full_ring(),
               "fill-opacity:0.5;fill:rgb(153,204,0);"
               "stroke:rgb(153,204,0);stroke-width:1");
}
#endif

template <typename Point>
void test_rectangle_point_on_piece_a()
{
    typedef bg::model::ring<Point> ring_type;
    typedef bg::detail::buffer::piece_border<ring_type, Point> border_type;

    ring_type offsetted, original;
    border_type border = setup_piece_border<border_type>(offsetted, original,
            rectangle_offsetted, rectangle_original, 'a');

#ifdef TEST_WITH_SVG
    std::ofstream svg("/tmp/border_rectangle_a.svg");
    bg::svg_mapper<Point> mapper(svg, 500, 500);
    start_svg(mapper, original, offsetted, border);
#else
    int mapper = 0;
#endif

    // Points inside
    test_point<Point>("POINT(1.5 2.5)", true, border, mapper, "red");

    // Points outside
    test_point<Point>("POINT(0.5 2.5)", false, border, mapper, "green");
    test_point<Point>("POINT(2.5 2.5)", false, border, mapper, "green");
    test_point<Point>("POINT(1.5 1.5)", false, border, mapper, "green");
    test_point<Point>("POINT(1.5 3.5)", false, border, mapper, "green");

    // Points on the original (should be INSIDE)
    test_point<Point>("POINT(1.0 2.0)", true, border, mapper, "orange");
    test_point<Point>("POINT(1.5 2.0)", true, border, mapper, "orange");
    test_point<Point>("POINT(2.0 2.0)", true, border, mapper, "orange");

    // Points on the offsetted ring (should be OUTSIDE)
    test_point<Point>("POINT(1.0 3.0)", false, border, mapper, "blue");
    test_point<Point>("POINT(1.5 3.0)", false, border, mapper, "blue");
    test_point<Point>("POINT(2.0 3.0)", false, border, mapper, "blue");

    // Points on between original and offsetted ring (should be INSIDE)
    test_point<Point>("POINT(1.0 2.5)", true, border, mapper, "cyan");
    test_point<Point>("POINT(2.0 2.5)", true, border, mapper, "cyan");
}

template <typename Point>
void test_rectangle_point_on_piece_c()
{
    typedef bg::model::ring<Point> ring_type;
    typedef bg::detail::buffer::piece_border<ring_type, Point> border_type;

    ring_type offsetted, original;
    border_type border = setup_piece_border<border_type>(offsetted, original,
            rectangle_offsetted, rectangle_original, 'c');

#ifdef TEST_WITH_SVG
    std::ofstream svg("/tmp/border_rectangle_c.svg");
    bg::svg_mapper<Point> mapper(svg, 500, 500);
    start_svg(mapper, original, offsetted, border);
#else
    int mapper = 0;
#endif

    // Piece labeled as 'c' : from (2,1) to (3,2)

    // Points inside
    test_point<Point>("POINT(2.5 1.5)", true, border, mapper, "red");

    // Points outside
    test_point<Point>("POINT(1.5 1.5)", false, border, mapper, "green");
    test_point<Point>("POINT(3.5 1.5)", false, border, mapper, "green");
    test_point<Point>("POINT(2.5 0.5)", false, border, mapper, "green");
    test_point<Point>("POINT(2.5 2.5)", false, border, mapper, "green");

    // Points on the original (should be INSIDE)
    test_point<Point>("POINT(2.0 1.0)", true, border, mapper, "orange");
    test_point<Point>("POINT(2.0 1.5)", true, border, mapper, "orange");
    test_point<Point>("POINT(2.0 2.0)", true, border, mapper, "orange");

    // Points on the offsetted ring (should be OUTSIDE)
    test_point<Point>("POINT(3.0 1.0)", false, border, mapper, "blue");
    test_point<Point>("POINT(3.0 1.5)", false, border, mapper, "blue");
    test_point<Point>("POINT(3.0 2.0)", false, border, mapper, "blue");

    // Points on between original and offsetted ring (should be INSIDE)
    test_point<Point>("POINT(2.5 2.0)", true, border, mapper, "cyan");
    test_point<Point>("POINT(2.5 1.0)", true, border, mapper, "cyan");
}

template <typename Point>
void test_diamond_point_on_piece_a()
{
    typedef bg::model::ring<Point> ring_type;
    typedef bg::detail::buffer::piece_border<ring_type, Point> border_type;

    ring_type offsetted, original;
    border_type border = setup_piece_border<border_type>(offsetted, original,
            diamond_offsetted, diamond_original, 'a');

#ifdef TEST_WITH_SVG
    std::ofstream svg("/tmp/border_diamond_a.svg");
    bg::svg_mapper<Point> mapper(svg, 500, 500);
    start_svg(mapper, original, offsetted, border);
#else
    int mapper = 0;
#endif

    // Points inside
    test_point<Point>("POINT(2 4)", true, border, mapper, "red");

    // Points outside
    test_point<Point>("POINT(1 3)", false, border, mapper, "green");
    test_point<Point>("POINT(1 5)", false, border, mapper, "green");
    test_point<Point>("POINT(3 3)", false, border, mapper, "green");
    test_point<Point>("POINT(3 5)", false, border, mapper, "green");

    // Points on the original (should be INSIDE)
    test_point<Point>("POINT(2 3)", true, border, mapper, "orange");
    test_point<Point>("POINT(2.5 3.5)", true, border, mapper, "orange");
    test_point<Point>("POINT(3 4)", true, border, mapper, "orange");

    // Points on the offsetted ring (should be OUTSIDE)
    test_point<Point>("POINT(1 4)", false, border, mapper, "blue");
    test_point<Point>("POINT(1.5 4.5)", false, border, mapper, "blue");
    test_point<Point>("POINT(2 5)", false, border, mapper, "blue");

    // Points on between original and offsetted ring (should be INSIDE)
    test_point<Point>("POINT(1.5 3.5)", true, border, mapper, "cyan");
    test_point<Point>("POINT(2.5 4.5)", true, border, mapper, "cyan");
}

template <typename Point>
void test_diamond_point_on_piece_c()
{
    typedef bg::model::ring<Point> ring_type;
    typedef bg::detail::buffer::piece_border<ring_type, Point> border_type;

    ring_type offsetted, original;
    border_type border = setup_piece_border<border_type>(offsetted, original,
            diamond_offsetted, diamond_original, 'c');

#ifdef TEST_WITH_SVG
    std::ofstream svg("/tmp/border_diamond_c.svg");
    bg::svg_mapper<Point> mapper(svg, 500, 500);
    start_svg(mapper, original, offsetted, border);
#else
    int mapper = 0;
#endif

    // Points inside
    test_point<Point>("POINT(4 4)", true, border, mapper, "red");

    // Points outside
    test_point<Point>("POINT(3 3)", false, border, mapper, "green");
    test_point<Point>("POINT(3 5)", false, border, mapper, "green");
    test_point<Point>("POINT(5 3)", false, border, mapper, "green");
    test_point<Point>("POINT(5 5)", false, border, mapper, "green");

    // Points on the original (should be INSIDE)
    test_point<Point>("POINT(3 4)", true, border, mapper, "orange");
    test_point<Point>("POINT(3.5 3.5)", true, border, mapper, "orange");
    test_point<Point>("POINT(4 3)", true, border, mapper, "orange");

    // Points on the offsetted ring (should be OUTSIDE)
    test_point<Point>("POINT(4 5)", false, border, mapper, "blue");
    test_point<Point>("POINT(4.5 4.5)", false, border, mapper, "blue");
    test_point<Point>("POINT(5 4)", false, border, mapper, "blue");

    // Points on between original and offsetted ring (should be INSIDE)
    test_point<Point>("POINT(3.5 4.5)", true, border, mapper, "cyan");
    test_point<Point>("POINT(4.5 3.5)", true, border, mapper, "cyan");
}

int test_main(int, char* [])
{
    BoostGeometryWriteTestConfiguration();

    typedef bg::model::point<default_test_type, 2, bg::cs::cartesian> point_type;

    test_rectangle_properties<point_type>();

    test_rectangle_point_on_piece_a<point_type>();
    test_rectangle_point_on_piece_c<point_type>();

    test_diamond_point_on_piece_a<point_type>();
    test_diamond_point_on_piece_c<point_type>();

    return 0;
}