/**
 *  Project: The Stock Libraries
 *
 *  File: globals.hpp
 *  Created: Jun 05, 2012
 *
 *  Author: Abhinav Sarje <abhinav.sarje@gmail.com>
 *
 *  Copyright (c) 2012-2017 Abhinav Sarje
 *  Distributed under the Boost Software License.
 *  See accompanying LICENSE file.
 */

#ifndef __GLOBALS_HPP__
#define __GLOBALS_HPP__

#include <boost/array.hpp>
#include <vector>
#include <cmath>

#include "typedefs.hpp"

namespace stock {

	typedef struct vector2_t {
		boost::array <real_t, 2> vec_;

		/* constructors */

		vector2_t() {
			vec_[0] = 0; vec_[1] = 0;
		} // vector2_t()

		vector2_t(real_t a, real_t b) {
			vec_[0] = a; vec_[1] = b;
		} // vector2_t()

		vector2_t(vector2_t& a) {
			vec_[0] = a[0]; vec_[1] = a[1];
		} // vector2_t()

		vector2_t(const vector2_t& a) {
			vec_ = a.vec_;
		} // vector2_t()

		/* operators */

		vector2_t& operator=(const vector2_t& a) {
			vec_ = a.vec_;
			return *this;
		} // operator=

		vector2_t& operator=(vector2_t& a) {
			vec_ = a.vec_;
			return *this;
		} // operator=

		real_t& operator[](int i) {
			return vec_[i];
		} // operator[]
	} vector2_t;

	typedef struct vector3_t {
		boost::array <real_t, 3> vec_;

		/* constructors */

		vector3_t() {
			vec_[0] = 0; vec_[1] = 0; vec_[2] = 0;
		} // vector3_t()

		vector3_t(real_t a, real_t b, real_t c) {
			vec_[0] = a; vec_[1] = b; vec_[2] = c;
		} // vector3_t()

		vector3_t(vector3_t& a) {
			vec_[0] = a[0]; vec_[1] = a[1]; vec_[2] = a[2];
		} // vector3_t()

		vector3_t(const vector3_t& a) {
			vec_ = a.vec_;
		} // vector3_t()

		/* operators */

		vector3_t& operator=(const vector3_t& a) {
			vec_ = a.vec_;
			return *this;
		} // operator=

		vector3_t& operator=(vector3_t& a) {
			vec_ = a.vec_;
			return *this;
		} // operator=

		real_t& operator[](int i) {
			return vec_[i];
		} // operator[]

		vector3_t operator+(int toadd) {
			return vector3_t(vec_[0] + toadd, vec_[1] + toadd, vec_[2] + toadd);
		} // operator+()

		vector3_t operator+(vector3_t toadd) {
			return vector3_t(vec_[0] + toadd[0], vec_[1] + toadd[1], vec_[2] + toadd[2]);
		} // operator+()

		vector3_t operator-(int tosub) {
			return vector3_t(vec_[0] - tosub, vec_[1] - tosub, vec_[2] - tosub);
		} // operator-()

		vector3_t operator-(vector3_t tosub) {
			return vector3_t(vec_[0] - tosub[0], vec_[1] - tosub[1], vec_[2] - tosub[2]);
		} // operator-()

		vector3_t operator/(vector3_t todiv) {
			return vector3_t(vec_[0] / todiv[0], vec_[1] / todiv[1], vec_[2] / todiv[2]);
		} // operator/()
	} vector3_t;

	typedef struct matrix3x3_t {
		typedef boost::array<real_t, 3> mat3_t;
		mat3_t mat_[3];

		/* constructors */

		matrix3x3_t() {
			for(int i = 0; i < 3; ++ i)
				for(int j = 0; j < 3; ++ j)
					mat_[i][j] = 0.0;
		} // matrix3x3_t()

		matrix3x3_t(const matrix3x3_t& a) {
			mat_[0] = a.mat_[0];
			mat_[1] = a.mat_[1];
			mat_[2] = a.mat_[2];
		} // matrix3x3_t

		/* operators */

		mat3_t& operator[](unsigned int index) {
			return mat_[index];
		} // operator[]()

		mat3_t& operator[](int index) {
			return mat_[index];
		} // operator[]()

		matrix3x3_t& operator=(matrix3x3_t& a) {
			for(int i = 0; i < 3; ++ i)
				for(int j = 0; j < 3; ++ j)
					mat_[i][j] = a[i][j];
			return *this;
		} // operstor=()

		matrix3x3_t operator+(int toadd) {
			matrix3x3_t sum;
			for(int i = 0; i < 3; ++ i)
				for(int j = 0; j < 3; ++ j)
					sum.mat_[i][j] = mat_[i][j] + toadd;
			return sum;
		} // operator+()

		matrix3x3_t operator+(matrix3x3_t& toadd) {
			matrix3x3_t sum;
			for(int i = 0; i < 3; ++ i)
				for(int j = 0; j < 3; ++ j)
					sum.mat_[i][j] = mat_[i][j] + toadd[i][j];
			return sum;
		} // operator+()

		matrix3x3_t operator*(int tomul) {
			matrix3x3_t prod;
			for(int i = 0; i < 3; ++ i)
				for(int j = 0; j < 3; ++ j)
					prod.mat_[i][j] = mat_[i][j] * tomul;
			return prod;
		} // operator*()

		matrix3x3_t operator*(matrix3x3_t& tomul) {
			matrix3x3_t prod;
			for(int i = 0; i < 3; ++ i)
				for(int j = 0; j < 3; ++ j)
					prod.mat_[i][j] = mat_[i][j] * tomul[i][j];
			return prod;
		} // operator*()

	} matrix3x3_t;

} // namespace stock

#endif /* __GLOBALS_HPP__ */


/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2009 Scientific Computing and Imaging Institute,
   University of Utah.

   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 <Core/Algorithms/Math/GetMatrixSliceAlgo.h>
#include <Core/Datatypes/DenseMatrix.h>
#include <Core/Datatypes/DenseColumnMatrix.h>
#include <Core/Datatypes/MatrixTypeConversions.h>
#include <Core/Datatypes/SparseRowMatrix.h>
#include <Core/Algorithms/Base/AlgorithmPreconditions.h>
#include <Core/Algorithms/Base/AlgorithmVariableNames.h>
#include <boost/lexical_cast.hpp>

using namespace SCIRun;
using namespace SCIRun::Core::Datatypes;
using namespace SCIRun::Core::Algorithms;
using namespace SCIRun::Core::Algorithms::Math;

ALGORITHM_PARAMETER_DEF(Math, IsSliceColumn);
ALGORITHM_PARAMETER_DEF(Math, SliceIndex);
ALGORITHM_PARAMETER_DEF(Math, MaxIndex);
ALGORITHM_PARAMETER_DEF(Math, PlayMode);

GetMatrixSliceAlgo::GetMatrixSliceAlgo()
{
  addParameter(Parameters::IsSliceColumn, true);
  addParameter(Parameters::SliceIndex, 0);
  addParameter(Parameters::PlayMode, false);
}

AlgorithmOutput GetMatrixSliceAlgo::run_generic(const AlgorithmInput& input) const
{
  auto inputMatrix = input.get<Matrix>(Variables::InputMatrix);
  auto outputMatrix = runImpl(inputMatrix, get(Parameters::SliceIndex).toInt(), get(Parameters::IsSliceColumn).toBool());

  AlgorithmOutput output;
  output[Variables::OutputMatrix] = outputMatrix.get<0>();
  output.setAdditionalAlgoOutput(boost::make_shared<Variable>(Name("maxIndex"), outputMatrix.get<1>()));

  return output;
}

boost::tuple<MatrixHandle, int> GetMatrixSliceAlgo::runImpl(MatrixHandle matrix, int index, bool getColumn) const
{
  ENSURE_ALGORITHM_INPUT_NOT_NULL(matrix, "Input matrix");
  if (getColumn)
  {
    checkIndex(index, matrix->ncols());
    auto max = matrix->ncols() - 1;

    // dense case only now
    auto dense = matrix_cast::as_dense(matrix);
    if (dense)
      return boost::make_tuple(boost::make_shared<DenseMatrix>(dense->col(index)), max);
    else
    {
      auto sparse = matrix_cast::as_sparse(matrix);
      if (sparse)
        return boost::make_tuple(boost::make_shared<SparseRowMatrix>(sparse->col(index)), max);
      return boost::make_tuple(nullptr, 0);
    }
  }
  else
  {
    checkIndex(index, matrix->nrows());
    auto max = matrix->nrows() - 1;

    // dense case only now
    auto dense = matrix_cast::as_dense(matrix);
    if (dense)
      return boost::make_tuple(boost::make_shared<DenseMatrix>(dense->row(index)), max);
    else
    {
      auto sparse = matrix_cast::as_sparse(matrix);
      if (sparse)
        return boost::make_tuple(boost::make_shared<SparseRowMatrix>(sparse->row(index)), max);
      return boost::make_tuple(nullptr, 0);
    }
  }
}

void GetMatrixSliceAlgo::checkIndex(int index, int max) const
{
  if (index < 0 || index >= max)
    THROW_ALGORITHM_INPUT_ERROR("Slice index out of range: " + boost::lexical_cast<std::string>(index));
}


#include <Eigen/Core>

#include <numpy_eigen/boost_python_headers.hpp>
Eigen::Matrix<boost::uint8_t, 6, 5> test_uchar_6_5(const Eigen::Matrix<boost::uint8_t, 6, 5> & M)
{
	return M;
}
void export_uchar_6_5()
{
	boost::python::def("test_uchar_6_5",test_uchar_6_5);
}


/**
 * GeoDa TM, Copyright (C) 2011-2015 by Luc Anselin - all rights reserved
 *
 * This file is part of GeoDa.
 *
 * GeoDa 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.
 *
 * GeoDa 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 <boost/functional/hash.hpp>
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp>
#include "../logger.h"
#include "ShapeUtils.h"

ShapeUtils::RingNode::RingNode(ring_2d* s_ring_p)
	: area(0), clockwise(true), ring_p(s_ring_p) {
	if (ring_p ) {
		area = boost::geometry::area(*s_ring_p);
		if (area < 0) { area = -area; clockwise = false; }
	}
}

void ShapeUtils::polygonContentsToRingList(Shapefile::PolygonContents& pc,
										   std::list<ring_2d>& l)
{
	int pnts_cnt = 0;
	for (int i = 0; i < pc.num_parts; i++) {
		ring_2d r;
		int pmax = i == pc.num_parts-1 ? pc.num_points : pc.parts[i+1];
		for (int j = pc.parts[i]; j < pmax; j++) {
			append(r, make<point_2d>(pc.points.at(j).x,
									 pc.points.at(j).y));
		}
		l.push_back(r);
	}
}

/** Note, this method allocates memory for every RingNode.  Use
 ShapeUtils::deleteRingTree to free this memory. */
ShapeUtils::RingNode* ShapeUtils::ringListToRingTree(std::list<ring_2d>& l)
{
	RingNode* tree = new RingNode(0); // the ring tree empty root
	BOOST_FOREACH( ring_2d& i, l ) {
		insertRingNode(new RingNode(&i), tree);
	}
	return tree;
}

void ShapeUtils::polygonContentsToPolyList(Shapefile::PolygonContents& pc,
										   std::list<polygon_2d>& l)
{
	using namespace std;
	using namespace Shapefile;

	list<ring_2d> ring_list;
	polygonContentsToRingList(pc, ring_list);

	if (pc.num_parts == 1) {
		ring_2d r;
		BOOST_FOREACH( Shapefile::Point& pt, pc.points ) {
			append(r, make<point_2d>(pt.x, pt.y));
		}
		ring_list.push_back(r);
		polygon_2d p;
		assign(p, r);
		l.push_back(p);
	} else {
		RingNode* ring_tree = 0;
		ring_tree = ringListToRingTree(ring_list);
		correctRings(ring_tree);
		ringTreeToPolygons(ring_tree, l);
		deleteRingTree(ring_tree);
	}
}

void ShapeUtils::printPolyList(const std::list<polygon_2d>& l, std::ostream& s,
							   int indent)
{
	std::string pre = Shapefile::getIndentString(indent);
	std::string pre1 = Shapefile::getIndentString(indent+1);
	int i =0;
	BOOST_FOREACH( const polygon_2d& p, l ) {
		s << pre << "polygon_2d " << i << ": " << std::endl;
		s << pre1 << dsv(p) << std::endl;
		i++;
	}
}

point_2d ShapeUtils::centroid(const std::list<polygon_2d>& poly_list)
{
	double total_area = 0;
	double total_x = 0;
	double total_y = 0;
	point_2d cent;
	double A = 0;
	BOOST_FOREACH( const polygon_2d& p, poly_list ) {
		boost::geometry::centroid<polygon_2d, point_2d>(p, cent);
		A = area(p);
		total_x += A*cent.x();
		total_y += A*cent.y();
		total_area += A;
	}
	return make<point_2d>(total_x/total_area, total_y/total_area);
}

point_2d ShapeUtils::meanCenter(const std::list<polygon_2d>& poly_list)
{
	double total_x = 0;
	double total_y = 0;
	int total_pts = 0;
	BOOST_FOREACH( const polygon_2d& p, poly_list ) {
		BOOST_FOREACH( const point_2d& pt, p.outer() ) {
			total_x += pt.x();
			total_y += pt.y();
			total_pts++;
		}
		BOOST_FOREACH( const ring_2d& r, p.inners() ) {
			BOOST_FOREACH( const point_2d& pt, r ) {
				total_x += pt.x();
				total_y += pt.y();
				total_pts++;
			}
		}
	}
	return make<point_2d>(total_x/total_pts, total_y/total_pts);
}

void ShapeUtils::appendChildrenToPolyList(RingNode* tree,
										  std::list<polygon_2d>& l)
{
	BOOST_FOREACH( RingNode* n, tree->children ) {
		polygon_2d p;
		assign(p, *n->ring_p);
		p.inners().resize(n->children.size());
		int i=0;
		BOOST_FOREACH( RingNode* c, n->children ) {
			// make c an inner ring of p
			assign( p.inners().at(i++), *c->ring_p);
		}
		correct(p);
		l.push_back(p);
		BOOST_FOREACH( RingNode* c, n->children ) {
			appendChildrenToPolyList( c, l );
		}
	}
}

void ShapeUtils::ringTreeToPolygons(RingNode* tree, std::list<polygon_2d>& l)
{
	appendChildrenToPolyList(tree, l);
}

/** We actually don't need the world to be correct since we never look at the
 tree->ring_p. */
bool ShapeUtils::insertRingNode(RingNode* node, RingNode* tree)
{
	using namespace std;
	//cout << "ring " << dsv(*node->ring_p) << " being inserted." << endl;

	// We need to compare ourselves to each child, if there are any
	// calculate list of all children I intersect with.  Q is a list
	// of iterators for a RingNode* list.
	list<list<RingNode*>::iterator> Q;
	for (list<RingNode*>::iterator child_iter=tree->children.begin(),
		 iend=tree->children.end(); child_iter != iend; child_iter++) {
		//cout << "Ring intersects " << dsv(*(*child_iter)->ring_p) << "? ";
		if (intersects(*node->ring_p, *(*child_iter)->ring_p)) {
			Q.push_front(child_iter);
			//cout << "true" << endl;
		}
		else {
			//cout << "false" << endl;
		}

	}

	//cout << "Q.size() = " << Q.size() << endl;

	if (Q.empty()) {
		// I don't intersect with anybody, so I'm a peer of the current nodes
		tree->children.push_front(node);
		return true;
	} else {
		// If there is more than one item in Q, then I'm a parent of all
		// items in Q.  If there's only one item, then we have to check
		// to see if we are a child of item or not.
		// I'm either a parent of all items in Q, or I'm a child of
		// one of them.
		if (Q.size() > 1 || (*Q.front())->area < node->area ) {
			// make every item referenced in Q a child of node
			BOOST_FOREACH( list<RingNode*>::iterator ii, Q ) {
				tree->children.erase(ii);  // this is an O(1) operation
				node->children.push_front(*ii);
			}
			tree->children.push_front(node);
			return true;
		} else {
			// there can only be one item in Q.  Insert me into the subtree
			// rooted at the only item in Q.
			return insertRingNode(node, *Q.front());
		}
	}
}

void ShapeUtils::deleteRingTree(RingNode* tree)
{
	if (tree) {
		BOOST_FOREACH( RingNode* c, tree->children ) {
			ShapeUtils::deleteRingTree( c );
		}
		delete tree;
		tree = 0;
	}
}

void ShapeUtils::reverseRing(ring_2d& r)
{
	std::list<point_2d> temp;
	BOOST_FOREACH( point_2d p, r ) { temp.push_front(p); }
	r.clear();
	BOOST_FOREACH( point_2d p, temp ) { append(r, p); }
}

bool correctRingsHelper(ShapeUtils::RingNode* tree, int curr_depth,
									bool& any_changed)
{
	using namespace ShapeUtils;
	if (tree->ring_p && (curr_depth % 2) == 1 && !tree->clockwise) {
		// ring_p is of odd parity and should be corrected to clockwise
		reverseRing(*tree->ring_p);
		tree->clockwise = true;
		any_changed = true;
	} else if (tree->ring_p && (curr_depth % 2) == 0 && tree->clockwise) {
		// ring_p is of even parity and should be corrected to
		// counter-clockwise.
		reverseRing(*tree->ring_p);
		tree->clockwise = false;
		any_changed = true;
	}
	BOOST_FOREACH( ShapeUtils::RingNode* r, tree->children ) {
		correctRingsHelper(r, curr_depth+1, any_changed);
	}
	return true;
}

bool ShapeUtils::correctRings(RingNode* tree)
{
	if (!tree) return false;
	bool any_changed = false;
	correctRingsHelper(tree, 0, any_changed);
	return any_changed;
}

void printTreeHelper(ShapeUtils::RingNode* tree, int indent)
{
	using namespace ShapeUtils;
	std::cout << Shapefile::getIndentString(indent) << dsv(*(tree->ring_p));
	std::cout << ", area = " << tree->area << ", clockwise = "
	<< boolToString(tree->clockwise) << std::endl;
	BOOST_FOREACH( RingNode* r, tree->children ) {
		printTreeHelper(r, indent+1);
	}
}

void ShapeUtils::printTree(RingNode* tree)
{
	if (!tree) return;
	std::cout << "()" << std::endl;
	BOOST_FOREACH( RingNode* r, tree->children ) {
		printTreeHelper(r, 1);
	}
}

void ShapeUtils::populatePointSet(const PolygonContents& pc1,
								  Point_set& pc1_set)
{
	// we assume the pc1_set was sized correctly upon
	// construction.
	BOOST_FOREACH( const Shapefile::Point& pt, pc1.points ) {
		pc1_set.insert(pt);
	}
}

bool ShapeUtils::isQueenNeighbors(const Point_set& pc1_set,
								  const Point_set& pc2_set)
{
	if (pc1_set.size() > pc2_set.size()) {
		BOOST_FOREACH( const Shapefile::Point& pt, pc2_set ) {
			if (pc1_set.find(pt) != pc1_set.end() ) return true;
		}
	} else {
		BOOST_FOREACH( const Shapefile::Point& pt, pc1_set ) {
			if (pc2_set.find(pt) != pc2_set.end() ) return true;
		}
	}
	return false;
}

bool ShapeUtils::isQueenNeighbors(const Point_set& pc1_set,
								  const PolygonContents& pc2)
{
	BOOST_FOREACH( const Shapefile::Point& pt, pc2.points ) {
		if (pc1_set.find(pt) != pc1_set.end() ) return true;
	}
	return false;
}

bool ShapeUtils::isQueenNeighbors(const PolygonContents& pc1,
								  const PolygonContents& pc2)
{
	Point_set pc1_set(pc1.num_points);
	BOOST_FOREACH( const Shapefile::Point& pt, pc1.points ) {
		pc1_set.insert(pt);
	}
	BOOST_FOREACH( const Shapefile::Point& pt, pc2.points ) {
		if (pc1_set.find(pt) != pc1_set.end() ) return true;
	}
	return false;
}

bool ShapeUtils::isRookNeighbors(const PolygonContents& pc1,
								 const PolygonContents& pc2)
{
	using namespace std;
	Edge_set pc1_set(pc1.num_points);
	// add every edge in pc1 to pc1_set
	int pts_offset = 0;
	for (int i=0, prt_end=pc1.num_parts; i<prt_end; i++) {
		int prt_len = pc1.num_points;
		if (pc1.num_parts > 1) {
			if (i == prt_end-1) {
				prt_len = pc1.num_points - pc1.parts[i];
			} else {
				prt_len = pc1.parts[i+1] - pc1.parts[i];
			}
		}
		// note that endpoints are repeated in Shapefiles
		for (int j=0; j<prt_len-1; j++) {
			Edge e(pc1.points[pts_offset+j],
				   pc1.points[pts_offset+j+1]);
			//cout << "adding " << edgeToString(e) << endl;
			pc1_set.insert(e);
		}
		pts_offset += prt_len;
	}

	// check if any edge in pc2 is in pc1_set

	pts_offset = 0;
	for (int i=0, prt_end=pc2.num_parts; i<prt_end; i++) {
		int prt_len = pc2.num_points;
		if (pc2.num_parts > 1) {
			if (i == prt_end-1) {
				prt_len = pc2.num_points - pc2.parts[i];
			} else {
				prt_len = pc2.parts[i+1] - pc2.parts[i];
			}
		}
		// note that endpoints are repeated in Shapefiles
		for (int j=0; j<prt_len-1; j++) {
			Edge e(pc2.points[pts_offset+j],
				   pc2.points[pts_offset+j+1]);
			//cout << "checking " << edgeToString(e) << endl;
			if ( pc1_set.find(e) != pc1_set.end() ) return true;
		}
		pts_offset += prt_len;
	}

	return false;
}

double ShapeUtils::CalcMapWidth(Shapefile::Header header)
{
	return header.bbox_x_max - header.bbox_x_min;
}

double ShapeUtils::CalcMapHeight(Shapefile::Header header)
{
	return header.bbox_y_max - header.bbox_y_min;
}

double ShapeUtils::CalcAspectRatio(Shapefile::Header header)
{
	double map_h = CalcMapHeight(header);
	if (map_h != 0) {
		return CalcMapWidth(header) / map_h;
	}
	return 0;
}

void ShapeUtils::populatePointShpFile(const std::vector<double>& x,
									  const std::vector<double>& y,
									  Shapefile::Index& index,
									  Shapefile::Main& main)
{
	initHeader(index.header);
	initHeader(main.header);
	index.header.shape_type = Shapefile::POINT_TYP;
	main.header.shape_type = Shapefile::POINT_TYP;
	double xmin = x[0];
	double xmax = x[0];
	double ymin = y[0];
	double ymax = y[0];
	int records = x.size();
	for (int i=0; i<records; i++) {
		if (x[i] < xmin) xmin = x[i];
		if (x[i] > xmax) xmax = x[i];
		if (y[i] < ymin) ymin = y[i];
		if (y[i] > ymax) ymax = y[i];
	}
	index.header.bbox_x_min = xmin;
	index.header.bbox_y_min = ymin;
	index.header.bbox_x_max = xmax;
	index.header.bbox_y_max = ymax;
	main.header.bbox_x_min = xmin;
	main.header.bbox_y_min = ymin;
	main.header.bbox_x_max = xmax;
	main.header.bbox_y_max = ymax;

	index.header.file_length = 50 + records*4;
	main.header.file_length = 50 + records*14;

	// each main record has:
	// rec number: 4 bytes
	// content length: 4 bytes
	// shape type: 4 bytes
	// x and y doubles: 16 bytes
	// total = 28 bytes = 14 words (16-bit words)

	// main record content length: 16 bytes for data plus 4 bytes for
	// shape type = 20 bytes = 10 (16-bit words)

	index.records.clear();
	main.records.clear();
	index.records.resize(records);
	main.records.resize(records);
	for (int i=0; i<records; i++) {
		index.records[i].offset = 50 + i*14;
		index.records[i].content_length = 10;

		main.records[i].header.record_number = i+1;
		main.records[i].header.content_length = 10;
		Shapefile::PointContents* pc = new Shapefile::PointContents();
		main.records[i].contents_p = pc;
		pc->x = x[i];
		pc->y = y[i];
	}
}

void ShapeUtils::convertGdaPolygonsToShpFile(
						const std::vector<GdaPolygon*>& voronoi_polygons,
						const double& voronoi_bb_xmin,
						const double& voronoi_bb_ymin,
						const double& voronoi_bb_xmax,
						const double& voronoi_bb_ymax,
						Shapefile::Index& index, Shapefile::Main& main)
{
	initHeader(index.header);
	initHeader(main.header);
	index.header.shape_type = Shapefile::POLYGON;
	main.header.shape_type = Shapefile::POLYGON;
	index.header.bbox_x_min = voronoi_bb_xmin;
	index.header.bbox_y_min = voronoi_bb_ymin;
	index.header.bbox_x_max = voronoi_bb_xmax;
	index.header.bbox_y_max = voronoi_bb_ymax;
	main.header.bbox_x_min = voronoi_bb_xmin;
	main.header.bbox_y_min = voronoi_bb_ymin;
	main.header.bbox_x_max = voronoi_bb_xmax;
	main.header.bbox_y_max = voronoi_bb_ymax;
	int records = voronoi_polygons.size();

	index.header.file_length = 50 + records*4;

	// each main record has:
	// rec number: 4 bytes // part of polygon header
	// content length: 4 bytes // part of polygon header

	// shape type: 4 bytes
	// bounding box: (4*8) bytes = 32 bytes
	// variable number of bytes in content:
	// main record content length: 4 bytes for shape type plus
	// number of parts: 4 bytes
	// number of points: 4 bytes
	// parts array: num_parts*(4 bytes per part) = 4 bytes
	// points array: num_points*(16 bytes per point: double double)
	// total = 48 bytes + num_points*(16 bytes) = 24 + num_points*8 words

	// 16 bytes = 8 words

	index.records.clear();
	main.records.clear();
	index.records.resize(records);
	main.records.resize(records);
	int total_records_words = 0;
	int cur_offset = 50;
	for (int i=0; i<records; i++) {
		main.records[i].header.record_number = i+1;
		main.records[i].header.content_length = 24 + (voronoi_polygons[i]->n)*8;

		index.records[i].offset = cur_offset;
		index.records[i].content_length = main.records[i].header.content_length;

		Shapefile::PolygonContents* pc = new Shapefile::PolygonContents;
		GdaShapeAlgs::getBoundingBoxOrig(voronoi_polygons[i], pc->box[0],
										pc->box[1], pc->box[2], pc->box[3]);
		//LOG_MSG(wxString::Format("polygon %d bounding box:", i));
		//LOG_MSG(wxString::Format("    %f, %f", pc->box[0], pc->box[1]));
		//LOG_MSG(wxString::Format("    %f, %f", pc->box[2], pc->box[3]));
		pc->num_parts = 1;
		pc->num_points = voronoi_polygons[i]->n;
		pc->parts.resize(1);
		pc->parts[0] = 0;
		pc->points.resize(voronoi_polygons[i]->n);
		//LOG_MSG(wxString::Format("polygon %d points:", i));
		for (int j=0; j<voronoi_polygons[i]->n; j++) {
			pc->points[j].x = voronoi_polygons[i]->points_o[j].x;
			pc->points[j].y = voronoi_polygons[i]->points_o[j].y;
			//LOG_MSG(wxString::Format("    %f, %f",
			//						 pc->points[j].x, pc->points[j].y));
		}
		main.records[i].contents_p = pc;
		total_records_words += 4 + main.records[i].header.content_length;
		cur_offset += 4 + main.records[i].header.content_length;
	}
	main.header.file_length = 50 + total_records_words;
}

void ShapeUtils::initHeader(Shapefile::Header& header)
{
	header.file_code = 9994;
	header.file_length = 0;
	header.version = 1000;
	header.shape_type = 0;
	header.bbox_x_min = 0;
	header.bbox_y_min = 0;
	header.bbox_x_max = 0;
	header.bbox_y_max = 0;
	header.bbox_z_min = 0;
	header.bbox_z_max = 0;
	header.bbox_m_min = 0;
	header.bbox_m_max = 0;
}


#include <cmath>
#include <type_traits>
#include <boost/math/special_functions/round.hpp>
#include "bicycle/kinematic.h"
#include "kalman.h"
/*
 * Member function definitions of sim::Bicycle template class.
 * See simbicycle.h for template class declaration.
 */

namespace sim {

template <typename Model>
Bicycle<Model>::Bicycle(real_t v, real_t dt) :
m_model(v, dt),
m_full_state(full_state_t::Zero()),
m_pose(BicyclePoseMessage_init_zero),
m_input(input_t::Zero()),
m_measurement(measurement_t::Zero()) {
    chBSemObjectInit(&m_state_sem, false); // initialize to not taken
}

template <typename Model>
void Bicycle<Model>::set_v(real_t v)  {
    using namespace boost::math::policies;
    using quantize_policy = policy<rounding_error<ignore_error>>;
    static constexpr quantize_policy policy;

    real_t v_quantized = v_quantization_resolution *
        boost::math::round(v/v_quantization_resolution, policy);
    if (v_quantized != this->v()) {
        m_model.set_v_dt(v_quantized, m_model.dt());
    }
}

template <typename Model>
void Bicycle<Model>::set_dt(real_t dt)  {
    if (dt != this->dt()) {
        m_model.set_v_dt(m_model.v(), dt);
    }
}

template <typename Model>
void Bicycle<Model>::reset() {
    m_full_state = full_state_t::Zero();
}

template <typename Model>
void Bicycle<Model>::update_dynamics(real_t roll_torque_input, real_t steer_torque_input,
    real_t yaw_angle_measurement,
    real_t steer_angle_measurement,
    real_t rear_wheel_angle_measurement) {

    //  While the rear wheel angle measurement can be used to determine velocity,
    //  it is assumed that velocity is determined outside this class and passed as
    //  an input. This allows the velocity resolution, and thus the model update
    //  frequency, to be determined by a filter unrelated to the bicycle model.
    //
    //  This could also be used to determine the wheel angles, as we assume
    //  no-slip conditions. However, we calculate the wheel angles during the
    //  kinematic  update and solely from bicycle velocity.
    (void)rear_wheel_angle_measurement;

    input_t u = input_t::Zero();
    model_t::set_input_element(u, model_t::input_index_t::roll_torque, roll_torque_input);
    model_t::set_input_element(u, model_t::input_index_t::steer_torque, steer_torque_input);
    measurement_t z = measurement_t::Zero();
    model_t::set_output_element(z, model_t::output_index_t::yaw_angle, yaw_angle_measurement);
    model_t::set_output_element(z, model_t::output_index_t::steer_angle, steer_angle_measurement);

    update_dynamics(u, z);
}

template <typename Model>
void Bicycle<Model>::update_dynamics(input_t u, measurement_t z) {
    m_input = u;
    m_measurement = z;

    // copy full state before performing state update
    chBSemWait(&m_state_sem);
    full_state_t full_state_copy = m_full_state;
    chBSemSignal(&m_state_sem);

    full_state_t full_state_next = m_model.integrate_full_state(
            full_state_copy,
            m_input,
            m_model.dt(),
            m_measurement);

    chBSemWait(&m_state_sem);
    m_full_state = full_state_next;
    chBSemSignal(&m_state_sem);
}

template <typename Model>
void Bicycle<Model>::update_kinematics() {
    chBSemWait(&m_state_sem);
    full_state_t full_state_copy = m_full_state;
    chBSemSignal(&m_state_sem);

    // solve for pitch as this does not get integrated
    const real_t roll = model_t::get_full_state_element(full_state_copy, full_state_index_t::roll_angle);
    const real_t steer = model_t::get_full_state_element(full_state_copy, full_state_index_t::steer_angle);
    real_t pitch = model_t::get_full_state_element(full_state_copy, full_state_index_t::pitch_angle);
    pitch = m_model.solve_constraint_pitch(roll, steer, pitch);

    // update full state with newest computed pitch angle
    chBSemWait(&m_state_sem);
    model_t::set_full_state_element(m_full_state, full_state_index_t::pitch_angle, pitch);

    // mod rear wheel angle so it does not grow beyond all bounds
    model_t::set_full_state_element(m_full_state, full_state_index_t::rear_wheel_angle,
            std::fmod(model_t::get_full_state_element(full_state_copy, full_state_index_t::rear_wheel_angle),
                      constants::two_pi)),
    chBSemSignal(&m_state_sem);

    m_pose.timestamp = chVTGetSystemTime();
    m_pose.x = model_t::get_full_state_element(full_state_copy, full_state_index_t::x);
    m_pose.y = model_t::get_full_state_element(full_state_copy, full_state_index_t::y);
    m_pose.rear_wheel = model_t::get_full_state_element(full_state_copy, full_state_index_t::rear_wheel_angle);
    m_pose.pitch = pitch;
    m_pose.yaw = model_t::get_full_state_element(full_state_copy, full_state_index_t::yaw_angle);
    m_pose.roll = roll;
    m_pose.steer = steer;
}

template <typename Model>
const BicyclePoseMessage& Bicycle<Model>::pose() const {
    return m_pose;
}

template <typename Model>
const typename Bicycle<Model>::input_t& Bicycle<Model>::input() const {
    return m_input;
}

template <typename Model>
typename Bicycle<Model>::model_t& Bicycle<Model>::model() {
    return m_model;
};

template <typename Model>
const typename Bicycle<Model>::model_t& Bicycle<Model>::model() const {
    return m_model;
};

template <typename Model>
const typename Bicycle<Model>::second_order_matrix_t& Bicycle<Model>::M() const {
    return m_model.M();
}

template <typename Model>
const typename Bicycle<Model>::second_order_matrix_t& Bicycle<Model>::C1() const {
    return m_model.C1();
}

template <typename Model>
const typename Bicycle<Model>::second_order_matrix_t& Bicycle<Model>::K0() const {
    return m_model.K0();
}

template <typename Model>
const typename Bicycle<Model>::second_order_matrix_t& Bicycle<Model>::K2() const {
    return m_model.K2();
}

template <typename Model>
model::real_t Bicycle<Model>::wheelbase() const {
    return m_model.wheelbase();
}

template <typename Model>
model::real_t Bicycle<Model>::trail() const {
    return m_model.trail();
}

template <typename Model>
model::real_t Bicycle<Model>::steer_axis_tilt() const {
    return m_model.steer_axis_tilt();
}

template <typename Model>
model::real_t Bicycle<Model>::rear_wheel_radius() const {
    return m_model.rear_wheel_radius();
}

template <typename Model>
model::real_t Bicycle<Model>::front_wheel_radius() const {
    return m_model.front_wheel_radius();
}

template <typename Model>
model::real_t Bicycle<Model>::v() const {
    return m_model.v();
}

template <typename Model>
model::real_t Bicycle<Model>::dt() const {
    return m_model.dt();
}

template <typename Model>
const typename Bicycle<Model>::full_state_t& Bicycle<Model>::full_state() const {
    return m_full_state;
}

} // namespace sim


// Copyright (c) 2020 Graphcore Ltd. All rights reserved.

#define BOOST_TEST_MODULE BlockSparseOpsTest
#include "poputil/exceptions.hpp"
#include <boost/random.hpp>
#include <boost/test/unit_test.hpp>
#include <cstdlib>
#include <poplar/IPUModel.hpp>
#include <poplibs_support/TestDevice.hpp>
#include <poplibs_test/Util.hpp>
#include <poplin/codelets.hpp>
#include <popnn/NonLinearity.hpp>
#include <popnn/NonLinearityDefUtil.hpp>
#include <popnn/codelets.hpp>
#include <popops/EncodingConstants.hpp>
#include <popops/codelets.hpp>
#include <poputil/TileMapping.hpp>
#include <random>
#include <unordered_map>
#include <vector>

#include "popsparse/BSMatrix.hpp"
#include "popsparse/BSNonLinearity.hpp"
#include "popsparse/BSOps.hpp"
#include "popsparse/experimental/BlockSparse.hpp"

using namespace poplar;
using namespace poplar::program;
using namespace popsparse::experimental;
using namespace poputil;
using namespace popnn;
using namespace poplibs_support;

unsigned computeNz(unsigned blockRows, unsigned blockCols,
                   const unsigned char *sparsity,
                   std::vector<unsigned> &nzBlocksByRow,
                   std::vector<unsigned> &nzBlocksByRowTotal) {
  unsigned nz = 0;
  unsigned total = 0;
  nzBlocksByRow.resize(blockRows, 0);
  nzBlocksByRowTotal.resize(blockRows, 0);
  for (unsigned int br = 0; br < blockRows; ++br) {
    for (unsigned int bc = 0; bc < blockCols; ++bc) {
      unsigned idxBlockDense = br * blockCols + bc;
      if (sparsity[idxBlockDense]) {
        ++nzBlocksByRow[br];
        ++nz;
      }
    }
    nzBlocksByRowTotal[br] = total;
    total += nzBlocksByRow[br];
  }
  return nz;
}

#define USE_RANDOM_VALUES 1

void populateSparseBlocks(unsigned blockRow, unsigned blockCol,
                          unsigned blockRows, unsigned blockCols, unsigned nz,
                          const std::vector<unsigned> &nzBlocksByRow,
                          bool needTranspose,
                          std::vector<float> &valuesRowMjSparse,
                          std::vector<float> &valuesBlockSparse,
                          std::pair<float, float> inputRange = {0, 1.0f}) {
  if (inputRange.first >= inputRange.second) {
    throw poplibs_error("Range for input random data is invalid");
  }
  const unsigned blockArea = blockRow * blockCol;
  valuesRowMjSparse.resize(nz * blockArea);
  valuesBlockSparse.resize(valuesRowMjSparse.size());

#if USE_RANDOM_VALUES
  std::mt19937 randomEngine;
  boost::random::uniform_real_distribution<> dist(inputRange.first,
                                                  inputRange.second);
  for (unsigned i = 0; i < valuesRowMjSparse.size(); ++i) {
    valuesRowMjSparse[i] = dist(randomEngine);
  }
#endif
  for (unsigned br = 0, idxBlock = 0, idxBlockComplete = 0; br < blockRows;
       ++br) {
    for (unsigned bcPacked = 0; bcPacked < nzBlocksByRow[br];
         ++bcPacked, ++idxBlock) {
      for (unsigned rb = 0; rb < blockRow; ++rb) {
        for (unsigned cb = 0; cb < blockCol; ++cb) {
          unsigned idxInBlock =
              !needTranspose ? (rb * blockCol + cb) : (cb * blockRow + rb);
          unsigned idxDense = idxBlockComplete * blockArea +
                              rb * nzBlocksByRow[br] * blockCol +
                              bcPacked * blockCol + cb;
#if !USE_RANDOM_VALUES
          valuesRowMjSparse[idxDense] = idxBlock;
#endif
          valuesBlockSparse[idxBlock * blockArea + idxInBlock] =
              valuesRowMjSparse[idxDense];
        }
      }
    }
    idxBlockComplete += nzBlocksByRow[br];
  }
}

/*
Testing slice()

Block: 2x2

Dense block shape:
1 . 2
. 3 4
5 6 .

Sparse block shape:
1 2 3 4 5 6

*/
BOOST_AUTO_TEST_CASE(slice_test) {
  auto device = createTestDeviceFullSize(TEST_TARGET);
  const auto &target = device.getTarget();
  Graph graph(target);

  const unsigned blockRow = 2;
  const unsigned blockCol = 2;
  const unsigned blockRows = 3;
  const unsigned blockCols = 3;
  const unsigned blockArea = blockRow * blockCol;

  std::vector<unsigned char> sparsity(blockRows * blockCols, 0);
  sparsity[0 * blockCols + 0] = 1; // 0,0
  sparsity[0 * blockCols + 2] = 1; // 0,2
  sparsity[1 * blockCols + 1] = 1; // 1,1
  sparsity[1 * blockCols + 2] = 1; // 1,2
  sparsity[2 * blockCols + 0] = 1; // 2,0
  sparsity[2 * blockCols + 1] = 1; // 2,1

  std::vector<unsigned> nzBlocksByRow;
  std::vector<unsigned> nzBlocksByRowTotal;
  unsigned nz = computeNz(blockRows, blockCols, sparsity.data(), nzBlocksByRow,
                          nzBlocksByRowTotal);
  std::vector<float> valuesRowMjSparse;
  std::vector<float> valuesBlockSparse;
  populateSparseBlocks(blockRow, blockCol, blockRows, blockCols, nz,
                       nzBlocksByRow, false, valuesRowMjSparse,
                       valuesBlockSparse);

  Tensor sparseTensor = graph.addConstant(poplar::FLOAT, {nz, blockArea},
                                          valuesBlockSparse.data(), "sparse");

  BlockSparseMatrix bm(blockRow * blockRows, blockCol * blockCols, blockRow,
                       blockCol, false, sparsity.data());
  Tensor sparseTensorShapeCheck =
      bm.createTensor(graph, FLOAT, "sparseShapeCheck");
  BOOST_TEST(sparseTensor.shape() == sparseTensorShapeCheck.shape());

  //      coord dim
  // slice(1,    0) => [3,4]
  unsigned rowSlice = 1;
  Tensor slice0 = slice(sparseTensor, rowSlice, 0, blockRow, blockCol,
                        blockRows, blockCols, false, sparsity.data());

  BOOST_TEST(slice0.shape() == std::vector<std::size_t>({2 * blockCol}));

  const float epsilon = 0.001f;
  float valueRead;
  unsigned brSlice = rowSlice / blockRow;
  unsigned rbSlice = rowSlice % blockRow;
  for (unsigned bc = 0, colDense = 0; bc < blockCols; ++bc) {
    unsigned idxDense = brSlice * blockCols + bc;
    if (sparsity[idxDense]) {
      for (unsigned cb = 0; cb < blockCol; ++cb, ++colDense) {
        bool ret = slice0[colDense].getConstantValue(&valueRead);
        BOOST_TEST(ret);
        unsigned idxDense = nzBlocksByRowTotal[brSlice] * blockArea +
                            rbSlice * nzBlocksByRow[brSlice] * blockCol +
                            colDense;
        float valueExpected = valuesRowMjSparse[idxDense];
        float err = fabs(valueRead - valueExpected);
        if (err > epsilon) {
          BOOST_TEST(err <= epsilon);
        }
      }
    }
  }

  //      coord dim
  // slice(0,    1) => [1,5]
  unsigned colSlice = 0;
  Tensor slice1 = slice(sparseTensor, colSlice, 1, blockRow, blockCol,
                        blockRows, blockCols, false, sparsity.data());

  BOOST_TEST(slice1.shape() == std::vector<std::size_t>({2 * blockRow}));
  unsigned bcSlice = colSlice / blockCol;
  for (unsigned br = 0, rowDense = 0; br < blockRows; ++br) {
    unsigned idxDense = br * blockCols + bcSlice;
    if (sparsity[idxDense]) {
      for (unsigned rb = 0; rb < blockRow; ++rb, ++rowDense) {
        bool ret = slice1[rowDense].getConstantValue(&valueRead);
        BOOST_TEST(ret);
        unsigned idxDense = nzBlocksByRowTotal[br] * blockArea +
                            rb * nzBlocksByRow[br] * blockCol + colSlice;
        float valueExpected = valuesRowMjSparse[idxDense];
        float err = fabs(valueRead - valueExpected);
        if (err > epsilon) {
          BOOST_TEST(err <= epsilon);
        }
      }
    }
  }
}

void softmaxTest(unsigned blockRow, unsigned blockCol, unsigned blockRows,
                 unsigned blockCols, const std::vector<unsigned char> &sparsity,
                 const Type &dataType, bool filterUpperTriangle,
                 unsigned numGroups, bool inPlace,
                 std::pair<float, float> inputRange = {0, 1.0f}) {
  auto device = createTestDeviceFullSize(TEST_TARGET);
  const auto &target = device.getTarget();
  Graph graph(target);
  popnn::addCodelets(graph);
  popops::addCodelets(graph);
  poplin::addCodelets(graph);

  const unsigned blockArea = blockRow * blockCol;
  const unsigned rows = blockRows * blockRow;

  std::vector<unsigned> nzBlocksByRow;
  std::vector<unsigned> nzBlocksByRowTotal;
  unsigned nz = computeNz(blockRows, blockCols, sparsity.data(), nzBlocksByRow,
                          nzBlocksByRowTotal);
  std::vector<float> valuesRowMjSparse;
  std::vector<float> valuesBlockSparse;
  populateSparseBlocks(blockRow, blockCol, blockRows, blockCols, nz,
                       nzBlocksByRow, false, valuesRowMjSparse,
                       valuesBlockSparse, inputRange);

  std::vector<std::vector<float>> valuesRowSparse(rows);
  for (unsigned br = 0, r = 0, idxElem = 0; br < blockRows; ++br) {
    for (unsigned rb = 0; rb < blockRow; ++rb, ++r) {
      for (unsigned bc = 0, c = 0; bc < blockCols; ++bc) {
        unsigned idxDense = br * blockCols + bc;
        for (unsigned cb = 0; cb < blockCol; ++cb, ++c) {
          if (sparsity[idxDense]) {
            if (!filterUpperTriangle || r >= c) {
              valuesRowSparse[r].push_back(valuesRowMjSparse[idxElem]);
            }
            ++idxElem;
          }
        }
      }
    }
  }

  std::vector<Tensor> packedDenseTensors(rows);
  for (unsigned r = 0; r < rows; ++r) {
    packedDenseTensors[r] = graph.addConstant(
        dataType, {1, valuesRowSparse[r].size()}, valuesRowSparse[r].data(),
        std::string("packedDense") + std::to_string(r));
    mapTensorLinearly(graph, packedDenseTensors[r]);
  }

  std::vector<std::size_t> sparseShape = {nz, blockArea};
  Tensor sparseTensorC = graph.addConstant(dataType, sparseShape,
                                           valuesBlockSparse.data(), "sparseC");
  mapTensorLinearly(graph, sparseTensorC);

  Tensor sparseTensor = graph.addVariable(dataType, sparseShape, "sparse");
  mapTensorLinearly(graph, sparseTensor);

  Sequence softmaxProg;
  softmaxProg.add(program::Copy(sparseTensorC, sparseTensor));

  Tensor bsSoftmax =
      bsSoftmaxInternal(graph, sparseTensor, inPlace, blockRow, blockCol,
                        blockRows * numGroups, blockCols, sparsity.data(),
                        filterUpperTriangle ? SubBlockMask::ZeroUpperTriangle
                                            : SubBlockMask::None,
                        numGroups, softmaxProg, "bSsoftmax");
  if (inPlace) {
    bsSoftmax = sparseTensor;
  }

  std::vector<Tensor> nnSoftmaxs(rows);
  for (unsigned int r = 0; r < rows; ++r) {
    nnSoftmaxs[r] = nonLinearity(graph, NonLinearityType::SOFTMAX_STABLE,
                                 packedDenseTensors[r], softmaxProg,
                                 std::string("nNsoftmax") + std::to_string(r));
  }

  Sequence uploadProg, downloadProg;
  std::vector<std::pair<std::string, char *>> streamMaps;

  std::unique_ptr<char[]> bsSoftmaxRawHost =
      poplibs_test::util::allocateHostMemoryForTensor(
          bsSoftmax, "bsSoftmax", graph, uploadProg, downloadProg, streamMaps);

  std::vector<std::unique_ptr<char[]>> nnSoftmaxRawHosts(rows);
  for (unsigned int r = 0; r < rows; ++r) {
    nnSoftmaxRawHosts[r] = poplibs_test::util::allocateHostMemoryForTensor(
        nnSoftmaxs[r], std::string("nnSoftmax") + std::to_string(r), graph,
        uploadProg, downloadProg, streamMaps);
  }

  Sequence mainSequence;
  mainSequence.add(uploadProg);
  mainSequence.add(softmaxProg);
  mainSequence.add(poplar::program::PrintTensor("bsSoftmax", bsSoftmax));
  for (unsigned int r = 0; r < rows; ++r) {
    mainSequence.add(poplar::program::PrintTensor(
        std::string("nnSoftmaxs") + std::to_string(r), nnSoftmaxs[r]));
  }
  mainSequence.add(downloadProg);

  const OptionFlags engineOptions{{"debug.allowOutOfMemory", "true"},
                                  {"debug.nanOverflowException", "true"}};

  Engine engine(graph, mainSequence, engineOptions);
  poplibs_test::util::attachStreams(engine, streamMaps);
  device.bind([&](const Device &d) {
    engine.load(d);
    engine.run(0);
  });

  boost::multi_array<float, 2> bsSoftmaxHost(
      boost::extents[sparseShape[0]][sparseShape[1]]);
  poplibs_test::util::copy(target, dataType, bsSoftmaxRawHost.get(),
                           bsSoftmaxHost);

  std::vector<boost::multi_array<float, 2>> nnSoftmaxHosts;
  for (unsigned int r = 0; r < rows; ++r) {
    boost::multi_array<float, 2> nnSoftmaxHost(
        boost::extents[1][valuesRowSparse[r].size()]);
    poplibs_test::util::copy(target, dataType, nnSoftmaxRawHosts[r].get(),
                             nnSoftmaxHost);
    nnSoftmaxHosts.push_back(nnSoftmaxHost);
  }

  const float epsilon = (dataType == FLOAT ? 0.001f : 0.1f);
  for (unsigned br = 0, idxBlock = 0; br < blockRows; ++br) {
    for (unsigned bcPacked = 0; bcPacked < nzBlocksByRow[br];
         ++bcPacked, ++idxBlock) {
      for (unsigned rb = 0; rb < blockRow; ++rb) {
        for (unsigned cb = 0; cb < blockCol; ++cb) {
          unsigned rowDense = br * blockRow + rb;
          unsigned colDense = bcPacked * blockCol + cb;
          unsigned idxInBlock = rb * blockCol + cb;
          // We compare the result only when softmaxrow is non-empty
          float valueBsSoftmax = bsSoftmaxHost[idxBlock][idxInBlock];
          BOOST_TEST(!std::isnan(valueBsSoftmax));
          float valueNnSoftmax;
          if (colDense < nnSoftmaxHosts[rowDense].shape()[1]) {
            valueNnSoftmax = nnSoftmaxHosts[rowDense][0][colDense];
          } else {
            valueNnSoftmax = 0.0f;
          }
          float err = fabs(valueBsSoftmax - valueNnSoftmax);
          if (err > epsilon) {
            BOOST_TEST(err <= epsilon);
          }
        }
      }
    }
  }
}

// Testing softmax()
/*
Block: 2x2

Dense block shape:
1 . .
2 3 .
. 4 .

Sparse block shape:
1 2 3 4

Dense block shapes to compare:
1
2 3
4

*/
BOOST_AUTO_TEST_CASE(softmax_testF32) {
  const unsigned blockRow = 2;
  const unsigned blockCol = 2;
  const unsigned blockRows = 3;
  const unsigned blockCols = 3;
  std::vector<unsigned char> sparsity(blockRows * blockCols, 0);
  sparsity[0 * blockCols + 0] = 1; // 0,0
  sparsity[1 * blockCols + 0] = 1; // 1,0
  sparsity[1 * blockCols + 1] = 1; // 1,1
  sparsity[2 * blockCols + 1] = 1; // 2,1

  softmaxTest(blockRow, blockCol, blockRows, blockCols, sparsity, FLOAT, false,
              1, false);
}

BOOST_AUTO_TEST_CASE(softmax_testF32_largeValues) {
  const unsigned blockRow = 2;
  const unsigned blockCol = 2;
  const unsigned blockRows = 3;
  const unsigned blockCols = 3;
  std::vector<unsigned char> sparsity(blockRows * blockCols, 0);
  sparsity[0 * blockCols + 0] = 1; // 0,0
  sparsity[1 * blockCols + 0] = 1; // 1,0
  sparsity[1 * blockCols + 1] = 1; // 1,1
  sparsity[2 * blockCols + 1] = 1; // 2,1

  softmaxTest(blockRow, blockCol, blockRows, blockCols, sparsity, FLOAT, false,
              1, false, {-1000, 1000});
}

BOOST_AUTO_TEST_CASE(softmax_testF32inPlace) {
  const unsigned blockRow = 2;
  const unsigned blockCol = 2;
  const unsigned blockRows = 3;
  const unsigned blockCols = 3;
  std::vector<unsigned char> sparsity(blockRows * blockCols, 0);
  sparsity[0 * blockCols + 0] = 1; // 0,0
  sparsity[1 * blockCols + 0] = 1; // 1,0
  sparsity[1 * blockCols + 1] = 1; // 1,1
  sparsity[2 * blockCols + 1] = 1; // 2,1

  softmaxTest(blockRow, blockCol, blockRows, blockCols, sparsity, FLOAT, false,
              1, true);
}

BOOST_AUTO_TEST_CASE(softmax_testF16) {
  const unsigned blockRow = 2;
  const unsigned blockCol = 2;
  const unsigned blockRows = 3;
  const unsigned blockCols = 3;
  std::vector<unsigned char> sparsity(blockRows * blockCols, 0);
  sparsity[0 * blockCols + 0] = 1; // 0,0
  sparsity[1 * blockCols + 0] = 1; // 1,0
  sparsity[1 * blockCols + 1] = 1; // 1,1
  sparsity[2 * blockCols + 1] = 1; // 2,1

  softmaxTest(blockRow, blockCol, blockRows, blockCols, sparsity, HALF, false,
              1, false);
}

// Testing softmax() with subblock mask
/*
Block: 2x3
Blocks: 4x3

Block mask:
1 . 1
. 1 .
1 . .
. . 1

Dense shape:
0 0 0 . . . . . .
0 0 0 . . . . . .
. . . 1 1 1 . . .
. . . 1 1 1 . . .
2 2 2 . . . . . .
2 2 2 . . . . . .
. . . . . . 3 3 3
. . . . . . 3 3 3
. . . . . . . . .
. . . . . . . . .

Dense masked shape:
0 . . . . . . . .
0 0 . . . . . . .
. . . . . . . . .
. . . 1 . . . . .
2 2 2 . . . . . .
2 2 2 . . . . . .
. . . . . . 3 . .
. . . . . . 3 3 .
. . . . . . . . .
. . . . . . . . .

*/
BOOST_AUTO_TEST_CASE(softmaxSubBlockMask_testF32) {
  const unsigned blockRow = 2;
  const unsigned blockCol = 3;
  const unsigned blockRows = 5;
  const unsigned blockCols = 3;
  std::vector<unsigned char> sparsity(blockRows * blockCols, 0);
  sparsity[0 * blockCols + 0] = 1; // 0,0
  sparsity[1 * blockCols + 1] = 1; // 1,1
  sparsity[2 * blockCols + 0] = 1; // 2,0
  sparsity[3 * blockCols + 2] = 1; // 3,2

  softmaxTest(blockRow, blockCol, blockRows, blockCols, sparsity, FLOAT, true,
              1, false);
}

BOOST_AUTO_TEST_CASE(softmaxSubBlockMask_testF16_pos) {
  const unsigned blockRow = 2;
  const unsigned blockCol = 3;
  const unsigned blockRows = 4;
  const unsigned blockCols = 3;
  std::vector<unsigned char> sparsity(blockRows * blockCols, 0);
  sparsity[0 * blockCols + 0] = 1; // 0,0
  sparsity[1 * blockCols + 1] = 1; // 1,1
  sparsity[2 * blockCols + 0] = 1; // 2,0
  sparsity[3 * blockCols + 2] = 1; // 3,2

  softmaxTest(blockRow, blockCol, blockRows, blockCols, sparsity, HALF, true, 1,
              false, {0, 1.0});
}

BOOST_AUTO_TEST_CASE(softmaxSubBlockMask_testF16_neg_pos) {
  const unsigned blockRow = 2;
  const unsigned blockCol = 3;
  const unsigned blockRows = 4;
  const unsigned blockCols = 3;
  std::vector<unsigned char> sparsity(blockRows * blockCols, 0);
  sparsity[0 * blockCols + 0] = 1; // 0,0
  sparsity[1 * blockCols + 1] = 1; // 1,1
  sparsity[2 * blockCols + 0] = 1; // 2,0
  sparsity[3 * blockCols + 2] = 1; // 3,2

  softmaxTest(blockRow, blockCol, blockRows, blockCols, sparsity, HALF, true, 1,
              false, {-1.0, 1.0});
}

BOOST_AUTO_TEST_CASE(softmaxSubBlockMask_testF16_largeValues) {
  const unsigned blockRow = 2;
  const unsigned blockCol = 3;
  const unsigned blockRows = 4;
  const unsigned blockCols = 3;
  std::vector<unsigned char> sparsity(blockRows * blockCols, 0);
  sparsity[0 * blockCols + 0] = 1; // 0,0
  sparsity[1 * blockCols + 1] = 1; // 1,1
  sparsity[2 * blockCols + 0] = 1; // 2,0
  sparsity[3 * blockCols + 2] = 1; // 3,2

  softmaxTest(blockRow, blockCol, blockRows, blockCols, sparsity, HALF, true, 1,
              false, {0, 30000.0f});
}

void softmaxGradTest(unsigned blockRow, unsigned blockCol, unsigned blockRows,
                     unsigned blockCols,
                     const std::vector<unsigned char> &sparsity,
                     const Type &outType, const Type &outGradType) {
  auto device = createTestDeviceFullSize(TEST_TARGET);
  const auto &target = device.getTarget();
  Graph graph(target);
  popnn::addCodelets(graph);
  popops::addCodelets(graph);
  poplin::addCodelets(graph);

  const unsigned blockArea = blockRow * blockCol;
  const unsigned rows = blockRows * blockRow;

  std::vector<unsigned> nzBlocksByRow;
  std::vector<unsigned> nzBlocksByRowTotal;
  unsigned nz = computeNz(blockRows, blockCols, sparsity.data(), nzBlocksByRow,
                          nzBlocksByRowTotal);
  std::vector<float> vOutRowMjSparse;
  std::vector<float> vOutBlockSparse;
  populateSparseBlocks(blockRow, blockCol, blockRows, blockCols, nz,
                       nzBlocksByRow, false, vOutRowMjSparse, vOutBlockSparse);

  std::vector<float> vOutGradRowMjSparse;
  std::vector<float> vOutGradBlockSparse;
  populateSparseBlocks(blockRow, blockCol, blockRows, blockCols, nz,
                       nzBlocksByRow, false, vOutGradRowMjSparse,
                       vOutGradBlockSparse);

  std::vector<std::vector<float>> vOutRowSparse(rows);
  std::vector<std::vector<float>> vOutGradRowSparse(rows);
  for (unsigned br = 0, r = 0, idxElem = 0; br < blockRows; ++br) {
    for (unsigned rb = 0; rb < blockRow; ++rb, ++r) {
      for (unsigned bc = 0, c = 0; bc < blockCols; ++bc) {
        unsigned idxDense = br * blockCols + bc;
        for (unsigned cb = 0; cb < blockCol; ++cb, ++c) {
          if (sparsity[idxDense]) {
            vOutRowSparse[r].push_back(vOutRowMjSparse[idxElem]);
            vOutGradRowSparse[r].push_back(vOutGradRowMjSparse[idxElem]);
            ++idxElem;
          }
        }
      }
    }
  }

  std::vector<Tensor> nnOutTensors(rows);
  std::vector<Tensor> nnOutGradTensors(rows);
  for (unsigned r = 0; r < rows; ++r) {
    nnOutTensors[r] = graph.addConstant(
        outType, {1, vOutRowSparse[r].size()}, vOutRowSparse[r].data(),
        std::string("nnOut") + std::to_string(r));
    mapTensorLinearly(graph, nnOutTensors[r]);
    nnOutGradTensors[r] =
        graph.addConstant(outGradType, {1, vOutGradRowSparse[r].size()},
                          vOutGradRowSparse[r].data(),
                          std::string("nnOutGrad") + std::to_string(r));
    mapTensorLinearly(graph, nnOutGradTensors[r]);
  }

  std::vector<std::size_t> sparseShape = {nz, blockArea};
  Tensor bsOutTensor =
      graph.addConstant(outType, sparseShape, vOutBlockSparse.data(), "bsOutC");
  mapTensorLinearly(graph, bsOutTensor);
  Tensor bsOutGradTensor = graph.addConstant(
      outGradType, sparseShape, vOutGradBlockSparse.data(), "bsOutGrad");
  mapTensorLinearly(graph, bsOutGradTensor);

  Sequence softmaxProg;
  Tensor bsSoftmaxGrad = bsSoftmaxGradInternal(
      graph, bsOutTensor, bsOutGradTensor, blockRow, blockCol, blockRows,
      blockCols, sparsity.data(), softmaxProg, "bsSoftmaxGrad");
  BOOST_TEST(bsSoftmaxGrad.elementType() == outType);

  std::vector<Tensor> nnSoftmaxGrads(rows);
  for (unsigned int r = 0; r < rows; ++r) {
    nnSoftmaxGrads[r] = nonLinearityInputGradient(
        graph, NonLinearityType::SOFTMAX_STABLE, nnOutTensors[r],
        nnOutGradTensors[r], softmaxProg,
        std::string("nnSoftmaxGrad") + std::to_string(r));
    BOOST_TEST(nnSoftmaxGrads[r].elementType() == outType);
  }

  Sequence uploadProg, downloadProg;
  std::vector<std::pair<std::string, char *>> streamMaps;

  std::unique_ptr<char[]> bsSoftmaxGradRawHost =
      poplibs_test::util::allocateHostMemoryForTensor(
          bsSoftmaxGrad, "bsSoftmaxGrad", graph, uploadProg, downloadProg,
          streamMaps);

  std::vector<std::unique_ptr<char[]>> nnSoftmaxGradRawHosts(rows);
  for (unsigned int r = 0; r < rows; ++r) {
    nnSoftmaxGradRawHosts[r] = poplibs_test::util::allocateHostMemoryForTensor(
        nnSoftmaxGrads[r], std::string("nnSoftmaxGrad") + std::to_string(r),
        graph, uploadProg, downloadProg, streamMaps);
  }

  Sequence mainSequence;
  mainSequence.add(uploadProg);
  mainSequence.add(softmaxProg);
  mainSequence.add(
      poplar::program::PrintTensor("bsSoftmaxGrad", bsSoftmaxGrad));
  for (unsigned int r = 0; r < rows; ++r) {
    mainSequence.add(poplar::program::PrintTensor(
        std::string("nnSoftmaxGrads") + std::to_string(r), nnSoftmaxGrads[r]));
  }
  mainSequence.add(downloadProg);

  const OptionFlags engineOptions{{"debug.allowOutOfMemory", "true"}};

  Engine engine(graph, mainSequence, engineOptions);
  poplibs_test::util::attachStreams(engine, streamMaps);
  device.bind([&](const Device &d) {
    engine.load(d);
    engine.run(0);
  });

  boost::multi_array<float, 2> bsSoftmaxGradHost(
      boost::extents[sparseShape[0]][sparseShape[1]]);
  poplibs_test::util::copy(target, outType, bsSoftmaxGradRawHost.get(),
                           bsSoftmaxGradHost);

  std::vector<boost::multi_array<float, 2>> nnSoftmaxGradHosts;
  for (unsigned int r = 0; r < rows; ++r) {
    boost::multi_array<float, 2> nnSoftmaxGradHost(
        boost::extents[1][vOutRowSparse[r].size()]);
    poplibs_test::util::copy(target, outType, nnSoftmaxGradRawHosts[r].get(),
                             nnSoftmaxGradHost);
    nnSoftmaxGradHosts.push_back(nnSoftmaxGradHost);
  }

  const float epsilon = (outType == FLOAT ? 0.001f : 0.1f);
  for (unsigned br = 0, idxBlock = 0; br < blockRows; ++br) {
    for (unsigned bcPacked = 0; bcPacked < nzBlocksByRow[br];
         ++bcPacked, ++idxBlock) {
      for (unsigned rb = 0; rb < blockRow; ++rb) {
        for (unsigned cb = 0; cb < blockCol; ++cb) {
          unsigned rowDense = br * blockRow + rb;
          unsigned colDense = bcPacked * blockCol + cb;
          unsigned idxInBlock = rb * blockCol + cb;
          // We compare the result only when softmaxrow is non-empty
          float valueBsSoftmaxGrad = bsSoftmaxGradHost[idxBlock][idxInBlock];
          float valueNnSoftmaxGrad;
          if (colDense < nnSoftmaxGradHosts[rowDense].shape()[1]) {
            valueNnSoftmaxGrad = nnSoftmaxGradHosts[rowDense][0][colDense];
          } else {
            valueNnSoftmaxGrad = 0.0f;
          }
          float err = fabs(valueBsSoftmaxGrad - valueNnSoftmaxGrad);
          if (err > epsilon) {
            BOOST_TEST(err <= epsilon);
          }
        }
      }
    }
  }
}

// Testing softmaxGrad()
/*
Block: 2x3

Dense block shape:
0 1 .
. 2 .
. 3 4
. . .

Sparse block shape:
0 1 2 3 4

Dense block shapes to compare:
0 1
2
3 4
.

*/
void softmaxGrad_test_3x3_4x3(const Type &outType, const Type &outGradType) {
  const unsigned blockRow = 2;
  const unsigned blockCol = 3;
  const unsigned blockRows = 4;
  const unsigned blockCols = 3;
  std::vector<unsigned char> sparsity(blockRows * blockCols, 0);
  sparsity[0 * blockCols + 0] = 1; // 0,0
  sparsity[0 * blockCols + 1] = 1; // 0,1
  sparsity[1 * blockCols + 1] = 1; // 1,1
  sparsity[2 * blockCols + 0] = 1; // 2,0
  sparsity[2 * blockCols + 1] = 1; // 2,1

  softmaxGradTest(blockRow, blockCol, blockRows, blockCols, sparsity, outType,
                  outGradType);
}

BOOST_AUTO_TEST_CASE(softmaxGrad_testF32_F32) {
  softmaxGrad_test_3x3_4x3(FLOAT, FLOAT);
}

BOOST_AUTO_TEST_CASE(softmaxGrad_testF16_F16) {
  softmaxGrad_test_3x3_4x3(HALF, HALF);
}


//
// Copyright © 2017 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//
#include <boost/test/unit_test.hpp>
#include "armnnCaffeParser/ICaffeParser.hpp"
#include "ParserPrototxtFixture.hpp"
#include <sstream>
#include <initializer_list>

namespace
{

template <typename T>
std::string TaggedSequence(const std::string & tag, const std::initializer_list<T> & data)
{
    bool first = true;
    std::stringstream ss;
    for (auto && d : data)
    {
        if (!first)
        {
            ss << " , ";
        }
        else
        {
            first = false;
        }
        ss << " " << tag << " : " << d << " ";
    }
    return ss.str();
}

template <typename T>
std::string TaggedSequence(const std::string & tag, T data, unsigned int n)
{
    std::stringstream ss;
    for (unsigned int i=0; i<n; ++i)
    {
        if (i>0)
        {
            ss << " , ";
        }
        ss << " " << tag << " : " << data << " ";
    }
    return ss.str();
}

} // namespace <anonymous>

BOOST_AUTO_TEST_SUITE(CaffeParser)

struct ConvolutionFixture : public armnnUtils::ParserPrototxtFixture<armnnCaffeParser::ICaffeParser>
{
    ConvolutionFixture(const std::initializer_list<unsigned int> & inputDims,
                       const std::initializer_list<float> & filterData,
                       unsigned int kernelSize,
                       unsigned int numOutput=1,
                       unsigned int group=1)
    {
        m_Prototext = R"(
            name: "ConvolutionTest"
            layer {
                name: "input1"
                type: "Input"
                top: "input1"
                input_param { shape: { )" + TaggedSequence("dim", inputDims) + R"( } }
            }
            layer {
                name: "conv1"
                type: "Convolution"
                bottom: "input1"
                top: "conv1"
                blobs: { )" + TaggedSequence("data", filterData) + R"( }
                blobs: { )" + TaggedSequence("data", 0, numOutput) + R"( }
                convolution_param {
                    num_output: )" + std::to_string(numOutput) + R"(
                    pad: 0
                    kernel_size: )" +  std::to_string(kernelSize) + R"(
                    stride: 1
                    group: )" +  std::to_string(group) + R"(
                }
            }
        )";
        SetupSingleInputSingleOutput("input1", "conv1");
    }
};

struct SimpleConvolutionFixture : public ConvolutionFixture
{
    SimpleConvolutionFixture()
    : ConvolutionFixture( {1, 1, 2, 2}, {1.0f, 1.0f, 1.0f, 1.0f}, 2)
    {
    }
};

BOOST_FIXTURE_TEST_CASE(SimpleConvolution, SimpleConvolutionFixture)
{
    RunTest<4>({ 1, 3, 5, 7 }, { 16 });
}

struct GroupConvolutionFixture : public ConvolutionFixture
{
    GroupConvolutionFixture()
    : ConvolutionFixture(
        {1, 2, 2, 2},
        {
            1.0f, 1.0f, 1.0f, 1.0f, // filter for channel #0
            2.0f, 2.0f, 2.0f, 2.0f  // filter for channel #1
        },
        2, // kernel size is 2x2
        2, // number of output channels is 2
        2) // number of groups (separate filters)
    {
    }
};

BOOST_FIXTURE_TEST_CASE(GroupConvolution, GroupConvolutionFixture)
{
    RunTest<4>(
        {
            1, 2, 3, 4, // input channel #0
            5, 6, 7, 8, // input channel #1
        },
        {
            10, // convolution result for channel #0 applying filter #0
            52  // same for channel #1 and filter #1
        }
    );
}

BOOST_AUTO_TEST_SUITE_END()


/*
 * PeriodData.cpp
 *
 *  Created on: Jun 23, 2014
 *      Author: sroehling
 */

#include <boost/test/unit_test.hpp>

#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include "PeriodValueRef.h"

using namespace boost::posix_time;
using namespace boost::gregorian;

#include "PeriodVal.h"

BOOST_AUTO_TEST_CASE( PeriodValTest_WithAdjustedValues )
{
	// Read a valid quotes file and check some of the values
	PeriodValCltnPtr periodData = PeriodVal::readFromFile("./patternScan/QCOR_DoubleBottom_Weekly.csv");
	BOOST_CHECK(periodData->size()==26);

	PeriodVal firstVal = periodData->front();
    // The close values are adjusted, based
    // upon the difference between the close and adjusted close.
    // Since this test data came from Yahoo Finance, however, the
    // volume is also adjusted.
    //
    // The values from the CSV file are as follows:
    // Date,Open,High,Low,Close,Volume,Adj Close
    // 2013-08-26,72.41,74.76,66.01,66.68,1469600,65.76
    //
    // The "scale factor to adjust the open, high, and low is
    // as follows: (from PerVal.cpp source):
    //      double adjScaleFactor = adjClose/close;
    // So, in this case, we expect the scale factor to be: 65.76/66.68 = 0.9862
    double scaleFactor = 65.76/66.68;
    BOOST_CHECK_EQUAL(firstVal.volume(),1469600);
    BOOST_CHECK_CLOSE(firstVal.close(),65.76,0.01);
    BOOST_CHECK_CLOSE(firstVal.open(),scaleFactor*72.41,0.01);
    BOOST_CHECK_CLOSE(firstVal.high(),scaleFactor*74.76,0.01);
    BOOST_CHECK_CLOSE(firstVal.low(),scaleFactor*66.01,0.01);

	ClosePeriodValueRef closeRef;
    BOOST_CHECK_CLOSE(closeRef.referencedVal(firstVal),65.76,0.001);

    // The values from the CSV file are as follows:
    // Date,Open,High,Low,Close,Volume,Adj Close
    // 2014-02-18,70.37,76.46,69.73,75.83,3770100,75.54
    PeriodVal lastVal = periodData->back();
    BOOST_CHECK_EQUAL(lastVal.volume(),3770100);
    BOOST_CHECK_CLOSE(lastVal.close(),75.54,0.001);
    BOOST_CHECK_EQUAL(lastVal.periodTime(),ptime(date(2014,2,18)));

}

BOOST_AUTO_TEST_CASE( PeriodValTest_InvalidCSV )
{
    // Verify error conditions are handled by the quote reading code.
    BOOST_CHECK_THROW(PeriodVal::readFromFile("invalidFileName.csv"),std::runtime_error);
    BOOST_CHECK_THROW(PeriodVal::readFromFile("./quoteData/MissingHeader.csv"),std::runtime_error);
    BOOST_CHECK_THROW(PeriodVal::readFromFile("./quoteData/MissingField.csv"),std::runtime_error);
    BOOST_CHECK_THROW(PeriodVal::readFromFile("./quoteData/CorruptDate.csv"),std::runtime_error);
    BOOST_CHECK_THROW(PeriodVal::readFromFile("./quoteData/CorruptVal.csv"),std::runtime_error);
}

BOOST_AUTO_TEST_CASE( PeriodValTest_ValidCSV )
{
    PeriodValCltnPtr periodData = PeriodVal::readFromFile("./quoteData/yahooFormatAAPL.csv");
    BOOST_TEST_MESSAGE( "Yahoo formatted CSV Data: number of rows: " << periodData->size() );
    BOOST_CHECK(periodData->size()==203);

    periodData = PeriodVal::readFromFile("./quoteData/GoogleFormatAAPL.csv");
    BOOST_TEST_MESSAGE( "Google formatted CSV Data: number of rows - AAPL: " << periodData->size() );
    BOOST_CHECK(periodData->size()==250);
    PeriodVal lastVal = periodData->back();
    BOOST_CHECK_EQUAL(lastVal.volume(),27442252);
    BOOST_CHECK_EQUAL(lastVal.periodTime(),ptime(date(2014,11,11)));

    periodData = PeriodVal::readFromFile("./quoteData/GoogleFormatAAPL-Short.csv");
    BOOST_CHECK(periodData->size()==2);
    lastVal = periodData->back();
    BOOST_CHECK_EQUAL(lastVal.periodTime(),ptime(date(2014,11,10)));
    PeriodVal firstVal = periodData->front();
    BOOST_CHECK_EQUAL(firstVal.periodTime(),ptime(date(2014,11,07)));

    periodData = PeriodVal::readFromFile("./quoteData/GoogleFormatCELG.csv");
    BOOST_TEST_MESSAGE( "Google formatted CSV Data - CELG: number of rows: " << periodData->size() );
    BOOST_CHECK(periodData->size()==252);
}

BOOST_AUTO_TEST_CASE( PeriodValTest_ValidCSV_yLoader )
{
    PeriodValCltnPtr periodData = PeriodVal::readFromFile("./quoteData/yLoaderFormatAAPL.csv");
    BOOST_TEST_MESSAGE( "yLoader formatted CSV Data - AAPL: number of rows: " << periodData->size() );
    BOOST_CHECK(periodData->size()==285);
    PeriodVal lastVal = periodData->back();
    BOOST_CHECK_EQUAL(lastVal.periodTime(),ptime(date(2014,11,14)));
    PeriodVal firstVal = periodData->front();
    BOOST_CHECK_EQUAL(firstVal.periodTime(),ptime(date(2013,10,01)));

}

BOOST_AUTO_TEST_CASE( PeriodValTest_ValidCSV_eodData )
{
    PeriodValCltnPtr periodData = PeriodVal::readFromFile("./quoteData/eodDataFormat_AAPL_20140101_20141117.csv");
    BOOST_TEST_MESSAGE( "eod data formatted CSV Data - AAPL: number of rows: " << periodData->size() );
    BOOST_CHECK(periodData->size()==229);
    PeriodVal lastVal = periodData->back();
    BOOST_CHECK_EQUAL(lastVal.periodTime(),ptime(date(2014,11,17)));
    BOOST_CHECK_EQUAL(lastVal.volume(),46746700);
    PeriodVal firstVal = periodData->front();
    BOOST_CHECK_EQUAL(firstVal.periodTime(),ptime(date(2014,01,01)));

}

BOOST_AUTO_TEST_CASE( PeriodValTest_MalformedHeaders )
{
    BOOST_CHECK_THROW(PeriodVal::readFromFile("./quoteData/MalformedHeader.csv"),std::runtime_error);
    BOOST_CHECK_THROW(PeriodVal::readFromFile("./quoteData/MalformedHeaderNoCloseField.csv"),std::runtime_error);
}


/*******************************************************************************
 *
 * MIT License
 *
 * Copyright (c) 2019 Advanced Micro Devices, Inc.
 *
 * 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.
 *
 *******************************************************************************/

#pragma once

#include <Tensile/Tensile.hpp>
#include <Tensile/ContractionProblem.hpp>

#include <boost/program_options.hpp>

namespace Tensile
{
    namespace Client
    {

        namespace po = boost::program_options;

        class ClientProblemFactory
        {
        public:
            ClientProblemFactory(po::variables_map const& args);
            ~ClientProblemFactory();

            std::vector<ContractionProblem> const& problems() const;

            std::vector<ContractionProblem> createProblems();
        private:
            std::vector<ContractionProblem> m_problems;

            ContractionProblem::FreeIndices  m_freeIndices;
            ContractionProblem::BatchIndices m_batchIndices;
            ContractionProblem::BoundIndices m_boundIndices;

            DataType m_aType;
            DataType m_bType;
            DataType m_cType;
            DataType m_dType;
            DataType m_alphaType;
            DataType m_betaType;
            bool m_highPrecisionAccumulate;

            std::vector<std::vector<size_t>> m_problemSizes;
            std::vector<std::vector<size_t>> m_aStrides;
            std::vector<std::vector<size_t>> m_bStrides;
            std::vector<std::vector<size_t>> m_cStrides;
            std::vector<std::vector<size_t>> m_dStrides;

            TensorOps m_aOps;
            TensorOps m_bOps;
            TensorOps m_cOps;
            TensorOps m_dOps;

            double m_beta;
        };

    }
}


#include <iostream>
#include <string>
#include <armadillo>

int main(int argc, char * argv[])
{
  if (argc != 3) {
    std::cout << "Give in a csv to convert followed by output file" << std::endl;
    return 1;
  }
  std::cout << "Converting CSV to ARMA" << std::endl;
  arma::Mat<float> A;
  A.load(argv[1], arma::csv_ascii);
  A.save(argv[2]);
  return 0;
}


#include <boost/test/unit_test.hpp>

#include "PatternMatch.h"
#include "PeriodVal.h"
#include "PeriodValSegment.h"
#include "ChartSegment.h"
#include "TestHelper.h"
#include "TimeHelper.h"
#include "TestPerValRange.h"
#include "CompositePatternMatchValidatorFactory.h"
#include "PrevPatternValueRatioValidatorFactory.h"
#include "HighestHighLessThanFirstHigh.h"
#include "PatternMatchValueRef.h"

using namespace testHelper;
using namespace boost::gregorian;

BOOST_AUTO_TEST_CASE( PatternMatchValidator_SynthesizePattern )
{
	TestPerValRangeList ranges;
	ranges.push_back(TestPerValRange(4,105.0,100.0)); // initial down-trend
	ranges.push_back(TestPerValRange(3,101.0,104.5)); // up-trend falling short of down-trend
	ranges.push_back(TestPerValRange(3,104.0,99.0)); // next down-trend, going below the initial down-trend
	ranges.push_back(TestPerValRange(4,99.5,106.0)); // final uptrend, goes above start
	synthesizePeriodValSegment(date(2014,1,1),ranges);
}

BOOST_AUTO_TEST_CASE( CompositeValidatorFactory )
{
    TestPerValRangeList ranges;
    ranges.push_back(TestPerValRange(4,100,110.0)); // uptrend
    PeriodValSegmentPtr perValSeg = synthesizePeriodValSegment(date(2014,1,1),ranges);
    PatternMatchPtr patternMatch = testHelper::segmentToPatternMatch(perValSeg);

    CompositePatternMatchValidatorFactory factory;
    factory.addStaticValidator(PatternMatchValidatorPtr(new HighestHighLessThanFirstHigh()));

    PatternMatchValidatorPtr validator = factory.createValidator0();
    bool isValid = validator->validPattern(*patternMatch);
    BOOST_TEST_MESSAGE("CompositeValidatorFactory: valid pattern? " << isValid);
    BOOST_CHECK(isValid == false);
}

BOOST_AUTO_TEST_CASE( PreviousPatternValueRatio )
{
    TestPerValRangeList previousRanges;
    previousRanges.push_back(TestPerValRange(10,100,110.0)); // uptrend
    PatternMatchPtr prevPatternMatch = synthesizePatternMatch(date(2014,1,1),previousRanges);

    DoubleRange validRatios(0.5,2.0);
    PrevPatternValueRatioValidatorFactory ratioValidatorFactory(validRatios,
               PatternMatchValueRefPtr(new NumPeriodsPatternMatchValueRef()));

    PatternMatchValidatorPtr validator = ratioValidatorFactory.createValidator1(prevPatternMatch);

    BOOST_TEST_MESSAGE("PreviousPatternValueRatio: checking pattern which is too long");
    TestPerValRangeList pat1Ranges;
    pat1Ranges.push_back(TestPerValRange(21,100,110.0)); // too long, more than double length of previous
    PatternMatchPtr pat1 = synthesizePatternMatch(date(2014,1,1),pat1Ranges);
    BOOST_CHECK(validator->validPattern(*pat1) == false);

    BOOST_TEST_MESSAGE("PreviousPatternValueRatio: checking pattern which is too short");
    TestPerValRangeList pat2Ranges;
    pat2Ranges.push_back(TestPerValRange(4,100,110.0)); // too short
    PatternMatchPtr pat2 = synthesizePatternMatch(date(2014,1,1),pat2Ranges);
    BOOST_CHECK(validator->validPattern(*pat2) == false);

    BOOST_TEST_MESSAGE("PreviousPatternValueRatio: checking pattern which within the valid ratio");
    TestPerValRangeList pat3Ranges;
    pat3Ranges.push_back(TestPerValRange(6,100,110.0)); // within valid ratio
    PatternMatchPtr pat3 = synthesizePatternMatch(date(2014,1,1),pat3Ranges);
    BOOST_CHECK(validator->validPattern(*pat3) == true);

    BOOST_TEST_MESSAGE("PreviousPatternValueRatio: checking pattern which within the valid ratio");
    TestPerValRangeList pat4Ranges;
    pat4Ranges.push_back(TestPerValRange(19,100,110.0)); // within valid ratio
    PatternMatchPtr pat4 = synthesizePatternMatch(date(2014,1,1),pat4Ranges);
    BOOST_CHECK(validator->validPattern(*pat4) == true);

}


// ------------------------------------------------------------------------
//  Copyright (C)
//  ETH Zurich - Switzerland
//
//  Kevis-Kokitsi Maninis <kmaninis@vision.ee.ethz.ch>
//  Jordi Pont-Tuset <jponttuset@vision.ee.ethz.ch>
//  July 2016
// ------------------------------------------------------------------------
// This file is part of the COB package presented in:
//    K.K. Maninis, J. Pont-Tuset, P. Arbelaez and L. Van Gool
//    Convolutional Oriented Boundaries
//    European Conference on Computer Vision (ECCV), 2016
// Please consider citing the paper if you use this code.
// ------------------------------------------------------------------------
#ifndef MEX_HELPERS_HPP
#define MEX_HELPERS_HPP

#include "mex.h"
#include <Eigen/Dense>

template<class T>
struct mex_types
{
    typedef typename Eigen::Array<T,Eigen::Dynamic,Eigen::Dynamic> eigen_type;
};

template<class T, class P>
Eigen::Map<Eigen::Array<T,Eigen::Dynamic,Eigen::Dynamic> > eigen_map(P matlab_pointer)
{
    return Eigen::Map<Eigen::Array<T,Eigen::Dynamic,Eigen::Dynamic> > ((T*)mxGetData(matlab_pointer),mxGetM(matlab_pointer),mxGetN(matlab_pointer));
}

#endif


//
// we only list the latest check up for the codes
//
// Feb 28th 2016:
//
// full test is made after we revise code in
// github commit a2d3c0d61d94f9d8c0f7eaeaf112d6658415bcf5
//
// the following modules has been well tested:
// two and three body KI, OV
// NAI
// all ERI
// MOM (auxL up to 5)
// are well tested, for L = 4, auxL = 5
// the test are performed for both single precision and
// double precision
// threshold value for single is given as 1.0*10^-5
// threshold value for double is given as 1.0*10^-10
// all of tests for "double" are below the given thresh
// value.
// see the sing.log and double.log for the test results.
//

#include "libgen.h"
#include "ov.h"
#include "ki.h"
#include "nai.h"
#include "tov.h"
#include "tki.h"
#include "eritest.h"
#include "mom.h"
#include "expr12test.h"
#include <boost/algorithm/string.hpp>   // string handling
#include <boost/lexical_cast.hpp>
using namespace ov;
using namespace tov;
using namespace tki;
using namespace ki;
using namespace nai;
using namespace eritest;
using namespace mom;
using namespace expr12test;

Int main(int argc, char* argv[])
{
	/////////////////////////////////////////////////////////////////////////////
	// setting the testing jobs
	// we note that the maxL must be in accordance with the hgp codes
	// so as the auxMaxL, order etc.
	/////////////////////////////////////////////////////////////////////////////
	bool testOV  = false;
	bool testKI  = false;
	bool testNAI = false;
	bool testERI = false;
	bool testTOV = false;
	bool testTKI = false;
	bool testMOM = false;
	bool test2BodyERI = false;
	bool test3BodyERI = false;
	bool testEXPR12   = false;

	// settings we need to further processed
	// here is default value
	Int order   = 0;
	Int maxL    = 4;
	Int auxMaxL = 5;
	Int momL    = 5;

	// parse the input parameter
	for(Int i=1; i<argc; i++) {
		string com = argv[i];
		boost::algorithm::to_lower(com);
		if (com == "ov"   ) testOV  = true;
		if (com == "tov"  ) testTOV = true;
		if (com == "tki"  ) testTKI = true;
		if (com == "ki"   ) testKI  = true;
		if (com == "nai"  ) testNAI = true;
		if (com == "eri"  ) testERI = true;
		if (com == "mom"  ) testMOM = true;
		if (com == "2eri" ) test2BodyERI = true;
		if (com == "3eri" ) test3BodyERI = true;
		if (com == "expr12") testEXPR12  = true;
	}

	// now print out the input information
	cout << "********job testing list********" << endl;
	cout << "integral testing for energy calculation" << endl;
	cout << "maximum angular momentum for integrals is " << maxL << endl;
	cout << "maximum aux angular momentum for integrals is " << auxMaxL << endl;
	cout << "maximum angular momentum for MOM integrals is " << momL << endl;
	if (testOV) cout <<  "two body overlap" << endl;
	if (testTOV) cout << "three body overlap" << endl;
	if (testKI) cout  << "two body kinetic" << endl;
	if (testTKI) cout << "three body kinetic" << endl;
	if (testNAI) cout << "two body nuclear attraction" << endl;
	if (testERI) cout << "normal ERI" << endl;
	if (test2BodyERI) cout << "two body ERI" << endl;
	if (test3BodyERI) cout << "three body ERI" << endl;
	if (testMOM) cout << "momentum integrals" << endl;
	if (testEXPR12) cout << "expr12 integrals" << endl;
	cout << "********************************" << endl;

	/////////////////////////////////////////////////////////////////////////////
	// now setting the shell data
	// all of shell data are in default to be composite shell
	// which contains two sub-shells
	/////////////////////////////////////////////////////////////////////////////

	// shell 1
	Int inp = 2;
	if (order > 0) inp = 1;
	vector<Double> iexp(inp);
	vector<Double> icoe(2*inp);
	if (order == 0) {
		iexp[0] = 1.8812885;
		iexp[1] = 0.5442493;
		icoe[0] = 0.1193324;
		icoe[1] = 0.1608542;
		icoe[2] = 0.8434564;
		icoe[3] = 0.0689991;
	}else{
		iexp[0] = 0.1442493;
		icoe[0] = 1.0;
		icoe[1] = 1.0;
	}
	Double A[3];
	A[0]    = 1.0;
	A[1]    = 0.0;
	A[2]    = 0.0;

	// shell 2
	Int jnp = 2;
	if (order > 0) jnp = 1;
	vector<Double> jexp(jnp);
	vector<Double> jcoe(2*jnp);
	if (order == 0) {
		jexp[0] = 0.5439130;
		jexp[1] = 0.0914760;
		jcoe[0] = 6.139703E-02;
		jcoe[1] = 3.061130E-01;
		jcoe[2] = 1.154890;
		jcoe[3] = 0.1891265;
	}else{
		jexp[0] = 0.0914760;
		jcoe[0] = 1.0;
		jcoe[1] = 1.0;
	}
	Double B[3];
	B[0]    = 0.0;
	B[1]    = 1.0;
	B[2]    = 0.0;

	// shell 3
	Int knp = 2;
	if (order > 0) knp = 1;
	vector<Double> kexp(knp);
	vector<Double> kcoe(2*knp);
	if (order == 0) {
		kexp[0] = 0.63628970;
		kexp[1] = 0.14786010;
		kcoe[0] = -0.09996723;
		kcoe[1] = 0.39951283;
		kcoe[2] = 0.70011547;
		kcoe[3] = 0.15591627;
	}else{
		kexp[0] = 0.04808870;
		kcoe[0] = 1.0;
		kcoe[1] = 1.0;
	}
	Double C[3];
	C[0]    = 0.0;
	C[1]    = 0.0;
	C[2]    = 1.0;

	// shell 4
	Int lnp = 2;
	if (order > 0) lnp = 1;
	vector<Double> lexp(lnp);
	vector<Double> lcoe(2*lnp);
	if (order == 0) {
		lexp[0] = 0.51982050;
		lexp[1] = 0.16906180;
		lcoe[0] = 0.06723;
		lcoe[1] = 0.51283;
		lcoe[2] = 0.30011547;
		lcoe[3] = 0.5591627;
	}else{
		lexp[0] = 0.23695610;
		lcoe[0] = 1.0;
		lcoe[1] = 1.0;
	}
	Double D[3];
	D[0]    = 0.0;
	D[1]    = 1.0;
	D[2]    = 1.0;

	// create the dummy shell
	// the most important thing for the
	// shell is that its exp is all zero
	Int dnp = 1;
	vector<Double> dexp(dnp,ZERO);
	vector<Double> dcoe(dnp,ONE);
	Double dummy[3];
	dummy[0] = 0.0;
	dummy[1] = 0.0;
	dummy[2] = 0.0;

	// nuclear centers as well as atomic numbers
	Int nAtoms = 3;
	vector<Double> coord(3*nAtoms);
	vector<Int> atomicNumbers(nAtoms);
	coord[0] = 0.0;
	coord[1] = 0.0;
	coord[2] = 1.0;
	coord[3] = 0.0;
	coord[4] = 1.0;
	coord[5] = 0.0;
	coord[6] = 1.0;
	coord[7] = 0.0;
	coord[8] = 0.0;
	atomicNumbers[0] = 1;
	atomicNumbers[1] = 2;
	atomicNumbers[2] = 3;

	/////////////////////////////////////////////////////////////////////////////
	//                  now this is the real working section
	/////////////////////////////////////////////////////////////////////////////
	if (testOV) {
		ov_test(maxL,inp,icoe,iexp,A,jnp,jcoe,jexp,B);
	}
	if (testTOV) {
		tov_test(maxL,inp,icoe,iexp,A,jnp,jcoe,jexp,B,knp,kcoe,kexp,C);
	}
	if (testTKI) {
		tki_test(maxL,inp,icoe,iexp,A,jnp,jcoe,jexp,B,knp,kcoe,kexp,C);
	}
	if (testMOM) {

		// the center for moment operator is set to 0
		Double cen[3];
		cen[0] = ZERO;
		cen[1] = ZERO;
		cen[2] = ZERO;
		mom_test(maxL,momL,inp,icoe,iexp,A,jnp,jcoe,jexp,B,cen);
	}
	if (testKI) {
		ki_test(maxL,inp,icoe,iexp,A,jnp,jcoe,jexp,B);
	}
	if (testNAI) {
		nai_test(maxL,inp,icoe,iexp,A,jnp,jcoe,jexp,B,coord,atomicNumbers);
	}
	if (testERI) {
		eri_test(maxL,auxMaxL,NORMAL_ERI,
				inp,icoe,iexp,A,
				jnp,jcoe,jexp,B,
				knp,kcoe,kexp,C,
				lnp,lcoe,lexp,D);
	}
	if (test2BodyERI) {
		eri_test(maxL,auxMaxL,TWO_BODY_ERI,
				inp,icoe,iexp,A,
				dnp,dcoe,dexp,dummy,
				knp,kcoe,kexp,C,
				dnp,dcoe,dexp,dummy);
	}
	if (test3BodyERI) {
		eri_test(maxL,auxMaxL,THREE_BODY_ERI,
				inp,icoe,iexp,A,
				jnp,jcoe,jexp,B,
				knp,kcoe,kexp,C,
				dnp,dcoe,dexp,dummy);
	}
	if (testEXPR12) {
		expr12_test(maxL,
				inp,icoe,iexp,A,
				jnp,jcoe,jexp,B,
				knp,kcoe,kexp,C);
	}
	cout << "all of jobs finished" << endl;

	return 0;

}


/*
@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/list/mcd.hpp>

#include <boost/hana/detail/assert.hpp>
#include <boost/hana/detail/constexpr.hpp>
#include <boost/hana/detail/minimal/comparable.hpp>
#include <boost/hana/detail/minimal/list.hpp>

#include <tuple>
using namespace boost::hana;

template <int i>
constexpr auto x = detail::minimal::comparable<>(i);

BOOST_HANA_CONSTEXPR_LAMBDA auto f = [](auto x) {
    return std::make_tuple(x);
};

template <typename mcd>
void test() {
    BOOST_HANA_CONSTEXPR_LAMBDA auto list = detail::minimal::list<mcd>;

    BOOST_HANA_CONSTANT_ASSERT(fmap(f, list()) == list());
    BOOST_HANA_CONSTEXPR_ASSERT(fmap(f, list(x<1>)) == list(f(x<1>)));
    BOOST_HANA_CONSTEXPR_ASSERT(fmap(f, list(x<1>, x<2>)) == list(f(x<1>), f(x<2>)));
    BOOST_HANA_CONSTEXPR_ASSERT(fmap(f, list(x<1>, x<2>, x<3>)) == list(f(x<1>), f(x<2>), f(x<3>)));
    BOOST_HANA_CONSTEXPR_ASSERT(fmap(f, list(x<1>, x<2>, x<3>, x<4>)) == list(f(x<1>), f(x<2>), f(x<3>), f(x<4>)));
}

int main() {
    test<List::mcd<void>>();
}


// Copyright 2021 Hannah Rittich
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef ALLIUM_LA_LOCAL_MATRIX_HPP
#define ALLIUM_LA_LOCAL_MATRIX_HPP

#include <cstdlib>
#include <Eigen/Core>
#include "local_vector.hpp"
#include <iostream>

namespace allium {

  /**
   @brief A dense, local (non-distributed) matrix.
   */
  template <typename N>
  class LocalMatrix {
    public:
      /**
        Create a matrix with storage for the specified number of rows and
        columns.
      */
      LocalMatrix(size_t rows, size_t cols);

      /**
        Create a matrix from a nested list of initializers.
      */
      explicit LocalMatrix(std::initializer_list<std::initializer_list<N>> entries);

      /**
        Access matrix elements.
      */
      N& operator() (size_t i_row, size_t i_col) {
        return m_storage(i_row, i_col);
      }

      size_t rows() { return m_storage.rows(); }
      size_t cols() { return m_storage.cols(); }

      /**
        Apply the matrix to a vector, i.e., multiply the matrix and the
        vector.
      */
      LocalVector<N> apply(const LocalVector<N>& x);

      void set_row(size_t i_row, const LocalVector<N>& v);
      void set_col(size_t i_col, const LocalVector<N>& v);

      LocalVector<N> get_row(size_t i_row) const;
      LocalVector<N> get_col(size_t i_col) const;
    private:
      Eigen::Matrix<N, Eigen::Dynamic, Eigen::Dynamic> m_storage;
  };

  template <typename N>
  LocalMatrix<N>::LocalMatrix(size_t rows, size_t cols)
    : m_storage(rows, cols)
  {}

  template <typename N>
  LocalMatrix<N>::LocalMatrix(std::initializer_list<std::initializer_list<N>> entries)
  {
    const size_t new_rows = entries.size();
    const size_t new_cols = (new_rows > 0) ? (*entries.begin()).size() : 0;

    m_storage.resize(new_rows, new_cols);

    int i = 0;
    for (auto row : entries) {
      if (row.size() != new_cols)
        throw std::runtime_error("Rows are not of equal length.");

      int j = 0;
      for (auto entry : row) {
        m_storage(i, j) = entry;
        ++j;
      }

      ++i;
    }
  }

  template <typename N>
  LocalVector<N> LocalMatrix<N>::apply(const LocalVector<N>& x)
  {
    return LocalVector<N>(m_storage * x.native());
  }

  template <typename N>
  void LocalMatrix<N>::set_row(size_t i_row, const LocalVector<N>& v) {
    m_storage.row(i_row) = v.native();
  }

  template <typename N>
  void LocalMatrix<N>::set_col(size_t i_col, const LocalVector<N>& v) {
    m_storage.col(i_col) = v.native();
  }

  template <typename N>
  LocalVector<N> LocalMatrix<N>::get_row(size_t i_row) const {
    return LocalVector<N>(m_storage.row(i_row));
  }

  template <typename N>
  LocalVector<N> LocalMatrix<N>::get_col(size_t i_col) const {
    return LocalVector<N>(m_storage.col(i_col));
  }
}

#endif


#include <opencv2/imgcodecs.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <boost/multi_array.hpp>
#include <opencv2/imgproc.hpp>
#include <cmath>
#include <vector>
#include <iostream>
#include <stack>
#include <queue>
#define oneighthPI (3.141592654/8.)

using namespace cv;
using namespace std;
class Point0{
    public:
	int x;
	int y;
	Point0(int x,int y);
};
class matrics
{
    public:
	int rows;
	int cols;
	Mat bgr;
	Mat gray;
	Mat GauBlurMat;
	typedef boost::multi_array<double,3> Esn0;
	typedef boost::multi_array<double,2> thinMat;
	thinMat In;
	thinMat Infinal;
	Esn0 es0;
	enum {strength,tangent};
	enum operators {oper2x2,sobel};
	matrics(const char *filename);
	Mat doubleArray2Mat(thinMat);
	thinMat triTo2d(Esn0 es0);
	void gausblur(int i);
	void enhance(operators op);
	void nonmaximaSupress();
	void doubleTholdNlink(double th,double tl);
};