/*********************************************************************
 * BSD 3-Clause License
 *
 * Copyright (c) 2018, Rawashdeh Research Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. Neither the name of the copyright holder 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
 * HOLDER 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: Mohamed Aladem

#include "lvt_local_map.h"
#include "lvt_image_features_handler.h"
#include "lvt_image_features_struct.h"
#include "lvt_visualization.h"
#include "lvt_logging_utils.h"
#include <cmath>
#include <Eigen/SVD>

#ifdef LVT_ENABLE_VISUALIZATION

#define VIS_BEGIN_ADDING_MATCHES()               \
    if (m_vo_params.enable_visualization)        \
    {                                            \
        m_visualization->begin_adding_matches(); \
    }

#define VIS_ADD_MATCH(_mp, _ip)               \
    if (m_vo_params.enable_visualization)     \
    {                                         \
        m_visualization->add_match(_mp, _ip); \
    }

#define VIS_FINISH_ADDING_MATCHES()               \
    if (m_vo_params.enable_visualization)         \
    {                                             \
        m_visualization->finish_adding_matches(); \
    }

#else
#define VIS_BEGIN_ADDING_MATCHES() ((void)0)
#define VIS_ADD_MATCH(_mp, _ip) ((void)0)
#define VIS_FINISH_ADDING_MATCHES() ((void)0)
#endif //LVT_ENABLE_VISUALIZATION

static float min_x, max_x, min_y, max_y;

static inline bool is_point_visible(const lvt_vector3 &pt, const lvt_matrix34 &w2c, const lvt_parameters &params,
                                    lvt_vector2 &out_projected_pt)
{
    lvt_vector4 pt_h;
    pt_h << pt, 1.0;
    lvt_vector3 pt_cam = w2c * pt_h;
    if (pt_cam.z() < params.near_plane_distance || pt_cam.z() > params.far_plane_distance)
    {
        return false;
    }
    const double inv_z = 1.0 / pt_cam.z();
    const double u = params.fx * pt_cam.x() * inv_z + params.cx;
    const double v = params.fy * pt_cam.y() * inv_z + params.cy;
    if (u < min_x || u > max_x ||
        v < min_y || v > max_y)
    {
        return false;
    }
    out_projected_pt << u, v;
    return true;
}

lvt_local_map::lvt_local_map(const lvt_parameters &vo_params,
                             lvt_image_features_handler *features_handler, lvt_visualization *vis) : m_vo_params(vo_params), m_features_handler(features_handler), m_visualization(vis)
{
    // compute image bounds
    if (fabs(m_vo_params.k1) < 1e-5)
    {
        min_x = 0.0;
        max_x = m_vo_params.img_width;
        min_y = 0.0;
        max_y = m_vo_params.img_height;
    }
    else
    {
        cv::Mat kps_mat(4, 2, CV_32F);
        kps_mat.at<float>(0, 0) = 0.0;
        kps_mat.at<float>(0, 1) = 0.0;
        kps_mat.at<float>(1, 0) = m_vo_params.img_width;
        kps_mat.at<float>(1, 1) = 0.0;
        kps_mat.at<float>(2, 0) = 0.0;
        kps_mat.at<float>(2, 1) = m_vo_params.img_height;
        kps_mat.at<float>(3, 0) = m_vo_params.img_width;
        kps_mat.at<float>(3, 1) = m_vo_params.img_height;
        cv::Matx33f intrinsics_mtrx(m_vo_params.fx, 0.0, m_vo_params.cx,
                                    0.0, m_vo_params.fy, m_vo_params.cy,
                                    0.0, 0.0, 1.0);
        std::vector<float> dist;
        dist.push_back(m_vo_params.k1);
        dist.push_back(m_vo_params.k2);
        dist.push_back(m_vo_params.p1);
        dist.push_back(m_vo_params.p2);
        dist.push_back(m_vo_params.k3);
        kps_mat = kps_mat.reshape(2);
        cv::undistortPoints(kps_mat, kps_mat, cv::Mat(intrinsics_mtrx), cv::Mat(dist), cv::Mat(), intrinsics_mtrx);
        kps_mat = kps_mat.reshape(1);
        min_x = std::min(kps_mat.at<float>(0, 0), kps_mat.at<float>(2, 0));
        max_x = std::max(kps_mat.at<float>(1, 0), kps_mat.at<float>(3, 0));
        min_y = std::min(kps_mat.at<float>(0, 1), kps_mat.at<float>(1, 1));
        max_y = std::max(kps_mat.at<float>(2, 1), kps_mat.at<float>(3, 1));
    }
}

lvt_local_map::~lvt_local_map()
{
    reset();
}

void lvt_local_map::reset()
{
    m_map_points.clear();
    m_staged_points.clear();
}

int lvt_local_map::find_matches(const lvt_pose &cam_pose, lvt_image_features_struct *left_struct,
                                lvt_vector3_array *out_map_points, std::vector<int> *out_matches_left)
{
    const lvt_matrix34 cml = lvt_pose_utils::compute_world_to_camera_transform(cam_pose);
    int matches_count = 0;
    VIS_BEGIN_ADDING_MATCHES();
    std::vector<int> matches(m_map_points.size(), -2); // mark each map point with its matching index from left_struct, -2 if not visible
    lvt_vector2_array projections(m_map_points.size());
#ifdef LVT_ENABLE_MEASURMENT_RECORD
    std::vector<float> d1s(m_map_points.size());
    std::vector<float> d2s(m_map_points.size());
#endif //LVT_ENABLE_MEASURMENT_RECORD

    for (int i = 0; i < m_map_points.size(); i++)
    {
        lvt_vector2 proj_pt_left;
        if (!is_point_visible(m_map_points[i].m_position, cml, m_vo_params, proj_pt_left))
        {
            m_map_points[i].m_counter += 1;
            matches[i] = -2;
            continue;
        }
        projections[i] = proj_pt_left;
        float d1, d2;
        int match_idx_left = left_struct->find_match_index(proj_pt_left, m_map_points[i].m_descriptor, &d1, &d2);
        matches[i] = match_idx_left;
        if (match_idx_left != -1)
        {
            matches_count++;
            left_struct->mark_as_matched(match_idx_left, true);
#ifdef LVT_ENABLE_MEASURMENT_RECORD
            d1s[i] = d1;
            d2s[i] = d2;
#endif //LVT_ENABLE_MEASURMENT_RECORD
        }
    }

    if (matches_count < LVT_N_MATCHES_TH)
    {
        matches_count = 0;
        left_struct->reset_matched_marks();
        int original_tracking_radius = left_struct->get_tracking_radius();
        left_struct->set_tracking_radius(2 * original_tracking_radius);
        for (int i = 0; i < m_map_points.size(); i++)
        {
            if (matches[i] == -2)
            {
                continue;
            }
            float d1, d2;
            int match_idx_left = left_struct->find_match_index(projections[i], m_map_points[i].m_descriptor, &d1, &d2);
            matches[i] = match_idx_left;
            if (match_idx_left != -1)
            {
                matches_count++;
                left_struct->mark_as_matched(match_idx_left, true);
#ifdef LVT_ENABLE_MEASURMENT_RECORD
                d1s[i] = d1;
                d2s[i] = d2;
#endif //LVT_ENABLE_MEASURMENT_RECORD
            }
        }
        left_struct->set_tracking_radius(original_tracking_radius);
    }

    for (int i = 0; i < matches.size(); i++)
    {
        m_map_points[i].m_match_idx = matches[i];
        if (matches[i] == -2)
        {
            continue;
        }

        if (matches[i] == -1)
        {
            m_map_points[i].m_counter += 1;
            continue;
        }

        m_map_points[i].m_age += 1;
        out_map_points->push_back(m_map_points[i].m_position);
        out_matches_left->push_back(matches[i]);
        LVT_RECORD("img feature x", left_struct->get_keypoint(matches[i]).pt.x);
        LVT_RECORD("img feature y", left_struct->get_keypoint(matches[i]).pt.y);
        LVT_RECORD("age", m_map_points[i].m_age);
        LVT_RECORD("closest descriptor distance", d1s[i]);
        LVT_RECORD("second descriptor distance", d2s[i]);
        VIS_ADD_MATCH(i, matches[i]);
    }

    VIS_FINISH_ADDING_MATCHES();
    LVT_RECORD("tracked map points", matches_count);
    return matches_count;
}

void lvt_local_map::triangulate_rgbd(const lvt_pose &cam_pose, lvt_image_features_struct *img_struct, lvt_map_point_array *out_points)
{
    const float inv_fx = 1.0f / m_vo_params.fx;
    const float inv_fy = 1.0f / m_vo_params.fy;
    lvt_matrix34 cam_to_world_mtrx;
    cam_to_world_mtrx << cam_pose.get_orientation_matrix(), cam_pose.get_position();
    for (int i = 0, count = img_struct->get_features_count(); i < count; i++)
    {
        const cv::Point2f pt = img_struct->get_keypoint(i).pt;
        const float u = pt.x;
        const float v = pt.y;
        const float z = img_struct->get_keypoint_depth(i);
        const float x = (u - m_vo_params.cx) * z * inv_fx;
        const float y = (v - m_vo_params.cy) * z * inv_fy;
        lvt_vector4 pt_h;
        pt_h << x, y, z, 1.0;
        lvt_vector3 pt_w = cam_to_world_mtrx * pt_h;

        lvt_map_point mp;
        mp.m_position = pt_w;
        mp.m_descriptor = img_struct->get_descriptor(i);
        mp.m_counter = 0;
        mp.m_age = 0;
        out_points->push_back(mp);
    }
}

void lvt_local_map::triangulate(const lvt_pose &cam_pose, lvt_image_features_struct *left_struct,
                                lvt_image_features_struct *right_struct, lvt_map_point_array *out_points)
{
    std::vector<cv::DMatch> matches;
    m_features_handler->row_match(left_struct, right_struct, &matches);
    if (matches.empty())
    {
        LVT_ASSERT(0 && "No row matches for triangulation found.");
        return;
    }

    const lvt_pose cam_pose_right = lvt_pose_utils::compute_right_camera_pose(cam_pose, m_vo_params.baseline);
    const lvt_matrix34 cml = lvt_pose_utils::compute_world_to_camera_transform(cam_pose);
    const lvt_matrix34 cmr = lvt_pose_utils::compute_world_to_camera_transform(cam_pose_right);
    const double cx = m_vo_params.cx, cy = m_vo_params.cy; // , f_inv = 1.0 / m_vo_params.focal_length;
    const double inv_fx = 1.0 / m_vo_params.fx, inv_fy = 1.0 / m_vo_params.fy;
    out_points->reserve(matches.size());

    for (size_t i = 0, count = matches.size(); i < count; i++)
    {
        const cv::Point2f u1 = left_struct->get_keypoint(matches[i].queryIdx).pt;
        const cv::Point2f u2 = right_struct->get_keypoint(matches[i].trainIdx).pt;
        double u1_x = (u1.x - cx) * inv_fx;
        double u1_y = (u1.y - cy) * inv_fy;
        double u2_x = (u2.x - cx) * inv_fx;
        double u2_y = (u2.y - cy) * inv_fy;

        // Linear-LS triangulation
        Eigen::Matrix<double, 4, 4> A;
        A << u1_x * cml.row(2) - cml.row(0),
            u1_y * cml.row(2) - cml.row(1),
            u2_x * cmr.row(2) - cmr.row(0),
            u2_y * cmr.row(2) - cmr.row(1);

        lvt_vector3 world_pt = A.leftCols<3>().jacobiSvd(Eigen::ComputeFullU | Eigen::ComputeFullV).solve(-A.col(3));
        assert(std::isfinite(world_pt.x()) && std::isfinite(world_pt.y()) && std::isfinite(world_pt.z()));

        // check if the point is in viewable region by camera
        lvt_vector2 proj_pt_l, proj_pt_r;
        if (!is_point_visible(world_pt, cml, m_vo_params, proj_pt_l) || !is_point_visible(world_pt, cmr, m_vo_params, proj_pt_r))
        {
            continue;
        }

        // check reprojection error
        {
            double err_x = proj_pt_l.x() - u1.x;
            double err_y = proj_pt_l.y() - u1.y;
            if ((err_x * err_x + err_y * err_y) > LVT_REPROJECTION_TH2)
            {
                continue;
            }
        }

        {
            double err_x = proj_pt_r.x() - u2.x;
            double err_y = proj_pt_r.y() - u2.y;
            if ((err_x * err_x + err_y * err_y) > LVT_REPROJECTION_TH2)
            {
                continue;
            }
        }

        // create map point
        lvt_map_point mp;
        mp.m_position = world_pt;
        mp.m_descriptor = left_struct->get_descriptor(matches[i].queryIdx);
        mp.m_counter = 0;
        mp.m_age = 0;
        out_points->push_back(mp);
    }
}

void lvt_local_map::update_with_new_triangulation(const lvt_pose &cam_pose,
                                                  lvt_image_features_struct *left_struct, lvt_image_features_struct *right_struct, bool dont_stage)
{
    // Triangulate new map points from features that were not matched/tracked
    lvt_map_point_array new_triangulations;
    if (left_struct->is_depth_associated())
    {
        triangulate_rgbd(cam_pose, left_struct, &new_triangulations);
    }
    else
    {
        triangulate(cam_pose, left_struct, right_struct, &new_triangulations);
    }
    LVT_ASSERT(!new_triangulations.empty() && "Nothing was triangulated");
    if (dont_stage || m_vo_params.staged_threshold == 0 || get_map_size() < LVT_N_MAP_POINTS)
    {
        m_map_points.insert(m_map_points.end(), new_triangulations.begin(), new_triangulations.end());
    }
    else
    {
        m_staged_points.insert(m_staged_points.end(), new_triangulations.begin(), new_triangulations.end());
    }
}

void lvt_local_map::update_staged_map_points(const lvt_pose &cam_pose, lvt_image_features_struct *left_struct)
{
    const lvt_matrix34 cml = lvt_pose_utils::compute_world_to_camera_transform(cam_pose);
    int n_erased_points = 0;
    int n_upgraded_points = 0;
    std::set<lvt_map_point *> points_to_be_deleted;
    for (int i = 0, count = m_staged_points.size(); i < count; i++)
    {
        lvt_map_point *mp = &(m_staged_points[i]);
        const lvt_vector3 world_pt = mp->m_position;
        lvt_vector2 proj_pt_left;
        float d1, d2;
        int match_idx_left = -1;
        if (!is_point_visible(world_pt, cml, m_vo_params, proj_pt_left) ||
            (match_idx_left = left_struct->find_match_index(proj_pt_left, mp->m_descriptor, &d1, &d2)) == -1)
        {
            points_to_be_deleted.insert(mp);
            n_erased_points++;
            continue;
        }
        left_struct->mark_as_matched(match_idx_left, true);
        mp->m_counter += 1;
        if (mp->m_counter == m_vo_params.staged_threshold || get_map_size() < LVT_N_MAP_POINTS)
        {
            m_map_points.push_back(m_staged_points[i]);
            points_to_be_deleted.insert(mp);
            n_upgraded_points++;
        }
    }

    m_staged_points.erase(std::remove_if(m_staged_points.begin(), m_staged_points.end(),
                                         [&points_to_be_deleted](lvt_map_point &pt) { return points_to_be_deleted.count(&pt) != 0; }),
                          m_staged_points.end());

    LVT_LOG("Staged map points updated: " + std::to_string(n_erased_points) + " erased, " +
            std::to_string(n_upgraded_points) + " upgraded to map points, " + std::to_string(m_staged_points.size()) + " remain.");
}

void lvt_local_map::clean_untracked_points(lvt_image_features_struct *left_struct)
{
    const int th = m_vo_params.untracked_threshold;
    lvt_map_point_array cleaned_map_points;
    cleaned_map_points.reserve(m_map_points.size());
    for (int i = 0; i < m_map_points.size(); i++)
    {
        if (m_map_points[i].m_counter >= th)
        {
            if (m_map_points[i].m_match_idx >= 0)
            {
                left_struct->mark_as_matched(m_map_points[i].m_match_idx, false);
            }
        }
        else
        {
            cleaned_map_points.push_back(m_map_points[i]);
        }
    }
    cleaned_map_points.swap(m_map_points);
}


// copyright (c) 2011-2015 the moorecoin core developers
// distributed under the mit software license, see the accompanying
// file copying or http://www.opensource.org/licenses/mit-license.php.

#include "policy/fees.h"
#include "txmempool.h"
#include "uint256.h"
#include "util.h"

#include "test/test_moorecoin.h"

#include <boost/test/unit_test.hpp>

boost_fixture_test_suite(policyestimator_tests, basictestingsetup)

boost_auto_test_case(blockpolicyestimates)
{
    ctxmempool mpool(cfeerate(1000));
    camount basefee(2000);
    double basepri = 1e6;
    camount deltafee(100);
    double deltapri=5e5;
    std::vector<camount> feev[2];
    std::vector<double> priv[2];

    // populate vectors of increasing fees or priorities
    for (int j = 0; j < 10; j++) {
        //v[0] is for fee transactions
        feev[0].push_back(basefee * (j+1));
        priv[0].push_back(0);
        //v[1] is for priority transactions
        feev[1].push_back(camount(0));
        priv[1].push_back(basepri * pow(10, j+1));
    }

    // store the hashes of transactions that have been
    // added to the mempool by their associate fee/pri
    // txhashes[j] is populated with transactions either of
    // fee = basefee * (j+1)  or  pri = 10^6 * 10^(j+1)
    std::vector<uint256> txhashes[10];

    // create a transaction template
    cscript garbage;
    for (unsigned int i = 0; i < 128; i++)
        garbage.push_back('x');
    cmutabletransaction tx;
    std::list<ctransaction> dummyconflicted;
    tx.vin.resize(1);
    tx.vin[0].scriptsig = garbage;
    tx.vout.resize(1);
    tx.vout[0].nvalue=0ll;
    cfeerate baserate(basefee, ::getserializesize(tx, ser_network, protocol_version));

    // create a fake block
    std::vector<ctransaction> block;
    int blocknum = 0;

    // loop through 200 blocks
    // at a decay .998 and 4 fee transactions per block
    // this makes the tx count about 1.33 per bucket, above the 1 threshold
    while (blocknum < 200) {
        for (int j = 0; j < 10; j++) { // for each fee/pri multiple
            for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx
                tx.vin[0].prevout.n = 10000*blocknum+100*j+k; // make transaction unique
                uint256 hash = tx.gethash();
                mpool.addunchecked(hash, ctxmempoolentry(tx, feev[k/4][j], gettime(), priv[k/4][j], blocknum, mpool.hasnoinputsof(tx)));
                txhashes[j].push_back(hash);
            }
        }
        //create blocks where higher fee/pri txs are included more often
        for (int h = 0; h <= blocknum%10; h++) {
            // 10/10 blocks add highest fee/pri transactions
            // 9/10 blocks add 2nd highest and so on until ...
            // 1/10 blocks add lowest fee/pri transactions
            while (txhashes[9-h].size()) {
                ctransaction btx;
                if (mpool.lookup(txhashes[9-h].back(), btx))
                    block.push_back(btx);
                txhashes[9-h].pop_back();
            }
        }
        mpool.removeforblock(block, ++blocknum, dummyconflicted);
        block.clear();
        if (blocknum == 30) {
            // at this point we should need to combine 5 buckets to get enough data points
            // so estimatefee(1) should fail and estimatefee(2) should return somewhere around
            // 8*baserate
            boost_check(mpool.estimatefee(1) == cfeerate(0));
            boost_check(mpool.estimatefee(2).getfeeperk() < 8*baserate.getfeeperk() + deltafee);
            boost_check(mpool.estimatefee(2).getfeeperk() > 8*baserate.getfeeperk() - deltafee);
        }
    }

    std::vector<camount> origfeeest;
    std::vector<double> origpriest;
    // highest feerate is 10*baserate and gets in all blocks,
    // second highest feerate is 9*baserate and gets in 9/10 blocks = 90%,
    // third highest feerate is 8*base rate, and gets in 8/10 blocks = 80%,
    // so estimatefee(1) should return 9*baserate.
    // third highest feerate has 90% chance of being included by 2 blocks,
    // so estimatefee(2) should return 8*baserate etc...
    for (int i = 1; i < 10;i++) {
        origfeeest.push_back(mpool.estimatefee(i).getfeeperk());
        origpriest.push_back(mpool.estimatepriority(i));
        if (i > 1) { // fee estimates should be monotonically decreasing
            boost_check(origfeeest[i-1] <= origfeeest[i-2]);
            boost_check(origpriest[i-1] <= origpriest[i-2]);
        }
        boost_check(origfeeest[i-1] < (10-i)*baserate.getfeeperk() + deltafee);
        boost_check(origfeeest[i-1] > (10-i)*baserate.getfeeperk() - deltafee);
        boost_check(origpriest[i-1] < pow(10,10-i) * basepri + deltapri);
        boost_check(origpriest[i-1] > pow(10,10-i) * basepri - deltapri);
    }

    // mine 50 more blocks with no transactions happening, estimates shouldn't change
    // we haven't decayed the moving average enough so we still have enough data points in every bucket
    while (blocknum < 250)
        mpool.removeforblock(block, ++blocknum, dummyconflicted);

    for (int i = 1; i < 10;i++) {
        boost_check(mpool.estimatefee(i).getfeeperk() < origfeeest[i-1] + deltafee);
        boost_check(mpool.estimatefee(i).getfeeperk() > origfeeest[i-1] - deltafee);
        boost_check(mpool.estimatepriority(i) < origpriest[i-1] + deltapri);
        boost_check(mpool.estimatepriority(i) > origpriest[i-1] - deltapri);
    }

    // mine 15 more blocks with lots of transactions happening and not getting mined
    // estimates should go up
    while (blocknum < 265) {
        for (int j = 0; j < 10; j++) { // for each fee/pri multiple
            for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx
                tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
                uint256 hash = tx.gethash();
                mpool.addunchecked(hash, ctxmempoolentry(tx, feev[k/4][j], gettime(), priv[k/4][j], blocknum, mpool.hasnoinputsof(tx)));
                txhashes[j].push_back(hash);
            }
        }
        mpool.removeforblock(block, ++blocknum, dummyconflicted);
    }

    for (int i = 1; i < 10;i++) {
        boost_check(mpool.estimatefee(i).getfeeperk() > origfeeest[i-1] - deltafee);
        boost_check(mpool.estimatepriority(i) > origpriest[i-1] - deltapri);
    }

    // mine all those transactions
    // estimates should still not be below original
    for (int j = 0; j < 10; j++) {
        while(txhashes[j].size()) {
            ctransaction btx;
            if (mpool.lookup(txhashes[j].back(), btx))
                block.push_back(btx);
            txhashes[j].pop_back();
        }
    }
    mpool.removeforblock(block, 265, dummyconflicted);
    block.clear();
    for (int i = 1; i < 10;i++) {
        boost_check(mpool.estimatefee(i).getfeeperk() > origfeeest[i-1] - deltafee);
        boost_check(mpool.estimatepriority(i) > origpriest[i-1] - deltapri);
    }

    // mine 100 more blocks where everything is mined every block
    // estimates should be below original estimates (not possible for last estimate)
    while (blocknum < 365) {
        for (int j = 0; j < 10; j++) { // for each fee/pri multiple
            for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx
                tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
                uint256 hash = tx.gethash();
                mpool.addunchecked(hash, ctxmempoolentry(tx, feev[k/4][j], gettime(), priv[k/4][j], blocknum, mpool.hasnoinputsof(tx)));
                ctransaction btx;
                if (mpool.lookup(hash, btx))
                    block.push_back(btx);
            }
        }
        mpool.removeforblock(block, ++blocknum, dummyconflicted);
        block.clear();
    }
    for (int i = 1; i < 9; i++) {
        boost_check(mpool.estimatefee(i).getfeeperk() < origfeeest[i-1] - deltafee);
        boost_check(mpool.estimatepriority(i) < origpriest[i-1] - deltapri);
    }
}

boost_auto_test_suite_end()


//  (C) Copyright Gennadiy Rozental 2001.
//  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)

//  See http://www.boost.org/libs/test for the library home page.
//
//! @file
//! @brief Floating point comparison tolerance manipulators
//!
//! This file defines several manipulators for floating point comparison. These
//! manipulators are intended to be used with BOOST_TEST.
// ***************************************************************************

#ifndef BOOST_TEST_TOOLS_DETAIL_TOLERANCE_MANIP_HPP_012705GER
#define BOOST_TEST_TOOLS_DETAIL_TOLERANCE_MANIP_HPP_012705GER

// Boost Test
#include <boost/test/tools/detail/fwd.hpp>
#include <boost/test/tools/detail/indirections.hpp>

#include <boost/test/tools/floating_point_comparison.hpp>
#include <boost/test/tools/fpc_tolerance.hpp>

#include <boost/test/detail/suppress_warnings.hpp>

//____________________________________________________________________________//

namespace boost {
namespace test_tools {
namespace tt_detail {

// ************************************************************************** //
// **************           fpc tolerance manipulator          ************** //
// ************************************************************************** //

template <typename FPT> struct tolerance_manip {
  explicit tolerance_manip(FPT const &tol) : m_value(tol) {}

  FPT m_value;
};

//____________________________________________________________________________//

struct tolerance_manip_delay {};

template <typename FPT>
inline tolerance_manip<FPT> operator%(FPT v, tolerance_manip_delay const &) {
  BOOST_STATIC_ASSERT_MSG(
      (fpc::tolerance_based<FPT>::value),
      "tolerance should be specified using a floating points type");

  return tolerance_manip<FPT>(FPT(v / 100));
}

//____________________________________________________________________________//

template <typename E, typename FPT>
inline assertion_result operator<<(assertion_evaluate_t<E> const &ae,
                                   tolerance_manip<FPT> const &tol) {
  local_fpc_tolerance<FPT> lt(tol.m_value);

  return ae.m_e.evaluate();
}

//____________________________________________________________________________//

template <typename FPT>
inline int operator<<(unit_test::lazy_ostream const &,
                      tolerance_manip<FPT> const &) {
  return 0;
}

//____________________________________________________________________________//

template <typename FPT>
inline check_type operator<<(assertion_type const & /*at*/,
                             tolerance_manip<FPT> const &) {
  return CHECK_BUILT_ASSERTION;
}

//____________________________________________________________________________//

} // namespace tt_detail

/*! Tolerance manipulator
 *
 * These functions return a manipulator that can be used in conjunction with
 * BOOST_TEST in order to specify the tolerance with which floating point
 * comparisons are made.
 */
template <typename FPT>
inline tt_detail::tolerance_manip<FPT> tolerance(FPT v) {
  BOOST_STATIC_ASSERT_MSG((fpc::tolerance_based<FPT>::value),
                          "tolerance only for floating points");

  return tt_detail::tolerance_manip<FPT>(v);
}

//____________________________________________________________________________//

//! @overload tolerance( FPT v )
template <typename FPT>
inline tt_detail::tolerance_manip<FPT>
tolerance(fpc::percent_tolerance_t<FPT> v) {
  BOOST_STATIC_ASSERT_MSG((fpc::tolerance_based<FPT>::value),
                          "tolerance only for floating points");

  return tt_detail::tolerance_manip<FPT>(static_cast<FPT>(v.m_value / 100));
}

//____________________________________________________________________________//

//! @overload tolerance( FPT v )
inline tt_detail::tolerance_manip_delay tolerance() {
  return tt_detail::tolerance_manip_delay();
}

//____________________________________________________________________________//

} // namespace test_tools
} // namespace boost

#include <boost/test/detail/enable_warnings.hpp>

#endif // BOOST_TEST_TOOLS_DETAIL_TOLERANCE_MANIP_HPP_012705GER


//

// g++ -std=c++11 -o findTiles findTiles.cpp -L/usr/local/lib -lcpprest -lboost_system -lboost_chrono -lboost_thread -lboost_random -lboost_regex -lssl -lcrypto

#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;

#include <boost/filesystem.hpp>
using namespace boost::filesystem;

#include "../json/metainfo.h"

// global const
float OFFX=72022.286767;
float OFFY=15966.638635;
float OFFZ=32203.173689;

float OX=68097.320;
float OY=13754.192;
float OZ=27557.100;

float SX=15984.70639534884;
float SY=16031.19140625;
float SZ=64066.66666666666;

float VX=0.249761;
float VY=0.250487;
float VZ=1.00104;

float DX=688;
float DY=512;
float DZ=228;

// delete 1d pointer
template <class T>
void del1dp(T *&p)
{
    if(p) {delete []p; p=NULL;}
    return;
}

// new 1d pointer
template<class T, class Tidx>
void new1dp(T *&p, Tidx n)
{
    //
    del1dp<T>(p);

    //
    try
    {
        p = new T [n];
    }
    catch(...)
    {
        cout<<"Attempt to allocate memory failed!"<<endl;
        del1dp<T>(p);
        return;
    }
    return;
}

//
class Point
{
public:
    float x, y, z;
};

//
typedef vector<Point> PointCloud;

//
std::string trim(std::string const& source, char const* delims = " \t\r\n")
{
    std::string result(source);
    std::string::size_type index = result.find_last_not_of(delims);
    if(index != std::string::npos)
    {
        result.erase(++index);
    }

    index = result.find_first_not_of(delims);
    if(index != std::string::npos)
    {
        result.erase(0, index);
    }
    else
    {
        result.erase();
    }
    return result;
}

bool readSWC(const char* fn, PointCloud &pc)
{
    string filename = const_cast<char *>(fn);

    std::ifstream in;

    in.open (fn, std::ios::in | std::ios::binary);
    if ( in.fail() )
    {
        in.close();
        std::cerr<< "The file could not be opened for read access "<< std::endl << "Filename: " << fn <<endl;
    }

    std::ostringstream InData;

    //
    std::filebuf *pbuf;
    pbuf = in.rdbuf();

    // get file size using buffer's members
    int size = pbuf->pubseekoff (0, std::ios::end, std::ios::in);
    pbuf->pubseekpos (0, std::ios::in);

    // allocate memory to contain file data
    char *buffer = NULL;
    new1dp<char, size_t>(buffer, size + 1);

    // get file data
    pbuf->sgetn (buffer, size);
    buffer[size] = '\0';
    InData << buffer;

    del1dp<char>(buffer);
    std::string data = InData.str();
    in.close();

    // Read line by line
    std::string::size_type position = 0;

    // check for line end convention
    std::string line_end("\n");

    if ( data.find('\n') == std::string::npos )
    {
        if ( data.find('\r') == std::string::npos )
        {
            std::cerr << "No line ending character found, not a valid SWC file"<<endl;
        }
        line_end = "\r";
    }

    long count;
    while ( position != std::string::npos && position < data.size() )
    {
        // Find the next string
        std::string::size_type end = data.find (line_end, position);

        if( end == std::string::npos )
        {
            std::cerr << "Couldn't find end of line in " << data <<endl;
        }

        std::string line = trim ( data.substr (position, end - position) );
        position = end + 1;

        if ( line.length() == 0 )
        {
            continue;
        }
        if ( line[0] == '#' || std::string::npos == line.find_first_not_of (" \t") )
        {
            // Skip lines beginning with #, or blank lines
            continue;
        }

        // Push back
        std::istringstream parse (line);

        Point p;

        float tmp;

        parse >> tmp; // n
        parse >> tmp; // type
        parse >> p.x; // x
        parse >> p.y; // y
        parse >> p.z; // z

        pc.push_back(p);
    }

    //
    return true;
}

bool insideBB(string octreepath, float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
{
    //
    std::vector<int> vect;

    std::stringstream ss(octreepath);

    int i;

    while (ss >> i)
    {
        vect.push_back(i);

        if (ss.peek() == '/')
            ss.ignore();
    }

    //
    int ioff,joff,koff;

    for (i=0; i<vect.size(); i++)
    {
        if(i==0)
        {
            switch(vect.at(i))
            {
                case 1:
                    ioff = 0;
                    joff = 0;
                    koff = 0;
                    break;
                case 2:
                    ioff = 1;
                    joff = 0;
                    koff = 0;
                    break;
                case 3:
                    ioff = 0;
                    joff = 1;
                    koff = 0;
                    break;
                case 4:
                    ioff = 1;
                    joff = 1;
                    koff = 0;
                    break;
                case 5:
                    ioff = 0;
                    joff = 0;
                    koff = 1;
                    break;
                case 6:
                    ioff = 1;
                    joff = 0;
                    koff = 1;
                    break;
                case 7:
                    ioff = 0;
                    joff = 1;
                    koff = 1;
                    break;
                case 8:
                    ioff = 1;
                    joff = 1;
                    koff = 1;
                    break;
                default:
                    cout<<"Invalid octree node!"<<endl;
                    break;
            }
        }
        else
        {
            switch(vect.at(i))
            {
                case 1:
                    ioff *= 2;
                    joff *= 2;
                    koff *= 2;
                    break;
                case 2:
                    ioff = 2*ioff + 1;
                    joff *= 2;
                    koff *= 2;
                    break;
                case 3:
                    ioff *= 2;
                    joff = 2*joff + 1;
                    koff *= 2;
                    break;
                case 4:
                    ioff = 2*ioff + 1;
                    joff = 2*joff + 1;
                    koff *= 2;
                    break;
                case 5:
                    ioff *= 2;
                    joff *= 2;
                    koff = 2*koff + 1;
                    break;
                case 6:
                    ioff = 2*ioff + 1;
                    joff *= 2;
                    koff = 2*koff + 1;
                    break;
                case 7:
                    ioff *= 2;
                    joff = 2*joff + 1;
                    koff = 2*koff + 1;
                    break;
                case 8:
                    ioff = 2*ioff + 1;
                    joff = 2*joff + 1;
                    koff = 2*koff + 1;
                    break;
                default:
                    cout<<"Invalid octree node!"<<endl;
                    break;
            }
        }
    }

    //
    float bx=ioff*DX*VX;
    float ex=(ioff+1)*DX*VX;

    float by=joff*DY*VY;
    float ey=(joff+1)*DY*VY;

    float bz=koff*DZ*VZ;
    float ez=(koff+1)*DZ*VZ;

    //cout<<"test tile "<<bx<<" "<<ex<<" "<<by<<" "<<ey<<" "<<bz<<" "<<ez<<endl;

    //
    if( (bx < maxX && ex > minX) && (by < maxY && ey > minY) && (bz < maxZ && ez > minZ) )
    {
        return true;
    }else{
        return false;
    }
}

//
void printJSONValue(json::value v, string indent, bool isArray)
{
    string indented=indent;

    //
    if(isArray)
    {
        cout << indent << "[" <<endl;
    }
    else
    {
        cout << indent << "{" <<endl;
    }

    //
    if (!v.is_null())
    {
        indented.append("    ");

        //
        if(isArray)
        {
            for (auto &value : v.as_array())
            {
                if (value.is_object() || value.is_array())
                {
                    printJSONValue(value, indented, value.is_array());
                }
                else
                {
                    //
                    if(value.is_double())
                    {
                        cout.precision(numeric_limits<double>::digits10 + 1);
                        cout << value.as_double();
                    }
                    else if(v.is_integer())
                    {
                        cout << value.as_integer();
                    }
                    else
                    {
                        ucout << value.serialize();
                    }
                    cout << endl;
                }
            }
        }
        else
        {
            for (auto iter = std::begin(v.as_object()); iter != std::end(v.as_object()); ++iter)
            {
                //
                auto key = iter->first;
                auto& value = iter->second;

                if (value.is_object() || value.is_array())
                {
                    ucout << indented << U("\"") << key << U("\"") << U(" : ") << endl;
                    printJSONValue(value, indented, value.is_array());
                }
                else
                {
                    //
                    ucout << indented << U("\"") << key << U("\"") << U(" : ");

                    if(value.is_double())
                    {
                        cout.precision(numeric_limits<double>::digits10 + 1);
                        cout << value.as_double();
                    }
                    else if(v.is_integer())
                    {
                        cout << value.as_integer();
                    }
                    else
                    {
                        ucout << value.serialize();
                    }
                    if(iter+1 != std::end(v.as_object()))
                        cout << U(",") << endl;
                    else
                        cout << endl;
                }
            }
        }

    }

    //
    if(isArray)
    {
        cout << indent << "]" <<endl;
    }
    else
    {
        cout << indent << "}," <<endl;
    }
}

//
int main (int argc, char* argv[])
{
    //
    float offsetX = OFFX - OX;
    float offsetY = OFFY - OY;
    float offsetZ = OFFZ - OZ;

    cout<<"offset ... "<<offsetX<<" "<<offsetY<<" "<<offsetZ<<endl;

    //
    if(argc<3)
    {
        std::cout<<"need at least 2 inputs"<<endl;
        return -1;
    }

    // read neuron file
    const char* swcFile = (const char *) (argv[1]); // swc

    PointCloud pc;

    //
    if(readSWC(swcFile, pc) == false)
    {
        cout<<"fail to read swc"<<endl;
        return -1;
    }

    if(pc.size()<1)
    {
        cout<<"Empty neuron (.swc). \n";
        return -1;
    }

    float minX, minY, minZ, maxX, maxY, maxZ;

    //
    minX = maxX = pc[0].x + offsetX;
    minY = maxY = pc[0].y + offsetY;
    minZ = maxZ = pc[0].z + offsetZ;

    //
    for(int i=1; i<pc.size(); i++)
    {
        pc[i].x = pc[i].x + offsetX;
        pc[i].y = pc[i].y + offsetY;
        pc[i].z = pc[i].z + offsetZ;

        if(pc[i].x < minX)
            minX = pc[i].x;
        if(pc[i].x > maxX)
            maxX = pc[i].x;

        if(pc[i].y < minY)
            minY = pc[i].y;
        if(pc[i].y > maxY)
            maxY = pc[i].y;

        if(pc[i].z < minZ)
            minZ = pc[i].z;
        if(pc[i].z > maxZ)
            maxZ = pc[i].z;
    }

    minX -= DX*VX;
    minY -= DY*VY;
    minZ -= DZ*VZ;
    maxX += DX*VX;
    maxY += DY*VY;
    maxZ += DZ*VZ;

    cout<< "test ... min "<<minX<<" "<<minY<<" "<<minZ<<endl;
    cout<< "test ... max "<<maxX<<" "<<maxY<<" "<<maxZ<<endl;

    // read tiles
    std::ifstream f(argv[2]); // json

    //
    json::value j = json::value::parse(f);
    //
    json::value jout;
    jout[U("name")] = j[U("name")];

    //
    json::value ja = j[U("tile")];
    //
    json::value joutarray;

    //
    int count=0;
    for(auto val : ja.as_array())
    {
        string octreepath = val[U("octreepath")].as_string();

        if(insideBB(octreepath, minX, minY, minZ, maxX, maxY, maxZ))
        {
            joutarray[count] = val;
            count++;
        }
    }

    //
    cout<<"find "<<count<<" tiles"<<endl;

    //
    jout[U("tile")] = joutarray;

    //
    printJSONValue(jout, "", jout.is_array());

    //
    return 0;

}


//
// Copyright (c) 2009 Rutger ter Borg
//
// 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_NUMERIC_BINDINGS_EIGEN_MATRIX_HPP
#define BOOST_NUMERIC_BINDINGS_EIGEN_MATRIX_HPP

#include <boost/mpl/equal.hpp>
#include <boost/numeric/bindings/detail/adaptor.hpp>
#include <boost/numeric/bindings/detail/if_row_major.hpp>
#include <Eigen/Core>

namespace boost {
namespace numeric {
namespace bindings {
namespace detail {

template< int Value >
struct eigen_size_type {
    typedef typename mpl::if_<
        mpl::bool_< Value == Eigen::Dynamic >,
        std::ptrdiff_t,
        mpl::int_<Value>
    >::type type;
};

template< int Value >
struct eigen_data_order {
    typedef typename mpl::if_<
        mpl::bool_< Value & Eigen::RowMajorBit >,
        tag::row_major,
        tag::column_major
    >::type type;
};

template< int Value >
struct eigen_data_order_trans {
    typedef typename mpl::if_<
        mpl::bool_< Value & Eigen::RowMajorBit >,
        tag::column_major,
        tag::row_major
    >::type type;
};

template< typename T, int Rows, int Cols, int Options,
          typename Id, typename Enable >
struct adaptor< Eigen::Matrix< T, Rows, Cols, Options >, Id, Enable > {

    typedef typename copy_const< Id, T >::type value_type;
    typedef typename eigen_size_type< Rows >::type size_type1;
    typedef typename eigen_size_type< Cols >::type size_type2;
    typedef typename eigen_data_order< Options >::type data_order;
    typedef mpl::map<
        mpl::pair< tag::value_type, value_type >,
        mpl::pair< tag::entity, tag::matrix >,
        mpl::pair< tag::size_type<1>, size_type1 >,
        mpl::pair< tag::size_type<2>, size_type2 >,
        mpl::pair< tag::data_structure, tag::linear_array >,
        mpl::pair< tag::data_order, data_order >,
        mpl::pair< tag::stride_type<1>,
            typename if_row_major< data_order, size_type2, tag::contiguous >::type >,
        mpl::pair< tag::stride_type<2>,
            typename if_row_major< data_order, tag::contiguous, size_type1 >::type >
    > property_map;

    static std::ptrdiff_t size1( const Id& id ) {
        return id.rows();
    }

    static std::ptrdiff_t size2( const Id& id ) {
        return id.cols();
    }

    static value_type* begin_value( Id& id ) {
        return id.data();
    }

    static value_type* end_value( Id& id ) {
        return id.data() + id.size();
    }

    static std::ptrdiff_t stride1( const Id& id ) {
        return id.cols();
    }

    static std::ptrdiff_t stride2( const Id& id ) {
        return id.rows();
    }

};

template< typename T, int Rows, int Cols, int Options,
          typename Id, typename Enable >
struct adaptor< Eigen::Transpose<Eigen::Matrix<T, Rows, Cols, Options > >, Id, Enable > {

    typedef typename copy_const< Id, T >::type value_type;
    typedef typename eigen_size_type< Rows >::type size_type1;
    typedef typename eigen_size_type< Cols >::type size_type2;
    typedef typename eigen_data_order_trans< Options >::type data_order;
    typedef mpl::map<
        mpl::pair< tag::value_type, value_type >,
        mpl::pair< tag::entity, tag::matrix >,
        mpl::pair< tag::size_type<1>, size_type1 >,
        mpl::pair< tag::size_type<2>, size_type2 >,
        mpl::pair< tag::data_structure, tag::linear_array >,
        mpl::pair< tag::data_order, data_order >,
        mpl::pair< tag::stride_type<1>,
            typename if_row_major< data_order, size_type2, tag::contiguous >::type >,
        mpl::pair< tag::stride_type<2>,
            typename if_row_major< data_order, tag::contiguous, size_type1 >::type >
    > property_map;

    static std::ptrdiff_t size1( const Id& id ) {
        return id.rows();
    }

    static std::ptrdiff_t size2( const Id& id ) {
        return id.cols();
    }

    static value_type* begin_value( Id& id ) {
        return id.data();
    }

    static value_type* end_value( Id& id ) {
        return id.data() + id.size();
    }

    static std::ptrdiff_t stride1( const Id& id ) {
        return id.cols();
    }

    static std::ptrdiff_t stride2( const Id& id ) {
        return id.rows();
    }

};

template< typename T, int Rows, int Cols, int Options,
          typename Id, typename Enable >
struct adaptor< Eigen::Transpose<const Eigen::Matrix<T, Rows, Cols, Options > >, Id, Enable > {

    typedef typename copy_const< Id, T >::type value_type;
    typedef typename eigen_size_type< Rows >::type size_type1;
    typedef typename eigen_size_type< Cols >::type size_type2;
    typedef typename eigen_data_order_trans< Options >::type data_order;
    typedef mpl::map<
        mpl::pair< tag::value_type, value_type >,
        mpl::pair< tag::entity, tag::matrix >,
        mpl::pair< tag::size_type<1>, size_type1 >,
        mpl::pair< tag::size_type<2>, size_type2 >,
        mpl::pair< tag::data_structure, tag::linear_array >,
        mpl::pair< tag::data_order, data_order >,
        mpl::pair< tag::stride_type<1>,
            typename if_row_major< data_order, size_type2, tag::contiguous >::type >,
        mpl::pair< tag::stride_type<2>,
            typename if_row_major< data_order, tag::contiguous, size_type1 >::type >
    > property_map;

    static std::ptrdiff_t size1( const Id& id ) {
        return id.rows();
    }

    static std::ptrdiff_t size2( const Id& id ) {
        return id.cols();
    }

    static value_type* begin_value( Id& id ) {
        return id.data();
    }

    static value_type* end_value( Id& id ) {
        return id.data() + id.size();
    }

    static std::ptrdiff_t stride1( const Id& id ) {
        return id.cols();
    }

    static std::ptrdiff_t stride2( const Id& id ) {
        return id.rows();
    }

};

} // namespace detail
} // namespace bindings
} // namespace numeric
} // namespace boost

#endif


// This file is part of the dune-gdt project:
//   https://github.com/dune-community/dune-gdt
// Copyright 2010-2018 dune-gdt developers and contributors. All rights reserved.
// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
//          with "runtime exception" (http://www.dune-project.org/license.html)
// Authors:
//   Felix Schindler (2015 - 2017)
//   Rene Milk       (2016 - 2018)
//   Tobias Leibner  (2016)

#ifndef DUNE_GDT_SPACES_RT_INTERFACE_HH
#define DUNE_GDT_SPACES_RT_INTERFACE_HH

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

#include <dune/xt/common/timedlogging.hh>

#include <dune/gdt/spaces/interface.hh>

namespace Dune {
namespace GDT {

template <class ImpTraits, size_t domainDim, size_t rangeDim, size_t rangeDimCols = 1>
class RtSpaceInterface : public SpaceInterface<ImpTraits, domainDim, rangeDim, rangeDimCols>
{
  typedef SpaceInterface<ImpTraits, domainDim, rangeDim, rangeDimCols> BaseType;
  typedef RtSpaceInterface<ImpTraits, domainDim, rangeDim, rangeDimCols> ThisType;

public:
  typedef ImpTraits Traits;

  using BaseType::polOrder;
  using BaseType::dimDomain;
  using typename BaseType::DomainFieldType;
  using typename BaseType::DomainType;
  using typename BaseType::EntityType;
  using typename BaseType::BaseFunctionSetType;
  using typename BaseType::PatternType;

  /**
   * \defgroup interface ´´These methods have to be implemented!''
   * @{
   **/

  /**
   *  \brief  Computes a vector 'indices' of length entity.count< 1 >(), where 'indices[intersection.indexInInside()]'
   *          is the index of the basis function (aka the local DoF index) corresponding to the intersection.
   */
  std::vector<size_t> local_DoF_indices(const EntityType& entity) const
  {
    CHECK_CRTP(this->as_imp().local_DoF_indices(entity));
    return this->as_imp().local_DoF_indices(entity);
  }

  /**
   * \defgroup provided ´´These methods are provided by the interface for convenience.''
   * @{
   **/
  std::vector<size_t> local_DoF_indices_2dsimplex_order0(const EntityType& entity) const
  {
    static_assert(dimDomain == 2, "Not implemented!");
    static_assert(polOrder == 0, "Not implemented!");
    // prepare
    const auto num_intersections = entity.subEntities(1);
    std::vector<size_t> local_DoF_index_of_vertex(num_intersections, std::numeric_limits<size_t>::infinity());
    std::vector<size_t> local_DoF_index_of_intersection(num_intersections, std::numeric_limits<size_t>::infinity());
    typedef typename BaseFunctionSetType::DomainType DomainType;
    std::vector<DomainType> vertices(num_intersections, DomainType(0));
    std::vector<bool> lies_on_intersection(num_intersections, false);
    DomainType corner(0);
    typedef typename BaseFunctionSetType::RangeType RangeType;
    const RangeType one(1);
    std::vector<RangeType> basis_values(num_intersections, one);
    const auto basis = this->base_function_set(entity);
    assert(basis.size() == num_intersections);
    const auto geometry = entity.geometry();
    const auto& reference_element = ReferenceElements<DomainFieldType, dimDomain>::general(geometry.type());
    // find the basis function index that corresponds to each vertex of the entity
    // (find the basis function that evaluates to zero at the vertex, and nonzero at the other ones)
    // therefore we walk the vertices
    assert(num_intersections == entity.subEntities(dimDomain));
    for (size_t vv = 0; vv < num_intersections; ++vv) {
      const auto vertex = entity.template subEntity<dimDomain>(boost::numeric_cast<int>(vv));
      // get the vertex coordinates
      vertices[vv] = vertex.geometry().center();
      const auto& vertex_entity = reference_element.position(boost::numeric_cast<int>(vv), dimDomain);
      // evalaute the basis
      basis.evaluate(vertex_entity, basis_values);
      // and find the basis that evaluates zero here
      size_t zeros = 0;
      size_t nonzeros = 0;
      for (size_t ii = 0; ii < num_intersections; ++ii) {
        // we would like to check against 0, but there is a bug in dune-commons FloatCmp
        if (XT::Common::FloatCmp::eq(basis_values[ii] + one, one)) {
          // this is a candidate for the basis function we are looking for
          local_DoF_index_of_vertex[vv] = ii;
          ++zeros;
        } else
          ++nonzeros;
      }
      // make sure there was only one candidate
      if (zeros != 1 || nonzeros != (num_intersections - 1))
        DUNE_THROW(XT::Common::Exceptions::internal_error,
                   "This must not happen for RTN0 in 2d!\n"
                       << "  zeros    = "
                       << zeros
                       << "\n"
                       << "  nonzeros = "
                       << nonzeros);
    } // walk the vertices
    // so from here on we have the local DoF index that corresponds to each vertex vv in local_DoF_index_of_vertex[vv]
    // now we need to find the intersection opposite to this vertex
    // therefore we walk the intersections
    size_t intersection_counter = 0;
    const auto intersection_it_end = this->grid_layer().iend(entity);
    for (auto intersection_it = this->grid_layer().ibegin(entity); intersection_it != intersection_it_end;
         ++intersection_it) {
      const auto& intersection = *intersection_it;
      const auto intersection_geometry = intersection.geometry();
      const size_t local_intersection_index = intersection.indexInInside();
      // make sure this index has not been already taken by another intersection
      assert(local_DoF_index_of_intersection[local_intersection_index] == std::numeric_limits<size_t>::infinity());
      // walk the corners of the intersection
      for (int cc = 0; cc < intersection_geometry.corners(); ++cc) {
        corner = intersection_geometry.corner(boost::numeric_cast<int>(cc));
        // check which vertices lie on the intersection
        for (size_t vv = 0; vv < num_intersections; ++vv)
          if (XT::Common::FloatCmp::eq(vertices[vv], corner))
            lies_on_intersection[vv] = true;
      } // walk the corners of the intersection
      // now see if we find a vertex that does not lie on the intersection
      size_t found = 0;
      size_t missed = 0;
      for (size_t vv = 0; vv < num_intersections; ++vv) {
        if (!(lies_on_intersection[vv])) {
          // this is a good candidate
          // so the local DoF id that corresponds to this vertex is the one that corresponds to the intersection
          local_DoF_index_of_intersection[local_intersection_index] = local_DoF_index_of_vertex[vv];
          ++found;
        } else
          ++missed;
        // and clear for the next intersection
        lies_on_intersection[vv] = false;
      } // walk the vertices of this entity
      // make sure there was only one candidate
      if (found != 1 || missed != (num_intersections - 1))
        DUNE_THROW(XT::Common::Exceptions::internal_error,
                   "This must not happen for RTN0 in 2d!\n"
                       << "  found  = "
                       << found
                       << "\n"
                       << "  missed = "
                       << missed);
      ++intersection_counter;
    } // walk the intersection
    assert(intersection_counter == num_intersections);
    return local_DoF_index_of_intersection;
  } // ... local_DoF_indices_2dsimplex_order0(...)

  using BaseType::compute_pattern;

  template <class GL, class S, size_t d, size_t r, size_t rC>
  typename std::enable_if<XT::Grid::is_layer<GL>::value, PatternType>::type
  compute_pattern(const GL& grd_layr, const SpaceInterface<S, d, r, rC>& ansatz_space) const
  {
    Dune::XT::Common::TimedLogger().get("gdt.spaces.rt.compute_pattern").warn() << "Returning largest possible pattern!"
                                                                                << std::endl;
    return BaseType::compute_face_and_volume_pattern(grd_layr, ansatz_space);
  }

  using BaseType::local_constraints;

  template <class S, size_t d, size_t r, size_t rC, class C, class R>
  void local_constraints(const SpaceInterface<S, d, r, rC>& /*ansatz_space*/,
                         const EntityType& /*entity*/,
                         ConstraintsInterface<C>& /*ret*/) const
  {
    DUNE_THROW(NotImplemented, "RT spaces do not implement constraints!");
  }

  static constexpr bool associates_data_with(int codim)
  {
    return codim < dimDomain;
  }
  /** @} */
}; // class RtSpaceInterface

} // namespace GDT
} // namespace Dune

#endif // DUNE_GDT_SPACES_RT_INTERFACE_HH


#ifdef COMPILATION_INSTRUCTIONS //sudo cpupower frequency-set --governor performance
clang++ -O3 $0 -o$0x -DBOOST_TEST_DYN_LINK -lboost_unit_test_framework -lboost_timer -lbenchmark -lpthread&&$0x&&rm $0x;exit
#endif

//#define BOOST_TEST_MODULE "C++ Unit Tests for Multi move"
//#include<boost/test/unit_test.hpp>

#include "../array.hpp"

#include <boost/timer/timer.hpp>

#include<vector>
#include<complex>
#include<numeric>

#include <benchmark/benchmark.h>

namespace multi = boost::multi;

#if 0
BOOST_AUTO_TEST_CASE(multi_array_copy){
	std::vector<multi::array<double, 2> > A(10, multi::array<double, 2>({4, 5}, 99.));
	std::vector<multi::array<double, 2> > B;
	B.reserve(10);

	for(auto& v: A)	B.emplace_back(v);

	BOOST_REQUIRE( size(B) == size(A) );
	BOOST_REQUIRE( size(A[4]) == 4 );
	BOOST_REQUIRE( size(B[5]) == 4 );
	BOOST_REQUIRE( B[5][1][2] == 99. );
}
#endif

//BOOST_AUTO_TEST_CASE(multi_copy_vs_vector_copy){
namespace boost{
namespace multi{
	template<class T1, class P1, class T2, class P2>
	auto copy(array_iterator<T1, 1, P1> f, array_iterator<T1, 1, P1> l, array_iterator<T2, 1, P2> d)
	->decltype(array_iterator<T2, 1, P2>{(stride(f)==1 and stride(d)==1)?std::copy(base(f), base(l), base(d)):std::copy(f, l, d)}){assert(stride(f)==stride(l));
		return array_iterator<T2, 1, P2>{(stride(f)==1 and stride(d)==1)?std::copy(base(f), base(l), base(d)):std::copy(f, l, d)};}
}}

class A{
	double i;
public:
	A(double const& i) : i{i}{}
	A(A const& o) = default;
	A& operator=(A const& a) = default;
};

static void CopyMulti(benchmark::State& state) {
	multi::array<A, 1> a(1 << 16, {1.1}); std::iota(begin(a), end(a), 1.1111);
	multi::array<A, 1> b(1 << 16, {99.});
	for(auto _ : state){
		using std::copy;
		copy(begin(a), end(a), begin(b));
		benchmark::DoNotOptimize(b);
	}
}
BENCHMARK(CopyMulti);

static void CopyMulti2(benchmark::State& state) {
	multi::array<A, 1> a(1 << 16, {1.1}); std::iota(begin(a), end(a), 1.1111);
	multi::array<A, 1> b(1 << 16, {99.});
	for(auto _ : state){
		std::copy(begin(a), end(a), begin(b));
		benchmark::DoNotOptimize(b);
	}
}
BENCHMARK(CopyMulti2);

static void Copy(benchmark::State& state) {
	std::vector<A> v(1 << 16, {1.1}); std::iota(begin(v), end(v), 1.1111);
	std::vector<A> w(1 << 16, {99.});
	for(auto _ : state){
		std::copy(begin(v), end(v), begin(w));
		benchmark::DoNotOptimize(w);
	}
}
BENCHMARK(Copy);

BENCHMARK_MAIN();


#include "teca_config.h"
#include "teca_cf_reader.h"
#include "teca_normalize_coordinates.h"
#include "teca_l2_norm.h"
#include "teca_vorticity.h"
#include "teca_derived_quantity.h"
#include "teca_derived_quantity_numerics.h"
#include "teca_tc_candidates.h"
#include "teca_tc_trajectory.h"
#include "teca_array_collection.h"
#include "teca_variant_array.h"
#include "teca_metadata.h"
#include "teca_table_reader.h"
#include "teca_table_reduce.h"
#include "teca_table_sort.h"
#include "teca_table_calendar.h"
#include "teca_table_writer.h"
#include "teca_mpi_manager.h"
#include "teca_coordinate_util.h"
#include "calcalcs.h"

#include <vector>
#include <string>
#include <iostream>
#include <boost/program_options.hpp>

using namespace std;
using namespace teca_derived_quantity_numerics;

using boost::program_options::value;

// --------------------------------------------------------------------------
int main(int argc, char **argv)
{
    // initialize mpi
    teca_mpi_manager mpi_man(argc, argv);

    // initialize command line options description
    // set up some common options to simplify use for most
    // common scenarios
    options_description basic_opt_defs(
        "Basic usage:\n\n"
        "The following options are the most commonly used. Information\n"
        "on advanced options can be displayed using --advanced_help\n\n"
        "Basic command line options", 120, -1
        );
    basic_opt_defs.add_options()
        ("input_file", value<string>(), "file path to the simulation to search for tropical cyclones")
        ("input_regex", value<string>(), "regex matching simulation files to search for tropical cylones")
        ("candidate_file", value<string>(), "file path to write the storm candidates to (candidates.bin)")
        ("850mb_wind_u", value<string>(), "name of variable with 850 mb wind x-component (U850)")
        ("850mb_wind_v", value<string>(), "name of variable with 850 mb wind x-component (V850)")
        ("surface_wind_u", value<string>(), "name of variable with surface wind x-component (UBOT)")
        ("surface_wind_v", value<string>(), "name of variable with surface wind y-component (VBOT)")
        ("sea_level_pressure", value<string>(), "name of variable with sea level pressure (PSL)")
        ("500mb_temp", value<string>(), "name of variable with 500mb temperature for warm core calc (T500)")
        ("200mb_temp", value<string>(), "name of variable with 200mb temperature for warm core calc (T200)")
        ("1000mb_height", value<string>(), "name of variable with 1000mb height for thickness calc (Z1000)")
        ("200mb_height", value<string>(), "name of variable with 200mb height for thickness calc (Z200)")
        ("storm_core_radius", value<double>(), "maximum number of degrees latitude separation between vorticity max and pressure min defining a storm (2.0)")
        ("min_vorticity", value<double>(), "minimum vorticty to be considered a tropical storm (1.6e-4)")
        ("vorticity_window", value<double>(), "size of the search window in degrees. storms core must have a local vorticity max centered on this window (7.74446)")
        ("pressure_delta", value<double>(), "maximum pressure change within specified radius (400.0)")
        ("pressure_delta_radius", value<double>(), "radius in degrees over which max pressure change is computed (5.0)")
        ("core_temp_delta", value<double>(), "maximum core temperature change over the specified radius (0.8)")
        ("core_temp_radius", value<double>(), "radius in degrees over which max core temperature change is computed (5.0)")
        ("thickness_delta", value<double>(), "maximum thickness change over the specified radius (50.0)")
        ("thickness_radius", value<double>(), "radius in degrees over with max thickness change is comuted (4.0)")
        ("lowest_lat", value<double>(), "lowest latitude in degrees to search for storms (80)")
        ("highest_lat", value<double>(), "highest latitude in degrees to search for storms (80)")
        ("max_daily_distance", value<double>(), "max distance in km that a storm can travel in one day (1600)")
        ("min_wind_speed", value<double>(), "minimum peak wind speed to be considered a tropical storm (17.0)")
        ("min_wind_duration", value<double>(), "number of, not necessarily consecutive, days min wind speed sustained (2.0)")
        ("track_file", value<string>(), "file path to write storm tracks to")
        ("first_step", value<long>(), "first time step to process")
        ("last_step", value<long>(), "last time step to process")
        ("start_date", value<string>(), "first time to proces in YYYY-MM-DD hh:mm:ss format")
        ("end_date", value<string>(), "first time to proces in YYYY-MM-DD hh:mm:ss format")
        ("n_threads", value<int>(), "thread pool size. default is 1. -1 for all")
        ("help", "display the basic options help")
        ("advanced_help", "display the advanced options help")
        ("full_help", "display entire help message")
        ;

    // add all options from each pipeline stage for more advanced use
    options_description advanced_opt_defs(
        "Advanced usage:\n\n"
        "The following list contains the full set options giving one full\n"
        "control over all runtime modifiable parameters. The basic options\n"
        "(see" "--help) map to these, and will override them if both are\n"
        "specified.\n\n"
        "tropical storms pipeline:\n\n"
        "   (sim_reader)\n"
        "        \\\n"
        "  (surface_wind_speed)--(850mb_vorticity)--(core_temperature)\n"
        "                                                    /\n"
        " (tracks)--(sort)--(map_reduce)--(candidates)--(thickness)\n"
        "     \\                               /\n"
        " (track_writer)              (candidate_writer)\n\n"
        "Advanced command line options", -1, 1
        );

    // create the pipeline stages here, they contain the
    // documentation and parse command line.
    // objects report all of their properties directly
    // set default options here so that command line options override
    // them. while we are at it connect the pipeline
    p_teca_cf_reader sim_reader = teca_cf_reader::New();
    sim_reader->get_properties_description("sim_reader", advanced_opt_defs);

    p_teca_normalize_coordinates sim_coords = teca_normalize_coordinates::New();
    sim_coords->set_input_connection(sim_reader->get_output_port());

    p_teca_l2_norm surf_wind = teca_l2_norm::New();
    surf_wind->set_input_connection(sim_coords->get_output_port());
    surf_wind->set_component_0_variable("UBOT");
    surf_wind->set_component_1_variable("VBOT");
    surf_wind->set_l2_norm_variable("surface_wind");
    surf_wind->get_properties_description("surface_wind_speed", advanced_opt_defs);

    p_teca_vorticity vort_850mb = teca_vorticity::New();
    vort_850mb->set_input_connection(surf_wind->get_output_port());
    vort_850mb->set_component_0_variable("U850");
    vort_850mb->set_component_1_variable("V850");
    vort_850mb->set_vorticity_variable("850mb_vorticity");
    vort_850mb->get_properties_description("850mb_vorticity", advanced_opt_defs);

    p_teca_derived_quantity core_temp = teca_derived_quantity::New();
    core_temp->set_input_connection(vort_850mb->get_output_port());
    core_temp->set_dependent_variables({"T500", "T200"});
    core_temp->set_derived_variable("core_temperature");
    core_temp->get_properties_description("core_temperature", advanced_opt_defs);

    p_teca_derived_quantity thickness = teca_derived_quantity::New();
    thickness->set_input_connection(core_temp->get_output_port());
    thickness->set_dependent_variables({"Z1000", "Z200"});
    thickness->set_derived_variable("thickness");
    thickness->get_properties_description("thickness", advanced_opt_defs);

    p_teca_tc_candidates candidates = teca_tc_candidates::New();
    candidates->set_input_connection(thickness->get_output_port());
    candidates->set_surface_wind_speed_variable("surface_wind");
    candidates->set_vorticity_850mb_variable("850mb_vorticity");
    candidates->set_sea_level_pressure_variable("PSL");
    candidates->set_core_temperature_variable("core_temperature");
    candidates->set_thickness_variable("thickness");
    candidates->set_max_core_radius(2.0);
    candidates->set_min_vorticity_850mb(1.6e-4);
    candidates->set_vorticity_850mb_window(7.74446);
    candidates->set_max_pressure_delta(400.0);
    candidates->set_max_pressure_radius(5.0);
    candidates->set_max_core_temperature_delta(0.8);
    candidates->set_max_core_temperature_radius(5.0);
    candidates->set_max_thickness_delta(50.0);
    candidates->set_max_thickness_radius(4.0);
    candidates->set_search_lat_low(-80.0);
    candidates->set_search_lat_high(80.0);
    candidates->get_properties_description("candidates", advanced_opt_defs);

    p_teca_table_reduce map_reduce = teca_table_reduce::New();
    map_reduce->set_input_connection(candidates->get_output_port());
    map_reduce->get_properties_description("map_reduce", advanced_opt_defs);

    p_teca_table_writer candidate_writer = teca_table_writer::New();
    candidate_writer->set_input_connection(map_reduce->get_output_port());
    candidate_writer->set_output_format_auto();
    candidate_writer->get_properties_description("candidate_writer", advanced_opt_defs);

    p_teca_table_sort sort = teca_table_sort::New();
    sort->set_input_connection(candidate_writer->get_output_port());
    sort->set_index_column("storm_id");
    sort->get_properties_description("sort", advanced_opt_defs);

    p_teca_tc_trajectory tracks = teca_tc_trajectory::New();
    tracks->set_input_connection(sort->get_output_port());
    tracks->set_max_daily_distance(1600.0);
    tracks->set_min_wind_speed(17.0);
    tracks->set_min_wind_duration(2.0);
    tracks->get_properties_description("tracks", advanced_opt_defs);

    p_teca_table_calendar calendar = teca_table_calendar::New();
    calendar->set_input_connection(tracks->get_output_port());
    calendar->get_properties_description("calendar", advanced_opt_defs);

    p_teca_table_writer track_writer = teca_table_writer::New();
    track_writer->set_input_connection(calendar->get_output_port());
    track_writer->set_output_format_auto();
    track_writer->get_properties_description("track_writer", advanced_opt_defs);

    // package basic and advanced options for display
    options_description all_opt_defs(-1, -1);
    all_opt_defs.add(basic_opt_defs).add(advanced_opt_defs);

    // parse the command line
    variables_map opt_vals;
    try
    {
        boost::program_options::store(
            boost::program_options::command_line_parser(argc, argv).options(all_opt_defs).run(),
            opt_vals);

        if (mpi_man.get_comm_rank() == 0)
        {
            if (opt_vals.count("help"))
            {
                cerr << endl
                    << "usage: teca_tc_detect [options]" << endl
                    << endl
                    << basic_opt_defs << endl
                    << endl;
                return -1;
            }
            if (opt_vals.count("advanced_help"))
            {
                cerr << endl
                    << "usage: teca_tc_detect [options]" << endl
                    << endl
                    << advanced_opt_defs << endl
                    << endl;
                return -1;
            }

            if (opt_vals.count("full_help"))
            {
                cerr << endl
                    << "usage: teca_tc_detect [options]" << endl
                    << endl
                    << all_opt_defs << endl
                    << endl;
                return -1;
            }
        }

        boost::program_options::notify(opt_vals);
    }
    catch (std::exception &e)
    {
        TECA_ERROR("Error parsing command line options. See --help "
            "for a list of supported options. " << e.what())
        return -1;
    }

    // pass command line arguments into the pipeline objects
    // advanced options are processed first, so that the basic
    // options will override them
    sim_reader->set_properties("sim_reader", opt_vals);
    surf_wind->set_properties("surface_wind_speed", opt_vals);
    vort_850mb->set_properties("850mb_vorticity", opt_vals);
    core_temp->set_properties("core_temperature", opt_vals);
    thickness->set_properties("thickness", opt_vals);
    candidates->set_properties("candidates", opt_vals);
    map_reduce->set_properties("map_reduce", opt_vals);
    candidate_writer->set_properties("candidate_writer", opt_vals);
    sort->set_properties("sort", opt_vals);
    tracks->set_properties("tracks", opt_vals);
    calendar->set_properties("calendar", opt_vals);
    track_writer->set_properties("track_writer", opt_vals);

    // now pass in the basic options, these are processed
    // last so that they will take precedence
    if (opt_vals.count("input_file"))
        sim_reader->append_file_name(
            opt_vals["input_file"].as<string>());

    if (opt_vals.count("input_regex"))
        sim_reader->set_files_regex(
            opt_vals["input_regex"].as<string>());

    if (opt_vals.count("850mb_wind_u"))
        vort_850mb->set_component_0_variable(
            opt_vals["850mb_wind_u"].as<string>());

    if (opt_vals.count("850mb_wind_v"))
        vort_850mb->set_component_1_variable(
            opt_vals["850mb_wind_v"].as<string>());

    if (opt_vals.count("surface_wind_u"))
        surf_wind->set_component_0_variable(
            opt_vals["surface_wind_u"].as<string>());

    if (opt_vals.count("surface_wind_v"))
        surf_wind->set_component_1_variable(
            opt_vals["surface_wind_v"].as<string>());

    std::vector<std::string> dep_var;
    core_temp->get_dependent_variables(dep_var);
    if (opt_vals.count("500mb_temp"))
        dep_var[0] = opt_vals["500mb_temp"].as<string>();
    if (opt_vals.count("200mb_temp"))
        dep_var[1] = opt_vals["200mb_temp"].as<string>();
    core_temp->set_dependent_variables(dep_var);
    dep_var.clear();

    thickness->get_dependent_variables(dep_var);
    if (opt_vals.count("1000mb_height"))
        dep_var[0] = opt_vals["1000mb_height"].as<string>();
    if (opt_vals.count("200mb_height"))
        dep_var[1] = opt_vals["200mb_height"].as<string>();
    thickness->set_dependent_variables(dep_var);
    dep_var.clear();

    if (opt_vals.count("sea_level_pressure"))
        candidates->set_sea_level_pressure_variable(
            opt_vals["sea_level_pressure"].as<string>());

    if (opt_vals.count("storm_core_radius"))
        candidates->set_max_core_radius(
            opt_vals["storm_core_radius"].as<double>());

    if (opt_vals.count("min_vorticity"))
        candidates->set_min_vorticity_850mb(
            opt_vals["min_vorticity"].as<double>());

    if (opt_vals.count("vorticity_window"))
        candidates->set_vorticity_850mb_window(
            opt_vals["vorticity_window"].as<double>());

    if (opt_vals.count("pressure_delta"))
        candidates->set_max_pressure_delta(
            opt_vals["pressure_delta"].as<double>());

    if (opt_vals.count("pressure_delta_radius"))
        candidates->set_max_pressure_radius(
            opt_vals["pressure_delta_radius"].as<double>());

    if (opt_vals.count("core_temp_delta"))
        candidates->set_max_core_temperature_delta(
            opt_vals["core_temp_delta"].as<double>());

    if (opt_vals.count("core_temp_radius"))
        candidates->set_max_core_temperature_radius(
            opt_vals["core_temp_radius"].as<double>());

    if (opt_vals.count("thickness_delta"))
        candidates->set_max_thickness_delta(
            opt_vals["thickness_delta"].as<double>());

    if (opt_vals.count("thickness_radius"))
        candidates->set_max_thickness_radius(
            opt_vals["thickness_radius"].as<double>());

    if (opt_vals.count("lowest_lat"))
        candidates->set_search_lat_low(
            opt_vals["lowest_lat"].as<double>());

    if (opt_vals.count("highest_lat"))
        candidates->set_search_lat_high(
            opt_vals["highest_lat"].as<double>());

    if (opt_vals.count("first_step"))
        map_reduce->set_start_index(opt_vals["first_step"].as<long>());

    if (opt_vals.count("last_step"))
        map_reduce->set_end_index(opt_vals["last_step"].as<long>());

    if (opt_vals.count("n_threads"))
        map_reduce->set_thread_pool_size(opt_vals["n_threads"].as<int>());
    else
        map_reduce->set_thread_pool_size(-1);

    if (opt_vals.count("max_daily_distance"))
        tracks->set_max_daily_distance(
            opt_vals["max_daily_distance"].as<double>());

    if (opt_vals.count("min_wind_speed"))
        tracks->set_min_wind_speed(
            opt_vals["min_wind_speed"].as<double>());

    if (opt_vals.count("min_wind_duration"))
        tracks->set_min_wind_duration(
            opt_vals["min_wind_duration"].as<double>());

    if (opt_vals.count("candidate_file"))
        candidate_writer->set_file_name(
            opt_vals["candidate_file"].as<string>());

    if (opt_vals.count("track_file"))
        track_writer->set_file_name(
            opt_vals["track_file"].as<string>());

    // some minimal check for missing options
    if (sim_reader->get_number_of_file_names() == 0
        && sim_reader->get_files_regex().empty())
    {
        if (mpi_man.get_comm_rank() == 0)
        {
            TECA_ERROR(
                "missing file name or regex for simulation reader. "
                "See --help for a list of command line options.")
        }
        return -1;
    }

    // now that command line opts have been parsed we can create
    // the programmable algorithms' functors
    core_temp->get_dependent_variables(dep_var);
    if (dep_var.size() != 2)
    {
        TECA_ERROR("core temperature calculation requires 2 "
            "variables. given " << dep_var.size())
        return -1;
    }
    core_temp->set_execute_callback(
        point_wise_average(dep_var[0], dep_var[1],
        core_temp->get_derived_variable()));
    dep_var.clear();

    thickness->get_dependent_variables(dep_var);
    if (dep_var.size() != 2)
    {
        TECA_ERROR("thickness calculation requires 2 "
            "variables. given " << dep_var.size())
        return -1;
    }
    thickness->set_execute_callback(
        point_wise_difference(dep_var[0], dep_var[1],
        thickness->get_derived_variable()));
    dep_var.clear();

    // and tell the candidate stage what variables the functors produce
    candidates->set_surface_wind_speed_variable(surf_wind->get_l2_norm_variable());
    candidates->set_vorticity_850mb_variable(vort_850mb->get_vorticity_variable());
    candidates->set_core_temperature_variable(core_temp->get_derived_variable());
    candidates->set_thickness_variable(thickness->get_derived_variable());

    // look for requested time step range, start
    bool parse_start_date = opt_vals.count("start_date");
    bool parse_end_date = opt_vals.count("end_date");
    if (parse_start_date || parse_end_date)
    {
        // run the reporting phase of the pipeline
        teca_metadata md = sim_reader->update_metadata();

        teca_metadata atrs;
        if (md.get("attributes", atrs))
        {
            TECA_ERROR("metadata mising attributes")
            return -1;
        }

        teca_metadata time_atts;
        std::string calendar;
        std::string units;
        if (atrs.get("time", time_atts)
           || time_atts.get("calendar", calendar)
           || time_atts.get("units", units))
        {
            TECA_ERROR("failed to determine the calendaring parameters")
            return -1;
        }

        teca_metadata coords;
        p_teca_double_array time;
        if (md.get("coordinates", coords) ||
            !(time = std::dynamic_pointer_cast<teca_double_array>(
                coords.get("t"))))
        {
            TECA_ERROR("failed to determine time coordinate")
            return -1;
        }

        // convert date string to step, start date
        if (parse_start_date)
        {
            unsigned long first_step = 0;
            std::string start_date = opt_vals["start_date"].as<string>();
            if (teca_coordinate_util::time_step_of(time, true, calendar,
                 units, start_date, first_step))
            {
                TECA_ERROR("Failed to lcoate time step for start date \""
                    <<  start_date << "\"")
                return -1;
            }
            map_reduce->set_start_index(first_step);
        }

        // and end date
        if (parse_end_date)
        {
            unsigned long last_step = 0;
            std::string end_date = opt_vals["end_date"].as<string>();
            if (teca_coordinate_util::time_step_of(time, false, calendar,
                 units, end_date, last_step))
            {
                TECA_ERROR("Failed to lcoate time step for end date \""
                    <<  end_date << "\"")
                return -1;
            }
            map_reduce->set_end_index(last_step);
        }
    }

    // run the pipeline
    track_writer->update();

    return 0;
}


/*
 * Copyright (C) 2020 Open Source Robotics Foundation
 *
 * 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 PYORIENTATIONCONSTRAINT_HPP
#define PYORIENTATIONCONSTRAINT_HPP

#include <Eigen/Dense>
#include <vector>

#include "rmf_fleet_adapter/agv/Adapter.hpp"
#include <rmf_utils/clone_ptr.hpp>

using Graph = rmf_traffic::agv::Graph;

// Trampoline rmf_traffic::agv::Graph::OrientationConstraint wrapper class
// to allow method overrides from Python
class PyOrientationConstraint :
  public Graph::OrientationConstraint
{
public:
  // Constructor
  using Graph::OrientationConstraint::OrientationConstraint;

  bool apply(Eigen::Vector3d& position,
             const Eigen::Vector2d& course_vector) const override
  {
    PYBIND11_OVERLOAD_PURE(
      bool,
      Graph::OrientationConstraint,
      apply,
      position,
      course_vector
    );
  }

  rmf_utils::clone_ptr<OrientationConstraint> clone() const override
  {
    PYBIND11_OVERLOAD_PURE(
      rmf_utils::clone_ptr<OrientationConstraint>,
      Graph::OrientationConstraint,
      clone,  // Trailing comma required
    );
  }
};

#endif // PYORIENTATIONCONSTRAINT_HPP


/*
 * Copyright 2017 Will Mitchell
 *
 * 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 <algorithm>
using std::remove_if;
#include <cctype>
using std::isalpha;
#include <cmath>
using std::sqrt;
#include <iostream>
using std::cout;
using std::endl;
#include <fstream>
using std::getline;
using std::ifstream;
#include <random>
using std::mt19937;
using std::uniform_int_distribution;
#include <string>
using std::stoul;
using std::string;
#include <utility>
using std::pair;
#include <vector>
using std::vector;

#include "bloomfilter/BloomFilter.h"
using bloomfilter::BloomFilter;
using bloomfilter::BloomFilterStandard;
#include "bloomfilter/HashSet.h"
using bloomfilter::HashSetPair;
#include "bloomfilter/InsertionPolicy.h"
using bloomfilter::InsertionBigramWithSentinel;
using bloomfilter::InsertionTrigramWithSentinel;
#include "hash/HashFactory.h"
#include "util/Timer.h"
using util::Timer;

#include "graph/Traversals.h"

#include <boost/algorithm/string.hpp>

#include "bfeattacks/FilterDictionary.h"
#include "bfeattacks/FilterRequireExactly.h"
#include "bfeattacks/FilterSize.h"
#include "bfeattacks/SingleRecord.h"

#include "bfeattacks/Accumulator.h"
using bfeattacks::Accumulator;

void test_bfe(string test, string alphabet,
              vector<graph::Traversal> &traversals, Accumulator &stats);
template <typename T>
string randomstring(string alphabet, unsigned length,
                    uniform_int_distribution<string::size_type> &dist,
                    T &rand_source);

template <typename T>
string randomstring(string alphabet, unsigned length,
                    uniform_int_distribution<string::size_type> &dist,
                    T &rand_source) {
  string ret;

  for (unsigned i = 0; i < length; ++i)
    ret += alphabet[dist(rand_source)];

  return ret;
}

void test_bfe(string test, string alphabet,
              vector<graph::Traversal> &traversals, Accumulator &stats) {

  // Construct the record
  HashSetPair hs(30);
  hs.add(hash::MD5).add(hash::SHA3_256);
  bfeattacks::SingleRecord<BloomFilterStandard> rec(
      BloomFilterStandard(1000, hs));

  // Populate the bloom filter
  rec.insert(test);

  // Construct the graph
  rec.construct_graph(alphabet);

  // std::ofstream outfile(test + ".graphml");
  // rec.write_graphml(outfile);
  // outfile.close();

  // cout << rec.bf << endl;
  // cout << "estimated elements: " << rec.estimate_elements() << endl;

  // Run the traversals
  rec.setup_traversals(traversals);
  for (const auto i : traversals)
    rec.run_traversal(i);
  rec.simplify_paths();

  // Apply filters
  // auto est_elts = std::lround(rec.estimate_elements());
  // size_t est_len = static_cast<size_t>(est_elts) - 1; // 1 is n of n-grams
  // minus 1
  // cout << "Filter len [" << est_len - 1 << ", " << est_len + 1 << "]" <<
  // endl;
  // bfeattacks::filter_size(rec, est_len - 1, est_len + 1);
  // bfeattacks::filter_size(rec, 9, 9);
  bfeattacks::filter_require_exactly(rec);

  // Collect stats
  stats.add(rec);
}

int main(int argc, char **argv) {
  // Defaults
  vector<graph::Traversal> traversals = {
    { graph::Traversal::depth_first_search, graph::Traversal::all_simple_paths,
      graph::Traversal::all_edge_disjoint_paths }
  };
  Accumulator stats(traversals);

  string alphabet = "1234567890";
  unsigned long count = 4;
  unsigned length = 9;

  // Parse command line
  if (argc > 1) {
    try {
      count = stoul(argv[1]);
    }
    catch (std::invalid_argument e) {
      cout << "Invalid number of iterations '" << argv[1] << "'" << endl;
    }
  }
  if (argc > 2) {
    try {
      length = static_cast<unsigned>(stoul(argv[2]));
    }
    catch (std::invalid_argument e) {
      cout << "Invalid length of '" << argv[2] << "'" << endl;
    }
  }
  if (argc > 3)
    alphabet = argv[3];

  // Set up RNG
  // Use a set seed
  mt19937 mt(12345);
  // Use a good seed, if possible
  // std::random_device seed;
  // mt19937 mt(seed);

  uniform_int_distribution<string::size_type> uniform_dist(0,
                                                           alphabet.size() - 1);

  cout << "Alphabet: " << alphabet << "\n"
       << "Length: " << length << "\n"
       << "Number of trials: " << count << endl;

  for (unsigned long i = 0; i < count; ++i) {
    string rand = randomstring(alphabet, length, uniform_dist, mt);

    if (count < 10)
      cout << "Test [" << i + 1 << "/" << count << "]: " << rand
           << " -------------------------------" << endl;
    test_bfe(rand, alphabet, traversals, stats);
  }

  cout << stats << endl;

  return 0;
}


/* boost random/detail/uniform_int_float.hpp header file
 *
 * Copyright Jens Maurer 2000-2001
 * 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)
 *
 * See http://www.boost.org for most recent version including documentation.
 *
 * $Id: uniform_int_float.hpp,v 1.3 2004/07/27 03:43:32 dgregor Exp $
 *
 */

#ifndef BOOST_RANDOM_DETAIL_UNIFORM_INT_FLOAT_HPP
#define BOOST_RANDOM_DETAIL_UNIFORM_INT_FLOAT_HPP

#include <boost/config.hpp>
#include <boost/random/uniform_01.hpp>

namespace boost {
namespace random {
namespace detail {

template<class UniformRandomNumberGenerator, class IntType = unsigned long>
class uniform_int_float
{
public:
  typedef UniformRandomNumberGenerator base_type;
  typedef IntType result_type;

  uniform_int_float(base_type rng, IntType min = 0, IntType max = 0xffffffff)
    : _rng(rng), _min(min), _max(max)
  {
    init();
  }

  result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return _min; }
  result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const { return _max; }
  base_type& base() { return _rng.base(); }
  const base_type& base() const { return _rng.base(); }

  result_type operator()()
  {
    return static_cast<IntType>(_rng() * _range) + _min;
  }

#if !defined(BOOST_NO_OPERATORS_IN_NAMESPACE) && !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
  template<class CharT, class Traits>
  friend std::basic_ostream<CharT,Traits>&
  operator<<(std::basic_ostream<CharT,Traits>& os, const uniform_int_float& ud)
  {
    os << ud._min << " " << ud._max;
    return os;
  }

  template<class CharT, class Traits>
  friend std::basic_istream<CharT,Traits>&
  operator>>(std::basic_istream<CharT,Traits>& is, uniform_int_float& ud)
  {
    is >> std::ws >> ud._min >> std::ws >> ud._max;
    ud.init();
    return is;
  }
#endif

private:
  void init()
  {
    _range = static_cast<base_result>(_max-_min)+1;
  }

  typedef typename base_type::result_type base_result;
  uniform_01<base_type> _rng;
  result_type _min, _max;
  base_result _range;
};

} // namespace detail
} // namespace random
} // namespace boost

#endif // BOOST_RANDOM_DETAIL_UNIFORM_INT_FLOAT_HPP


/*ckwg +29
 * Copyright 2015 by Kitware, Inc.
 * 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 name of Kitware, Inc. nor the names of any 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 AUTHORS 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.
 */

/**
 * \file
 * \brief core fundamental matrix template implementations
 */

#include "fundamental_matrix.h"

#include <cmath>

#include <vital/exceptions/math.h>

#include <Eigen/SVD>

namespace kwiver {
namespace vital {

/// Construct from a provided matrix
template <typename T>
fundamental_matrix_<T>
::fundamental_matrix_( Eigen::Matrix<T,3,3> const &mat )
{
  Eigen::JacobiSVD<matrix_t> svd(mat, Eigen::ComputeFullU |
                                      Eigen::ComputeFullV);
  auto S = svd.singularValues();
  const matrix_t& U = svd.matrixU();
  const matrix_t& V = svd.matrixV();

  // clear the last singular value
  S[2] = T(0);
  S /= S.norm();
  mat_ = U*S.asDiagonal()*V.transpose();
}

/// Conversion Copy constructor -- float specialization
template <>
template <>
fundamental_matrix_<float>
::fundamental_matrix_( fundamental_matrix_<float> const &other )
  : mat_( other.mat_ )
{
}

/// Conversion Copy constructor -- double specialization
template <>
template <>
fundamental_matrix_<double>
::fundamental_matrix_( fundamental_matrix_<double> const &other )
  : mat_( other.mat_ )
{
}

/// Construct from a generic fundamental_matrix
template <typename T>
fundamental_matrix_<T>
::fundamental_matrix_( fundamental_matrix const &base )
  : mat_( base.matrix().template cast<T>() )
{
}

/// Construct from a generic fundamental_matrix -- double specialization
template <>
fundamental_matrix_<double>
::fundamental_matrix_( fundamental_matrix const &base )
  : mat_( base.matrix() )
{
}

/// Create a clone of outself as a shared pointer
template <typename T>
fundamental_matrix_sptr
fundamental_matrix_<T>
::clone() const
{
  return fundamental_matrix_sptr( new fundamental_matrix_<T>( *this ) );
}

/// Get a double-typed copy of the underlying matrix
template <typename T>
Eigen::Matrix<double,3,3>
fundamental_matrix_<T>
::matrix() const
{
  return this->mat_.template cast<double>();
}

/// Specialization for matrices with native double type
template <>
Eigen::Matrix<double,3,3>
fundamental_matrix_<double>
::matrix() const
{
  return this->mat_;
}

// ===========================================================================
// Other Functions
// ---------------------------------------------------------------------------

/// Output stream operator for \p fundamental_matrix instances
std::ostream&
operator<<( std::ostream &s, fundamental_matrix const &f )
{
  s << f.matrix();
  return s;
}

// ===========================================================================
// Template class instantiation
// ---------------------------------------------------------------------------
/// \cond DoxygenSuppress
#define INSTANTIATE_FUNDAMENTAL_MATRIX(T) \
  template class fundamental_matrix_<T>;

INSTANTIATE_FUNDAMENTAL_MATRIX(float);
INSTANTIATE_FUNDAMENTAL_MATRIX(double);
#undef INSTANTIATE_FUNDAMENTAL_MATRIX
/// \endcond

} } // end vital namespace