#ifndef FSTSEARCHSEQOS_H
#define FSTSEARCHSEQOS_H

/*!======================================================================
   Feature Selection Toolbox 3 source code
   ---------------------------------------

   \file    search_seq_os.hpp
   \brief   Implements Oscillating_Search
   \author  Petr Somol (somol@utia.cas.cz) with collaborators, see Contacts at http://fst.utia.cz
   \date    March 2011
   \version 3.1.0.beta
   \note    FST3 was developed using gcc 4.3 and requires
   \note    \li Boost library (http://www.boost.org/, tested with versions 1.33.1 and 1.44),
   \note    \li (\e optionally) LibSVM (http://www.csie.ntu.edu.tw/~cjlin/libsvm/,
                tested with version 3.00)
   \note    Note that LibSVM is required for SVM related tools only,
            as demonstrated in demo12t.cpp, demo23.cpp, demo25t.cpp, demo32t.cpp, etc.

*/ /*
=========================================================================
Copyright:
  * FST3 software (with exception of any externally linked libraries)
    is copyrighted by Institute of Information Theory and Automation (UTIA),
    Academy of Sciences of the Czech Republic.
  * FST3 source codes as presented here do not contain code of third parties.
    FST3 may need linkage to external libraries to exploit its functionality
    in full. For details on obtaining and possible usage restrictions
    of external libraries follow their original sources (referenced from
    FST3 documentation wherever applicable).
  * FST3 software is available free of charge for non-commercial use.
    Please address all inquires concerning possible commercial use
    of FST3, or if in doubt, to FST3 maintainer (see http://fst.utia.cz).
  * Derivative works based on FST3 are permitted as long as they remain
    non-commercial only.
  * Re-distribution of FST3 software is not allowed without explicit
    consent of the copyright holder.
Disclaimer of Warranty:
  * FST3 software is presented "as is", without warranty of any kind,
    either expressed or implied, including, but not limited to, the implied
    warranties of merchantability and fitness for a particular purpose.
    The entire risk as to the quality and performance of the program
    is with you. Should the program prove defective, you assume the cost
    of all necessary servicing, repair or correction.
Limitation of Liability:
  * The copyright holder will in no event be liable to you for damages,
    including any general, special, incidental or consequential damages
    arising out of the use or inability to use the code (including but not
    limited to loss of data or data being rendered inaccurate or losses
    sustained by you or third parties or a failure of the program to operate
    with any other programs).
========================================================================== */

#include <boost/smart_ptr.hpp>
#include <iostream>
#include <sstream>
#include "error.hpp"
#include "global.hpp"
#include "stopwatch.hpp"
#include "search_bif.hpp"
#include "search_seq.hpp"

/*============== Template parameter type naming conventions ==============
--------- Numeric types: -------------------------------------------------
DATATYPE - data sample values - usually real numbers (but may be integers
          in text processing etc.)
REALTYPE - must be real numbers - for representing intermediate results of
          calculations like mean, covariance etc.
IDXTYPE - index values for enumeration of data samples - (nonnegative) integers,
          extent depends on numbers of samples in data
DIMTYPE - index values for enumeration of features (dimensions), or classes (not
          class sizes) - (nonnegative) integers, usually lower extent than IDXTYPE,
          but be aware of expressions like _classes*_features*_features !
          in linearized representations of feature matrices for all classes
BINTYPE - feature selection marker type - represents ca. <10 different feature
          states (selected, deselected, sel./desel. temporarily 1st nested loop, 2nd...)
RETURNTYPE - criterion value: real value, but may be extended in future to support
          multiple values
--------- Class types: ---------------------------------------------------
SUBSET       - class of class type Subset
CLASSIFIER   - class implementing interface defined in abstract class Classifier
EVALUATOR    - class implementing interface defined in abstract class Sequential_Step
DISTANCE     - class implementing interface defined in abstract class Distance
DATAACCESSOR - class implementing interface defined in abstract class Data_Accessor
INTERVALCONTAINER - class of class type TIntervaller
CONTAINER    - STL container of class type TInterval
========================================================================== */

namespace FST {

/*! \brief Implements Oscillating_Search

	\note By default sub is expected to be initialized prior to search() call, i.e., it should contain a valid initial subset of requested size,
	      from which the search will start. If this is not the case and sub does not contain target_d features, sub contents are adjusted
	      (sub is enlarged by adding features or shrinked by removing features based on individual
	      feature ranking) before the search actually starts. Setting target_d to 0 leads to repeated search() calls for each possible
	      subset size, to eventually select among them the solution with highest criterion value.
	      To ensure that any initial contents of sub are completely ignored (and initial subset is composed from scratch based purely
	      on individual feature ranking), call disable_initial_subset().

   \warning Search_OS can not be used with Sequential_Step_Hybrid() parametrized _keep_perc==0 (due to initial filtercrit and crit values mix-up)
*/
template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
class Search_OS : public Search_Sequential<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>{
public:
	typedef Search_Sequential<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR> parent;
	typedef typename parent::parent grandparent;
	typedef Search_BIF<RETURNTYPE,DIMTYPE,SUBSET,CRITERION> SearchBIF;
	typedef boost::shared_ptr<SearchBIF> PSearchBIF;
	typedef boost::shared_ptr<EVALUATOR> PEvaluator;
	typedef boost::shared_ptr<CRITERION> PCriterion;
	typedef boost::shared_ptr<SUBSET> PSubset;
	Search_OS(const PEvaluator evaluator) : Search_Sequential<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>(evaluator), _delta(1), track(false) {parent::enable_initial_subset(); notify("Search_OS constructor.");}
	virtual ~Search_OS() {notify("Search_OS destructor.");}

	// NOTE: crit is expected to be already initialize()d before use here
	virtual bool search(const DIMTYPE target_d, RETURNTYPE &result, const PSubset sub, const PCriterion crit, std::ostream& os=std::cout); //!< returns found subset + criterion value

	void set_delta(const DIMTYPE delta) {assert(delta>0); _delta=delta;} //!< oscillating cycle depth
	DIMTYPE get_delta() const {return _delta;}

	virtual std::ostream& print(std::ostream& os) const;
protected:
	enum SwingPhase {
		RESTART=0,
		DOWN=1,
		RETURN_UP=2,
		UP=4,
		RETURN_DOWN=8
	};

	//! standard OS initialized by sub, target dimensionality sub->get_d()
	virtual bool search_one(RETURNTYPE &result, const PSubset sub, const PCriterion crit, std::ostream& os); //!< returns found subset + criterion value
	//! if sub->get_d!=target_d, the contents of sub are either supplemented by best individual features or reduced by removing worst individual features to reach the target_d cardinality
	virtual bool adjust_initial_subset(const DIMTYPE target_d, const PSubset sub, const PSearchBIF bif, std::ostream& os);

	DIMTYPE _delta;

	boost::scoped_ptr<SUBSET> pivotsub;
	boost::scoped_ptr<SUBSET> pivot_top; //!< helper to prevent redundant search when OS cycle depth increases
	boost::scoped_ptr<SUBSET> pivot_bottom; //!< helper to prevent redundant search when OS cycle depth increases

	boost::scoped_ptr<SUBSET> inisub; //!< used when testing all subset sizes
	boost::shared_ptr<SUBSET> tmpsub; //!< used when testing all subset sizes
private:
	bool track;
};

template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
std::ostream& Search_OS<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>::print(std::ostream& os) const
{
	if(parent::get_generalization_level()>1) os << "(G)";
	os << "OS";
	if(parent::get_generalization_level()>1) os << ", G=" << parent::get_generalization_level();
	os << ", delta=" << _delta;
	os << "  [Search_OS()";
	if(parent::_evaluator) os << " with " << *parent::_evaluator;
	if(grandparent::result_tracker_active()) os << " with " << *grandparent::_tracker;
	os << "]";
	return os;
}

template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
bool Search_OS<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>::adjust_initial_subset(const DIMTYPE target_d, const PSubset sub, const PSearchBIF bif, std::ostream& os)
{
	assert(sub);
	assert(bif);
	assert(target_d>0 && target_d<=sub->get_n());
	RETURNTYPE value;
	DIMTYPE f;

	if(target_d!=0 && sub->get_d()!=target_d) {
		if(grandparent::output_normal()) {std::ostringstream sos; sos << "original initial "<< *sub << std::endl << std::flush; syncout::print(os,sos);}
		if(!bif->getFirstBIF(value, f)) return false;
		assert(f>=0 && f<=sub->get_n());
		if(sub->get_d()<target_d) sub->select(f); else sub->deselect(f);
		while(sub->get_d()!=target_d)
		{
			if(!bif->getNextBIF(value, f)) return false;
			assert(f>=0 && f<=sub->get_n());
			if(sub->get_d()<target_d) sub->select(f); else sub->deselect(f);
		}
		if(grandparent::output_normal()) {std::ostringstream sos; sos << "adjusted initial "<< *sub << std::endl << std::endl << std::flush; syncout::print(os,sos);}
	}
	return true;
}

template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
bool Search_OS<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>::search(const DIMTYPE target_d, RETURNTYPE &result, const PSubset sub, const PCriterion crit, std::ostream& os) // returns found subset + criterion value
{
	// NOTE: certain parameter combinations (initial sub->get_d() versus _delta versus target_d versus parent::_generalization_level)
	//       make it impossible to yield a valid solution. In such case search() just returns false
	StopWatch swatch;
	if(parent::_evaluator) parent::_evaluator->set_output_detail(grandparent::get_output_detail()); else throw FST::fst_error("Search_Sequential: Missing evaluator.");
	track=grandparent::result_tracker_active(); if(track) grandparent::_tracker->set_output_detail(grandparent::get_output_detail());
	assert(sub);
	assert(crit);
	assert(sub->get_n()>0);
	assert(target_d>=0 && target_d<sub->get_n());
	assert(parent::get_generalization_level()>=1 && parent::get_generalization_level()<=sub->get_n());

	if(!parent::initial_subset_enabled()) sub->deselect_all();

	if( (_delta<parent::get_generalization_level()) ) return false; // impossible to run the search

	if(grandparent::output_normal()) {
		std::ostringstream sos;
		sos << "---------------------------------------" << std::endl;
		sos << "Starting " << *this << std::endl;
		sos << "with Criterion: " << *crit << std::endl;
		sos << "with initial subset: " << *sub << std::endl;
		if(target_d==0) sos << "Subset size to be optimized." << std::endl; else sos << "Target subset size set to: " << target_d << std::endl;
		sos << std::endl << std::flush;
		syncout::print(os,sos);
	}

	PSearchBIF bif;
	DIMTYPE _n=sub->get_n();

	if(target_d==0) { // evaluate all subset sizes
		if(!inisub || inisub->get_n()!=_n) inisub.reset(new SUBSET(_n));
		if(!tmpsub || tmpsub->get_n()!=_n) tmpsub.reset(new SUBSET(_n));
		RETURNTYPE value;
		if(!bif) {
			bif.reset(new SearchBIF());
			if(!bif->evaluate_individuals(_n, crit, os)) return false;
		}
		inisub->stateless_copy(*sub);
		for(DIMTYPE d=1;d<=_n;d++) {
			tmpsub->stateless_copy(*inisub);
			if(!adjust_initial_subset(d,tmpsub,bif,os)) return false;
			if(d<_n) {if(!search_one(value,tmpsub,crit,os)) return false;} else {if(!crit->evaluate(value,tmpsub)) return false;}
			if(d==1 || value>result) {
				result=value;
				sub->stateless_copy(*tmpsub);
				if(grandparent::output_normal()) {std::ostringstream sos; sos << "NEW MAXVAL (d="<<d<<") ="<<result<<", " << *sub << std::endl << swatch << std::endl << std::endl << std::flush; syncout::print(os,sos);}
			} else
				if(grandparent::output_normal()) {std::ostringstream sos; sos << "result (d="<<d<<") ="<<value<<", " << *tmpsub << std::endl << std::endl << std::flush; syncout::print(os,sos);}
		}
	} else { // evaluate subset of target_d features
		if(sub->get_d()!=target_d) {
			if(!bif) {
				bif.reset(new SearchBIF);
				if(!bif->evaluate_individuals(_n,crit,os)) return false;
			}
			if(!adjust_initial_subset(target_d,sub,bif,os)) return false;
		}
		if(!search_one(result,sub,crit,os)) return false;
	}
	if(grandparent::output_normal()) {
		std::ostringstream sos;
		sos << "......................................." << std::endl;
		sos << "Search_OS() search finished. " << swatch << std::endl;
		sos << "Search result: "<< std::endl << *sub << std::endl << "Criterion value: " << result << std::endl << std::endl << std::flush;
		syncout::print(os,sos);
	}
	return true;
}

template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class CRITERION, class EVALUATOR>
bool Search_OS<RETURNTYPE,DIMTYPE,SUBSET,CRITERION,EVALUATOR>::search_one(RETURNTYPE &result, const PSubset sub, const PCriterion crit, std::ostream& os) // returns found subset + criterion value
{
	notify("Search_OS::search_one().");
	assert(sub);
	assert(crit);
	assert(sub->get_n()>0);
	assert(sub->get_d()>0);
	assert(parent::get_generalization_level()>=1 && parent::get_generalization_level()<=sub->get_n());

	if( (sub->get_d()==0) ||
		(_delta<parent::get_generalization_level()) ) return false; // impossible to run the search

	DIMTYPE _n=sub->get_n();
	if(!pivotsub || pivotsub->get_n()!=_n) pivotsub.reset(new SUBSET(_n));
	if(!pivot_top || pivot_top->get_n()!=_n) pivot_top.reset(new SUBSET(_n));
	if(!pivot_bottom || pivot_bottom->get_n()!=_n) pivot_bottom.reset(new SUBSET(_n));

	RETURNTYPE bestval;

	// sub is assumed to contain the initial subset
	sub->set_forward_mode(true);

	// NOTE: this is not sufficient with Sequential_Step_Hybrid() parametrized _keep_perc==0
	crit->evaluate(result,sub); // store first candidate as the initially best pivot
	if(track) grandparent::_tracker->add(result,sub);

	SwingPhase swing=RESTART;

	DIMTYPE _d=sub->get_d();
	DIMTYPE _d_piv=_d;
	DIMTYPE _cur_delta=parent::get_generalization_level();
	bool pivot_top_avail=false;
	bool pivot_bottom_avail=false;

	bool swinged=false; // to avoid redundant subset copying

	while(_cur_delta<=_delta)
		switch(swing) {
		case RESTART:
			assert(_d_piv==sub->get_d());
			pivotsub->stateless_copy(*sub); bestval=result;
			pivot_top_avail=pivot_bottom_avail=false;
			_cur_delta=parent::get_generalization_level();
			swing=DOWN;
			if(grandparent::output_normal()) {std::ostringstream sos; sos << "new subresult ="<<result<<", " << *sub << std::endl << std::endl << std::flush; syncout::print(os,sos);}
			break;
		case DOWN:
			assert(_d==sub->get_d());
			assert(sub->get_d()!=0);
			if(pivot_bottom_avail) {assert(pivot_bottom->get_d()!=0); sub->stateless_copy(*pivot_bottom); _d=sub->get_d();}
			swinged=false;
			while(_d>parent::get_generalization_level() && _d_piv<_cur_delta+_d){
				if(!parent::Step(false/*forward*/, result, sub, crit, os)) {if(grandparent::output_detailed()) {std::ostringstream sos; sos << "DOWN failure" << std::endl << std::flush; syncout::print(os,sos);} return false;}
				if(track) grandparent::_tracker->add(result,sub);
				_d=sub->get_d(); swinged=true;
			}
			if(swinged) {
				assert(_d==sub->get_d());
				if(_d>parent::get_generalization_level()) {
					pivot_bottom_avail=true;
					pivot_bottom->stateless_copy(*sub);
				}
				swing=RETURN_UP;
			} else {
				pivot_bottom_avail=false;
				swing=UP;
			}
			assert(_d==sub->get_d());
			break;
		case RETURN_UP:
			assert(_d==sub->get_d());
			while(_d<_d_piv){
				assert(_d==sub->get_d());
				if(!parent::Step(true/*forward*/, result, sub, crit, os)) {if(grandparent::output_detailed()) {std::ostringstream sos; sos << "RETURN UP failure" << std::endl << std::flush; syncout::print(os,sos);} return false;}
				if(track) grandparent::_tracker->add(result,sub);
				_d=sub->get_d();
			}
			if(result>bestval) swing=RESTART; // down-swing successful - reset
			else {
				sub->stateless_copy(*pivotsub);
				swing=UP; swinged=false;
			}
			assert(_d==sub->get_d());
			break;
		case UP:
			assert(_d==sub->get_d());
			if(pivot_top_avail) {assert(pivot_top->get_d()!=0); sub->stateless_copy(*pivot_top); _d=sub->get_d();}
			while(_d+parent::get_generalization_level()<=_n && _d<_cur_delta+_d_piv){
				assert(_d==sub->get_d());
				if(!parent::Step(true/*forward*/, result, sub, crit, os)) {if(grandparent::output_detailed()) {std::ostringstream sos; sos << "UP failure" << std::endl << std::flush; syncout::print(os,sos);} return false;}
				if(track) grandparent::_tracker->add(result,sub);
				_d=sub->get_d(); swinged=true;
			}
			if(swinged) {
				assert(_d==sub->get_d());
				if(_d+parent::get_generalization_level()<=_n) {
					pivot_top_avail=true;
					pivot_top->stateless_copy(*sub);
				}
				swing=RETURN_DOWN;
			} else {
				pivot_top_avail=false;
				swing=DOWN;
				_cur_delta+=parent::get_generalization_level();
			}
			assert(_d==sub->get_d());
			break;
		case RETURN_DOWN:
			assert(_d==sub->get_d());
			while(_d>_d_piv){
				assert(_d==sub->get_d());
				if(!parent::Step(false/*forward*/, result, sub, crit, os)) {if(grandparent::output_detailed()) {std::ostringstream sos; sos << "RETURN DOWN failure" << std::endl << std::flush; syncout::print(os,sos);} return false;}
				if(track) grandparent::_tracker->add(result,sub);
				_d=sub->get_d();
			}
			if(result>bestval) swing=RESTART; // up-swing successful - reset
			else {
				sub->stateless_copy(*pivotsub);
				swing=DOWN; swinged=false;
				_cur_delta+=parent::get_generalization_level();
			}
			assert(_d==sub->get_d());
			break;
		}

	if(pivotsub->get_d()!=0) {
		sub->stateless_copy(*pivotsub);
		result=bestval;
		return true;
	}
	return false;
}

} // namespace
#endif // FSTSEARCHSEQOS_H ///:~


#include <algorithm>
#include <functional>
#include <boost/serialization/export.hpp>
#include "Dropout.hpp"
#include "../layer/BaseLayer.hpp"

using namespace std;
using namespace snn;
using namespace internal;

BOOST_CLASS_EXPORT(Dropout)

bool Dropout::randomProbability() const
{
    return rand() / static_cast<float>(RAND_MAX) >= this->value;
}

Dropout::Dropout(const float value, BaseLayer* layer)
    : LayerOptimizer(layer), value(value)
{
    this->reverseValue = 1.0f - this->value;
    auto size = layer->getNumberOfNeurons();
    this->presenceProbabilities.resize(size);
    std::generate(this->presenceProbabilities.begin(), this->presenceProbabilities.end(), [&]() mutable { return this->randomProbability(); });
}

Dropout::Dropout(const Dropout& dropout, const BaseLayer* layer)
    : LayerOptimizer(layer)
{
    this->value = dropout.value;
    this->reverseValue = dropout.reverseValue;
    this->presenceProbabilities = dropout.presenceProbabilities;
}

unique_ptr<LayerOptimizer> Dropout::clone(const BaseLayer* newLayer) const
{
    return make_unique<Dropout>(*this, newLayer);
}

void Dropout::applyAfterOutputForTraining(std::vector<float>& outputs, bool temporalReset)
{
    if (temporalReset)
        std::generate(this->presenceProbabilities.begin(), this->presenceProbabilities.end(), [&]() mutable { return this->randomProbability(); });
    ranges::transform(outputs, this->presenceProbabilities, outputs.begin(), multiplies<float>());
}

void Dropout::applyAfterOutputForTesting(std::vector<float>& outputs)
{
    ranges::transform(outputs, outputs.begin(), bind(multiplies<float>(), placeholders::_1, this->reverseValue));
}

void Dropout::applyBeforeBackpropagation(std::vector<float>& inputErrors)
{
    ranges::transform(inputErrors, this->presenceProbabilities, inputErrors.begin(), multiplies<float>());
}

bool Dropout::operator==(const LayerOptimizer& optimizer) const
{
    try
    {
        const auto& o = dynamic_cast<const Dropout&>(optimizer);
        return this->LayerOptimizer::operator==(optimizer)
            && this->value == o.value
            && this->reverseValue == o.reverseValue;
    }
    catch (bad_cast&)
    {
        return false;
    }
}

bool Dropout::operator!=(const LayerOptimizer& optimizer) const
{
    return !(*this == optimizer);
}


#include "game.h"
#include "cpu_parity.h"
#include "cpu_bf.h"
#include "cpu_list_ranking.h"
#include "cpu_vj.h"
#include "cpu_bv.h"
#include "valuation.h"
#include <fstream>
#include <iostream>
#include <sys/time.h>
#include <stdlib.h>

#include <boost/algorithm/string/predicate.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/bzip2.hpp>
#include <boost/iostreams/filter/gzip.hpp>

using namespace std;

void solve_si(StrategyImprovement& si, int* br_count, int* iter_count, bool reset)
{
    si.init_strat();

    bool first = true;
    while(true)
    {
        if(not first and reset)
            si.reset_opponent_strategy();
        while(true)
        {
            if(first)
                si.compute_first_val();
            else
                si.compute_valuation();

            (*br_count)++;
            si.mark_solved(1);

            if(si.switch_strategy(1) == 0)
                break;
        }

        first = false;

        si.mark_solved(0);
        (*iter_count)++;

        int total_switched = si.switch_strategy(0);
        //cout << "Total switched: " << total_switched << endl;
        if(total_switched == 0)
            break;
    }

}

template <typename T> void solve_bf(CPU_BellmanFord<T>& si, int* br_count, int* iter_count, bool reset)
{
    si.init_strat();

    while(true)
    {

        si.best_response(br_count);

        si.mark_solved(0);

        (*iter_count)++;

        int total_switched = si.switch_strategy(0);
        //cout << "Total switched: " << total_switched << endl;
        if(total_switched == 0)
            break;
    }

    //cout << "Solved after " << iter_count << " iterations" << endl;
}

// Run a timed test of a strategy improvement algorithm
template <typename T> double test(T& si, void (*f)(T&, int*, int*, bool), bool reset)
{
    timeval start;
    gettimeofday(&start, nullptr);
    int iter_count = 0;
    int br_count = 0;

    (*f)(si, &br_count, &iter_count, reset);

    timeval end;
    gettimeofday(&end, nullptr);

    double solve_time = (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec)/1000000.0;

    cout << std::fixed << solve_time << " " << iter_count << " " << br_count << endl;
}

// Checks that alg1 and alg2 give the same answer
bool verify(StrategyImprovement& alg1, StrategyImprovement& alg2)
{
    for(int p = 0; p < 2; p++)
    {
        vector<int> alg1out;
        vector<int> alg2out;

        alg1.get_winning(alg1out, p);
        alg2.get_winning(alg2out, p);

        //cout << "winning set";
        //for(auto v : alg1out)
            //cout << v << " ";
        //cout << endl;

        if(alg1out.size() != alg2out.size())
        {
            return false;
        }

        for(int i = 0; i < alg1out.size(); i++)
            if(alg1out[i] != alg2out[i])
                return false;
    }
    return true;
}

int main(int argc, char** argv)
{
    if(argc < 3)
    {
        cout << "usage: " << argv[0] << " [algorithm] [game] [threads-for-cpulist]" << endl << endl;
        cout << "    algorithms: cpu, cpubf, cpu-reset, cpubv, cpulist" << endl << endl;
        return 1;
    }

    string algorithm = argv[1];
    string filename = argv[2];

    Game g;

    try {
        ifstream file(filename, ios_base::binary);
        boost::iostreams::filtering_istream in;
        if (boost::algorithm::ends_with(filename, ".bz2")) in.push(boost::iostreams::bzip2_decompressor());
        if (boost::algorithm::ends_with(filename, ".gz")) in.push(boost::iostreams::gzip_decompressor());
        in.push(file);
        g.parse_pgsolver(in);
        file.close();
    } catch (const char *err) {
        std::cerr << "parsing error: " << err << std::endl;
        return -1;
    }

    if(algorithm == "cpu")
    {
        CPUParity cpu = CPUParity(g);
        test<StrategyImprovement>(cpu, &solve_si, false);
    }
    else if(algorithm == "cpu-reset")
    {
        CPUParity cpu = CPUParity(g);
        test<StrategyImprovement>(cpu, &solve_si, true);
    }
    else if(algorithm == "cpubv")
    {
        CPUBV<ArrayVal> cpu = CPUBV<ArrayVal>(g);
        test<StrategyImprovement>(cpu, &solve_si, false);
    }
    //else if(algorithm == "cpubvmap")
    //{
        //CPUBV<MapVal> cpu = CPUBV<MapVal>(g);
        //test<StrategyImprovement>(cpu, &solve_si);
        ////CPUBV<ArrayVal> cpu2 = CPUBV<ArrayVal>(g);
        ////test<StrategyImprovement>(cpu2, &solve_si);
        ////cout << verify(cpu, cpu2) << endl;
    //}
    else if(algorithm == "cpubf")
    {
        CPU_BellmanFord<ArrayVal> cpu_bf = CPU_BellmanFord<ArrayVal>(g);
        test(cpu_bf, &solve_bf, false);

    }
    else if(algorithm == "cpulist")
    {
        int w = 8; // default: 8 threads
        if (argc > 3) w = atoi(argv[3]);
        if (w <= 0) {
            cout << "invalid number of threads." << endl;
        } else {
            CPUList<ArrayVal> cpu_list = CPUList<ArrayVal>(g, w);
            test<StrategyImprovement>(cpu_list, &solve_si, false);
        }

    }
    //else if(algorithm == "cpuvj")
    //{
        //Game g2 = g;
        //g2.unique_priority_transform();
        //CPUVJ<MapVal> cpu_vj = CPUVJ<MapVal>(g2);
        //test<StrategyImprovement>(cpu_vj, &solve_si);

        ////GPUGame gpu_g(g);
        ////GPUList gpu_list(g, gpu_g);
        ////test<StrategyImprovement>(gpu_list, &solve_si);
        ////cout << verify(cpu_vj, gpu_list) << endl;
    //}
    else
    {
        cout << "unknown algorithm: " << algorithm << endl;
    }

}


// This file is part of GLviz.
//
// Copyright(c) 2014, 2015 Sebastian Lipponer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

#include "utility.hpp"

#include <GL/glew.h>

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

namespace GLviz
{

void
load_raw(std::string const& filename, std::vector<Eigen::Vector3f>& vertices,
    std::vector<std::array<unsigned int, 3> >& faces)
{
    std::ifstream input(filename, std::ios::in | std::ios::binary);

    if (input.fail())
    {
        std::ostringstream error_message;
        error_message << "Error: Can not open "
            << filename << "." << std::endl;

        throw std::runtime_error(error_message.str().c_str());
    }

    unsigned int nv;
    input.read(reinterpret_cast<char*>(&nv), sizeof(unsigned int));
    vertices.resize(nv);

    for (unsigned int i(0); i < nv; ++i)
    {
        input.read(reinterpret_cast<char*>(vertices[i].data()),
            3 * sizeof(float));
    }

    unsigned int nf;
    input.read(reinterpret_cast<char*>(&nf), sizeof(unsigned int));
    faces.resize(nf);

    for (unsigned int i(0); i < nf; ++i)
    {
        input.read(reinterpret_cast<char*>(faces[i].data()),
            3 * sizeof(unsigned int));
    }

    input.close();
}

void
save_raw(std::string const& filename, std::vector<Eigen::Vector3f>
    const& vertices, std::vector<std::array<unsigned int, 3> >& faces)
{
    std::ofstream output(filename, std::ios::out | std::ios::binary);

    if (output.fail())
    {
        std::ostringstream error_message;
        error_message << "Error: Can not open "
            << filename << "." << std::endl;

        throw std::runtime_error(error_message.str().c_str());
    }

    unsigned int nv = static_cast<unsigned int>(vertices.size());
    output.write(reinterpret_cast<char const*>(&nv), sizeof(unsigned int));

    for (unsigned int i(0); i < nv; ++i)
    {
        output.write(reinterpret_cast<char const*>(vertices[i].data()),
            3 * sizeof(float));
    }

    unsigned int nf = static_cast<unsigned int>(faces.size());
    output.write(reinterpret_cast<char const*>(&nf), sizeof(unsigned int));

    for (unsigned int i(0); i < nf; ++i)
    {
        output.write(reinterpret_cast<char const*>(faces[i].data()),
            3 * sizeof(unsigned int));
    }

    output.close();
}

void
set_vertex_normals_from_triangle_mesh(std::vector<Eigen::Vector3f>
    const& vertices, std::vector<std::array<unsigned int, 3> > const& faces,
    std::vector<Eigen::Vector3f>& normals)
{
    unsigned int nf(static_cast<unsigned int>(faces.size())),
        nv(static_cast<unsigned int>(vertices.size()));

    normals.resize(vertices.size());
    std::fill(normals.begin(), normals.end(), Eigen::Vector3f::Zero());

    for (unsigned int i(0); i < nf; ++i)
    {
        std::array<unsigned int, 3> const& f_i = faces[i];

        Eigen::Vector3f const& p0(vertices[f_i[0]]);
        Eigen::Vector3f const& p1(vertices[f_i[1]]);
        Eigen::Vector3f const& p2(vertices[f_i[2]]);

        Eigen::Vector3f n_i = (p0 - p1).cross(p0 - p2);

        normals[f_i[0]] += n_i;
        normals[f_i[1]] += n_i;
        normals[f_i[2]] += n_i;
    }

    for (unsigned int i(0); i < nv; ++i)
    {
        if (!normals[i].isZero())
        {
            normals[i].normalize();
        }
    }
}

std::string
get_gl_error_string(GLenum gl_error)
{
    std::string error_string;

    switch (gl_error)
    {
    case GL_NO_ERROR:
        error_string = "GL_NO_ERROR";
        break;
    case GL_INVALID_ENUM:
        error_string = "GL_INVALID_ENUM";
        break;
    case GL_INVALID_VALUE:
        error_string = "GL_INVALID_VALUE";
        break;
    case GL_INVALID_OPERATION:
        error_string = "GL_INVALID_OPERATION";
        break;
    case GL_INVALID_FRAMEBUFFER_OPERATION:
        error_string = "GL_INVALID_FRAMEBUFFER_OPERATION";
        break;
    case GL_OUT_OF_MEMORY:
        error_string = "GL_OUT_OF_MEMORY";
        break;
    default:
        error_string = "UNKNOWN";
    }

    return error_string;
}

std::string
get_gl_framebuffer_status_string(GLenum framebuffer_status)
{
    std::string status_string;

    switch (framebuffer_status)
    {
        case GL_FRAMEBUFFER_COMPLETE:
            status_string = "GL_FRAMEBUFFER_COMPLETE";
            break;

        case GL_FRAMEBUFFER_UNDEFINED:
            status_string = "GL_FRAMEBUFFER_UNDEFINED";
            break;

        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
            status_string = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
            break;

        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
            status_string = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
            break;

        case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
            status_string = "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER";
            break;

        case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
            status_string = "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER";
            break;

        case GL_FRAMEBUFFER_UNSUPPORTED:
            status_string = "GL_FRAMEBUFFER_UNSUPPORTED";
            break;

        case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
            status_string = "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
            break;

        case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
            status_string = "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS";
            break;

        default:
            status_string = "UNKNOWN";
    }

    return status_string;
}

}


#ifndef TYPES_HPP
#define TYPES_HPP
#include <cstdint>
#include <unordered_map>
#include <cstdint>
#include <map>
#include <vector>
#include <boost/functional/hash.hpp>

typedef uint32_t formid;

struct rgba
{
    uint8_t r,g,b,a;
};

template <typename V>
using IDHash = std::unordered_map<formid, V>;

using Block = std::pair<int32_t, int32_t>;
template <typename V>
using MapHash = std::unordered_map <Block, IDHash<V>>;

namespace std
{
template<typename S, typename T> struct hash<pair<S, T>>
{

    inline size_t operator()(const pair<S, T> & v) const
    {
        size_t seed = 0;
        boost::hash_combine(seed, v.first);
        boost::hash_combine(seed, v.second);
        return seed;
    }
};
}

#pragma pack(push, 1)

struct ObjectBounds
{
    uint16_t x1, y1, z1;
    uint16_t x2, y2, z2;
};
#pragma pack(pop)
#endif // TYPES_HPP


//=======================================================================
// Copyright 2009 Trustees of Indiana University.
// Authors: Michael Hansen, Andrew Lumsdaine
//
// 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_GRAPH_GRID_GRAPH_HPP
#define BOOST_GRAPH_GRID_GRAPH_HPP

#include <cmath>
#include <functional>
#include <numeric>

#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/limits.hpp>
#include <boost/make_shared.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/properties.hpp>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/property_map/property_map.hpp>

#define BOOST_GRID_GRAPH_TEMPLATE_PARAMS \
  std::size_t DimensionsT, typename VertexIndexT, \
    typename EdgeIndexT

#define BOOST_GRID_GRAPH_TYPE \
  grid_graph<DimensionsT, VertexIndexT, EdgeIndexT>

#define BOOST_GRID_GRAPH_TYPE_MEM typename BOOST_GRID_GRAPH_TYPE::

#define BOOST_GRID_GRAPH_TYPE_TD(mem) \
  typedef typename BOOST_GRID_GRAPH_TYPE::mem mem

#define BOOST_GRID_GRAPH_TRAITS_T \
  typename graph_traits<BOOST_GRID_GRAPH_TYPE >

namespace boost {

  // Class prototype for grid_graph
  template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
  class grid_graph;

  //===================
  // Index Property Map
  //===================

  template <typename Graph,
            typename Descriptor,
            typename Index>
  struct grid_graph_index_map {
  public:
    typedef Index value_type;
    typedef Index reference_type;
    typedef reference_type reference;
    typedef Descriptor key_type;
    typedef readable_property_map_tag category;

    grid_graph_index_map() { }

    grid_graph_index_map(const Graph& graph) :
      m_graph(make_shared<Graph>(graph)) { }

    value_type operator[](key_type key) const {
      return (m_graph->index_of(key));
    }

  protected:
    shared_ptr<Graph> m_graph;
  };

  template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
  struct property_map<BOOST_GRID_GRAPH_TYPE, vertex_index_t> {
    typedef grid_graph_index_map<BOOST_GRID_GRAPH_TYPE,
                                 BOOST_GRID_GRAPH_TRAITS_T::vertex_descriptor,
                                 BOOST_GRID_GRAPH_TRAITS_T::vertices_size_type> type;
    typedef type const_type;
  };

  template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
  struct property_map<BOOST_GRID_GRAPH_TYPE, edge_index_t> {
    typedef grid_graph_index_map<BOOST_GRID_GRAPH_TYPE,
                                 BOOST_GRID_GRAPH_TRAITS_T::edge_descriptor,
                                 BOOST_GRID_GRAPH_TRAITS_T::edges_size_type> type;
    typedef type const_type;
  };

  //=================
  // Function Objects
  //=================

  namespace detail {

    // vertex_at
    template <typename Graph>
    struct grid_graph_vertex_at {

      typedef typename graph_traits<Graph>::vertex_descriptor result_type;

      grid_graph_vertex_at() : m_graph(0) {}

      grid_graph_vertex_at(const Graph* graph) :
        m_graph(graph) { }

      result_type
      operator()
      (typename graph_traits<Graph>::vertices_size_type vertex_index) const {
        return (vertex(vertex_index, *m_graph));
      }

    private:
      const Graph* m_graph;
    };

    // out_edge_at
    template <typename Graph>
    struct grid_graph_out_edge_at {

    private:
      typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;

    public:
      typedef typename graph_traits<Graph>::edge_descriptor result_type;

      grid_graph_out_edge_at() : m_vertex(), m_graph(0) {}

      grid_graph_out_edge_at(vertex_descriptor source_vertex,
                             const Graph* graph) :
        m_vertex(source_vertex),
        m_graph(graph) { }

      result_type
      operator()
      (typename graph_traits<Graph>::degree_size_type out_edge_index) const {
        return (out_edge_at(m_vertex, out_edge_index, *m_graph));
      }

    private:
      vertex_descriptor m_vertex;
      const Graph* m_graph;
    };

    // in_edge_at
    template <typename Graph>
    struct grid_graph_in_edge_at {

    private:
      typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;

    public:
      typedef typename graph_traits<Graph>::edge_descriptor result_type;

      grid_graph_in_edge_at() : m_vertex(), m_graph(0) {}

      grid_graph_in_edge_at(vertex_descriptor target_vertex,
                            const Graph* graph) :
        m_vertex(target_vertex),
        m_graph(graph) { }

      result_type
      operator()
      (typename graph_traits<Graph>::degree_size_type in_edge_index) const {
        return (in_edge_at(m_vertex, in_edge_index, *m_graph));
      }

    private:
      vertex_descriptor m_vertex;
      const Graph* m_graph;
    };

    // edge_at
    template <typename Graph>
    struct grid_graph_edge_at {

      typedef typename graph_traits<Graph>::edge_descriptor result_type;

      grid_graph_edge_at() : m_graph(0) {}

      grid_graph_edge_at(const Graph* graph) :
        m_graph(graph) { }

      result_type
      operator()
      (typename graph_traits<Graph>::edges_size_type edge_index) const {
        return (edge_at(edge_index, *m_graph));
      }

    private:
      const Graph* m_graph;
    };

    // adjacent_vertex_at
    template <typename Graph>
    struct grid_graph_adjacent_vertex_at {

    public:
      typedef typename graph_traits<Graph>::vertex_descriptor result_type;

      grid_graph_adjacent_vertex_at(result_type source_vertex,
                                    const Graph* graph) :
        m_vertex(source_vertex),
        m_graph(graph) { }

      result_type
      operator()
      (typename graph_traits<Graph>::degree_size_type adjacent_index) const {
        return (target(out_edge_at(m_vertex, adjacent_index, *m_graph), *m_graph));
      }

    private:
      result_type m_vertex;
      const Graph* m_graph;
    };

  } // namespace detail

  //===========
  // Grid Graph
  //===========

  template <std::size_t Dimensions,
            typename VertexIndex = std::size_t,
            typename EdgeIndex = VertexIndex>
  class grid_graph {

  private:
    typedef boost::array<bool, Dimensions> WrapDimensionArray;
    grid_graph() { };

  public:

    typedef grid_graph<Dimensions, VertexIndex, EdgeIndex> type;

    // sizes
    typedef VertexIndex vertices_size_type;
    typedef EdgeIndex edges_size_type;
    typedef EdgeIndex degree_size_type;

    // descriptors
    typedef boost::array<VertexIndex, Dimensions> vertex_descriptor;
    typedef std::pair<vertex_descriptor, vertex_descriptor> edge_descriptor;

    // vertex_iterator
    typedef counting_iterator<vertices_size_type> vertex_index_iterator;
    typedef detail::grid_graph_vertex_at<type> vertex_function;
    typedef transform_iterator<vertex_function, vertex_index_iterator> vertex_iterator;

    // edge_iterator
    typedef counting_iterator<edges_size_type> edge_index_iterator;
    typedef detail::grid_graph_edge_at<type> edge_function;
    typedef transform_iterator<edge_function, edge_index_iterator> edge_iterator;

    // out_edge_iterator
    typedef counting_iterator<degree_size_type> degree_iterator;
    typedef detail::grid_graph_out_edge_at<type> out_edge_function;
    typedef transform_iterator<out_edge_function, degree_iterator> out_edge_iterator;

    // in_edge_iterator
    typedef detail::grid_graph_in_edge_at<type> in_edge_function;
    typedef transform_iterator<in_edge_function, degree_iterator> in_edge_iterator;

    // adjacency_iterator
    typedef detail::grid_graph_adjacent_vertex_at<type> adjacent_vertex_function;
    typedef transform_iterator<adjacent_vertex_function, degree_iterator> adjacency_iterator;

    // categories
    typedef undirected_tag directed_category;
    typedef disallow_parallel_edge_tag edge_parallel_category;
    struct traversal_category : virtual public incidence_graph_tag,
                                virtual public adjacency_graph_tag,
                                virtual public vertex_list_graph_tag,
                                virtual public edge_list_graph_tag,
                                virtual public bidirectional_graph_tag,
                                virtual public adjacency_matrix_tag { };

    static inline vertex_descriptor null_vertex()
    {
      vertex_descriptor maxed_out_vertex;
      std::fill(maxed_out_vertex.begin(), maxed_out_vertex.end(),
                (std::numeric_limits<vertices_size_type>::max)());

      return (maxed_out_vertex);
    }

    // Constructor that defaults to no wrapping for all dimensions.
    grid_graph(vertex_descriptor dimension_lengths) :
      m_dimension_lengths(dimension_lengths) {

      std::fill(m_wrap_dimension.begin(),
                m_wrap_dimension.end(), false);

      precalculate();
    }

    // Constructor that allows for wrapping to be specified for all
    // dimensions at once.
    grid_graph(vertex_descriptor dimension_lengths,
               bool wrap_all_dimensions) :
      m_dimension_lengths(dimension_lengths) {

      std::fill(m_wrap_dimension.begin(),
                m_wrap_dimension.end(),
                wrap_all_dimensions);

      precalculate();
    }

    // Constructor that allows for individual dimension wrapping to be
    // specified.
    grid_graph(vertex_descriptor dimension_lengths,
               WrapDimensionArray wrap_dimension) :
      m_dimension_lengths(dimension_lengths),
      m_wrap_dimension(wrap_dimension) {

      precalculate();
    }

    // Returns the number of dimensions in the graph
    inline std::size_t dimensions() const {
      return (Dimensions);
    }

    // Returns the length of dimension [dimension_index]
    inline vertices_size_type length(std::size_t dimension) const {
      return (m_dimension_lengths[dimension]);
    }

    // Returns a value indicating if dimension [dimension_index] wraps
    inline bool wrapped(std::size_t dimension) const {
      return (m_wrap_dimension[dimension]);
    }

    // Gets the vertex that is [distance] units ahead of [vertex] in
    // dimension [dimension_index].
    vertex_descriptor next
    (vertex_descriptor vertex,
     std::size_t dimension_index,
     vertices_size_type distance = 1) const {

      vertices_size_type new_position =
        vertex[dimension_index] + distance;

      if (wrapped(dimension_index)) {
        new_position %= length(dimension_index);
      }
      else {
        // Stop at the end of this dimension if necessary.
        new_position =
          (std::min)(new_position,
                     length(dimension_index) - 1);
      }

      vertex[dimension_index] = new_position;

      return (vertex);
    }

    // Gets the vertex that is [distance] units behind [vertex] in
    // dimension [dimension_index].
    vertex_descriptor previous
    (vertex_descriptor vertex,
     std::size_t dimension_index,
     vertices_size_type distance = 1) const {

      // We're assuming that vertices_size_type is unsigned, so we
      // need to be careful about the math.
      vertex[dimension_index] =
        (distance > vertex[dimension_index]) ?
        (wrapped(dimension_index) ?
         (length(dimension_index) - (distance % length(dimension_index))) : 0) :
        vertex[dimension_index] - distance;

      return (vertex);
    }

  protected:

    // Returns the number of vertices in the graph
    inline vertices_size_type num_vertices() const {
      return (m_num_vertices);
    }

    // Returns the number of edges in the graph
    inline edges_size_type num_edges() const {
      return (m_num_edges);
    }

    // Returns the number of edges in dimension [dimension_index]
    inline edges_size_type num_edges
    (std::size_t dimension_index) const {
      return (m_edge_count[dimension_index]);
    }

    // Returns the index of [vertex] (See also vertex_at)
    vertices_size_type index_of(vertex_descriptor vertex) const {

      vertices_size_type vertex_index = 0;
      vertices_size_type index_multiplier = 1;

      for (std::size_t dimension_index = 0;
           dimension_index < Dimensions;
           ++dimension_index) {

        vertex_index += (vertex[dimension_index] * index_multiplier);
        index_multiplier *= length(dimension_index);
      }

      return (vertex_index);
    }

    // Returns the vertex whose index is [vertex_index] (See also
    // index_of(vertex_descriptor))
    vertex_descriptor vertex_at
    (vertices_size_type vertex_index) const {

      array<vertices_size_type, Dimensions> vertex;
      vertices_size_type index_divider = 1;

      for (std::size_t dimension_index = 0;
           dimension_index < Dimensions;
           ++dimension_index) {

        vertex[dimension_index] = (vertex_index / index_divider) %
          length(dimension_index);

        index_divider *= length(dimension_index);
      }

      return (vertex);
    }

    // Returns the edge whose index is [edge_index] (See also
    // index_of(edge_descriptor)).  NOTE: The index mapping is
    // dependent upon dimension wrapping.
    edge_descriptor edge_at(edges_size_type edge_index) const {

      // Edge indices are sorted into bins by dimension
      std::size_t dimension_index = 0;
      edges_size_type dimension_edges = num_edges(0);

      while (edge_index >= dimension_edges) {
        edge_index -= dimension_edges;
        ++dimension_index;
        dimension_edges = num_edges(dimension_index);
      }

      vertex_descriptor vertex_source, vertex_target;
      bool is_forward = ((edge_index / (num_edges(dimension_index) / 2)) == 0);

      if (wrapped(dimension_index)) {
        vertex_source = vertex_at(edge_index % num_vertices());
        vertex_target = is_forward ?
          next(vertex_source, dimension_index) :
          previous(vertex_source, dimension_index);
      }
      else {

        // Dimensions can wrap arbitrarily, so an index needs to be
        // computed in a more complex manner.  This is done by
        // grouping the edges for each dimension together into "bins"
        // and considering [edge_index] as an offset into the bin.
        // Each bin consists of two parts: the "forward" looking edges
        // and the "backward" looking edges for the dimension.

        edges_size_type vertex_offset = edge_index % num_edges(dimension_index);

        // Consider vertex_offset an index into the graph's vertex
        // space but with the dimension [dimension_index] reduced in
        // size by one.
        vertices_size_type index_divider = 1;

        for (std::size_t dimension_index_iter = 0;
             dimension_index_iter < Dimensions;
             ++dimension_index_iter) {

          std::size_t dimension_length = (dimension_index_iter == dimension_index) ?
            length(dimension_index_iter) - 1 :
            length(dimension_index_iter);

          vertex_source[dimension_index_iter] = (vertex_offset / index_divider) %
            dimension_length;

          index_divider *= dimension_length;
        }

        if (is_forward) {
          vertex_target = next(vertex_source, dimension_index);
        }
        else {
          // Shift forward one more unit in the dimension for backward
          // edges since the algorithm above will leave us one behind.
          vertex_target = vertex_source;
          ++vertex_source[dimension_index];
        }

      } // if (wrapped(dimension_index))

      return (std::make_pair(vertex_source, vertex_target));
    }

    // Returns the index for [edge] (See also edge_at)
    edges_size_type index_of(edge_descriptor edge) const {
      vertex_descriptor source_vertex = source(edge, *this);
      vertex_descriptor target_vertex = target(edge, *this);

      // Determine the dimension where the source and target vertices
      // differ (should only be one if this is a valid edge).
      std::size_t different_dimension_index = 0;

      while (source_vertex[different_dimension_index] ==
             target_vertex[different_dimension_index]) {

        ++different_dimension_index;
      }

      edges_size_type edge_index = 0;

      // Offset the edge index into the appropriate "bin" (see edge_at
      // for a more in-depth description).
      for (std::size_t dimension_index = 0;
           dimension_index < different_dimension_index;
           ++dimension_index) {

        edge_index += num_edges(dimension_index);
      }

      // Get the position of both vertices in the differing dimension.
      vertices_size_type source_position = source_vertex[different_dimension_index];
      vertices_size_type target_position = target_vertex[different_dimension_index];

      // Determine if edge is forward or backward
      bool is_forward = true;

      if (wrapped(different_dimension_index)) {

        // If the dimension is wrapped, an edge is going backward if
        // either A: its target precedes the source in the differing
        // dimension and the vertices are adjacent or B: its source
        // precedes the target and they're not adjacent.
        if (((target_position < source_position) &&
             ((source_position - target_position) == 1)) ||
            ((source_position < target_position) &&
             ((target_position - source_position) > 1))) {

          is_forward = false;
        }
      }
      else if (target_position < source_position) {
        is_forward = false;
      }

      // "Backward" edges are in the second half of the bin.
      if (!is_forward) {
        edge_index += (num_edges(different_dimension_index) / 2);
      }

      // Finally, apply the vertex offset
      if (wrapped(different_dimension_index)) {
        edge_index += index_of(source_vertex);
      }
      else {
        vertices_size_type index_multiplier = 1;

        if (!is_forward) {
          --source_vertex[different_dimension_index];
        }

        for (std::size_t dimension_index = 0;
             dimension_index < Dimensions;
             ++dimension_index) {

          edge_index += (source_vertex[dimension_index] * index_multiplier);
          index_multiplier *= (dimension_index == different_dimension_index) ?
            length(dimension_index) - 1 :
            length(dimension_index);
        }
      }

      return (edge_index);
    }

    // Returns the number of out-edges for [vertex]
    degree_size_type out_degree(vertex_descriptor vertex) const {

      degree_size_type out_edge_count = 0;

      for (std::size_t dimension_index = 0;
           dimension_index < Dimensions;
           ++dimension_index) {

        // If the vertex is on the edge of this dimension, then its
        // number of out edges is dependent upon whether the dimension
        // wraps or not.
        if ((vertex[dimension_index] == 0) ||
            (vertex[dimension_index] == (length(dimension_index) - 1))) {
          out_edge_count += (wrapped(dimension_index) ? 2 : 1);
        }
        else {
          // Next and previous edges, regardless or wrapping
          out_edge_count += 2;
        }
      }

      return (out_edge_count);
    }

    // Returns an out-edge for [vertex] by index. Indices are in the
    // range [0, out_degree(vertex)).
    edge_descriptor out_edge_at
    (vertex_descriptor vertex,
     degree_size_type out_edge_index) const {

      edges_size_type edges_left = out_edge_index + 1;
      std::size_t dimension_index = 0;
      bool is_forward = false;

      // Walks the out edges of [vertex] and accommodates for dimension
      // wrapping.
      while (edges_left > 0) {

        if (!wrapped(dimension_index)) {
          if (!is_forward && (vertex[dimension_index] == 0)) {
            is_forward = true;
            continue;
          }
          else if (is_forward &&
                   (vertex[dimension_index] == (length(dimension_index) - 1))) {
            is_forward = false;
            ++dimension_index;
            continue;
          }
        }

        --edges_left;

        if (edges_left > 0) {
          is_forward = !is_forward;

          if (!is_forward) {
            ++dimension_index;
          }
        }
      }

      return (std::make_pair(vertex, is_forward ?
                             next(vertex, dimension_index) :
                             previous(vertex, dimension_index)));
    }

    // Returns the number of in-edges for [vertex]
    inline degree_size_type in_degree(vertex_descriptor vertex) const {
      return (out_degree(vertex));
    }

    // Returns an in-edge for [vertex] by index. Indices are in the
    // range [0, in_degree(vertex)).
    edge_descriptor in_edge_at
    (vertex_descriptor vertex,
     edges_size_type in_edge_index) const {

      edge_descriptor out_edge = out_edge_at(vertex, in_edge_index);
      return (std::make_pair(target(out_edge, *this), source(out_edge, *this)));

    }

    // Pre-computes the number of vertices and edges
    void precalculate() {
      m_num_vertices =
        std::accumulate(m_dimension_lengths.begin(),
                        m_dimension_lengths.end(), 1,
                        std::multiplies<vertices_size_type>());

      // Calculate number of edges in each dimension
      m_num_edges = 0;

      for (std::size_t dimension_index = 0;
           dimension_index < Dimensions;
           ++dimension_index) {

        if (wrapped(dimension_index)) {
          m_edge_count[dimension_index] = num_vertices() * 2;
        }
        else {
          m_edge_count[dimension_index] =
            (num_vertices() - (num_vertices() / length(dimension_index))) * 2;
        }

        m_num_edges += num_edges(dimension_index);
      }
    }

    const vertex_descriptor m_dimension_lengths;
    WrapDimensionArray m_wrap_dimension;
    vertices_size_type m_num_vertices;

    boost::array<edges_size_type, Dimensions> m_edge_count;
    edges_size_type m_num_edges;

  public:

    //================
    // VertexListGraph
    //================

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline std::pair<BOOST_GRID_GRAPH_TYPE_MEM vertex_iterator,
                            BOOST_GRID_GRAPH_TYPE_MEM vertex_iterator>
    vertices(const BOOST_GRID_GRAPH_TYPE& graph) {
      BOOST_GRID_GRAPH_TYPE_TD(vertex_iterator);
      BOOST_GRID_GRAPH_TYPE_TD(vertex_function);
      BOOST_GRID_GRAPH_TYPE_TD(vertex_index_iterator);

      return (std::make_pair
              (vertex_iterator(vertex_index_iterator(0),
                               vertex_function(&graph)),
               vertex_iterator(vertex_index_iterator(graph.num_vertices()),
                               vertex_function(&graph))));
    }

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline  BOOST_GRID_GRAPH_TYPE_MEM vertices_size_type
    num_vertices(const BOOST_GRID_GRAPH_TYPE& graph) {
      return (graph.num_vertices());
    }

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline BOOST_GRID_GRAPH_TYPE_MEM vertex_descriptor
    vertex(BOOST_GRID_GRAPH_TYPE_MEM vertices_size_type vertex_index,
           const BOOST_GRID_GRAPH_TYPE& graph) {

      return (graph.vertex_at(vertex_index));
    }

    //===============
    // IncidenceGraph
    //===============

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline std::pair<BOOST_GRID_GRAPH_TYPE_MEM out_edge_iterator,
                            BOOST_GRID_GRAPH_TYPE_MEM out_edge_iterator>
    out_edges(BOOST_GRID_GRAPH_TYPE_MEM vertex_descriptor vertex,
              const BOOST_GRID_GRAPH_TYPE& graph) {
      BOOST_GRID_GRAPH_TYPE_TD(degree_iterator);
      BOOST_GRID_GRAPH_TYPE_TD(out_edge_function);
      BOOST_GRID_GRAPH_TYPE_TD(out_edge_iterator);

      return (std::make_pair
              (out_edge_iterator(degree_iterator(0),
                                 out_edge_function(vertex, &graph)),
               out_edge_iterator(degree_iterator(graph.out_degree(vertex)),
                                 out_edge_function(vertex, &graph))));
    }

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline BOOST_GRID_GRAPH_TYPE_MEM degree_size_type
    out_degree
    (BOOST_GRID_GRAPH_TYPE_MEM vertex_descriptor vertex,
     const BOOST_GRID_GRAPH_TYPE& graph) {
      return (graph.out_degree(vertex));
    }

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline BOOST_GRID_GRAPH_TYPE_MEM edge_descriptor
    out_edge_at(BOOST_GRID_GRAPH_TYPE_MEM vertex_descriptor vertex,
                BOOST_GRID_GRAPH_TYPE_MEM degree_size_type out_edge_index,
                const BOOST_GRID_GRAPH_TYPE& graph) {
      return (graph.out_edge_at(vertex, out_edge_index));
    }

    //===============
    // AdjacencyGraph
    //===============

    template <BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend typename std::pair<BOOST_GRID_GRAPH_TYPE_MEM adjacency_iterator,
                              BOOST_GRID_GRAPH_TYPE_MEM adjacency_iterator>
    adjacent_vertices (BOOST_GRID_GRAPH_TYPE_MEM vertex_descriptor vertex,
                       const BOOST_GRID_GRAPH_TYPE& graph) {
      BOOST_GRID_GRAPH_TYPE_TD(degree_iterator);
      BOOST_GRID_GRAPH_TYPE_TD(adjacent_vertex_function);
      BOOST_GRID_GRAPH_TYPE_TD(adjacency_iterator);

      return (std::make_pair
              (adjacency_iterator(degree_iterator(0),
                                 adjacent_vertex_function(vertex, &graph)),
               adjacency_iterator(degree_iterator(graph.out_degree(vertex)),
                                 adjacent_vertex_function(vertex, &graph))));
    }

    //==============
    // EdgeListGraph
    //==============

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline BOOST_GRID_GRAPH_TYPE_MEM edges_size_type
    num_edges(const BOOST_GRID_GRAPH_TYPE& graph) {
      return (graph.num_edges());
    }

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline BOOST_GRID_GRAPH_TYPE_MEM edge_descriptor
    edge_at(BOOST_GRID_GRAPH_TYPE_MEM edges_size_type edge_index,
            const BOOST_GRID_GRAPH_TYPE& graph) {
      return (graph.edge_at(edge_index));
    }

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline std::pair<BOOST_GRID_GRAPH_TYPE_MEM edge_iterator,
                            BOOST_GRID_GRAPH_TYPE_MEM edge_iterator>
    edges(const BOOST_GRID_GRAPH_TYPE& graph) {
      BOOST_GRID_GRAPH_TYPE_TD(edge_index_iterator);
      BOOST_GRID_GRAPH_TYPE_TD(edge_function);
      BOOST_GRID_GRAPH_TYPE_TD(edge_iterator);

      return (std::make_pair
              (edge_iterator(edge_index_iterator(0),
                             edge_function(&graph)),
               edge_iterator(edge_index_iterator(graph.num_edges()),
                             edge_function(&graph))));
    }

    //===================
    // BiDirectionalGraph
    //===================

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline std::pair<BOOST_GRID_GRAPH_TYPE_MEM in_edge_iterator,
                            BOOST_GRID_GRAPH_TYPE_MEM in_edge_iterator>
    in_edges(BOOST_GRID_GRAPH_TYPE_MEM vertex_descriptor vertex,
             const BOOST_GRID_GRAPH_TYPE& graph) {
      BOOST_GRID_GRAPH_TYPE_TD(in_edge_function);
      BOOST_GRID_GRAPH_TYPE_TD(degree_iterator);
      BOOST_GRID_GRAPH_TYPE_TD(in_edge_iterator);

      return (std::make_pair
              (in_edge_iterator(degree_iterator(0),
                                in_edge_function(vertex, &graph)),
               in_edge_iterator(degree_iterator(graph.in_degree(vertex)),
                                in_edge_function(vertex, &graph))));
    }

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline BOOST_GRID_GRAPH_TYPE_MEM degree_size_type
    in_degree (BOOST_GRID_GRAPH_TYPE_MEM vertex_descriptor vertex,
               const BOOST_GRID_GRAPH_TYPE& graph) {
      return (graph.in_degree(vertex));
    }

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline BOOST_GRID_GRAPH_TYPE_MEM degree_size_type
    degree (BOOST_GRID_GRAPH_TYPE_MEM vertex_descriptor vertex,
            const BOOST_GRID_GRAPH_TYPE& graph) {
      return (graph.out_degree(vertex) * 2);
    }

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline BOOST_GRID_GRAPH_TYPE_MEM edge_descriptor
    in_edge_at(BOOST_GRID_GRAPH_TYPE_MEM vertex_descriptor vertex,
               BOOST_GRID_GRAPH_TYPE_MEM degree_size_type in_edge_index,
               const BOOST_GRID_GRAPH_TYPE& graph) {
      return (graph.in_edge_at(vertex, in_edge_index));
    }

    //==================
    // Adjacency Matrix
    //==================

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend std::pair<BOOST_GRID_GRAPH_TYPE_MEM edge_descriptor, bool>
    edge (BOOST_GRID_GRAPH_TYPE_MEM vertex_descriptor source_vertex,
          BOOST_GRID_GRAPH_TYPE_MEM vertex_descriptor destination_vertex,
          const BOOST_GRID_GRAPH_TYPE& graph) {

      std::pair<BOOST_GRID_GRAPH_TYPE_MEM edge_descriptor, bool> edge_exists =
        std::make_pair(std::make_pair(source_vertex, destination_vertex), false);

      for (std::size_t dimension_index = 0;
           dimension_index < Dimensions;
           ++dimension_index) {

        BOOST_GRID_GRAPH_TYPE_MEM vertices_size_type dim_difference = 0;
        BOOST_GRID_GRAPH_TYPE_MEM vertices_size_type
          source_dim = source_vertex[dimension_index],
          dest_dim = destination_vertex[dimension_index];

        dim_difference = (source_dim > dest_dim) ?
          (source_dim - dest_dim) : (dest_dim - source_dim);

        if (dim_difference > 0) {

          // If we've already found a valid edge, this would mean that
          // the vertices are really diagonal across dimensions and
          // therefore not connected.
          if (edge_exists.second) {
            edge_exists.second = false;
            break;
          }

          // If the difference is one, the vertices are right next to
          // each other and the edge is valid.  The edge is still
          // valid, though, if the dimension wraps and the vertices
          // are on opposite ends.
          if ((dim_difference == 1) ||
              (graph.wrapped(dimension_index) &&
               (((source_dim == 0) && (dest_dim == (graph.length(dimension_index) - 1))) ||
                ((dest_dim == 0) && (source_dim == (graph.length(dimension_index) - 1)))))) {

            edge_exists.second = true;
            // Stay in the loop to check for diagonal vertices.
          }
          else {

            // Stop checking - the vertices are too far apart.
            edge_exists.second = false;
            break;
          }
        }

      } // for dimension_index

      return (edge_exists);
    }

    //=============================
    // Index Property Map Functions
    //=============================

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline BOOST_GRID_GRAPH_TYPE_MEM vertices_size_type
    get(vertex_index_t,
        const BOOST_GRID_GRAPH_TYPE& graph,
        BOOST_GRID_GRAPH_TYPE_MEM vertex_descriptor vertex) {
      return (graph.index_of(vertex));
    }

    template<BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline BOOST_GRID_GRAPH_TYPE_MEM edges_size_type
    get(edge_index_t,
        const BOOST_GRID_GRAPH_TYPE& graph,
        BOOST_GRID_GRAPH_TYPE_MEM edge_descriptor edge) {
      return (graph.index_of(edge));
    }

    template <BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline grid_graph_index_map<
                    BOOST_GRID_GRAPH_TYPE,
                    BOOST_GRID_GRAPH_TYPE_MEM vertex_descriptor,
                    BOOST_GRID_GRAPH_TYPE_MEM vertices_size_type>
    get(vertex_index_t, const BOOST_GRID_GRAPH_TYPE& graph) {
      return (grid_graph_index_map<
                BOOST_GRID_GRAPH_TYPE,
                BOOST_GRID_GRAPH_TYPE_MEM vertex_descriptor,
                BOOST_GRID_GRAPH_TYPE_MEM vertices_size_type>(graph));
    }

    template <BOOST_GRID_GRAPH_TEMPLATE_PARAMS>
    friend inline grid_graph_index_map<
                    BOOST_GRID_GRAPH_TYPE,
                    BOOST_GRID_GRAPH_TYPE_MEM edge_descriptor,
                    BOOST_GRID_GRAPH_TYPE_MEM edges_size_type>
    get(edge_index_t, const BOOST_GRID_GRAPH_TYPE& graph) {
      return (grid_graph_index_map<
                BOOST_GRID_GRAPH_TYPE,
                BOOST_GRID_GRAPH_TYPE_MEM edge_descriptor,
                BOOST_GRID_GRAPH_TYPE_MEM edges_size_type>(graph));
    }

    template<typename Graph,
             typename Descriptor,
             typename Index>
    friend inline Index
    get(const grid_graph_index_map<Graph, Descriptor, Index>& index_map,
        const typename grid_graph_index_map<Graph, Descriptor, Index>::key_type& key)
    {
      return (index_map[key]);
    }

    template<typename Graph,
             typename Descriptor,
             typename Index>
    friend struct grid_graph_index_map;

  }; // grid_graph

} // namespace boost

#undef BOOST_GRID_GRAPH_TYPE
#undef BOOST_GRID_GRAPH_TYPE_TD
#undef BOOST_GRID_GRAPH_TYPE_MEM
#undef BOOST_GRID_GRAPH_TEMPLATE_PARAMS
#undef BOOST_GRID_GRAPH_TRAITS_T

#endif // BOOST_GRAPH_GRID_GRAPH_HPP


//
// Copyright(c) 2018-2020 CNRS INRIA
//

#include "pinocchio/algorithm/copy.hpp"
#include "pinocchio/algorithm/kinematics.hpp"
#include "pinocchio/algorithm/rnea.hpp"
#include "pinocchio/algorithm/joint-configuration.hpp"
#include "pinocchio/parsers/sample-models.hpp"

#include <boost/test/unit_test.hpp>
#include <boost/utility/binary.hpp>

BOOST_AUTO_TEST_SUITE(BOOST_TEST_MODULE)

BOOST_AUTO_TEST_CASE(test_data_copy)
{
  using namespace Eigen;
  using namespace pinocchio;

  Model model;
  buildModels::humanoidRandom(model);

  model.upperPositionLimit.head<3>().fill(100);
  model.upperPositionLimit.segment<4>(3).setOnes();
  model.lowerPositionLimit.head<7>() = - model.upperPositionLimit.head<7>();

  VectorXd q(model.nq);
  q = randomConfiguration(model);
  VectorXd v(VectorXd::Random(model.nv));
  VectorXd a(VectorXd::Random(model.nv));

  Data data_ref(model), data(model);
  forwardKinematics(model,data_ref,q,v,a);
  rnea(model,data_ref,q,v,a); // for a_gf to be initialized

  // Check zero order kinematic quantities
  copy(model,data_ref,data,POSITION);
  for(Model::JointIndex i = 1; i < (Model::JointIndex)model.njoints; ++i)
  {
    BOOST_CHECK(data.oMi[i] == data_ref.oMi[i]);
  }

  // Check first order kinematic quantities
  copy(model,data_ref,data,VELOCITY);
  for(Model::JointIndex i = 1; i < (Model::JointIndex)model.njoints; ++i)
  {
    BOOST_CHECK(data.oMi[i] == data_ref.oMi[i]);
    BOOST_CHECK(data.v[i] == data_ref.v[i]);
  }

  // Check second order kinematic quantities
  copy(model,data_ref,data,ACCELERATION);
  for(Model::JointIndex i = 1; i < (Model::JointIndex)model.njoints; ++i)
  {
    BOOST_CHECK(data.oMi[i] == data_ref.oMi[i]);
    BOOST_CHECK(data.v[i] == data_ref.v[i]);
    BOOST_CHECK(data.a[i] == data_ref.a[i]);
    BOOST_CHECK(data.a_gf[i] == data_ref.a_gf[i]);
  }

}

BOOST_AUTO_TEST_SUITE_END()


// Compares two outfiles from phase2, adds appropriate labels
//#include <boost/algorithm/string/replace.hpp>

#include <iostream>
#include <cstdlib>
#include <vector>
#include <algorithm>

#include <TMath.h>
#include <TH1.h>
#include <TH2.h>
#include <TFile.h>
#include <TString.h>
#include <TRegexp.h>
#include <TGraph.h>
#include <TGraphErrors.h>
#include <TMultiGraph.h>
#include <TProfile.h>
#include <TCanvas.h>
#include <TLegend.h>
#include <TSystem.h>
#include <TSystemDirectory.h>
#include <TColor.h>
#include <TStyle.h>
#include <TKey.h>

#include "analysis_params.h"

int nTriggerPtBins = nJetPtBins;
std::vector<double> fTriggerPtBins = jetPtBins;

void set_plot_style() {
    const Int_t NRGBs = 5;
    const Int_t NCont = 255;

    Double_t stops[NRGBs] = { 0.00, 0.34, 0.61, 0.84, 1.00 };
    Double_t red[NRGBs]   = { 0.00, 0.00, 0.87, 1.00, 0.51 };
    Double_t green[NRGBs] = { 0.00, 0.81, 1.00, 0.20, 0.00 };
    Double_t blue[NRGBs]  = { 0.51, 1.00, 0.12, 0.00, 0.00 };
    TColor::CreateGradientColorTable(NRGBs, stops, red, green, blue, NCont);
    gStyle->SetNumberContours(NCont);
}

using namespace std;

struct fileLabel {
  fileLabel(TString _filepath, TString _label): filepath(_filepath),label(_label) {}
  TString filepath;
  TString label;
};

/**
  * Calculates the systematic fractional uncertainty in the tgraphs in the input matrices by using k =1,2, nVersions
  * trials.  Saves them to the parameter output file
  */
void  CalculateSystematicUncertaintyN(vector <vector<TGraphErrors * >> tGraphs, TFile * f){
  // tGraphs expected to be indexed by k = 0,1, .., nVersions, then j = 0,1, .. nJetPtBins
	int nModes = tGraphs.size();

  for (int i = 0; i < nTriggerPtBins; i++) {
    int n;
    TGraph * SysErr;
    n = tGraphs[0][i]->GetN();
    SysErr = new TGraph(n);
    SysErr->SetName(Form("%s_SysUncert",tGraphs[0][i]->GetName()));
    for (int j = 0; j < n; j++) {
      double sumOfSquares = 0;
      double sum = 0;
      double x = 0;
      double y;
      for (int k = 0; k < nModes; k++) {
        y=0;
        tGraphs[k][i]->GetPoint(j,x,y);
        sumOfSquares += y*y;
        sum += y;
      }
			//y = sum / nModes;// New Fix
			tGraphs[0][i]->GetPoint(j,x,y); // Newer Fix, use the first value as the primary value to be reported.
      if (y != 0) SysErr->SetPoint(j,x,(sqrt((sumOfSquares - sum*sum/(nModes))/(nModes-1))/abs(y)));
    }
    f->Add(SysErr);
  }

}

void cleanName(TString &str) {
		str.ReplaceAll(" ","_");
		str.ReplaceAll("%","_");
		str.ReplaceAll("#","_");
		str.ReplaceAll(",","_");
		str.ReplaceAll("-","_");
		str.ReplaceAll("=","_");
}

vector <vector<TGraphErrors * >> extractTGraphArray(TString name, vector<TFile *> * files) {

	int nModes = files->size();
	vector <vector<TGraphErrors * >> tGraphArray;// = new vector <vector<TGraphErrors * >>;

	for (int k = 0; k < nModes; k++) {
		TFile * f1 = files->at(k);

		vector<TGraphErrors *> tGraphSubArray;
		for (int i = 0; i < nTriggerPtBins; i++) {
			TGraphErrors * tGraph = (TGraphErrors *) f1->Get(Form(name,fTriggerPtBins.at(i), fTriggerPtBins.at(i+1)));
			if (!tGraph) {
				fprintf(stderr,"Error: Could not find %s\n",Form(name,fTriggerPtBins.at(i),fTriggerPtBins.at(i+1)));
				exit(1);
			}
//			printf("Found %s in file %s\n",tGraph->GetName(),f1->GetTitle());
			tGraphSubArray.push_back(tGraph);
		}
		tGraphArray.push_back(tGraphSubArray);
	}

	return tGraphArray;
}

const Int_t nTypeList = 9;

void calcSystUncert(vector<fileLabel> *fileLabels, char * fSystematicUncertOutFilePath)  {

  Int_t colorList[nTypeList] = {kBlack,kRed,kOrange-3,kGreen-3,kBlue,kViolet,kMagenta-5,kRed-10,kGray};
  Int_t markerList[nTypeList] = {kFullSquare,kFullCircle,kFullDiamond,kOpenSquare,kOpenCircle,kOpenDiamond,kFullStar,23,34};

  double c_width = 600;
  double c_height = 600;

	TFile * fSystematicUncertOutFile = TFile::Open(fSystematicUncertOutFilePath,"RECREATE");
	if (!fSystematicUncertOutFile) {
		fprintf(stderr,"Error: Unable to open systematic error output file!\n");
		exit(1);
	}

  vector<TFile *> *files = new vector<TFile *>;

  for (int i = 0 ; i < fileLabels->size(); i++ ) {
    fileLabel fl = fileLabels->at(i);
    printf("Opening File %s [%s] ... \n",fl.filepath.Data(),fl.label.Data());
    TFile *f = TFile::Open(fl.filepath.Data(),"READ");
    if (!f) {
      fprintf(stderr,"Error: file %s not found!\nExiting.\n",fl.filepath.Data());
      exit(1);
    }
		TString nameNoSpace  = fl.label;
		cleanName(nameNoSpace);
//		printf("MHO nameNoSpace = %s\n",nameNoSpace.Data());
    f->SetTitle(fl.label.Data());
		f->SetName(nameNoSpace);
    files->push_back(f);

    printf("file %s, title %s\n",f->GetName(),f->GetTitle());

  }

  if ( !files->size()) exit(0);
  TFile *f0 = files->at(0);

  TCanvas *canvas = new TCanvas("canvas","canvas",c_width,c_height);
  gStyle->SetOptStat(0);

	// Need to construct arrays of objects, with an arbitrary first index,
  // second index is jetpt

	int iTrigger = 0; // 0 for jet, 1 for pi0Pt, 2 for pi0Zt

  TString jetClass = "jetPt_%.0f_%.0f";

	TString triggerClass = jetClass;

	if (iTrigger > 0) {
		triggerClass = "pi0Pt_%.0f_%.0f";
		nTriggerPtBins = nPi0PtBins;
		fTriggerPtBins = pi0PtBins;
	}

	vector <vector<TGraphErrors * >> ptBinASIntegralsGraphsEP_0 = extractTGraphArray(triggerClass + "_EP_0_AS_Integrals", files);
	vector <vector<TGraphErrors * >> ptBinASIntegralsGraphsEP_1 = extractTGraphArray(triggerClass + "_EP_1_AS_Integrals", files);
	vector <vector<TGraphErrors * >> ptBinASIntegralsGraphsEP_2 = extractTGraphArray(triggerClass + "_EP_2_AS_Integrals", files);
	vector <vector<TGraphErrors * >> ptBinASIntegralsGraphsEP_3 = extractTGraphArray(triggerClass + "_EP_3_AS_Integrals", files);

	CalculateSystematicUncertaintyN(ptBinASIntegralsGraphsEP_0,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinASIntegralsGraphsEP_1,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinASIntegralsGraphsEP_2,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinASIntegralsGraphsEP_3,fSystematicUncertOutFile);

	vector <vector<TGraphErrors * >> ptBinNSIntegralsGraphsEP_0 = extractTGraphArray(triggerClass + "_EP_0_NS_Integrals", files);
	vector <vector<TGraphErrors * >> ptBinNSIntegralsGraphsEP_1 = extractTGraphArray(triggerClass + "_EP_1_NS_Integrals", files);
	vector <vector<TGraphErrors * >> ptBinNSIntegralsGraphsEP_2 = extractTGraphArray(triggerClass + "_EP_2_NS_Integrals", files);
	vector <vector<TGraphErrors * >> ptBinNSIntegralsGraphsEP_3 = extractTGraphArray(triggerClass + "_EP_3_NS_Integrals", files);

	CalculateSystematicUncertaintyN(ptBinNSIntegralsGraphsEP_0,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinNSIntegralsGraphsEP_1,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinNSIntegralsGraphsEP_2,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinNSIntegralsGraphsEP_3,fSystematicUncertOutFile);

	vector <vector<TGraphErrors * >> ptBinASYieldsGraphsEP_0 = extractTGraphArray(triggerClass + "_EP_0_AS_Yields", files);
	vector <vector<TGraphErrors * >> ptBinASYieldsGraphsEP_1 = extractTGraphArray(triggerClass + "_EP_1_AS_Yields", files);
	vector <vector<TGraphErrors * >> ptBinASYieldsGraphsEP_2 = extractTGraphArray(triggerClass + "_EP_2_AS_Yields", files);
	vector <vector<TGraphErrors * >> ptBinASYieldsGraphsEP_3 = extractTGraphArray(triggerClass + "_EP_3_AS_Yields", files);

	CalculateSystematicUncertaintyN(ptBinASYieldsGraphsEP_0,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinASYieldsGraphsEP_1,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinASYieldsGraphsEP_2,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinASYieldsGraphsEP_3,fSystematicUncertOutFile);

	vector <vector<TGraphErrors * >> ptBinASRmsGraphsEP_0 = extractTGraphArray(triggerClass + "_EP_0_AS_Rms", files);
	vector <vector<TGraphErrors * >> ptBinASRmsGraphsEP_1 = extractTGraphArray(triggerClass + "_EP_1_AS_Rms", files);
	vector <vector<TGraphErrors * >> ptBinASRmsGraphsEP_2 = extractTGraphArray(triggerClass + "_EP_2_AS_Rms", files);
	vector <vector<TGraphErrors * >> ptBinASRmsGraphsEP_3 = extractTGraphArray(triggerClass + "_EP_3_AS_Rms", files);

	CalculateSystematicUncertaintyN(ptBinASRmsGraphsEP_0,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinASRmsGraphsEP_1,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinASRmsGraphsEP_2,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinASRmsGraphsEP_3,fSystematicUncertOutFile);

	vector <vector<TGraphErrors * >> ptBinNSRmsGraphsEP_0 = extractTGraphArray(triggerClass + "_EP_0_NS_Rms", files);
	vector <vector<TGraphErrors * >> ptBinNSRmsGraphsEP_1 = extractTGraphArray(triggerClass + "_EP_1_NS_Rms", files);
	vector <vector<TGraphErrors * >> ptBinNSRmsGraphsEP_2 = extractTGraphArray(triggerClass + "_EP_2_NS_Rms", files);
	vector <vector<TGraphErrors * >> ptBinNSRmsGraphsEP_3 = extractTGraphArray(triggerClass + "_EP_3_NS_Rms", files);

	CalculateSystematicUncertaintyN(ptBinNSRmsGraphsEP_0,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinNSRmsGraphsEP_1,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinNSRmsGraphsEP_2,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinNSRmsGraphsEP_3,fSystematicUncertOutFile);

	vector <vector<TGraphErrors * >> ptBinASWidthsGraphsEP_0 = extractTGraphArray(triggerClass + "_EP_0_AS_Widths", files);
	vector <vector<TGraphErrors * >> ptBinASWidthsGraphsEP_1 = extractTGraphArray(triggerClass + "_EP_1_AS_Widths", files);
	vector <vector<TGraphErrors * >> ptBinASWidthsGraphsEP_2 = extractTGraphArray(triggerClass + "_EP_2_AS_Widths", files);
	vector <vector<TGraphErrors * >> ptBinASWidthsGraphsEP_3 = extractTGraphArray(triggerClass + "_EP_3_AS_Widths", files);

	CalculateSystematicUncertaintyN(ptBinASWidthsGraphsEP_0,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinASWidthsGraphsEP_1,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinASWidthsGraphsEP_2,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(ptBinASWidthsGraphsEP_3,fSystematicUncertOutFile);

	// Now doing the Ratios
	vector <vector<TGraphErrors * >> OutOverIn_AS = extractTGraphArray("OutOverIn_AS_" + triggerClass, files);
	vector <vector<TGraphErrors * >> MidOverIn_AS = extractTGraphArray("MidOverIn_AS_" + triggerClass, files);
	vector <vector<TGraphErrors * >> OutOverIn_NS = extractTGraphArray("OutOverIn_NS_" + triggerClass, files);
	vector <vector<TGraphErrors * >> MidOverIn_NS = extractTGraphArray("MidOverIn_NS_" + triggerClass, files);
	vector <vector<TGraphErrors * >> OutOverInPar_AS = extractTGraphArray("OutOverInPar_AS_" + triggerClass, files);
	vector <vector<TGraphErrors * >> MidOverInPar_AS = extractTGraphArray("MidOverInPar_AS_" + triggerClass, files);

	CalculateSystematicUncertaintyN(OutOverIn_AS,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(MidOverIn_AS,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(OutOverIn_NS,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(MidOverIn_NS,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(OutOverInPar_AS,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(MidOverInPar_AS,fSystematicUncertOutFile);

	vector <vector<TGraphErrors * >> OutMinusIn_AS = extractTGraphArray("OutMinusIn_AS_" + triggerClass, files);
	vector <vector<TGraphErrors * >> MidMinusIn_AS = extractTGraphArray("MidMinusIn_AS_" + triggerClass, files);
	vector <vector<TGraphErrors * >> OutMinusIn_NS = extractTGraphArray("OutMinusIn_NS_" + triggerClass, files);
	vector <vector<TGraphErrors * >> MidMinusIn_NS = extractTGraphArray("MidMinusIn_NS_" + triggerClass, files);

	CalculateSystematicUncertaintyN(OutMinusIn_AS,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(MidMinusIn_AS,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(OutMinusIn_NS,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(MidMinusIn_NS,fSystematicUncertOutFile);

	vector <vector<TGraphErrors * >> RMSOutOverIn_AS = extractTGraphArray("RMSOutOverIn_AS_" + triggerClass, files);
	vector <vector<TGraphErrors * >> RMSMidOverIn_AS = extractTGraphArray("RMSMidOverIn_AS_" + triggerClass, files);
	vector <vector<TGraphErrors * >> RMSOutOverIn_NS = extractTGraphArray("RMSOutOverIn_NS_" + triggerClass, files);
	vector <vector<TGraphErrors * >> RMSMidOverIn_NS = extractTGraphArray("RMSMidOverIn_NS_" + triggerClass, files);

	CalculateSystematicUncertaintyN(RMSOutOverIn_AS,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(RMSMidOverIn_AS,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(RMSOutOverIn_NS,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(RMSMidOverIn_NS,fSystematicUncertOutFile);

	// FIXME add width ratios?
	vector <vector<TGraphErrors * >> WidthsOutOverIn_AS = extractTGraphArray("WidthsOutOverIn_AS_" + triggerClass, files);
	vector <vector<TGraphErrors * >> WidthsMidOverIn_AS = extractTGraphArray("WidthsMidOverIn_AS_" + triggerClass, files);

	CalculateSystematicUncertaintyN(WidthsOutOverIn_AS,fSystematicUncertOutFile);
	CalculateSystematicUncertaintyN(WidthsMidOverIn_AS,fSystematicUncertOutFile);

// MidOverIn_AS_pi0Pt

  /*
  vector <vector<TGraphErrors *> > ptBinASWidthsGraphsEP;
  vector <vector<TGraphErrors *> > ptBinASRmsGraphsEP;
  vector <vector<TGraphErrors *> > ptBinASIntegralsGraphsEP;
  vector <vector<TGraphErrors *> > ptBinASFwhmGraphsEP;
  vector <vector<TGraphErrors *> > ptBinASFwhmRescaledGraphsEP;
  vector <vector<TGraphErrors *> > ptBinASYieldsGraphsEP;
*/

	fSystematicUncertOutFile->Write();

	printf("Finished calculating systematic uncertainties for integrals,yields,rms,width\n");

}

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

  if (argc < 3) {
    printf("Usage: %s <SystematicUncertaintyRootFile> [Filepath_1] [Label_1] [Filepath_2] [Label_2] ... \n",argv[0]);
//    printf("    Or %s -p [PATTERN] [Filepath_1] [Label_1] [Filepath_2] [Label_2] ... \n",argv[0]);
    exit(0);
  }

  int num = ((argc) / 2) ;
  vector<fileLabel> *fileLabels = new vector<fileLabel>;
  for (int i = 1; i < num; i++) {
    fileLabels->push_back(fileLabel(TString(argv[2*i]),TString(argv[2*i+1])));
  }
	printf("Starting calculation of systematic uncertainties using files:");
	for (int i = 0; i < num - 1 ; i++) {
		printf("%s ",fileLabels->at(i).filepath.Data());
	}
	printf("\n");
  calcSystUncert(fileLabels,argv[1]);
}


/*
 * Neighbourhood.cpp
 *
 *  Created on: May 18, 2012
 *      Author: david
 */

#include "Neighbourhood.hpp"
#include "Metric.hpp"
#include <Slimage/Convert.hpp>
#include <boost/graph/copy.hpp>
#include <iostream>

namespace dasp
{

DaspGraph CreateDaspNeighbourhoodGraph(const Superpixels& superpixels)
{
	NeighbourhoodGraph gnb = CreateNeighborhoodGraph(superpixels, NeighborGraphSettings::NoCut());
	DaspGraph result;
	boost::copy_graph(gnb, result,
		boost::vertex_copy(
			[&superpixels,&gnb,&result](UndirectedWeightedGraph::vertex_descriptor src, DaspGraph::vertex_descriptor dst) {
				result[dst] = superpixels.cluster[src].center;
			}
		)
		.edge_copy(
			[&gnb,&result](UndirectedWeightedGraph::edge_descriptor src, DaspGraph::edge_descriptor dst) {
				result[dst] = 1.0f;
			}
		)
	);
	return result;
}

DaspGraph CreateDaspGraph(const Superpixels& superpixels, const UndirectedWeightedGraph& weighted_graph)
{
	DaspGraph result;
	boost::copy_graph(weighted_graph, result,
		boost::vertex_copy(
			[&superpixels,&weighted_graph,&result](UndirectedWeightedGraph::vertex_descriptor src, DaspGraph::vertex_descriptor dst) {
				result[dst] = superpixels.cluster[src].center;
			}
		)
		.edge_copy(
			[&weighted_graph,&result](UndirectedWeightedGraph::edge_descriptor src, DaspGraph::edge_descriptor dst) {
				result[dst] = weighted_graph[src];
			}
		)
	);
	return result;
}

DaspGraph ConvertToSimilarityGraph(const DaspGraph& source, const float sigma) {
	DaspGraph result;
	boost::copy_graph(source, result,
			boost::edge_copy([&source,&result,sigma](typename DaspGraph::edge_descriptor src, DaspGraph::edge_descriptor dst) {
				const float d = source[src];
				const float s = std::exp(-d/sigma);
				result[dst] = s;
	}));
	return result;
}

std::vector<unsigned int> ComputeAllBorderPixels(const Superpixels& superpixels)
{
	slimage::Image1i labels = superpixels.ComputeLabels();
	std::set<unsigned int> u;
	int face_neighbours[] = { -1, +1, -labels.width(), +labels.width() };
	for(unsigned int y=1; y<labels.height()-1; y++) {
		for(unsigned int x=1; x<labels.width()-1; x++) {
			int q = labels.index(x,y);
			unsigned int lc = labels[q];
			for(unsigned int i=0; i<4; i++) {
				if(labels[q + face_neighbours[i]] != lc) {
					u.insert(q);
				}
			}
		}
	}
	return std::vector<unsigned int>(u.begin(), u.end());
}

//std::vector<unsigned int> ComputeBorderPixels(const std::vector<unsigned int>& pixel_ids, unsigned int desired_neighbour_cid, const slimage::Image1i& labels)
//{
//	const int w = static_cast<int>(labels.width());
//	const int h = static_cast<int>(labels.height());
//	const int d[4] = { -1, +1, -w, +w };
//	std::vector<unsigned int> border;
//	for(unsigned int pid : pixel_ids) {
//		int x = pid % w;
//		int y = pid / w;
//		if(1 <= x && x+1 <= w && 1 <= y && y+1 <= h) {
//			for(int i=0; i<4; i++) {
//				int label = labels[pid + d[i]];
//				if(label == static_cast<int>(desired_neighbour_cid)) {
//					border.push_back(pid);
//				}
//			}
//		}
//	}
//	return border;
//}

struct BorderPixel
{
	unsigned int pixel_id;
	unsigned int label;
};

std::vector<BorderPixel> ComputeBorderLabels(unsigned int cid, const Superpixels& spc, const slimage::Image1i& labels)
{
	const int w = static_cast<int>(labels.width());
	const int h = static_cast<int>(labels.height());
	const int d[4] = { -1, +1, -w, +w };
	std::vector<BorderPixel> border;
	for(unsigned int pid : spc.cluster[cid].pixel_ids) {
		int x = pid % w;
		int y = pid / w;
		if(1 <= x && x+1 < w && 1 <= y && y+1 < h) {
			for(int i=0; i<4; i++) {
				int label = labels[pid + d[i]];
				if(label != cid && label != -1) {
					border.push_back(BorderPixel{pid, label});
				}
			}
		}
	}
	return border;
}

std::vector<std::vector<BorderPixel>> ComputeBorderLabels(const Superpixels& spc)
{
	slimage::Image1i labels = spc.ComputeLabels();
	std::vector<std::vector<BorderPixel>> border_pixels(spc.cluster.size());
	for(unsigned int cid=0; cid<spc.cluster.size(); cid++) {
		border_pixels[cid] = ComputeBorderLabels(cid, spc, labels);
	}
	return border_pixels;
}

std::vector<unsigned int> FindCommonBorder(const std::vector<std::vector<BorderPixel>>& border, unsigned int cid, unsigned cjd) {
	const std::vector<BorderPixel>& bi = border[cid];
	const std::vector<BorderPixel>& bj = border[cjd];
	std::vector<unsigned int> common;
	common.reserve(bi.size() + bj.size());
	for(const BorderPixel& q : bi) {
		if(q.label == cjd) {
			common.push_back(q.pixel_id);
		}
	}
	for(const BorderPixel& q : bi) {
		if(q.label == cjd) {
			common.push_back(q.pixel_id);
		}
	}
	return common;
}

NeighbourhoodGraph CreateNeighborhoodGraph(const Superpixels& superpixels, NeighborGraphSettings settings)
{
	// create one node for each superpixel
	NeighbourhoodGraph neighbourhood_graph(superpixels.clusterCount());
	// compute superpixel borders
	std::vector<std::vector<BorderPixel> > border = ComputeBorderLabels(superpixels);
	// connect superpixels
	const float spatial_distance_threshold = settings.spatial_distance_mult_threshold * superpixels.opt.base_radius;
	const float pixel_distance_mult_threshold = settings.pixel_distance_mult_threshold;
	for(unsigned int i=0; i<superpixels.cluster.size(); i++) {
		for(unsigned int j=i+1; j<superpixels.cluster.size(); j++) {
			const Point& c_i = superpixels.cluster[i].center;
			const Point& c_j = superpixels.cluster[j].center;
			// early test if the two superpixels are even near to each other
			if(settings.cut_by_spatial) {
				// spatial distance
				float d = (c_i.position - c_j.position).norm();
				// only test if distance is smaller than threshold
				if(d > spatial_distance_threshold) {
					continue;
				}
			}
			else {
				// pixel distance on camera image plane
				float d = std::sqrt(static_cast<float>(metric::PixelDistanceSquared(c_i,c_j)));
				// only test if pixel distance is smaller then C * pixel_radius
				float r = std::max(c_i.cluster_radius_px, c_j.cluster_radius_px);
				if(d > pixel_distance_mult_threshold * r) {
					continue;
				}
			}
			// compute intersection of border pixels
			std::vector<unsigned int> common_border = FindCommonBorder(border, i, j);
			// test if superpixels have a common border
			unsigned int common_border_size = common_border.size();
			if(common_border_size < settings.min_abs_border_overlap) {
				continue;
			}
			float p = static_cast<float>(common_border_size) / static_cast<float>(std::min(border[i].size(), border[j].size()));
			if(p < settings.min_border_overlap) {
				continue;
			}
			// add edge
			NeighbourhoodGraph::edge_descriptor eid;
			bool ok;
			boost::tie(eid,ok) = boost::add_edge(i, j, neighbourhood_graph); // FIXME correctly convert superpixel_id to vertex descriptor
			assert(ok);
			neighbourhood_graph[eid].num_border_pixels = common_border_size;
			neighbourhood_graph[eid].border_pixel_ids = common_border;
		}
	}
	return neighbourhood_graph;
}

}


/*-----------------------------------------------------------------------------+
Interval Container Library
Author: Joachim Faulhaber
Copyright (c) 2007-2010: Joachim Faulhaber
Copyright (c) 1999-2006: Cortex Software GmbH, Kantstrasse 57, Berlin
+------------------------------------------------------------------------------+
   Distributed under the Boost Software License, Version 1.0.
      (See accompanying file LICENCE.txt or copy at
           http://www.boost.org/LICENSE_1_0.txt)
+-----------------------------------------------------------------------------*/
#include <iostream>
#include <boost/icl/interval_map.hpp>
#include "../toytime.hpp"

using namespace std;
using namespace boost::icl;

/**
    Party.cpp demonstrates the possibilities of an interval map (interval_map or
    split_interval_map). An interval_map maps intervals to a given content. In
    this case the content is a set of party guests represented by their name
    strings.

    As time goes by, groups of people join the party and leave later in the
    evening. So we add a time interval and a name set to the interval_map for
    the attendance of each group of people, that come together and leave
    together.

    On every overlap of intervals, the corresponding name sets are accumulated.
    At the points of overlap the intervals are split. The accumulation of content
    on overlap of intervals is always done via an operator += that has to be
    implemented for the content parameter of the interval_map.

    Finally the interval_map contains the history of attendance and all points
    in time, where the group of party guests changed.

    Party.cpp demonstrates a principle that we call aggregate on overlap
    (aggovering;) On insertion a value associated to the interval is aggregated
    (added) to those values in the interval_map that overlap with the inserted
    value.

    There are two behavioral aspects to aggovering: a decompositional behavior
    and a accumulative behavior.

    The decompositional behavior splits up intervals on the time dimension of
    the interval_map so that the intervals change whenever associated values
    change.

    The accumulative behavior accumulates associated values on every overlap of
    an insertion for the associated values.
*/

// Type set<string> collects the names of party guests. Since std::set is
// a model of the itl's set concept, the concept provides an operator +=
// that performs a set union on overlap of intervals.
typedef std::set<string> GuestSetT;

// 'Time' is the domain type the interval_map. It's key values are therefore
// time intervals. The content is the set of names: GuestSetT.

void party()
{
    GuestSetT mary_harry;
    mary_harry.insert("Mary");
    mary_harry.insert("Harry");

    GuestSetT diana_susan;
    diana_susan.insert("Diana");
    diana_susan.insert("Susan");

    GuestSetT peter;
    peter.insert("Peter");

    interval_map<Time, GuestSetT> party;

    party += make_pair( interval<Time>::right_open(Time(19,30), Time(23,00)), mary_harry);
    party += make_pair( interval<Time>::right_open(Time(20,10), Time(monday,0,0)), diana_susan);
    party += make_pair( interval<Time>::right_open(Time(22,15), Time(monday,0,30)), peter);

    interval_map<Time, GuestSetT>::iterator it = party.begin();
    while(it != party.end())
    {
        discrete_interval<Time> when = it->first;
        // Who is at the party within the time interval 'when' ?
        GuestSetT who = (*it++).second;
        cout << when << ": " << who << endl;
    }
}

int main()
{
    cout << ">>Interval Container Library: Sample party.cpp       <<\n";
    cout << "-------------------------------------------------------\n";
    party();
    return 0;
}

// Program output:

// >>Interval Container Library: Sample party.cpp <<
// -------------------------------------------------
// [sun:19:30,sun:20:10): Harry Mary
// [sun:20:10,sun:22:15): Diana Harry Mary Susan
// [sun:22:15,sun:23:00): Diana Harry Mary Peter Susan
// [sun:23:00,mon:00:00): Diana Peter Susan
// [mon:00:00,mon:00:30): Peter