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

#ifndef NODE_HPP
#define NODE_HPP

#include <assert.h>
#include <cmath>
#include <memory>
#include <vector>

#include <Eigen/Core>

#include <ippp/types.h>
#include <ippp/util/UtilVec.hpp>

namespace ippp {

/*!
* \brief   Class Node to present nodes of the path planner.
* \details Consists of the position by an Vec, a cost parameter, an Edge to the parent and a list of child Edges
* \author  Sascha Kaden
* \date    2016-05-23
*/
template <unsigned int dim>
class Node {
  public:
    Node();
    Node(const Vector<dim> &config);

    bool empty() const;

    void setCost(double cost);
    void addCost(double cost);
    double getCost() const;

    void setParent(const std::shared_ptr<Node> &parent, const double edgeCost);
    std::shared_ptr<Node> getParentNode() const;
    std::pair<std::shared_ptr<Node<dim>>, double> getParentEdge() const;
    void clearParent();

    void setQueryParent(const std::shared_ptr<Node> &parent, const double edgeCost);
    std::shared_ptr<Node> getQueryParentNode() const;
    std::pair<std::shared_ptr<Node<dim>>, double> getQueryParentEdge() const;
    void clearQueryParent();

    void addChild(const std::shared_ptr<Node> &child, const double edgeCost);
    std::vector<std::shared_ptr<Node>> getChildNodes() const;
    std::vector<std::pair<std::shared_ptr<Node<dim>>, double>> getChildEdges() const;
    size_t getChildSize() const;
    bool isChild(const std::shared_ptr<Node> &child) const;
    void clearChildren();

    void addInvalidChild(const std::shared_ptr<Node> &child);
    bool isInvalidChild(const std::shared_ptr<Node> &node) const;
    void clearInvalidChildren();

    Vector<dim> getValues() const;
    double getValue(const unsigned int index) const;

  private:
    Vector<dim> m_config;
    double m_cost = -1;

    std::pair<std::shared_ptr<Node<dim>>, double> m_parent = std::make_pair(nullptr, 0);
    std::pair<std::shared_ptr<Node<dim>>, double> m_queryParent = std::make_pair(nullptr, 0);
    std::vector<std::pair<std::shared_ptr<Node<dim>>, double>> m_children;
    std::vector<std::shared_ptr<Node>> m_invalidChildren;
};

/*!
*  \brief      Default constructor of the class Node, sets all elements of the Vector NaN.
*  \details    A default Node contains a NAN config and is therefore empty.
*  \author     Sascha Kaden
*  \date       2016-05-24
*/
template <unsigned int dim>
Node<dim>::Node() {
    for (unsigned int i = 0; i < dim; ++i)
        m_config[i] = NAN;
}

/*!
*  \brief      Constructor of the class Node
*  \author     Sascha Kaden
*  \param[in]  Eigen Vector
*  \date       2016-05-24
*/
template <unsigned int dim>
Node<dim>::Node(const Vector<dim> &config) {
    m_config = config;
}

/*!
*  \brief      Return true, if the vector is empty
*  \author     Sascha Kaden
*  \param[out] State of the Node
*  \date       2016-05-24
*/
template <unsigned int dim>
bool Node<dim>::empty() const {
    return std::isnan(m_config[0]);
}

/*!
*  \brief      Set cost of Node
*  \author     Sascha Kaden
*  \param[in]  cost
*  \date       2016-05-24
*/
template <unsigned int dim>
void Node<dim>::setCost(const double cost) {
    if (cost >= 0) {
        m_cost = cost;
    }
}

/*!
*  \brief      Add cost to Node
*  \author     Sascha Kaden
*  \param[in]  cost
*  \date       2016-10-22
*/
template <unsigned int dim>
void Node<dim>::addCost(const double cost) {
    if (cost >= 0) {
        m_cost += cost;
    }
}

/*!
*  \brief      Return cost of the Node
*  \author     Sascha Kaden
*  \param[out] cost
*  \date       2016-05-24
*/
template <unsigned int dim>
double Node<dim>::getCost() const {
    return m_cost;
}

/*!
*  \brief      Set parent of the Node
*  \author     Sascha Kaden
*  \param[in]  shared_ptr parent Node
*  \date       2016-07-15
*/
template <unsigned int dim>
void Node<dim>::setParent(const std::shared_ptr<Node> &parent, const double edgeCost) {
    if (!parent->empty())
        m_parent = std::make_pair(parent, edgeCost);
}

/*!
*  \brief      Return parent Node
*  \author     Sascha Kaden
*  \param[out] shared_ptr of parent Node
*  \date       2016-07-15
*/
template <unsigned int dim>
std::shared_ptr<Node<dim>> Node<dim>::getParentNode() const {
    return m_parent.first;
}

/*!
*  \brief      Return parent Edge
*  \author     Sascha Kaden
*  \param[out] Edge
*  \date       2016-10-22
*/
template <unsigned int dim>
std::pair<std::shared_ptr<Node<dim>>, double> Node<dim>::getParentEdge() const {
    return m_parent;
}

/*!
*  \brief      Removes the parent and set it as nullptr
*  \author     Sascha Kaden
*  \date       2016-07-15
*/
template <unsigned int dim>
void Node<dim>::clearParent() {
    m_parent = std::make_pair(nullptr, 0);
}

/*!
*  \brief      Set query parent of the Node
*  \author     Sascha Kaden
*  \param[in]  shared_ptr query parent Node
*  \date       2016-07-15
*/
template <unsigned int dim>
void Node<dim>::setQueryParent(const std::shared_ptr<Node> &queryParent, const double edgeCost) {
    if (!queryParent->empty())
        m_queryParent = std::make_pair(queryParent, edgeCost);
}

/*!
*  \brief      Return query parent Node
*  \author     Sascha Kaden
*  \param[out] shared_ptr of query parent Node
*  \date       2016-07-15
*/
template <unsigned int dim>
std::shared_ptr<Node<dim>> Node<dim>::getQueryParentNode() const {
    return m_queryParent.first;
}

/*!
*  \brief      Return query parent Edge
*  \author     Sascha Kaden
*  \param[out] Edge
*  \date       2016-10-22
*/
template <unsigned int dim>
std::pair<std::shared_ptr<Node<dim>>, double> Node<dim>::getQueryParentEdge() const {
    return m_queryParent;
}

/*!
*  \brief      Removes the parent and set it as nullptr
*  \author     Sascha Kaden
*  \date       2016-07-15
*/
template <unsigned int dim>
void Node<dim>::clearQueryParent() {
    m_queryParent = std::make_pair(nullptr, -1);
}

/*!
*  \brief      Add a child Node to the child list
*  \author     Sascha Kaden
*  \param[in]  shared_ptr child Node
*  \date       2016-07-15
*/
template <unsigned int dim>
void Node<dim>::addChild(const std::shared_ptr<Node<dim>> &child, const double edgeCost) {
    if (!child->empty())
        m_children.push_back(std::make_pair(child, edgeCost));
}

/*!
*  \brief      Return list of child nodes
*  \author     Sascha Kaden
*  \param[out] list of child nodes
*  \date       2016-07-15
*/
template <unsigned int dim>
std::vector<std::shared_ptr<Node<dim>>> Node<dim>::getChildNodes() const {
    std::vector<std::shared_ptr<Node>> childNodes;
    for (auto &&child : m_children)
        childNodes.push_back(child.first);

    return childNodes;
}

/*!
*  \brief      Return list of child edges
*  \author     Sascha Kaden
*  \param[out] list of child edges
*  \date       2016-07-15
*/
template <unsigned int dim>
std::vector<std::pair<std::shared_ptr<Node<dim>>, double>> Node<dim>::getChildEdges() const {
    return m_children;
}

/*!
*  \brief      Return size of the child edges
*  \author     Sascha Kaden
*  \param[out] child size
*  \date       2017-10-24
*/
template <unsigned int dim>
size_t Node<dim>::getChildSize() const {
    return m_children.size();
}

/*!
*  \brief      Check if passed node is child
*  \author     Sascha Kaden
*  \param[in]  node
*  \param[out] true if existing child
*  \date       2017-10-12
*/
template <unsigned int dim>
bool Node<dim>::isChild(const std::shared_ptr<Node<dim>> &node) const {
    for (auto &child : m_children)
        if (child.first == node)
            return true;

    return false;
}

/*!
*  \brief      Clear list of children
*  \author     Sascha Kaden
*  \date       2016-07-15
*/
template <unsigned int dim>
void Node<dim>::clearChildren() {
    m_children.clear();
}

/*!
*  \brief      Add an invalid child Node to the invalid children list
*  \author     Sascha Kaden
*  \param[in]  shared_ptr child Node
*  \date       2017-10-12
*/
template <unsigned int dim>
void Node<dim>::addInvalidChild(const std::shared_ptr<Node<dim>> &child) {
    if (child->empty())
        return;

    m_invalidChildren.push_back(child);
}

/*!
*  \brief      Check if passed node is invalid child of the Node
*  \author     Sascha Kaden
*  \param[in]  node
*  \param[out] true if existing invalid child
*  \date       2017-10-12
*/
template <unsigned int dim>
bool Node<dim>::isInvalidChild(const std::shared_ptr<Node<dim>> &node) const {
    for (auto &child : m_invalidChildren)
        if (child == node)
            return true;

    return false;
}

/*!
*  \brief      Clear list of invalid children
*  \author     Sascha Kaden
*  \date       2017-10-12
*/
template <unsigned int dim>
void Node<dim>::clearInvalidChildren() {
    m_invalidChildren.clear();
}

/*!
*  \brief      Return const Vector of the Node
*  \author     Sascha Kaden
*  \param[out] Vec
*  \date       2016-05-24
*/
template <unsigned int dim>
Vector<dim> Node<dim>::getValues() const {
    return m_config;
}

/*!
*  \brief      Return value from index
*  \author     Sascha Kaden
*  \param[in]  index
*  \param[out] value
*  \date       2016-05-24
*/
template <unsigned int dim>
double Node<dim>::getValue(const unsigned int index) const {
    return m_config[index];
}

} /* namespace ippp */

#endif /* NODE_HPP */


#include <iostream>
#include <string>
#include <sstream>

#include <itkImage.h>
#include <itkImageRegionIterator.h>

#include <itkImageFileReader.h>
#include <itkImageFileWriter.h>
#include <itkImageDuplicator.h>

#include <boost/program_options.hpp>

#undef __FUNCT__
#define __FUNCT__ "main"
int main(int argc, char **argv)
{
    std::string inImgFile;
    std::string outImgFile;
    double      lowerThr;
    double      upperThr;
    double      insideVal;
    double      outsideVal;
    bool        outsideSameAsInput;

    //------------------- Set up the command line options database and parse them -----------------------------//
    boost::program_options::options_description optionsDescription("binarize the input image by setting inside value to the pixels having values within the given range");
    optionsDescription.add_options()
            ("help,h", "display help message")
            ("input,i", boost::program_options::value< std::string >(&inImgFile), "input FileName")
            ("output,o", boost::program_options::value< std::string >(&outImgFile), "output filename")
            ("lower,l",boost::program_options::value< double >(&lowerThr), "Lower value, includes this value and above as in the selected region.")
            ("upper,u",boost::program_options::value< double >(&upperThr), "Upper value, includes this value and below as in the selected region.")
            ("inside,x",boost::program_options::value< double >(&insideVal)->default_value(1), "Inside pixel value")
            ("outsideSameAsInput,s",boost::program_options::value< bool >(&outsideSameAsInput)->default_value(false), "copy input image everywhere except at the regions with given range")
            ("outside,y",boost::program_options::value< double >(&outsideVal)->default_value(0), "if outsideSameAsInput is false, sets this value to all regions outside the given range labels.")
            ;

    boost::program_options::variables_map options;
    boost::program_options::store(boost::program_options::parse_command_line(argc,argv,optionsDescription),options);
    boost::program_options::notify(options);

    //If help is asked!
    if(options.count("help")) {
        std::cout<<optionsDescription<<std::endl;
        return EXIT_SUCCESS;
    }

    //Confirm all the options required are given.
    if(!options.count("input") || !options.count("output")
            || !options.count("lower") || !options.count("upper")) {
        std::cerr<<"invalid options! run with --help or -h to see the proper options."<<std::endl;
        return EXIT_FAILURE;
    }

    //---------------------  Read the image type ----------------------//
    typedef itk::Image<double, 3>              ImageType;
    typedef itk::ImageFileReader<ImageType>     ImageReaderType;

    ImageReaderType::Pointer reader = ImageReaderType::New();
    reader->SetFileName(inImgFile);
    reader->Update();
    ImageType::Pointer inImg = reader->GetOutput();

    typedef itk::ImageDuplicator< ImageType > DuplicatorType;
    DuplicatorType::Pointer duplicator = DuplicatorType::New();
    duplicator->SetInputImage(inImg);
    duplicator->Update();
    ImageType::Pointer outImg = duplicator->GetModifiableOutput();
    //fill all pixels with outsideValue or copy image;
    if(!outsideSameAsInput)
            outImg->FillBuffer(outsideVal);

    typedef itk::ImageRegionIterator< ImageType >       IteratorType;
    IteratorType    out_it(outImg,outImg->GetLargestPossibleRegion());
    IteratorType    in_it(inImg,inImg->GetLargestPossibleRegion());

    for(out_it.GoToBegin(), in_it.GoToBegin(); !out_it.IsAtEnd(); ++out_it, ++in_it) {
        {
            double curr_val = in_it.Get();
            if(curr_val >= lowerThr && curr_val <= upperThr )
                out_it.Set(insideVal);
        }
    }

    //------------------------ Write the output Image -------------------------//
    typedef itk::ImageFileWriter< ImageType >       ImageWriterType;
    ImageWriterType::Pointer    writer = ImageWriterType::New();
    writer->SetFileName(outImgFile);
    writer->SetInput(outImg);
    writer->Update();
    return EXIT_SUCCESS;
}


// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//
// The following only applies to changes made to this file as part of YugaByte development.
//
// Portions Copyright (c) YugaByte, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.  You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the License
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
// or implied.  See the License for the specific language governing permissions and limitations
// under the License.
//

#include <boost/lexical_cast.hpp>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include <vector>
#include "yb/util/knapsack_solver.h"
#include "yb/util/stopwatch.h"
#include "yb/util/test_util.h"

using std::vector;

namespace yb {

class TestKnapsack : public YBTest {
};

// A simple test item for use with the knapsack solver.
// The real code will be solving knapsack over RowSet objects --
// using simple value/weight pairs in the tests makes it standalone.
struct TestItem {
  TestItem(double v, int w)
    : value(v), weight(w) {
  }

  double value;
  int weight;
};

// A traits class to adapt the knapsack solver to TestItem.
struct TestItemTraits {
  typedef TestItem item_type;
  typedef double value_type;
  static int get_weight(const TestItem &item) {
    return item.weight;
  }
  static value_type get_value(const TestItem &item) {
    return item.value;
  }
};

// Generate random items into the provided vector.
static void GenerateRandomItems(int n_items, int max_weight,
                                vector<TestItem> *out) {
  for (int i = 0; i < n_items; i++) {
    double value = 10000.0 / (random() % 10000 + 1);
    int weight = random() % max_weight;
    out->push_back(TestItem(value, weight));
  }
}

// Join and stringify the given list of ints.
static string JoinInts(const vector<int> &ints) {
  string ret;
  for (int i = 0; i < ints.size(); i++) {
    if (i > 0) {
      ret.push_back(',');
    }
    ret.append(boost::lexical_cast<string>(ints[i]));
  }
  return ret;
}

TEST_F(TestKnapsack, Basics) {
  KnapsackSolver<TestItemTraits> solver;

  vector<TestItem> in;
  in.push_back(TestItem(500, 3));
  in.push_back(TestItem(110, 1));
  in.push_back(TestItem(125, 1));
  in.push_back(TestItem(100, 1));

  vector<int> out;
  double max_val;

  // For 1 weight, pick item 2
  solver.Solve(in, 1, &out, &max_val);
  ASSERT_DOUBLE_EQ(125, max_val);
  ASSERT_EQ("2", JoinInts(out));
  out.clear();

  // For 2 weight, pick item 1, 2
  solver.Solve(in, 2, &out, &max_val);
  ASSERT_DOUBLE_EQ(110 + 125, max_val);
  ASSERT_EQ("2,1", JoinInts(out));
  out.clear();

  // For 3 weight, pick item 0
  solver.Solve(in, 3, &out, &max_val);
  ASSERT_DOUBLE_EQ(500, max_val);
  ASSERT_EQ("0", JoinInts(out));
  out.clear();

  // For 10 weight, pick all.
  solver.Solve(in, 10, &out, &max_val);
  ASSERT_DOUBLE_EQ(500 + 110 + 125 + 100, max_val);
  ASSERT_EQ("3,2,1,0", JoinInts(out));
  out.clear();
}

// Test which generates random knapsack instances and verifies
// that the result satisfies the constraints.
TEST_F(TestKnapsack, Randomized) {
  SeedRandom();
  KnapsackSolver<TestItemTraits> solver;

  const int kNumTrials = AllowSlowTests() ? 200 : 1;
  const int kMaxWeight = 1000;
  const int kNumItems = 1000;

  for (int i = 0; i < kNumTrials; i++) {
    vector<TestItem> in;
    vector<int> out;
    GenerateRandomItems(kNumItems, kMaxWeight, &in);
    double max_val;
    int max_weight = random() % kMaxWeight;
    solver.Solve(in, max_weight, &out, &max_val);

    // Verify that the max_val is equal to the sum of the chosen items' values.
    double sum_val = 0;
    int sum_weight = 0;
    for (int i : out) {
      sum_val += in[i].value;
      sum_weight += in[i].weight;
    }
    ASSERT_NEAR(max_val, sum_val, 0.000001);
    ASSERT_LE(sum_weight, max_weight);
  }
}

#ifdef NDEBUG
TEST_F(TestKnapsack, Benchmark) {
  KnapsackSolver<TestItemTraits> solver;

  const int kNumTrials = 1000;
  const int kMaxWeight = 1000;
  const int kNumItems = 1000;

  vector<TestItem> in;
  GenerateRandomItems(kNumItems, kMaxWeight, &in);

  LOG_TIMING(INFO, "benchmark") {
    vector<int> out;
    for (int i = 0; i < kNumTrials; i++) {
      out.clear();
      double max_val;
      solver.Solve(in, random() % kMaxWeight, &out, &max_val);
    }
  }
}
#endif

} // namespace yb


#include "lm/sri.hh"

#include <stdlib.h>

#define BOOST_TEST_MODULE SRITest
#include <boost/test/unit_test.hpp>

namespace lm {
namespace sri {
namespace {

#define StartTest(word, ngram, score) \
  ret = model.FullScore( \
      state, \
      model.GetVocabulary().Index(word), \
      out);\
  BOOST_CHECK_CLOSE(score, ret.prob, 0.001); \
  BOOST_CHECK_EQUAL(static_cast<unsigned int>(ngram), ret.ngram_length); \
  BOOST_CHECK_EQUAL(std::min<unsigned char>(ngram, 5 - 1), out.valid_length_);

#define AppendTest(word, ngram, score) \
  StartTest(word, ngram, score) \
  state = out;

template <class M> void Starters(M &model) {
  FullScoreReturn ret;
  Model::State state(model.BeginSentenceState());
  Model::State out;

  StartTest("looking", 2, -0.4846522);

  // , probability plus <s> backoff
  StartTest(",", 1, -1.383514 + -0.4149733);
  // <unk> probability plus <s> backoff
  StartTest("this_is_not_found", 0, -1.995635 + -0.4149733);
}

template <class M> void Continuation(M &model) {
  FullScoreReturn ret;
  Model::State state(model.BeginSentenceState());
  Model::State out;

  AppendTest("looking", 2, -0.484652);
  AppendTest("on", 3, -0.348837);
  AppendTest("a", 4, -0.0155266);
  AppendTest("little", 5, -0.00306122);
  State preserve = state;
  AppendTest("the", 1, -4.04005);
  AppendTest("biarritz", 1, -1.9889);
  AppendTest("not_found", 0, -2.29666);
  AppendTest("more", 1, -1.20632);
  AppendTest(".", 2, -0.51363);
  AppendTest("</s>", 3, -0.0191651);

  state = preserve;
  AppendTest("more", 5, -0.00181395);
  AppendTest("loin", 5, -0.0432557);
}

BOOST_AUTO_TEST_CASE(starters) { Model m("test.arpa", 5); Starters(m); }
BOOST_AUTO_TEST_CASE(continuation) { Model m("test.arpa", 5); Continuation(m); }

} // namespace
} // namespace sri
} // namespace lm


/*
 * uptest.cpp
 *
 *  Created on: Dec 12, 2011
 *      Author: joe
 */

#define BOOST_TEST_MODULE UPTest
#include "../src/config.h"
#define BOOST_TEST_MAIN
#ifdef USE_DYNAMIC_UNIT_TEST
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif
#include <boost/shared_ptr.hpp>
#include <list>
#include <algorithm>
#include "logic/UnitProp.h"
#include "TestUtilities.h"
#include "SISet.h"
#include "SpanInterval.h"
#include "Interval.h"
#include "logic/ELSyntax.h"

BOOST_AUTO_TEST_CASE( qconstraints ) {
    boost::shared_ptr<Sentence> s = getAsSentence("<>{m} P(a)");
    boost::shared_ptr<DiamondOp> dia = boost::dynamic_pointer_cast<DiamondOp>(s);

    TQConstraints constraints(Interval(1,2));
    constraints.mustBeIn.add(SpanInterval(1,2,1,2));
    constraints.mustNotBeIn.add(SpanInterval(1,1,1,1));

    boost::shared_ptr<DiamondOp> diaCopy(new DiamondOp(dia->sentence(),dia->relations().begin(), dia->relations().end(), &constraints));
    BOOST_CHECK_EQUAL(diaCopy->toString(), "<>{m:&{[1:2]},\\{[1:1]}} P(a)");
}

BOOST_AUTO_TEST_CASE( simple_lit ) {
    std::string facts = "video(a) @ [1:20]";
    std::string formulas = "P(a) @ [1:3]\n"
            "!P(a) @ [8:11]\n"
            "Q(a) @ [1:10]\n"
            "P(a) v Q(a) v R(a) @ [1:20]";

    Domain d = loadDomainWithStreams(facts, formulas);

    std::vector<ELSentence> flist(d.formulas_begin(), d.formulas_end());
    QCNFClauseList clauseList = convertToQCNFClauseList(flist);

    QUnitsFormulasPair result = performUnitPropagation(clauseList);

    for(QCNFLiteralList::const_iterator it = result.first.begin(); it != result.first.end(); it++) {
        QCNFLiteral lit = *it;
        ELSentence s = convertFromQCNFClause(lit);
        std::cout << "unit clause: " << s.toString() << std::endl;
    }

    for(QCNFClauseList::const_iterator it = result.second.begin(); it != result.second.end(); it++) {
        QCNFClause c = *it;
        ELSentence s = convertFromQCNFClause(c);
        std::cout << "formula: " << s.toString() << std::endl;
    }
    BOOST_REQUIRE_EQUAL(result.first.size(), 3);
    // TODO: write test
}

BOOST_AUTO_TEST_CASE( dia_lit ) {
    std::string facts = "video(a) @ [1:20]";
    std::string formulas = "P(a) @ [1:3]\n"
            "<>{d} P(a) v Q(a) @ [1:4]\n";

    Domain d = loadDomainWithStreams(facts, formulas);

    std::vector<ELSentence> flist(d.formulas_begin(), d.formulas_end());
    QCNFClauseList clauseList = convertToQCNFClauseList(flist);

    QUnitsFormulasPair result = performUnitPropagation(clauseList);

    for(QCNFLiteralList::const_iterator it = result.first.begin(); it != result.first.end(); it++) {
        QCNFLiteral lit = *it;
        ELSentence s = convertFromQCNFClause(lit);
        std::cout << "unit clause: " << s.toString() << std::endl;
    }

    for(QCNFClauseList::const_iterator it = result.second.begin(); it != result.second.end(); it++) {
        QCNFClause c = *it;
        ELSentence s = convertFromQCNFClause(c);
        std::cout << "formula: " << s.toString() << std::endl;
    }
}

BOOST_AUTO_TEST_CASE( simple_lit_directly ) {

    std::string facts = "video(a) @ [1:20]";
    std::string formulas = "P(a) @ [1:10]\n"
            "Q(a) @ [1:10]\n"
            "P(a) v Q(a) @ [1:20]";

    Domain d = loadDomainWithStreams(facts, formulas);

    std::vector<ELSentence> flist(d.formulas_begin(), d.formulas_end());
    ELSentence s1 = flist[0];
    ELSentence s2 = flist[1];
    ELSentence s3 = flist[2];

    QCNFClause qs1 = convertToQCNFClause(s1);
    QCNFClause qs2 = convertToQCNFClause(s2);
    QCNFClause qs3 = convertToQCNFClause(s3);

    QCNFLiteral l1(qs1.first.front(), qs1.second);
    /*
    l1.first = qs1.first.front();
    l1.second = qs1.second;
    */
    QCNFClauseList list = propagateLiteral(l1, qs3);
    BOOST_REQUIRE_EQUAL(list.size(), 1);
    BOOST_CHECK_EQUAL(list.front().second.toString(), "{[(1, 10), (11, 20)], [11:20]}");

    // try wrapping l1 in a negation and propagating
    boost::shared_ptr<Sentence> negl1(new Negation(l1.first));
    l1.first = negl1;

    list = propagateLiteral(l1, qs3);
    BOOST_REQUIRE_EQUAL(list.size(), 2);
    QCNFClauseList::const_iterator it = list.begin();
    ELSentence first = convertFromQCNFClause(*it);
    it++;
    ELSentence second = convertFromQCNFClause(*it);
    BOOST_CHECK_EQUAL(first.sentence()->toString(), "Q(a)");
    BOOST_CHECK_EQUAL(first.quantification().toString(), "{[1:10]}");

    BOOST_CHECK_EQUAL(second.sentence()->toString(), "P(a) v Q(a)");
    BOOST_CHECK_EQUAL(second.quantification().toString(), "{[(1, 10), (11, 20)], [11:20]}");
}

BOOST_AUTO_TEST_CASE( simpleLitToLiq) {
    std::string facts = "P(a) @ [1:20]\n";
    std::string formulas = "[ P(a) ] v Q(a) @ [1:30]\n";

    Domain d = loadDomainWithStreams(facts, formulas);
    BOOST_CHECK_EQUAL(d.formulas_size(), 1);
    Domain newD = performUnitPropagation(d);
    BOOST_REQUIRE_EQUAL(newD.formulas_size(), 1);

    std::ostringstream str;
    std::copy(newD.formulas_begin(), newD.formulas_end(), std::ostream_iterator<ELSentence>(str, ", "));
    BOOST_CHECK_EQUAL(str.str(), "inf: [ P(a) ] v Q(a) @ {[(1, 20), (21, 30)], [21:30]}, ");
    //str.str("");
    //std::copy(newD.facts_begin(), newD.facts_end(), std::ostream_iterator<ELSentence>(str, ", "));
    /*
    std::vector<std::pair<Proposition, SISet> > newFacts(newD.facts_begin(), newD.facts_end());
    BOOST_REQUIRE_EQUAL(newFacts.size(), 1);
    BOOST_CHECK_EQUAL(newf, "P(a) @ {[1:20]}, !P(a) @ {[21:30]}, Q(a) @ {[(1, 20), (21, 30)], [21:30]}, ");
    */

}

BOOST_AUTO_TEST_CASE( cnfConvertBasic ) {

    boost::shared_ptr<Sentence> a = getAsSentence("P(a) v Q(a) v !R(a) v S(a)");
    CNFClause c = convertToCNFClause(a);

    BOOST_REQUIRE_EQUAL(c.size(), 4);
    boost::shared_ptr<Sentence> pa = getAsSentence("P(a)");
    boost::shared_ptr<Sentence> qa = getAsSentence("Q(a)");
    boost::shared_ptr<Sentence> nra = getAsSentence("!R(a)");
    boost::shared_ptr<Sentence> sa = getAsSentence("S(a)");
    /* TODO: why doesn't the below work?
    BOOST_CHECK(std::find(c.begin(), c.end(), pa) != c.end());
    BOOST_CHECK(std::find(c.begin(), c.end(), qa) != c.end());
    BOOST_CHECK(std::find(c.begin(), c.end(), nra) != c.end());
    BOOST_CHECK(std::find(c.begin(), c.end(), sa) != c.end());
     */
}

BOOST_AUTO_TEST_CASE( contradictionTest ) {
    std::string facts = "P(a) @ [1:20]\n"
                        "Q(a) @ [20:30]\n";
    std::string formulas = "inf: [!P(a) v !Q(a)] @ [1:30]\n";
    Domain d = loadDomainWithStreams(facts, formulas);
    BOOST_CHECK_THROW(performUnitPropagation(d), contradiction);

}


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

    wraps Boost or regular (std::rand) random number generators. doesn't seed
    in a hacker-safe way (pid and time are child's play for a local attacker)
    unless GRAEHL_USE_RANDOM_DEVICE
*/

#ifndef GRAEHL_SHARED__RANDOM_HPP
#define GRAEHL_SHARED__RANDOM_HPP
#pragma once
#include <graehl/shared/cpp11.hpp>

/// std mt19937_64 is not as fast as boost lagged_fibonacci607 and have not
/// heard any quality complaints against latter (only downside: uses 4kbyte of
/// memory but probably only touches some of it for each gen). recommend preferring boost
#define GRAEHL_PREFER_BOOST_RANDOM 1

#if GRAEHL_CPP11 && !GRAEHL_PREFER_BOOST_RANDOM
// TODO: implement 1 here
#define GRAEHL_PREFER_CPP11_RANDOM 0
#else
#define GRAEHL_PREFER_CPP11_RANDOM 0
#endif

#ifndef GRAEHL_USE_RANDOM_DEVICE
#define GRAEHL_USE_RANDOM_DEVICE 1
#endif

#ifndef GRAEHL_GLOBAL_RANDOM_USE_STD
#define GRAEHL_GLOBAL_RANDOM_USE_STD 0
#endif

#include <graehl/shared/os.hpp>
#include <boost/optional.hpp>
#include <algorithm>  // min for boost/random
#include <cmath>  // also needed for boost/random :( (pow)
#include <ctime>

#include <graehl/shared/warning_push.h>
GCC_DIAG_IGNORE(attributes)

#if GRAEHL_USE_RANDOM_DEVICE
#include <boost/random/random_device.hpp>
#endif

// TODO: c++11 std::random

#if GRAEHL_PREFER_CPP11_RANDOM
#include <random>
#else
//#include <boost/random.hpp>
//#include <boost/random/generate_canonical.hpp>
//#include <boost/random/seed_seq.hpp>
#include <boost/random/lagged_fibonacci.hpp>
#include <boost/random/random_number_generator.hpp>
#include <boost/random/uniform_01.hpp>
#include <boost/random/variate_generator.hpp>
#endif

#include <graehl/shared/warning_pop.h>

#if GRAEHL_GLOBAL_RANDOM_USE_STD
#include <cstdlib>
#endif

#ifdef GRAEHL_TEST
#include <graehl/shared/test.hpp>
#include <cctype>
#endif

#if !GRAEHL_CPP11
#include <boost/scoped_array.hpp>
#endif

namespace graehl {

typedef unsigned random_seed_type;  // this is what random_device actually returns.

struct std_rand {
  unsigned operator()() const { return std::rand(); }  // NOLINT
};

/**
   for a series of runs with different, but reproducible, seeds.
*/
inline random_seed_type debugging_random_seed(random_seed_type n) {
  return (n + 1) * 2654435769U;
}

inline random_seed_type default_random_seed() {
// long pid=get_process_id();
#if GRAEHL_USE_RANDOM_DEVICE
  return boost::random_device().operator()();
#else
  random_seed_type pid = (random_seed_type)get_process_id();
  return (random_seed_type)std::time(0) ^ pid ^ (pid << 17);
#endif
}

/**
   allow configuring constant initial seeds.
*/
struct random_seed {
  boost::optional<random_seed_type> seed;
  template <class Configure>
  void configure(Configure& c) {
    c("random-seed", &seed)("if set, use as initial seed for RNG");
    c.is("random_seed");
  }
  void operator=(random_seed_type x) { seed = x; }
  operator random_seed_type() const { return seed ? *seed : default_random_seed(); }
};

#if GRAEHL_PREFER_CPP11_RANDOM
// TODO: different API:
/**
   random_generator random_gen;
   uniform_real_dist uniform01(0, 1);
   double r = uniform01(random_gen);
*/
typedef std::mt19937_64 random_generator;
typedef std::uniform_real_distribution<double> uniform_real_dist;
#else
typedef boost::lagged_fibonacci607 random_generator;
// lagged_fibonacci607 is the fastest for generating random floats and only 20% slower for ints - see
// http://www.boost.org/doc/libs/1_59_0/doc/html/boost_random/performance.html
typedef boost::uniform_01<double> uniform_01_dist;
typedef boost::variate_generator<random_generator, uniform_01_dist> random_01_generator;
#endif

#if !defined(GRAEHL__NO_G_RANDOM01)

#if !GRAEHL_GLOBAL_RANDOM_USE_STD
/**
   global (thread-unsafe).
*/
#if (!defined(GRAEHL__NO_RANDOM_MAIN) && defined(GRAEHL__SINGLE_MAIN)) || defined(GRAEHL__RANDOM_MAIN)
namespace {
// random_generator g_random_gen(default_random_seed());
}
random_01_generator g_random01((random_generator(default_random_seed())), uniform_01_dist());
#else
extern random_01_generator g_random01;
#endif
#endif

inline void set_random_seed(random_seed_type value = default_random_seed()) {
#if GRAEHL_GLOBAL_RANDOM_USE_STD
  srand(value);
#else
  g_random01.engine().seed(value);
#endif
}

// FIXME: use boost random? and can't necessarily port executable across platforms with different rand syscall
// :(
inline double random01()  // returns uniform random number on [0..1)
{
#if GRAEHL_GLOBAL_RANDOM_USE_STD
  return ((double)std::rand()) * (1. / ((double)RAND_MAX + 1.));
#else
  return g_random01();
#endif
}

#include <graehl/shared/random.ipp>
#endif

struct set_random_pos_fraction {
  template <class C>
  void operator()(C& c) {
    c = random_pos_fraction();
  }
};

/**
   use explicit random state for thread safety.
*/
struct random {
  typedef double result_type;
  random_01_generator random01;
  result_type operator()() { return random01(); }
  random(random_seed_type seed = default_random_seed())
      : random01(random_generator(seed), uniform_01_dist()) {}
  void set_random_seed(random_seed_type value = default_random_seed()) { random01.engine().seed(value); }
#include <graehl/shared/random.ipp>
};

#ifdef GRAEHL_TEST
BOOST_AUTO_TEST_CASE(TEST_RANDOM) {
  using namespace std;
  const unsigned NREP = 10000;
  for (unsigned i = 1; i < NREP; ++i) {
    unsigned ran_lt_i = random_less_than(i);
    BOOST_CHECK(0 <= ran_lt_i && ran_lt_i < i);
    BOOST_CHECK(std::isalpha(random_alpha()));
    char r_alphanum = random_alphanum();
    BOOST_CHECK(std::isalpha(r_alphanum) || std::isdigit(r_alphanum));
  }
}
#endif

}

#endif


#ifdef STAN_OPENCL

#include <stan/math/opencl/kernel_generator.hpp>
#include <stan/math/opencl/matrix_cl.hpp>
#include <stan/math/opencl/copy.hpp>
#include <Eigen/Dense>
#include <gtest/gtest.h>

using Eigen::Matrix;
using Eigen::MatrixXd;
using Eigen::MatrixXi;
using stan::math::matrix_cl;

#define EXPECT_MATRIX_NEAR(A, B, DELTA) \
  EXPECT_EQ(A.rows(), B.rows());        \
  EXPECT_EQ(A.cols(), B.cols());        \
  for (int i = 0; i < A.size(); i++)    \
    EXPECT_NEAR(A(i), B(i), DELTA);

TEST(KernelGenerator, is_without_output_test) {
  EXPECT_TRUE((stan::math::is_without_output<
               stan::math::calc_if_<false, stan::math::scalar_<int>>>::value));
  EXPECT_TRUE((stan::math::is_without_output<const stan::math::calc_if_<
                   false, stan::math::scalar_<int>>&>::value));
  EXPECT_FALSE((stan::math::is_without_output<
                stan::math::calc_if_<true, stan::math::scalar_<int>>>::value));
  EXPECT_FALSE(
      (stan::math::is_without_output<stan::math::scalar_<int>>::value));
}

TEST(KernelGenerator, calc_if_pass_test) {
  double eps = 0;
  MatrixXd m(2, 2);
  m << 1, 2, 3, 4;
  matrix_cl<double> m_cl(m);

  matrix_cl<double> res_cl = stan::math::calc_if<true>(m_cl);
  MatrixXd res = stan::math::from_matrix_cl(res_cl);

  EXPECT_MATRIX_NEAR(res, m, eps);
}

TEST(KernelGenerator, calc_if_multi_result_pass_test) {
  double eps = 0;
  MatrixXd m(2, 2);
  m << 1, 2, 3, 4;
  matrix_cl<double> m_cl(m);

  auto tmp = stan::math::calc_if<true>(m_cl);
  matrix_cl<double> res_cl;
  stan::math::results(res_cl) = stan::math::expressions(tmp);
  MatrixXd res = stan::math::from_matrix_cl(res_cl);

  EXPECT_MATRIX_NEAR(res, m, eps);
}

TEST(KernelGenerator, calc_if_multi_result_no_output_test) {
  double eps = 0;
  MatrixXd m(2, 2);
  m << 1, 2, 3, 4;
  matrix_cl<double> m_cl(m);

  auto tmp = stan::math::calc_if<false>(m_cl);
  matrix_cl<double> res_cl;
  stan::math::results(res_cl) = stan::math::expressions(tmp);
  EXPECT_EQ(res_cl.size(), 0);
}

TEST(KernelGenerator, calc_if_multi_result_one_output_test) {
  double eps = 0;
  MatrixXd m(2, 2);
  m << 1, 2, 3, 4;
  matrix_cl<double> m_cl(m);

  matrix_cl<double> res1_cl;
  matrix_cl<double> res2_cl;
  stan::math::results(res1_cl, res2_cl) = stan::math::expressions(
      stan::math::calc_if<false>(m_cl + 1), stan::math::calc_if<true>(m_cl));
  EXPECT_EQ(res1_cl.size(), 0);

  MatrixXd res = stan::math::from_matrix_cl(res2_cl);
  EXPECT_MATRIX_NEAR(res, m, eps);
}

TEST(KernelGenerator, calc_if_colwise_min_test) {
  MatrixXd m(3, 2);
  m << 1.1, 1.2, 1.3, 1.4, 1.5, 1.6;

  matrix_cl<double> m_cl(m);

  matrix_cl<double> res_cl
      = stan::math::calc_if<true>(stan::math::colwise_min(m_cl));
  MatrixXd raw_res = stan::math::from_matrix_cl(res_cl);
  EXPECT_GE(m.rows(), raw_res.rows());
  MatrixXd res = raw_res.colwise().minCoeff();
  MatrixXd correct = m.colwise().minCoeff();
  EXPECT_EQ(correct.rows(), res.rows());
  EXPECT_EQ(correct.cols(), res.cols());
  EXPECT_MATRIX_NEAR(correct, res, 1e-9);
}

#endif


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#ifndef MTL_MATRIX_BRACKETS_INCLUDE
#define MTL_MATRIX_BRACKETS_INCLUDE

#include <algorithm>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/numeric/mtl/mtl_fwd.hpp>
#include <boost/numeric/mtl/utility/irange.hpp>
#include <boost/numeric/mtl/utility/iset.hpp>
#include <boost/numeric/mtl/concept/collection.hpp>
#include <boost/numeric/mtl/operation/column_in_matrix.hpp>
#include <boost/numeric/mtl/operation/row_in_matrix.hpp>

namespace mtl { namespace operations {

    template <typename Matrix, typename Ref, typename ValueRef>
    struct bracket_proxy
    {
	typedef typename Matrix::value_type      value_type;
	typedef typename Matrix::size_type       size_type;
	typedef RowInMatrix<typename boost::remove_reference<Ref>::type> row_traits;

	explicit bracket_proxy(Ref matrix, size_type row) : matrix(matrix), row(row) {}

	template <typename Size>
	typename boost::enable_if<boost::is_integral<Size>, ValueRef>::type
	operator[] (Size col) { return matrix(row, size_type(col));	}

	template <typename T> struct my_traits { static const bool value= boost::is_same<T, mtl::irange>::value && row_traits::exists; };

	template <typename T>
	typename boost::lazy_enable_if_c<my_traits<T>::value, row_traits>::type
	operator[] (const T& col_range)
	{
	    return row_traits::apply(matrix, row, col_range);
	}
      protected:
	Ref         matrix;
	size_type   row;
    };

    template <typename Matrix, typename Ref, typename ValueRef>
    struct range_bracket_proxy
    {
	typedef typename Matrix::size_type       size_type;
	typedef ColumnInMatrix<typename boost::remove_reference<Ref>::type> col_traits;

	explicit range_bracket_proxy(Ref matrix, const irange& row_range) : matrix(matrix), row_range(row_range) {}

	ValueRef operator[] (const irange& col_range)
	{
	    return sub_matrix(matrix, row_range.start(), row_range.finish(),
			      col_range.start(), col_range.finish());
	}

	template <typename T> struct my_traits { static const bool value = boost::is_integral<T>::value && col_traits::exists; };

	template <typename T>
	typename boost::lazy_enable_if_c<my_traits<T>::value, col_traits>::type
	operator[] (T col)  { return col_traits::apply(matrix, row_range, col); }

      protected:
	Ref         matrix;
	irange      row_range;
    };

    template <typename Matrix, typename Ref, typename ValueRef>
    struct set_bracket_proxy
    {
	set_bracket_proxy(Ref matrix, const iset& row_set) : matrix(matrix), row_set(row_set) {}

	mtl::mat::indirect<Matrix> operator[] (const iset& col_set)
	{
	    return mtl::mat::indirect<Matrix>(matrix, row_set, col_set);
	}

      protected:
	Ref         matrix;
	iset        row_set;
     };

} // namespace operations

} // NAMESPACE mtl

#endif // MTL_MATRIX_BRACKETS_INCLUDE


#include <iostream>
#include <cmath>
#include <complex>
#include <vector>
#include <cstddef>
#include <cassert>
#include <cstdlib>
#include <iomanip>
#include <ios>
#include <fstream>
#include <deque>
#include <chrono>

#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>

#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>

#include "cmdline.h"
#include "mandel_hp.h"
//#include "makemovie.h"

using boost::asio::ip::tcp;
using namespace std::literals;

cmdline cmd;

gboolean idle_func(gpointer data);

extern gboolean delete_event(GtkWidget* widget, GdkEvent* event, gpointer data);

gboolean nil_cb(GtkWidget*, gpointer) { return TRUE; }

GtkWidget* log13[13];
std::vector<std::string> logs;
std::deque<std::string> logtop(13);

extern std::string add_time(std::string s);

void add_log(std::string s)
{
	logtop.push_back(s);
	logtop.pop_front();

	logs.push_back(add_time(s));

	for (int i=0; i<13; ++i)
	{
		auto str = logtop[i];
		if (i==12)
			str = ">>> "s + str.c_str() + " <<<"s;
		gtk_label_set_text(GTK_LABEL(log13[i]), str.c_str());
	}
}

void add_note(std::string s)
{
	logs.push_back(add_time(s));
}

[[maybe_unused]] constexpr int max_length = 1024;

struct Item
{
	Item(std::string name)
		: name(name), value()
	{}
	std::string name;
	std::string value;
};

std::vector<Item> params = {
	"client-id"s, "center-x"s, "center-y"s, "update-cap"s, "zoom-start"s,
	"width"s, "height"s, "col-base"s, "col-pow"s
};

struct Connection
{
	Connection(std::string addr, std::string port)
		: io_service()
		, resolver(io_service)
		, query(tcp::v4(), addr, port)
		, iterator(resolver.resolve(query))
		, sock(io_service)
	{
		boost::asio::connect(sock, iterator);
	}

	boost::asio::io_service io_service;
    tcp::resolver resolver;
    tcp::resolver::query query;
    tcp::resolver::iterator iterator;
    tcp::socket sock;
};

std::unique_ptr<Connection> connection;

GtkWidget* window;
GtkWidget* edit;

auto snd_rcv(std::string msg) -> std::string
{
	add_log("Sending : "s + msg);

	boost::asio::write(connection->sock, boost::asio::buffer(msg.c_str(), msg.size()));

	char reply[max_length];
	[[maybe_unused]] size_t reply_length;
	reply_length = boost::asio::read(connection->sock, boost::asio::buffer(reply, max_length));

	std::string ret((char*)reply);
	add_log("Got answer : "s + ret);
	return ret;
}

bool have_job = false;
int job_start, job_len, job_curr;

#define DV std::declval

struct pick_3          {};
struct pick_2 : pick_3 {};
struct pick_1 : pick_2 {};

template<typename T>
auto get_param(pick_2, std::string name)
	-> decltype( DV<std::istream&>() >> DV<T&>() , T{} )
{
	for (auto&& x : params)
	{
		if (name == x.name)
		{
			T ret;
			std::stringstream ss;
			ss.str(x.value.c_str());
			ss >> ret;
			return ret;
		}
	}
	return {};
}

template<typename T>
auto get_param(pick_1, std::string name)
	-> decltype( T{std::string{}}, T{} )
{
	for (auto&& x : params)
	{
		if (name == x.name)
		{
			T ret{x.value.c_str()};
			return ret;
		}
	}
	return {};
}

template<typename T>
auto get_param(pick_3, std::string name) -> T
{
	for (auto&& x : params)
	{
		if (name == x.name)
		{
			return (T)x.value.c_str();
		}
	}
	return {};
}

template<typename T>
auto get_param(std::string name) -> auto
{
	return get_param<T>(pick_1{}, name);
}

void client_start()
{
	add_log("Start");
	std::string ed = gtk_entry_get_text(GTK_ENTRY(edit));
	auto p = ed.find(':');
	std::string addr = ed.substr(0,p);
	std::string port = ed.substr(p+1);

	connection = std::make_unique<Connection>(addr,port);

	for (auto& itm : params)
	{
		itm.value = snd_rcv("send:"s + itm.name);
	}
}

auto mkname(int i) -> std::string
{
	std::string name = "./temp/img_";
	static char buff[32];
	sprintf(buff, "%05d", i);
	name += buff;
	name += ".bmp";
	return name;
};

void make_10(int f)
{
	Map<FltH> m;
	m.width    = get_param<int>("width"s);
	m.height   = get_param<int>("height"s);
	m.center_x = get_param<FltH>("center-x"s);
	m.center_y = get_param<FltH>("center-y"s);
	m.zoom_mul = 0.99;

	static double mod_base = get_param<double>("col-base"s);
	static double mod_pow  = get_param<double>("col-pow"s);

	FltH z = get_param<FltH>("zoom-start"s);
	for (int i=0; i<f; ++i)
		z *= m.zoom_mul;

	m.scale_x = z;
	m.scale_y = (z * (FltH)m.height) / (FltH)m.width;

	ModFunc mod_func = [](double d) -> float { return mod_base / (float)pow(d, mod_pow); };

	UL update_cap = get_param<UL>("update-cap"s);
	UL maxout = m.generate_N_threaded(10, update_cap, true);

	for (int j=0; j<10; ++j)
	{
		auto curr_name = mkname(f+j);
		auto img = m.makeimage_N(j, mod_func);
		img.Save(curr_name);
		//mm::AddFrame(img);
	}

	add_log("wrote 10 images");

	(void)maxout;
}

gboolean idle_func([[maybe_unused]] gpointer data)
{
	if (!connection) return TRUE;

	if (!have_job)
	{
		auto ret = snd_rcv("request:"s + params[0].value);
		auto p = ret.find(',');
		job_start = std::stoi(ret.substr(0, p));
		job_len = std::stoi(ret.substr(p+1));
		add_log("Starting job : "s + ret);
		job_curr = job_start;
		have_job = true;

		[[maybe_unused]] int w = get_param<int>("width");
		[[maybe_unused]] int h = get_param<int>("height");
		//mm::SetupMovie(w,h, "./temp/mov_"s + std::to_string(job_curr) + ".mp4" );

		return TRUE;
	}

	if (!have_job) return TRUE;

	if (job_curr < (job_start+job_len))
	{
		make_10(job_curr);
		job_curr += 10;
		return TRUE;
	}

	//mm::Encode();
	add_log("finished one batch");
	have_job = false;

	return TRUE;
}

extern void do_log();

typedef void (*VFunc)();

struct Btn
{
	GtkWidget* btn;
	std::string lbl;
	VFunc func;
};

std::vector<Btn> btn_lst = {
	Btn{nullptr, "Start" , client_start },
	Btn{nullptr, "Stop"  , [](){add_log("Stop");} },
	Btn{nullptr, "Log"   , do_log },
	Btn{nullptr, "Exit"  , gtk_main_quit }
};

gboolean button_press(GtkWidget* widget, GdkEventButton* event, gpointer data)
{
	(void)widget;
	(void)event;
	(void)data;

	if (event->type != GDK_BUTTON_PRESS)
		return TRUE;

	VFunc func = (VFunc)data;
	func();

	return TRUE;
}

void gtk_app()
{
	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(delete_event), nullptr);

	gtk_container_set_border_width(GTK_CONTAINER(window), 8);

	GtkWidget* pane = gtk_hpaned_new();
	GtkWidget* buttons = gtk_vbutton_box_new();
	gtk_widget_show(buttons);

	for (auto&& btn : btn_lst)
	{
		btn.btn = gtk_button_new_with_label(btn.lbl.c_str());
		gtk_widget_show(btn.btn);
		gtk_container_add(GTK_CONTAINER(buttons), btn.btn);
		g_signal_connect(GTK_OBJECT(btn.btn), "button-press-event", G_CALLBACK(button_press), (void*)btn.func);
	}

	GtkWidget *frame_log = gtk_vbox_new(TRUE, 1);
	gtk_widget_show(frame_log);

	edit = gtk_entry_new();
	gtk_widget_show(edit);
	gtk_entry_set_text(GTK_ENTRY(edit), "127.0.0.1:9090");
	gtk_box_pack_start(GTK_BOX(frame_log), edit, TRUE, TRUE, 0);

	for (int i=0; i<13; ++i)
	{
		log13[i] = gtk_label_new("");
		gtk_box_pack_start(GTK_BOX(frame_log), log13[i], TRUE, TRUE, 0);
	}

	gtk_paned_pack1(GTK_PANED (pane), frame_log, TRUE, TRUE);
	gtk_paned_pack2(GTK_PANED (pane), buttons, FALSE, FALSE);

	gtk_container_add(GTK_CONTAINER(window), pane);
	gtk_widget_show(pane);

	gtk_widget_show_all(window);
	gtk_widget_show(window);
	gtk_idle_add(&idle_func, nullptr);
	gtk_main();
}


#ifndef NFP_HPP_
#define NFP_HPP_

#include <iostream>
#include <list>
#include <string>
#include <fstream>
#include <streambuf>
#include <vector>
#include <set>
#include <exception>
#include <random>
#include <limits>

#ifdef LIBNFP_USE_RATIONAL
#include <boost/multiprecision/gmp.hpp>
#include <boost/multiprecision/number.hpp>
#endif
#include <boost/geometry.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/io/svg/svg_mapper.hpp>
#include <boost/geometry/algorithms/intersects.hpp>
#include <boost/geometry/geometries/register/point.hpp>

#ifdef LIBNFP_USE_RATIONAL
namespace bm = boost::multiprecision;
#endif
namespace bg = boost::geometry;
namespace trans = boost::geometry::strategy::transform;

namespace libnfp {
#ifdef NFP_DEBUG
#define DEBUG_VAL(x) std::cerr << x << std::endl;
#define DEBUG_MSG(title, value) std::cerr << title << ":" << value << std::endl;
#else
#define DEBUG_VAL(x)
#define DEBUG_MSG(title, value)
#endif

using std::string;

static constexpr long double NFP_EPSILON=0.00000001;

class LongDouble {
private:
	long double val_;
public:
	LongDouble() : val_(0) {
	}

	LongDouble(const long double& val) : val_(val) {
	}

	void setVal(const long double& v) {
		val_ = v;
	}

	long double val() const {
		return val_;
	}

	LongDouble operator/(const LongDouble& other) const {
		return this->val_ / other.val_;
	}

	LongDouble operator*(const LongDouble& other) const {
		return this->val_ * other.val_;
	}

	LongDouble operator-(const LongDouble& other) const {
		return this->val_ - other.val_;
	}

	LongDouble operator-() const {
		return this->val_ * -1;
	}

	LongDouble operator+(const LongDouble& other) const {
		return this->val_ + other.val_;
	}

	void operator/=(const LongDouble& other) {
		this->val_ = this->val_ / other.val_;
	}

	void operator*=(const LongDouble& other) {
		this->val_ = this->val_ * other.val_;
	}

	void operator-=(const LongDouble& other) {
		this->val_ = this->val_ - other.val_;
	}

	void operator+=(const LongDouble& other) {
		this->val_ = this->val_ + other.val_;
	}

	bool operator==(const int& other) const {
		return this->operator ==(static_cast<long double>(other));
	}

	bool operator==(const LongDouble& other) const {
		return this->operator ==(other.val());
	}

	bool operator==(const long double& other) const {
		return this->val() == other;
	}

	bool operator!=(const int& other) const {
		return !this->operator ==(other);
	}

	bool operator!=(const LongDouble& other) const {
		return !this->operator ==(other);
	}

	bool operator!=(const long double& other) const {
		return !this->operator ==(other);
	}

	bool operator<(const int& other) const {
		return this->operator <(static_cast<long double>(other));
	}

	bool operator<(const LongDouble& other) const {
		return this->operator <(other.val());
	}

	bool operator<(const long double& other) const {
		return this->val() < other;
	}

	bool operator>(const int& other) const {
		return this->operator >(static_cast<long double>(other));
	}

	bool operator>(const LongDouble& other) const {
		return this->operator >(other.val());
	}

	bool operator>(const long double& other) const {
		return this->val() > other;
	}

	bool operator>=(const int& other) const {
		return this->operator >=(static_cast<long double>(other));
	}

	bool operator>=(const LongDouble& other) const {
		return this->operator >=(other.val());
	}

	bool operator>=(const long double& other) const {
		return this->val() >= other;
	}

	bool operator<=(const int& other) const {
		return this->operator <=(static_cast<long double>(other));
	}

	bool operator<=(const LongDouble& other) const {
		return this->operator <=(other.val());
	}

	bool operator<=(const long double& other) const {
		return this->val() <= other;
	}
};
}

namespace std {
template<>
   struct numeric_limits<libnfp::LongDouble>
   {
     static constexpr bool is_specialized = true;

     static constexpr long double
     min() noexcept { return std::numeric_limits<long double>::min(); }

     static constexpr long double
     max() noexcept { return std::numeric_limits<long double>::max(); }

#if __cplusplus >= 201103L
     static constexpr long double
     lowest() noexcept { return -std::numeric_limits<long double>::lowest(); }
#endif

	 static constexpr int digits = std::numeric_limits<long double>::digits;
     static constexpr int digits10 = std::numeric_limits<long double>::digits10;
#if __cplusplus >= 201103L
     static constexpr int max_digits10
	 = std::numeric_limits<long double>::max_digits10;
#endif
     static constexpr bool is_signed = true;
     static constexpr bool is_integer = false;
     static constexpr bool is_exact = false;
     static constexpr int radix = std::numeric_limits<long double>::radix;

     static constexpr long double
     epsilon() noexcept { return libnfp::NFP_EPSILON; }

     static constexpr long double
     round_error() noexcept { return 0.5L; }

     static constexpr int min_exponent = std::numeric_limits<long double>::min_exponent;
     static constexpr int min_exponent10 = std::numeric_limits<long double>::min_exponent10;
     static constexpr int max_exponent = std::numeric_limits<long double>::max_exponent;
     static constexpr int max_exponent10 = std::numeric_limits<long double>::max_exponent10;

     static constexpr bool has_infinity = std::numeric_limits<long double>::has_infinity;
     static constexpr bool has_quiet_NaN = std::numeric_limits<long double>::has_quiet_NaN;
     static constexpr bool has_signaling_NaN = has_quiet_NaN;
	 static constexpr float_denorm_style has_denorm
		 = std::numeric_limits<long double>::has_denorm;
     static constexpr bool has_denorm_loss
	     = std::numeric_limits<long double>::has_denorm_loss;

     static constexpr long double
		infinity() noexcept { return std::numeric_limits<long double>::infinity(); }

     static constexpr long double
		 quiet_NaN() noexcept { return std::numeric_limits<long double>::quiet_NaN(); }

     static constexpr long double
		 signaling_NaN() noexcept { return std::numeric_limits<long double>::signaling_NaN(); }

     static constexpr long double
		 denorm_min() noexcept { return std::numeric_limits<long double>::denorm_min(); }

     static constexpr bool is_iec559
	 = has_infinity && has_quiet_NaN && has_denorm == denorm_present;

     static constexpr bool is_bounded = true;
     static constexpr bool is_modulo = false;

     static constexpr bool traps = std::numeric_limits<long double>::traps;
     static constexpr bool tinyness_before =
    		 std::numeric_limits<long double>::tinyness_before;
     static constexpr float_round_style round_style =
						      round_to_nearest;
   };
}

namespace boost {
namespace numeric {
	template<>
	struct raw_converter<boost::numeric::conversion_traits<double, libnfp::LongDouble>>
	{
		typedef typename boost::numeric::conversion_traits<double, libnfp::LongDouble>::result_type   result_type   ;
		typedef typename boost::numeric::conversion_traits<double, libnfp::LongDouble>::argument_type argument_type ;

		static result_type low_level_convert ( argument_type s ) { return s.val() ; }
	} ;
}
}

namespace libnfp {

#ifndef LIBNFP_USE_RATIONAL
typedef LongDouble coord_t;
#else
typedef bm::number<bm::gmp_rational, bm::et_off> rational_t;
typedef rational_t coord_t;
#endif

bool equals(const LongDouble& lhs, const LongDouble& rhs);
#ifdef LIBNFP_USE_RATIONAL
bool equals(const rational_t& lhs, const rational_t& rhs);
#endif
bool equals(const long double& lhs, const long double& rhs);

const coord_t MAX_COORD = 999999999999999999;
const coord_t MIN_COORD = std::numeric_limits<coord_t>::min();

class point_t {
public:
	point_t() : x_(0), y_(0) {
	}
	point_t(coord_t x, coord_t y) : x_(x), y_(y) {
	}
	bool marked_ = false;
	coord_t x_;
	coord_t y_;

	point_t operator-(const point_t& other) const {
		point_t result = *this;
		bg::subtract_point(result, other);
		return result;
	}

	point_t operator+(const point_t& other) const {
		point_t result = *this;
		bg::add_point(result, other);
		return result;
	}

	bool operator==(const point_t&  other) const {
			return bg::equals(this, other);
  }

  bool operator!=(const point_t&  other) const {
      return !this->operator ==(other);
  }

	bool operator<(const point_t&  other) const {
      return  boost::geometry::math::smaller(this->x_, other.x_) || (equals(this->x_, other.x_) && boost::geometry::math::smaller(this->y_, other.y_));
  }
};

inline long double toLongDouble(const LongDouble& c) {
	return c.val();
}

#ifdef LIBNFP_USE_RATIONAL
inline long double toLongDouble(const rational_t& c) {
	return bm::numerator(c).convert_to<long double>() / bm::denominator(c).convert_to<long double>();
}
#endif

std::ostream& operator<<(std::ostream& os, const coord_t& p)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	os << toLongDouble(p);
	return os;
}
#endif

std::istream& operator>>(std::istream& is, LongDouble& c)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	long double val;
	is >> val;
	c.setVal(val);
	return is;
}
#endif

std::ostream& operator<<(std::ostream& os, const point_t& p)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	os << "{" << toLongDouble(p.x_) << "," << toLongDouble(p.y_) << "}";
	return os;
}
#endif

const point_t INVALID_POINT = {MAX_COORD, MAX_COORD};

typedef bg::model::segment<point_t> segment_t;

}

#ifdef LIBNFP_USE_RATIONAL
inline long double acos(const libnfp::rational_t& r) {
	return acos(libnfp::toLongDouble(r));
}
#endif

inline long double acos(const libnfp::LongDouble& ld) {
	return acos(libnfp::toLongDouble(ld));
}

#ifdef LIBNFP_USE_RATIONAL
inline long double sqrt(const libnfp::rational_t& r) {
	return sqrt(libnfp::toLongDouble(r));
}
#endif

inline long double sqrt(const libnfp::LongDouble& ld) {
	return sqrt(libnfp::toLongDouble(ld));
}

namespace libnfp {
#ifdef LIBNFP_USE_RATIONAL
inline long double cos(const rational_t& r) {
  return std::cos(toLongDouble(r));
}
#endif

inline long double cos(const LongDouble& ld) {
  return std::cos(libnfp::toLongDouble(ld));
}

#ifdef LIBNFP_USE_RATIONAL
inline long double sin(const rational_t& r) {
  return std::sin(toLongDouble(r));
}
#endif

inline long double sin(const LongDouble& ld) {
  return std::sin(ld.val());
}

}

BOOST_GEOMETRY_REGISTER_POINT_2D(libnfp::point_t, libnfp::coord_t, cs::cartesian, x_, y_)

namespace boost {
namespace geometry {
namespace math {
namespace detail {

template <>
struct square_root<libnfp::LongDouble>
{
  typedef libnfp::LongDouble return_type;

	static inline libnfp::LongDouble apply(libnfp::LongDouble const& a)
  {
        return std::sqrt(a.val());
  }
};

#ifdef LIBNFP_USE_RATIONAL
template <>
struct square_root<libnfp::rational_t>
{
  typedef libnfp::rational_t return_type;

	static inline libnfp::rational_t apply(libnfp::rational_t const& a)
  {
        return std::sqrt(libnfp::toLongDouble(a));
  }
};
#endif

template<>
struct abs<libnfp::LongDouble>
	{
	static libnfp::LongDouble apply(libnfp::LongDouble const& value)
			{
				libnfp::LongDouble const zero = libnfp::LongDouble();
					return value.val() < zero.val() ? -value.val() : value.val();
			}
	};

template <>
struct equals<libnfp::LongDouble, false>
{
	template<typename Policy>
	static inline bool apply(libnfp::LongDouble const& lhs, libnfp::LongDouble const& rhs, Policy const& policy)
  {
		if(lhs.val() == rhs.val())
			return true;

	  return bg::math::detail::abs<libnfp::LongDouble>::apply(lhs.val() - rhs.val()) <=  policy.apply(lhs.val(), rhs.val()) * libnfp::NFP_EPSILON;
  }
};

template <>
struct smaller<libnfp::LongDouble>
{
	static inline bool apply(libnfp::LongDouble const& lhs, libnfp::LongDouble const& rhs)
  {
		if(lhs.val() == rhs.val() || bg::math::detail::abs<libnfp::LongDouble>::apply(lhs.val() - rhs.val()) <=  libnfp::NFP_EPSILON * std::max(lhs.val(), rhs.val()))
			return false;

	  return lhs < rhs;
  }
};
}
}
}
}

namespace libnfp {
inline bool smaller(const LongDouble& lhs, const LongDouble& rhs) {
	return boost::geometry::math::detail::smaller<LongDouble>::apply(lhs, rhs);
}

inline bool larger(const LongDouble& lhs, const LongDouble& rhs) {
  return smaller(rhs, lhs);
}

bool equals(const LongDouble& lhs, const LongDouble& rhs)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
	;
#else
{
	if(lhs.val() == rhs.val())
		return true;

  return bg::math::detail::abs<libnfp::LongDouble>::apply(lhs.val() - rhs.val()) <=  libnfp::NFP_EPSILON * std::max(lhs.val(), rhs.val());
}
#endif

#ifdef LIBNFP_USE_RATIONAL
inline bool smaller(const rational_t& lhs, const rational_t& rhs) {
	return lhs < rhs;
}

inline bool larger(const rational_t& lhs, const rational_t& rhs) {
  return smaller(rhs, lhs);
}

bool equals(const rational_t& lhs, const rational_t& rhs) {
	return lhs == rhs;
}
#endif

inline bool smaller(const long double& lhs, const long double& rhs) {
	return lhs < rhs;
}

inline bool larger(const long double& lhs, const long double& rhs) {
  return smaller(rhs, lhs);
}

bool equals(const long double& lhs, const long double& rhs)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	return lhs == rhs;
}
#endif

typedef bg::model::polygon<point_t, false, true> polygon_t;
typedef std::vector<polygon_t::ring_type> nfp_t;
typedef bg::model::linestring<point_t> linestring_t;

typedef typename polygon_t::ring_type::size_type psize_t;

typedef bg::model::d2::point_xy<long double> pointf_t;
typedef bg::model::segment<pointf_t> segmentf_t;
typedef bg::model::polygon<pointf_t, false, true> polygonf_t;

polygonf_t::ring_type convert(const polygon_t::ring_type& r)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	polygonf_t::ring_type rf;
	for(const auto& pt : r) {
		rf.push_back(pointf_t(toLongDouble(pt.x_), toLongDouble(pt.y_)));
	}
	return rf;
}
#endif

polygonf_t convert(polygon_t p)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	polygonf_t pf;
	pf.outer() = convert(p.outer());

	for(const auto& r : p.inners()) {
		pf.inners().push_back(convert(r));
	}

	return pf;
}
#endif

polygon_t nfpRingsToNfpPoly(const nfp_t& nfp)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	polygon_t nfppoly;
	for (const auto& pt : nfp.front()) {
		nfppoly.outer().push_back(pt);
	}

	for (size_t i = 1; i < nfp.size(); ++i) {
		nfppoly.inners().push_back({});
		for (const auto& pt : nfp[i]) {
			nfppoly.inners().back().push_back(pt);
		}
	}

	return nfppoly;
}
#endif

void write_svg(std::string const& filename,const std::vector<segment_t>& segments)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
    std::ofstream svg(filename.c_str());

    boost::geometry::svg_mapper<pointf_t> mapper(svg, 100, 100, "width=\"200mm\" height=\"200mm\" viewBox=\"-250 -250 500 500\"");
    for(const auto& seg : segments) {
    	segmentf_t segf({toLongDouble(seg.first.x_), toLongDouble(seg.first.y_)}, {toLongDouble(seg.second.x_), toLongDouble(seg.second.y_)});
    	mapper.add(segf);
    	mapper.map(segf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
    }
}
#endif

void write_svg(std::string const& filename,	const polygon_t& p, const polygon_t::ring_type& ring)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	std::ofstream svg(filename.c_str());

	boost::geometry::svg_mapper<pointf_t> mapper(svg, 100, 100,	"width=\"200mm\" height=\"200mm\" viewBox=\"-250 -250 500 500\"");
	auto pf = convert(p);
	auto rf = convert(ring);

	mapper.add(pf);
	mapper.map(pf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
	mapper.add(rf);
	mapper.map(rf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
}
#endif

void write_svg(std::string const& filename,	typename std::vector<polygon_t> const& polygons)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	std::ofstream svg(filename.c_str());

	boost::geometry::svg_mapper<pointf_t> mapper(svg, 100, 100, "width=\"200mm\" height=\"200mm\" viewBox=\"-250 -250 500 500\"");
	for (auto p : polygons) {
		auto pf  = convert(p);
		mapper.add(pf);
		mapper.map(pf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
	}
}
#endif

void write_svg(std::string const& filename,	typename std::vector<polygon_t> const& polygons, const nfp_t& nfp)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	polygon_t nfppoly;
	for (const auto& pt : nfp.front()) {
		nfppoly.outer().push_back(pt);
	}

	for (size_t i = 1; i < nfp.size(); ++i) {
		nfppoly.inners().push_back({});
		for (const auto& pt : nfp[i]) {
			nfppoly.inners().back().push_back(pt);
		}
	}
	std::ofstream svg(filename.c_str());

	boost::geometry::svg_mapper<pointf_t> mapper(svg, 100, 100,	"width=\"200mm\" height=\"200mm\" viewBox=\"-250 -250 500 500\"");
	for (auto p : polygons) {
		auto pf  = convert(p);
		mapper.add(pf);
		mapper.map(pf, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
	}
	bg::correct(nfppoly);
	auto nfpf = convert(nfppoly);
	mapper.add(nfpf);
	mapper.map(nfpf, "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(204,153,0);stroke-width:2");

	for(auto& r: nfpf.inners()) {
		if(r.size() == 1) {
			mapper.add(r.front());
			mapper.map(r.front(), "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(204,153,0);stroke-width:2");
		} else if(r.size() == 2) {
			segmentf_t seg(r.front(), *(r.begin()+1));
			mapper.add(seg);
			mapper.map(seg, "fill-opacity:0.5;fill:rgb(204,153,0);stroke:rgb(204,153,0);stroke-width:2");
		}
	}
}
#endif

std::ostream& operator<<(std::ostream& os, const segment_t& seg)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	os << "{" << seg.first << "," << seg.second << "}";
	return os;
}
#endif

inline bool operator<(const segment_t& lhs, const segment_t& rhs) {
	return lhs.first < rhs.first || ((lhs.first == rhs.first) && (lhs.second < rhs.second));
}

inline bool operator==(const segment_t& lhs, const segment_t& rhs) {
	return (lhs.first == rhs.first && lhs.second == rhs.second) || (lhs.first == rhs.second && lhs.second == rhs.first);
}

inline bool operator!=(const segment_t& lhs, const segment_t& rhs) {
	return !operator==(lhs,rhs);
}

enum Alignment {
	LEFT,
	RIGHT,
	ON
};

point_t normalize(const point_t& pt)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	point_t norm = pt;
	coord_t len = bg::length(segment_t{{0,0},pt});

	if(len == 0.0L)
		return {0,0};

	norm.x_ /= len;
	norm.y_ /= len;

	return norm;
}
#endif

Alignment get_alignment(const segment_t& seg, const point_t& pt)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	coord_t res = ((seg.second.x_ - seg.first.x_)*(pt.y_ - seg.first.y_)
			- (seg.second.y_ - seg.first.y_)*(pt.x_ - seg.first.x_));

	if(equals(res, 0)) {
		return ON;
	} else	if(larger(res,0)) {
		return LEFT;
	} else {
		return RIGHT;
	}
}
#endif

long double get_inner_angle(const point_t& joint, const point_t& end1, const point_t& end2)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	coord_t dx21 = end1.x_-joint.x_;
	coord_t dx31 = end2.x_-joint.x_;
	coord_t dy21 = end1.y_-joint.y_;
	coord_t dy31 = end2.y_-joint.y_;
	coord_t m12 = sqrt((dx21*dx21 + dy21*dy21));
	coord_t m13 = sqrt((dx31*dx31 + dy31*dy31));
	if(m12 == 0.0L || m13 == 0.0L)
		return 0;
	return acos( (dx21*dx31 + dy21*dy31) / (m12 * m13) );
}
#endif

struct TouchingPoint {
	enum Type {
		VERTEX,
		A_ON_B,
		B_ON_A
	};
	Type type_;
	psize_t A_;
	psize_t B_;
};

struct TranslationVector {
	point_t vector_;
	segment_t edge_;
	bool fromA_;
	string name_;

	bool operator<(const TranslationVector& other) const {
		return this->vector_ < other.vector_ || ((this->vector_ == other.vector_) && (this->edge_ < other.edge_));
	}
};

std::ostream& operator<<(std::ostream& os, const TranslationVector& tv)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	os << "{" << tv.edge_ << " -> " << tv.vector_ << "} = " << tv.name_;
	return os;
}
#endif

void read_wkt_polygon(const string& filename, polygon_t& p)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	std::ifstream t(filename);

	std::string str;
	t.seekg(0, std::ios::end);
	str.reserve(t.tellg());
	t.seekg(0, std::ios::beg);

	str.assign((std::istreambuf_iterator<char>(t)),
							std::istreambuf_iterator<char>());

	str.pop_back();
	bg::read_wkt(str, p);
	bg::correct(p);
}
#endif

std::vector<psize_t> find_minimum_y(const polygon_t& p)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	std::vector<psize_t> result;
	coord_t min = MAX_COORD;
	auto& po = p.outer();
	for(psize_t i = 0; i < p.outer().size() - 1; ++i) {
		if(smaller(po[i].y_, min)) {
			result.clear();
			min = po[i].y_;
			result.push_back(i);
		} else if (equals(po[i].y_, min)) {
			result.push_back(i);
		}
	}
	return result;
}
#endif

std::vector<psize_t> find_maximum_y(const polygon_t& p)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	std::vector<psize_t> result;
	coord_t max = MIN_COORD;
	auto& po = p.outer();
	for(psize_t i = 0; i < p.outer().size() - 1; ++i) {
		if(larger(po[i].y_, max)) {
			result.clear();
			max = po[i].y_;
			result.push_back(i);
		} else if (equals(po[i].y_, max)) {
			result.push_back(i);
		}
	}
	return result;
}
#endif

psize_t find_point(const polygon_t::ring_type& ring, const point_t& pt)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	for(psize_t i = 0; i < ring.size(); ++i) {
		if(ring[i] == pt)
			return i;
	}
	return std::numeric_limits<psize_t>::max();
}
#endif

std::vector<TouchingPoint> findTouchingPoints(const polygon_t::ring_type& ringA, const polygon_t::ring_type& ringB)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	std::vector<TouchingPoint> touchers;
	for(psize_t i = 0; i < ringA.size() - 1; i++) {
		psize_t nextI = i+1;
		for(psize_t j = 0; j < ringB.size() - 1; j++) {
			psize_t nextJ = j+1;
			if(ringA[i] == ringB[j]) {
				touchers.push_back({TouchingPoint::VERTEX, i, j});
			} else if (ringA[nextI] != ringB[j] && bg::intersects(segment_t(ringA[i],ringA[nextI]), ringB[j])) {
				touchers.push_back({TouchingPoint::B_ON_A, nextI, j});
			} else if (ringB[nextJ] != ringA[i] && bg::intersects(segment_t(ringB[j],ringB[nextJ]), ringA[i])) {
				touchers.push_back({TouchingPoint::A_ON_B, i, nextJ});
			}
		}
	}
	return touchers;
}
#endif

//TODO deduplicate code
TranslationVector trimVector(const polygon_t::ring_type& rA, const polygon_t::ring_type& rB, const TranslationVector& tv)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	coord_t shortest = bg::length(tv.edge_);
	TranslationVector trimmed = tv;
	for(const auto& ptA : rA) {
		point_t translated;
		//for polygon A we invert the translation
		trans::translate_transformer<coord_t, 2, 2> translate(-tv.vector_.x_, -tv.vector_.y_);
		boost::geometry::transform(ptA, translated, translate);
		linestring_t projection;
		segment_t segproj(ptA, translated);
		projection.push_back(ptA);
		projection.push_back(translated);
		std::vector<point_t> intersections;
		bg::intersection(rB, projection, intersections);
		if(bg::touches(projection, rB) && intersections.size() < 2) {
			continue;
		}

		//find shortest intersection
		coord_t len;
		segment_t segi;
		for(const auto& pti : intersections) {
			segi = segment_t(ptA,pti);
			len = bg::length(segi);
			if(smaller(len, shortest)) {
				trimmed.vector_ = ptA - pti;
				trimmed.edge_ = segi;
				shortest = len;
			}
		}
	}

	for(const auto& ptB : rB) {
		point_t translated;

		trans::translate_transformer<coord_t, 2, 2> translate(tv.vector_.x_, tv.vector_.y_);
		boost::geometry::transform(ptB, translated, translate);
		linestring_t projection;
		segment_t segproj(ptB, translated);
		projection.push_back(ptB);
		projection.push_back(translated);
		std::vector<point_t> intersections;
		bg::intersection(rA, projection, intersections);
		if(bg::touches(projection, rA) && intersections.size() < 2) {
			continue;
		}

		//find shortest intersection
		coord_t len;
		segment_t segi;
		for(const auto& pti : intersections) {

			segi = segment_t(ptB,pti);
			len = bg::length(segi);
			if(smaller(len, shortest)) {
				trimmed.vector_ = pti - ptB;
				trimmed.edge_ = segi;
				shortest = len;
			}
		}
	}
 	return trimmed;
}
#endif

std::vector<TranslationVector> findFeasibleTranslationVectors(polygon_t::ring_type& ringA, polygon_t::ring_type& ringB, const std::vector<TouchingPoint>& touchers)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	//use a set to automatically filter duplicate vectors
	std::vector<TranslationVector> potentialVectors;
	std::vector<std::pair<segment_t,segment_t>> touchEdges;

	for (psize_t i = 0; i < touchers.size(); i++) {
		point_t& vertexA = ringA[touchers[i].A_];
		vertexA.marked_ = true;

		// adjacent A vertices
		signed long prevAindex = touchers[i].A_ - 1;
		signed long nextAindex = touchers[i].A_ + 1;

		prevAindex = (prevAindex < 0) ? ringA.size() - 2 : prevAindex; // loop
		nextAindex = (static_cast<psize_t>(nextAindex) >= ringA.size()) ? 1 : nextAindex; // loop

		point_t& prevA = ringA[prevAindex];
		point_t& nextA = ringA[nextAindex];

		// adjacent B vertices
		point_t& vertexB = ringB[touchers[i].B_];

		signed long prevBindex = touchers[i].B_ - 1;
		signed long nextBindex = touchers[i].B_ + 1;

		prevBindex = (prevBindex < 0) ? ringB.size() - 2 : prevBindex; // loop
		nextBindex = (static_cast<psize_t>(nextBindex) >= ringB.size()) ? 1 : nextBindex; // loop

		point_t& prevB = ringB[prevBindex];
		point_t& nextB = ringB[nextBindex];

		if (touchers[i].type_ == TouchingPoint::VERTEX) {
			segment_t a1 = { vertexA, nextA };
			segment_t a2 = { vertexA, prevA };
			segment_t b1 = { vertexB, nextB };
			segment_t b2 = { vertexB, prevB };

			//swap the segment elements so that always the first point is the touching point
			//also make the second segment always a segment of ringB
			touchEdges.push_back({a1, b1});
			touchEdges.push_back({a1, b2});
			touchEdges.push_back({a2, b1});
			touchEdges.push_back({a2, b2});
#ifdef NFP_DEBUG
			write_svg("touchersV" + std::to_string(i) + ".svg", {a1,a2,b1,b2});
#endif

			//TODO test parallel edges for floating point stability
			Alignment al;
			//a1 and b1 meet at start vertex
			al = get_alignment(a1, b1.second);
			if(al == LEFT) {
				potentialVectors.push_back({b1.first - b1.second, b1, false, "vertex1"});
			} else if(al == RIGHT) {
				potentialVectors.push_back({a1.second - a1.first, a1, true, "vertex2"});
			} else {
				potentialVectors.push_back({a1.second - a1.first, a1, true, "vertex3"});
			}

			//a1 and b2 meet at start and end
			al = get_alignment(a1, b2.second);
			if(al == LEFT) {
				//no feasible translation
			} else if(al == RIGHT) {
				potentialVectors.push_back({a1.second - a1.first, a1, true, "vertex4"});
			} else {
				potentialVectors.push_back({a1.second - a1.first, a1, true, "vertex5"});
			}

			//a2 and b1 meet at end and start
			al = get_alignment(a2, b1.second);
			if(al == LEFT) {
				//no feasible translation
			} else if(al == RIGHT) {
				potentialVectors.push_back({b1.first - b1.second, b1, false, "vertex6"});
			} else {
				potentialVectors.push_back({b1.first - b1.second, b1, false, "vertex7"});
			}
		} else if (touchers[i].type_ == TouchingPoint::B_ON_A) {
			segment_t a1 = {vertexB, vertexA};
			segment_t a2 = {vertexB, prevA};
			segment_t b1 = {vertexB, prevB};
			segment_t b2 = {vertexB, nextB};

			touchEdges.push_back({a1, b1});
			touchEdges.push_back({a1, b2});
			touchEdges.push_back({a2, b1});
			touchEdges.push_back({a2, b2});
#ifdef NFP_DEBUG
			write_svg("touchersB" + std::to_string(i) + ".svg", {a1,a2,b1,b2});
#endif
			potentialVectors.push_back({vertexA - vertexB, {vertexB, vertexA}, true, "bona"});
		} else if (touchers[i].type_ == TouchingPoint::A_ON_B) {
			//TODO testme
			segment_t a1 = {vertexA, prevA};
			segment_t a2 = {vertexA, nextA};
			segment_t b1 = {vertexA, vertexB};
			segment_t b2 = {vertexA, prevB};
#ifdef NFP_DEBUG
			write_svg("touchersA" + std::to_string(i) + ".svg", {a1,a2,b1,b2});
#endif
			touchEdges.push_back({a1, b1});
			touchEdges.push_back({a2, b1});
			touchEdges.push_back({a1, b2});
			touchEdges.push_back({a2, b2});
			potentialVectors.push_back({vertexA - vertexB, {vertexA, vertexB}, false, "aonb"});
		}
	}

	//discard immediately intersecting translations
	std::vector<TranslationVector> vectors;
	for(const auto& v : potentialVectors) {
		bool discarded = false;
		for(const auto& sp : touchEdges) {
			point_t normEdge = normalize(v.edge_.second - v.edge_.first);
			point_t normFirst = normalize(sp.first.second - sp.first.first);
			point_t normSecond = normalize(sp.second.second - sp.second.first);

			Alignment a1 = get_alignment({{0,0},normEdge}, normFirst);
			Alignment a2 = get_alignment({{0,0},normEdge}, normSecond);

			if(a1 == a2 && a1 != ON) {
				long double df = get_inner_angle({0,0},normEdge, normFirst);
				long double ds = get_inner_angle({0,0},normEdge, normSecond);

				point_t normIn = normalize(v.edge_.second - v.edge_.first);
				if (equals(df, ds)) {
					TranslationVector trimmed = trimVector(ringA,ringB, v);
					polygon_t::ring_type translated;
					trans::translate_transformer<coord_t, 2, 2> translate(trimmed.vector_.x_,	trimmed.vector_.y_);
					boost::geometry::transform(ringB, translated, translate);
					if (!(bg::intersects(translated, ringA) && !bg::overlaps(translated, ringA) && !bg::covered_by(translated, ringA) && !bg::covered_by(ringA, translated))) {
						discarded = true;
						std::cerr << "discarded" << std::endl;
						break;
					}
				} else {

					if (normIn == normalize(v.vector_)) {
						if (larger(ds, df)) {
							discarded = true;
							break;
						}
					} else {
						if (smaller(ds, df)) {
							discarded = true;
							break;
						}
					}
				}
			}
		}
		if(!discarded)
			vectors.push_back(v);
	}
	return vectors;
}
#endif

bool find(const std::vector<TranslationVector>& h, const TranslationVector& tv)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	for(const auto& htv : h) {
		if(htv.edge_ == tv.edge_)
			return true;
	}
	return false;
}
#endif

TranslationVector getLongest(const std::vector<TranslationVector>& tvs)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	coord_t len;
	coord_t maxLen = MIN_COORD;
	TranslationVector longest;
	longest.vector_ = INVALID_POINT;

	for(auto& tv : tvs) {
		len = bg::length(segment_t{{0,0},tv.vector_});
		if(larger(len, maxLen)) {
			maxLen = len;
			longest = tv;
		}
	}
	return longest;
}
#endif

TranslationVector selectNextTranslationVector(const polygon_t& pA, const polygon_t::ring_type& rA,	const polygon_t::ring_type& rB, const std::vector<TranslationVector>& tvs, const std::vector<TranslationVector>& history)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	if(!history.empty()) {
		TranslationVector last = history.back();
		std::vector<TranslationVector> historyCopy = history;
		if(historyCopy.size() >= 2) {
			historyCopy.erase(historyCopy.end() - 1);
			historyCopy.erase(historyCopy.end() - 1);
			if(historyCopy.size() > 4) {
				historyCopy.erase(historyCopy.begin(), historyCopy.end() - 4);
			}

		} else {
			historyCopy.clear();
		}
		DEBUG_MSG("last", last);

		psize_t laterI = std::numeric_limits<psize_t>::max();
		point_t previous = rA[0];
		point_t next;

		if(last.fromA_) {
			for (psize_t i = 1; i < rA.size() + 1; ++i) {
				if (i >= rA.size())
					next = rA[i % rA.size() + 1];
				else
					next = rA[i];

				segment_t candidate( previous, next );
				if(candidate == last.edge_) {
					laterI = i;
					break;
				}
				previous = next;
			}

			if (laterI == std::numeric_limits<psize_t>::max()) {
				point_t later;
				if (last.vector_ == (last.edge_.second - last.edge_.first)) {
					later = last.edge_.second;
				} else {
					later = last.edge_.first;
				}

				laterI = find_point(rA, later);
			}
		} else {
			point_t later;
			if (last.vector_ == (last.edge_.second - last.edge_.first)) {
				later = last.edge_.second;
			} else {
				later = last.edge_.first;
			}

			laterI = find_point(rA, later);
		}

		if (laterI == std::numeric_limits<psize_t>::max()) {
			throw std::runtime_error(
					"Internal error: Can't find later point of last edge");
		}

		std::vector<segment_t> viableEdges;
		previous = rA[laterI];
		for(psize_t i = laterI + 1; i < rA.size() + laterI + 1; ++i) {
			if(i >= rA.size())
				next = rA[i % rA.size() + 1];
			else
				next = rA[i];

			viableEdges.push_back({previous, next});
			previous = next;
		}

//		auto rng = std::default_random_engine {};
//		std::shuffle(std::begin(viableEdges), std::end(viableEdges), rng);

		//search with consulting the history to prevent oscillation
		std::vector<TranslationVector> viableTrans;
		for(const auto& ve: viableEdges) {
			for(const auto& tv : tvs) {
				if((tv.fromA_ && (normalize(tv.vector_) == normalize(ve.second - ve.first))) && (tv.edge_.first != last.edge_.first || tv.edge_.second != last.edge_.second) && !find(historyCopy, tv)) {
					viableTrans.push_back(tv);
				}
			}
			for (const auto& tv : tvs) {
				if (!tv.fromA_) {
					point_t later;
					if (tv.vector_ == (tv.edge_.second - tv.edge_.first) && (tv.edge_.first != last.edge_.first || tv.edge_.second != last.edge_.second)) {
						later = tv.edge_.second;
					} else if (tv.vector_ == (tv.edge_.first - tv.edge_.second)) {
						later = tv.edge_.first;
					} else
						continue;

					if (later == ve.first || later == ve.second) {
						viableTrans.push_back(tv);
					}
				}
			}
		}

		if(!viableTrans.empty())
			return getLongest(viableTrans);

		//search again without the history
		for(const auto& ve: viableEdges) {
			for(const auto& tv : tvs) {
				if((tv.fromA_ && (normalize(tv.vector_) == normalize(ve.second - ve.first))) && (tv.edge_.first != last.edge_.first || tv.edge_.second != last.edge_.second)) {
					viableTrans.push_back(tv);
				}
			}
			for (const auto& tv : tvs) {
				if (!tv.fromA_) {
					point_t later;
					if (tv.vector_ == (tv.edge_.second - tv.edge_.first) && (tv.edge_.first != last.edge_.first || tv.edge_.second != last.edge_.second)) {
						later = tv.edge_.second;
					} else if (tv.vector_ == (tv.edge_.first - tv.edge_.second)) {
						later = tv.edge_.first;
					} else
						continue;

					if (later == ve.first || later == ve.second) {
						viableTrans.push_back(tv);
					}
				}
			}
		}
		if(!viableTrans.empty())
			return getLongest(viableTrans);

		/*
		//search again without the history and without checking last edge
		for(const auto& ve: viableEdges) {
			for(const auto& tv : tvs) {
				if((tv.fromA_ && (normalize(tv.vector_) == normalize(ve.second - ve.first)))) {
					return tv;
				}
			}
			for (const auto& tv : tvs) {
				if (!tv.fromA_) {
					point_t later;
					if (tv.vector_ == (tv.edge_.second - tv.edge_.first)) {
						later = tv.edge_.second;
					} else if (tv.vector_ == (tv.edge_.first - tv.edge_.second)) {
						later = tv.edge_.first;
					} else
						continue;

					if (later == ve.first || later == ve.second) {
						return tv;
					}
				}
			}
		}*/

		if(tvs.size() == 1)
			return *tvs.begin();

		TranslationVector tv;
		tv.vector_ = INVALID_POINT;
		return tv;
	} else {
		return getLongest(tvs);
	}
}
#endif

bool inNfp(const point_t& pt, const nfp_t& nfp)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	for(const auto& r : nfp) {
		if(bg::touches(pt, r))
			return true;
	}

	return false;
}
#endif

enum SearchStartResult {
	FIT,
	FOUND,
	NOT_FOUND
};

SearchStartResult searchStartTranslation(polygon_t::ring_type& rA, const polygon_t::ring_type& rB, const nfp_t& nfp,const bool& inside, point_t& result)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	for(psize_t i = 0; i < rA.size() - 1; i++) {
		psize_t index;
		if (i >= rA.size())
			index = i % rA.size() + 1;
		else
			index = i;

		auto& ptA = rA[index];

		if(ptA.marked_)
			continue;

		ptA.marked_ = true;

		for(const auto& ptB: rB) {
			point_t testTranslation = ptA - ptB;
			polygon_t::ring_type translated;
			boost::geometry::transform(rB, translated, trans::translate_transformer<coord_t, 2, 2>(testTranslation.x_, testTranslation.y_));

			//check if the translated rB is identical to rA
			bool identical = false;
			for(const auto& ptT: translated) {
				identical = false;
				for(const auto& ptA: rA) {
					if(ptT == ptA) {
						identical = true;
						break;
					}
				}
				if(!identical)
					break;
			}

			if(identical) {
				result = testTranslation;
				return FIT;
			}

			bool bInside = false;
			for(const auto& ptT: translated) {
				if(bg::within(ptT, rA)) {
					bInside = true;
					break;
				} else if(!bg::touches(ptT, rA)) {
					bInside = false;
					break;
				}
			}

			if(((bInside && inside) || (!bInside && !inside)) && (!bg::overlaps(translated, rA) && !bg::covered_by(translated, rA) && !bg::covered_by(rA, translated)) && !inNfp(translated.front(), nfp)){
				result = testTranslation;
				return FOUND;
			}

			point_t nextPtA = rA[index + 1];
			TranslationVector slideVector;
			slideVector.vector_ = nextPtA - ptA;
			slideVector.edge_ = {ptA, nextPtA};
			slideVector.fromA_ = true;
			TranslationVector trimmed = trimVector(rA, translated, slideVector);
			polygon_t::ring_type translated2;
			trans::translate_transformer<coord_t, 2, 2> trans(trimmed.vector_.x_, trimmed.vector_.y_);
			boost::geometry::transform(translated, translated2, trans);

			//check if the translated rB is identical to rA
			identical = false;
			for(const auto& ptT: translated) {
				identical = false;
				for(const auto& ptA: rA) {
					if(ptT == ptA) {
						identical = true;
						break;
					}
				}
				if(!identical)
					break;
			}

			if(identical) {
				result = trimmed.vector_ + testTranslation;
				return FIT;
			}

			bInside = false;
			for(const auto& ptT: translated2) {
				if(bg::within(ptT, rA)) {
					bInside = true;
					break;
				} else if(!bg::touches(ptT, rA)) {
					bInside = false;
					break;
				}
			}

			if(((bInside && inside) || (!bInside && !inside)) && (!bg::overlaps(translated2, rA) && !bg::covered_by(translated2, rA) && !bg::covered_by(rA, translated2)) && !inNfp(translated2.front(), nfp)){
				result = trimmed.vector_ + testTranslation;
				return FOUND;
			}
		}
	}
	return NOT_FOUND;
}
#endif

enum SlideResult {
	LOOP,
	NO_LOOP,
	NO_TRANSLATION
};

SlideResult slide(polygon_t& pA, polygon_t::ring_type& rA, polygon_t::ring_type& rB, nfp_t& nfp, const point_t& transB, bool inside)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	polygon_t::ring_type rifsB;
	boost::geometry::transform(rB, rifsB, trans::translate_transformer<coord_t, 2, 2>(transB.x_, transB.y_));
	rB = std::move(rifsB);

#ifdef NFP_DEBUG
	write_svg("ifs.svg", pA, rB);
#endif

	bool startAvailable = true;
	psize_t cnt = 0;
	point_t referenceStart = rB.front();
	std::vector<TranslationVector> history;

	//generate the nfp for the ring
	while(startAvailable) {
		DEBUG_VAL(cnt);
		//use first point of rB as reference
		nfp.back().push_back(rB.front());
		if(cnt == 15)
			std::cerr << "";

		std::vector<TouchingPoint> touchers = findTouchingPoints(rA, rB);

#ifdef NFP_DEBUG
		DEBUG_MSG("touchers", touchers.size());
		for(auto t : touchers) {
			DEBUG_VAL(t.type_);
		}
#endif
		if(touchers.empty()) {
			throw std::runtime_error("Internal error: No touching points found");
		}
		std::vector<TranslationVector> transVectors = findFeasibleTranslationVectors(rA, rB, touchers);

#ifdef NFP_DEBUG
		DEBUG_MSG("collected vectors", transVectors.size());
		for(auto pt : transVectors) {
			DEBUG_VAL(pt);
		}
#endif

		if(transVectors.empty()) {
			return NO_LOOP;
		}

		TranslationVector next = selectNextTranslationVector(pA, rA, rB, transVectors, history);

		if(next.vector_ == INVALID_POINT)
			return NO_TRANSLATION;

		DEBUG_MSG("next", next);

		TranslationVector trimmed = trimVector(rA, rB, next);
		DEBUG_MSG("trimmed", trimmed);

		history.push_back(next);

		polygon_t::ring_type nextRB;
		boost::geometry::transform(rB, nextRB, trans::translate_transformer<coord_t, 2, 2>(trimmed.vector_.x_, trimmed.vector_.y_));
		rB = std::move(nextRB);

#ifdef NFP_DEBUG
		write_svg("next" + std::to_string(cnt) + ".svg", pA,rB);
#endif

		++cnt;
		if(referenceStart == rB.front() || (inside && bg::touches(rB.front(), nfp.front()))) {
			startAvailable = false;
		}
	}
	return LOOP;
}
#endif

void removeCoLinear(polygon_t::ring_type& r)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	assert(r.size() > 2);
	psize_t nextI;
	psize_t prevI = 0;
	segment_t segment(r[r.size() - 2], r[0]);
	polygon_t::ring_type newR;

	for (psize_t i = 1; i < r.size() + 1; ++i) {
		if (i >= r.size())
			nextI = i % r.size() + 1;
		else
			nextI = i;

		if (get_alignment(segment, r[nextI]) != ON) {
			newR.push_back(r[prevI]);
		}
		segment = {segment.second, r[nextI]};
		prevI = nextI;
	}

	r = newR;
}
#endif

void removeCoLinear(polygon_t& p)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	removeCoLinear(p.outer());
	for (auto& r : p.inners())
		removeCoLinear(r);
}
#endif

nfp_t generateNFP(polygon_t& pA, polygon_t& pB, const bool checkValidity = true)
#ifndef LIBNFP_PROTOTYPES_IMPLEMENTATION
;
#else
{
	removeCoLinear(pA);
	removeCoLinear(pB);

	if(checkValidity)  {
		std::string reason;
		if(!bg::is_valid(pA, reason))
			throw std::runtime_error("Polygon A is invalid: " + reason);

		if(!bg::is_valid(pB, reason))
			throw std::runtime_error("Polygon B is invalid: " + reason);
	}

	nfp_t nfp;

#ifdef NFP_DEBUG
	write_svg("start.svg", {pA, pB});
#endif

	DEBUG_VAL(bg::wkt(pA))
	DEBUG_VAL(bg::wkt(pB));

	//prevent double vertex connections at start because we might come back the same way we go which would end the nfp prematurely
	std::vector<psize_t> ptyaminI = find_minimum_y(pA);
	std::vector<psize_t> ptybmaxI = find_maximum_y(pB);

	point_t pAstart;
	point_t pBstart;

	if(ptyaminI.size() > 1 || ptybmaxI.size() > 1) {
		//find right-most of A and left-most of B to prevent double connection at start
		coord_t maxX = MIN_COORD;
		psize_t iRightMost = 0;
		for(psize_t& ia : ptyaminI) {
			const point_t& candidateA = pA.outer()[ia];
			if(larger(candidateA.x_, maxX)) {
				maxX = candidateA.x_;
				iRightMost = ia;
			}
		}

		coord_t minX = MAX_COORD;
		psize_t iLeftMost = 0;
		for(psize_t& ib : ptybmaxI) {
			const point_t& candidateB = pB.outer()[ib];
			if(smaller(candidateB.x_, minX)) {
				minX = candidateB.x_;
				iLeftMost = ib;
			}
		}
		pAstart = pA.outer()[iRightMost];
		pBstart = pB.outer()[iLeftMost];
	} else {
		pAstart = pA.outer()[ptyaminI.front()];
		pBstart = pB.outer()[ptybmaxI.front()];
	}

	nfp.push_back({});
	point_t transB = {pAstart - pBstart};

	if(slide(pA, pA.outer(), pB.outer(), nfp, transB, false) != LOOP) {
			throw std::runtime_error("Unable to complete outer nfp loop");
	}

	DEBUG_VAL("##### outer #####");
	point_t startTrans;
  while(true) {
  	SearchStartResult res = searchStartTranslation(pA.outer(), pB.outer(), nfp, false, startTrans);
  	if(res == FOUND) {
			nfp.push_back({});
			DEBUG_VAL("##### interlock start #####")
			polygon_t::ring_type rifsB;
			boost::geometry::transform(pB.outer(), rifsB, trans::translate_transformer<coord_t, 2, 2>(startTrans.x_, startTrans.y_));
			if(inNfp(rifsB.front(), nfp)) {
				continue;
			}
			SlideResult sres = slide(pA, pA.outer(), pB.outer(), nfp, startTrans, true);
			if(sres != LOOP) {
				if(sres == NO_TRANSLATION) {
					//no initial slide found -> jiggsaw
					if(!inNfp(pB.outer().front(),nfp)) {
						nfp.push_back({});
						nfp.back().push_back(pB.outer().front());
					}
				}
			}
			DEBUG_VAL("##### interlock end #####");
  	} else if(res == FIT) {
  		point_t reference = pB.outer().front();
  		point_t translated;
			trans::translate_transformer<coord_t, 2, 2> translate(startTrans.x_, startTrans.y_);
			boost::geometry::transform(reference, translated, translate);
			if(!inNfp(translated,nfp)) {
				nfp.push_back({});
				nfp.back().push_back(translated);
			}
			break;
  	} else
  		break;
	}

	for(auto& rA : pA.inners()) {
		while(true) {
			SearchStartResult res = searchStartTranslation(rA, pB.outer(), nfp, true, startTrans);
			if(res == FOUND) {
				nfp.push_back({});
				DEBUG_VAL("##### hole start #####");
				slide(pA, rA, pB.outer(), nfp, startTrans, true);
				DEBUG_VAL("##### hole end #####");
			} else if(res == FIT) {
	  		point_t reference = pB.outer().front();
	  		point_t translated;
				trans::translate_transformer<coord_t, 2, 2> translate(startTrans.x_, startTrans.y_);
				boost::geometry::transform(reference, translated, translate);
				if(!inNfp(translated,nfp)) {
					nfp.push_back({});
					nfp.back().push_back(translated);
				}
				break;
			} else
				break;
		}
  }

#ifdef NFP_DEBUG
  write_svg("nfp.svg", {pA,pB}, nfp);
#endif

  return nfp;
}
#endif

}

#endif


#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "utils.h"
#include <iostream>
#include <fstream>
#include <boost/filesystem.hpp>
#include <boost/crc.hpp>

/*
Tape Dump format:
- Each file on the tape is set up of a set of records
- Each record contains a header and an optional footer, both of which are 4 bytes long.
- The header and the footer contains the length of the record
- The footer doesn't exist if length is set to 0, which marks the end of file
*/

// This CRC is documented in http://bitsavers.trailing-edge.com/pdf/ibm/24xx/A22-6862-4_2400_Series_Magnetic_Tape_Units_OEM.pdf
// page 8
class MyCrc_c {
public:
	explicit MyCrc_c(uint8_t aInitValue = 0, bool aOddParity = false) : mAccumulator(aInitValue), mOddParity(aOddParity), mAccumulatorOddParity(true) {}
	void process_byte(uint8_t aByte) {
		// Calculate parity first...
		uint8_t Parity = 0;
		uint16_t Data = aByte;
		for (int i = 0; i < 7; ++i) {
			Parity ^= Data & 1;
			Data >>= 1;
		}
		CRAY_ASSERT(Parity <= 1);
		if (mOddParity) {
			Parity = 1 - Parity;
		}
		Data = (Parity << 8) | aByte;
		mAccumulator ^= Data;
		// Rotate by 1
		uint8_t Msb = (mAccumulator & 256) >> 8;
		if (Msb == 1) {
			mAccumulator ^= (1 << 2 | 1 << 3 | 1 << 4 | 1 << 5);
		}
		mAccumulator = ((mAccumulator & 255) << 1) | Msb;
	}
	void process_bytes(void *aData, size_t aDataSize) {
		uint8_t *Data = (uint8_t*)aData;
		for (size_t i = 0; i < aDataSize; ++i) {
			process_byte(Data[i]);
		}
	}
	uint16_t checksum() const {
		uint16_t RetVal = mAccumulator ^ ((~(1 << 2 | 1 << 4) & 511));
		if (!mAccumulatorOddParity) {
			RetVal ^= 1;
		}
		return RetVal;
	}
protected:
	bool mOddParity;
	bool mAccumulatorOddParity;
	uint16_t mAccumulator;
};

bool IsPowerOf2(uint32_t aVal) {
	if (aVal == 0) return false;
	return (aVal & (aVal - 1)) == 0;
}

uint32_t ReadRecord(std::istream &aFile, std::vector<uint8_t> &aData, bool aCorrectRecordSize, size_t aExpectedRecordSize, bool &aHasChecksum, int aRecordIdx) {
	uint32_t RecordSize;
	if (aFile.eof()) {
		aData.resize(0);
		return 0;
	}
	aFile.read((char*)(&RecordSize), 4);
	if (aFile.eof()) {
		aData.resize(0);
		return 0;
	}
	// Test to see if record size is 2^n+2 format. If it is, it's likely the record has a checksum and CRC at the end
	bool LikelyHasChecksum = IsPowerOf2(RecordSize - 2);

	bool LikelyCorruptedRecord = false;
	if (aRecordIdx > 0) {
		if (LikelyHasChecksum || aHasChecksum) {
			LikelyCorruptedRecord = RecordSize - 2 != aExpectedRecordSize;
		}
		else {
			LikelyCorruptedRecord = RecordSize != aExpectedRecordSize;
		}
	}
	if (RecordSize > 0x00100000) {
		throw Generic_x("Record size is suspicicously large. Aborting...");
	}
	uint32_t ReadSize = RecordSize;
	aData.resize(ReadSize);

	if (RecordSize > 0) {
		aFile.read((char*)(&(aData[0])), ReadSize);

		uint8_t LRC = 0;
		for (uint32_t i = 0; i < ReadSize; ++i) {
			LRC ^= aData[i];
		}
		if (LikelyHasChecksum || aHasChecksum) {
			aData.resize(aData.size() - 2); // Remove checksum from record
		}
		if (LRC != 0) {
			if (LikelyHasChecksum || aHasChecksum) {
				std::cout << "LRC error in record " << DecPrinter(aRecordIdx+1) << std::endl;
			}
		}
		else {
			if (aRecordIdx == 0) aHasChecksum = true;
		}
		uint32_t EndRecordSize;
		aFile.read((char*)(&EndRecordSize), 4);
		if (EndRecordSize != RecordSize) throw Generic_x("Record size mismatch at end");
		if (LikelyCorruptedRecord) {
			// Peek into the stream to see if this is the last record
			uint32_t NextRecordSize;
			aFile.read((char*)(&NextRecordSize), 4);
			if (NextRecordSize != 0 && aCorrectRecordSize) {
				std::cout << "Incorrect record size " << DecPrinter(aData.size()) << " detected in record " << DecPrinter(aRecordIdx+1) << ". Correcting to " << DecPrinter(aExpectedRecordSize) << std::endl;
				aData.resize(aExpectedRecordSize);
			}
			aFile.seekg(-4, std::ios_base::cur);
		}
	}
	return RecordSize;
}

int PrintUsage(const char *aExecName, const char *ErrorStr = nullptr) {
	std::cout << "Usage: " << aExecName << " <image file> -c <output base file name>" << std::endl;
	std::cout << "     -c: assume all records in the file are the same length. Correct if not." << std::endl;
	return 1;
}

int main(int argc, const char* argv[])
{
	CommandLine_c CommandLine(argc, argv);
	std::string OutputBaseFileName;
	std::string InFileName;
	bool CorrectRecordSize = false;
	try {
		while (CommandLine.HasMoreParams()) {
			std::string CurParam = CommandLine.GetNextParam();
			if (CurParam.length() == 0) continue;
			if (CurParam == "-c") {
				CorrectRecordSize = true;
			} else if (InFileName.empty()) {
				InFileName = CurParam;
			}
			else if (OutputBaseFileName.length() == 0) {
				OutputBaseFileName = CurParam;
			} else {
				throw Generic_x("Unkown command line parameter");
			}
		}
		if (InFileName.empty()) throw Generic_x("Input file name must be specified");
	}
	catch (std::exception &Ex) {
		return PrintUsage(argv[0], Ex.what());
	}
	if (OutputBaseFileName.length() == 0) {
		//Figure out base-name from input
		OutputBaseFileName = boost::filesystem::basename(InFileName);
	}

	errno = 0;

	std::ifstream InStrm;
	try {
		InStrm.open(InFileName, std::ios_base::in | std::ios_base::binary);
	}
	catch (std::exception &e) {
		std::cout << "Can't open input file: " << InFileName << " - " << e.what() << std::endl;
		return 1;
	}

	size_t EOTCount = 0;
	try {
		size_t FileIdx = 1;
		while (!InStrm.eof()) {
			std::vector<uint8_t> Record;
			int RecordIdx = 0;
			std::ofstream OutStrm;
			size_t ExpectedRecordSize = 0;
			bool HasChecksum = false;
			while (ReadRecord(InStrm, Record, CorrectRecordSize, ExpectedRecordSize, HasChecksum, RecordIdx) > 0) { // Read till EOF marker
				if (ExpectedRecordSize == 0) ExpectedRecordSize = Record.size();
				if (RecordIdx == 0) {
					if (EOTCount > 0) {
						std::cout << "Warning: non-0 file after EOT" << std::endl;
					}
					std::stringstream OutFileName;
					OutFileName << OutputBaseFileName << "." << std::setw(3) << std::setfill('0') << FileIdx;
					OutStrm.open(OutFileName.str(), std::ios_base::out | std::ios_base::binary);
					if (!OutStrm.good()) throw Generic_x("Can't create file");
				}
				OutStrm.write((char*)(&(Record[0])), Record.size());
				RecordIdx++;
			}
			OutStrm.close();
			if ((RecordIdx == 0) && !InStrm.eof()) { ++EOTCount; }
			if (RecordIdx > 0) ++FileIdx; // Only increment file name if it was in fact created
		}
	}
	catch (std::exception &e) {
		std::cout << e.what() << std::endl;
		return 1;
	}
	if (EOTCount == 0) {
		std::cout << "Warning: no EOT marker found" << std::endl;
	}
	if (EOTCount != 1) {
		std::cout << "Warning: " << EOTCount << " of EOT markers found" << std::endl;
	}
	return 0;
}