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

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

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

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_INSERTER_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_INSERTER_HPP

#include <cstddef>

#include <boost/numeric/conversion/cast.hpp>

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

#include <boost/geometry/core/exterior_ring.hpp>
#include <boost/geometry/core/interior_rings.hpp>

#include <boost/geometry/extensions/strategies/buffer_side.hpp>
#include <boost/geometry/extensions/algorithms/buffer/buffered_piece_collection.hpp>
#include <boost/geometry/extensions/algorithms/buffer/line_line_intersection.hpp>

#ifdef BOOST_GEOMETRY_DEBUG_WITH_MAPPER
#  include <boost/geometry/extensions/algorithms/buffer/buffered_piece_collection_with_mapper.hpp>
#endif

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace buffer
{

template
<
    typename RingOutput,
    typename Tag
>
struct buffer_range
{
    typedef typename point_type<RingOutput>::type output_point_type;
    typedef typename coordinate_type<RingOutput>::type coordinate_type;
    typedef model::referring_segment<output_point_type const> segment_type;

    template
        <
            typename Point,
            typename DistanceStrategy
        >
    static inline void generate_side(Point const& ip1, Point const& ip2,
                buffer_side_selector side,
                DistanceStrategy const& distance,
                output_point_type& p1, output_point_type& p2)
    {
        // Generate a block along (left or right of) the segment

        // Simulate a vector d (dx,dy)
        coordinate_type dx = get<0>(ip2) - get<0>(ip1);
        coordinate_type dy = get<1>(ip2) - get<1>(ip1);

        // For normalization [0,1] (=dot product d.d, sqrt)
        // TODO promoted_type
        coordinate_type const length = sqrt(dx * dx + dy * dy);

        // Because coordinates are not equal, length should not be zero
        BOOST_ASSERT((! geometry::math::equals(length, 0)));

        // Generate the normalized perpendicular p, to the left (ccw)
        coordinate_type const px = -dy / length;
        coordinate_type const py = dx / length;

        coordinate_type const d = distance.apply(ip1, ip2, side);

        set<0>(p1, get<0>(ip1) + px * d);
        set<1>(p1, get<1>(ip1) + py * d);
        set<0>(p2, get<0>(ip2) + px * d);
        set<1>(p2, get<1>(ip2) + py * d);
    }

    template
    <
        typename Collection,
        typename Iterator,
        typename DistanceStrategy,
        typename JoinStrategy,
        typename EndStrategy
    >
    static inline void iterate(Collection& collection,
                Iterator begin, Iterator end,
                buffer_side_selector side,
                DistanceStrategy const& distance,
                JoinStrategy const& join_strategy,
                EndStrategy const& end_strategy,
                bool close = false)
    {
        output_point_type previous_p1, previous_p2;
        output_point_type first_p1, first_p2;

        bool first = true;

        Iterator it = begin;

        // We want to memorize the last segment too.
        typedef BOOST_TYPEOF(*it) point_type;
        point_type penultimate_point, ultimate_point; // last two points from begin/end

        for (Iterator prev = it++; it != end; ++it)
        {
            if (! detail::equals::equals_point_point(*prev, *it))
            {
                output_point_type p1, p2;
                penultimate_point = *prev;
                ultimate_point = *it;
                generate_side(*prev, *it, side, distance, p1, p2);

                std::vector<output_point_type> range_out;
                if (! first)
                {
                    output_point_type p;
                    segment_type s1(p1, p2);
                    segment_type s2(previous_p1, previous_p2);

                    if (line_line_intersection<output_point_type, segment_type>::apply(s1, s2, p))
                    {
                        join_strategy.apply(p, *prev, previous_p2, p1,
                                    distance.apply(*prev, *it, side),
                                    range_out);
                    }
                }
                else
                {
                    first = false;
                    first_p1 = p1;
                    first_p2 = p2;
                }
                if (! range_out.empty())
                {
                    collection.add_piece(buffered_join, *prev, range_out);
                    range_out.clear();
                }
                collection.add_piece(buffered_segment, *prev, *it, p1, p2);

                previous_p1 = p1;
                previous_p2 = p2;
                prev = it;
            }
        }

        // TODO: take care of degenerate segments

        // Might be replaced by specialization
        if(boost::is_same<Tag, ring_tag>::value)
        {
            // Generate closing corner
            output_point_type p;
            segment_type s1(previous_p1, previous_p2);
            segment_type s2(first_p1, first_p2);
            if (line_line_intersection<output_point_type, segment_type>::apply(s1, s2, p))
            {
                std::vector<output_point_type> range_out;
                join_strategy.apply(p, *begin, previous_p2, first_p1,
                    distance.apply(*(end - 1), *begin, side),
                    range_out);
                if (! range_out.empty())
                {
                    collection.add_piece(buffered_join, *begin, range_out);
                }
            }

            // Buffer is closed automatically by last closing corner (NOT FOR OPEN POLYGONS - TODO)
        }
        else if (boost::is_same<Tag, linestring_tag>::value)
        {

            // Generate perpendicular points to the reverse side,
            // these points are necessary for all end-cap strategies
            // TODO fix this (approach) for one-side buffer (1.5 - -1.0)
            output_point_type rp1, rp2;
            generate_side(ultimate_point, penultimate_point,
                    side == buffer_side_left
                    ? buffer_side_right
                    : buffer_side_left,
                    distance, rp2, rp1);

            std::vector<output_point_type> range_out;

            end_strategy.apply(penultimate_point, previous_p2, ultimate_point, rp2, side, distance, range_out);
            collection.add_endcap(end_strategy, range_out, ultimate_point);
        }
    }
};

template
<
    typename Point,
    typename RingOutput
>
struct buffer_point
{
    typedef typename point_type<RingOutput>::type output_point_type;
    typedef typename coordinate_type<RingOutput>::type coordinate_type;
    typedef model::referring_segment<output_point_type const> segment_type;

    typedef typename geometry::select_most_precise
        <
            typename geometry::select_most_precise
                <
                    typename geometry::coordinate_type<Point>::type,
                    typename geometry::coordinate_type<output_point_type>::type
                >::type,
            double
        >::type promoted_type;

    template <typename RangeOut>
    static inline void generate_points(Point const& point,
                promoted_type const& buffer_distance,
                RangeOut& range_out)
    {

        promoted_type two = 2.0;
        promoted_type two_pi = two * geometry::math::pi<promoted_type>();
        int point_buffer_count = 88; // 88 gives now fixed problem (collinear opposite / robustness. TODO: make this value flexible

        promoted_type diff = two_pi / promoted_type(point_buffer_count);
        promoted_type a = 0;

        output_point_type first;
        for (int i = 0; i < point_buffer_count; i++, a -= diff)
        {
            output_point_type p;
            set<0>(p, get<0>(point) + buffer_distance * cos(a));
            set<1>(p, get<1>(point) + buffer_distance * sin(a));
            range_out.push_back(p);
            if (i == 0)
            {
                first = p;
            }
        }

        // Close it:
        range_out.push_back(first);
    }

    template
    <
        typename Collection,
        typename DistanceStrategy,
        typename JoinStrategy,
        typename EndStrategy
    >
    static inline void generate_circle(Point const& point,
                Collection& collection,
                DistanceStrategy const& distance,
                JoinStrategy const& join_strategy,
                EndStrategy const& end_strategy)
    {
        std::vector<output_point_type> range_out;
        //RingOutput range_out;

        generate_points(point,
            distance.apply(point, point, buffer_side_left),
            range_out);

        collection.add_piece(buffered_circle, range_out, false);

        //std::cout << std::setprecision(20);
        //std::cout << geometry::wkt(range_out) << std::endl;
    }
};

}} // namespace detail::buffer
#endif // DOXYGEN_NO_DETAIL

#ifndef DOXYGEN_NO_DISPATCH
namespace dispatch
{

template
<
    typename Tag,
    typename RingInput,
    typename RingOutput
>
struct buffer_inserter
{};

template
<
    typename Point,
    typename RingOutput
>
struct buffer_inserter<point_tag, Point, RingOutput>
    : public detail::buffer::buffer_point<Point, RingOutput>
{
    template<typename Collection, typename DistanceStrategy, typename JoinStrategy, typename EndStrategy>
    static inline void apply(Point const& point, Collection& collection,
            DistanceStrategy const& distance,
            JoinStrategy const& join_strategy,
            EndStrategy const& end_strategy)
    {
        collection.start_new_ring();
            typedef detail::buffer::buffer_point<Point, RingOutput> base;
        base::generate_circle(point, collection, distance, join_strategy, end_strategy);
    }
};

template
<
    typename RingInput,
    typename RingOutput
>
struct buffer_inserter<ring_tag, RingInput, RingOutput>
    : public detail::buffer::buffer_range
        <
            RingOutput,
            ring_tag
        >
{
    typedef detail::buffer::buffer_range
        <
            RingOutput,
            ring_tag
        > base;

    template
    <
        typename Collection, typename DistanceStrategy, typename JoinStrategy, typename EndStrategy
    >
    static inline void apply(RingInput const& ring,
            Collection& collection,
            DistanceStrategy const& distance,
            JoinStrategy const& join_strategy,
            EndStrategy const& end_strategy)
    {
        if (boost::size(ring) > 3)
        {
            base::iterate(collection, boost::begin(ring), boost::end(ring),
                    buffer_side_left,
                    distance, join_strategy, end_strategy);
        }
    }
};

template
<
    typename Linestring,
    typename Polygon
>
struct buffer_inserter<linestring_tag, Linestring, Polygon>
    : public detail::buffer::buffer_range
        <
            typename ring_type<Polygon>::type,
            linestring_tag
        >
{
    typedef detail::buffer::buffer_range
        <
            typename ring_type<Polygon>::type,
            linestring_tag
        > base;

    template<typename Collection, typename DistanceStrategy, typename JoinStrategy, typename EndStrategy>
    static inline void apply(Linestring const& linestring, Collection& collection,
            DistanceStrategy const& distance,
            JoinStrategy const& join_strategy,
            EndStrategy const& end_strategy)
    {
        if (boost::size(linestring) > 1)
        {
            collection.start_new_ring();
            base::iterate(collection, boost::begin(linestring), boost::end(linestring),
                    buffer_side_left,
                    distance, join_strategy, end_strategy);

            base::iterate(collection, boost::rbegin(linestring), boost::rend(linestring),
                    buffer_side_right,
                    distance, join_strategy, end_strategy, true);
        }

    }
};

template
<
    typename PolygonInput,
    typename PolygonOutput
>
struct buffer_inserter<polygon_tag, PolygonInput, PolygonOutput>
{

    template <typename Collection, typename DistanceStrategy, typename JoinStrategy, typename EndStrategy>
    static inline void apply(PolygonInput const& polygon, Collection& collection,
            DistanceStrategy const& distance,
            JoinStrategy const& join_strategy,
            EndStrategy const& end_strategy)
    {

        typedef typename ring_type<PolygonInput>::type input_ring_type;
        typedef typename ring_type<PolygonOutput>::type output_ring_type;

        typedef buffer_inserter<ring_tag, input_ring_type, output_ring_type> policy;

        {
            collection.start_new_ring();
            policy::apply(exterior_ring(polygon), collection,
                    distance, join_strategy, end_strategy);
        }

        typename interior_return_type<PolygonInput const>::type rings
                    = interior_rings(polygon);
        for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it)
        {
            collection.start_new_ring();
            policy::apply(*it, collection, distance, join_strategy, end_strategy);
        }

    }
};

} // namespace dispatch
#endif // DOXYGEN_NO_DISPATCH

template
<
    typename GeometryOutput,
    typename GeometryInput,
    typename OutputIterator,
    typename DistanceStrategy,
    typename JoinStrategy,
    typename EndStrategy
#ifdef BOOST_GEOMETRY_DEBUG_WITH_MAPPER
    , typename Mapper
#endif
>
inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator out,
        DistanceStrategy const& distance_strategy,
        JoinStrategy const& join_strategy,
        EndStrategy const& end_strategy
#ifdef BOOST_GEOMETRY_DEBUG_WITH_MAPPER
            , Mapper& mapper
#endif
    )
{
#ifdef BOOST_GEOMETRY_DEBUG_WITH_MAPPER
    detail::buffer::buffered_piece_collection_with_mapper
#else
    detail::buffer::buffered_piece_collection
#endif
        <
            typename geometry::ring_type<GeometryOutput>::type
        > collection;

    dispatch::buffer_inserter
        <
            typename tag<GeometryInput>::type,
            GeometryInput,
            GeometryOutput
        >::apply(geometry_input, collection, distance_strategy, join_strategy, end_strategy);

    //std::cout << "BEGIN GET TURNS" << std::endl;
    collection.get_turns(geometry_input, distance_strategy);
    //std::cout << "END GET TURNS" << std::endl;

#ifdef BOOST_GEOMETRY_DEBUG_WITH_MAPPER
    //collection.map_offsetted(mapper);
    //collection.map_offsetted_points(mapper);
    collection.map_turns(mapper);
    //collection.map_opposite_locations(mapper);
#endif

    collection.discard_rings();
    collection.discard_turns();
    collection.enrich();
    collection.traverse();

#ifdef BOOST_GEOMETRY_DEBUG_WITH_MAPPER
    //collection.map_turns(mapper);
    collection.map_pieces<geometry::polygon_tag>(mapper); //, false, true);
    //collection.map_traverse(mapper);
#endif

    collection.template assign<GeometryOutput>(out);
}

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_INSERTER_HPP


#include <iostream>
#include <fstream>

#include "XercesUtils.hpp"
#include "LoadOSM.hpp"

#include "OSMDatabase.hpp"
#include "BasicRelationFeatureFactory.hpp"
#include "BasicWayFeatureFactory.hpp"

#include <boost/archive/binary_oarchive.hpp>

#include <cairomm/context.h>
#include <cairomm/surface.h>

#include <array>

using namespace std;

#include "MultipolyCloser.hpp"

const std::vector<std::array<float,3>> colours{
	std::array<float,3>{ 1.0f, 0.0f, 0.0f },
	std::array<float,3>{ 0.0f, 1.0f, 0.0f },
	std::array<float,3>{ 0.0f, 0.0f, 1.0f },
	std::array<float,3>{ 0.0f, 1.0f, 1.0f },
	std::array<float,3>{ 1.0f, 0.0f, 1.0f },
	std::array<float,3>{ 1.0f, 1.0f, 0.0f },
	std::array<float,3>{ 0.5f, 0.0f, 0.0f },
	std::array<float,3>{ 0.0f, 0.5f, 0.0f },
	std::array<float,3>{ 0.0f, 0.0f, 0.5f },
	std::array<float,3>{ 0.0f, 0.5f, 0.5f },
	std::array<float,3>{ 0.5f, 0.0f, 0.5f },
	std::array<float,3>{ 0.5f, 0.5f, 0.0f }
};

#include <boost/math/constants/constants.hpp>

class MidLatProjection
{
public:
	MidLatProjection(LatLon llc) :
		m_llc(llc),
		m_cosphi(llc.lat*boost::math::constants::pi<float>()/180.0f)
	{}

	void scale(float s){ m_scale=s; }

	std::array<double,2> operator()(LatLon p) const
	{
		return std::array<double,2>
		{
			m_scale*(p.lon-m_llc.lon)*m_cosphi,
			m_scale*(p.lat-m_llc.lat)
		};
	}

private:
	LatLon 	m_llc;
	float	m_cosphi;
	float	m_scale=1.0f;
};
void drawPoly(Cairo::RefPtr<Cairo::Context> cr,MidLatProjection proj,const std::vector<LatLon>& poly,bool fill=false);

void drawPolys(Cairo::RefPtr<Cairo::Context> cr,MidLatProjection proj,const std::vector<std::vector<LatLon>>& polys,bool fill=false)
{
	cr->save();
	cr->set_line_width(0.1f);

	for(unsigned i=0;i<polys.size();++i)
		drawPoly(cr,proj,polys[i],fill);
	cr->restore();
}

void drawFeatures(Cairo::RefPtr<Cairo::Context> cr,MidLatProjection proj,const std::vector<Feature>& F)
{
	cr->save();
	cr->set_line_width(0.1f);

	cr->set_source_rgb(0.0f,0.4f,0.6f);
	for(const auto& f : F | boost::adaptors::filtered([](const Feature& f){ return f.type() == Lake || f.type() == River; }))
		drawPoly(cr,proj,f.points(),true);

	cr->set_source_rgb(0.8f,0.6f,0.2f);
	for(const auto& f : F | boost::adaptors::filtered([](const Feature& f){ return f.type() == Island; }))
			drawPoly(cr,proj,f.points(),true);

	cr->set_source_rgb(0.0f,0.4f,0.6f);
	for(const auto& f : F | boost::adaptors::filtered([](const Feature& f){ return f.type() == Stream; }))
		drawPoly(cr,proj,f.points(),false);

	cr->restore();
}

void drawPoly(Cairo::RefPtr<Cairo::Context> cr,MidLatProjection proj,const std::vector<LatLon>& poly,bool fill)
{
	array<double,2> xy;

	if (poly.size() > 0)
	{
		xy = proj(poly.front());
		cr->move_to(xy[0],xy[1]);
		for(const LatLon ll : poly)
		{
			xy = proj(ll);
			cr->line_to(xy[0],xy[1]);
		}
		if (fill)
		{
			cr->close_path();
			cr->fill();
		}
		else
			cr->stroke();
	}
}

int main(int argc,char **argv)
{
	XMLPlatform xmlp;

	string fn("/Users/jcassidy/src/ECE297/maps/hamilton_canada.osm.gz");
	string oFn("lake.pdf");

	if (argc > 1)
		fn=argv[1];

	if (argc > 2)
		oFn = argv[2];

	OSMDatabase db;

	db = loadOSM(fn);

	bool mapIsIsland=true;
	bool mapHasUnboundedLake=false;

	cout << "INFO: Starting on assumption that map is an island" << endl;

	bool image=false;

	////// Setup Cairo canvas

	std::array<unsigned,2> dims;

	if(image)
		dims = array<unsigned,2>{2048,1536};
	else
		dims = array<unsigned,2>{11*72,17*72/2};

	Cairo::RefPtr<Cairo::PdfSurface> surf = Cairo::PdfSurface::create(oFn,dims[0],dims[1]);

	Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(surf);

	cr->save();

	double k;
	std::array<double,2> ddims{(double)dims[0],(double)dims[1]};
	std::array<double,2> offset;

	LatLon llc, urc;

	tie(llc,urc) = db.bounds();

	MidLatProjection proj(llc);
	pair<array<double,2>,array<double,2>> bb;
	bb = make_pair(
			proj(llc),
			proj(urc));

	assert(urc.lon > llc.lon);
	assert(urc.lat > llc.lat);

	double fillfactor=0.9;
	array<double,2> mapsize{bb.second[0]-bb.first[0], bb.second[1]-bb.first[1]};

	k = fillfactor/std::max(mapsize[0]/ddims[0], mapsize[1]/ddims[1]);

	proj.scale(k);

	bb = make_pair(
				proj(llc),
				proj(urc));

	offset = std::array<double,2>{ (ddims[0]-(bb.second[0]-bb.first[0]))*0.5, (ddims[1]-(bb.second[1]-bb.first[1]))*0.5 };

	cout << "  offset=" << offset[0] << ',' << offset[1] << " scale=" << k << endl;

	/** Cairo affine matrix is provided column-major as follows:
	 * [u] 		[	arg0	arg2	arg4]	[ x ]
	 * [v]	=	[	arg1	arg3	arg5]	[ y ]
	 * 										[ 1 ]
	 */

	Cairo::Matrix mat;

	mat = Cairo::Matrix(1,0,0,-1,offset[0],ddims[1]-offset[1]);

	cr->set_matrix(mat);

	cr->set_source_rgb(1.0,1.0,1.0);
	cr->fill();

	bb = make_pair(proj(llc), proj(urc));

	cr->set_source_rgb(0.0f,0.0f,0.0f);

	cr->rectangle(bb.first[0],bb.first[1],bb.second[0]-bb.first[0],bb.second[1]-bb.first[1]);
	cr->stroke();

	{
		array<double,2> uv;
		uv = proj(llc);
		mat.transform_point(uv[0],uv[1]);

		cout << "llc: (" << llc.lat << ',' << llc.lon << ") -> (" << uv[0] << ',' << uv[1] << ")" << endl;

		uv = proj(urc);
		mat.transform_point(uv[0],uv[1]);

		cout << "urc: (" << urc.lat << ',' << urc.lon << ") -> (" << uv[0] << ',' << uv[1] << ")" << endl;
	}

	////// Extract way water features

	vector<Feature> features;

	BasicWayFeatureFactory WF(db);

	for(const auto& w : db.ways())
	{
		auto f = WF(w);
		if (f)
			features.emplace_back(*f);
	}

	////// Extract lake multipolys and draw them

	cr->set_line_width(0.1f);
	cr->set_source_rgb(.0f,0.4f,0.6f);

	BasicRelationFeatureFactory FF(db);

	for(const auto& r : db.relations())
	{
		for(auto& feat : FF(r))
		{
			cout << "  Created feature (" << asString(feat.type()) << ") from relation ID " << r.id() << endl;

			if (!feat.bounded() && feat.isWater())
			{
				mapIsIsland=false;
				mapHasUnboundedLake=true;
				cout << "INFO: Found an unbounded water feature, so we conclude this is not an island" << endl;
			}
			features.emplace_back(std::move(feat));
		}
	}

	vector<const OSMWay*> coastline;
	unsigned m_kiNatural=db.wayTags().getIndexForKeyString("natural");
	unsigned m_viCoastline=db.wayTags().getIndexForValueString("coastline");

	// extract coastlines
	for(const auto& w : db.ways())
		if (w.hasTagWithValue(m_kiNatural,m_viCoastline))
			coastline.push_back(&w);

	cout << "Extracted " << coastline.size() << " ways with coastline tag" << endl;

	MultipolyCloser C(db,coastline);
	C.direction(MultipolyCloser::CCW);
	vector<pair<vector<LatLon>,bool>> F = C.loops(MultipolyCloser::All);

	mapIsIsland &= coastline.size()>0;

	for(const auto& poly : F)
		mapIsIsland &= poly.second;		// if none of the coastlines is unbounded, then we're looking at an island

	if (F.size() == 0)
	{
		cout << "INFO: No coastlines, so I conclude this is not an island" << endl;
		mapIsIsland=false;
	}

	if (mapIsIsland)
		cout << "INFO: There are " << F.size() << " coastline ways, none unbounded so I still think the map is an island" << endl;

	if (mapIsIsland)
	{
		cout << "INFO: Concluded the map is an island for lack of contradictory evidence" << endl;
		features.emplace_back(0,Relation,Lake,"<big ocean>",db.corners());
	}

	for(auto& poly : F)
	{
		if (poly.second)	// was originally closed
			features.emplace_back(0,Way,Island,"<unspecified>",std::move(poly.first));
		else
			features.emplace_back(0,Way,Lake,"<unspecified>",std::move(poly.first));
	}

	drawFeatures(cr,proj,features);
}


//#include <Rcpp.h>
//#include <armadillo>
#include <RcppArmadillo.h>
#include <Rmath.h>
#include <math.h>
#include <iostream>
#include <fstream>
#include <R_ext/Utils.h>
#include "MIMOSA.h"
//#define DEBUG2
//#define NDEBUG
/*
 * 18 parameters
 */
 // [[register]]
RcppExport SEXP fitMCMC(SEXP _stim, SEXP _unstim, SEXP _alphas, SEXP _alphau, SEXP _q, SEXP _z,SEXP _iter, SEXP _burn, SEXP _thin, SEXP _tune,SEXP _outfile, SEXP _filter, SEXP _upper, SEXP _lower, SEXP _bfilter, SEXP _fast, SEXP _exprate, SEXP _pXi){
	BEGIN_RCPP
	ntune=0;
	//TODO add argument to pass the complete list of unstimulated samples.
	using namespace Rcpp;
	using namespace arma;
	using namespace std;
	bool fixed = false;

	RNGScope globalscope;
	//			printf("%f %f %f %f\n",exp(normconstIBeta(15,3000,30,3000)),(normconstMC(30,3000,15,3000)),(exp(normconstIBeta(3000,30,3000,15))),(normconstMC(3000,15,3000,30)));
	//			exit(0);
	/*
	 * Copy R variables to standard vectors
	 */
	Rcpp::LogicalVector rfilter(_filter);
	std::vector<bool> filter(rfilter.length(),0);
	filter.resize(rfilter.length());
	copy(rfilter.begin(),rfilter.end(),filter.begin());

	FILTER = Rcpp::as<bool> (_bfilter);
	FAST = Rcpp::as<bool> (_fast);
	double UPPER = Rcpp::as<double>(_upper);
	double LOWER = Rcpp::as<double>(_lower);
	bool REJECT = false;
	EXPRATE = Rcpp::as<double>(_exprate);
	//printf("exponential prior rate = %f\n",EXPRATE);

	std::vector< double > alphas = Rcpp::as<std::vector<double> >(_alphas);
	std::vector< double > alphau = Rcpp::as<std::vector<double> 	>(_alphau);
	double q = Rcpp::as<double>(_q);

	std::vector< double > z = Rcpp::as<vector <double> >(_z);
	std::vector< double > stdstim = Rcpp::as < vector < double > > (_stim);
	std::vector< double > stdunstim = Rcpp::as < vector < double > > (_unstim);
  //double pXi = Rcpp::as<double>(_pXi);
  NumericVector pXi(_pXi);
	/*
	 * Wrap R variables in Rcpp objects
	 */
	Rcpp::NumericMatrix const stim(_stim);
	Rcpp::NumericMatrix const unstim(_unstim);
	Rcpp::NumericVector const iter(_iter);
	Rcpp::NumericVector const burn(_burn);
	Rcpp::NumericVector const thin(_thin);
	Rcpp::NumericVector const tune(_tune);

	std::string outfile = Rcpp::as<std::string>(_outfile);

	std::string outfilep(outfile.data());
	outfilep.append("P");

	Rprintf("Creating %s\n",outfile.data());

	FILE* file = fopen(outfile.data(),"w");
	FILE* fileP = fopen(outfilep.data(),"w");
	if(file==NULL|fileP==NULL){
		return(wrap("Can't open file!"));
	}
	/*
	 * Parameters
	 */
	const double NITERS=iter[0];
	const double BURNIN=burn[0];
	const double THINNING=thin[0];
	const double TUNING=tune[0];
	double realitcounter=0;

	/*
	 * Assert that dimensions match
	 */
	if(alphas.size()!=stim.ncol()){
     ::Rf_error( "dimensions don't match");
	}
	if(stim.nrow()!=unstim.nrow()){
     ::Rf_error( "dimensions don't match");
	}
	if(stim.ncol()!=unstim.ncol()){
     ::Rf_error( "dimensions don't match");
	}
	if(alphas.size()!=alphau.size()){
     ::Rf_error( "dimensions don't match");
	}
	/*
	 * Dimensions of the problem
	 */
	const int k = stim.ncol();
	const int P = stim.nrow();

	/*
	 * Output variables
	 */
	std::vector<double> ps(P,0),pu(P,0);
	std::vector <double> llnull(P,0);
	std::vector <double> llresp(P,0);
	std::vector <double> llnullNew(P,0);
	std::vector <double> llrespNew(P,0);

	std::vector <double> stdsum_data_alphas(P*k,0);
	std::vector <double> stdsum_data_alphau(P*k,0);

	arma::vec asi, aui;
	/*
	 * Armadillo objects will store the simulations from the tuning phase
	 * so we can easily compute variances and so forth.
	 */
	arma::mat Ms(int(TUNING),alphas.size());
#ifdef DEBUG2
	Rprintf("init Ms\n");
#endif
	arma::mat Mu(int(TUNING),alphau.size());
#ifdef DEBUG2
	Rprintf("init Mu\n");
#endif

	/*
	 * Precompute and preallocate a few things we'll need
	 */
	std::vector <double> sum_stim_unstim(P*k,0);
	std::transform(stdstim.begin(),stdstim.end(),stdunstim.begin(),sum_stim_unstim.begin(),plus<double>());

	std::vector<double> newalphas(alphas.size(),0);
	newalphas.resize(alphas.size());
	std::vector<double> newalphau(alphau.size(),0);
	newalphau.resize(alphau.size());

	NumericVector sigmas(alphas.size(),10.0);
	NumericVector sigmau(alphau.size(),10.0);

	arma::vec rateS(alphas.size());
#ifdef DEBUG2
	Rprintf("init rateS\n");
#endif
	arma::vec rateU(alphau.size());
#ifdef DEBUG2
	Rprintf("init rateU\n");
#endif
	rateS.fill(RATE);
	rateU.fill(RATE);
	NumericVector accepts(alphas.size(),0.0);
	NumericVector acceptu(alphas.size(),0.0);
	std::vector<double> cll(P,0.0);
	std::vector<double> p(P,0.0);
	std::vector<double> cz(P,0.0);
	std::vector<double> prr(NITERS-BURNIN,0.0);

	double prior=0,newprior=0;
	double oldll=0,newll=0;

	/*
	 * Construct the header string
	 *
	 */
	std::stringstream headers(stringstream::in|stringstream::out);
	std::stringstream headersP(stringstream::in|stringstream::out);

	//write the z's and p's to the second file
	for(int i=0;i<P;i++){
		headersP<<"z."<<i<<"\t";
	}
	for(int i=0;i<P-1;i++){
		headersP<<"ps."<<i<<"\t"<<"pu."<<i<<"\t";
	}
	headersP<<"ps."<<P-1<<"\t"<<"pu."<<P-1<<std::endl;

	for(int i=0;i<alphas.size();i++){
		headers<<"alphas."<<i<<"\t";
	}
	for(int i=0;i<alphau.size();i++){
		headers<<"alphau."<<i<<"\t";
	}
	headers<<"q"<<std::endl;
	//counters
	int iteration=0,j=0;
	/*
	 * Run the MCMC algorithm
	 */

	/*
	 * Initialize the normalizing constants
	 */

	for(iteration = 0; iteration < NITERS; iteration++){
		//::Rprintf("%d ",iteration);
		for(j=0;j<k;j++){
			/*
			 * prior for the current alphas_j
			 */

			prior=::Rf_dexp(alphas[j],1.0/EXPRATE,true);
			std::copy(alphas.begin(),alphas.end(),newalphas.begin());

			//current null marginal log likelihood
			loglikenull(sum_stim_unstim,alphau,llnull,stdsum_data_alphau,P,k);

			//If alternative is greater compute the ratio of normalizing constants for the alternative marginal log likelihood
			if(FILTER&&k==2&&!FAST){
				normalizingConstant(stdstim,stdunstim,alphas,alphau,llresp,P,k);
			}else{
				//otherwise the two sided marginal log likelihood
				loglikeresp(stdstim,alphas,stdunstim,alphau,llresp,stdsum_data_alphas,stdsum_data_alphau,P,k);
			}

			//compute z1*lnull+z2*lresp+prior
			completeLL(z,llnull,llresp,cll,filter,P,k);

			oldll=std::accumulate(cll.begin(),cll.end(),0.0)+prior;
			if(isfinite(oldll)!=1){
         ::Rf_error( "oldll != 1");
			}

			//simulate alphas_j
			//try integral values for alpha_s beta_s
			if(FILTER&&k==2&&!FAST){
				newalphas[j]=ceil(alphaProposal(alphas,sigmas[j]*rateS[j],j));
			}else{
				newalphas[j]=alphaProposal(alphas,sigmas[j]*rateS[j],j);
			}
			if(newalphas[j]>0){
//				if(FILTER&&k==2&&!FAST){
//					newprior=dgeom(newalphas[j],EXPRATE);
//				}else{
					newprior=::Rf_dexp(newalphas[j],1.0/EXPRATE,true);
//				}

				//don't need to recompute the null marginal log likelihood since it doesn't depend on alphas_j
				std::copy(llnull.begin(),llnull.end(),llnullNew.begin());
				if(FILTER&&k==2&&!FAST){
					//compute one sided marginal log likelihood
					normalizingConstant(stdstim,stdunstim,newalphas,alphau,llrespNew,P,k);
				}else{
					//two sided
					loglikeresp(stdstim,newalphas,stdunstim,alphau,llrespNew,stdsum_data_alphas,stdsum_data_alphau,P,k);
				}
				//compute z1*lnull+z2*lresp+prior
				completeLL(z,llnullNew,llrespNew,cll,filter,P,k);
				newll=std::accumulate(cll.begin(),cll.end(),0.0)+newprior;
			}else{
				REJECT=true;
				newll=nan("0");
			}

			//Rprintf("newll - oldll = %f isfinite()=%d\n",newll-oldll,isfinite(newll-oldll));

			if(!REJECT&&newalphas[j]>0&&(::log(Rf_runif(0.0,1.0)) <= (newll-oldll) )&&(!ISNAN(newll-oldll)&&(!ISNAN(newll))&&isfinite(::exp(newll-oldll)))){
#ifdef NDEBUG
				Rprintf("ACCEPTED alphas_%d %f prob: %f newll %f oldll %f\n",j,newalphas[j],::exp(newll-oldll), newll, oldll);
#endif
				accepts[j]=accepts[j]+1;//increment acceptance count
				alphas[j]=newalphas[j]; //new alphas_j is accepted
				oldll=newll-newprior; //save the new complete data log likelihood (minus the prior for alphas_j) so we don't recompute it for the next step
				std::copy(llnullNew.begin(),llnullNew.end(),llnull.begin()); //ditto for the null and alternative marginal log likelihood
				std::copy(llrespNew.begin(),llrespNew.end(),llresp.begin());
			}else{
				oldll=oldll-prior; //reject so just subtract the alpha-specific prior
#ifdef NDEBUG
				Rprintf("REJECTED alphas_%d %f, deltall: %f newll %f oldll %f\n",j,newalphas[j],(newll-oldll),newll, oldll);
				REJECT=false;
#endif
			}
			if(isfinite(oldll)!=1){
         ::Rf_error( "oldll != 1");
			}

			/*
			 * Simulate alphau_j
			 */
			//prior for the current alphau_j
//			if(FILTER&&k==2&&!FAST){
//				prior=dgeom(alphau[j],EXPRATE);
//			}else{
				prior=Rf_dexp(alphau[j],1.0/EXPRATE,true);
//			}
			oldll=oldll+prior;

			//recompute to see if we still have problems with alpha u beta u estimates
//			loglikenull(stdsum_stim_unstim,stdalphau,stdllnullRes,stdsum_data_alphau,P,k); //new null marginal likelihood.
//			if(FILTER&&k==2&&!FAST){
//				normalizingConstant(stdstim,stdunstim,stdalphas,stdalphau,stdllrespRes,P,k); //new responder marginal LL - one sided
//			}else{
//				//two sided
//				loglikeresp(stdstim,stdalphas,stdunstim,stdalphau,stdllrespRes,stdsum_data_alpha,stdsum_data_alphau,P,k);
//			}
//			completeLL(z,stdllnullRes,stdllrespRes,cll,filter,P,k);
//			oldll=std::accumulate(cll.begin(),cll.end(),0.0)+prior;

			if(isfinite(oldll)!=1){
         ::Rf_error( "dimensions don't match");
			}
			//copy the alphau vector to the proposal vector.
			std::copy(alphau.begin(),alphau.end(),newalphau.begin());//copy the current alpha vector to the new alpha vector prior to drawing a sample

			//simulate alphau)j
			if(FILTER&&k==2&&!FAST){
				newalphau[j]=ceil(alphaProposal(alphau,sigmau[j]*rateU[j],j));
			}else{
				newalphau[j]=alphaProposal(alphau,sigmau[j]*rateU[j],j);
			}
			if(newalphau[j]>0){

//				if(FILTER&&k==2&&!FAST){
//					newprior=dgeom(newalphau[j],EXPRATE);
//				}else{
					newprior=Rf_dexp(newalphau[j],1.0/EXPRATE,true);
//				}

				//compute z1*lnull+z2*lresp+prior
				loglikenull(sum_stim_unstim,newalphau,llnullNew,stdsum_data_alphau,P,k); //new null marginal likelihood.
				if(FILTER&&k==2&&!FAST){
					normalizingConstant(stdstim,stdunstim,alphas,newalphau,llrespNew,P,k); //new responder marginal LL - one sided
				}else{
					//two sided
					loglikeresp(stdstim,alphas,stdunstim,newalphau,llrespNew,stdsum_data_alphas,stdsum_data_alphau,P,k);
				}
				//compute z1*lnull+z2*lresp+prior
				completeLL(z,llnullNew,llrespNew,cll,filter,P,k);
				newll=std::accumulate(cll.begin(),cll.end(),0.0)+newprior;
			}else{
				REJECT=true;
				newll=nan("0");
			}

			//	printf("newll - oldll = %f isfinite()=%d\n",newll-oldll,isfinite(newll-oldll));
			if(!REJECT&&newalphau[j]>0&&(log(Rf_runif(0.0,1.0)) <= (newll-oldll) )&&(!ISNAN(newll-oldll)&&(!ISNAN(newll))&&isfinite(::exp(newll-oldll)))){
				//increment acceptance count for alphauj
#ifdef NDEBUG
				Rprintf("ACCEPTED alphau_%d %f, prob ratio %f newll %f oldll %f\n",j,newalphau[j],::exp(newll-oldll),newll, oldll);
#endif
				acceptu[j]=acceptu[j]+1;
				alphau[j]=newalphau[j];//new alphau_j is accepted
				oldll=newll-newprior; //complete data log likelihood (minus the prior)
				//marginal null and alternative log likelihoods for the accepted parameter are saved so we don't have to recompute them
				std::copy(llnullNew.begin(),llnullNew.end(),llnull.begin());
				std::copy(llrespNew.begin(),llrespNew.end(),llresp.begin());
			}else{
				oldll=oldll-prior;
				REJECT=false;
#ifdef NDEBUG
				Rprintf("REJECTED alphau_%d %f, deltall: %f newll %f oldll %f\n",j,newalphau[j],(newll-oldll),newll, oldll);
#endif
			}
			//simulate q (w)
			q=simQ(z,P,k,pXi);
			//simulate z
			simZ(q,llnull,llresp,z,p,filter,P,k); //overwrites the current z. A running average is stored in cz

		}

		/*
		 * If we haven't fixed the step sizes yet..
		 */
		if(!fixed){
			/*
			 * Tuning phase
			 */
			//Fill in the Ms and Mu matrices
			asi = conv_to<arma::vec>::from(alphas);
#ifdef DEBUG2
			Rprintf("conv alphas to asi\n");
#endif
			aui = conv_to<arma::vec>::from(alphau);
#ifdef DEBUG2
			Rprintf("conv alphau to aui\n");
#endif
//print some dimensions
#ifdef DEBUG2
			Rprintf("Ms size is %d rows by %d columns and asi size is %d rows by %d columns\n",Ms.n_rows,Ms.n_cols,asi.n_rows,asi.n_cols);
#endif
			Ms.row(iteration%(int)TUNING) = trans(asi);
#ifdef DEBUG2
			Rprintf("assign asi to Ms.row\n");
#endif
			Mu.row(iteration%(int)TUNING) = trans(aui);
#ifdef DEBUG2
			Rprintf("assign aui to Mu.row\n");
#endif
			//Tuning
			if(((iteration+1) % (int)TUNING)==0){
				ntune=ntune+1;
				//Compute the covariances
				arma::mat covarianceS = arma::cov(Ms);
#ifdef DEBUG2
				Rprintf("compute cov Ms\n");
#endif
				arma::mat covarianceU = arma::cov(Mu);
#ifdef DEBUG2
				Rprintf("compute cov Mu\n");
#endif
				arma::vec dS = arma::sqrt(covarianceS.diag());
#ifdef DEBUG2
				Rprintf("sqrt diag S\n");
#endif
				arma::vec dU = arma::sqrt(covarianceU.diag());
#ifdef DEBUG2
				Rprintf("sqrt diag U\n");
#endif
				//Weight and assign
				//Tweak the acceptance rates
				accepts=accepts/TUNING;
				acceptu=acceptu/TUNING;

//				printf("sigmas: ");
//				for(j=0;j<sigmas.length();j++){
//					printf("%f %f ",sigmas[j],sigmau[j]);
//				}
//				printf("\n Acceptance rates:");
//				for(j = 0; j < accepts.length(); j++){
//					printf("%f %f ",accepts[j],acceptu[j]);
//				}
//				printf("\n");
				Rprintf("Tuning: %d\t",ntune);
				if((ntune<=12&&(Rcpp::any(accepts > UPPER).is_true() || Rcpp::any(acceptu > UPPER).is_true() || Rcpp::any(accepts < LOWER).is_true() || Rcpp::any(acceptu < LOWER).is_true()))){
					for(j=0;j<accepts.length();j++){
						//stimulated
						if((accepts[j]/DEFAULT_RATE) > 1.1 || (accepts[j]/DEFAULT_RATE )<0.8){
							rateS[j]=accepts[j]/DEFAULT_RATE;
						}
						//unstimulated
						if((acceptu[j]/DEFAULT_RATE) > 1.1 || (acceptu[j]/DEFAULT_RATE) < 0.8){
							rateU[j]=acceptu[j]/DEFAULT_RATE;
						}
					}
					for(j=0;j<sigmas.length();j++){
						sigmas[j] = dS(j);
						sigmau[j] = dU(j);
					}

					for(j=0;j<k;j++){
						if(rateS[j]==0){
							R_CheckUserInterrupt();
							rateS[j]=1;
							accepts[j]=0;
							iteration=0;
						}
						if(ISNAN(sigmas[j])||sigmas[j]==0){
							R_CheckUserInterrupt();
							sigmas[j]=1;
							accepts[j]=0;
							iteration=0;
						}
						if(rateU[j]==0){
							R_CheckUserInterrupt();
							rateU[j]=1;
							iteration=0;
							acceptu[j]=0;
						}if(ISNAN(sigmau[j])||sigmau[j]==0){
							R_CheckUserInterrupt();
							sigmau[j]=1;
							iteration=0;
							acceptu[j]=0;
						}
					}

				}else{
					Rprintf("Fixed step size\n");
					Rprintf("sigmas: ");
					for(int j=0;j<sigmas.length();j++){
						Rprintf("%f %f ",sigmas[j],sigmau[j]);
					}
					Rprintf("\n Acceptance rates:");
					for(int j = 0; j < accepts.length(); j++){
						Rprintf("%f %f ",accepts[j],acceptu[j]);
					}
					Rprintf("\n");
					fixed=true;
					iteration = 0;
					//write out the headers for the two data files
					fprintf(file,"%s",headers.str().data());
					fprintf(fileP,"%s",headersP.str().data());
				}
				//reset the acceptance counts
				accepts.fill(0);
				acceptu.fill(0);
			}
		}
		/*
		 * Running average of z1's after the burn-in
		 */

		if((iteration+1)%10000==0){
			R_CheckUserInterrupt();
			if(iteration==0){
				std::copy(p.begin(),p.end(),cz.begin());
			}
			Rprintf("--- Done %i iterations ---\n",(int)iteration+1);
			//			printf("\n Acceptance rates:");
			//			for(int j = 0; j < accepts.length(); j++){
			//				printf("%f %f",accepts[j],acceptu[j]);
			//			}
			//			printf("\n");
		}

		if(iteration>=BURNIN&&iteration%(int)THINNING==0&&fixed){
			realitcounter++;
			for(int j=0;j<p.size();j++){
				double f = realitcounter/(realitcounter+1.0);
				double foo = p[j]/realitcounter;
				cz[j]=(cz[j]+foo)*f;
			}
			//write chain to file
			//write out the z's
			//when the model is two dimensional..
			if(k==2){
				//and the proportions sampled from each model
				sampleP(sum_stim_unstim,stdstim,stdunstim,alphas,alphau,z,ps,pu,P,k);
				for(int obs=0;obs<P;obs++){
					fprintf(fileP,"%f\t", z[obs]);
				  // prr[iteration-BURNIN]+=(1-z[obs]);
				}
				for(int obs=0;obs<P-1;obs++){
					fprintf(fileP,"%f\t%f\t",ps[obs],pu[obs]);
				}
				fprintf(fileP,"%f\t%f\n",ps[P-1],pu[P-1]);
			}
			for(int obs=0;obs<alphas.size();obs++){
				fprintf(file,"%f\t", alphas[obs]);
			}
			for(int obs=0;obs<alphau.size();obs++){
				fprintf(file,"%f\t", alphau[obs]);
			}
			fprintf(file,"%f\n",q);
			// std::cout<<"PRR: "<<prr[iteration-BURNIN]/P<<std::endl;
		}
#ifdef NDEBUG
		Rprintf("alphas: %f %f alphau: %f %f\n",alphas[0],alphas[1],alphau[0],alphau[1]);
#endif
	}
	if(!fixed){
		Rprintf("Failed to set step size. Run a longer chain.\n");
	}

	/*
	 * Close the file and return some stuff to R.
	 */
	fflush(file);fflush(fileP);
	fclose(file);fclose(fileP);
	ntune=0;
	return Rcpp::List::create(
			Rcpp::Named("z") = cz,
			Rcpp::Named("stepsizeS") = sigmas,
			Rcpp::Named("stepsizeU") = sigmau);
	    // Rcpp::Named("prr") = prr);
	END_RCPP
}

/*
 * Null component log likelihood
 * data is the data, alpha is the parameters, output is the result, sum_dat_alphau is data+alpha
 */
void loglikenull(const std::vector<double> &data,const std::vector<double>  &alpha,std::vector<double> &output, std::vector<double> &sum_dat_alphau,int P, int k){
	int i=0,j=0;
	double da=0,a=0;
	a=lkbeta(alpha);
	for(i=0;i<P;i++){
		for(j=0;j<k;j++){
			sum_dat_alphau[j*P+i]=data[j*P+i]+alpha[j];
		}
		da=lkbeta(sum_dat_alphau,i,k,P);
		output[i]=da-a;
	}

}
/*
 * Responder component log-likelihood
 */
void loglikeresp(const std::vector<double>  &stim, const std::vector<double>  &alphas,const  std::vector<double>  &unstim, const std::vector<double>  &alphau,std::vector<double> &output, std::vector<double> &sum_dat_alphas,std::vector<double> &sum_dat_alphau,int P, int k){
	int i=0,j=0;
	double da,db,a,b;
	b=lkbeta(alphau);
	a=lkbeta(alphas);
	for(i=0;i<P;i++){
		for(j=0;j<k;j++){
			sum_dat_alphas[j*P+i]=stim[j*P+i]+alphas[j];
			sum_dat_alphau[j*P+i]=unstim[j*P+i]+alphau[j];
		}
		da=lkbeta(sum_dat_alphas,i,k,P);
		db=lkbeta(sum_dat_alphau,i,k,P);
		output[i]=da+db-a-b;
	}
}

/*
 * K-dimensional Beta function
 */
inline double lkbeta(const std::vector<double>& alpha,int I,int k,int P){
	double sum_alpha=0;
	double sum_log_gamma_alpha=0;
	double log_gamma_sum_alpha=0;
	for(int j = 0;j<k;j++){
		sum_alpha=sum_alpha+alpha[I+j*P];
		sum_log_gamma_alpha = sum_log_gamma_alpha+lgamma(alpha[j*P+I]);
	}
	log_gamma_sum_alpha = lgamma(sum_alpha);
	return sum_log_gamma_alpha-log_gamma_sum_alpha;
}

inline double lkbeta(const std::vector<double> &alpha){
	double sum_alpha = std::accumulate(alpha.begin(),alpha.end(),0.0);
	double log_gamma_sum_alpha = lgamma(sum_alpha);
	double sum_log_gamma_alpha=0;
	for(int i=0;i<alpha.size();i++){
		sum_log_gamma_alpha=sum_log_gamma_alpha+lgamma(alpha[i]);
	}
	return sum_log_gamma_alpha-log_gamma_sum_alpha;
}

/*
 * log-gamma function for use with std::transform
 */
double op_lgamma(double i){
	return lgamma(i);
}

/*
 * Draw a proposal for the ith component of an alpha vector
 */
double alphaProposal(const std::vector<double> &alpha, double sigma, int i){
	double na;
	na = ::Rf_rnorm(alpha[i],sigma);
	return na;
}
double alphaDiscreteProposal(const std::vector<double> &alpha, double d, int i){
	double na;
	d=round(std::abs(d));
	if(d<1){
    ::Rf_error( "d is < 1");
	}
	na = ::Rf_runif(-d,d);
	na=alpha[i]+na;
	return na;
}

/*
 * Compute the complete data log-likelihood
 * If FILTER is true, we check each index against the value of filter, and set the posterior probability to zero for FILTER_j = true
 */
void completeLL(std::vector<double> &z,std::vector<double> &lnull, std::vector<double> &lresp,std::vector<double> &cll,std::vector<bool> &filter,int P, int k){
	int i;
	for(i=0;i< P;i++){
		if(FAST&filter[i]){
			z[i+P]=0.0;z[i]=1.0;
		}
		cll[i] = z[i]*lnull[i]+z[i+P]*(lresp[i]);
	}
}
void simZ(double &q,std::vector<double> &lnull, std::vector<double> &lresp,std::vector<double>& z,std::vector<double> &p,std::vector<bool> &filter,int P, int k){
	int i;
	double lq = ::log(q);
	double mlq = ::log(1.0-q);
	for(i=0;i < lnull.size(); i++){
		lnull[i]=lnull[i]+lq;
		lresp[i]=lresp[i]+mlq;
		double mx=std::max(lnull[i],lresp[i]);
		//printf("null:%f\talternative: %f\n",lnull[i],lresp[i]);
		if(FAST&filter[i]){
			p[i]=1;
		}else{
			p[i] = ::exp(lnull[i]-::log(::exp(lnull[i]-mx)+::exp(lresp[i]-mx))-mx);
			z[i] = ::Rf_rbinom(1.0,p[i]);
			z[i+P] = 1.0-z[i];
		}
	}
}
//inline double simQ(std::vector<double> &z, int P,int k,double pXi){
inline double simQ(std::vector<double> &z, int P,int k, NumericVector pXi){
	std::vector<double> ab(2,0);
	double q;
	for(int j=0;j<2;j++){
		for(int i=0;i<P;i++){
			ab[j]=ab[j]+z[j*P+i];
		}
	}
	q = 1.0-::Rf_rbeta(ab[1]+pXi[0],ab[0]+pXi[1]);
	return q;
}

void normalizingConstant(std::vector<double> &stim,std::vector<double> &unstim,std::vector<double> &alphas,std::vector<double> &alphau,std::vector<double> &llresp, int P,int k){
	if(k!=2){
     ::Rf_error( "k!=2");
	}
	double numerator=0,denominator=0,nummc,denommc;
	double C=1,CC=1;
	//If any alphas are <= 0 fill with nan;
	std::vector<double> u(2,0), s(2,0);
	int i=0,j=0;
	for(i=0;i<P;i++){
		for(j=0;j<2;j++){
			s[j]=stim[i+j*P]+alphas[j];
			u[j]=unstim[i+j*P]+alphau[j];
		}
		//alphas[1] is alpha, alphas[0] is beta
		//data+hyperparameters
		//Rprintf("s1 %f, s0 %f, u1 %f u0 %f\n",s[1],s[0],u[1],u[0]);
		//Rprintf("alphas %f, betas %f, alphau %f betau %f\n",alphas[1],alphas[0],alphau[1],alphau[0]);

		numerator=normconstIBeta((double)s[0],(double)s[1],(double)u[0],(double)u[1]);
		//hyperparameters only
		denominator=normconstIBeta((double)alphas[0],(double)alphas[1],(double)alphau[0],(double)alphau[1]);
		//					nummc = normconstMC((double)s[1],(double)s[0],(double)u[1],(double)u[0]);
		//					denommc = normconstMC((double)alphas[1],(double)alphas[0],(double)alphau[1],(double)alphau[0]);
		//	Rprintf("%f/%f\n",nummc,denommc);
		//	Rprintf("%f  %f  %f\n",log(nummc),log(denommc),log(nummc)-log(denommc));

		//Rprintf("numerator: %f, denominator %f\n",exp(numerator), exp(denominator));
		CC=numerator-denominator;
		//printf("alphas %f betas %f alphau %f betau %f\n",alphas[1], alphas[0], alphau[1],alphau[0]);

		//		C=log(nummc)-log(denommc);
		//printf("%d.\t%f\t%f\n",i,nummc,denommc);
		//	printf("C: %f CC: %f  diff: %f\n",C,CC, C-CC);
		//double K=lgamma((double)s[1])+lgamma((double)s[0])-lgamma(s[1]+s[0])+lgamma((double)u[1])+lgamma((double)u[0])-lgamma(u[1]+u[0])-lgamma((double)alphas[1])-lgamma((double)alphas[0])+lgamma(alphas[1]+alphas[0])-lgamma((double)alphau[1])-lgamma((double)alphau[0])+lgamma(alphau[1]+alphau[0]);
		double K = ::Rf_lbeta((double) s[1],(double) s[0])+::Rf_lbeta((double) u[1], (double) u[0])-::Rf_lbeta((double) alphas[1],(double)alphas[0])-::Rf_lbeta((double)alphau[1],(double)alphau[0]);
		if(ISNAN(CC)){
			//			printf("s0: %f s1: %f u0: %f u1: %f as0: %f as1: %f au0: %f au1: %f \n",s[0],s[1],u[0],u[1],alphas[0],alphas[1],alphau[0],alphau[1]);
			//			printf("numerator: %f  denominator %f  C: %f\n",numerator,denominator,numerator-denominator);
			//			printf("log(1-exp(C))=%f\n",log(1-exp(numerator-denominator)));
			nummc = (normconstMC((double)s[1],(double)s[0],(double)u[1],(double)u[0]));
			denommc = (normconstMC((double)alphas[1],(double)alphas[0],(double)alphau[1],(double)alphau[0]));
			C=log(nummc)-log(denommc);
			CC=C;
		}
		//		printf("mc: %f acpprox: %f difference %f\n",C+K,K+CC,C-CC);
		//Rprintf("C:%f\n",exp(CC));
		//Rprintf("ll:%f\n\n",K+CC);

		llresp[i]=K+CC;
		//printf("%d.\t%f\n",i,llresp[i]);
	}
}

double normconstMC(double as, double bs, double au, double bu){
	double res;
	NumericVector r = rbeta(1000,au,bu);
	r=pbeta(r,as,bs,false,false);
	res = std::accumulate(r.begin(),r.end(),0.0)/r.length();
	return res;
}

//estimate_logZus_int<-function(alpha.u,beta.u,alpha.s,beta.s)
//{
//  if(round(beta.u)!=beta.u) # Test if it's an integer
//  {
//    # Compute for integer values around the true value
//    betaU<-beta.u
//    beta.u<-floor(betaU)
//    j<-alpha.u:(alpha.u+beta.u-1)
//    K<- -lbeta(alpha.s,beta.s)+lfactorial(alpha.u+beta.u-1)+lbeta(j+alpha.s,alpha.u+beta.u-1-j+beta.s)-lfactorial(j)-lfactorial(alpha.u+beta.u-1-j)
//    I1<-sum(exp(K))
//
//    beta.u<-ceiling(betaU)
//    j<-alpha.u:(alpha.u+beta.u-1)
//    K<- -lbeta(alpha.s,beta.s)+lfactorial(alpha.u+beta.u-1)+lbeta(j+alpha.s,alpha.u+beta.u-1-j+beta.s)-lfactorial(j)-lfactorial(alpha.u+beta.u-1-j)
//    I2<-sum(exp(K))
//
//    # Interpolation
//    I<-(ceiling(betaU)-betaU)*I1+(betaU-floor(betaU))*I2
//  }
//  else
//  {
//  j<-alpha.u:(alpha.u+beta.u-1)
//  K<- -lbeta(alpha.s,beta.s)+lfactorial(alpha.u+beta.u-1)+lbeta(j+alpha.s,alpha.u+beta.u-1-j+beta.s)-lfactorial(j)-lfactorial(alpha.u+beta.u-1-j)
//  I<-sum(exp(K))
//  }
//  return(I)
//}

double normconstIBeta(double au, double bu, double as, double bs){
	double alphau = round(au);
	double betaU = round(bu);
	double alphas = as;
	double betas = bs;
	double mx;
	std::vector<double> upper((int)betaU,0.0);
	double sum=0;
	double K=-Rf_lbeta(alphas,betas)+lgamma(alphau+betaU);
	//printf("summation length: %d\n",betaU);
	for(int j=alphau;j<=alphau+betaU-1;j++){
		upper[j-alphau]=Rf_lbeta(j+alphas,alphau+betaU-1-j+betas)-lgamma(j+1)-lgamma(alphau+betaU-j)+K;
	}
	std::vector<double>::iterator where=std::max_element(upper.begin(),upper.end());
	mx = *where;
	for(int i=0;i<upper.size();i++){
		sum=sum+::exp(upper[i]-mx);
	}
	sum=log(sum)+mx;
	return sum;
}
//double normconstIBeta(double as, double bs, double au, double bu){
//	double alphas = as;
//	double betas = bs;
//	double alphau = (double)ceil(au);
//	double betau = (double)ceil(bu);
//	//moved the test for negative proposal outside to the main loop
//	//	if(alphas<=0||betas<=0||alphau<=0||betau<=0){
//	//		return 0.0/0.0; //if any parameters are negative return nan.
//	//	}
//	double sum=0,mx=0;
//	double upper = (double) (alphau+betau);
//	std::vector<double> res((int)betau,0.0);
//	//	printf("as=%f bs=%f au=%f bu=%f\n",as,bs,au,bu);
//	//	printf("INTS: as=%f bs=%f au=%f bu=%f\n",alphas,betas,alphau,betau);
//	//	printf("upper: %d, alphau: %d size: %d\n",upper, alphau,res.size());
//	double K = -::Rf_lbeta(alphas,betas)+Rf_lgammafn(alphau+betau)-Rf_lgammafn(alphas+betas+alphau+betau-1);
//#ifdef FOO
//	printf("upper=%f\n",upper);
//	printf("K=%f\n",K);
//	printf("-::Rf_lbeta(alphas,betas)=%f\n",-::Rf_lbeta(alphas,betas));
//	printf("Rf_lgammafn(alphau+betau)=%f\n",Rf_lgammafn(alphau+betau));
//	printf("-Rf_lgammafn(alphas+betas+alphau-1)=%f\n",-Rf_lgammafn(alphas+betas+alphau-1));
//#endif
//	for(int j = (int)alphau;j<=((int)upper-1);j++){
//#ifdef FOO
//		printf("j=%d\n",j);
//		printf("Rf_lgammafn(alphas+j)=%f\n",Rf_lgammafn(alphas+j));
//		printf("Rf_lgammafn(alphau+betau+betas-1)=%f\n",Rf_lgammafn(alphau+betau+betas-j-1));
//		printf("-Rf_lgammafn(j+1)=%f\n",-Rf_lgammafn(j+1));
//		printf("-Rf_lgammafn(alphau+betau-j)=%f\n",-Rf_lgammafn(alphau+betau-j));
//		printf("sm = %f\n",K+Rf_lgammafn(alphas+j)+Rf_lgammafn(alphau+betau+betas-j-1)-Rf_lgammafn(j+1)-Rf_lgammafn(alphau+betau-j));
//#endif
//		res[j-(int)alphau]=K+Rf_lgammafn(alphas+j)+Rf_lgammafn(alphau+betau+betas-j-1)-Rf_lgammafn(j+1)-Rf_lgammafn(alphau+betau-j);
//	}
//
//	//todo normalized sum of exponentials
//	std::vector<double>::iterator where=std::max_element(res.begin(),res.end());
//	mx = *where;
//	for(int i=0;i<res.size();i++){
//		sum=sum+::exp(res[i]-mx);
//	}
//	sum=log(sum)+mx;
//	//printf("%f \n",sum);
//	return(sum);
//}

//samples P's for the 2-d case only.
void sampleP(std::vector<double>& sumdata,std::vector<double>& stim,std::vector<double>& unstim,std::vector<double>& alphas,std::vector<double>& alphau,std::vector<double>& z, std::vector<double> &ps, std::vector<double> &pu, int P,int k){
	for(int i=0;i<P;i++){
		if(z[i+P]==0){
			//sample from the null model
			ps[i]=Rf_rbeta(sumdata[i+1*P]+alphas[1]+alphau[1],sumdata[i+0*P]+alphas[0]+alphau[0]);
			pu[i]=ps[i];
		}else{
			//otherwise sample from the responder model
			ps[i]=Rf_rbeta(stim[i+1*P]+alphas[1],stim[i+0*P]+alphas[0]);
			pu[i]=Rf_rbeta(unstim[i+1*P]+alphau[1],unstim[i+0*P]+alphau[0]);
		}
	}
}

double nc(double as, double bs, double au,double bu,double B){
	double K,mx,sm=0;
	std::vector<double> s(B+1,0);
	K=::Rf_lbeta(au+as,bu+bs)-::log(au);
	s[0]=K;
	for(int i=0;i<(s.size()-1);i++){
		s[i+1]=(Rf_lbeta(au+1,i+1)+Rf_lbeta(au+as+i+1,bu+bs)-Rf_lbeta(au+bu,i+1)-log(au));
	}
	std::vector<double>::iterator where=std::max_element(s.begin(),s.end());
	mx=*where;
	for(int i=0;i<s.size();i++){
		s[i]=::exp(s[i]-mx);
		sm=sm+s[i];
	}
	sm=log(sm)+mx;
	return(sm);
}

double dgeom(int k,double p){
	if(k<1){
   ::Rf_error( "k<1");
	}
	if(p<0||p>1){
   ::Rf_error( "p is not between 0 and 1");
	}
	double olp=log(1-p);
	double lp=log(p);
	double res=olp*(k-1)+lp;
	return res;
}


#ifndef ASLAM_BACKEND_DV_MATRIX_HPP
#define ASLAM_BACKEND_DV_MATRIX_HPP

#include <aslam/backend/JacobianContainer.hpp>
#include <boost/shared_ptr.hpp>
#include <set>

namespace aslam {
  namespace backend {

    /**
     * \class MatrixExpressionNode
     * \brief The superclass of all classes representing transformations.
     */
    class MatrixExpressionNode
    {
    public:
      EIGEN_MAKE_ALIGNED_OPERATOR_NEW

      MatrixExpressionNode();
      virtual ~MatrixExpressionNode();

      /// \brief Evaluate the transformation matrix.
      Eigen::Matrix3d toFullMatrix();

      /// \brief Evaluate the Jacobians
      void evaluateJacobians(JacobianContainer & outJacobians) const;

      /// \brief Evaluate the Jacobians and apply the chain rule.
      void evaluateJacobians(JacobianContainer & outJacobians, const Eigen::MatrixXd & applyChainRule) const;
      void getDesignVariables(DesignVariable::set_t & designVariables) const;
    protected:
      // These functions must be implemented by child classes.
      virtual Eigen::Matrix3d toFullMatrixImplementation() = 0;
      virtual void evaluateJacobiansImplementation(JacobianContainer & outJacobians) const = 0;
      virtual void evaluateJacobiansImplementation(JacobianContainer & outJacobians, const Eigen::MatrixXd & applyChainRule) const = 0;
      virtual void getDesignVariablesImplementation(DesignVariable::set_t & designVariables) const = 0;

    };

    /**
     * \class MatrixExpressionNodeMultiply
     *
     * \brief A class representing the multiplication of two transformation matrices.
     *
     */
    class MatrixExpressionNodeMultiply : public MatrixExpressionNode
    {
    public:
      EIGEN_MAKE_ALIGNED_OPERATOR_NEW

      MatrixExpressionNodeMultiply(boost::shared_ptr<MatrixExpressionNode> lhs,
				     boost::shared_ptr<MatrixExpressionNode> rhs);
      virtual ~MatrixExpressionNodeMultiply();

    private:
      virtual Eigen::Matrix3d toFullMatrixImplementation();
      virtual void evaluateJacobiansImplementation(JacobianContainer & outJacobians) const;
      virtual void evaluateJacobiansImplementation(JacobianContainer & outJacobians, const Eigen::MatrixXd & applyChainRule) const;
      virtual void getDesignVariablesImplementation(DesignVariable::set_t & designVariables) const;

      boost::shared_ptr<MatrixExpressionNode> _lhs;
      Eigen::Matrix3d _A_lhs;
      boost::shared_ptr<MatrixExpressionNode> _rhs;
      Eigen::Matrix3d _A_rhs;
    };

    /**
     * \class MatrixExpressionNodeInverse
     *
     * \brief A class representing the inverse of a transformation matrix.
     *
     */
    class MatrixExpressionNodeInverse : public MatrixExpressionNode
    {
    public:
    	MatrixExpressionNodeInverse(boost::shared_ptr<MatrixExpressionNode> dvTrafo);

      virtual ~MatrixExpressionNodeInverse();

    private:
      virtual Eigen::Matrix3d toFullMatrixImplementation();
      virtual void evaluateJacobiansImplementation(JacobianContainer & outJacobians) const;
      virtual void evaluateJacobiansImplementation(JacobianContainer & outJacobians, const Eigen::MatrixXd & applyChainRule) const;
      virtual void getDesignVariablesImplementation(DesignVariable::set_t & designVariables) const;

      boost::shared_ptr<MatrixExpressionNode> _dvTrafo;
      Eigen::Matrix3d _A;
    };

  } // namespace backend
} // namespace aslam

#endif /* ASLAM_BACKEND_DV_MATRIX_HPP */


#pragma once

#include <iostream>
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <fstream>
#include <string>
#include <array>

using namespace std;
using namespace Eigen;

bool writeToCSV(
	ofstream & outfile_,
	const string & file_name_,
	const array<Isometry3d, 2> & T_list_
);

Vector3d transCoord(
	const Vector3d & pos_,
	const Isometry3d & T_,
	const Matrix3d & Mat_intri_=Matrix3d::Zero()
);

array<Vector3d, 4> getHorPlanePosAnns(const Vector3d& anns_size_);


#ifndef MODEL_SPACE_HPP
#define MODEL_SPACE_HPP

#include <iostream>
#include <utility>

#include <boost/icl/interval_map.hpp>

#include "../utils/Typedefs.hpp"
#include "../utils/DateUtils.hpp"
#include "../utils/IOUtils.hpp"

class Space {
public:

    // Attributes
    SpaceID id;
    Description desc;
    Coordinates coords;
    MaxCap cap;
    SpaceIDList neighbors;

    // Synthetic Data Generation
    boost::icl::interval_map<long, int> occ;

    void insertOccupancy(const DateTime& s, const DateTime& e);
    int getOccupancy(const DateTime& dt) const;
    DateTime getNextOpenTime(const DateTime& dt) const;

    friend std::ostream& operator<<(std::ostream& oss, const Space& c);
};

void Space::insertOccupancy(const DateTime& s, const DateTime& e) {
    occ.add(std::make_pair(
            boost::icl::interval<long>::right_open(s.count(), e.count()),
            1));
}

int Space::getOccupancy(const DateTime& dt) const {
    auto it = occ.find(dt.count());
    return it != occ.end() ? it->second : 0;
}

DateTime Space::getNextOpenTime(const DateTime& dt) const {
    DateTime next{dt};
    const DateTime& last = dt.lastTime();
    while (next <= last) {
        if (cap == -1 || getOccupancy(next) <= cap)
            return next;
        next += Time{1};
    }
    return DateTime{};
}

std::ostream& operator<<(std::ostream& oss, const Space& c) {
    oss << "Space("
        << "id=" << c.id << ", "
        << "desc=\"" << c.desc << "\", "
        << "cap=" << c.cap << ", "
        << "coords=" << c.coords << ", "
        << "neighbors=" << c.neighbors
        << ")";
    return oss;
}

#endif // MODEL_SPACE_HPP


#include <Eigen/Core>
#include "densecrf.h"
#include "densecrf_wrapper.h"

DenseCRFWrapper::DenseCRFWrapper(int W, int H, int nlabels)
: W(W), H(H), m_nlabels(nlabels) {
	m_crf = new DenseCRF2D(W, H, nlabels);
}

DenseCRFWrapper::~DenseCRFWrapper() {
	delete m_crf;
}

int DenseCRFWrapper::npixels() { return W * H; }
int DenseCRFWrapper::nlabels() { return m_nlabels; }

void DenseCRFWrapper::add_pairwise_energy(float w1,
			 float theta_alpha_1, float theta_alpha_2,
			 float theta_betta_1, float theta_betta_2, float theta_betta_3,
			 float w2,
			 float theta_gamma_1, float theta_gamma_2,
			 unsigned char* im) {

	m_crf->addPairwiseGaussian(theta_gamma_1, theta_gamma_2, new PottsCompatibility(w2));
	m_crf->addPairwiseBilateral(theta_alpha_1, theta_alpha_2,
				    theta_betta_1, theta_betta_2, theta_betta_3,
				    im,
				    new PottsCompatibility(w1));
}

void DenseCRFWrapper::set_unary_energy(float* unary_costs_ptr) {
	m_crf->setUnaryEnergy(
		Eigen::Map<const Eigen::MatrixXf>(
			unary_costs_ptr, m_nlabels, W * H)
	);
}

void DenseCRFWrapper::map(int n_iters, int* labels) {
	VectorXs labels_vec = m_crf->map(n_iters);
	for (int i = 0; i < (W * H); ++i)
		labels[i] = labels_vec(i);
}

void DenseCRFWrapper::inference(int n_iters, float* probs_out) {
  MatrixXf probs = m_crf->inference(n_iters);
	for (int i = 0; i < npixels(); ++i)
    for (int j = 0; j < nlabels(); ++j)
      probs_out[i * nlabels() + j] = probs(j, i);
}


#include "catch.hpp"

#include "xyLoc.hpp"
#include "OctileHeuristic.hpp"
#include "ManhattanHeuristic.hpp"
#include "GridMapState.hpp"
#include "GridBranching.hpp"
#include "types.hpp"
#include <boost/filesystem.hpp>
#include "IPathFindingMapReader.hpp"
#include "GridMap.hpp"
#include "MovingAIGridMapReader.hpp"
#include "AStar.hpp"
#include "GridMapGraphConverter.hpp"
#include <cpp-utils/adjacentGraph.hpp>
#include "DijkstraSearchAlgorithm.hpp"
#include "StandardLocationGoalChecker.hpp"
#include "StandardStateExpander.hpp"
#include "GraphState.hpp"
#include "ISolutionPath.hpp"
#include <functional>
#include "utils.hpp"
#include "pathValidators.hpp"

using namespace pathfinding;
using namespace cpp_utils;

SCENARIO("test xyLoc") {

    GIVEN("testing locations") {

		WHEN("location has same x") {

			REQUIRE(xyLoc{5,6}.isAdjacentTo(xyLoc{5,5}));
			REQUIRE(xyLoc{5,6}.isAdjacentTo(xyLoc{5,5}));

			REQUIRE(!xyLoc{5,6}.isAdjacentTo(xyLoc{5,8}));
			REQUIRE(!xyLoc{5,8}.isAdjacentTo(xyLoc{5,6}));
		}

		WHEN("location has same y") {
			REQUIRE(xyLoc{5,6}.isAdjacentTo(xyLoc{4,6}));
			REQUIRE(xyLoc{4,6}.isAdjacentTo(xyLoc{5,6}));

			REQUIRE(!xyLoc{5,6}.isAdjacentTo(xyLoc{3,6}));
			REQUIRE(!xyLoc{3,6}.isAdjacentTo(xyLoc{5,6}));
		}

		WHEN("locations are diagonal") {
			REQUIRE(xyLoc{5,6}.isAdjacentTo(xyLoc{6,7}));
			REQUIRE(xyLoc{6,7}.isAdjacentTo(xyLoc{5,6}));

			REQUIRE(!xyLoc{5,6}.isAdjacentTo(xyLoc{6,8}));
			REQUIRE(!xyLoc{6,8}.isAdjacentTo(xyLoc{5,6}));
		}

		WHEN("nearby locations") {
			critical("output is", xyLoc::getNearbyDiagonalCells(xyLoc{4, 5}, xyLoc{5, 6}));
			REQUIRE(xyLoc::getNearbyDiagonalCells(xyLoc{4, 5}, xyLoc{5, 6}) == std::pair<xyLoc, xyLoc>{xyLoc{4, 6}, xyLoc{5, 5}});
			REQUIRE(xyLoc::getNearbyDiagonalCells(xyLoc{4, 5}, xyLoc{3, 6}) == std::pair<xyLoc, xyLoc>{xyLoc{4, 6}, xyLoc{3, 5}});
			REQUIRE(xyLoc::getNearbyDiagonalCells(xyLoc{4, 5}, xyLoc{3, 4}) == std::pair<xyLoc, xyLoc>{xyLoc{4, 4}, xyLoc{3, 5}});
		}
	}

	GIVEN("testing fetching adjacent cells") {
		xyLoc current{3, 6};

		REQUIRE(current.getAdjacent(Direction::NORTH) == xyLoc{3, 5});
		REQUIRE(current.getAdjacent(Direction::NORTHEAST) == xyLoc{4, 5});
		REQUIRE(current.getAdjacent(Direction::NORTHWEST) == xyLoc{2, 5});
		REQUIRE(current.getAdjacent(Direction::EAST) == xyLoc{4, 6});
		REQUIRE(current.getAdjacent(Direction::WEST) == xyLoc{2, 6});
		REQUIRE(current.getAdjacent(Direction::SOUTH) == xyLoc{3, 7});
		REQUIRE(current.getAdjacent(Direction::SOUTHEAST) == xyLoc{4, 7});
		REQUIRE(current.getAdjacent(Direction::SOUTHWEST) == xyLoc{2, 7});
	}

	GIVEN("testing adjacent cells") {

		WHEN("top left corner") {
			xyLoc loc{0,0};
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTH, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTH, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::EAST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::WEST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHEAST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHWEST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHEAST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHWEST, xyLoc{10,10}) == false);
		}

		WHEN("top right corner") {
			xyLoc loc{10,0};
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTH, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTH, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::EAST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::WEST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHEAST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHWEST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHEAST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHWEST, xyLoc{10,10}) == true);
		}

		WHEN("top corner") {
			xyLoc loc{5,0};
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTH, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTH, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::EAST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::WEST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHEAST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHWEST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHEAST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHWEST, xyLoc{10,10}) == true);
		}

		WHEN("on left") {
			xyLoc loc{0,5};
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTH, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTH, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::EAST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::WEST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHEAST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHWEST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHEAST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHWEST, xyLoc{10,10}) == false);
		}

		WHEN("on right") {
			xyLoc loc{10,5};
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTH, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTH, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::EAST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::WEST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHEAST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHWEST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHEAST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHWEST, xyLoc{10,10}) == true);
		}

		WHEN("bottom left corner") {
			xyLoc loc{0,10};
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTH, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTH, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::EAST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::WEST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHEAST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHWEST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHEAST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHWEST, xyLoc{10,10}) == false);
		}

		WHEN("bottom") {
			xyLoc loc{5,10};
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTH, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTH, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::EAST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::WEST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHEAST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHWEST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHEAST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHWEST, xyLoc{10,10}) == false);
		}

		WHEN("bottom right corner") {
			xyLoc loc{10,10};
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTH, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTH, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::EAST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::WEST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHEAST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHWEST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHEAST, xyLoc{10,10}) == false);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHWEST, xyLoc{10,10}) == false);
		}

		WHEN("center") {
			xyLoc loc{3,5};
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTH, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTH, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::EAST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::WEST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHEAST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::NORTHWEST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHEAST, xyLoc{10,10}) == true);
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTHWEST, xyLoc{10,10}) == true);
		}

		WHEN("other cases") {
			xyLoc loc{4,4};
			REQUIRE(loc.isThereLocationInDirectionOf(Direction::SOUTH, xyLoc{4,4}) == false);
		}
	}

}

SCENARIO("test manhattan") {

    search::ManhattanHeuristic<bool> h{maps::GridBranching::FOUR_CONNECTED};

    REQUIRE(h.getHeuristic(search::GridMapState<bool>{0, 0, xyLoc{10,10}, false}, search::GridMapState<bool>{0, 0, xyLoc{10, 10}, false}) == 0L);
    REQUIRE(h.getHeuristic(search::GridMapState<bool>{0, 0, xyLoc{10,10}, false}, search::GridMapState<bool>{0, 0, xyLoc{10, 0}, false}) == 10L);
    REQUIRE(h.getHeuristic(search::GridMapState<bool>{0, 0, xyLoc{10,10}, false}, search::GridMapState<bool>{0, 0, xyLoc{0, 10}, false}) == 10L);

    REQUIRE(h.getHeuristic(search::GridMapState<bool>{0, 0, xyLoc{100,100}, false}, search::GridMapState<bool>{0, 0, xyLoc{150, 130}, false}) == (50L + 30L));

    REQUIRE(h.getHeuristic(search::GridMapState<bool>{0, 0, xyLoc{100,100}, false}, search::GridMapState<bool>{0, 0, xyLoc{150, 170}, false}) == (50L + 70L));
}

SCENARIO("test validator") {

	maps::MovingAIGridMapReader reader{
		'.', cost_t{100}, color_t::WHITE,
		'T', cost_t::INFTY, color_t::BLACK,
		'@', cost_t::INFTY, color_t::BLACK
	};
	maps::GridMap map{reader.load(boost::filesystem::path{"./square03.map"})};

	/*
	* MAP
	*
	*  .....
	*  ...@@
	*  ..@@.
	*  ...@@
	*  .....
	*
	*/

	maps::GridMapGraphConverter converter{maps::GridBranching::EIGHT_CONNECTED};
	graphs::AdjacentGraph<std::string, xyLoc, cost_t> graph{*converter.toGraph(map)};

	function_t<nodeid_t, nodeid_t> stateToId = [&](auto& n) { return n;};

	GIVEN("optimality") {
		search::GraphSolutionPath<std::string, xyLoc, cost_t> path{graph, GetCost<cost_t>{}};
		path.add(
			graph.idOfVertex(xyLoc{0,0}),
			graph.idOfVertex(xyLoc{1,0}),
			graph.idOfVertex(xyLoc{2,0}),
			graph.idOfVertex(xyLoc{3,0})
		);

		cpp_utils::function_t<nodeid_t, nodeid_t> idMapper = [&](auto id) {return id;};
		validator::checkPathValid(graph, path, idMapper);
		validator::checkIfPathOptimal<std::string, xyLoc, cost_t, graphs::nodeid_t, graphs::nodeid_t>(
			graph,
			graph.idOfVertex(xyLoc{0,0}), graph.idOfVertex(xyLoc{3,0}),
			path,
			GetCost<cost_t>{},
			idMapper
		);

		REQUIRE_THROWS(validator::checkIfPathOptimal<std::string, xyLoc, cost_t, graphs::nodeid_t, graphs::nodeid_t>(
			graph,
			graph.idOfVertex(xyLoc{0,0}), graph.idOfVertex(xyLoc{4,0}),
			path,
			GetCost<cost_t>{},
			stateToId
		));
	}

	GIVEN("suboptimality") {

		WHEN("check optimal path") {
			search::GraphSolutionPath<std::string, xyLoc, cost_t> optimalPath{graph, GetCost<cost_t>{}};
			optimalPath.add(
				graph.idOfVertex(xyLoc{0,0}),
				graph.idOfVertex(xyLoc{1,0}),
				graph.idOfVertex(xyLoc{2,0}),
				graph.idOfVertex(xyLoc{3,0})
			);

			validator::checkIfPathSuboptimalityBound<std::string, xyLoc, cost_t, graphs::nodeid_t, graphs::nodeid_t>(
				2,
				graph,
				graph.idOfVertex(xyLoc{0,0}), graph.idOfVertex(xyLoc{3,0}),
				optimalPath,
				GetCost<cost_t>{},
				stateToId
			);
		}

		WHEN("checking compliant suboptimal path") {
			search::GraphSolutionPath<std::string, xyLoc, cost_t> path{graph, GetCost<cost_t>{}};
			path.add(
				graph.idOfVertex(xyLoc{0,0}),
				graph.idOfVertex(xyLoc{1,1}),
				graph.idOfVertex(xyLoc{2,0}),
				graph.idOfVertex(xyLoc{3,0})
			);

			validator::checkIfPathSuboptimalityBound<std::string, xyLoc, cost_t, graphs::nodeid_t, graphs::nodeid_t>(
				2,
				graph,
				graph.idOfVertex(xyLoc{0,0}), graph.idOfVertex(xyLoc{3,0}),
				path,
				GetCost<cost_t>{},
				stateToId
			);
		}

		WHEN("checking uncompliant suboptimal path") {
			search::GraphSolutionPath<std::string, xyLoc, cost_t> path{graph, GetCost<cost_t>{}};
			path.add(
				graph.idOfVertex(xyLoc{0,0}),
				graph.idOfVertex(xyLoc{1,0}),
				graph.idOfVertex(xyLoc{2,1}),
				graph.idOfVertex(xyLoc{3,0})
			);

			REQUIRE_THROWS(validator::checkIfPathSuboptimalityBound<std::string, xyLoc, cost_t, graphs::nodeid_t, graphs::nodeid_t>(
				1.001,
				graph,
				graph.idOfVertex(xyLoc{0,0}), graph.idOfVertex(xyLoc{3,0}),
				path,
				GetCost<cost_t>{},
				stateToId
			));
		}

		WHEN("checking a path not ending in goal") {

			search::GraphSolutionPath<std::string, xyLoc, cost_t> path{graph, GetCost<cost_t>{}};
			path.add(
				graph.idOfVertex(xyLoc{0,0}),
				graph.idOfVertex(xyLoc{1,0}),
				graph.idOfVertex(xyLoc{2,0})
			);

			REQUIRE_THROWS(validator::checkIfPathSuboptimalityBound<std::string, xyLoc, cost_t, graphs::nodeid_t, graphs::nodeid_t>(
				2,
				graph,
				graph.idOfVertex(xyLoc{0,0}), graph.idOfVertex(xyLoc{3,0}),
				path,
				GetCost<cost_t>{},
				stateToId
			));
		}

		WHEN("checking a path not starting in start") {

			search::GraphSolutionPath<std::string, xyLoc, cost_t> path{graph, GetCost<cost_t>{}};
			path.add(
				graph.idOfVertex(xyLoc{1,0}),
				graph.idOfVertex(xyLoc{2,0}),
				graph.idOfVertex(xyLoc{3,0})
			);

			REQUIRE_THROWS(validator::checkIfPathSuboptimalityBound<std::string, xyLoc, cost_t, graphs::nodeid_t, graphs::nodeid_t>(
				2,
				graph,
				graph.idOfVertex(xyLoc{0,0}), graph.idOfVertex(xyLoc{3,0}),
				path,
				GetCost<cost_t>{},
				stateToId
			));
		}

		WHEN("checking a path jumping an edge") {

			search::GraphSolutionPath<std::string, xyLoc, cost_t> path{graph, GetCost<cost_t>{}};
			path.add(
				graph.idOfVertex(xyLoc{0,0}),
				graph.idOfVertex(xyLoc{1,0}),
				graph.idOfVertex(xyLoc{3,0})
			);

			REQUIRE_THROWS(validator::checkIfPathSuboptimalityBound<std::string, xyLoc, cost_t, graphs::nodeid_t, graphs::nodeid_t>(
				2,
				graph,
				graph.idOfVertex(xyLoc{0,0}), graph.idOfVertex(xyLoc{3,0}),
				path,
				GetCost<cost_t>{},
				stateToId
			));

		}
	}
}


// Strategy.cpp

#include "Strategy.hpp"

#include "../tools/FileReadingUtilities.hpp"
#include "../tools/JSONUtilities.hpp"
#include "../tools/MarketData.hpp"
#include "../tools/AccountData.hpp"
#include "../tools/PreprocessorOptions.hpp"
#include "../tools/SentimentAnalysis.hpp"
#include "../tools/OutputOptions.hpp"

#include <cstddef>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <iostream>

namespace
{
	void debugMessage(const std::string& message, double val=-1)
	{
		if (tools::OutputOptions::isDebug) {
			if (val != -1)
				std::cout << message << " " << val << std::endl;
			else
				std::cout << message << std::endl;
		}
	}

	void verboseDebugMessage(const std::string& message, double val=-1)
	{
		if (tools::OutputOptions::isVerbose) {
			if (val != -1)
				std::cout << message << " " << val << std::endl;
			else
				std::cout << message << std::endl;
		}
	}
}

namespace trading
{
	bool Strategy::shouldBuy(const std::string& symbol)
	{
		// first check some base 'false' conditions
		double cashInAccount = tools::AccountData::getCashBalance();
		double currentPrice = tools::MarketData::getPrices({symbol})[0];

		bool notEnoughMoney = (currentPrice > cashInAccount);
		bool belowReserveMoney = (reserveCash >= cashInAccount);
		bool marketClosed = (!tools::MarketData::isOpen());
		bool alreadyOwnConfiguredAmountOfStocks = (tools::AccountData::getAccountPositions().size() >= stocksToOwn);

		marketOpen = !marketClosed;
		if (!marketClosed && marketOpen == false)
		{
			// i.e. market just opened (last time we checked it was closed)
			updateTodaysSentiment();
		}

		if (notEnoughMoney || belowReserveMoney || marketClosed || alreadyOwnConfiguredAmountOfStocks)
		{
			return false;
		}

		double testsToMeet = getParamValue("buy-num-tests-met");

		// 1. RSI
		if (tools::MarketData::getRSI(symbol) <= getParamValue("buy-RSI-below"))
		{
			::verboseDebugMessage("RSI under set oversold condition");
			testsToMeet--;
		}

		std::vector<double> keyStats = tools::MarketData::getKeyStats(symbol, {"marketcap", "dividendYield", "ytdChangePercent", "day200MovingAvg", "day50MovingAvg"});
		double peRatio = tools::MarketData::getPE(symbol);

		// 2. P/E ratio
		if (peRatio >= getParamValue("buy-PE-greater-than") && peRatio <= getParamValue("buy-PE-less-than"))
		{
			::verboseDebugMessage("P/E within specified ratio.");
			testsToMeet--;
		}

		// 3. Market cap
		if (keyStats[0] >= getParamValue("buy-min-market-cap"))
		{
			::verboseDebugMessage("Maret cap greater than minimum: ", keyStats[0]);
			testsToMeet--;
		}

		// 4. Dividend yield
		if (keyStats[1] >= getParamValue("buy-min-dividend"))
		{
			::verboseDebugMessage("Dividend yield greater than specified minimum: " , keyStats[1]);
			testsToMeet--;
		}

		// 5. YTD percentage change
		if (keyStats[2] >= getParamValue("buy-min-ytd-change"))
		{
			::verboseDebugMessage("YTD percentage change: ", keyStats[2]);
			testsToMeet--;
		}

		// 6. 50-day SMA (if set in config file)
		if ((currentPrice >= keyStats[4]) && priceAbove50SMA)
		{
			::verboseDebugMessage("Price above 50-day MA.");
			testsToMeet--;
		}
		// 7. 200-day SMA (if set in config file)
		if (currentPrice >= keyStats[3] && priceAbove200SMA)
		{
			::verboseDebugMessage("Price above 200-day MA.");
			testsToMeet--;
		}

		// 8. sentiment analysis on news headlines related to stock
		if (getSentiment(symbol) >= sentimentMinScore)
		{
			::verboseDebugMessage("Today's news sentiment score above specified minumum.");
			testsToMeet--;
		}

		return (testsToMeet<=0);
	}

	bool Strategy::shouldSell(const std::string& symbol)
	{
		// first check if market is even open
		if ( ! tools::MarketData::isOpen()) { return false; }

		double testsToMeet = getParamValue("sell-num-tests-met");

		// 1. RSI
		if (tools::MarketData::getRSI(symbol) >= getParamValue("sell-RSI-over"))
		{
			::debugMessage("Should sell because RSI is over set limit of " + std::to_string(getParamValue("sell-RSI-over")));
			testsToMeet--;
		}

		// 2. Profit margin
		double paid = tools::AccountData::getPurchasePrice(symbol);
		if (paid == -1)
		{
			// order has not executed yet so it does not show up in account positions
			return false;
		}

		double currentPrice = tools::MarketData::getPrices({symbol})[0];
		double margin = (currentPrice-paid)/paid;
		if (margin >= getParamValue("sell-profit-margin-over"))
		{
			::debugMessage("Should sell because of profit margin of " + std::to_string(margin));
			testsToMeet--;
		}

		// 3. Loss tolerance
		if (margin <= getParamValue("sell-loss-tolerance"))
		{
			::debugMessage("Should sell because of loss tolerance of " + std::to_string(margin));
			testsToMeet--;
		}
		return (testsToMeet<=0);
	}

	// parse out json strategy parameters from file
	// also store list of stocks to track from config file
	bool Strategy::setup(const std::string& params, const std::string& stocks)
	{
		std::string paramFile = (params.empty()) ? PARAM_CONFIG_FILE : params;
		std::string stockFile = (stocks.empty()) ? WATCHLIST_CONFIG_FILE : stocks;

		if (tools::fileExists(paramFile) && tools::fileExists(stockFile))
		{
			rapidjson::Document strategyJSON = tools::getDOMTree(tools::getWholeFile(paramFile));
			std::vector<std::string> stockLines = tools::getLines(stockFile);

			// parse out sell-when conditions
			parameters.insert(parameters.begin(), std::pair<std::string, double>(SELL_WHEN[0], strategyJSON["sell-when"]["num-tests-met"].GetDouble()));
			parameters.insert(parameters.begin(), std::pair<std::string, double>(SELL_WHEN[1], strategyJSON["sell-when"]["profit-margin-over"].GetDouble()));
			parameters.insert(parameters.begin(), std::pair<std::string, double>(SELL_WHEN[2], strategyJSON["sell-when"]["RSI-over"].GetDouble()));
			parameters.insert(parameters.begin(), std::pair<std::string, double>(SELL_WHEN[3], strategyJSON["sell-when"]["loss-more-than"].GetDouble()));
			std::cout << "SELL-WHEN conditions saved." << std::endl;

			// parse out buy conditions
			parameters.insert(parameters.begin(), std::pair<std::string, double>(BUY_WHEN[0], strategyJSON["buy-when"]["num-tests-met"].GetDouble()));
			parameters.insert(parameters.begin(), std::pair<std::string, double>(BUY_WHEN[1], strategyJSON["buy-when"]["RSI-below"].GetDouble()));
			parameters.insert(parameters.begin(), std::pair<std::string, double>(BUY_WHEN[2], strategyJSON["buy-when"]["min-market-cap-billions"].GetDouble()));
			parameters.insert(parameters.begin(), std::pair<std::string, double>(BUY_WHEN[3], strategyJSON["buy-when"]["PE-greater-than"].GetDouble()));
			parameters.insert(parameters.begin(), std::pair<std::string, double>(BUY_WHEN[4], strategyJSON["buy-when"]["PE-less-than"].GetDouble()));
			parameters.insert(parameters.begin(), std::pair<std::string, double>(BUY_WHEN[5], strategyJSON["buy-when"]["min-dividend-yield"].GetDouble()));
			parameters.insert(parameters.begin(), std::pair<std::string, double>(BUY_WHEN[6], strategyJSON["buy-when"]["min-ytd-change"].GetDouble()));
			std::cout << "BUY-WHEN conditions saved." << std::endl;
			std::cout << "Parameter map filled!" << std::endl;

			// parse out reserve cash and stocks to own
			reserveCash = strategyJSON["min-cash"].GetDouble();
			std::cout << "Reserve cash saved." << std::endl;

			stocksToOwn = strategyJSON["stocks-to-own"].GetUint();
			std::cout << "Stocks to own parsed. " << std::endl;

			// parse out 200 and 50-day SMA indicators
			priceAbove200SMA = strategyJSON["buy-when"]["price-above-200-SMA"].GetBool();
			priceAbove50SMA = strategyJSON["buy-when"]["price-above-50-SMA"].GetBool();

			// news sentiment minimum score
			sentimentMinScore = strategyJSON["buy-when"]["news-sentiment-above"].GetDouble();
			headlines = strategyJSON["headlines"].GetUint();

			// parse out watchlist
			for (std::string& line : stockLines)
			{
				if (line.empty() || line[0] == '#') { continue; }

				std::vector<std::string> lineElements;
				boost::split(lineElements,line,boost::is_any_of(","));
				boost::algorithm::trim(lineElements[0]);

				if (lineElements.size() == 1)
				{
					// just symbol
					Stock s(lineElements[0]);
					watchlist.push_back(s);
				}
				else if (lineElements.size() == 2)
				{
					// symbol, name specified
					boost::algorithm::trim(lineElements[1]);
					Stock s(lineElements[0], lineElements[1]);
					watchlist.push_back(s);
				}
				else
				{
					// symbol, name, industry
					boost::algorithm::trim(lineElements[1]);
					boost::algorithm::trim(lineElements[2]);
					Stock s(lineElements[0], lineElements[1], lineElements[2]);
					watchlist.push_back(s);
				}

				::verboseDebugMessage(lineElements[0]);
			}

			updateTodaysSentiment();

			return true;
		}
		else
		{
			return false;
		}
	}

	void Strategy::updateTodaysSentiment()
	{
		todaysSentiments.clear();

		// fill today's sentiment scores on start-up (avoids rate limiting)
		for (const Stock& s : watchlist)
		{
			std::string latestHeadlines = tools::MarketData::getLatestHeadlines(s.getSymbol(), headlines);
			double score = tools::SentimentAnalysis::getSentimentScore(latestHeadlines);

			::debugMessage("Todays sentiment score for " + s.getSymbol() + ": ",score);

			todaysSentiments.insert(todaysSentiments.begin(), std::pair<std::string, double>(s.getSymbol(), score));
		}
	}

	// are static
	const std::string Strategy::PARAM_CONFIG_FILE = "settings/strategy.config";
	const std::string Strategy::WATCHLIST_CONFIG_FILE = "settings/stocks.config";

	const std::string Strategy::SELL_WHEN[] =
	{"sell-num-tests-met", "sell-profit-margin-over", "sell-RSI-over", "sell-loss-tolerance"};
	const std::string Strategy::BUY_WHEN[] =
	{"buy-num-tests-met", "buy-RSI-below", "buy-min-market-cap", "buy-PE-greater-than", "buy-PE-less-than", "buy-min-dividend", "buy-min-ytd-change"};

	// for linking with static members
	std::map<std::string, double> Strategy::parameters;
	std::map<std::string, double> Strategy::todaysSentiments;

	std::vector<Stock> Strategy::watchlist;

	double Strategy::reserveCash;
	double Strategy::sentimentMinScore;

	size_t Strategy::stocksToOwn;
	size_t Strategy::headlines;

	bool Strategy::priceAbove200SMA;
	bool Strategy::priceAbove50SMA;
	bool Strategy::marketOpen = false;

}


//
//  FrustumRange.hpp
//  PinkTopaz
//
//  Created by Andrew Fox on 11/18/17.
//
//

#ifndef FrustumRange_hpp
#define FrustumRange_hpp

#include "GridIndexer.hpp"
#include "Frustum.hpp"

#include <boost/coroutine2/all.hpp>

namespace FrustumRange {

// Iterate over cells which fall within the specified frustum.
inline void forEachCell(boost::coroutines2::coroutine<glm::ivec3>::push_type &sink,
                        size_t depth,
                        size_t depthOfLeaves,
                        const AABB &box,
                        const Frustum &frustum,
                        const AABB &sliceBoundingBox,
                        const GridIndexer &grid)
{
    if (frustum.boxIsInside(box) &&
        frustum.boxIsInside(sliceBoundingBox)) {

        if (depth == depthOfLeaves) {
            glm::ivec3 point = grid.cellCoordsAtPoint(box.center);
            sink(point);
        } else {
            for (auto &octant : box.octants()) {
                forEachCell(sink, depth+1, depthOfLeaves, octant, frustum,
                            sliceBoundingBox, grid);
            }
        }
    }
}

} // namespace FrustumRange

// Return a range object which can iterate over a frustum within the grid.
inline boost::coroutines2::coroutine<glm::ivec3>::pull_type
slice(const GridIndexer &grid,
      const Frustum &frustum,
      const AABB &sliceBoundingBox)
{
    const auto res = grid.gridResolution();
    const auto bbox = grid.boundingBox();
    assert((res.x == res.y) && (res.x == res.z));
    assert(isPowerOfTwo(res.x));
    return boost::coroutines2::coroutine<glm::ivec3>::pull_type([&](boost::coroutines2::coroutine<glm::ivec3>::push_type &sink){
        FrustumRange::forEachCell(sink, 0, ilog2(res.x), bbox, frustum,
                                  sliceBoundingBox, grid);
    });
}

#endif /* FrustumRange_hpp */


#ifndef BOOST_ASTRONOMY_COORDINATE_BASE_EQUATORIAL_FRAME_HPP
#define BOOST_ASTRONOMY_COORDINATE_BASE_EQUATORIAL_FRAME_HPP

#include <type_traits>
#include <boost/astronomy/coordinate/base_frame.hpp>
#include <boost/astronomy/coordinate/spherical_representation.hpp>
#include <boost/astronomy/coordinate/spherical_coslat_differential.hpp>

namespace boost { namespace astronomy { namespace coordinate {

namespace bu = boost::units;
namespace bg = boost::geometry;

template
<
    typename Representation, typename Differential
>
struct base_equatorial_frame : public base_frame<Representation, Differential>
{
    ///@cond INTERNAL
    BOOST_STATIC_ASSERT_MSG((std::is_base_of
            <spherical_representation<typename Representation::type,
            typename Representation::quantity1, typename Representation::quantity2,
            typename Representation::quantity3>, Representation>::value),
            "argument type is expected to be a spherical_representation class");
    BOOST_STATIC_ASSERT_MSG((std::is_base_of
            <spherical_coslat_differential<typename Differential::type,
            typename Differential::quantity1, typename Differential::quantity2,
            typename Differential::quantity3>, Differential>::value),
            "argument type is expected to be a spherical_coslat_differential class");
    ///@endcond

public:
    //default constructor no initialization
    base_equatorial_frame() {}

    //!constructs object from another representation object
    template <typename OtherRepresentation>
    base_equatorial_frame(OtherRepresentation const& representation_data)
    {
        BOOST_STATIC_ASSERT_MSG((boost::astronomy::detail::is_base_template_of
            <boost::astronomy::coordinate::base_representation, OtherRepresentation>::value),
            "argument type is expected to be a representation class");

        auto temp = make_spherical_representation
            <
                typename Representation::type,
                typename Representation::quantity1,
                typename Representation::quantity2,
                typename Representation::quantity3,
                typename OtherRepresentation::type,
                typename OtherRepresentation::quantity1,
                typename OtherRepresentation::quantity2,
                typename OtherRepresentation::quantity3
            >(representation_data);
        this->data = temp;
    }

    //!constructs object from provided components of representation
    base_equatorial_frame
    (
        typename Representation::quantity1 const& dec,
        typename Representation::quantity2 const& ra,
        typename Representation::quantity3 const& distance
    )
    {
        this->set_dec(dec);
        this->set_ra(ra);
        this->set_distance(distance);
    }

    //!constructs object from provided components of representation and differential
    base_equatorial_frame
    (
        typename Representation::quantity1 const& dec,
        typename Representation::quantity2 const& ra,
        typename Representation::quantity3 const& distance,
        typename Differential::quantity1 const& pm_dec,
        typename Differential::quantity2 const& pm_ra_cosdec,
        typename Differential::quantity3 const& radial_velocity
    ) : base_equatorial_frame(dec, ra, distance)
    {
        this->motion.set_dlat_dlon_coslat_ddist(pm_dec, pm_ra_cosdec, radial_velocity);
    }

    //!constructs object from other representation and differential
    template <typename OtherRepresentation, typename OtherDifferential>
    base_equatorial_frame
    (
        OtherRepresentation const& representation_data,
        OtherDifferential const& differential_data
    )
    {
        BOOST_STATIC_ASSERT_MSG((boost::astronomy::detail::is_base_template_of
            <boost::astronomy::coordinate::base_representation, OtherRepresentation>::value),
            "argument type is expected to be a representation class");

        BOOST_STATIC_ASSERT_MSG((boost::astronomy::detail::is_base_template_of
            <boost::astronomy::coordinate::base_differential, OtherDifferential>::value),
            "argument type is expected to be a differential class");

        auto rep_temp = make_spherical_representation
            <
                typename Representation::type,
                typename Representation::quantity1,
                typename Representation::quantity2,
                typename Representation::quantity3,
                typename OtherRepresentation::type,
                typename OtherRepresentation::quantity1,
                typename OtherRepresentation::quantity2,
                typename OtherRepresentation::quantity3
            >(representation_data);
        this->data = rep_temp;

        auto dif_temp = make_spherical_coslat_differential
            <
                typename Differential::type,
                typename Differential::quantity1,
                typename Differential::quantity2,
                typename Differential::quantity3,
                typename OtherDifferential::type,
                typename OtherDifferential::quantity1,
                typename OtherDifferential::quantity2,
                typename OtherDifferential::quantity3
            >(differential_data);
        this->motion = dif_temp;
    }

    //!returns Declination component of the coordinate
    typename Representation::quantity1 get_dec() const
    {
        return this->data.get_lat();
    }

    //!returns Right Ascension component of the coordinate
    typename Representation::quantity2 get_ra() const
    {
        return this->data.get_lon();
    }

    //!returns distance component of the coordinate
    typename Representation::quantity3 get_distance() const
    {
        return this->data.get_dist();
    }

    //!returns the (dec, ra, dist) in the form of tuple
    std::tuple
    <
        typename Representation::quantity1,
        typename Representation::quantity2,
        typename Representation::quantity3
    > get_dec_ra_dist() const
    {
        return this->data.get_lat_lon_dist();
    }

    //!returns proper motion in Declination
    typename Differential::quantity1 get_pm_dec() const
    {
        return this->motion.get_dlat();
    }

    //!returns proper motion in Right Ascension including cos(dec)
    typename Differential::quantity2 get_pm_ra_cosdec() const
    {
        return this->motion.get_dlon_coslat();
    }

    //!returns radial_velocity
    typename Differential::quantity3 get_radial_velocity() const
    {
        return this->motion.get_ddist();
    }

    //!returns the proper motion in form of tuple including cos(dec)
    std::tuple
    <
        typename Differential::quantity1,
        typename Differential::quantity2,
        typename Differential::quantity3
    > get_pm_dec_ra_radial() const
    {
        return this->motion.get_dlat_dlon_coslat_ddist();
    }

    //!sets value of Declination component of the coordinate
    void set_dec(typename Representation::quantity1 const& dec)
    {
        this->data.set_lat(dec);
    }

    //!sets value of Right Ascension component of the coordinate
    void set_ra(typename Representation::quantity2 const& ra)
    {
        this->data.set_lon(ra);
    }

    //!sets value of distance component of the coordinate
    void set_distance(typename Representation::quantity3 const& distance)
    {
        this->data.set_dist(distance);
    }

    //!sets values of all component of the coordinate
    void set_dec_ra_dist
    (
        typename Representation::quantity1 const& dec,
        typename Representation::quantity2 const& ra,
        typename Representation::quantity3 const& dist
    )
    {
        this->set_dec(dec);
        this->set_ra(ra);
        this->set_distance(dist);
    }

    //!sets the proper motion in Declination
    void set_pm_dec(typename Differential::quantity1 const& pm_dec)
    {
        this->motion.set_dlat(pm_dec);
    }

    //!sets the proper motion in Right Ascension including cos(dec)
    void set_pm_ra_cosdec(typename Differential::quantity2 const& pm_ra_cosdec)
    {
        this->motion.set_dlon_coslat(pm_ra_cosdec);
    }

    //!sets the radial_velocity
    void set_radial_velocity(typename Differential::quantity3 const& radial_velocity)
    {
        this->motion.set_ddist(radial_velocity);
    }

    //!set value of motion including cos(dec)
    void set_pm_dec_ra_radial
    (
        typename Differential::quantity1 const& pm_dec,
        typename Differential::quantity2 const& pm_ra_cosdec,
        typename Differential::quantity3 const& radial_velocity
    )
    {
        this->motion.set_dlat_dlon_coslat_ddist(pm_dec, pm_ra_cosdec, radial_velocity);
    }
}; //base_equatorial_frame

}}} //namespace boost::astronomy::coordinate

#endif // !BOOST_ASTRONOMY_COORDINATE_BASE_EQUATORIAL_FRAME_HPP


/*!
@file
Defines `boost::hana::detail::array`.

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

#ifndef BOOST_HANA_DETAIL_ARRAY_HPP
#define BOOST_HANA_DETAIL_ARRAY_HPP

#include <boost/hana/config.hpp>
#include <boost/hana/detail/algorithm.hpp>
#include <boost/hana/functional/placeholder.hpp>

#include <cstddef>

BOOST_HANA_NAMESPACE_BEGIN namespace detail {
    template <typename N>
    constexpr N factorial(N n) {
        N result = 1;
        while (n != 0)
            result *= n--;
        return result;
    }

    //! @ingroup group-details
    //! A minimal `std::array` with better `constexpr` support.
    //!
    //! We also provide some algorithms from the `constexpr/algorithm.hpp`
    //! header as member functions to make them easier to use in constexpr
    //! contexts, since a `constexpr` `array` can't be mutated in place.
    template <typename T, std::size_t Size>
    struct array {
        T elems_[Size > 0 ? Size : 1];

        constexpr T& operator[](std::size_t n)
        { return elems_[n]; }

        constexpr T const& operator[](std::size_t n) const
        { return elems_[n]; }

        constexpr std::size_t size() const noexcept
        { return Size; }

        constexpr T* begin() noexcept             { return elems_; }
        constexpr T const* begin() const noexcept { return elems_; }
        constexpr T* end() noexcept               { return elems_ + Size; }
        constexpr T const* end() const noexcept   { return elems_ + Size; }

        // Algorithms from constexpr/algorithm.hpp
        constexpr array reverse() const {
            array result = *this;
            detail::reverse(result.begin(), result.end());
            return result;
        }

        template <typename BinaryPred>
        constexpr auto permutations(BinaryPred pred) const {
            array<array<T, Size>, detail::factorial(Size)> result{};
            auto out = result.begin();
            array copy = *this;

            do *out++ = copy;
            while (detail::next_permutation(copy.begin(), copy.end(), pred));

            return result;
        }

        constexpr auto permutations() const
        { return this->permutations(hana::_ < hana::_); }

        template <typename BinaryPred>
        constexpr auto sort(BinaryPred pred) const {
            array result = *this;
            detail::sort(result.begin(), result.end(), pred);
            return result;
        }

        constexpr auto sort() const
        { return this->sort(hana::_ < hana::_); }

        template <typename U>
        constexpr auto iota(U value) const {
            array result = *this;
            detail::iota(result.begin(), result.end(), value);
            return result;
        }
    };

    template <typename T, std::size_t M, typename U, std::size_t N>
    constexpr bool operator==(array<T, M> a, array<U, N> b)
    { return M == N && detail::equal(a.begin(), a.end(), b.begin(), b.end()); }

    template <typename T, std::size_t M, typename U, std::size_t N>
    constexpr bool operator<(array<T, M> a, array<U, N> b) {
        return M < N || detail::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
    }

} BOOST_HANA_NAMESPACE_END

#endif // !BOOST_HANA_DETAIL_ARRAY_HPP