// Copyright (c) 2014-2018 Crown developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <boost/test/unit_test.hpp>

#include "platform/governance-approval-voting.h"
#include "arith_uint256.h"
#include "utiltime.h"
#include "primitives/transaction.h"

static std::ostream& operator<<(std::ostream& os, uint256 value)
{
    return os << value.ToString();
}

namespace
{
    struct TestApprovalVotingFixture
    {
        uint256 agent006{ArithToUint256(0xA006)};
        uint256 agent007{ArithToUint256(0xA007)};
        uint256 agent008{ArithToUint256(0xA008)};

        CTxIn voter1{CTxIn(COutPoint(ArithToUint256(1), 1 * COIN))};
        CTxIn voter2{CTxIn(COutPoint(ArithToUint256(2), 1 * COIN))};

        TestApprovalVotingFixture()
        {
            SetMockTime(525960000);
        }

        ~TestApprovalVotingFixture()
        {
            SetMockTime(0);
        }

        Platform::Vote VoteFor(uint256 candidate, const CTxIn& voter, int64_t time = GetTime())
        {
            return {
                candidate,
                Platform::VoteValue::yes,
                time,
                voter
            };

        }

        Platform::Vote VoteAgainst(uint256 candidate, const CTxIn& voter, int64_t time = GetTime())
        {
            return {
                candidate,
                Platform::VoteValue::no,
                time,
                voter
            };

        }
    };
}

BOOST_FIXTURE_TEST_SUITE(TestApprovalVoting, TestApprovalVotingFixture)

    BOOST_AUTO_TEST_CASE(EmptyRoundIsEmpty)
    {
        auto threshold = 0;
        auto av = Platform::ApprovalVoting(threshold);

        auto agents = av.CalculateResult();

        BOOST_CHECK(agents.empty());
    }

    BOOST_AUTO_TEST_CASE(VoteForOneAgent)
    {
        auto threshold = 0;
        auto av = Platform::ApprovalVoting(threshold);

        av.RegisterCandidate(agent007);
        av.AcceptVote(VoteFor(agent007, voter1));

        auto agents = av.CalculateResult();
        auto expected = std::vector<uint256>{agent007};

        BOOST_CHECK_EQUAL_COLLECTIONS(agents.begin(), agents.end(), expected.begin(), expected.end());
    }

    BOOST_AUTO_TEST_CASE(VoteForUnregisteredCandidate)
    {
        auto threshold = 0;
        auto av = Platform::ApprovalVoting(threshold);

        av.AcceptVote(VoteFor(agent007, voter1));

        auto agents = av.CalculateResult();

        BOOST_CHECK(agents.empty());
    }

    BOOST_AUTO_TEST_CASE(VoteForTwoAgents)
    {
        auto threshold = 0;
        auto av = Platform::ApprovalVoting(threshold);

        av.RegisterCandidate(agent006);
        av.RegisterCandidate(agent007);
        av.RegisterCandidate(agent008);

        av.AcceptVote(VoteFor(agent007, voter1));
        av.AcceptVote(VoteFor(agent008, voter1));

        auto agents = av.CalculateResult();
        auto expected = std::vector<uint256>{agent007, agent008};

        BOOST_CHECK_EQUAL_COLLECTIONS(agents.begin(), agents.end(), expected.begin(), expected.end());
    }

    BOOST_AUTO_TEST_CASE(VoteForTwoAgentsByTwoVoters)
    {
        auto threshold = 0;
        auto av = Platform::ApprovalVoting(threshold);

        av.RegisterCandidate(agent006);
        av.RegisterCandidate(agent007);
        av.RegisterCandidate(agent008);

        av.AcceptVote(VoteFor(agent007, voter1));
        av.AcceptVote(VoteFor(agent008, voter1));

        av.AcceptVote(VoteFor(agent006, voter2));
        av.AcceptVote(VoteFor(agent007, voter2));

        auto agents = av.CalculateResult();
        auto expected = std::vector<uint256>{agent006, agent007, agent008};

        BOOST_CHECK_EQUAL_COLLECTIONS(agents.begin(), agents.end(), expected.begin(), expected.end());
    }

    BOOST_AUTO_TEST_CASE(VoteForAndAgainst)
    {
        auto threshold = 0;
        auto av = Platform::ApprovalVoting(threshold);

        av.RegisterCandidate(agent006);
        av.RegisterCandidate(agent007);
        av.RegisterCandidate(agent008);

        av.AcceptVote(VoteFor(agent007, voter1));
        av.AcceptVote(VoteFor(agent008, voter1));

        av.AcceptVote(VoteAgainst(agent006, voter2));
        av.AcceptVote(VoteAgainst(agent007, voter2));

        auto agents = av.CalculateResult();
        auto expected = std::vector<uint256>{agent008};

        BOOST_CHECK_EQUAL_COLLECTIONS(agents.begin(), agents.end(), expected.begin(), expected.end());
    }

    BOOST_AUTO_TEST_CASE(NonZeroThreshold)
    {
        auto threshold = 1;
        auto av = Platform::ApprovalVoting(threshold);

        av.RegisterCandidate(agent006);
        av.RegisterCandidate(agent007);
        av.RegisterCandidate(agent008);

        av.AcceptVote(VoteFor(agent007, voter1));
        av.AcceptVote(VoteFor(agent008, voter1));

        av.AcceptVote(VoteFor(agent006, voter2));
        av.AcceptVote(VoteFor(agent007, voter2));

        auto agents = av.CalculateResult(); // 006 && 008 have 1 vote, 007 has two votes
        auto expected = std::vector<uint256>{agent007};

        BOOST_CHECK_EQUAL_COLLECTIONS(agents.begin(), agents.end(), expected.begin(), expected.end());
    }

    BOOST_AUTO_TEST_CASE(DoubleVoting)
    {
        auto threshold = 1;
        auto av = Platform::ApprovalVoting(threshold);

        av.RegisterCandidate(agent007);
        av.AcceptVote(VoteFor(agent007, voter1));
        av.AcceptVote(VoteFor(agent007, voter1));

        auto agents = av.CalculateResult();

        BOOST_CHECK(agents.empty());
    }

    BOOST_AUTO_TEST_CASE(ChangeThreshold)
    {
        auto threshold = 0;
        auto av = Platform::ApprovalVoting(threshold);

        av.RegisterCandidate(agent007);
        av.AcceptVote(VoteFor(agent007, voter1));

        BOOST_REQUIRE(!av.CalculateResult().empty());
        av.SetThreshold(1);

        BOOST_CHECK(av.CalculateResult().empty());
    }

    BOOST_AUTO_TEST_CASE(ChangeThresholdFiresNotification)
    {
        auto threshold = 0;
        auto av = Platform::ApprovalVoting(threshold);

        auto fired = false;
        auto OnChanged = [&fired](){ fired = true; };
        av.NotifyResultChange(OnChanged);

        av.SetThreshold(1);

        BOOST_CHECK(fired);
    }

    BOOST_AUTO_TEST_CASE(AcceptVoteFiresNotification)
    {
        auto threshold = 0;
        auto av = Platform::ApprovalVoting(threshold);

        auto fired = false;
        auto OnChanged = [&fired](){ fired = true; };
        av.NotifyResultChange(OnChanged);

        av.RegisterCandidate(agent007);
        av.AcceptVote(VoteFor(agent007, voter1));

        BOOST_CHECK(fired);
    }

    BOOST_AUTO_TEST_CASE(RejectVoteDoesntFireNotification)
    {
        auto threshold = 0;
        auto av = Platform::ApprovalVoting(threshold);

        auto fired = false;
        auto OnChanged = [&fired](){ fired = true; };
        av.NotifyResultChange(OnChanged);

        av.AcceptVote(VoteFor(agent007, voter1));

        BOOST_CHECK(!fired);
    }

BOOST_AUTO_TEST_SUITE_END()


#include "util/bearing.hpp"
#include "util/typedefs.hpp"

#include <boost/functional/hash.hpp>
#include <boost/test/test_case_template.hpp>
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(bearing_test)

using namespace osrm;
using namespace osrm::util;

// Verify that the bearing-bounds checking function behaves as expected
BOOST_AUTO_TEST_CASE(bearing_range_test)
{
    // Simple, non-edge-case checks
    BOOST_CHECK_EQUAL(true, bearing::CheckInBounds(45, 45, 10));
    BOOST_CHECK_EQUAL(true, bearing::CheckInBounds(35, 45, 10));
    BOOST_CHECK_EQUAL(true, bearing::CheckInBounds(55, 45, 10));

    BOOST_CHECK_EQUAL(false, bearing::CheckInBounds(34, 45, 10));
    BOOST_CHECK_EQUAL(false, bearing::CheckInBounds(56, 45, 10));

    BOOST_CHECK_EQUAL(false, bearing::CheckInBounds(34, 45, 10));
    BOOST_CHECK_EQUAL(false, bearing::CheckInBounds(56, 45, 10));

    // When angle+limit goes > 360
    BOOST_CHECK_EQUAL(true, bearing::CheckInBounds(359, 355, 10));

    // When angle-limit goes < 0
    BOOST_CHECK_EQUAL(true, bearing::CheckInBounds(359, 5, 10));
    BOOST_CHECK_EQUAL(false, bearing::CheckInBounds(354, 5, 10));
    BOOST_CHECK_EQUAL(false, bearing::CheckInBounds(16, 5, 10));

    // Checking other cases of wraparound
    BOOST_CHECK_EQUAL(true, bearing::CheckInBounds(359, -5, 10));
    BOOST_CHECK_EQUAL(false, bearing::CheckInBounds(344, -5, 10));
    BOOST_CHECK_EQUAL(false, bearing::CheckInBounds(6, -5, 10));

    BOOST_CHECK_EQUAL(true, bearing::CheckInBounds(-1, 5, 10));
    BOOST_CHECK_EQUAL(false, bearing::CheckInBounds(-6, 5, 10));

    BOOST_CHECK_EQUAL(true, bearing::CheckInBounds(-721, 5, 10));
    BOOST_CHECK_EQUAL(true, bearing::CheckInBounds(719, 5, 10));

    BOOST_CHECK_EQUAL(false, bearing::CheckInBounds(1, 1, -1));
    BOOST_CHECK_EQUAL(true, bearing::CheckInBounds(1, 1, 0));
}

BOOST_AUTO_TEST_SUITE_END()


/* Copyright (c) 2016 - 2020, the adamantine authors.
 *
 * This file is subject to the Modified BSD License and may not be distributed
 * without copyright and license information. Please refer to the file LICENSE
 * for the text and further information on this license.
 */

#ifndef GEOMETRY_TEMPLATES_HH
#define GEOMETRY_TEMPLATES_HH

#include <Geometry.hh>
#include <instantiation.hh>
#include <types.hh>
#include <utils.hh>

#include <deal.II/grid/filtered_iterator.h>
#include <deal.II/grid/grid_generator.h>

namespace adamantine
{

template <int dim>
Geometry<dim>::Geometry(MPI_Comm const &communicator,
                        boost::property_tree::ptree const &database)
    : _triangulation(communicator)
{
  std::vector<unsigned int> repetitions(dim);
  repetitions[0] = database.get("length_divisions", 10);
  repetitions[1] = database.get("height_divisions", 10);
  if (dim == 3)
    repetitions[2] = database.get("width_divisions", 10);

  dealii::Point<dim> p1;
  dealii::Point<dim> p2;
  p2[0] = database.get<double>("length");
  p2[1] = database.get<double>("height");
  _max_height = p2[1];
  if (dim == 3)
    p2[2] = database.get<double>("width");

  // For now we assume that the geometry is very simple.
  dealii::GridGenerator::subdivided_hyper_rectangle(_triangulation, repetitions,
                                                    p1, p2, true);

  // Assign the MaterialID and the MaterialState.
  dealii::types::boundary_id const top_boundary = 3;
  for (auto cell : _triangulation.active_cell_iterators())
  {
    cell->set_material_id(0);
    if (cell->at_boundary())
    {
      bool is_powder = false;
      for (unsigned int i = 0; i < dealii::GeometryInfo<dim>::faces_per_cell;
           ++i)
      {
        if ((cell->face(i)->at_boundary()) &&
            (cell->face(i)->boundary_id() == top_boundary))
        {
          cell->set_user_index(static_cast<int>(MaterialState::powder));
          is_powder = true;
          break;
        }
      }
      if (is_powder == false)
        cell->set_user_index(static_cast<int>(MaterialState::solid));
    }
    else
      cell->set_user_index(static_cast<int>(MaterialState::solid));
  }
}
} // namespace adamantine

INSTANTIATE_DIM(Geometry)

#endif


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

#include "cam.h"
#include "offset.h"
#include <boost/polygon/voronoi.hpp>

using namespace FlexScan;
using namespace cam;
using namespace std;

template<typename Derived>
struct VoronoiEdge {
    struct Index {
        PointWithZ point;
        PointWithZ otherPoint;
        Derived* edge;
        bool isPoint2;
        bool taken = false;

        Index() = default;
        Index(PointWithZ point, PointWithZ otherPoint = {}, bool isPoint2 = false, Derived* edge = nullptr) :
            point{point},
            otherPoint{otherPoint},
            edge{edge},
            isPoint2{isPoint2}
        {
        }

        Index(const Index&) = default;
        Index& operator=(const Index&) = default;

        bool operator<(const Index& rhs) const
        {
            return this->point.x < rhs.point.x || this->point.x == rhs.point.x && this->point.y < rhs.point.y;
        }
    };

    bool isGeometry = false;
    bool isInGeometry = false;
    bool taken = false;
    Index* index1 = nullptr;
    Index* index2 = nullptr;
    size_t sourceIndex = 0;

    void setTaken() {
        taken = true;
        index1->taken = true;
        index2->taken = true;
    }
};

struct SetIsInGeometry {
    template<typename Unit, typename HighPrecision, typename It>
    void operator()(Unit x, HighPrecision y, It begin, It end) const
    {
        while (begin != end) {
            begin->edge->isInGeometry = begin->windingNumberBefore && begin->windingNumberAfter;
            ++begin;
        }
    }
};

template<typename F>
void getCorner(const Segment& s1, const Segment& s2, F f)
{
    if (low(s1) == low(s2))
        f(low(s1), high(s1), high(s2));
    else if (low(s1) == high(s2))
        f(low(s1), high(s1), low(s2));
    else if (high(s1) == low(s2))
        f(high(s1), low(s1), high(s2));
    else if (high(s1) == high(s2))
        f(high(s1), low(s1), low(s2));
}

static double lenSquared(PointWithZ p)
{
    return (double)x(p)*x(p) + (double)y(p)*y(p);
}

static double len(PointWithZ p)
{
    return sqrt(lenSquared(p));
}

static double projectionRatio(PointWithZ lineBegin, PointWithZ lineEnd, PointWithZ p)
{
    return (double)dot(lineEnd-lineBegin, p-lineBegin) / lenSquared(lineEnd-lineBegin);
}

static double dist(Point p, const Segment& s)
{
    auto a = low(s);
    auto delta = high(s)-low(s);
    double l = len(delta);
    double nx = x(delta)/l;
    double ny = y(delta)/l;
    double dot = (x(a) - x(p)) * nx + (y(a) - y(p)) * ny;
    double rx = x(a) - x(p) - nx * dot;
    double ry = y(a) - y(p) - ny * dot;
    double result = sqrt(rx * rx + ry * ry);
    return result;
}

// Linearize the parabola which is equidistant from p and s. The parabola's
// endpoints are begin, end.
template<typename Edge>
void linearizeParabola(vector<Edge>& edges, Point p, Segment s, PointWithZ begin, PointWithZ end, double angle)
{
    PointWithZ p1 = low(s);
    PointWithZ p2 = high(s);
    int deltaX = x(p2) - x(p1);
    int deltaY = y(p2) - y(p1);

    //printf("curve\n");

    size_t numSegments = 20;
    auto tbegin = projectionRatio(p1, p2, begin);
    auto tend = projectionRatio(p1, p2, end);

    bool done = false;
    while (!done) {
        done = true;
        PointWithZ lastPoint = begin;
        for (size_t i = 0; i <= numSegments; ++i) {
            double t = tbegin + (tend-tbegin)*i/numSegments;

            // {xt, yt} traces s
            int xt = x(p1) + lround(deltaX * t);
            int yt = y(p1) + lround(deltaY * t);

            // {ax, ay} is p relative to {xt, yt}
            int ax = x(p) - xt;
            int ay = y(p) - yt;

            double aLengthSquare = (double)ax*ax + (double)ay*ay;
            double denom = 2*((double)ax*deltaY - (double)ay*deltaX);

            int thisX = xt + lround((double)deltaY * aLengthSquare / denom);
            int thisY = yt - lround((double)deltaX * aLengthSquare / denom);

            if (i == numSegments) {
                thisX = end.x;
                thisY = end.y;
            }

            int thisZ = -lround(len(Point{thisX, thisY} - p) / tan(angle/2));

            if (i == 0)
                lastPoint.z = thisZ;
            else {
                edges.emplace_back(lastPoint, PointWithZ{thisX, thisY, thisZ}, true);
                lastPoint = PointWithZ{thisX, thisY, thisZ};
            }
        }
    }
} // linearizeParabola

template<typename ScanlineEdge>
vector<typename ScanlineEdge::Edge> getVoronoiEdges(int debugArg0, int debugArg1, PolygonSet& geometry, double angle)
{
    using Edge = typename ScanlineEdge::Edge;
    using Scan = Scan<ScanlineEdge>;

    vector<Segment> segments;
    for (auto& poly: geometry) {
        for (size_t i = 0; i < poly.size(); ++i) {
            if (i+1 < poly.size())
                segments.emplace_back(poly[i], poly[i+1]);
            else
                segments.emplace_back(poly[i], poly[0]);
        }
    }

    printf("b\n");
    bp::voronoi_diagram<double> vd;
    bp::default_voronoi_builder builder;
    for (auto& segment: segments)
        builder.insert_segment(x(low(segment)), y(low(segment)), x(high(segment)), y(high(segment)));
    printf("c\n");
    builder.construct(&vd);

    vector<Edge> filterEdges;
    printf("d\n");
    Scan::insertPolygons(filterEdges, geometry.begin(), geometry.end(), true);
    printf("e\n");
    for (size_t i = 0; i < filterEdges.size(); ++i)
        filterEdges[i].isGeometry = true;

    for (auto& edge: vd.edges())
        edge.color(0);
    printf("f: %d voronoi edges\n", vd.edges().size());

    for (size_t i = 0; i < vd.edges().size(); ++i) {
        auto& edge = vd.edges()[i];
        if (edge.is_primary() && edge.is_finite() && !(edge.color()&1)) {
            edge.color(1);
            edge.twin()->color(1);
            Point p1{lround(edge.vertex0()->x()), lround(edge.vertex0()->y())};
            Point p2{lround(edge.vertex1()->x()), lround(edge.vertex1()->y())};
            filterEdges.emplace_back(p1, p2, true);
            filterEdges.back().sourceIndex = i;
        }
    }

    printf("g1: %d filterEdges\n", filterEdges.size());
    Scan::intersectEdges(filterEdges, filterEdges.begin(), filterEdges.end());
    printf("g2: %d filterEdges\n", filterEdges.size());
    Scan::sortEdges(filterEdges.begin(), filterEdges.end());
    printf("g3: %d filterEdges\n", filterEdges.size());
    Scan::scan(
        filterEdges.begin(), filterEdges.end(),
        makeAccumulateWindingNumber([](ScanlineEdge& e){return e.edge->isGeometry; }),
        SetIsInGeometry{});
    printf("g4\n");

    vector<Edge> edges;
    for (auto& e: filterEdges) {
        if (e.isGeometry || !e.isInGeometry)
            continue;

        auto& edge = vd.edges()[e.sourceIndex];

        //if (debugArg0 && edges.size() == (size_t)debugArg0)
        //    break;
        auto cell = edge.cell();
        auto twinCell = edge.twin()->cell();
        Point p1{lround(edge.vertex0()->x()), lround(edge.vertex0()->y())};
        Point p2{lround(edge.vertex1()->x()), lround(edge.vertex1()->y())};

        if (edge.is_linear()) {
            auto segment1 = segments[cell->source_index()];
            auto segment2 = segments[twinCell->source_index()];
            bool keep = true;
            getCorner(segment1, segment2, [&keep](Point center, Point e1, Point e2) {
                double c = dot(e1-center, e2-center) / euclidean_distance(e1, center) / euclidean_distance(e2, center);
                if (c <= cos(95/2/M_PI))
                    keep = false;
            });
            if (!keep)
                continue;

            if (cell->source_category() == bp::SOURCE_CATEGORY_SEGMENT_START_POINT || cell->source_category() == bp::SOURCE_CATEGORY_SEGMENT_END_POINT) {
                PointWithZ ref;
                if (cell->source_category() == bp::SOURCE_CATEGORY_SEGMENT_START_POINT)
                    ref = low(segments[cell->source_index()]);
                else
                    ref = high(segments[cell->source_index()]);
                int numSegments = 20;
                PointWithZ lastPoint;
                for (int i = 0; i <= numSegments; ++i) {
                    PointWithZ p{
                        lround(x(p1) + (double)i * (x(p2) - x(p1)) / numSegments),
                        lround(y(p1) + (double)i * (y(p2) - y(p1)) / numSegments)};
                    p.z = -lround(len(p - ref) / tan(angle/2));
                    //printf("zzz0: %d, %d, i: %d, p:(%d, %d), p1: (%d, %d), p2: (%d, %d), ref:(%d, %d)\n", lastPoint.z, p.z, i, x(p), y(p), x(p1), y(p1), x(p2), y(p2), x(ref), y(ref));
                    if (i)
                        edges.emplace_back(lastPoint, p, true);
                    lastPoint = p;
                }
            }
            else
            {
                double dist1 = dist(p1, segments[cell->source_index()]);
                double dist2 = dist(p2, segments[cell->source_index()]);
                int z1 = -lround(dist1 / tan(angle/2));
                int z2 = -lround(dist2 / tan(angle/2));
                //printf("zzz1: %d, %d\n", z1, z2);
                edges.emplace_back(Edge{{x(p1), y(p1), z1}, {x(p2), y(p2), z2}, true});
            }

        }
        else if (edge.is_curved()) {
            Point point;
            Segment segment;

            if (cell->contains_point()) {
                if (cell->source_category() == bp::SOURCE_CATEGORY_SEGMENT_START_POINT)
                    point = low(segments[cell->source_index()]);
                else
                    point = high(segments[cell->source_index()]);
                segment = segments[twinCell->source_index()];
            }
            else {
                if (twinCell->source_category() == bp::SOURCE_CATEGORY_SEGMENT_START_POINT)
                    point = low(segments[twinCell->source_index()]);
                else
                    point = high(segments[twinCell->source_index()]);
                segment = segments[cell->source_index()];
            }

            linearizeParabola(edges, point, segment, p1, p2, angle);
        }
    }

    printf("g5: %d edges\n", edges.size());
    return edges;
} // getVoronoiEdges

template<typename Edge, typename Callback>
void reorderEdges(int debugArg0, int debugArg1, vector<Edge>& edges, Callback callback) {
    printf("i\n");
    vector<typename Edge::Index> edgeIndexes;
    edgeIndexes.reserve(edges.size() * 2);
    for (auto& edge: edges) {
        edgeIndexes.emplace_back(edge.point1, edge.point2, false, &edge);
        edgeIndexes.emplace_back(edge.point2, edge.point1, true, &edge);
    }
    sort(edgeIndexes.begin(), edgeIndexes.end());
    for (auto& edgeIndex: edgeIndexes) {
        if (edgeIndex.isPoint2)
            edgeIndex.edge->index2 = &edgeIndex;
        else
            edgeIndex.edge->index1 = &edgeIndex;
    }

    printf("j: edgeIndexes: %d\n", edgeIndexes.size());
    auto start = find_if(edgeIndexes.begin(), edgeIndexes.end(), [](const typename Edge::Index& index){return !index.point.z; });
    if (start == edgeIndexes.end())
        start = edgeIndexes.begin(); // !!!!
    start->edge->setTaken();
    if (start->isPoint2)
        swap(start->edge->point1, start->edge->point2);
    PointWithZ p = callback(*start->edge, edges.size() == 1);
    size_t numProcessed = 1;

    auto rank = [&p](const typename Edge::Index& e) {
        if ((e.point.z == 0) != (p.z == 0) || e.point != p) {
            if (e.point.z != 0)
                return 0;
            else
                return 1;
        }
        if ((e.otherPoint.z == 0) != (p.z == 0))
            return 2;
        return 3;
    };

    printf("k\n");
    while (numProcessed < edges.size()) {
        //printf("numProcessed: %d/%d\n", numProcessed, edges.size());
        auto searchStart = lower_bound(edgeIndexes.begin(), edgeIndexes.end(), typename Edge::Index{p});
        auto closest = edgeIndexes.begin();
        int closestRank = 0;
        int closestZdist = numeric_limits<int>::max();
        int closestOtherZdist = numeric_limits<int>::max();
        double closestDist = numeric_limits<double>::max();

        //if (debugArg0 && numProcessed == (size_t)debugArg0)
        //    break;

        //if (debugArg1 && numProcessed == (size_t)debugArg1)
        //    printf("P: %d, %d, %d\n", p.x, p.y, p.z);

        auto setClosest = [&](typename vector<typename Edge::Index>::iterator it) -> bool{
            if (!it->taken) {
                int r = rank(*it);
                int zDist = abs(p.z - it->point.z);
                int otherZdist = abs(p.z - it->otherPoint.z);
                double dist = lenSquared(p - it->point);

                if (closestRank >= 1 && abs(it->point.x - p.x) > closestDist)
                    return false;

                if (r > closestRank || r == closestRank && (
                    it->point == p && zDist < closestZdist || (it->point != p || zDist == closestZdist) && (
                    it->point == p && otherZdist < closestOtherZdist || (it->point != p || otherZdist == closestOtherZdist) &&
                    dist < closestDist))) {

                    //if (debugArg1 && numProcessed == (size_t)debugArg1)
                    //    printf("  +rank: %d zDist:%d otherZdist:%d dist: %lld (%d, %d, %d) -> (%d, %d, %d)\n", r, zDist, otherZdist, llround(dist), it->point.x, it->point.y, it->point.z, it->otherPoint.x, it->otherPoint.y, it->otherPoint.z);
                    closest = it;
                    closestZdist = zDist;
                    closestOtherZdist = otherZdist;
                    closestDist = dist;
                    closestRank = r;
                }
                //else if (debugArg1 && numProcessed == (size_t)debugArg1/* && p == it->point)*/)
                //    printf("  -rank: %d zDist:%d otherZdist:%d dist: %lld (%d, %d, %d) -> (%d, %d, %d)\n", r, zDist, otherZdist, llround(dist), it->point.x, it->point.y, it->point.z, it->otherPoint.x, it->otherPoint.y, it->otherPoint.z);
            }
            return true;
        };

        for (auto it2 = searchStart; it2 != edgeIndexes.end(); ++it2)
            if (!setClosest(it2))
                break;
        for (auto it2 = searchStart; it2 != edgeIndexes.begin(); --it2)
            if (!setClosest(it2 - 1))
                break;

        if (p.z == 0 && closest->point.z != 0)
            printf("dive\n");
        if (p.z != 0 && closest->point.z == 0)
            printf("retract\n");

        closest->edge->setTaken();
        if (closest->isPoint2)
            swap(closest->edge->point1, closest->edge->point2);

        //if (debugArg1 && numProcessed == (size_t)debugArg1)
        //    printf("Old P: %d, %d, %d\n", p.x, p.y, p.z);

        //if (debugArg1 && numProcessed == (size_t)debugArg1)
        //    for (auto& ind: edgeIndexes)
        //        if (ind.point.x == p.x && ind.point.y == p.y)
        //            printf("  (%d, %d, %d) -> (%d, %d, %d) taken=%d\n", ind.point.x, ind.point.y, ind.point.z, ind.otherPoint.x, ind.otherPoint.y, ind.otherPoint.z, ind.taken);

        p = callback(*closest->edge, edges.size() == numProcessed + 1);
        ++numProcessed;

        //if (debugArg1 && numProcessed == (size_t)debugArg1)
        //    printf("New P: %d, %d, %d\n", p.x, p.y, p.z);
    } // while (numProcessed < edges.size())
} // reorderEdges

template<typename Edge>
void clipTopZ(double passDepth, double maxDepth, vector<PointWithZ>& path, const Edge& edge)
{
    if (path.empty() || path.back() != edge.point1 || path.back().z != edge.point1.z)
        path.push_back(edge.point1);
    if (path.empty() || path.back() != edge.point2 || path.back().z != edge.point2.z)
        path.push_back(edge.point2);
}

template<typename Edge>
void processSpan(double passDepth, double maxDepth, vector<vector<PointWithZ>>& result, vector<Edge>& span)
{
    //printf("processSpan\n");
    //passDepth = min(passDepth, maxDepth);
    int minZ = 0;
    for (auto& edge: span)
        minZ = min(minZ, min(edge.point1.z, edge.point2.z));

    vector<PointWithZ> path;

    int deltaZ = max(0.0, -passDepth - minZ);
    //printf("    span: %d, passDepth: %d, minZ: %d, deltaZ: %d\n", span.size(), (int)passDepth, minZ, deltaZ);

    bool reverse = false;
    while (true) {
        //printf("    span: %d, reverse: %d, deltaZ: %d\n", span.size(), reverse, deltaZ);

        if (reverse) {
            for (auto it = span.rbegin(); it != span.rend(); ++it)
            {
                clipTopZ(passDepth, maxDepth, path, Edge{
                    PointWithZ{it->point2.x, it->point2.y, it->point2.z + deltaZ},
                    PointWithZ{it->point1.x, it->point1.y, it->point1.z + deltaZ}, false});
            }
        }
        else {
            for (auto& edge: span)
            {
                clipTopZ(passDepth, maxDepth, path, Edge{
                    PointWithZ{edge.point1.x, edge.point1.y, edge.point1.z + deltaZ},
                    PointWithZ{edge.point2.x, edge.point2.y, edge.point2.z + deltaZ}, false});
            }
        }

        if (deltaZ == 0)
            break;
        deltaZ = max(0.0, deltaZ - passDepth);
        reverse = !reverse;
    }

    if (path.front().z != 0)
        path.insert(path.begin(), PointWithZ{path.front().x, path.front().y, 0});
    if (path.back().z != 0)
        path.insert(path.end(), PointWithZ{path.back().x, path.back().y, 0});
    result.emplace_back(move(path));
    //printf("    result: %d\n", result.size());
}

extern "C" void vPocket(
    int debugArg0, int debugArg1,
    double** paths, int numPaths, int* pathSizes,
    double cutterAngle, double passDepth, double maxDepth,
    double**& resultPaths, int& resultNumPaths, int*& resultPathSizes)
{
    try {
        using Edge = Edge<PointWithZ, VoronoiEdge>;
        using ScanlineEdge = ScanlineEdge<Edge, ScanlineEdgeWindingNumber>;
        double angle = cutterAngle * M_PI / 180;

        printf("a\n");
        PolygonSet geometry = convertPathsFromC(paths, numPaths, pathSizes);

        auto edges = getVoronoiEdges<ScanlineEdge>(debugArg0, debugArg1, geometry, angle);

        if (edges.empty()) {
            convertPathsToC(resultPaths, resultNumPaths, resultPathSizes, vector<vector<PointWithZ>>{});
            return;
        }

        vector<Edge> span;
        vector<vector<PointWithZ>> result;
        reorderEdges(debugArg0, debugArg1, edges, [passDepth, maxDepth, &span, &result](Edge& edge, bool isLast) {
            if (!span.empty() && edge.point1 != span.back().point2) {
                processSpan(passDepth, maxDepth, result, span);
                span.clear();
            }

            span.emplace_back(edge);

            if (isLast || edge.point2.z == 0) {
                processSpan(passDepth, maxDepth, result, span);
                span.clear();
            }

            if (!span.empty())
                return span.back().point2;
            else
                return result.back().back();
        });

        printf("z - done\n");
        convertPathsToC(resultPaths, resultNumPaths, resultPathSizes, result);
        return;
    }
    catch (exception& e) {
        printf("%s\n", e.what());
    }
    catch (...) {
        printf("???? unknown exception\n");
    }
};


#include "pauli_operator.hpp"

#include <boost/dynamic_bitset.hpp>
#include <csim/stat_ops_dm.hpp>
#include <vector>

#ifdef _USE_GPU
#include <gpusim/stat_ops.h>
#endif

#include "state.hpp"
#include "type.hpp"

void MultiQubitPauliOperator::set_bit(
    const UINT pauli_id, const UINT target_index) {
    while (this->_x.size() <= target_index) {
        this->_x.resize(this->_x.size() * 2 + 1);
    }
    this->_z.resize(this->_x.size());
    if (pauli_id == PAULI_ID_X) {
        this->_x.set(target_index);
    } else if (pauli_id == PAULI_ID_Y) {
        this->_x.set(target_index);
        this->_z.set(target_index);
    } else if (pauli_id == PAULI_ID_Z) {
        this->_z.set(target_index);
    }
}

const std::vector<UINT>& MultiQubitPauliOperator::get_pauli_id_list() const {
    return _pauli_id;
}

const std::vector<UINT>& MultiQubitPauliOperator::get_index_list() const {
    return _target_index;
}

void MultiQubitPauliOperator::add_single_Pauli(
    UINT qubit_index, UINT pauli_type) {
    if (pauli_type >= 4)
        throw std::invalid_argument("pauli type must be any of 0,1,2,3");
    _target_index.push_back(qubit_index);
    _pauli_id.push_back(pauli_type);
    set_bit(pauli_type, qubit_index);
}

CPPCTYPE MultiQubitPauliOperator::get_expectation_value(
    const QuantumStateBase* state) const {
    if (state->get_device_type() == DEVICE_CPU) {
        if (state->is_state_vector()) {
            return expectation_value_multi_qubit_Pauli_operator_partial_list(
                this->_target_index.data(), this->_pauli_id.data(),
                static_cast<UINT>(this->_target_index.size()), state->data_c(),
                state->dim);
        } else {
            return dm_expectation_value_multi_qubit_Pauli_operator_partial_list(
                this->_target_index.data(), this->_pauli_id.data(),
                static_cast<UINT>(this->_target_index.size()), state->data_c(),
                state->dim);
        }
    } else if (state->get_device_type() == DEVICE_GPU) {
#ifdef _USE_GPU
        if (state->is_state_vector()) {
            return expectation_value_multi_qubit_Pauli_operator_partial_list_host(
                this->get_index_list().data(), this->get_pauli_id_list().data(),
                (UINT)this->get_index_list().size(), state->data(), state->dim,
                state->get_cuda_stream(), state->device_number);
        } else {
            throw std::runtime_error(
                "Get expectation value for DensityMatrix on GPU is not "
                "supported");
        }
#else
        throw std::invalid_argument("GPU is not supported in this build");
#endif
    } else {
        throw std::invalid_argument("Unsupported device type");
    }
}

CPPCTYPE MultiQubitPauliOperator::get_transition_amplitude(
    const QuantumStateBase* state_bra,
    const QuantumStateBase* state_ket) const {
    if (state_bra->get_device_type() != state_ket->get_device_type())
        throw std::invalid_argument("Device type is different");
    if (state_bra->is_state_vector() != state_ket->is_state_vector())
        throw std::invalid_argument("is_state_vector is not matched");
    if (state_bra->dim != state_ket->dim)
        throw std::invalid_argument("state_bra->dim != state_ket->dim");

    if (state_bra->get_device_type() == DEVICE_CPU) {
        if (state_bra->is_state_vector()) {
            return transition_amplitude_multi_qubit_Pauli_operator_partial_list(
                this->_target_index.data(), this->_pauli_id.data(),
                (UINT)this->_target_index.size(), state_bra->data_c(),
                state_ket->data_c(), state_bra->dim);
        } else {
            throw std::invalid_argument(
                "TransitionAmplitude for density matrix is not implemtend");
        }
    } else if (state_bra->get_device_type() == DEVICE_GPU) {
#ifdef _USE_GPU
        if (state_bra->is_state_vector()) {
            return transition_amplitude_multi_qubit_Pauli_operator_partial_list_host(
                this->get_index_list().data(), this->get_pauli_id_list().data(),
                (UINT)this->get_index_list().size(), state_bra->data(),
                state_ket->data(), state_bra->dim, state_bra->get_cuda_stream(),
                state_bra->device_number);
        } else {
            throw std::runtime_error(
                "Get expectation value for DensityMatrix on GPU is not "
                "supported");
        }
#else
        throw std::invalid_argument("GPU is not supported in this build");
#endif
    } else {
        throw std::invalid_argument("Unsupported device");
    }
}

MultiQubitPauliOperator* MultiQubitPauliOperator::copy() const {
    auto pauli =
        new MultiQubitPauliOperator(this->_target_index, this->_pauli_id);
    return pauli;
}

bool MultiQubitPauliOperator::operator==(
    const MultiQubitPauliOperator& target) const {
    auto x = this->_x;
    auto z = this->_z;
    auto target_x = target.get_x_bits();
    auto target_z = target.get_z_bits();
    if (target_x.size() != this->_x.size()) {
        size_t max_size = std::max(this->_x.size(), target_x.size());
        x.resize(max_size);
        z.resize(max_size);
        target_x.resize(max_size);
        target_z.resize(max_size);
    }
    return x == target_x && z == target_z;
}

MultiQubitPauliOperator MultiQubitPauliOperator::operator*(
    const MultiQubitPauliOperator& target) const {
    auto x = this->_x;
    auto z = this->_z;
    auto target_x = target.get_x_bits();
    auto target_z = target.get_z_bits();
    if (target_x.size() != this->_x.size()) {
        size_t max_size = std::max(x.size(), target_x.size());
        x.resize(max_size);
        z.resize(max_size);
        target_x.resize(max_size);
        target_z.resize(max_size);
    }
    MultiQubitPauliOperator res(x ^ target_x, z ^ target_z);
    return res;
}

MultiQubitPauliOperator& MultiQubitPauliOperator::operator*=(
    const MultiQubitPauliOperator& target) {
    auto target_x = target.get_x_bits();
    auto target_z = target.get_z_bits();
    size_t max_size = std::max(this->_x.size(), target_x.size());
    if (target_x.size() != this->_x.size()) {
        this->_x.resize(max_size);
        this->_z.resize(max_size);
        target_x.resize(max_size);
        target_z.resize(max_size);
    }
    this->_x ^= target_x;
    this->_z ^= target_z;
    _target_index.clear();
    _pauli_id.clear();
    ITYPE i;
    for (i = 0; i < max_size; i++) {
        UINT pauli_id = PAULI_ID_I;
        if (this->_x[i] && !this->_z[i]) {
            pauli_id = PAULI_ID_X;
        } else if (this->_x[i] && this->_z[i]) {
            pauli_id = PAULI_ID_Y;
        } else if (!this->_x[i] && this->_z[i]) {
            pauli_id = PAULI_ID_Z;
        }
        _target_index.push_back(i);
        _pauli_id.push_back(pauli_id);
    }
    return *this;
}

std::string MultiQubitPauliOperator::to_string() const {
    std::string res;
    std::string id;
    ITYPE i;
    for (i = 0; i < _x.size(); i++) {
        if (!_x[i] && !_z[i]) {
            id = "I";
        } else if (_x[i] && !_z[i]) {
            id = "X";
        } else if (_x[i] && _z[i]) {
            id = "Y";
        } else if (!_x[i] && _z[i]) {
            id = "Z";
        }
        if (id != "I") {
            res += id + " " + std::to_string(i) + " ";
        }
    }
    return res;
}


#ifndef PYTHONIC_INCLUDE_NUMPY_DEG2RAD_HPP
#define PYTHONIC_INCLUDE_NUMPY_DEG2RAD_HPP

#include "pythonic/include/utils/functor.hpp"
#include "pythonic/include/types/ndarray.hpp"
#include "pythonic/include/utils/numpy_traits.hpp"
#include <boost/simd/function/inrad.hpp>

PYTHONIC_NS_BEGIN

namespace numpy
{
#define NUMPY_NARY_FUNC_NAME deg2rad
#define NUMPY_NARY_FUNC_SYM boost::simd::inrad
#include "pythonic/include/types/numpy_nary_expr.hpp"
}
PYTHONIC_NS_END

#endif


/*******************************************************************************
#      ____               __          __  _      _____ _       _               #
#     / __ \              \ \        / / | |    / ____| |     | |              #
#    | |  | |_ __   ___ _ __ \  /\  / /__| |__ | |  __| | ___ | |__   ___      #
#    | |  | | '_ \ / _ \ '_ \ \/  \/ / _ \ '_ \| | |_ | |/ _ \| '_ \ / _ \     #
#    | |__| | |_) |  __/ | | \  /\  /  __/ |_) | |__| | | (_) | |_) |  __/     #
#     \____/| .__/ \___|_| |_|\/  \/ \___|_.__/ \_____|_|\___/|_.__/ \___|     #
#           | |                                                                #
#           |_|                                                                #
#                                                                              #
#                                (c) 2011 by                                   #
#           University of Applied Sciences Northwestern Switzerland            #
#                     Institute of Geomatics Engineering                       #
#                           robert.wueest@fhnw.ch                              #
********************************************************************************
*     Licensed under MIT License. Read the file LICENSE for more information   *
*******************************************************************************/

#include "ogprocess.h"
#include "errors.h"
#include "app/ProcessingSettings.h"
#include "geo/MercatorQuadtree.h"
#include "geo/CoordinateTransformation.h"
#include "geo/ElevationLayerSettings.h"
#include "string/FilenameUtils.h"
#include "string/StringUtils.h"
#include "geo/ImageLayerSettings.h"
#include "io/FileSystem.h"
#include "image/ImageLoader.h"
#include "image/ImageWriter.h"
#include "app/Logger.h"
#include "math/mathutils.h"
#include <iostream>
#include <boost/program_options.hpp>
#include <sstream>
#include <omp.h>
#include <mpi.h>
#include "mpi/Utils.h"
#include  "hillshading.h"

namespace po = boost::program_options;

//-------------------------------------------------------------------
// Job-Struct
struct SJob
{
   int xx, yy, lod;
};

bool bError = false;
std::string sLayerPath;
int iLayerMaxZoom;
std::string sAlgorithm;
int iNumThreads = 8;
bool bVerbose = false;
int queueSize = 4096;
int inputX = 768;
int inputY = 768;
int outputX = 256;
int outputY = 256;
double z_depth = 2.0;
double azimut = 315;
double altitude = 45;
double sscale = 1;
int iX = 0;
int iY = 0;
std::string sTempTileDir;
std::string sTileDir;
boost::shared_ptr<MercatorQuadtree> qQuadtree;
int64 layerTileX0, layerTileY0, layerTileX1, layerTileY1;

//------------------------------------------------------------------------------
// MPI Job callback function (called every thread/compute node)
void jobCallback(const SJob& job, int rank)
{
   std::string sCurrentQuadcode = qQuadtree->TileCoordToQuadkey(job.xx,job.yy,job.lod);

   //std::cout << sCurrentQuadcode << "\n";
   HSProcessChunk pData;
   pData.dfXMax = -1e20;
   pData.dfYMax = -1e20;
   pData.dfXMin = 1e20;
   pData.dfYMin = 1e20;
   pData.data.AllocateImage(inputX, inputY);
   for (int ty=-1;ty<=1;ty++)
   {
      for (int tx=-1;tx<=1;tx++)
      {
         std::string sQuadcode = qQuadtree->TileCoordToQuadkey(job.xx+tx,job.yy+ty,job.lod);
         std::string sTilefile = ProcessingUtils::GetTilePath(sTempTileDir, ".raw" , job.lod, job.xx+tx, job.yy+ty);

         double sx0, sy1, sx1, sy0;
         qQuadtree->QuadKeyToMercatorCoord(sQuadcode, sx0, sy1, sx1, sy0);

         pData.dfXMax = math::Max<double>(pData.dfXMax, sx1);
         pData.dfYMax = math::Max<double>(pData.dfXMax, sy1);
         pData.dfXMin = math::Min<double>(pData.dfXMin, sx0);
         pData.dfYMin = math::Min<double>(pData.dfXMin, sy0);

         assert(sx0 < sx1);
         assert(sy0 < sy1);

         //std::cout << "   " << sTilefile << "\n";

         std::ifstream fin;
         fin.open(sTilefile.c_str(), std::ios::binary);
         int posX = (tx+1)*(inputX/3);
         int posY = (ty+1)*(inputY/3);
         int offX = 0;
         int offY = 0;
         if (fin.good())
         {
            while (!fin.eof())
            {

               float value;
               fin.read((char*)&(value), sizeof(float));
               if (!fin.eof())
               {
                  pData.data.SetValue(posX+offX, posY+offY, value);
               }
               offX++;
               if(offX % 256 == 0)
               {
                  offX = 0;
                  offY++;
               }

            }
         }
         fin.close();
      }
   }
   // Generate tile
   process_hillshading(sTileDir, pData, job.xx, job.yy, job.lod, z_depth, azimut, altitude, sscale, 1,false, false, outputX, outputY);
}
//---

//------------------------------------------------------------------------------
void BroadcastString(std::string& sStr, int sender)
{
   unsigned int len = sStr.length();
   MPI_Bcast(&len, 1, MPI_UNSIGNED, sender, MPI_COMM_WORLD);
   if (sStr.length() < len )
   {
      sStr.insert(sStr.end(),(size_t)(len-sStr.length()), ' ');
   }
   else if (sStr.length() > len )
   {
      sStr.erase( len,sStr.length()-len);
   }

   MPI_Bcast(const_cast<char *>(sStr.data()), len, MPI_CHAR, sender, MPI_COMM_WORLD);
}
//------------------------------------------------------------------------------
void BroadcastInt(int& val, int sender)
{
   MPI_Bcast(&val, 1, MPI_INT, sender, MPI_COMM_WORLD);
}
//------------------------------------------------------------------------------
void BroadcastDouble(double& val, int sender)
{
   MPI_Bcast(&val, 1, MPI_DOUBLE, sender, MPI_COMM_WORLD);
}
//------------------------------------------------------------------------------
void BroadcastInt64(int64& val, int sender)
{
   MPI_Bcast(&val, 1, MPI_LONG_LONG, sender, MPI_COMM_WORLD);
}
//------------------------------------------------------------------------------
void BroadcastBool(bool& val, int sender)
{
   MPI_Datatype bool_type;
   if (sizeof(bool) == 1) bool_type= MPI_BYTE;
   else if (sizeof(bool) == 2) bool_type = MPI_SHORT;
   else if (sizeof(bool) == 4) bool_type = MPI_INT;
   else { assert(false); return;}
   MPI_Bcast(&val, 1, bool_type, sender, MPI_COMM_WORLD);
}
//------------------------------------------------------------------------------------
void GenerateRenderJobs(int count, int lod, std::vector<SJob> &vJobs)
{
   int idx = 0;
   if(bVerbose)
     std::cout << " Generating " << count << " jobs starting from (z, x, y) " << "(" << lod << ", " << iX << ", " << iY << ")\n";
   for (int64 xx = iX; xx < layerTileX1; ++xx)
   {
      for (int64 yy = iY; yy < layerTileY1; ++yy)
      {
         SJob work;
         work.xx = xx;
         work.yy = yy;
         work.lod = lod;
         vJobs.push_back(work);
         iY = (yy < layerTileY1-1)? yy+1 : -1;
         iX = (yy < layerTileY1-1) ? xx: xx+1;
         if(idx >= (count-1))
         {
            return;
         }
         idx++;
      }
      iY = layerTileY0+1;
   }
}

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

   int rank, totalnodes;

   MPI_Init(&argc, &argv);
   MPI_Comm_size(MPI_COMM_WORLD, &totalnodes);
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);

   if (rank == 0)
   {
      po::options_description desc("Program-Options");
      desc.add_options()
         ("layer_path", po::value<std::string>(), "path of layer to generate data")
         ("layer_zoom", po::value<int>(), "maximum zoom which has to be generated previously using ogAddData")
         ("numthreads", po::value<int>(), "[optional] force number of threads")
         ("mpi_queue_size", po::value<int>(), "[optional] mpi queue size (Default: 10000)")
         ("verbose", "[optional] verbose output")
         ;

      po::variables_map vm;

      try
      {
         po::store(po::parse_command_line(argc, argv, desc), vm);
         po::notify(vm);
      }
      catch (std::exception&)
      {
         bError = true;
      }

      if(vm.count("layer_path"))
      {
        sLayerPath = vm["layer_path"].as<std::string>();
        if(!(sLayerPath.at(sLayerPath.length()-1) == '\\' || sLayerPath.at(sLayerPath.length()-1) == '/'))
           sLayerPath = sLayerPath + "/";
      }
      else
         bError = true;
      if(vm.count("layer_zoom"))
         iLayerMaxZoom = vm["layer_zoom"].as<int>();
      else
         bError = true;
      if(vm.count("num_threads"))
         iNumThreads = vm["num_threads"].as<int>();
      if(vm.count("mpi_queue_size"))
         queueSize = vm["mpi_queue_size"].as<int>();
      if(vm.count("verbose"))
         bVerbose = true;

      //---------------------------------------------------------------------------
      // init options:

      boost::shared_ptr<ProcessingSettings> qSettings =  ProcessingUtils::LoadAppSettings();

      if (!qSettings)
      {
         std::cout << "Error in configuration! Check setup.xml\n";
         return ERROR_CONFIG;
      }
      if(bError)
      {
         std::cout << "Wrong parameters!\n";
         std::cout << desc << "\n";
         return MPI_Abort(MPI_COMM_WORLD, ERROR_PARAMS);
      }
   }
   BroadcastString(sLayerPath,0);
   BroadcastInt(iLayerMaxZoom,0);
   BroadcastInt(iX,0);
   BroadcastInt(iY,0);
   BroadcastInt(iNumThreads,0);
   BroadcastBool(bVerbose,0);
   BroadcastInt(queueSize,0);

   MPIJobManager<SJob> jobmgr(queueSize);

   //---------------------------------------------------------------------------
   // -- Beginn process
   sTempTileDir = sLayerPath + "temp/tiles/";
   sTileDir = sLayerPath + "tiles/";

   boost::shared_ptr<ImageLayerSettings> qImageLayerSettings = ImageLayerSettings::Load(sLayerPath);
   if (!qImageLayerSettings)
   {
      std::cout << "Failed retrieving image layer settings! Make sure to create it using 'createlayer'.\n";
      return ERROR_IMAGELAYERSETTINGS;
   }
   int lod = qImageLayerSettings->GetMaxLod();

   qImageLayerSettings->GetTileExtent(layerTileX0, layerTileY0, layerTileX1, layerTileY1);
   if (bVerbose)
   {
      std::cout << "\nRaw Image Layer:\n";
      std::cout << "   name = " << qImageLayerSettings->GetLayerName() << "\n";
      std::cout << "   maxlod = " << lod << "\n";
      std::cout << "   extent = " << layerTileX0 << ", " << layerTileY0 << ", " << layerTileX1 << ", " << layerTileY1 << "\n";
   }
   int64 width = layerTileX1-layerTileX0+1;
   int64 height = layerTileY1-layerTileY0+1;
   if (width<3 || height<3)
   {
      std::cout << "Extent is too small for hillshading processing\n";
      return ERROR_ELVLAYERSETTINGS;
   }

   // Retrieve dataset extent in mercator coord:
   qQuadtree = boost::shared_ptr<MercatorQuadtree>(new MercatorQuadtree());
   double xmin, ymin, xmax, ymax;
   std::string qc0 = qQuadtree->TileCoordToQuadkey(layerTileX0,layerTileY0,lod);
   std::string qc1 = qQuadtree->TileCoordToQuadkey(layerTileX1,layerTileY1,lod);

   double x00, y00, x10, y10;
   double x01, y01, x11, y11;
   qQuadtree->QuadKeyToMercatorCoord(qc0, x00,y00,x10,y10);
   qQuadtree->QuadKeyToMercatorCoord(qc1, x01,y01,x11,y11);

   xmin = x00;
   ymin = y11;
   xmax = x11;
   ymax = y00;
   if (bVerbose)
   {
      std::cout << "\nExtent mercator:";
      std::cout << "   extent = " << xmin << ", " << ymin << ", " << xmax << ", " << ymax << "\n";
   }

   if (!ProcessingUtils::init_gdal())
   {
      std::cout << "Warning: gdal-data directory not found. Ouput may be wrong!\n";
      return 1;
   }

   bool bDone = false;
   //---------------------------------------------------------------------------
   // -- performance measurement
   int tileCount = 0;
   int currentJobQueueSize = 0;
   clock_t t_0, t_1;
   t_0 = clock();
   while (!bDone)
   {
      if (rank == 0)
      {
         std::vector<SJob> vJobs;
         if(iX <= 0) { iX = layerTileX0+1; }
         if(iY <= 0) { iY = layerTileY0+1; }
         GenerateRenderJobs(totalnodes*queueSize, lod, vJobs);
         currentJobQueueSize = vJobs.size();
         tileCount += currentJobQueueSize;
         if (vJobs.size() == 0) // no more jobs
         {
            bDone = true;
            t_1 = clock();
            double time=(double(t_1-t_0)/double(CLOCKS_PER_SEC));
            double tps = tileCount/time;
            std::cout << ">>> Finished processing " << tileCount << " tiles at " << tps << " tiles per second! TOTAL TIME: " << time << "<<<\n" << std::flush;
         }
         else
         {
            jobmgr.AddJobs(vJobs);
         }
      }

      MPI_Barrier(MPI_COMM_WORLD);
      BroadcastBool(bDone, 0);
      if (!bDone)
      {
         clock_t t0,t1;
         t0 = clock();
         jobmgr.Process(jobCallback, bVerbose);
         t1 = clock();
         double tilesPerSecond = currentJobQueueSize/(double(t1-t0)/double(CLOCKS_PER_SEC));
      }
      else
      {
         MPI_Finalize();
      }
   }
   return 0;
}