/******************************************************************************
 *
 * AMDiS - Adaptive multidimensional simulations
 *
 * Copyright (C) 2013 Dresden University of Technology. All Rights Reserved.
 * Web: https://fusionforge.zih.tu-dresden.de/projects/amdis
 *
 * Authors:
 * Simon Vey, Thomas Witkowski, Andreas Naumann, Simon Praetorius, et al.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 *
 * This file is part of AMDiS
 *
 * See also license.opensource.txt in the distribution.
 *
 ******************************************************************************/

/** \file PetscSolverGlobalMatrix.h */

#ifndef AMDIS_PETSC_SOLVER_GLOBAL_MATRIX_H
#define AMDIS_PETSC_SOLVER_GLOBAL_MATRIX_H

#include <boost/tuple/tuple.hpp>
#include "AMDiS_fwd.h"
#include "parallel/PetscSolver.hpp"

namespace AMDiS
{
  namespace Parallel
  {

    /** \ingroup Solver
     *
     * \brief
     * PETSc solver which creates a globally distributed matrix. Supports also
     * coarse space assembling.
     */
    class PetscSolverGlobalMatrix : public PetscSolver
    {
    public:
      /// Creator class
      class Creator : public LinearSolverCreator
      {
      public:
        virtual ~Creator() {}

        /// Returns a new PetscSolver object.
        LinearSolverInterface* create()
        {
          return new PetscSolverGlobalMatrix(this->name);
        }
      };

      PetscSolverGlobalMatrix(std::string name, bool setOptions = true);

      void fillPetscMatrix(Matrix<DOFMatrix*>* mat);

      void fillPetscMatrixWithCoarseSpace(Matrix<DOFMatrix*>* mat);

      void fillPetscRhs(SystemVector* vec);

      void solvePetscMatrix(SystemVector& vec, AdaptInfo& adaptInfo);

      void solveGlobal(Vec& rhs, Vec& sol);

      void extractVectorComponent(Vec input, int i, Vec* output, int numberOfComponents=1);

      void extractMatrixComponent(Mat input, int startRow, int numberOfRows, int startCol, int numberOfCols, Mat* output);

      void destroyMatrixData();

      void destroyVectorData();

    protected:
      void removeDirichletRows(Matrix<DOFMatrix*>* seqMat);

      void removeDirichletRows(SystemVector* seqVec);

      /// Reads field split information and creats a splitting based on
      /// component numbers.
      void createFieldSplit(PC pc);

      /** \brief
      * Creates a new field split for a preconditioner object.
      *
      * \param[in] pc          PETSc preconditioner object, must be of
      *                        type PCFIELDSPLIT
      * \param[in] splitName   Name of the field split, can be used to set other
      *                        parameters of the PCFIELDSPLIT preconditioner on
      *                        the command line.
      * \param[in] components  System component numbers of the field split. At
      *                        the moment only continuous splits are allowed.
      */
      void createFieldSplit(PC pc, const char* splitName, std::vector<int>& components);

      /// Wrapper to create field split from only one component.
      void createFieldSplit(PC pc, const char* splitName, int component)
      {
        std::vector<int> components;
        components.push_back(component);
        createFieldSplit(pc, splitName, components);
      }

      virtual void initSolver(KSP& ksp);

      virtual void exitSolver(KSP& ksp);

      virtual void initPreconditioner(PC pc);
      virtual void initPreconditioner(const Matrix<DOFMatrix*>& A, const Mat& fullMatrix)
      {
        initPreconditioner(getPc());
      }

      virtual void exitPreconditioner(PC pc);

      /// Takes a DOF matrix and sends the values to the global PETSc matrix.
      void setDofMatrix(DOFMatrix* mat, int rowComp = 0, int colComp = 0);

      /// Takes a DOF vector and sends its values to a given PETSc vector.
      void setDofVector(Vec vecInterior,
                        Vec vecCoarse,
                        DOFVector<double>* vec,
                        int rowCompxo,
                        bool rankOnly = false);

      inline void setDofVector(Vec vecInterior,
                               DOFVector<double>* vec,
                               int rowComp,
                               bool rankOnly = false)
      {
        setDofVector(vecInterior, PETSC_NULL, vec, rowComp, rankOnly);
      }

      inline void setDofVector(Vec vecInterior,
                               SystemVector& vec,
                               bool rankOnly = false)
      {
        for (int i = 0; i < vec.getSize(); i++)
          setDofVector(vecInterior, PETSC_NULL, vec.getDOFVector(i), i, rankOnly);
      }

      PetscSolver* createSubSolver(int component, std::string kspPrefix);

      void setConstantNullSpace(KSP ksp, int constFeSpace, bool test = false);

      void setConstantNullSpace(KSP ksp);

    protected:
      bool zeroStartVector;

      /// If true, after parallel assembling, information about the matrix
      /// are printed.
      bool printMatInfo;
    };

  } // end namespace Parallel

} // end namespace AMDiS

#endif


/*
 * test4_exprtk.cpp
 *
 *  Created on: Sep 8, 2015
 *      Author: dimitar
 */
#include "gtest/gtest.h"
#include <kaluun/exprtk_expression.hpp>
#include <kaluun/context.hpp>
#include <kaluun/parser.hpp>
#include <kaluun/template.hpp>
#include <kaluun/expression.hpp>
#include <boost/date_time/posix_time/posix_time_duration.hpp>
#include <boost/date_time/date.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <unordered_map>

using namespace std;

TEST(Functional, exprtk_condition) {
	typedef kaluun::template_tree<unordered_map, string, stringstream, kaluun::exprtk::expression<kaluun::context<string, unordered_map<string, kaluun::value>>>, kaluun::exprtk::condition<kaluun::context<string, unordered_map<string, kaluun::value>>>, string> template_type;

	template_type tpl;
	template_type::in_type template_text("{% set x = 5 %}{% if x > 5 %}bigger than five{% else %}less than five{% endif %}");
	template_type::out_type str_out;
	template_type::context_type ctx;
	kaluun::parser<template_type>::parse_template(template_text, tpl);
	tpl.generate(ctx, str_out);
	EXPECT_STREQ("less than five", str_out.str().c_str());
}

TEST(Functional, exprtk_expression) {
	typedef kaluun::template_tree<unordered_map, string, stringstream, kaluun::exprtk::expression<kaluun::context<string, unordered_map<string, kaluun::value>>>, kaluun::exprtk::condition<kaluun::context<string, unordered_map<string, kaluun::value>>>, string> template_type;

	template_type tpl;
	template_type::in_type template_text("{{b}} {% set x = 5 %}{% set y = 10 %}{% set z = x + y %}{{z}}{% set z = a + 1%} {{z}}{% set z = b%} {{z}} {{'sss'}} {{ x<y }}");
	template_type::out_type str_out;
	template_type::context_type ctx;
	ctx["a"] = 26;
	ctx["b"] = string("26.62");
	kaluun::parser<template_type>::parse_template(template_text, tpl);
	tpl.generate(ctx, str_out);
	EXPECT_STREQ("26.62 15 27 26.620000000000001 sss 1", str_out.str().c_str());
}


// © 2016 PORT INC.

#include <cassert>
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
#include <algorithm>
#include <boost/lexical_cast.hpp>
#include <random>
#include "ujson.hpp"
#include "Logger.hpp"
#include "Error.hpp"

using namespace boost;
using namespace std;

vector<vector<double>> y2x;
// =
// {
// 	{ 0.3125, 0.0937, 0.2188, 0.3125, 0.0625 },
// 	{ 0.2206, 0.4412, 0.1176, 0.1471, 0.0735 }
// };

vector<vector<double>> y2y;
// =
// {
// 	{ 0.7999, 0.1999 },
// 	{ 0.0999, 0.8999 }
// };

vector<vector<double>> a_y2x;
vector<vector<double>> a_y2y;

void preProcess()
{
	for( int i = 0; i < y2x.size(); i++ ) {
		a_y2x.push_back(vector<double>(y2x.at(i).size()));
	}

	for( int i = 0; i < y2x.size(); i++ ) {
		for( int j = 0; j < y2x.at(i).size(); j++ ) {
			if( 0 < j ) {
				a_y2x.at(i).at(j) = a_y2x.at(i).at(j-1) + y2x.at(i).at(j);
			} else {
				a_y2x.at(i).at(j) = y2x.at(i).at(j);
			}
		}
	}

	for( int i = 0; i < y2y.size(); i++ ) {
		a_y2y.push_back(vector<double>(y2y.at(i).size()));
	}

	for( int i = 0; i < y2y.size(); i++ ) {
		for( int j = 0; j < y2y.at(i).size(); j++ ) {
			if( 0 < j ) {
				a_y2y.at(i).at(j) = a_y2y.at(i).at(j-1) + y2y.at(i).at(j);
			} else {
				a_y2y.at(i).at(j) = y2y.at(i).at(j);
			}
		}
	}
}

int f_y2x(int y, double r)
{
	int i;
	assert ( 0 < a_y2x.size() );
	for( i = 0; i < a_y2x.at(y).size(); i++ ) {
		if( i == 0 && 0                          <= r && r < a_y2x.at(y).at(i) ) {
			break;
		} else if(  0 < i && a_y2x.at(y).at(i-1) <= r && r < a_y2x.at(y).at(i) ) {
			break;
		}
	}
	if( !(i < a_y2x.at(y).size()) ){
		Logger::warn() << "r=" << r << ", ay2x(" << y << ")=" << a_y2x.at(y).at(i-1);
		--i;
	}
	return i;
}

int f_y2y(int y, double r)
{
	int i;
	assert ( 0 < a_y2y.size() );
	for( i = 0; i < a_y2y.at(y).size(); i++ ) {
		if( i == 0 && 0                          <= r && r < a_y2y.at(y).at(i) ) {
			break;
		} else if(  0 < i && a_y2y.at(y).at(i-1) <= r && r < a_y2y.at(y).at(i) ) {
			break;
		}
	}
	if( !(i < a_y2y.size()) ) {
		Logger::warn() << "r=" << r << ", ay2y(" << y << ")=" << a_y2y.at(y).at(i-1);
		--i;
	}
	return i;
}

class Options {
public:
	Options()
		: length(16)
		, iteration(16)
		, seed(42)
		, logLevel(0)
		, file("")
		, nomalize(false)
		{};
	void parse(int argc, char *argv[]);
public:
	int length;
	int iteration;
	int seed;
	int logLevel;
	string file;
	bool nomalize;
};

void Options::parse(int argc, char *argv[])
{
	try {

		for( int i = 1; i < argc; i++ ) {
			string arg = argv[i];
			if( arg == "-l" || arg == "--length") {
				length = lexical_cast<int>(argv[++i]);
			} else if( arg == "-r" || arg == "--iteration") {
				iteration = lexical_cast<int>(argv[++i]);
			} else if( arg == "-i" || arg == "--input-file") {
				file = lexical_cast<string>(argv[++i]);
			} else if( arg == "-s" ) {
				seed = lexical_cast<int>(argv[++i]);
			} else if( arg == "--log-level" ) {
				logLevel = lexical_cast<int>(argv[++i]);
			} else if( arg == "-n" || arg == "--nomalize") {
				nomalize = true;
			} else {
				stringstream ss;
				ss << "unknown option specified";
				throw Error(ss.str());
			}
		}

	} catch(Error& e) {
		throw e;
	} catch(...) {
		stringstream ss;
		ss << "invalid option specified";
		throw Error(ss.str());
	}
}

template <class T>
void open(T& strm, const std::string& arg) {
	strm.open( arg.c_str() );
	if( strm.fail() ) {
		std::stringstream ss;
		ss << "cannot open such file: " << arg;
		throw Error(ss.str());
	}
}

void make_array(const ujson::object::const_iterator& it, vector<vector<double>>& vec, bool nomalize = false)
{
	double total = 0.0;

	std::vector<ujson::value> array0 = array_cast(std::move(it->second));

	vec.resize(array0.size());
	int p = 0;

    for( auto i = array0.begin(); i != array0.end(); ++i, ++p ) {

		if( !i->is_array() ) {
			throw Error("invalid format");
		}

		std::vector<ujson::value> array1 = array_cast(std::move(*i));
		for( auto j = array1.begin(); j != array1.end(); ++j ) {

			if( !j->is_number() ) {
				throw Error("invalid format");
			}

			double v = double_cast(std::move(*j));
			total += v;
			vec.at(p).push_back(v);
		}
	}

	if( nomalize ) {

		for( auto& i : vec ) {
			for( auto& j : i ) {
				j /= total;
			}
		}

		for( auto& i : vec ) {
			double row_wize_total = 0.0;
			for( auto& j : i ) {
				row_wize_total += j;
			}
			for( auto& j : i ) {
				j /= row_wize_total;
			}
		}

		Logger::info() << "{";
		for( auto& i : vec ) {
			Logger::info() << "{ ";
			for( auto& j : i ) {
				Logger::info() << j << " ";
			}
			Logger::info() << "}";
		}
		Logger::info() << "}";
	}
}

void make_arrays(ujson::value v, bool nomalize = false)
{
	if( !v.is_object() ) {
		throw Error("object expected for make_book");
	}

	std::vector<std::pair<std::string, ujson::value>> object = object_cast(std::move(v));

	{
		auto it = find(object, "y2x");
		if( it == object.end() || !it->second.is_array() ) {
			throw Error("'y2x' with type array not found");
		}
		make_array(it, y2x, nomalize);
	}
	{
		auto it = find(object, "y2y");
		if( it == object.end() || !it->second.is_array() ) {
			throw Error("'y2y' with type array not found");
		}
		make_array(it, y2y, nomalize);
	}
}

int main(int argc, char *argv[])
{
	int ret = 0x0;

	try {

		Options options;
		options.parse(argc, argv);
		Logger::setLevel(options.logLevel);

		if( options.length < 3 ) {
			stringstream ss;
			ss << "length must be greater than 2";
			throw Error(ss.str());
		}

		if( options.file.empty() ) {
			stringstream ss;
			ss << "no input file specifed";
			throw Error(ss.str());
		}

		std::ifstream ifs; // 入力
		open(ifs, options.file);
		std::string jsonstr;
		jsonstr.assign((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());

		try {
			auto v = ujson::parse(jsonstr);
			make_arrays(v, options.nomalize);
		} catch(...) {
			throw Error("json parse error");
		}

		preProcess();

		ujson::array ary0;
		ujson::array datas;

		mt19937 mt(options.seed);
		uniform_real_distribution<double> score(0.0,1.0);

		for( int j = 0; j < options.iteration; j++ ) {

			ujson::array ary1;
			ujson::array ary2;

			vector<int> y;
			vector<int> x;
			int py = 0;
			for( int i = 0; i < options.length; i++ ) {
				int yi = f_y2y(py, score(mt)); y.push_back(py);
				int xi = f_y2x(py, score(mt)); x.push_back(xi);
				py = yi;
			}

			auto xi = x.begin();
			auto yi = y.begin();
			auto yj = y.begin(); yj += 1;
			auto yk = y.begin(); yk += 2;

			ary2.push_back(boost::lexical_cast<std::string>(*xi));

			if( *yi == *yj ) {
				ary2.push_back("S");
			} else {
				ary2.push_back("S/E");
			}

			ary2.push_back(boost::lexical_cast<std::string>(*yi));
			ary1.push_back(std::move(ary2));
			// ary2.resize(0);

			ujson::array ary3;

			++xi;
			for( ; yk != y.end(); ++xi, ++yi, ++yj, ++yk ) {
				ary3.push_back(boost::lexical_cast<std::string>(*xi));
				if( *yi != *yj && *yj == *yk ) {
					ary3.push_back("S");
				} else if( *yi == *yj && *yj == *yk ) {
					ary3.push_back("M");
				} else if( *yi != *yj && *yj != *yk ) {
					ary3.push_back("S/E");
				} else if( *yi == *yj && *yj != *yk ) {
					ary3.push_back("E");
				} else {
					assert(0);
				}
				ary3.push_back(boost::lexical_cast<std::string>(*yj));
				ary1.push_back(std::move(ary3));
				// ary3.resize(0);
			}

			ujson::array ary4;

			ary4.push_back(boost::lexical_cast<std::string>(*xi));

			if( *yi != *yj ) {
				ary4.push_back("S/E");
			} else if( *yi == *yj ) {
				ary4.push_back("E");
			} else {
				assert(0);
			}

			ary4.push_back(boost::lexical_cast<std::string>(*yj));
			ary1.push_back(std::move(ary4));
			// ary4.resize(0);
			datas.push_back(std::move(ary1));
		}

		auto object0 = ujson::object {{ "data", datas }, { "title", "test" }};
		ary0.push_back(std::move(std::move(object0)));

		auto object1 = ujson::object {
			{ "dimension", std::move(ujson::array { int(y2x.at(0).size()), int(y2y.size()) }) },
			{ "feature", "DIGIT" },
			{ "pages", std::move(ary0) }
		};

		std::cout << "" << to_string(object1) << std::endl;

	} catch(Error& e) {

		Logger::error() << e.what();
		ret = 0x1;

	} catch(...) {

		Logger::error() << "unexpected exception";
		ret = 0x2;
	}

	exit(ret);
}


//| This file is a part of the sferes2 framework.
//| Copyright 2009, ISIR / Universite Pierre et Marie Curie (UPMC)
//| Main contributor(s): Jean-Baptiste Mouret, mouret@isir.fr
//|
//| This software is a computer program whose purpose is to facilitate
//| experiments in evolutionary computation and evolutionary robotics.
//|
//| This software is governed by the CeCILL license under French law
//| and abiding by the rules of distribution of free software.  You
//| can use, modify and/ or redistribute the software under the terms
//| of the CeCILL license as circulated by CEA, CNRS and INRIA at the
//| following URL "http://www.cecill.info".
//|
//| As a counterpart to the access to the source code and rights to
//| copy, modify and redistribute granted by the license, users are
//| provided only with a limited warranty and the software's author,
//| the holder of the economic rights, and the successive licensors
//| have only limited liability.
//|
//| In this respect, the user's attention is drawn to the risks
//| associated with loading, using, modifying and/or developing or
//| reproducing the software by the user in light of its specific
//| status of free software, that may mean that it is complicated to
//| manipulate, and that also therefore means that it is reserved for
//| developers and experienced professionals having in-depth computer
//| knowledge. Users are therefore encouraged to load and test the
//| software's suitability as regards their requirements in conditions
//| enabling the security of their systems and/or data to be ensured
//| and, more generally, to use and operate it in the same conditions
//| as regards security.
//|
//| The fact that you are presently reading this means that you have
//| had knowledge of the CeCILL license and that you accept its terms.

#ifndef BITSTRING_HPP_
#define BITSTRING_HPP_

#include <vector>
#include <limits>
#include <bitset>
#include <boost/foreach.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/nvp.hpp>
#include <sferes/stc.hpp>
#include <sferes/misc.hpp>
#include <sferes/dbg/dbg.hpp>
#include <iostream>

namespace boost {
  namespace serialization {
    template<class Archive, size_t Nb>
    void save(Archive& ar, const std::bitset<Nb>& bs, const unsigned int version) {
      std::string s = bs.to_string();
      ar << BOOST_SERIALIZATION_NVP(s);
    }

    template<class Archive, size_t Nb>
    void load(Archive& ar, std::bitset<Nb>& bs, const unsigned int version) {
      std::string s;
      ar >> BOOST_SERIALIZATION_NVP(s);
      assert(s.size() == bs.size());
      bs = 0;
      for (size_t i = 0; i < Nb; ++i)
        bs[Nb - i - 1] = (s[i] == '1');
    }
    template<class Archive, size_t Nb>
    void serialize(Archive& ar, std::bitset<Nb>& bs, const unsigned int version) {
      boost::serialization::split_free(ar, bs, version);
    }
  }
}

namespace sferes {
  namespace gen {
    namespace _bitstring {
      template<long K, long N>
      struct _pow {
        SFERES_CONST double result = K * _pow<K, N - 1>::result;
      };
      template<long K>
      struct _pow<K, 1> {
        SFERES_CONST double result = K;
      };

    }
    /// in range [0;1]
    template<int Size, typename Params, typename Exact = stc::Itself>
    class BitString : public stc::Any<Exact> {
     public:
      typedef Params params_t;
      typedef BitString<Size, Params, Exact> this_t;
      typedef std::bitset<Params::bit_string::nb_bits> bs_t;
      SFERES_CONST double bs_max = _bitstring::_pow<2, Params::bit_string::nb_bits>::result - 1;

      BitString() : _data(Size) {
      }

      //@{
      void mutate() {
        BOOST_FOREACH(bs_t & b, _data)
        if (misc::rand<float>() < Params::bit_string::mutation_rate)
          for (size_t i = 0; i < b.size(); ++i)
            if (misc::rand<float>() < Params::bit_string::mutation_rate_bit)
              b[i].flip();
      }
      // 1-point cross-over
      void cross(const BitString& o, BitString& c1, BitString& c2) {
        assert(Size == _data.size());
        assert(c1._data.size() == _data.size());
        assert(c2._data.size() == _data.size());

        for (size_t i = 0; i < c1._data.size(); ++i) {
          size_t k = misc::rand(c1._data.size());
          for (size_t j = 0; j < c1._data[i].size(); ++j)
            if (j < k) {
              c1._data[i][j] = _data[i][j];
              c2._data[i][j] = o._data[i][j];
            } else {
              c1._data[i][j] = o._data[i][j];
              c2._data[i][j] = _data[i][j];
            }
        }
      }
      void random() {
        BOOST_FOREACH(bs_t & v, _data)
        for (size_t i = 0; i < v.size(); ++i)
          v[i] = (int) misc::flip_coin();
      }
      //@}

      //@{
      float data(size_t i) const {
        assert(bs_max != 0);
        assert(i < _data.size());
        return _to_double(_data[i]) / bs_max;
      }
      unsigned long int_data(size_t i) const {
        assert(i < _data.size());
        return _data[i].to_ulong();
      }

      bs_t bs_data(size_t i) const {
        assert(i < _data.size());
        return _data[i];
      }

      size_t size() const {
        return Size;
      }
      //@}

      template<class Archive>
      void serialize(Archive& ar, const unsigned int version) {
        ar& BOOST_SERIALIZATION_NVP(_data);
      }
     protected:
      template<size_t N>
      double _to_double(const std::bitset<N>& d) const {
        double x = 0;
        size_t k = 1;
        for (size_t i = 0; i < N; ++i) {
          x += d[i] * k;
          k *= 2;
        }
        return x;
      }
      std::vector<bs_t> _data;
    };

  } // gen
} // sferes

#endif


#include "SignatureGraph.hpp"
#include "color.hpp"
#include "filesystem.hpp"
#include "orderPairs.hpp"
using namespace ChanZuckerberg::ExpressionMatrix2;

#include <boost/algorithm/string.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/graph/iteration_macros.hpp>
#include "boost_lexical_cast.hpp"
#include <boost/math/constants/constants.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>

#include "fstream.hpp"
#include "stdexcept.hpp"
#include "utility.hpp"

// Given the vertices, create the edges.
void SignatureGraph::createEdges(size_t lshBitCount)
{
    SignatureGraph& graph = *this;

    // Loop over all vertices.
    BitSet signature1(lshBitCount);
    BGL_FORALL_VERTICES(v0, graph, SignatureGraph) {
        const SignatureGraphVertex& vertex0 = graph[v0];
        const BitSetPointer signature0 = vertex0.signature;

        // For each zero bit, add an edge to the vertex
        // with the same bit set to 1.
        for(size_t bit=0; bit!=lshBitCount; bit++) {
            if(vertex0.signature.get(bit) == 0) {
                signature1 = signature0;
                signature1.set(bit);
                const auto it1 = vertexMap.find(signature1);
                if(it1 != vertexMap.end()) {
                    const vertex_descriptor v1 = it1->second;
                    add_edge(v0, v1, graph);
                }
            }
        }

    }
}

// Write out the signature graph in Graphviz format.
void SignatureGraph::writeGraphviz(const string& fileName) const
{
    ofstream s(fileName);
    writeGraphviz(s);
}
void SignatureGraph::writeGraphviz(ostream& s) const
{
    Writer writer(*this);
    boost::write_graphviz(s, *this, writer, writer, writer);
}
SignatureGraph::Writer::Writer(const SignatureGraph& graph) :
    graph(graph)
{
}

void SignatureGraph::Writer::operator()(std::ostream& s) const
{
    // s << "size=100 ratio=expand;\n";
    s << "node [shape=point];\n";
    // s << "edge [style=invis];n"; // len=10 penwidth=0.1
}

// Write out a vertex of the cell graph.
void SignatureGraph::Writer::operator()(std::ostream& s, vertex_descriptor v) const
{
    const SignatureGraphVertex& vertex = graph[v];
    const double vertexSize = 1.e-2 * sqrt(double(vertex.cellCount()));

    s << "[";
    s << "width=" << vertexSize;
    s << "]";
}

void SignatureGraph::Writer::operator()(std::ostream& s, edge_descriptor e) const
{
    const vertex_descriptor v0 = source(e, graph);
    const vertex_descriptor v1 = source(e, graph);
    const SignatureGraphVertex& vertex0 = graph[v0];
    const SignatureGraphVertex& vertex1 = graph[v0];
    double edgeWeight = double(vertex0.cellCount()) * double(vertex1.cellCount());

    s << "[weight=\"" << edgeWeight << "\"]";
}

// Use Graphviz to compute the graph layout and store it in the vertex positions.
void SignatureGraph::computeLayout()
{
    if(layoutWasComputed) {
        return;
    }

    using filesystem::remove;

    // Write the graph in Graphviz format.
    const string uuid = boost::uuids::to_string(boost::uuids::uuid(boost::uuids::random_generator()()));
    const string dotFileName = "/dev/shm/SignatureGraph-" + uuid + ".dot";
    writeGraphviz(dotFileName);

    // Run sfdp with output in Graphviz plain format.
    // See https://www.graphviz.org/doc/info/output.html#d:plain.
    const string dotPlainFileName = dotFileName + ".plain";
    const int systemReturnCode = ::system(("sfdp -O -Tplain " + dotFileName).c_str());
    const int sfdpReturnCode = WEXITSTATUS(systemReturnCode);   // Man page for system is not super clear on this.
    if(sfdpReturnCode!=0 && sfdpReturnCode!=1) {    // Sfdp returns 1 if built without triangulation library.
        remove(dotFileName);
        remove(dotPlainFileName);
        throw runtime_error("Error " +
            std::to_string(systemReturnCode) + " " +
            std::to_string(sfdpReturnCode) +
            " running sfdp.");
    }

    // Extract vertex positions from the output.
    ifstream file(dotPlainFileName);
    string line;
    vector<string> tokens;
    while(true) {

        // Get a line.
        getline(file, line);
        if(!file) {
            break;
        }

        // Parse it.
        boost::algorithm::split(tokens, line, boost::algorithm::is_any_of(" "));

        // Only parse lines that describe vertices.
        CZI_ASSERT(tokens.size() >= 1);
        if(tokens[0] != "node") {
            continue;
        }
        CZI_ASSERT(tokens.size() >= 4);

        // Extract the positions for this vertex.
        try {
            const vertex_descriptor v = lexical_cast<vertex_descriptor>(tokens[1]);
            SignatureGraphVertex& vertex = (*this)[v];
            vertex.position[0] = lexical_cast<double>(tokens[2]);
            vertex.position[1] = lexical_cast<double>(tokens[3]);
        } catch(std::exception& e) {
            const string message = "Error processing the following line of " + dotPlainFileName + ": " +  line + "\nError is: " + e.what();
            throw runtime_error(message);
        }
    }
    layoutWasComputed = true;

    // Remove the files we created.
    remove(dotFileName);
    remove(dotPlainFileName);
}

// Write out the signature graph in Graphviz format.
void SignatureGraph::writeSvg(
    const string& fileName,
    SvgParameters& svgParameters)
{
    ofstream s(fileName);
    writeSvg(s, svgParameters);
}
void SignatureGraph::writeSvg(
    ostream& s,
    SvgParameters& svgParameters)
{
    // Make sure the layout was computed.
    computeLayout();

    // Compute minimum and maximum of the vertices coordinates,
    // and the maximum number of cells.
    double xMin = std::numeric_limits<double>::max();
    double xMax = std::numeric_limits<double>::min();
    double yMin = xMin;
    double yMax = xMax;
    CellId maxCellCount = 0;
    const SignatureGraph& graph = *this;
    BGL_FORALL_VERTICES(v, graph, SignatureGraph) {
        const SignatureGraphVertex& vertex = graph[v];
        const double x = vertex.position[0];
        const double y = vertex.position[1];
        xMin = min(xMin, x);
        xMax = max(xMax, x);
        yMin = min(yMin, y);
        yMax = max(yMax, y);
        maxCellCount = max(maxCellCount, CellId(vertex.cellCount()));
    }

    // Center and size of the square bounding box containing all the vertices.
    const double xBoundingBoxCenter = (xMin + xMax) / 2.;
    const double yBoundingBoxCenter = (yMin + yMax) / 2.;
    const double xBoundingBoxSize = xMax - xMin;
    const double yBoundingBoxSize = yMax - yMin;
    const double boundingBoxSize = max(xBoundingBoxSize, yBoundingBoxSize);

    // Viewbox parameters.
    const double xViewBoxCenter = xBoundingBoxCenter - svgParameters.xShift;
    const double yViewBoxCenter = yBoundingBoxCenter - svgParameters.yShift;
    const double viewBoxSize = 1.05 * boundingBoxSize / svgParameters.zoomFactor;
    const double xMinViewBox = xViewBoxCenter - viewBoxSize/2.;
    const double yMinViewBox = yViewBoxCenter - viewBoxSize/2.;

    // Fill in the rest of the SvgParameters.
    svgParameters.xCenter = xViewBoxCenter;
    svgParameters.yCenter = yViewBoxCenter;
    svgParameters.halfViewBoxSize = viewBoxSize / 2.;
    svgParameters.pixelSize = viewBoxSize / svgParameters.svgSizePixels;

    // Compute a table of vertices ordered by decreasing number of cells.
    vector< pair<vertex_descriptor, CellId> > sortedVertices;
    BGL_FORALL_VERTICES(v, graph, SignatureGraph) {
        const SignatureGraphVertex& vertex = graph[v];
        sortedVertices.push_back(make_pair(v, vertex.cellCount()));
    }
    sort(sortedVertices.begin(), sortedVertices.end(), OrderPairsBySecondGreater< pair<vertex_descriptor, CellId> >());

    // Start the svg object.
    s <<
        "<svg id=svgObject "
        "style='border:solid DarkBlue;margin:2;' "
        "width='" << svgParameters.svgSizePixels << "' "
        "height='" << svgParameters.svgSizePixels << "' "
        "viewBox='" << xMinViewBox << " " << yMinViewBox << " " << viewBoxSize << " " << viewBoxSize << "'"
        ">";

    // Draw the edges before the vertices, to avoid obscuring the vertices.
    if(!svgParameters.hideEdges) {
        s << "<g id=edges>";
        const double unscaledEdgeThickness = 5.e-4 * boundingBoxSize;
        BGL_FORALL_EDGES(e, graph, SignatureGraph) {
            const vertex_descriptor v1 = source(e, graph);
            const vertex_descriptor v2 = target(e, graph);
            const SignatureGraphVertex& vertex1 = graph[v1];
            const SignatureGraphVertex& vertex2 = graph[v2];
            s << "<line x1='" << vertex1.position[0] << "' y1='" << vertex1.position[1] << "'";
            s << " x2='" << vertex2.position[0] << "' y2='" << vertex2.position[1] << "'";

            s << " style='stroke:black;stroke-width:" <<
                unscaledEdgeThickness * svgParameters.edgeThicknessFactor << "' />";
        }
        s << "</g>";
    }

    // Write the vertices in order of decreasing size.
    // This way we mitigate obscuring of vertices by other vertices.
    s << "<g id=vertices>"; // fill-opacity='0.5'
    const double largestVertexUnscaledRadius = 0.03 * boundingBoxSize;
    for(const auto& p: sortedVertices) {
        const vertex_descriptor v = p.first;
        const SignatureGraphVertex& vertex = graph[v];
        const double x = vertex.position[0];
        const double y = vertex.position[1];
        const double vertexRadius =
            largestVertexUnscaledRadius *
            sqrt(double(vertex.cellCount())/double(maxCellCount));

        if(vertex.colors.size() < 2) {

            // Draw the vertex as a circle.
            const string vertexColor = vertex.colors.empty() ? string("black") : vertex.colors.front().first;
            s << "<circle cx='0' cy='0' r='" <<
                vertexRadius << "' stroke='none' fill='" << vertexColor << "'"
                " transform='translate(" << x << " " << y << ") scale(" << svgParameters.vertexSizeFactor << ")'"
                "></circle>";

        } else {

            // Draw the vertex as a pie chart.
            double totalAngle = 0.;
            for(const auto& p: vertex.colors) {
                const string& color = p.first;
                const double angle = p.second * 2. * boost::math::constants::pi<double>();
                const double angle0 = totalAngle;
                const double angle1 = angle0 + angle;
                s << "<path d='M 0 0 L " << vertexRadius*cos(angle0) << " " << vertexRadius*sin(angle0) << " A " <<
                    vertexRadius << " " << vertexRadius << " 0 " <<
                    ((angle>boost::math::constants::pi<double>()) ? 1 : 0) << " 1 " <<
                    vertexRadius*cos(angle1) << " " << vertexRadius*sin(angle1) <<
                    " Z' stroke='none' fill='" << color << "'"
                    " transform='translate(" << x << " " << y << ") scale(" << svgParameters.vertexSizeFactor << ")'"
                    "></path>";

                totalAngle = angle1;
            }
        }
    }
    s << "</g>";

    // End the svg object.
    s << "</svg>";
}


#include <cpprest/filestream.h>
#include <boost/program_options.hpp>
#include <cpprest/http_listener.h>
#include <cpprest/json.h>
#include <cpprest/uri.h>
#include <cpprest/ws_client.h>
#include <cpprest/containerstream.h>
#include <cpprest/interopstream.h>
#include <cpprest/rawptrstream.h>
#include <cpprest/producerconsumerstream.h>
#include <chrono>
#include <KDTree.h>
#include <Point.h>
#include <json11.hpp>
#include <boost/graph/adj_list_serialize.hpp>
#include <src/routesfetcher.h>
#include <boost/archive/text_oarchive.hpp>
#include <iostream>
#include <sstream>
#include <boost/archive/text_iarchive.hpp>

using namespace utility;
using namespace web;
using namespace web::http;
using namespace web::http::client;
using namespace concurrency::streams;
using namespace web::http::experimental::listener;
using namespace web::experimental::web_sockets::client;
using namespace arlib;
using namespace std;

using Graph = boost::adjacency_list<boost::vecS, boost::vecS,
        boost::bidirectionalS, Location,
        boost::property<boost::edge_weight_t, float>>;
using Vertex = typename boost::graph_traits<Graph>::vertex_descriptor;
using Edge = typename boost::graph_traits<Graph>::edge_descriptor;

class RoutesDealer
{
public:
    RoutesDealer() {}
    RoutesDealer(utility::string_t url, Graph &g, KDTree<2, int> &tree);

    pplx::task<void> open() { return m_listener.open(); }
    pplx::task<void> close() { return m_listener.close(); }

private:
    void handle_get(http_request request);
    Graph g;
    KDTree<2, int> tree;
    http_listener m_listener;
};

RoutesDealer::RoutesDealer(utility::string_t url, Graph &g, KDTree<2, int> &tree) : m_listener(url)
{
    m_listener.support(methods::GET, std::bind(&RoutesDealer::handle_get, this, std::placeholders::_1));
    this->g = g;
    this->tree = tree;
}

void send_error(http_request message, std::string body) {
    http_response response(status_codes::BadRequest);
    response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
    response.set_body(body);
    message.reply(response);
}

void RoutesDealer::handle_get(http_request request)
{
    try {
        bool reroute = false;
        float lat, lon;
        auto url_message= uri::decode(request.relative_uri().to_string());
        url_message.erase(0,2);
        map<utility::string_t, utility::string_t> keyMap = uri::split_query(url_message);
        int num_routes;
        if (keyMap.find("s_lat") != keyMap.end()) {
            lat = stof(keyMap["s_lat"]);
        } else send_error(request, "Starting latitude missing");
        if (keyMap.find("s_lon") != keyMap.end()) {
            lon = stof(keyMap["s_lon"]);
        } else send_error(request, "Starting longitude missing");
        Vertex start;
        auto start_get_vertices = chrono::steady_clock::now();
        get_vertex(lat, lon, tree, start);
        if (keyMap.find("e_lat") != keyMap.end()) {
            lat = stof(keyMap["e_lat"]);
        } else send_error(request, "Destination latitude missing");
        if (keyMap.find("e_lon") != keyMap.end()) {
            lon = stof(keyMap["e_lon"]);
        } else send_error(request, "Destination longitude missing");
        if (keyMap.find("n_routes") != keyMap.end()) {
            num_routes = std::stoi(keyMap["n_routes"]);
        }
        else num_routes = 1;
        Vertex end;
        get_vertex(lat, lon, tree, end);
        auto end_get_vertices = chrono::steady_clock::now();
        logElapsedMillis("Found vertices in graph", start_get_vertices, end_get_vertices);
        if (keyMap.find("reroute") != keyMap.end()) {
            reroute = parseBoolean(keyMap["reroute"]);
        }
        auto paths = get_alternative_routes(g, start, end, num_routes, 0.9, reroute);
        http_response response(status_codes::OK);
        response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
        response.set_body(paths.dump());
        request.reply(response);
        cout << endl;
    }
    catch (const std::invalid_argument& e) {
        send_error(request, e.what());
    }
    catch (...) {
        send_error(request, "ERROR!");
    }
}

int main(int argc, char* argv[]) {
    namespace po = boost::program_options;
    using dataset = vector<pair<Point<2>, int>>;
    po::options_description desc("Allowed options");
    std::string endpoint;
    desc.add_options()
            ("endpoint", po::value<string>()->default_value("http://0.0.0.0:1337/getroutes?"), "Insert the server endpoint")
            ;
    po::variables_map opts;
    po::positional_options_description p;
    p.add("endpoint", -1);
    po::store(po::command_line_parser(argc,argv).options(desc).positional(p).run(), opts);
    try {
        po::notify(opts);
    } catch (std::exception& e) {
        std::cerr << "Error: " << e.what() << "\n";
        return 1;
    }
    endpoint = opts["endpoint"].as<std::string>();
    Graph g;
    auto start = chrono::steady_clock::now();
    dataset data = location_graph_from_string("weights", "ids", g, true);
    auto end = chrono::steady_clock::now();
    logElapsedMillis("Loaded coordinates and weights", start, end);
    KDTree<2, int> kd(data);
    utility::string_t address = U(endpoint);
    RoutesDealer listener(address, g, kd);
    listener.open().wait();

    cout << utility::string_t(U("Listening for requests at 0.0.0.0:1337"))  << endl;

    std::string cline;
    std::wcout << U("Hit Enter to close the listener.") << endl;
    std::getline(std::cin, cline);

    listener.close().wait();
}


// -*-c++-*-

/*!
  \file test_rundom_convex_hull.cpp
  \brief test code for rcsc::ConvexHull
*/

/*
 *Copyright:

 Copyright (C) Hidehisa Akiyama

 This code is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 3 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 *EndCopyright:
 */

#ifdef HAVE_CONFIG
#include <config.h>
#endif

#include "convex_hull.h"

#include <boost/random.hpp>

#include <fstream>
#include <ctime>

#include <sys/time.h> // struct timeval, gettimeofday()

void
print_elapsed( const timeval & start,
               const timeval & end )
{
    long sec_diff = end.tv_sec - start.tv_sec;
    long usec_diff = end.tv_usec - start.tv_usec;

    while ( usec_diff >= (1000 * 1000) )
    {
        usec_diff -= 1000 * 1000;
        ++sec_diff;
    }

    while ( usec_diff < 0 && sec_diff > 0 )
    {
        usec_diff += (1000 * 1000);
        --sec_diff;
    }

    std::cout << "  elapsed "
              << sec_diff * 1000.0 + usec_diff * 0.001
              << " [ms]" << std::endl;
}

int
main()
{
    boost::mt19937 eng( std::time( 0 ) );

    //const double min_x = -10.0;
    //const double max_x = +10.0;
    //const double min_y = -10.0;
    //const double max_y = +10.0;
    //boost::variate_generator< boost::mt19937&, boost::uniform_real<> > x_rng( eng, boost::uniform_real<>( min_x, max_x ) );
    //boost::variate_generator< boost::mt19937&, boost::uniform_real<> > y_rng( eng, boost::uniform_real<>( min_y, max_y ) );

    boost::variate_generator< boost::mt19937&, boost::normal_distribution<> > x_rng( eng, boost::normal_distribution<>( 0.0, 5.0 ) );
    boost::variate_generator< boost::mt19937&, boost::normal_distribution<> > y_rng( eng, boost::normal_distribution<>( 0.0, 5.0 ) );

    rcsc::ConvexHull c0;
    rcsc::ConvexHull c1;

    for ( int i = 0; i < 1000; ++i )
    {
        rcsc::Vector2D p( x_rng(), y_rng() );
        c0.addPoint( p );
        c1.addPoint( p );
    }

    {
        timeval start;
        if ( ::gettimeofday( &start, NULL ) == -1 )
        {
            std::perror( "gettimeofday" );
        }

        c0.compute( rcsc::ConvexHull::WrappingMethod );

        timeval end;
        if ( ::gettimeofday( &end, NULL ) == -1 )
        {
            std::perror( "gettimeofday" );
        }

        std::cout << "WrappingMethod\n"
                  << "  points = " << c0.inputPoints().size() << '\n'
                  << "  vertices = " << c0.vertices().size() << '\n'
                  << "  edges = " << c0.edges().size() << '\n';

        print_elapsed( start, end );
    }

    {
        timeval start;
        if ( ::gettimeofday( &start, NULL ) == -1 )
        {
            std::perror( "gettimeofday" );
        }

        c1.compute( rcsc::ConvexHull::GrahamScan );

        timeval end;
        if ( ::gettimeofday( &end, NULL ) == -1 )
        {
            std::perror( "gettimeofday" );
        }

        std::cout << "GrahamScan\n"
                  << "  points = " << c1.inputPoints().size() << '\n'
                  << "  vertices = " << c1.vertices().size() << '\n'
                  << "  edges = " << c1.edges().size() << '\n';

        print_elapsed( start, end );
    }

    std::ofstream point_file( "points.dat" );
    std::ofstream edge_file( "edges.dat" );

    c1.printInputPoints( point_file );
    c1.printEdges( edge_file );

    /*
      > gnuplot
      > plot "points.dat" w p, "edges.dat" w l
     */

    return 0;
}


#ifndef STAN_MATH_OPENCL_PRIM_CATEGORICAL_LOGIT_GLM_LPMF_HPP
#define STAN_MATH_OPENCL_PRIM_CATEGORICAL_LOGIT_GLM_LPMF_HPP
#ifdef STAN_OPENCL

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/err.hpp>
#include <stan/math/prim/fun/size_zero.hpp>
#include <stan/math/prim/fun/sum.hpp>
#include <stan/math/prim/fun/to_ref.hpp>
#include <Eigen/Core>

#include <stan/math/opencl/matrix_cl.hpp>
#include <stan/math/opencl/copy.hpp>
#include <stan/math/opencl/multiply.hpp>
#include <stan/math/opencl/kernel_generator.hpp>
#include <stan/math/opencl/kernels/categorical_logit_glm_lpmf.hpp>

namespace stan {
namespace math {

/** \ingroup opencl
 * Returns the log PMF of the Generalized Linear Model (GLM)
 * with categorical distribution and logit (softmax) link function.
 * This is an overload of the GLM in
 * prim/prob/categorical_logit_glm_lpmf.hpp that is implemented in OpenCL.
 *
 * @tparam T_alpha type of the intercept vector
 * @tparam T_beta type of a scalar in the matrix of weights
 * @param y_cl a scalar or vector of classes. If it is a scalar it will be
 * broadcast - used for all instances. Values should be between 1 and number of
 * classes, including endpoints.
 * @param x_cl design matrix on OpenCL device. This overload does not support
 * broadcasting of a row vector x!
 * @param alpha intercept vector (in log odds)
 * @param beta weight matrix
 * @return log probability or log sum of probabilities
 * @throw std::domain_error x, beta or alpha is infinite or y is not within
 * bounds
 * @throw std::invalid_argument if container sizes mismatch.
 */
template <bool propto, typename T_alpha, typename T_beta,
          require_eigen_col_vector_t<T_alpha>* = nullptr,
          require_eigen_matrix_t<T_beta>* = nullptr>
return_type_t<T_alpha, T_beta> categorical_logit_glm_lpmf(
    const matrix_cl<int>& y_cl, const matrix_cl<double>& x_cl,
    const T_alpha& alpha, const T_beta& beta) {
  using T_partials_return = partials_return_t<T_alpha, T_beta>;
  static const char* function = "categorical_logit_glm_lpmf";

  using Eigen::Array;
  using Eigen::Dynamic;
  using Eigen::Matrix;

  const size_t N_instances = x_cl.rows();
  const size_t N_attributes = x_cl.cols();
  const size_t N_classes = beta.cols();

  if (y_cl.size() != 1) {
    check_size_match(function, "x.rows()", N_instances, "y.size()",
                     y_cl.size());
  }
  check_consistent_size(function, "Intercept vector", alpha, N_classes);
  check_size_match(function, "x.cols()", N_attributes, "beta.rows()",
                   beta.rows());

  if (N_instances == 0 || N_classes <= 1) {
    return 0;
  }

  if (!include_summand<propto, T_alpha, T_beta>::value) {
    return 0;
  }

  const auto& alpha_ref = to_ref_if<!is_constant<T_alpha>::value>(alpha);
  const auto& beta_ref = to_ref_if<!is_constant<T_beta>::value>(beta);

  const auto& alpha_val = value_of_rec(alpha_ref);
  const auto& beta_val = value_of_rec(beta_ref);

  const int local_size
      = opencl_kernels::categorical_logit_glm.get_option("LOCAL_SIZE_");
  const int wgs = (N_instances + local_size - 1) / local_size;

  const matrix_cl<double> beta_cl(beta_val);
  const matrix_cl<double> alpha_cl(alpha_val);

  matrix_cl<double> x_beta_cl = x_cl * beta_cl;

  bool need_alpha_derivative = !is_constant_all<T_alpha>::value;
  bool need_beta_derivative = !is_constant_all<T_beta>::value;

  matrix_cl<double> logp_cl(wgs, 1);
  matrix_cl<double> exp_lin_cl(N_instances, N_classes);
  matrix_cl<double> inv_sum_exp_lin_cl(N_instances, 1);
  matrix_cl<double> neg_softmax_lin_cl(
      need_alpha_derivative || need_beta_derivative ? N_instances : 0,
      N_classes);
  matrix_cl<double> alpha_derivative_cl(need_alpha_derivative ? wgs : 0,
                                        N_classes);

  try {
    opencl_kernels::categorical_logit_glm(
        cl::NDRange(local_size * wgs), cl::NDRange(local_size), logp_cl,
        exp_lin_cl, inv_sum_exp_lin_cl, neg_softmax_lin_cl, alpha_derivative_cl,
        y_cl, x_beta_cl, alpha_cl, N_instances, N_attributes, N_classes,
        y_cl.size() != 1, need_alpha_derivative, need_beta_derivative);
  } catch (const cl::Error& e) {
    check_opencl_error(function, e);
  }
  T_partials_return logp = sum(from_matrix_cl(logp_cl));

  if (!std::isfinite(logp)) {
    check_bounded(function, "categorical outcome out of support",
                  from_matrix_cl(y_cl), 1, N_classes);
    check_finite(function, "Weight vector", beta);
    check_finite(function, "Intercept", alpha);
    check_finite(function, "Matrix of independent variables",
                 from_matrix_cl(x_cl));
  }

  operands_and_partials<decltype(alpha_ref), decltype(beta_ref)> ops_partials(
      alpha_ref, beta_ref);
  if (!is_constant_all<T_alpha>::value) {
    ops_partials.edge1_.partials_
        = from_matrix_cl(alpha_derivative_cl).colwise().sum();
  }
  if (!is_constant_all<T_beta>::value && N_attributes != 0) {
    matrix_cl<double> beta_derivative_cl = transpose(x_cl) * neg_softmax_lin_cl;
    matrix_cl<double> temp(N_classes, local_size * N_attributes);
    try {
      opencl_kernels::categorical_logit_glm_beta_derivative(
          cl::NDRange(local_size * N_attributes), cl::NDRange(local_size),
          beta_derivative_cl, temp, y_cl, x_cl, N_instances, N_attributes,
          N_classes, y_cl.size() != 1);
    } catch (const cl::Error& e) {
      check_opencl_error(function, e);
    }
    ops_partials.edge2_.partials_ = from_matrix_cl(beta_derivative_cl);
  }
  return ops_partials.build(logp);
}

}  // namespace math
}  // namespace stan

#endif
#endif


/**************************************************************************\
|
|    Copyright (C) 2009 Marc Stevens
|
|    This program is free software: you can redistribute it and/or modify
|    it under the terms of the GNU General Public License as published by
|    the Free Software Foundation, either version 3 of the License, or
|    (at your option) any later version.
|
|    This program is distributed in the hope that it will be useful,
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
|    GNU General Public License for more details.
|
|    You should have received a copy of the GNU General Public License
|    along with this program.  If not, see <http://www.gnu.org/licenses/>.
|
\**************************************************************************/

#include <string>
#include <stdexcept>
#include <cmath>

#include <boost/lexical_cast.hpp>
#include <boost/filesystem/operations.hpp>

#include <hashclash/saveload_gz.hpp>
#include <hashclash/md5detail.hpp>
#include <hashclash/booleanfunction.hpp>
#include <hashclash/rng.hpp>

#include "main.hpp"

#define HASHCLASH_MD5COMPRESS_STEP(f, a, b, c, d, m, ac, rc) \
        a += f(b, c, d) + m + ac; a = rotate_left(a,rc); a += b;

double testnearcollprob(uint32 dm11, uint32 diffcd, uint32 diffb) {
        uint32 count = 0;
        for (unsigned k = 0; k < (1<<23); ++k)
        {
                uint32 a = xrng64(), b = xrng64()+xrng64()*11, c = xrng64(), d = xrng64()+xrng64()*11;
                uint32 a2 = a, b2 = b, c2 = c, d2 = d;
                uint32 m11 = xrng64(), m2 = xrng64(), m9 = xrng64();
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, d, a, b, c, m11, 0xbd3af235, 10);
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, c, d, a, b, m2, 0x2ad7d2bb, 15);
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, b, c, d, a, m9, 0xeb86d391, 21);
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, d2, a2, b2, c2, m11+dm11, 0xbd3af235, 10);
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, c2, d2, a2, b2, m2, 0x2ad7d2bb, 15);
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, b2, c2, d2, a2, m9, 0xeb86d391, 21);
                if (d2-d==diffcd && c2-c==diffcd && b2-b==diffb)
                        ++count;
        }
        return double(count)/double(1<<23);
}

void constructupperpath_sbcpc(differentialpath& path, uint32 dm[16], uint32 diffihv[4])
{
	uint32 a,b,c,d,a2,b2,c2,d2,oa,ob,oc,od,m11,m2,m9,m4;
	a = xrng64(); b = xrng64()+xrng64()*11; c = xrng64(); d = xrng64()+xrng64()*11;
	while (true) {
                oa = a2 = a; ob = b2 = b; oc = c2 = c; od = d2 = d;
                m4 = xrng64();
		HASHCLASH_MD5COMPRESS_STEP(md5_ii, a, b, c, d, m4, 0xf7537e82,  6);
		HASHCLASH_MD5COMPRESS_STEP(md5_ii, a2, b2, c2, d2, m4+dm[4], 0xf7537e82,  6);
		if (a2-a != diffihv[0]) {
			b = xrng64(); c = xrng64(); d = xrng64();
			continue;
		}
		m11 = xrng64();
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, d, a, b, c, m11, 0xbd3af235, 10);
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, d2, a2, b2, c2, m11+dm[11], 0xbd3af235, 10);
		if (d2-d != diffihv[3]) {
			b = xrng64(); c = xrng64();
			continue;
		}
		m2 = xrng64();
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, c, d, a, b, m2, 0x2ad7d2bb, 15);
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, c2, d2, a2, b2, m2+dm[2], 0x2ad7d2bb, 15);
		if (c2-c != diffihv[2]) {
			b = xrng64();
			continue;
		}
		m9 = xrng64();
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, b, c, d, a, m9, 0xeb86d391, 21);
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, b2, c2, d2, a2, m9+dm[9], 0xeb86d391, 21);
		if (b2-b == diffihv[1]) break;
        }
	for (unsigned i = 55; i <= 64; ++i)
		path[i].clear();
	path[61] = sdr(a,a2);
	path[62] = sdr(d,d2);
	path[63] = sdr(c,c2);
	path[64] = sdr(b,b2);

	a2 = oa; b2 = ob; c2 = oc; d2 = od;
	a = oa; b = ob; c = oc; d = od;
	HASHCLASH_MD5COMPRESS_STEP(md5_ii, a, b, c, d, m4, 0xf7537e82,  6);
	HASHCLASH_MD5COMPRESS_STEP(md5_ii, a2, b2, c2, d2, m4+dm[4], 0xf7537e82,  6);
	sdr dF61 = sdr(md5_ii(a,b,c),md5_ii(a2,b2,c2));
	HASHCLASH_MD5COMPRESS_STEP(md5_ii, d, a, b, c, m11, 0xbd3af235, 10);
	HASHCLASH_MD5COMPRESS_STEP(md5_ii, d2, a2, b2, c2, m11+dm[11], 0xbd3af235, 10);
	sdr dF62 = sdr(md5_ii(d,a,b),md5_ii(d2,a2,b2));
	HASHCLASH_MD5COMPRESS_STEP(md5_ii, c, d, a, b, m2, 0x2ad7d2bb, 15);
	HASHCLASH_MD5COMPRESS_STEP(md5_ii, c2, d2, a2, b2, m2+dm[2], 0x2ad7d2bb, 15);
	sdr dF63 = sdr(md5_ii(c,d,a),md5_ii(c2,d2,a2));

	for (unsigned b = 0; b < 32; ++b) {
		bitcondition f61 = bc_constant, f62 = bc_constant, f63 = bc_constant;
		if (dF61.get(b) == +1) f61 = bc_plus;
		if (dF61.get(b) == -1) f61 = bc_minus;
		if (dF62.get(b) == +1) f62 = bc_plus;
		if (dF62.get(b) == -1) f62 = bc_minus;
		if (dF63.get(b) == +1) f63 = bc_plus;
		if (dF63.get(b) == -1) f63 = bc_minus;
		bf_conditions bc0 = MD5_I_data.forwardconditions(path[61][b], bc_constant, bc_constant, f61);
		bf_conditions bc1 = MD5_I_data.forwardconditions(path[62][b], bc0.first, bc0.second, f62);
		bf_conditions bc2 = MD5_I_data.forwardconditions(path[63][b], bc1.first, bc1.second, f63);
		path.setbitcondition(59, b, bc0.third);
		path.setbitcondition(60, b, bc1.third);
		path.setbitcondition(61, b, bc2.third);
		path.setbitcondition(62, b, bc2.second);
		path.setbitcondition(63, b, bc2.first);
	}
}

void constructupperpath(differentialpath& path, uint32 dm11, uint32 diffcd, uint32 diffb)
{
	uint32 a,b,c,d,a2,b2,c2,d2,oa,ob,oc,od,m11,m2,m9;
	while (true) {
                a = xrng64(); b = xrng64()+xrng64()*11; c = xrng64(); d = xrng64()+xrng64()*11;
                a2 = a; b2 = b; c2 = c; d2 = d;
                oa = a; ob = b; oc = c; od = d;
                m11 = xrng64(); m2 = xrng64(); m9 = xrng64();
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, d, a, b, c, m11, 0xbd3af235, 10);
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, c, d, a, b, m2, 0x2ad7d2bb, 15);
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, b, c, d, a, m9, 0xeb86d391, 21);
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, d2, a2, b2, c2, m11+dm11, 0xbd3af235, 10);
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, c2, d2, a2, b2, m2, 0x2ad7d2bb, 15);
                HASHCLASH_MD5COMPRESS_STEP(md5_ii, b2, c2, d2, a2, m9, 0xeb86d391, 21);
                if (d2-d==diffcd && c2-c==diffcd && b2-b==diffb)
                	break;
        }
	for (unsigned i = 58; i <= 64; ++i)
		path[i].clear();
	path[62] = sdr(d,d2);
	path[63] = sdr(c,c2);
	path[64] = sdr(b,b2);

	a2 = oa; b2 = ob; c2 = oc; d2 = od;
	a = oa; b = ob; c = oc; d = od;
	HASHCLASH_MD5COMPRESS_STEP(md5_ii, d, a, b, c, m11, 0xbd3af235, 10);
	HASHCLASH_MD5COMPRESS_STEP(md5_ii, d2, a2, b2, c2, m11+dm11, 0xbd3af235, 10);
	sdr dF62 = sdr(md5_ii(d,a,b),md5_ii(d2,a2,b2));
	HASHCLASH_MD5COMPRESS_STEP(md5_ii, c, d, a, b, m2, 0x2ad7d2bb, 15);
	HASHCLASH_MD5COMPRESS_STEP(md5_ii, c2, d2, a2, b2, m2, 0x2ad7d2bb, 15);
	sdr dF63 = sdr(md5_ii(c,d,a),md5_ii(c2,d2,a2));

	for (unsigned b = 0; b < 32; ++b) {
		bitcondition f62 = bc_constant, f63 = bc_constant;
		if (dF62.get(b) == +1) f62 = bc_plus;
		if (dF62.get(b) == -1) f62 = bc_minus;
		if (dF63.get(b) == +1) f63 = bc_plus;
		if (dF63.get(b) == -1) f63 = bc_minus;
		bf_conditions bc1 = MD5_I_data.forwardconditions(path[62][b], bc_constant, bc_constant, f62);
		bf_conditions bc2 = MD5_I_data.forwardconditions(path[63][b], bc1.first, bc1.second, f63);
		path.setbitcondition(60, b, bc1.third);
		path.setbitcondition(61, b, bc2.third);
		path.setbitcondition(62, b, bc2.second);
		path.setbitcondition(63, b, bc2.first);
	}
}

int startnearcollision(parameters_type& parameters)
{
	uint32 ihv1[4];
	uint32 ihv2[4];
	uint32 msg1[16];
	uint32 msg2[16];
	{
		ifstream if1(parameters.infile1.c_str(), ios::binary);
		if (!if1) {
			cerr << "Error: cannot open inputfile 1 '" << parameters.infile1 << "'!" << endl;
			return 1;
		}
		ifstream if2(parameters.infile2.c_str(), ios::binary);
		if (!if2) {
			cerr << "Error: cannot open inputfile 2 '" << parameters.infile2 << "'!" << endl;
			return 1;
		}

		for (unsigned k = 0; k < 4; ++k)
			ihv1[k] = ihv2[k] = md5_iv[k];

		// load, md5 and save inputfile1
		unsigned file1blocks = 0;
		while (load_block(if1, msg1) > 0) {
			md5compress(ihv1, msg1);
			++file1blocks;
		}
		// load, md5 and save inputfile2
		unsigned file2blocks = 0;
		while (load_block(if2, msg2) > 0) {
			md5compress(ihv2, msg2);
			++file2blocks;
		}
		if (file1blocks != file2blocks) {
			cerr << "Error: inputfile 1 and 2 are not of equal size" << endl;
			return 2;
		}
	}

	cout << "IHV1   = {" << ihv1[0] << "," << ihv1[1] << "," << ihv1[2] << "," << ihv1[3] << "}" << endl;
	cout << "IHV1   = " << hex;
	for (unsigned k = 0; k < 4; ++k)
		for (unsigned c = 0; c < 4; ++c)
		{
			cout.width(2); cout.fill('0');
			cout << ((ihv1[k]>>(c*8))&0xFF);
		}
	cout << dec << endl << endl;

	cout << "IHV2   = {" << ihv2[0] << "," << ihv2[1] << "," << ihv2[2] << "," << ihv2[3] << "}" << endl;
	cout << "IHV2   = " << hex;
	for (unsigned k = 0; k < 4; ++k)
		for (unsigned c = 0; c < 4; ++c)
		{
			cout.width(2); cout.fill('0');
			cout << ((ihv2[k]>>(c*8))&0xFF);
		}
	cout << dec << endl << endl;

	uint32 dihv[4] = { ihv2[0] - ihv1[0], ihv2[1] - ihv1[1], ihv2[2] - ihv1[2], ihv2[3] - ihv1[3] };
	cout << "dIHV   = {" << dihv[0] << "," << dihv[1] << "," << dihv[2] << "," << dihv[3] << "}" << endl;

	bool sbcpc = false;
	if (dihv[0]==0 && dihv[1]==0 && dihv[2]==0 && dihv[3]==0)
	{
		cerr << "dIHV is zero!" << endl;
		return 4;
	}
	if (dihv[0] == +(1<<5) && dihv[3] == +(1<<5) -(1<<25))
		sbcpc = true;
	else if (dihv[0] != 0 || dihv[2] != dihv[3])
	{
		cerr << "Error: dIHV is not of the required form dIHV[0]=0, dIHV[2]=dIHV[3]" << endl;
		return 3;
	}
	uint32 m_diff[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
	vector<uint32> dm11;
	vector< pair<uint32,uint32> > ncdiff;
	unsigned j = 0;
	if (sbcpc) {
		m_diff[11] = 1<<15;
		m_diff[4] = 1<<31;
		m_diff[14] = 1<<31;
		m_diff[2] = 1<<8;
	} else {
		sdr diffcd = naf(0-dihv[3]);
		sdr diffb  = naf(dihv[3] - dihv[1]);
		unsigned bb = 0;
		while (bb < 32) {
			if (diffcd.get(bb) != 0) {
				unsigned bend=bb+1;
				while (bend < 32 && bend<=bb+parameters.pathtyperange && diffcd.get(bend)==0)
					++bend;
				uint32 diff1 = 1<<bb;
				if (diffcd.get(bb) == -1)
					diff1 = -(1<<bb);
				uint32 diff2 = diff1;
				for (unsigned b2 = bb; b2 < bend; ++b2)
					if (diffb.get((b2+21)%32) == +1)
						diff2 += 1<<((b2+21)%32);
					else if (diffb.get((b2+21)%32) == -1)
						diff2 -= 1<<((b2+21)%32);
				ncdiff.push_back( pair<uint32,uint32>(diff1,diff2) );
				bb = bend;
			} else if (diffb.get((bb+21)%32) != 0) {
				uint32 diff1 = 0, diff2 = 0;
				unsigned bend=bb+1;
				unsigned tempinc=0;
				if (bb+1 < 32 && diffcd.get(bb+1) != 0) {
					if (diffcd.get(bb+1) == +1)
						diff1 += 1<<bb;
					else
						diff1 -= 1<<bb;
					ncdiff.push_back( pair<uint32,uint32>(diff1,diff1) );
					bend=bb+2;
					tempinc=1;
				} else {
					diff1 = 1<<bb;
					if (diffb.get((bb+21)%32) == +1)
						diff1 = 0-(1<<bb);
					ncdiff.push_back( pair<uint32,uint32>(0-diff1,0-diff1) );
				}
				diff2 = diff1;
				while (bend < 32 && bend<=bb+parameters.pathtyperange+tempinc && diffcd.get(bend)==0)
					++bend;
				for (unsigned b2 = bb; b2 < bend; ++b2)
					if (diffb.get((b2+21)%32) == +1)
						diff2 += 1<<((b2+21)%32);
					else if (diffb.get((b2+21)%32) == -1)
						diff2 -= 1<<((b2+21)%32);
				ncdiff.push_back( pair<uint32,uint32>(diff1,diff2) );
				bb = bend;
			} else ++bb;
		}
		if (ncdiff.size()> 1 && 0!=naf(ncdiff[ncdiff.size()-1].first).get(31) && 0!=(ncdiff[0].first&1)) {
			ncdiff[0].first += ncdiff[ncdiff.size()-1].first;
			ncdiff[0].second += ncdiff[ncdiff.size()-1].second;
			ncdiff.pop_back();
		}
		uint32 temp1 = 0, temp2 = 0;
		for (unsigned i = 0; i < ncdiff.size(); ++i)
		{
			cout << "NC" << i << ": delta c = delta d = " << naf(ncdiff[i].first) << endl;
			cout << "NC" << i << ": delta b           = " << naf(ncdiff[i].second) << endl;
			temp1 += ncdiff[i].first;
			temp2 += ncdiff[i].second;
		}
		if (dihv[3] != uint32(0-temp1) || dihv[1] != uint32(0-temp2)) {
			cerr << "Internal error in determining near-collision blocks:" << endl;
			cerr << dihv[3] << "=?" << uint32(0-temp1) << endl;
			cerr << dihv[1] << "=?" << uint32(0-temp2) << endl;
		}
		dm11.resize(ncdiff.size(),0);
		vector<double> prob(ncdiff.size(),0);
		for (unsigned i = 0; i < ncdiff.size(); ++i)
		{
			sdr fnaf = naf(ncdiff[i].first);
			for (unsigned b = 0; b < 32; ++b)
				if (fnaf.get(b) == +1) {
					dm11[i] = uint32(1)<<((b-10)%32);
					break;
				} else if (fnaf.get(b) == -1) {
					dm11[i] = 0-(uint32(1)<<((b-10)%32));
					break;
				}
			if (fnaf.get(0) != 0 && fnaf.get(31) != 0) {
				if (fnaf.get(0) == +1)
					dm11[i] = uint32(1)<<21;
				else
					dm11[i] = 0-(uint32(1)<<21);
			}
			prob[i] = testnearcollprob(dm11[i], ncdiff[i].first, ncdiff[i].second);
			cout << "NC" << i << ": prob=" << log(prob[i])/log(double(2)) << endl;
		}
		j = 0;
		for (unsigned i = 1; i < prob.size(); ++i)
			if (prob[i] > prob[j]) j = i;
		unsigned skipnum = parameters.skipnc % ncdiff.size();
		for (unsigned skip = 0; skip < skipnum; ++skip) {
			swap(dm11[j],dm11[dm11.size()-1]); dm11.pop_back();
			swap(ncdiff[j],ncdiff[ncdiff.size()-1]); ncdiff.pop_back();
			swap(prob[j],prob[prob.size()-1]); prob.pop_back();
			j = 0;
			for (unsigned i = 1; i < prob.size(); ++i)
				if (prob[i] > prob[j]) j = i;
		}
		unsigned b = 0;
		int m11sign = +1;
		for (bb = 0; bb < 32; ++bb)
			if (naf(dm11[j]).get(bb) != 0) {
				b = bb;
				if (naf(dm11[j]).get(bb) == -1)
					m11sign = -1;
				break;
			}

		cout << "delta m_11 = " << naf(dm11[j]) << endl;
		m_diff[11] = dm11[j];
	}

	/*** Construct lower diff. path ***/
	differentialpath lowerpath;
	uint32 Q1[4] = { ihv1[0], ihv1[3], ihv1[2], ihv1[1] };
	uint32 Q2[4] = { ihv2[0], ihv2[3], ihv2[2], ihv2[1] };
	for (int i = 0; i < 4; ++i)
		for (unsigned k = 0; k < 32; ++k)
		{
			if ((Q1[i]>>k)&1) {
				if ((Q2[i]>>k)&1)
					lowerpath.setbitcondition(i-3,k,bc_one);
				else
					lowerpath.setbitcondition(i-3,k,bc_minus);
			} else {
				if ((Q2[i]>>k)&1)
					lowerpath.setbitcondition(i-3,k,bc_plus);
				else
					lowerpath.setbitcondition(i-3,k,bc_zero);
			}
		}
	uint32 dF = 0;
	for (unsigned k = 0; k < 32; ++k)
	{
		bf_outcome outcome = MD5_F_data.outcome(lowerpath(0,k), lowerpath(-1,k), lowerpath(-2,k));
		if (outcome.size()) {
			if (outcome[0] == bc_plus) 			dF += 1<<k;
			else if (outcome[0] == bc_minus)	dF -= 1<<k;
		}
	}
	uint32 dQtm3 = lowerpath[-3].diff();
	uint32 dT = dQtm3 + dF + m_diff[0];
	std::vector<std::pair<uint32,double> > rotateddiff;
	rotate_difference(dT, md5_rc[0], rotateddiff);
	double bestrot = 0;
	for (unsigned i = 0; i < rotateddiff.size(); ++i)
	{
		uint32 dR = rotateddiff[i].first;
		wordconditions Q1 = naf(lowerpath[0].diff() + dR);
		rotateddiff[i].second = check_rotation(dR, dT, md5_rc[0], lowerpath[0], Q1, 1<<12);
		if (rotateddiff[i].second > bestrot) {
			lowerpath[1] = Q1;
			bestrot = rotateddiff[i].second;
		}
	}

	/*** Show and store lower diff. path ***/
	show_path(lowerpath, m_diff);
	try {
		vector<differentialpath> temp;
		temp.push_back(lowerpath);
		save_gz(temp, workdir +  "/lowerpath", binary_archive);
		cout << "Saved lower diff. path to '" << workdir + "/lowerpath.bin.gz'." << endl;
	} catch (...) {
		cerr << "Error: could not write '" << workdir + "/lowerpath.bin.gz'!" << endl;
	}
	cout << endl;

	/*** Construct upper diff. path ***/
	differentialpath upperpath, bestpath;
	unsigned bestcond = 1<<31;
	for (unsigned i = 0; i < 1<<10; ++i) {
		if (sbcpc) {
			uint32 dihvmin[4] = { -dihv[0], -dihv[1], -dihv[2], -dihv[3] };
			constructupperpath_sbcpc(upperpath, m_diff, dihvmin);
		} else {
			upperpath[32].clear();
			constructupperpath(upperpath, dm11[j], ncdiff[j].first, ncdiff[j].second);
		}
		if (upperpath.nrcond() < bestcond) {
			bestcond = upperpath.nrcond();
			bestpath = upperpath;
		}
	}
	upperpath = bestpath;

	/*** Show and store upper diff. path ***/
	show_path(upperpath, m_diff);
	try {
		vector<differentialpath> temp;
		temp.push_back(upperpath);
		save_gz(temp, workdir +  "/upperpath", binary_archive);
		cout << "Saved upper diff. path to '" << workdir + "/upperpath.bin.gz'." << endl;
	} catch (...) {
		cerr << "Error: could not write '" << workdir + "/upperpath.bin.gz'!" << endl;
	}

	/*** Write md5diffpath_forward.cfg ***/
	{
		ofstream off("md5diffpathforward.cfg");
		if (!off)
			cerr << "Error: could not write md5diffpathforward.cfg!" << endl;
		else {
			/* write IHV info */
			off << "# IHV" << endl;
			off << "ihv0a = " << ihv1[0] << endl;
			off << "ihv1a = " << ihv1[1] << endl;
			off << "ihv2a = " << ihv1[2] << endl;
			off << "ihv3a = " << ihv1[3] << endl << endl;
			off << "# IHV'" << endl;
			off << "ihv0b = " << ihv2[0] << endl;
			off << "ihv1b = " << ihv2[1] << endl;
			off << "ihv2b = " << ihv2[2] << endl;
			off << "ihv3b = " << ihv2[3] << endl << endl;
			/* write delta m11 */
			off << "# message block difference" << endl;
			for (unsigned i = 0; i < 16; ++i)
				for (unsigned b = 0; b < 32; ++b)
					if (naf(m_diff[i]).get(b) != 0)
						off << "diffm" << i << " = " << int(b+1)*naf(m_diff[i]).get(b) << endl;
			/* other settings */
			off << "# parameters" << endl;

			/* copy template file if any */
			ifstream iff("md5diffpathforward.cfg.template");
			if (!iff)
				cerr << "Warning: could not read md5diffpathforward.cfg.template" << endl;
			else
				off << iff.rdbuf() << endl;
			cout << "Saved 'md5diffpathforward.cfg'." << endl;
		}
	}

	/*** Write md5diffpath_backward.cfg ***/
	{
		ofstream ofb("md5diffpathbackward.cfg");
		if (!ofb)
			cerr << "Error: could not write md5diffpathbackward.cfg!" << endl;
		else {
			/* write delta m11 */
			ofb << "# message block difference" << endl;
			for (unsigned i = 0; i < 16; ++i)
				for (unsigned b = 0; b < 32; ++b)
					if (naf(m_diff[i]).get(b) != 0)
						ofb << "diffm" << i << " = " << int(b+1)*naf(m_diff[i]).get(b) << endl;
			/* other settings */
			ofb << "# parameters" << endl;

			/* copy template file if any */
			ifstream ifb("md5diffpathbackward.cfg.template");
			if (!ifb)
				cerr << "Warning: could not read md5diffpathbackward.cfg.template" << endl;
			else
				ofb << ifb.rdbuf() << endl;
			cout << "Saved 'md5diffpathbackward.cfg'." << endl;
		}
	}

	/*** Write md5diffpath_connect.cfg ***/
	{
		ofstream ofc("md5diffpathconnect.cfg");
		if (!ofc)
			cerr << "Error: could not write md5diffpathconnect.cfg!" << endl;
		else {
			/* write delta m11 */
			ofc << "# message block difference" << endl;
			for (unsigned i = 0; i < 16; ++i)
				for (unsigned b = 0; b < 32; ++b)
					if (naf(m_diff[i]).get(b) != 0)
						ofc << "diffm" << i << " = " << int(b+1)*naf(m_diff[i]).get(b) << endl;
			/* other settings */
			ofc << "# parameters" << endl;

			/* copy template file if any */
			ifstream ifc("md5diffpathconnect.cfg.template");
			if (!ifc)
				cerr << "Warning: could not read md5diffpathconnect.cfg.template" << endl;
			else
				ofc << ifc.rdbuf() << endl;
			cout << "Saved 'md5diffpathconnect.cfg'." << endl;
		}
	}

	/*** Write md5diffpath_helper.cfg ***/
	{
		ofstream ofc("md5diffpathhelper.cfg");
		if (!ofc)
			cerr << "Error: could not write md5diffpathhelper.cfg!" << endl;
		else {
			/* write delta m11 */
			ofc << "# message block difference" << endl;
			for (unsigned i = 0; i < 16; ++i)
				for (unsigned b = 0; b < 32; ++b)
					if (naf(m_diff[i]).get(b) != 0)
						ofc << "diffm" << i << " = " << int(b+1)*naf(m_diff[i]).get(b) << endl;

			cout << "Saved 'md5diffpathhelper.cfg'." << endl;
		}
	}

	return 0;
}

void writeupperpath(std::string dir, parameters_type& parameters, unsigned b, int m11sign)
{
	try {
		boost::filesystem::create_directory(dir);
	} catch( std::exception& e) {
		std::cerr << e.what() << endl;
		std::cerr << "failed to create directory: " << dir << endl;
		throw;
	} catch(...) {
		std::cerr << "failed to create directory: " << dir << endl;
		throw;
	}
	bitcondition plus = bc_plus, minus = bc_minus;
	if (m11sign == -1)
		swap(plus, minus);
	uint32 m_diff[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
	m_diff[11] = m11sign * (1<<b);

	/*** Construct upper diff. path ***/
	differentialpath upperpath;
	upperpath[32].clear();
	upperpath.setbitcondition(60, (b+10)&31, bc_zero);
	upperpath.setbitcondition(61, (b+10)&31, bc_one);
	upperpath.setbitcondition(62, (b+10)&31, plus);
	upperpath.setbitcondition(63, (b+10)&31, plus);
	upperpath.setbitcondition(64, (b+10)&31, plus);

	/*** Show and store upper diff. path ***/
	try {
		vector<differentialpath> temp;
		temp.push_back(upperpath);
		save_gz(temp, dir +  "/upperpath", binary_archive);
		//cout << "Saved upper diff. path to '" << dir + "/upperpath.bin'." << endl;
	} catch (...) {
		cerr << "Error: could not write '" << dir << "/upperpath.bin'!" << endl;
	}

	/*** Write md5diffpath_backward.cfg ***/
	{
		ofstream ofb((dir+"/md5diffpathbackward.cfg").c_str());
		if (!ofb)
			cerr << "Error: could not write " << dir << "md5diffpathbackward.cfg!" << endl;
		else {
			/* write delta m11 */
			ofb << "# message block difference" << endl;
			ofb << "diffm11 = " << m11sign*int(b+1) << endl << endl;
			/* other settings */
			ofb << "# parameters" << endl;

			/* copy template file if any */
			ifstream ifb("md5diffpathbackward.cfg.template");
			if (!ifb)
				cerr << "Error: could not read md5diffpathbackward.cfg.template" << endl;
			else {
				ofb << ifb.rdbuf() << endl;
			}
			cout << "Saved '" << dir << "md5diffpathbackward.cfg'." << endl;
		}
	}
}

int upperpaths(parameters_type& parameters)
{
	for (unsigned b = 0; b < 32; ++b)
	{
		string bitstr = boost::lexical_cast<string>(b);
		writeupperpath(workdir + "plus2power" + bitstr, parameters, b, 1);
		writeupperpath(workdir + "minus2power" + bitstr, parameters, b, -1);
	}
	return 0;
}


// Boost.Geometry

// Copyright (c) 2020-2021, Oracle and/or its affiliates.

// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Licensed under the Boost Software License version 1.0.
// http://www.boost.org/users/license.html

#ifndef BOOST_GEOMETRY_STRATEGIES_AREA_GEOGRAPHIC_HPP
#define BOOST_GEOMETRY_STRATEGIES_AREA_GEOGRAPHIC_HPP

#include <boost/geometry/strategy/geographic/area.hpp>
#include <boost/geometry/strategy/geographic/area_box.hpp>

#include <boost/geometry/strategies/area/services.hpp>
#include <boost/geometry/strategies/detail.hpp>

namespace boost { namespace geometry
{

namespace strategies { namespace area
{

template
<
    typename FormulaPolicy = strategy::andoyer,
    typename Spheroid = srs::spheroid<double>,
    typename CalculationType = void
>
class geographic
    : public strategies::detail::geographic_base<Spheroid>
{
    using base_t = strategies::detail::geographic_base<Spheroid>;

public:
    geographic() = default;

    explicit geographic(Spheroid const& spheroid)
        : base_t(spheroid)
    {}

    template <typename Geometry>
    auto area(Geometry const&,
              std::enable_if_t<! util::is_box<Geometry>::value> * = nullptr) const
    {
        return strategy::area::geographic
            <
                FormulaPolicy,
                strategy::default_order<FormulaPolicy>::value,
                Spheroid, CalculationType
            >(base_t::m_spheroid);
    }

    template <typename Geometry>
    auto area(Geometry const&,
              std::enable_if_t<util::is_box<Geometry>::value> * = nullptr) const
    {
        return strategy::area::geographic_box
            <
                Spheroid, CalculationType
            >(base_t::m_spheroid);
    }
};

namespace services
{

template <typename Geometry>
struct default_strategy<Geometry, geographic_tag>
{
    using type = strategies::area::geographic<>;
};

template <typename FP, std::size_t SO, typename S, typename CT>
struct strategy_converter<strategy::area::geographic<FP, SO, S, CT> >
{
    struct altered_strategy
        : strategies::area::geographic<FP, S, CT>
    {
        explicit altered_strategy(S const& spheroid)
            : strategies::area::geographic<FP, S, CT>(spheroid)
        {}

        using strategies::area::geographic<FP, S, CT>::area;

        template <typename Geometry>
        auto area(Geometry const&,
                  std::enable_if_t<! util::is_box<Geometry>::value> * = nullptr) const
        {
            return strategy::area::geographic<FP, SO, S, CT>(this->m_spheroid);
        }
    };

    static auto get(strategy::area::geographic<FP, SO, S, CT> const& strategy)
    {
        return altered_strategy(strategy.model());
    }
};

} // namespace services

}} // namespace strategies::area

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGIES_AREA_GEOGRAPHIC_HPP


// NOT USED -- INCOMPLETE -- DOES NOT COMPILE
// here for future reference

#include <boost/python.hpp>

#include <ccp4io_adaptbx/dev/fftlib.hpp>

namespace ccp4io_dev { namespace ext {

  void
  fftlib_cmplft(
    af::ref<float> const & xy,
    int const n,
    af::ref<int> const & d)
  {
    ASSERTBX(n > 0);
    ASSERTBX(xy.size() >= 2 * n);
    ASSERTBX(d.size() == 5);
    cmplft(xy[0], xy[1], n, d[0]);
  }

  void
  fftlib_realft(
    af::ref<float> const & xy,
    int const n,
    af::ref<int> const & d)
  {
    ASSERTBX(n > 0);
    ASSERTBX(xy.size() >= 2 * n + 2);
    ASSERTBX(d.size() == 5);
    realft(xy[0], xy[1], n, d[0]);
  }

  void
  fftlib_hermft(
    af::ref<float> const & xy,
    int const n,
    af::ref<int> const & d)
  {
    ASSERTBX(n > 0);
    ASSERTBX(xy.size() >= 2 * n);
    ASSERTBX(d.size() == 5);
    hermft(xy[0], xy[1], n, d[0]);
  }

  void
  fftlib_real_to_complex_3d_real_imag_w(
    af::ref<float> const & x,
    int const nu,
    int const nv,
    int const nw,
    bool complete)
  {
    ASSERTBX(nu > 0);
    ASSERTBX(nv > 0);
    ASSERTBX(nw > 0);
    ASSERTBX(nw % 2 == 0);
    ASSERTBX(x.size() >= nu*nv*(nw+2)); // memory required also if not complete
    int nuv = nu*nv;
    int nuvw2 = (complete ? nuv*(nw+2) : nuv*nw);
    // transform along w
    int5d d;
    d(1)=nuvw2;
    d(2)=2*nuv;
    d(3)=nuvw2;
    d(4)=nuv;
    d(5)=1;
    realft(x[0],x[nuv],nw/2,d);
      // realft always does a complete transform, but
      // if !complete, the layer at nw/2 is ignored below
    // transform along v
    d(1)=nuvw2;
    d(2)=nu;
    d(3)=2*nuv;
    d(4)=nu;
    d(5)=1;
    cmplft(x[0],x[nuv],nv,d);
    // transform along u
    d(1)=nuvw2;
    d(2)=1;
    d(3)=2*nuv;
    d(4)=nuv;
    d(5)=nu;
    cmplft(x[0],x[nuv],nu,d);
  }

  void
  fftlib_complex_to_real_3d_real_imag_w(
    af::ref<float> const & x,
    int const nu,
    int const nv,
    int const nw,
    bool complete)
  {
    ASSERTBX(nu > 0);
    ASSERTBX(nv > 0);
    ASSERTBX(nw > 0);
    ASSERTBX(nw % 2 == 0);
    ASSERTBX(x.size() >= nu*nv*(nw+2)); // memory required also if not complete
    int nuv=nu*nv;
    int nuvw2 = (complete ? nuv*(nw+2) : nuv*nw);
    // transform along u
    int5d d;
    d(1)=nuvw2;
    d(2)=1;
    d(3)=2*nuv;
    d(4)=nuv;
    d(5)=nu;
    cmplft(x[0],x[nuv],nu,d);
    // transform along v
    d(1)=nuvw2;
    d(2)=nu;
    d(3)=2*nuv;
    d(4)=nu;
    d(5)=1;
    cmplft(x[0],x[nuv],nv,d);
    if (complete) {
      // copy real part of last value along w to imag part of first
      for(int v=0;v<nv;v++) {
        for(int u=0;u<nu;u++) {
          int uv1  = ((1 *nv)+v)*nu+u;
          int uvnw = ((nw*nv)+v)*nu+u;
          x[uv1] = x[uvnw];
        }
      }
    }
    // transform along w
    d(1)=nuvw2;
    d(2)=2*nuv;
    d(3)=nuvw2;
    d(4)=nuv;
    d(5)=1;
    hermft(x[0],x[nuv],nw/2,d);
  }

  void
  hermitian_conjugate_3d_real_imag_w(
    af::ref<float> const & x,
    int const nu,
    int const nv,
    int const nw)
  {
    ASSERTBX(nu > 0);
    ASSERTBX(nv > 0);
    ASSERTBX(nw > 0);
    ASSERTBX(nw % 2 == 0);
    ASSERTBX(x.size() >= nu*nv*(nw+2));
    for(int w=0;w<nw+2;w+=2) {
      for(int v=0;v<nv;v++) {
        for(int u=0;u<nu;u++) {
          int uvw_imag = (((w+1)*nv)+v)*nu+u;
          x[uvw_imag] *= -1.0f;
        }
      }
    }
  }

  void
  wrap()
  {
    using namespace boost::python;
    def("fftlib_cmplft", fftlib_cmplft, (arg("xy"), arg("n"), arg("d")));
    def("fftlib_realft", fftlib_realft, (arg("xy"), arg("n"), arg("d")));
    def("fftlib_hermft", fftlib_hermft, (arg("xy"), arg("n"), arg("d")));
    def("fftlib_real_to_complex_3d_real_imag_w",
         fftlib_real_to_complex_3d_real_imag_w, (
      arg("x"), arg("nu"), arg("nv"), arg("nw"), arg("complete")));
    def("fftlib_complex_to_real_3d_real_imag_w",
         fftlib_complex_to_real_3d_real_imag_w, (
      arg("x"), arg("nu"), arg("nv"), arg("nw"), arg("complete")));
    def("hermitian_conjugate_3d_real_imag_w",
         hermitian_conjugate_3d_real_imag_w, (
      arg("x"), arg("nu"), arg("nv"), arg("nw")));
  }

}} // namespace ccp4io_dev::ext

BOOST_PYTHON_MODULE(ccp4io_dev_ext)
{
  ccp4io_dev::ext::wrap();
}


//==================================================================================================
/*
  Copyright 2017 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
//! [lookup]
#include <boost/simd/swar.hpp>
#include <boost/simd/pack.hpp>
#include <iostream>

namespace bs =  boost::simd;
using pack_ft = bs::pack <float, 4>;
using pack_it = bs::pack <std::int32_t, 4>;

int main()
{
  pack_ft pf = {1.0f, 2.0f, -1.0f, 0.5f};
  pack_it qi = {1, 2, -1, 3};

  std::cout
    << "---- simd" << '\n'
    << "<- pf =                " << pf << '\n'
    << "<- qi =                " << qi << '\n'
    << "-> bs::lookup(pf, qi) = " << bs::lookup(pf, qi) << '\n';

  return 0;
}
//! [lookup]


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

/**
 * @file MatrixTypes.hpp
 * @author Thomas Wiemann (twiemann@uos.de)
 * @date 2019-08-14
 *
 * @copyright Copyright (c) 2019
 *
 */

#ifndef LVR2_TYPES_MATRIXTYPES_HPP
#define LVR2_TYPES_MATRIXTYPES_HPP

#include <Eigen/Dense>

namespace lvr2
{
/// General alias for row major 4x4 matrices
template<typename T>
using Matrix4RM = Eigen::Matrix<T, 4, 4, Eigen::RowMajor>;

/// 4x4 row major matrix with float scalars
using Matrix4fRM = Matrix4RM<float>;

/// 4x4 row major matrix with double scalars
using Matrix4dRM = Matrix4RM<double>;

/// General alias for row major 3x3 matrices
template<typename T>
using Matrix3RM = Eigen::Matrix<T, 3, 3, Eigen::RowMajor>;

/// 3x3 row major matrix with float scalars
using Matrix3fRM = Matrix3RM<float>;
/// 3x3 row major matrix with double scalars
using Matrix3dRM = Matrix3RM<double>;

/// General 4x4 transformation matrix (4x4)
template<typename T>
using Transform = Eigen::Matrix<T, 4, 4>;

/// 4x4 single precision transformation matrix
using Transformf = Transform<float>;

/// 4x4 double precision transformation matrix
using Transformd = Transform<double>;

/// General 3x3 rotation matrix
template<typename T>
using Rotation = Eigen::Matrix<T, 3, 3>;

/// Single precision 3x3 rotation matrix
using Rotationf = Rotation<float>;

/// Double precision 3x3 rotation matrix
using Rotationd = Rotation<double>;

/// 4x4 extrinsic calibration
template<typename T>
using Extrinsics = Eigen::Matrix<T, 4, 4>;

/// 4x4 extrinsic calibration (single precision)
using Extrinsicsf = Extrinsics<float>;

/// 4x4 extrinsic calibration (double precision)
using Extrinsicsd = Extrinsics<double>;

/// 4x4 extrinsic calibration
template<typename T>
using Intrinsics = Eigen::Matrix<T, 3, 3>;

/// 4x4 intrinsic calibration (single precision)
using Intrinsicsf = Intrinsics<float>;

/// 4x4 extrinsic calibration (double precision)
using Intrinsicsd = Intrinsics<double>;

/// Distortion Parameters
template<typename T>
using Distortion = Eigen::Matrix<T, 6, 1>;

/// Distortion Parameters (double precision)
using Distortiond = Distortion<double>;

/// Distortion Parameters (single precision)
using Distortionf = Distortion<float>;

/// Eigen 3D vector
template<typename T>
using Vector3 = Eigen::Matrix<T, 3, 1>;

/// Eigen 3D vector, single precision
using Vector3f = Eigen::Vector3f;

/// Eigen 3D vector, double precision
using Vector3d = Eigen::Vector3d;

/// Eigen 3D vector, integer
using Vector3i = Eigen::Vector3i;

/// Eigen 4D vector
template<typename T>
using Vector4 = Eigen::Matrix<T, 4, 1>;

/// Eigen 4D vector, single precision
using Vector4f = Eigen::Vector4f;

/// Eigen 4D vector, double precision
using Vector4d = Eigen::Vector4d;

/// Eigen 2D vector
template<typename T>
using Vector2 = Eigen::Matrix<T, 2, 1>;

/// Eigen 2D vector, single precision
using Vector2f = Eigen::Vector2f;

/// Eigen 2D vector, double precision
using Vector2d = Eigen::Vector2d;

/// Eigen 4x4 matrix, single precision
using Matrix4f = Eigen::Matrix4f;

/// Eigen 4x4 matrix, double precision
using Matrix4d = Eigen::Matrix4d;

/// 6D Matrix, single precision
using Matrix6f = Eigen::Matrix<float, 6, 6>;

/// 6D vector, single precision
using Vector6f = Eigen::Matrix<float, 6, 1>;

/// 6D matrix double precision
using Matrix6d = Eigen::Matrix<double, 6, 6>;

/// 6D vector double precision
using Vector6d = Eigen::Matrix<double, 6, 1>;

template<typename T>
Vector3<T> multiply(const Transform<T>& transform, const Vector3<T>& p)
{
    Vector4<T> ret(p.coeff(0), p.coeff(1), p.coeff(2), 1.0);
    ret = transform * ret;
    return Vector3<T>(ret.coeff(0), ret.coeff(1), ret.coeff(2));
}

} // namespace lvr2

// additional operators

template<typename T>
lvr2::Vector3<T> operator*(const lvr2::Transform<T>& transform, const lvr2::Vector3<T>& p)
{
    return lvr2::multiply(transform, p);
}

#endif // LVR2_TYPES_MATRIXTYPES_HPP


/*
 * perftester.cc
 *
 *  Created on: Aug 29, 2014
 *      Author: Thomas Latzko
 */
#include <assert.h>
#include <vector>
#include <deque>
#include <iostream>
#include <fstream>
#include <atomic>
#include <thread>
#include <memory>
#include <Eigen/Dense>
#include <Eigen/Core>
#include "gloc/cuboid.hpp"
#include "gloc/channel.hpp"

using namespace gloc;

// std::string localData = "/media/latzko/store/20120828_152843.cub";
//
std::string localData = "../data/20120913_135249.cub";
// typedef std::shared_ptr<CuboidFrameHeader>;
typedef Eigen::ArrayXXd frame_t;
typedef std::shared_ptr<frame_t> frame_ptr;

class Runnable {
public:
  Runnable() : stop_(), thread_() {}
  virtual ~Runnable() {
    try {
      stop();
    }
    catch (...) { /*??*/
    }
  }

  Runnable(Runnable const &) = delete;

  Runnable &operator=(Runnable const &) = delete;

  void stop() {

    stop_ = true;
    thread_.join();
  }

  void start() { thread_ = std::thread(&Runnable::run, this); }
  void join() { thread_.join(); }

protected:
  virtual void run() = 0;
  std::atomic<bool> stop_;

private:
  std::thread thread_;
};

/**
 * The ReadNode should read frames transform into
 * a an image usually an opencv image and send it
 * with a channel
 **/

template <class frametype, class frame_to_frametype, int buffer_size = 5>
class ReadNode : public Runnable {
private:
  std::string file_name_;
  gloc::ochannel<frametype, buffer_size> frame_out_;
  gloc::ochannel<gloc::CuboidFrameHeader> frame_header_out_;

public:
  ReadNode() = delete;
  ReadNode(std::string file_name, gloc::ochannel<frametype, buffer_size> out,
           ochannel<CuboidFrameHeader> header_out)
      : file_name_(file_name), frame_out_(out), frame_header_out_(header_out) {}
  ~ReadNode() {}

  void run() {

    CuboidFile cub(localData);

    frame_to_frametype F;
    const size_t frame_count = cub.getFrameCount();
    for (size_t i = 0; i < frame_count; ++i) {
      auto frame = cub.getFrame(i);
      auto header = frame.getFrameHeader();
      auto image = F(frame);
      std::cout << "push frame " << i << std::endl;
      frame_out_.send(std::move(image));
      frame_header_out_.send(header);
    }
    std::cout << "finish read node\n";
    frame_out_.send(nullptr);
    CuboidFrameHeader he;
    frame_header_out_.send(he);
  }
};

template <class frametype> class FrameRecv : public Runnable {
protected:
  channel<frametype, 5> frames_;

public:
  FrameRecv(channel<frametype, 5> chan) : frames_(chan) {}

  void finalize() { std::cout << "final steps\n"; }

  void run() {
    int i = 0;
    while (!stop_) {
      auto frame = frames_.recv();
      if (frame == nullptr) {
        break;
      }

      ++i;
    }
    std::cout << "frame recv end: " << i << "\n ";

    this->finalize();
  }
};

template <class frametype> class FrameMean : public Runnable {
protected:
  ichannel<frametype, 5> frames_;
  ochannel<frametype, 5> frames_out_;

public:
  FrameMean(ichannel<frametype, 5> chan, ochannel<frametype, 5> chanout)
      : frames_(chan), frames_out_(chanout) {}
  void run() {

    auto frame = frames_.recv();
    assert(frame != nullptr);

    size_t i = 1;
    frame_t M_(*frame);
    while (true) {
      auto frame = frames_.recv();
      if (frame == nullptr) {
        frames_out_.send(std::move(frame));
        break;
      }

      auto M = *frame;
      M_ = M_ + M;
      frames_out_.send(std::move(frame));
      ++i;
      std::cout << "frame recv " << i << std::endl;
    }
    std::cout << "mean end\n";
    // std::cout<< M_ / i<< std::endl;
  }
};

template <class frametype, size_t buffer_size>
class BufferedMean : public Runnable {
protected:
  ichannel<frametype, 5> frames_;
  ochannel<frametype, 5> frames_out_;
  std::deque<frametype> buffer_;

public:
  BufferedMean(ichannel<frametype, 5> chan, ochannel<frametype, 5> chan_out)
      : frames_(chan), frames_out_(chan_out) {}

  frame_ptr compute_the_mean() {
    frame_ptr m0(nullptr);

    for (size_t i = 0; i < buffer_size; ++i) {
      if (i == 0) {
        m0.reset(new frame_t((*buffer_[i])));
      } else {
        if (!buffer_[i]) {
          std::cout << "chrash";
          break;
        }

        (*m0) += (*buffer_[i]);
      }
    }
    return m0;
  }

  bool fill_the_buffer() {
    for (size_t i = 0; i < buffer_size; ++i) {
      std::cout << "recv frame\n";
      auto frame = frames_.recv();
      if (frame == nullptr) {
        frames_out_.send(nullptr);
        return false;
      }
      buffer_.push_back(std::move(frame));
    }
    buffer_.shrink_to_fit();
    return true;
  }

  void run() {
    bool filled = fill_the_buffer();

    if (filled) {
      while (true) {
        auto frame = frames_.recv();
        if (frame == nullptr) {
          frames_out_.send(std::move(frame));
          break;
        }
        auto fout = compute_the_mean();
        this->buffer_.pop_front();
        this->buffer_.push_back(std::move(frame));

        frames_out_.send(std::move(fout));
      }
    }
    std::cout << "end buffer\n";
  }
};

class HeaderRecv : public Runnable {
private:
  ichannel<CuboidFrameHeader> c_;

public:
  HeaderRecv(ichannel<CuboidFrameHeader> chan) : c_(chan) {}
  void run() {
    int i = 0;
    while (true) {

      std::cout << "recv header" << std::endl;
      auto header = c_.recv();
      ++i;
      if (header.status == 0) {
        break;
      }
    }
    std::cout << "reach end header\n" << std::endl;
  }
};
struct convertArrayToMat {
  frame_ptr operator()(const CuboidFrame &frame) {
    uint16_t *values = frame.pixel_values;
    auto width = frame.width_;
    auto height = frame.height_;
    frame_ptr res(new frame_t(width, height));
    for (size_t j = 0; j < width; ++j) {
      for (size_t i = 0; i < height; ++i) {
        const size_t pos = j * height + i;
        (*res)(j, i) = static_cast<double>(values[pos]);
      }
    }
    return res;
  }
};

int main() {
  Eigen::initParallel();
  CuboidFile cub(localData);
  cub.printHeader();

  // required channels
  channel<frame_ptr, 5> frame_chan;
  channel<frame_ptr, 5> buffer_chan;
  channel<frame_ptr, 5> mean_chan;
  channel<CuboidFrameHeader> header_chan;
  // nodes
  ReadNode<frame_ptr, convertArrayToMat, 5> r(localData, frame_chan,
                                              header_chan);
  BufferedMean<frame_ptr, 40> bf(frame_chan, buffer_chan);
  FrameRecv<frame_ptr> fr(mean_chan);
  HeaderRecv hr(header_chan);
  FrameMean<frame_ptr> mf(buffer_chan, mean_chan);
  fr.start();
  bf.start();
  mf.start();
  hr.start();
  r.start();
  r.join();
  std::cout << "end reading stop all other " << std::endl;
  hr.join();
  mf.join();
  bf.join();
  fr.join();
  std::cout << "measure time";

  return 0;
}