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

/* Author: Andreas Orthey */
#include <ompl/geometric/planners/quotientspace/algorithms/QRRTImpl.h>
#include <ompl/tools/config/SelfConfig.h>
#include <boost/foreach.hpp>

#define foreach BOOST_FOREACH

ompl::geometric::QRRTImpl::QRRTImpl(const base::SpaceInformationPtr &si, QuotientSpace *parent_) : BaseT(si, parent_)
{
    setName("QRRTImpl" + std::to_string(id_));
    Planner::declareParam<double>("range", this, &QRRTImpl::setRange, &QRRTImpl::getRange, "0.:1.:10000.");
    Planner::declareParam<double>("goal_bias", this, &QRRTImpl::setGoalBias, &QRRTImpl::getGoalBias, "0.:.1:1.");
    qRandom_ = new Configuration(Q1);
}

ompl::geometric::QRRTImpl::~QRRTImpl()
{
    deleteConfiguration(qRandom_);
}

void ompl::geometric::QRRTImpl::setGoalBias(double goalBias)
{
    goalBias_ = goalBias;
}

double ompl::geometric::QRRTImpl::getGoalBias() const
{
    return goalBias_;
}

void ompl::geometric::QRRTImpl::setRange(double maxDistance)
{
    maxDistance_ = maxDistance;
}

double ompl::geometric::QRRTImpl::getRange() const
{
    return maxDistance_;
}

void ompl::geometric::QRRTImpl::setup()
{
    BaseT::setup();
    ompl::tools::SelfConfig sc(Q1, getName());
    sc.configurePlannerRange(maxDistance_);
    goal_ = pdef_->getGoal().get();
}

void ompl::geometric::QRRTImpl::clear()
{
    BaseT::clear();
}

bool ompl::geometric::QRRTImpl::getSolution(base::PathPtr &solution)
{
    if (hasSolution_)
    {
        bool baset_sol = BaseT::getSolution(solution);
        if (baset_sol)
        {
            shortestPathVertices_ = shortestVertexPath_;
        }
        return baset_sol;
    }
    else
    {
        return false;
    }
}

void ompl::geometric::QRRTImpl::grow()
{
    if (firstRun_)
    {
        init();
        firstRun_ = false;
    }

    if (hasSolution_)
    {
        // No Goal Biasing if we already found a solution on this quotient space
        sample(qRandom_->state);
    }
    else
    {
        double s = rng_.uniform01();
        if (s < goalBias_)
        {
            Q1->copyState(qRandom_->state, qGoal_->state);
        }
        else
        {
            sample(qRandom_->state);
        }
    }

    const Configuration *q_nearest = nearest(qRandom_);
    double d = Q1->distance(q_nearest->state, qRandom_->state);
    if (d > maxDistance_)
    {
        Q1->getStateSpace()->interpolate(q_nearest->state, qRandom_->state, maxDistance_ / d, qRandom_->state);
    }

    totalNumberOfSamples_++;
    if (Q1->checkMotion(q_nearest->state, qRandom_->state))
    {
        totalNumberOfFeasibleSamples_++;
        Configuration *q_next = new Configuration(Q1, qRandom_->state);
        Vertex v_next = addConfiguration(q_next);
        if (!hasSolution_)
        {
            // only add edge if no solution exists
            addEdge(q_nearest->index, v_next);

            double dist = 0.0;
            bool satisfied = goal_->isSatisfied(q_next->state, &dist);
            if (satisfied)
            {
                vGoal_ = addConfiguration(qGoal_);
                addEdge(q_nearest->index, vGoal_);
                hasSolution_ = true;
            }
        }
    }
}

double ompl::geometric::QRRTImpl::getImportance() const
{
    // Should depend on
    // (1) level : The higher the level, the more importance
    // (2) total samples: the more we already sampled, the less important it
    // becomes
    // (3) has solution: if it already has a solution, we should explore less
    // (only when nothing happens on other levels)
    // (4) vertices: the more vertices we have, the less important (let other
    // levels also explore)
    //
    // exponentially more samples on level i. Should depend on ALL levels.
    // const double base = 2;
    // const double normalizer = powf(base, level);
    // double N = (double)GetNumberOfVertices()/normalizer;
    double N = (double)getNumberOfVertices();
    return 1.0 / (N + 1);
}

// Make it faster by removing the validity check
bool ompl::geometric::QRRTImpl::sample(base::State *q_random)
{
    if (parent_ == nullptr)
    {
        Q1_sampler_->sampleUniform(q_random);
    }
    else
    {
        if (X1_dimension_ > 0)
        {
            X1_sampler_->sampleUniform(s_X1_tmp_);
            parent_->sampleQuotient(s_Q0_tmp_);
            mergeStates(s_Q0_tmp_, s_X1_tmp_, q_random);
        }
        else
        {
            parent_->sampleQuotient(q_random);
        }
    }
    return true;
}

bool ompl::geometric::QRRTImpl::sampleQuotient(base::State *q_random_graph)
{
    // RANDOM VERTEX SAMPLING
    const Vertex v = boost::random_vertex(graph_, rng_boost);
    Q1->getStateSpace()->copyState(q_random_graph, graph_[v]->state);
    return true;
}


// This file is part of CBOR-lite which is copyright Isode Limited
// and others and released under a MIT license. For details, see the
// COPYRIGHT.md file in the top-level folder of the CBOR-lite software
// distribution.
#include "../include/cbor-lite/codec.h"
#include <boost/test/unit_test.hpp>
#include <tuple>

BOOST_AUTO_TEST_SUITE(cbor_lite)
BOOST_AUTO_TEST_SUITE(codec)

BOOST_AUTO_TEST_CASE(non_negative) {
    const std::vector<std::pair<std::uint_fast64_t, const std::string>> cases{
        {0u, std::string("\x00", 1)},
        {1u, "\x01"},
        {10u, "\x0a"},
        {23u, "\x17"},
        {24u, "\x18\x18"},
        {25u, "\x18\x19"},
        {100u, "\x18\x64"},
        {1000u, "\x19\x03\xe8"},
        {1000000u, std::string("\x1a\x00\x0f\x42\x40", 5)},
        {1000000000000u, std::string("\x1b\x00\x00\x00\xe8\xd4\xa5\x10\x00", 9)},
        {18446744073709551615u, std::string("\x1b\xff\xff\xff\xff\xff\xff\xff\xff", 9)},
    };
    for (const auto& test : cases) {
        BOOST_CHECK_NO_THROW({
            std::string buffer;
            auto len = CborLite::encodeUnsigned(buffer, test.first);
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(buffer, test.second);
            std::uint_fast64_t value = 0u;
            auto pos = std::begin(test.second);
            len = CborLite::decodeUnsigned(pos, std::end(test.second), value);
            BOOST_CHECK(pos == std::end(test.second));
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(value, test.first);
        });
    }
}

BOOST_AUTO_TEST_CASE(negative) {
    const std::vector<std::pair<std::uint_fast64_t, const std::string>> cases{
        {0, "\x20"},
        {9, "\x29"},
        {99, "\x38\x63"},
        {999, "\x39\x03\xe7"},
    };
    for (const auto& test : cases) {
        BOOST_CHECK_NO_THROW({
            std::string buffer;
            auto len = CborLite::encodeNegative(buffer, test.first);
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(buffer, test.second);
            std::uint_fast64_t value = 0u;
            auto pos = std::begin(test.second);
            len = CborLite::decodeNegative(pos, std::end(test.second), value);
            BOOST_CHECK(pos == std::end(test.second));
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(value, test.first);
        });
    }
}

BOOST_AUTO_TEST_CASE(integer) {
    const std::vector<std::pair<std::int_fast64_t, const std::string>> cases{
        {0, std::string("\x00", 1)},
        {1, "\x01"},
        {10, "\x0a"},
        {23, "\x17"},
        {24, "\x18\x18"},
        {25, "\x18\x19"},
        {100, "\x18\x64"},
        {1000, "\x19\x03\xe8"},
        {1000000, std::string("\x1a\x00\x0f\x42\x40", 5)},
        {1000000000000, std::string("\x1b\x00\x00\x00\xe8\xd4\xa5\x10\x00", 9)},
        {-1, "\x20"},
        {-10, "\x29"},
        {-100, "\x38\x63"},
        {-1000, "\x39\x03\xe7"},
    };
    for (const auto& test : cases) {
        BOOST_CHECK_NO_THROW({
            std::string buffer;
            auto len = CborLite::encodeInteger(buffer, test.first);
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(buffer, test.second);
            std::int_fast64_t value = 0;
            auto pos = std::begin(test.second);
            len = CborLite::decodeInteger(pos, std::end(test.second), value);
            BOOST_CHECK(pos == std::end(test.second));
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(value, test.first);
        });
    }
}

BOOST_AUTO_TEST_CASE(boolean) {
    const std::vector<std::pair<bool, const std::string>> cases{
        {false, "\xf4"},
        {true, "\xf5"},
    };
    for (const auto& test : cases) {
        BOOST_CHECK_NO_THROW({
            std::string buffer;
            auto len = CborLite::encodeBool(buffer, test.first);
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(buffer, test.second);
            auto value = false;
            auto pos = std::begin(test.second);
            len = CborLite::decodeBool(pos, std::end(test.second), value);
            BOOST_CHECK(pos == std::end(test.second));
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(value, test.first);
        });
    }
}

BOOST_AUTO_TEST_CASE(bytes) {
    const std::vector<std::pair<const std::string, const std::string>> cases{
        {"", "\x40"},
        {"a", "\x41\x61"},
        {"A", "\x41\x41"},
        {"IETF", "\x44\x49\x45\x54\x46"},
        {"\"\\", "\x42\x22\x5c"},
        {"\xc3\xbc", "\x42\xc3\xbc"},
        {"\xe6\xb0\xb4", "\x43\xe6\xb0\xb4"},
        {"\xf0\x90\x85\x91", "\x44\xf0\x90\x85\x91"},
        {"\x01\x02\x03\x04", "\x44\x01\x02\x03\x04"},
        {"@@@@", "\x44\x40\x40\x40\x40"},
    };
    for (const auto& test : cases) {
        BOOST_CHECK_NO_THROW({
            std::string buffer;
            auto len = CborLite::encodeBytes(buffer, test.first);
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(buffer, test.second);
            std::string value;
            auto pos = std::begin(test.second);
            len = CborLite::decodeBytes(pos, std::end(test.second), value);
            BOOST_CHECK(pos == std::end(test.second));
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(value, test.first);
        });
    }
    BOOST_CHECK_NO_THROW({
        std::vector<char> buffer;
        std::string input = "@@@@";
        std::vector<char> payload(std::begin(input), std::end(input));
        auto len = CborLite::encodeBytes(buffer, payload);
        std::string expect = "\x44\x40\x40\x40\x40";
        BOOST_CHECK_EQUAL(len, expect.length());
        BOOST_CHECK_EQUAL(std::string(std::begin(buffer), std::end(buffer)), expect);
    });
}

BOOST_AUTO_TEST_CASE(encodedBytes) {
    const std::vector<std::pair<const std::string, const std::string>> cases{
        {"", "\xd8\x18\x40"},
        {"a", "\xd8\x18\x41\x61"},
        {"A", "\xd8\x18\x41\x41"},
        {"IETF", "\xd8\x18\x44\x49\x45\x54\x46"},
        {"\"\\", "\xd8\x18\x42\x22\x5c"},
        {"\xc3\xbc", "\xd8\x18\x42\xc3\xbc"},
        {"\xe6\xb0\xb4", "\xd8\x18\x43\xe6\xb0\xb4"},
        {"\xf0\x90\x85\x91", "\xd8\x18\x44\xf0\x90\x85\x91"},
        {"\x01\x02\x03\x04", "\xd8\x18\x44\x01\x02\x03\x04"},
        {"@@@@", "\xd8\x18\x44\x40\x40\x40\x40"},
    };
    for (const auto& test : cases) {
        BOOST_CHECK_NO_THROW({
            std::string buffer;
            auto len = CborLite::encodeEncodedBytes(buffer, test.first);
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(buffer, test.second);

            buffer.clear();
            len = CborLite::encodeEncodedBytesPrefix(buffer, test.first.length());
            BOOST_CHECK_EQUAL(len, 3);
            BOOST_CHECK_EQUAL(buffer, test.second.substr(0, 3));

            std::string value;
            auto pos = std::begin(test.second);
            len = CborLite::decodeEncodedBytes(pos, std::end(test.second), value);
            BOOST_CHECK(pos == std::end(test.second));
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(value, test.first);

            std::size_t got = 0u;
            pos = std::begin(test.second);
            len = CborLite::decodeEncodedBytesPrefix(pos, pos + 3, got);
            BOOST_CHECK_EQUAL(len, 3);
            BOOST_CHECK_EQUAL(got, test.first.length());
        });
    }
    BOOST_CHECK_NO_THROW({
        std::vector<char> buffer;
        std::string input = "@@@@";
        std::vector<char> payload(std::begin(input), std::end(input));
        auto len = CborLite::encodeEncodedBytes(buffer, payload);
        std::string expect = "\xd8\x18\x44\x40\x40\x40\x40";
        BOOST_CHECK_EQUAL(len, expect.length());
        BOOST_CHECK_EQUAL(std::string(std::begin(buffer), std::end(buffer)), expect);
    });
}

BOOST_AUTO_TEST_CASE(strings) {
    const std::vector<std::pair<const std::string, const std::string>> cases{
        {"", "\x60"},
        {"a", "\x61\x61"},
        {"A", "\x61\x41"},
        {"IETF", "\x64\x49\x45\x54\x46"},
        {"\"\\", "\x62\x22\x5c"},
        {"\xc3\xbc", "\x62\xc3\xbc"},
        {"\xe6\xb0\xb4", "\x63\xe6\xb0\xb4"},
        {"\xf0\x90\x85\x91", "\x64\xf0\x90\x85\x91"},
        {"\x01\x02\x03\x04", "\x64\x01\x02\x03\x04"},
    };
    for (const auto& test : cases) {
        BOOST_CHECK_NO_THROW({
            std::string buffer;
            auto len = CborLite::encodeText(buffer, test.first);
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(buffer, test.second);
            std::string value;
            auto pos = std::begin(test.second);
            len = CborLite::decodeText(pos, std::end(test.second), value);
            BOOST_CHECK(pos == std::end(test.second));
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(value, test.first);
        });
    }
}

BOOST_AUTO_TEST_CASE(array) {
    const std::vector<std::pair<std::uint_fast64_t, const std::string>> cases{
        {0u, "\x80"},
        {1u, "\x81"},
        {10u, "\x8a"},
        {23u, "\x97"},
        {24u, "\x98\x18"},
        {25u, "\x98\x19"},
        {100u, "\x98\x64"},
        {1000u, "\x99\x03\xe8"},
        {1000000u, std::string("\x9a\x00\x0f\x42\x40", 5)},
        {1000000000000u, std::string("\x9b\x00\x00\x00\xe8\xd4\xa5\x10\x00", 9)},
        {18446744073709551615u, std::string("\x9b\xff\xff\xff\xff\xff\xff\xff\xff", 9)},
    };
    for (const auto& test : cases) {
        BOOST_CHECK_NO_THROW({
            std::string buffer;
            auto len = CborLite::encodeArraySize(buffer, test.first);
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(buffer, test.second);
            unsigned long long value = 0;
            auto pos = std::begin(test.second);
            len = CborLite::decodeArraySize(pos, std::end(test.second), value);
            BOOST_CHECK(pos == std::end(test.second));
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(value, test.first);
        });
    }
}

BOOST_AUTO_TEST_CASE(map) {
    const std::vector<std::pair<std::uint_fast64_t, const std::string>> cases{
        {0u, "\xa0"},
        {1u, "\xa1"},
        {10u, "\xaa"},
        {23u, "\xb7"},
        {24u, "\xb8\x18"},
        {25u, "\xb8\x19"},
        {100u, "\xb8\x64"},
        {1000u, "\xb9\x03\xe8"},
        {1000000u, std::string("\xba\x00\x0f\x42\x40", 5)},
        {1000000000000u, std::string("\xbb\x00\x00\x00\xe8\xd4\xa5\x10\x00", 9)},
        {18446744073709551615u, std::string("\xbb\xff\xff\xff\xff\xff\xff\xff\xff", 9)},
    };
    for (const auto& test : cases) {
        BOOST_CHECK_NO_THROW({
            std::string buffer;
            auto len = CborLite::encodeMapSize(buffer, test.first);
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(buffer, test.second);
            unsigned long long value = 0;
            auto pos = std::begin(test.second);
            len = CborLite::decodeMapSize(pos, std::end(test.second), value);
            BOOST_CHECK(pos == std::end(test.second));
            BOOST_CHECK_EQUAL(len, test.second.size());
            BOOST_CHECK_EQUAL(value, test.first);
        });
    }
}

BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()


#pragma once

#include <nano/lib/config.hpp>
#include <nano/lib/locks.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/utility.hpp>

#include <boost/optional.hpp>
#include <boost/thread/thread.hpp>

#include <atomic>
#include <memory>

namespace nano
{
enum class work_version
{
	unspecified,
	work_1
};
std::string to_string (nano::work_version const version_a);

class block;
class block_details;
enum class block_type : uint8_t;
bool work_validate_entry (nano::block const &);
bool work_validate_entry (nano::work_version const, nano::root const &, uint64_t const);

uint64_t work_difficulty (nano::work_version const, nano::root const &, uint64_t const);

uint64_t work_threshold_base (nano::work_version const);
uint64_t work_threshold_entry (nano::work_version const, nano::block_type const);
// Ledger threshold
uint64_t work_threshold (nano::work_version const, nano::block_details const);

namespace work_v1
{
	uint64_t value (nano::root const & root_a, uint64_t work_a);
	uint64_t threshold_base ();
	uint64_t threshold_entry ();
	uint64_t threshold (nano::block_details const);
}

double normalized_multiplier (double const, uint64_t const);
double denormalized_multiplier (double const, uint64_t const);
class opencl_work;
class work_item final
{
public:
	work_item (nano::work_version const version_a, nano::root const & item_a, uint64_t difficulty_a, std::function<void(boost::optional<uint64_t> const &)> const & callback_a) :
	version (version_a), item (item_a), difficulty (difficulty_a), callback (callback_a)
	{
	}
	nano::work_version const version;
	nano::root const item;
	uint64_t const difficulty;
	std::function<void(boost::optional<uint64_t> const &)> const callback;
};
class work_pool final
{
public:
	work_pool (unsigned, std::chrono::nanoseconds = std::chrono::nanoseconds (0), std::function<boost::optional<uint64_t> (nano::work_version const, nano::root const &, uint64_t, std::atomic<int> &)> = nullptr);
	~work_pool ();
	void loop (uint64_t);
	void stop ();
	void cancel (nano::root const &);
	void generate (nano::work_version const, nano::root const &, uint64_t, std::function<void(boost::optional<uint64_t> const &)>);
	boost::optional<uint64_t> generate (nano::work_version const, nano::root const &, uint64_t);
	// For tests only
	boost::optional<uint64_t> generate (nano::root const &);
	boost::optional<uint64_t> generate (nano::root const &, uint64_t);
	size_t size ();
	nano::network_constants network_constants;
	std::atomic<int> ticket;
	bool done;
	std::vector<boost::thread> threads;
	std::list<nano::work_item> pending;
	std::mutex mutex;
	nano::condition_variable producer_condition;
	std::chrono::nanoseconds pow_rate_limiter;
	std::function<boost::optional<uint64_t> (nano::work_version const, nano::root const &, uint64_t, std::atomic<int> &)> opencl;
	nano::observer_set<bool> work_observers;
};

std::unique_ptr<container_info_component> collect_container_info (work_pool & work_pool, const std::string & name);
}


#include "MechanicsFwd.hpp"
#include "NewtonEulerDS.hpp"
#include "BodyDS.hpp"
#include "SiconosContactor.hpp"

#include <boost/make_shared.hpp>

BodyDS::BodyDS(SP::SiconosVector position,
               SP::SiconosVector velocity,
               double mass,
               SP::SimpleMatrix inertia)
  : NewtonEulerDS(position, velocity, mass, inertia)
  , _contactors(std11::make_shared<SiconosContactorSet>())
  , _useContactorInertia(true)
  , _allowSelfCollide(true)
{
}

BodyDS::~BodyDS()
{
}


#include <NTL/GF2X.h>

#include <cstdio>

NTL_CLIENT

double clean_data(double *t)
{
   double x, y, z;
   long i, ix, iy, n;

   x = t[0]; ix = 0;
   y = t[0]; iy = 0;

   for (i = 1; i < 5; i++) {
      if (t[i] < x) {
         x = t[i];
         ix = i;
      }
      if (t[i] > y) {
         y = t[i];
         iy = i;
      }
   }

   z = 0; n = 0;
   for (i = 0; i < 5; i++) {
      if (i != ix && i != iy) z+= t[i], n++;
   }

   z = z/n;

   return z;
}

void print_flag()
{

#ifdef NTL_GF2X_ALTCODE
printf("NTL_GF2X_ALTCODE ");
#endif

#ifdef NTL_GF2X_ALTCODE1
printf("NTL_GF2X_ALTCODE1 ");
#endif

#ifdef NTL_GF2X_NOINLINE
printf("NTL_GF2X_NOINLINE ");
#endif

printf("\n");

}

int main()
{
   long n, i, j, iter, s, k;
   double t;

   SetSeed(ZZ(0));

   for (i = 0; i < 10000; i++) {
      GF2X a, b, c, d;
      long da = RandomBnd(5*NTL_BITS_PER_LONG);
      long db = RandomBnd(5*NTL_BITS_PER_LONG);
      long dc = RandomBnd(5*NTL_BITS_PER_LONG);
      long dd = RandomBnd(5*NTL_BITS_PER_LONG);
      random(a, da);  random(b, db);  random(c, dc);  random(d, dd);

      if ((a + b)*(c + d) != c*a + d*a + c*b + d*b) {
	 printf("999999999999999 ");
	 print_flag();
	 return 0;
      }
   }

   n = 16;
   s = 56;

   GF2X *a = new GF2X[s];
   GF2X *b = new GF2X[s];

   GF2X c;

   for (k = 0; k < s; k++) {
      random(a[k], (n + (k % 7))*NTL_BITS_PER_LONG);
      random(b[k], (n + (k % 8))*NTL_BITS_PER_LONG);
   }

   for (k = 0; k < s; k++) mul(c, a[k], b[k]);

   iter = 1;

   do {
     t = GetTime();
     for (i = 0; i < iter; i++) {
        for (j = 0; j < 1; j++) for (k = 0; k < s; k++) mul(c, a[k], b[k]);
     }
     t = GetTime() - t;
     iter = 2*iter;
   } while(t < 1);

   iter = iter/2;

   iter = long((3/t)*iter) + 1;

   double tvec[5];
   long w;

   for (w = 0; w < 5; w++) {
     t = GetTime();
     for (i = 0; i < iter; i++) {
        for (j = 0; j < 1; j++) for (k = 0; k < s; k++) mul(c, a[k], b[k]);
     }
     t = GetTime() - t;
     tvec[w] = t;
   }

   t = clean_data(tvec);

   t = floor((t/iter)*1e14);

   if (t < 0 || t >= 1e15)
      printf("999999999999999 ");
   else
      printf("%015.0f ", t);

   printf(" [%ld] ", iter);

   print_flag();

   return 0;
}


#pragma once

#include <src/common.hpp>
#include <src/lib/interface.h>

#include <boost/asio.hpp>

#include <bitset>

#include <xxhash/xxhash.h>

#include <src/lib/epoch.h>
#include <unordered_set>

namespace germ
{
using endpoint = boost::asio::ip::udp::endpoint;
bool parse_port (std::string const &, uint16_t &);
bool parse_address_port (std::string const &, boost::asio::ip::address &, uint16_t &);
using tcp_endpoint = boost::asio::ip::tcp::endpoint;
bool parse_endpoint (std::string const &, germ::endpoint &);
bool parse_tcp_endpoint (std::string const &, germ::tcp_endpoint &);
bool reserved_address (germ::endpoint const &, bool);
}
static uint64_t endpoint_hash_raw (germ::endpoint const & endpoint_a)
{
    assert (endpoint_a.address ().is_v6 ());
    germ::uint128_union address;
    address.bytes = endpoint_a.address ().to_v6 ().to_bytes ();
    XXH64_state_t hash;
    XXH64_reset (&hash, 0);
    XXH64_update (&hash, address.bytes.data (), address.bytes.size ());
    auto port (endpoint_a.port ());
    XXH64_update (&hash, &port, sizeof (port));
    auto result (XXH64_digest (&hash));
    return result;
}
static uint64_t ip_address_hash_raw (boost::asio::ip::address const & ip_a)
{
    assert (ip_a.is_v6 ());
    germ::uint128_union bytes;
    bytes.bytes = ip_a.to_v6 ().to_bytes ();
    XXH64_state_t hash;
    XXH64_reset (&hash, 0);
    XXH64_update (&hash, bytes.bytes.data (), bytes.bytes.size ());
    auto result (XXH64_digest (&hash));
    return result;
}

namespace std
{
template <size_t size>
struct endpoint_hash
{
};
template <>
struct endpoint_hash<8>
{
    size_t operator() (germ::endpoint const & endpoint_a) const
    {
        return endpoint_hash_raw (endpoint_a);
    }
};
template <>
struct endpoint_hash<4>
{
    size_t operator() (germ::endpoint const & endpoint_a) const
    {
        uint64_t big (endpoint_hash_raw (endpoint_a));
        uint32_t result (static_cast<uint32_t> (big) ^ static_cast<uint32_t> (big >> 32));
        return result;
    }
};
template <>
struct hash<germ::endpoint>
{
    size_t operator() (germ::endpoint const & endpoint_a) const
    {
        endpoint_hash<sizeof (size_t)> ehash;
        return ehash (endpoint_a);
    }
};
template <size_t size>
struct ip_address_hash
{
};
template <>
struct ip_address_hash<8>
{
    size_t operator() (boost::asio::ip::address const & ip_address_a) const
    {
        return ip_address_hash_raw (ip_address_a);
    }
};
template <>
struct ip_address_hash<4>
{
    size_t operator() (boost::asio::ip::address const & ip_address_a) const
    {
        uint64_t big (ip_address_hash_raw (ip_address_a));
        uint32_t result (static_cast<uint32_t> (big) ^ static_cast<uint32_t> (big >> 32));
        return result;
    }
};
template <>
struct hash<boost::asio::ip::address>
{
    size_t operator() (boost::asio::ip::address const & ip_a) const
    {
        ip_address_hash<sizeof (size_t)> ihash;
        return ihash (ip_a);
    }
};
}
namespace boost
{
template <>
struct hash<germ::endpoint>
{
    size_t operator() (germ::endpoint const & endpoint_a) const
    {
        std::hash<germ::endpoint> hash;
        return hash (endpoint_a);
    }
};
}

namespace germ
{
enum class message_type : uint8_t
{
    invalid,
    not_a_type,
    keepalive,
    publish,
    confirm_req,
    confirm_ack,
    bulk_pull,
    bulk_push,
    frontier_req,
    bulk_pull_blocks,
    node_id_handshake,
    epoch_req,
    epoch_bulk_pull,
    epoch_bulk_push,
    transaction
};
enum class bulk_pull_blocks_mode : uint8_t
{
    list_blocks,
    checksum_blocks
};
class message_visitor;
class message_header
{
public:
    message_header (germ::message_type, size_t body_size_a);
    message_header (bool &, germ::stream &);
    void serialize (germ::stream &);
    bool deserialize (germ::stream &);
    germ::block_type block_type () const;
    void block_type_set (germ::block_type);
    bool ipv4_only ();
    void ipv4_only_set (bool);
    static std::array<uint8_t, 2> constexpr magic_number = germ::rai_network == germ::germ_networks::germ_test_network ? std::array<uint8_t, 2>{ { 'R', 'A' } } : germ::rai_network == germ::germ_networks::germ_beta_network ? std::array<uint8_t, 2>{ { 'R', 'B' } } : std::array<uint8_t, 2>{ { 'R', 'C' } };
    uint8_t version;
    germ::message_type type;
    std::bitset<16> extensions;
    static size_t constexpr ipv4_only_position = 1;
    static size_t constexpr bootstrap_server_position = 2;
    static std::bitset<16> constexpr block_type_mask = std::bitset<16> (0x0f00);
    size_t body_size;
};
class message
{
public:
    message (germ::message_type, size_t);
    message (germ::message_header const &);
    virtual ~message () = default;
    virtual void serialize (germ::stream &) = 0;
    virtual bool deserialize (germ::stream &) = 0;
    virtual void visit (germ::message_visitor &) const = 0;
    germ::message_header header;
};
class work_pool;
class message_parser
{
public:
    enum class parse_status
    {
        success,
        invalid_header,
        invalid_message_type,
        invalid_keepalive_message,
        invalid_publish_message,
        invalid_confirm_req_message,
        invalid_confirm_ack_message,
        invalid_node_id_handshake_message,
        invalid_epoch_req_message,
        invalid_epoch_bulk_pull_message,
        invalid_epoch_bulk_push_message,
        invalid_transaction_message
    };
    message_parser (germ::message_visitor &, germ::work_pool &);
    void deserialize_buffer (uint8_t const *, size_t);
    void deserialize_keepalive (germ::stream &, germ::message_header const &);
    void deserialize_publish (germ::stream &, germ::message_header const &);
    void deserialize_confirm_req (germ::stream &, germ::message_header const &);
    void deserialize_confirm_ack (germ::stream &, germ::message_header const &);
    void deserialize_node_id_handshake (germ::stream &, germ::message_header const &);

    void deserialize_epoch_req(germ::stream & stream_r, germ::message_header const & header_r);
    void deserialize_epoch_bulk_pull(germ::stream & stream_r, germ::message_header const & header_r);
    void deserialize_epoch_bulk_push(germ::stream & stream_r, germ::message_header const & header_r);

    void deserialize_transaction(germ::stream & stream_r, germ::message_header const & header_r);

    bool at_end (germ::stream &);
    germ::message_visitor & visitor;
    germ::work_pool & pool;
    parse_status status;
};
class keepalive : public message
{
public:
    keepalive (bool &, germ::stream &, germ::message_header const &);
    keepalive ();
    void visit (germ::message_visitor &) const override;
    bool deserialize (germ::stream &) override;
    void serialize (germ::stream &) override;
    bool operator== (germ::keepalive const &) const;
    std::array<germ::endpoint, 8> peers;
};
class publish : public message
{
public:
    publish (bool &, germ::stream &, germ::message_header const &);
    publish (std::shared_ptr<germ::tx>);
    void visit (germ::message_visitor &) const override;
    bool deserialize (germ::stream &) override;
    void serialize (germ::stream &) override;
    bool operator== (germ::publish const &) const;
    std::shared_ptr<germ::tx> block;
};
class confirm_req : public message
{
public:
    confirm_req (bool &, germ::stream &, germ::message_header const &);
    confirm_req (std::shared_ptr<germ::tx>);
    bool deserialize (germ::stream &) override;
    void serialize (germ::stream &) override;
    void visit (germ::message_visitor &) const override;
    bool operator== (germ::confirm_req const &) const;
    std::shared_ptr<germ::tx> block;
};
class confirm_ack : public message
{
public:
    confirm_ack (bool &, germ::stream &, germ::message_header const &);
    confirm_ack (std::shared_ptr<germ::vote>);
    bool deserialize (germ::stream &) override;
    void serialize (germ::stream &) override;
    void visit (germ::message_visitor &) const override;
    bool operator== (germ::confirm_ack const &) const;
    std::shared_ptr<germ::vote> vote;
};
class frontier_req : public message
{
public:
    frontier_req ();
    frontier_req (bool &, germ::stream &, germ::message_header const &);
    bool deserialize (germ::stream &) override;
    void serialize (germ::stream &) override;
    void visit (germ::message_visitor &) const override;
    bool operator== (germ::frontier_req const &) const;
    germ::account start;
    uint32_t age;
    uint32_t count;
};
class bulk_pull : public message
{
public:
    bulk_pull ();
    bulk_pull (bool &, germ::stream &, germ::message_header const &);
    bool deserialize (germ::stream &) override;
    void serialize (germ::stream &) override;
    void visit (germ::message_visitor &) const override;
    germ::uint256_union start;
    germ::block_hash end;
};
class bulk_pull_blocks : public message
{
public:
    bulk_pull_blocks ();
    bulk_pull_blocks (bool &, germ::stream &, germ::message_header const &);
    bool deserialize (germ::stream &) override;
    void serialize (germ::stream &) override;
    void visit (germ::message_visitor &) const override;
    germ::block_hash min_hash;
    germ::block_hash max_hash;
    bulk_pull_blocks_mode mode;
    uint32_t max_count;
};
class bulk_push : public message
{
public:
    bulk_push ();
    bulk_push (germ::message_header const &);
    bool deserialize (germ::stream &) override;
    void serialize (germ::stream &) override;
    void visit (germ::message_visitor &) const override;
};
class node_id_handshake : public message
{
public:
    node_id_handshake (bool &, germ::stream &, germ::message_header const &);
    node_id_handshake (boost::optional<germ::block_hash>, boost::optional<std::pair<germ::account, germ::signature>>);
    bool deserialize (germ::stream &) override;
    void serialize (germ::stream &) override;
    void visit (germ::message_visitor &) const override;
    bool operator== (germ::node_id_handshake const &) const;
    boost::optional<germ::uint256_union> query;
    boost::optional<std::pair<germ::account, germ::signature>> response;
    static size_t constexpr query_flag = 0;
    static size_t constexpr response_flag = 1;
};

class epoch_req : public message
{
public:
    epoch_req();
    epoch_req(bool & error_r, germ::stream & stream_r, germ::message_header const & header_r);
    bool deserialize (germ::stream & stream_r) override ;
    void serialize (germ::stream & stream_r) override ;
    void visit (germ::message_visitor & visit_r) const override ;
    bool operator== (germ::epoch_req const & epoch) const;

    germ::epoch_hash start;
//    germ::epoch_hash end;
    uint32_t age;
    uint32_t count;
};

class epoch_bulk_pull : public message
{
public:
    epoch_bulk_pull ();
    epoch_bulk_pull (bool &, germ::stream &, germ::message_header const &);
    bool deserialize (germ::stream &) override;
    void serialize (germ::stream &) override;
    void visit (germ::message_visitor &) const override;
    germ::epoch_hash start;
    germ::epoch_hash end;
};

class epoch_bulk_push : public message
{
public:
    epoch_bulk_push ();
    epoch_bulk_push (germ::message_header const &);
    bool deserialize (germ::stream &) override;
    void serialize (germ::stream &) override;
    void visit (germ::message_visitor &) const override;
};

class transaction_message : public message
{
public:
    transaction_message (bool &, germ::stream &, germ::message_header const &);
    transaction_message (std::shared_ptr<germ::tx>);
    bool deserialize (germ::stream &) override;
    void serialize (germ::stream &) override;
    void visit (germ::message_visitor &) const override;
    bool operator== (germ::transaction_message const &) const;
    std::shared_ptr<germ::tx> block;
};

class message_visitor
{
public:
    virtual void keepalive (germ::keepalive const &) = 0;
    virtual void publish (germ::publish const &) = 0;
    virtual void confirm_req (germ::confirm_req const &) = 0;
    virtual void confirm_ack (germ::confirm_ack const &) = 0;
    virtual void bulk_pull (germ::bulk_pull const &) = 0;
    virtual void bulk_pull_blocks (germ::bulk_pull_blocks const &) = 0;
    virtual void bulk_push (germ::bulk_push const &) = 0;
    virtual void frontier_req (germ::frontier_req const &) = 0;
    virtual void node_id_handshake (germ::node_id_handshake const &) = 0;
    virtual void epoch_req (germ::epoch_req const &) = 0;
    virtual void epoch_bulk_pull (germ::epoch_bulk_pull const &) = 0;
    virtual void epoch_bulk_push (germ::epoch_bulk_push const &) = 0;
    virtual void transaction (germ::transaction_message const &) = 0;

    virtual ~message_visitor ();
};

/**
 * Returns seconds passed since unix epoch (posix time)
 */
inline uint64_t seconds_since_epoch ()
{
    return std::chrono::duration_cast<std::chrono::seconds> (std::chrono::system_clock::now ().time_since_epoch ()).count ();
}
}


/*
 * Copyright 2019 Xilinx, 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.
 */
/*--
 * ---------------------------------------------------------------------------------------------------------------------*/
/*-- DISCLAIMER AND CRITICAL APPLICATIONS */
/*--
 * ---------------------------------------------------------------------------------------------------------------------*/
/*-- */
/*-- (c) Copyright 2019 Xilinx, Inc. All rights reserved. */
/*-- */
/*-- This file contains confidential and proprietary information of Xilinx, Inc. and is protected under U.S. and */
/*-- international copyright and other intellectual property laws. */
/*-- */
/*-- DISCLAIMER */
/*-- This disclaimer is not a license and does not grant any rights to the materials distributed herewith. Except as */
/*-- otherwise provided in a valid license issued to you by Xilinx, and to the maximum extent permitted by applicable */
/*-- law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
 */
/*-- AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON- */
/*-- INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and (2) Xilinx shall not be liable (whether in contract or
 * tort,*/
/*-- including negligence, or under any other theory of liability) for any loss or damage of any kind or nature */
/*-- related to, arising under or in connection with these materials, including for any direct, or any indirect, */
/*-- special, incidental, or consequential loss or damage (including loss of data, profits, goodwill, or any type of */
/*-- loss or damage suffered as a retVal of any action brought by a third party) even if such damage or loss was */
/*-- reasonably foreseeable or Xilinx had been advised of the possibility of the same. */
/*-- */
/*-- CRITICAL APPLICATIONS */
/*-- Xilinx products are not designed or intended to be fail-safe, or for use in any application requiring fail-safe */
/*-- performance, such as life-support or safety devices or systems, Class III medical devices, nuclear facilities, */
/*-- applications related to the deployment of airbags, or any other applications that could lead to death, personal */
/*-- injury, or severe property or environmental damage (individually and collectively, "Critical */
/*-- Applications"). Customer assumes the sole risk and liability of any use of Xilinx products in Critical */
/*-- Applications, subject only to applicable laws and regulations governing limitations on product liability. */
/*-- */
/*-- THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS PART OF THIS FILE AT ALL TIMES. */
/*--
 * ---------------------------------------------------------------------------------------------------------------------*/

#include <stdio.h>
#include <string.h>

#include <chrono>
#include <vector>
#include <boost/algorithm/string.hpp>

#include "xf_fintech_api.hpp"
#include "models/xf_fintech_fdbslv.hpp"
using namespace xf::fintech;

struct testcaseParams {
    unsigned int solverN;
    unsigned int solverM;
    double solverTheta;
    double modelS;
    double modelK;
    double boundaryLower;
    double boundaryUpper;
};

int ReadVector(const std::string filename, std::vector<float>& A, const unsigned int size) {
    std::ifstream file(filename);

    std::vector<std::string> v;
    std::string line;

    if (file.good()) {
        std::cout << "Opened " << filename << " OK" << std::endl;

        unsigned int i = 0;
        while (file.good()) {
            getline(file, line);
            if (line.length()) {
                A[i] = std::stof(line);
                i++;
            }
            if (i > size) {
                std::cout << "Warning! File has more than expected " << size << " lines..." << std::endl;
                break;
            }
        }
    } else {
        std::cout << "Couldn't open " << filename << std::endl;
        return 1;
    }
    return 0;
}

/// @brief Read testcase parameters
int ReadTestcaseParameters(std::string filename, testcaseParams& params) {
    std::ifstream file(filename);

    std::vector<std::string> v;
    std::string line;

    if (file.good()) {
        std::cout << "Opened " << filename << " OK" << std::endl;

        getline(file, line);
        boost::split(v, line, [](char c) { return c == ','; });
        params.solverN = std::stoi(v[0]);
        params.solverM = std::stoi(v[1]);
        params.solverTheta = std::stod(v[2]);
        params.modelS = std::stod(v[3]);
        params.modelK = std::stod(v[4]);
        params.boundaryLower = std::stod(v[5]);
        params.boundaryUpper = std::stod(v[6]);
    } else {
        std::cout << "Couldn't open " << filename << std::endl;
        return 1;
    }
    return 0;
}

int main(int argc, char** argv) {
    // xclbin file
    std::string path = std::string(argv[1]);

    // test data
    std::string test_dir = std::string(argv[2]);

    // Get the testcase parameters
    testcaseParams params;
    std::cout << "Loading testcase..." << std::endl;
    if (ReadTestcaseParameters(test_dir + "/parameters.csv", params)) return EXIT_FAILURE;

    // Extract N, M to aid readability
    const unsigned int N = params.solverN;
    const unsigned int M = params.solverM;

    // device
    std::string device = TOSTRING(DEVICE_PART);
    if (argc == 4) {
        device = std::string(argv[3]);
    }

    // Create the FD solver object
    fdbslv fdbslv(N, M, path);

    int retval = XLNX_OK;

    std::vector<Device*> deviceList;
    Device* pChosenDevice;

    deviceList = DeviceManager::getDeviceList(device);

    if (deviceList.size() == 0) {
        printf("No matching devices found\n");
        exit(0);
    }

    printf("Found %zu matching devices\n", deviceList.size());

    // we'll just pick the first device in the...
    pChosenDevice = deviceList[0];

    if (retval == XLNX_OK) {
        // turn off trace output...turn it on here if you want extra debug output...
        Trace::setEnabled(true);
    }

    // clain the device
    printf("\n\n\n");
    printf("[XF_FINTECH] trying to claim device...\n");
    retval = fdbslv.claimDevice(pChosenDevice);
    if (retval != XLNX_OK) {
        printf("[XF_FINTECH] Failed to claim device - error = %d\n", retval);
    }

    // read input data
    std::vector<float> xGrid(N);
    std::vector<float> tGrid(M);
    std::vector<float> sigma(N * M);
    std::vector<float> rate(M);
    std::vector<float> initialCondition(N);
    std::vector<float> solution(N);
    std::vector<float> reference(N);

    if (ReadVector(test_dir + "/xGrid.csv", xGrid, N)) return 1;
    if (ReadVector(test_dir + "/tGrid.csv", tGrid, M)) return 1;
    if (ReadVector(test_dir + "/sigma.csv", sigma, N * M)) return 1;
    if (ReadVector(test_dir + "/rate.csv", rate, M)) return 1;
    if (ReadVector(test_dir + "/initialCondition.csv", initialCondition, N)) return 1;
    if (ReadVector(test_dir + "/reference.csv", reference, N)) return 1;

    // run the kernel
    retval = fdbslv.run(xGrid, tGrid, sigma, rate, initialCondition, params.solverTheta, params.boundaryLower,
                        params.boundaryUpper, solution);

    // check the results
    float max_diff = 0;
    int index = 0;
    std::cout << "actual (expected)" << std::endl;
    for (unsigned int i = 0; i < N; i++) {
        std::cout << solution[i] << "  (" << reference[i] << ")" << std::endl;
        if (std::abs(solution[i] - reference[i]) > max_diff) {
            max_diff = std::abs(solution[i] - reference[i]);
            index = i;
        }
    }
    std::cout << "Max difference = " << max_diff << " at index " << index << std::endl;

    // release the device
    retval = fdbslv.releaseDevice();

    int ret = 0; // assume pass
    if (max_diff > 0.003) {
        std::cout << "FAIL" << std::endl;
        ret = 1;
    } else {
        std::cout << "PASS" << std::endl;
    }

    return ret;
}


#include <cmath>
#include <ctime>
#include <regex>

#include <boost/filesystem.hpp>

#include "util.h"

void ReadFileNames(const std::string& directory, std::vector<std::string>& fileNames, int nDocs) {
    boost::filesystem::path dirPath(directory);
    boost::filesystem::recursive_directory_iterator start(dirPath);
    boost::filesystem::recursive_directory_iterator end;
    for (auto it = start; it != end; it++) {
        if (boost::filesystem::is_directory(it->path())) {
            continue;
        }
        std::string path = it->path().string();
        if (path.substr(path.length() - 5) == ".html") {
            fileNames.push_back(path);
        }
        if (nDocs != -1 && fileNames.size() == static_cast<size_t>(nDocs)) {
            break;
        }
    }
}

const std::regex hostRegex("(http|https)://(?:www\\.)?([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");

std::string GetHost(const std::string& url) {
    // if u know better way to do it - it would be nice if u rewrite it
    std::string output = "";
    // https://stackoverflow.com/questions/2616011/easy-way-to-parse-a-url-in-c-cross-platform
    try {
        std::smatch what;
        if (std::regex_match(url, what, hostRegex) && what.size() >= 3) {
            output = std::string(what[2].first, what[2].second);
        }
    } catch (...) {
        LOG_DEBUG("Can't parse host from url: " << url);
        return output;
    }
    return output;
}

std::string CleanFileName(const std::string& fileName) {
    return fileName.substr(fileName.find_last_of("/") + 1);
}

float Sigmoid(float x) {
    if (x >= 0.0f) {
        float z = exp(-x);
        return 1.0f / (1.0f + z);
    }
    float z = exp(x);
    return z / (1.0f + z);
}

double Sigmoid(double x) {
    if (x >= 0.0) {
        double z = exp(-x);
        return 1.0 / (1.0 + z);
    }
    double z = exp(x);
    return z / (1.0 + z);
}

uint64_t DateToTimestamp(const std::string& date) {
    std::regex ex("(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)T(\\d\\d):(\\d\\d):(\\d\\d)([+-])(\\d\\d):(\\d\\d)");
    std::smatch what;
    if (!std::regex_match(date, what, ex) || what.size() < 10) {
        throw std::runtime_error("wrong date format");
    }
    std::tm t = {};
    t.tm_sec = std::stoi(what[6]);
    t.tm_min = std::stoi(what[5]);
    t.tm_hour = std::stoi(what[4]);
    t.tm_mday = std::stoi(what[3]);
    t.tm_mon = std::stoi(what[2]) - 1;
    t.tm_year = std::stoi(what[1]) - 1900;

    time_t timestamp = timegm(&t);
    uint64_t zone_ts = std::stoi(what[8]) * 60 * 60 + std::stoi(what[9]) * 60;
    if (what[7] == "+") {
        timestamp = timestamp - zone_ts;
    } else if (what[7] == "-") {
        timestamp = timestamp + zone_ts;
    }
    return timestamp > 0 ? timestamp : 0;
}


// Copyright (c) 2020-2021 Franz Alt
// This code is licensed under MIT license (see LICENSE.txt for details).

#include <libcvpg/imageproc/scripting/algorithms/mean.hpp>

#include <chrono>
#include <functional>
#include <string>

#include <boost/asynchronous/continuation_task.hpp>

#include <libcvpg/core/exception.hpp>
#include <libcvpg/core/image.hpp>
#include <libcvpg/imageproc/algorithms/tiling.hpp>
#include <libcvpg/imageproc/algorithms/tiling/mean.hpp>
#include <libcvpg/imageproc/scripting/item.hpp>
#include <libcvpg/imageproc/scripting/processing_context.hpp>
#include <libcvpg/imageproc/scripting/detail/compiler.hpp>
#include <libcvpg/imageproc/scripting/detail/handler.hpp>
#include <libcvpg/imageproc/scripting/detail/parser.hpp>

namespace detail {

struct mean_task :  public boost::asynchronous::continuation_task<std::shared_ptr<cvpg::imageproc::scripting::processing_context> >
{
    mean_task(std::shared_ptr<cvpg::imageproc::scripting::processing_context> context, std::uint32_t result_id, cvpg::imageproc::scripting::detail::parser::item item)
        : boost::asynchronous::continuation_task<std::shared_ptr<cvpg::imageproc::scripting::processing_context> >("algorithms::mean_task")
        , m_context(context)
        , m_result_id(result_id)
        , m_item(std::move(item))
    {}

    void operator()()
    {
        try
        {
            auto id = std::any_cast<std::uint32_t>(m_item.arguments.at(0).value());
            auto width = std::any_cast<std::int32_t>(m_item.arguments.at(1).value());
            auto height = std::any_cast<std::int32_t>(m_item.arguments.at(2).value());
            auto border_mode_str = std::any_cast<std::string>(m_item.arguments.at(3).value());

            auto input = m_context->load(id);
            auto parameters = m_context->parameters();

            std::uint32_t cutoff_x = 512;
            std::uint32_t cutoff_y = 512;

            {
                auto it = parameters.find("cutoff_x");

                if (it != parameters.end())
                {
                    cutoff_x = std::any_cast<std::uint32_t>(it->second);
                }
            }

            {
                auto it = parameters.find("cutoff_y");

                if (it != parameters.end())
                {
                    cutoff_y = std::any_cast<std::uint32_t>(it->second);
                }
            }

            auto border_mode = cvpg::imageproc::algorithms::to_border_mode(border_mode_str);

            if (input.type() == cvpg::imageproc::scripting::item::types::grayscale_8_bit_image)
            {
                auto image = std::any_cast<cvpg::image_gray_8bit>(input.value());

                const auto image_width = image.width();
                const auto image_height = image.height();

                auto start = std::chrono::system_clock::now();

                auto tf = cvpg::imageproc::algorithms::tiling_functors::image<cvpg::image_gray_8bit>({{ std::move(image) }});
                tf.parameters.image_width = image_width;
                tf.parameters.image_height = image_height;
                tf.parameters.cutoff_x = cutoff_x;
                tf.parameters.cutoff_y = cutoff_y;
                tf.parameters.signed_integer_numbers.push_back(width); // filter width
                tf.parameters.signed_integer_numbers.push_back(height); // filter height
                tf.parameters.border_mode = border_mode;

                tf.tile_algorithm_task = [](std::shared_ptr<cvpg::image_gray_8bit> src1, std::shared_ptr<cvpg::image_gray_8bit> /*src2*/, std::shared_ptr<cvpg::image_gray_8bit> dst, std::size_t from_x, std::size_t to_x, std::size_t from_y, std::size_t to_y, cvpg::imageproc::algorithms::tiling_parameters parameters)
                {
                    cvpg::imageproc::algorithms::mean_gray_8bit(src1->data(0).get(), dst->data(0).get(), from_x, to_x, from_y, to_y, std::move(parameters));
                };

                boost::asynchronous::create_callback_continuation(
                    [result = this->this_task_result(), context = m_context, result_id = m_result_id, start](auto cont_res) mutable
                    {
                        auto stop = std::chrono::system_clock::now();

                        try
                        {
                            context->store(result_id, std::move(std::get<0>(cont_res).get()), std::chrono::duration_cast<std::chrono::microseconds>(stop - start));

                            result.set_value(context);
                        }
                        catch (...)
                        {
                            result.set_exception(std::current_exception());
                        }
                    },
                    cvpg::imageproc::algorithms::tiling(std::move(tf))
                );
            }
            else if (input.type() == cvpg::imageproc::scripting::item::types::rgb_8_bit_image)
            {
                auto image = std::any_cast<cvpg::image_rgb_8bit>(input.value());

                const auto image_width = image.width();
                const auto image_height = image.height();

                auto start = std::chrono::system_clock::now();

                auto tf = cvpg::imageproc::algorithms::tiling_functors::image<cvpg::image_rgb_8bit>({{ std::move(image) }});
                tf.parameters.image_width = image_width;
                tf.parameters.image_height = image_height;
                tf.parameters.cutoff_x = cutoff_x;
                tf.parameters.cutoff_y = cutoff_y;
                tf.parameters.signed_integer_numbers.push_back(width); // filter width
                tf.parameters.signed_integer_numbers.push_back(height); // filter height
                tf.parameters.border_mode = border_mode;

                tf.tile_algorithm_task = [](std::shared_ptr<cvpg::image_rgb_8bit> src1, std::shared_ptr<cvpg::image_rgb_8bit> /*src2*/, std::shared_ptr<cvpg::image_rgb_8bit> dst, std::size_t from_x, std::size_t to_x, std::size_t from_y, std::size_t to_y, cvpg::imageproc::algorithms::tiling_parameters parameters)
                {
                    cvpg::imageproc::algorithms::mean_gray_8bit(src1->data(0).get(), dst->data(0).get(), from_x, to_x, from_y, to_y, parameters);
                    cvpg::imageproc::algorithms::mean_gray_8bit(src1->data(1).get(), dst->data(1).get(), from_x, to_x, from_y, to_y, parameters);
                    cvpg::imageproc::algorithms::mean_gray_8bit(src1->data(2).get(), dst->data(2).get(), from_x, to_x, from_y, to_y, std::move(parameters));
                };

                boost::asynchronous::create_callback_continuation(
                    [result = this->this_task_result(), context = m_context, result_id = m_result_id, start](auto cont_res) mutable
                    {
                        auto stop = std::chrono::system_clock::now();

                        try
                        {
                            context->store(result_id, std::move(std::get<0>(cont_res).get()), std::chrono::duration_cast<std::chrono::microseconds>(stop - start));

                            result.set_value(context);
                        }
                        catch (...)
                        {
                            result.set_exception(std::current_exception());
                        }
                    },
                    cvpg::imageproc::algorithms::tiling(std::move(tf))
                );
            }
        }
        catch (...)
        {
            this->this_task_result().set_exception(std::current_exception());
        }
    }

private:
    std::shared_ptr<cvpg::imageproc::scripting::processing_context> m_context;

    std::uint32_t m_result_id;

    cvpg::imageproc::scripting::detail::parser::item m_item;
};

auto mean(std::shared_ptr<cvpg::imageproc::scripting::processing_context> context, std::uint32_t result_id, cvpg::imageproc::scripting::detail::parser::item item)
{
    return boost::asynchronous::top_level_callback_continuation<std::shared_ptr<cvpg::imageproc::scripting::processing_context> >(
               mean_task(context, result_id, std::move(item))
           );
}

} // namespace detail

namespace cvpg::imageproc::scripting::algorithms {

std::string mean::name() const
{
    return "mean";
}

std::string mean::category() const
{
    return "filters/smoothing";
}

std::vector<scripting::item::types> mean::result() const
{
    return
    {
        scripting::item::types::grayscale_8_bit_image,
        scripting::item::types::rgb_8_bit_image,
    };
}

parameter_set mean::parameters() const
{
    using namespace std::string_literals;

    return parameter_set
           ({
               parameter("image", "input image", "", { scripting::item::types::grayscale_8_bit_image, scripting::item::types::rgb_8_bit_image }),
               parameter("filter_width", "filter width", "pixels", scripting::item::types::signed_integer, static_cast<std::int32_t>(3), static_cast<std::int32_t>(65535), static_cast<std::int32_t>(2)),
               parameter("filter_height", "filter height", "pixels", scripting::item::types::signed_integer, static_cast<std::int32_t>(3), static_cast<std::int32_t>(65535), static_cast<std::int32_t>(2)),
               parameter("border_mode", "border mode", "", scripting::item::types::characters, { "ignore"s, "constant"s, "mirror"s })
           });
}

void mean::on_parse(std::shared_ptr<detail::parser> parser) const
{
    // all parameters
    {
        std::function<std::uint32_t(std::uint32_t, std::uint32_t, std::uint32_t, std::string)> fct =
            [parser, parameters = this->parameters()](std::uint32_t image_id, std::uint32_t width, std::uint32_t height, std::string border_mode)
            {
                std::uint32_t result_id = 0;

                // find image
                if (!parser)
                {
                    throw cvpg::invalid_parameter_exception("invalid parser");
                }

                auto image = parser->find_item(image_id);

                if (image.arguments.empty())
                {
                    throw cvpg::invalid_parameter_exception("invalid input ID");
                }

                auto input_type = image.arguments.front().type();

                // check parameters
                if (!(input_type == scripting::item::types::grayscale_8_bit_image || input_type == scripting::item::types::rgb_8_bit_image))
                {
                    throw cvpg::invalid_parameter_exception("invalid input type");
                }

                if (!parameters.is_valid("filter_width", static_cast<std::int32_t>(width)))
                {
                    throw cvpg::invalid_parameter_exception("invalid filter width");
                }

                if (!parameters.is_valid("filter_height", static_cast<std::int32_t>(height)))
                {
                    throw cvpg::invalid_parameter_exception("invalid filter height");
                }

                if (!parameters.is_valid("border_mode", border_mode))
                {
                    throw cvpg::invalid_parameter_exception("invalid border mode");
                }

                switch (input_type)
                {
                    case scripting::item::types::grayscale_8_bit_image:
                    {
                        detail::parser::item result_item
                        {
                            "mean",
                            {
                                scripting::item(scripting::item::types::grayscale_8_bit_image, image_id),
                                scripting::item(scripting::item::types::signed_integer, static_cast<std::int32_t>(width)),
                                scripting::item(scripting::item::types::signed_integer, static_cast<std::int32_t>(height)),
                                scripting::item(scripting::item::types::characters, border_mode)
                            }
                        };

                        result_id = parser->register_item(std::move(result_item));

                        break;
                    }

                    case scripting::item::types::rgb_8_bit_image:
                    {
                        detail::parser::item result_item
                        {
                            "mean",
                            {
                                scripting::item(scripting::item::types::rgb_8_bit_image, image_id),
                                scripting::item(scripting::item::types::signed_integer, static_cast<std::int32_t>(width)),
                                scripting::item(scripting::item::types::signed_integer, static_cast<std::int32_t>(height)),
                                scripting::item(scripting::item::types::characters, border_mode)
                            }
                        };

                        result_id = parser->register_item(std::move(result_item));

                        break;
                    }

                    default:
                    {
                        // to make the compiler happy ; other input types are not allowed and should be handled above
                        break;
                    }
                }

                if (result_id != 0)
                {
                    parser->register_link(image_id, result_id);
                }

                return result_id;
            };

        parser->register_specification(name(), std::move(fct));
    }

    // default for border mode
    {
        std::function<std::uint32_t(std::uint32_t, std::uint32_t, std::uint32_t)> fct =
            [parser, parameters = this->parameters()](std::uint32_t image_id, std::uint32_t width, std::uint32_t height)
            {
                // find image
                if (!parser)
                {
                    throw cvpg::invalid_parameter_exception("invalid parser");
                }

                auto image = parser->find_item(image_id);

                if (image.arguments.empty())
                {
                    throw cvpg::invalid_parameter_exception("invalid input ID");
                }

                auto input_type = image.arguments.front().type();

                // check parameters
                if (!(input_type == scripting::item::types::grayscale_8_bit_image || input_type == scripting::item::types::rgb_8_bit_image))
                {
                    throw cvpg::invalid_parameter_exception("invalid input type");
                }

                if (!parameters.is_valid("filter_width", static_cast<std::int32_t>(width)))
                {
                    throw cvpg::invalid_parameter_exception("invalid filter width");
                }

                if (!parameters.is_valid("filter_height", static_cast<std::int32_t>(height)))
                {
                    throw cvpg::invalid_parameter_exception("invalid filter height");
                }

                std::uint32_t result_id = 0;

                std::string border_mode = "constant";

                switch (input_type)
                {
                    case scripting::item::types::grayscale_8_bit_image:
                    {
                        detail::parser::item result_item
                        {
                            "mean",
                            {
                                scripting::item(scripting::item::types::grayscale_8_bit_image, image_id),
                                scripting::item(scripting::item::types::signed_integer, static_cast<std::int32_t>(width)),
                                scripting::item(scripting::item::types::signed_integer, static_cast<std::int32_t>(height)),
                                scripting::item(scripting::item::types::characters, border_mode)
                            }
                        };

                        result_id = parser->register_item(std::move(result_item));

                        break;
                    }

                    case scripting::item::types::rgb_8_bit_image:
                    {
                        detail::parser::item result_item
                        {
                            "mean",
                            {
                                scripting::item(scripting::item::types::rgb_8_bit_image, image_id),
                                scripting::item(scripting::item::types::signed_integer, static_cast<std::int32_t>(width)),
                                scripting::item(scripting::item::types::signed_integer, static_cast<std::int32_t>(height)),
                                scripting::item(scripting::item::types::characters, border_mode)
                            }
                        };

                        result_id = parser->register_item(std::move(result_item));

                        break;
                    }

                    default:
                    {
                        // to make the compiler happy ; other input types are not allowed and should be handled above
                        break;
                    }
                }

                if (result_id != 0)
                {
                    parser->register_link(image_id, result_id);
                }

                return result_id;
            };

        parser->register_specification(name(), std::move(fct));
    }
}

void mean::on_compile(std::uint32_t item_id, std::shared_ptr<detail::compiler> compiler) const
{
    auto handler =
        detail::handler(
            [result_id = item_id, item = compiler->get_item(item_id)](std::shared_ptr<processing_context> context)
            {
                return ::detail::mean(context, result_id, std::move(item));
            });

    compiler->register_handler(item_id, name(), std::move(handler));
}

} // namespace cvpg::imageproc::scripting::algorithms


// Boost.Units - A C++ library for zero-overhead dimensional analysis and
// unit/quantity manipulation and conversion
//
// Copyright (C) 2003-2008 Matthias Christian Schabel
// Copyright (C) 2008 Steven Watanabe
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_UNITS_CODATA_TYPEDEFS_HPP
#define BOOST_UNITS_CODATA_TYPEDEFS_HPP

#include <boost/units/operators.hpp>
#include <boost/units/systems/si/amount.hpp>
#include <boost/units/systems/si/area.hpp>
#include <boost/units/systems/si/capacitance.hpp>
#include <boost/units/systems/si/electric_charge.hpp>
#include <boost/units/systems/si/current.hpp>
#include <boost/units/systems/si/electric_potential.hpp>
#include <boost/units/systems/si/energy.hpp>
#include <boost/units/systems/si/force.hpp>
#include <boost/units/systems/si/frequency.hpp>
#include <boost/units/systems/si/magnetic_flux_density.hpp>
#include <boost/units/systems/si/mass.hpp>
#include <boost/units/systems/si/length.hpp>
#include <boost/units/systems/si/power.hpp>
#include <boost/units/systems/si/solid_angle.hpp>
#include <boost/units/systems/si/temperature.hpp>
#include <boost/units/systems/si/time.hpp>
#include <boost/units/systems/si/volume.hpp>

namespace boost {

namespace units {

namespace si {

namespace constants {

namespace codata {

typedef divide_typeof_helper<frequency,electric_potential>::type frequency_over_electric_potential;
typedef divide_typeof_helper<electric_charge,mass>::type electric_charge_over_mass;
typedef divide_typeof_helper<mass,amount>::type mass_over_amount;
typedef divide_typeof_helper<energy,magnetic_flux_density>::type energy_over_magnetic_flux_density;
typedef divide_typeof_helper<frequency,magnetic_flux_density>::type frequency_over_magnetic_flux_density;
typedef divide_typeof_helper<current,energy>::type current_over_energy;
typedef divide_typeof_helper<dimensionless,amount>::type inverse_amount;
typedef divide_typeof_helper<energy,temperature>::type energy_over_temperature;
typedef divide_typeof_helper<energy_over_temperature,amount>::type energy_over_temperature_amount;
typedef divide_typeof_helper<
            divide_typeof_helper<power,area>::type,
            power_typeof_helper<temperature,static_rational<4> >::type
        >::type power_over_area_temperature_4;
typedef multiply_typeof_helper<power,area>::type power_area;
typedef divide_typeof_helper<power_area,solid_angle>::type power_area_over_solid_angle;
typedef multiply_typeof_helper<length,temperature>::type length_temperature;
typedef divide_typeof_helper<frequency,temperature>::type frequency_over_temperature;
typedef divide_typeof_helper<divide_typeof_helper<force,current>::type,current>::type force_over_current_squared;
typedef divide_typeof_helper<capacitance,length>::type capacitance_over_length;
typedef divide_typeof_helper<
            divide_typeof_helper<divide_typeof_helper<volume,mass>::type,time>::type,
            time
        >::type volume_over_mass_time_squared;
typedef multiply_typeof_helper<energy,time>::type energy_time;
typedef divide_typeof_helper<electric_charge,amount>::type electric_charge_over_amount;

} // namespace codata

} // namespace constants

} // namespace si

} // namespace units

} // namespace boost

#endif