////////////////////////////////////////////////////////////////////////////////
// msh_processor.cc
////////////////////////////////////////////////////////////////////////////////
/*! @file
//      Extracts and processes fields from a .msh file. The field processor is
//      essentually an RPN evaluator that maintains a stack of values and
//      applies the filters specified on the command line in order.
//
//      Component-wise Binary Operations:
//          add, sub, mul, div
//      Component-wise Unary Operations:
//          abs, scale, set
//      (Partial) reductions:
//          min, max, minMag, maxMag, norm, index, sum, mean
//      Reductions can be done in multiple wasy for multi-indexed objects
//      (e.g. vector fields, vector interpolants, fields of vector interpolants).
//      By default, reductions are done over the innermost index
//      ("pointwise" for fields/interpolants)
//          Field<NonScalar>       -> Field<Reduced>       (recursive)
//          Interpolant<NonScalar> -> Interpolant<Reduced> (recursive)
//          Field<Scalar>          -> Scalar
//          Interpolant<Scalar>    -> Scalar
//          PointValue             -> Scalar
//      When "outer reduction" mode is requested, reductions are done over the
//      outer index (per-component reduction for vector fields/interpolants).
//      Note: this is probably the more natural action for sum, mean of vector
//      fields.
//          Field<NonScalar>     -> Scalar
//          Field<Scalar>        -> Scalar
//          Interpolant<Scalar>  -> Scalar
//          Field<T>             -> T
//          Interpolant<T>       -> T
//          PointValue           -> Scalar
//      Warning: operations treat interpolants as vectors of nodal values, so
//      for interpolants with negative weights component-wise min/max won't
//      necessarily be the min/max over the simplex.
//
//      TODO: store element *index* on interpolant: binary operations can only
//      act on a pair of interpolants with matching element index.
*/
//  Author:  Julian Panetta (jpanetta), julian.panetta@gmail.com
//  Company:  New York University
//  Created:  08/16/2014 15:26:04
////////////////////////////////////////////////////////////////////////////////
#include "argparse.hh"
#include "Sampler.hh"
#include "Values.hh"
#include "MeshConnectivity.hh"
#include <MeshFEM/ExpressionVector.hh>
#include <MeshFEM/filters/remove_dangling_vertices.hh>
#include <MeshFEM/MSHFieldParser.hh>
#include <MeshFEM/MSHFieldWriter.hh>
#include <MeshFEM/SimplicialMesh.hh>
#include <MeshFEM/Types.hh>
#include <MeshFEM/VonMises.hh>
#include <boost/algorithm/string.hpp>
#include <iomanip>
#include <regex>
#include <vector>
#include <map>
#include <set>
#include <stdexcept>
#include <cmath>
#include <cctype>
#include <memory>
#include <functional>
#include <limits>
#include <sstream>

//using namespace MeshIO;
using namespace std;

// Global parsers used for 2 and 3D cases.
unique_ptr<MSHFieldParser<2>> g_parser2D;
unique_ptr<MSHFieldParser<3>> g_parser3D;

template<size_t N, typename... Args>
void parseMSH(Args&&... args) {
    static_assert(N == 2 || N == 3, "Invalid parser dimension");
    if (N == 2) g_parser2D = make_unique<MSHFieldParser<2>>(std::forward<Args>(args)...);
    else        g_parser3D = make_unique<MSHFieldParser<3>>(std::forward<Args>(args)...);
}

template<size_t N>
const MSHFieldParser<N> &getParser();
template<> const MSHFieldParser<2> &getParser<2>() { return *g_parser2D; }
template<> const MSHFieldParser<3> &getParser<3>() { return *g_parser3D; }

template<size_t N>
MSHFieldParser<N> &getMutableParser();
template<>       MSHFieldParser<2> &getMutableParser<2>() { return *g_parser2D; }
template<>       MSHFieldParser<3> &getMutableParser<3>() { return *g_parser3D; }

// Global lazily-constructed element samplers used for 2 and 3D cases.
unique_ptr<ElementSampler::Sampler<2>> g_sampler2D;
unique_ptr<ElementSampler::Sampler<3>> g_sampler3D;

template<size_t N>
const ElementSampler::Sampler<N> &getElementSampler();
template<> const ElementSampler::Sampler<2> &getElementSampler<2>() { if (g_sampler2D) return *g_sampler2D; g_sampler2D = make_unique<ElementSampler::Sampler<2>>(g_parser2D->vertices(), g_parser2D->elements()); return *g_sampler2D; }
template<> const ElementSampler::Sampler<3> &getElementSampler<3>() { if (g_sampler3D) return *g_sampler3D; g_sampler3D = make_unique<ElementSampler::Sampler<3>>(g_parser3D->vertices(), g_parser3D->elements()); return *g_sampler3D; }

// Global lazily-constructed mesh data structures for 2 and 3D cases.
unique_ptr<SimplicialMesh<2>> g_triMesh;
unique_ptr<SimplicialMesh<3>> g_tetMesh;

template<size_t N>
const SimplicialMesh<N> &getMeshDS();
template<> const SimplicialMesh<2> &getMeshDS<2>() { if (g_triMesh) return *g_triMesh; else g_triMesh = make_unique<SimplicialMesh<2>>(g_parser2D->elements(), g_parser2D->vertices().size()); return *g_triMesh; }
template<> const SimplicialMesh<3> &getMeshDS<3>() { if (g_tetMesh) return *g_tetMesh; else g_tetMesh = make_unique<SimplicialMesh<3>>(g_parser3D->elements(), g_parser3D->vertices().size()); return *g_tetMesh; }

////////////////////////////////////////////////////////////////////////////////
// Stack operations
////////////////////////////////////////////////////////////////////////////////
using Stack = vector<NamedValue>;

struct Modifiers {
    bool outerReduction = false;
    bool applyAll = false;
};

NamedValue &getValue(Stack &stack, size_t offset = 0) {
    if (stack.size() <= offset) throw std::runtime_error("Accessed out of stack bounds.");
    size_t idx = stack.size() - 1 - offset;
    return stack.at(idx);
}

NamedValue popValue(Stack &stack) {
    if (stack.empty()) throw std::runtime_error("Tried to pop from empty stack.");
    NamedValue val = std::move(getValue(stack));
    stack.pop_back();
    return val;
}

template<typename T>
TypedNamedValue<T> &getTypedValue(Stack &stack, size_t offset = 0) {
    return TypedNamedValue<T>(getValue(stack, offset));
}

template<typename T>
TypedNamedValue<T> popTypedValue(Stack &stack) {
    if (stack.empty()) throw std::runtime_error("Tried to pop from empty stack.");
    NamedValue val = std::move(stack.back());
    stack.pop_back();
    return val;
}

////////////////////////////////////////////////////////////////////////////////
// Filters - operate on the stack.
// These are all template functions with the signature:
// template<size_t N>
// size_t f(const string &op, const string &arg, Stack &stack, const Modifiers &m)
//
// Filters return the number of results they pushed onto the stack. Filters are
// considered to first pop all of their arguments then push "results" onto the
// stack. For example, the print operation that acts on the stack top but
// does not modify the stack is considered to have one result (it pops and
// pushes back the argument). The reverse operation, which acts on the whole
// stack, returns the stack size.
//
// Placed in their own namespace because their names overload those defined
// elsewhere, causing problems with the lookup table definition
////////////////////////////////////////////////////////////////////////////////
namespace Filter {
// Data source filters
// Extract field(s) matching the pattern in "arg", pushing them on the top of
// the stack.
size_t pushScalarField(Stack &stack, const string &name, const ScalarField<Real> &sf, const DomainType &dtype) {
    TypedNamedValue<FSValue> sfv(name, dtype, sf.domainSize());
    for (size_t i = 0; i < sf.domainSize(); ++i)
        sfv->value[i] = SValue(sf[i]);
    stack.push_back(std::move(sfv));
    return 1;
}

template<size_t N>
size_t pushVectorField(Stack &stack, const string &name, const VectorField<Real, N> &vf, const DomainType &dtype) {
    TypedNamedValue<FVValue> vfv(name, dtype, vf.domainSize());
    for (size_t i = 0; i < vf.domainSize(); ++i)
        vfv->value[i] = VValue(vf(i).eval());
    stack.push_back(std::move(vfv));
    return 1;
}

template<size_t N>
size_t pushSymmetricMatrixField(Stack &stack, const string &name, const SymmetricMatrixField<Real, N> &smf, const DomainType &dtype) {
    TypedNamedValue<FSMValue> smfv(name, dtype, smf.domainSize());
    for (size_t i = 0; i < smf.domainSize(); ++i)
        smfv->value[i] = SMValue(smf(i));
    stack.push_back(std::move(smfv));
    return 1;
}

template<class IFType, class RawType>
size_t pushInterpolantField(Stack &stack, const string &name, const RawType &raw_if, const DomainType &dtype) {
    TypedNamedValue<IFType> ifv(name, dtype, raw_if.size());
    for (size_t i = 0; i < raw_if.size(); ++i)
        ifv->value[i] = raw_if[i];
    stack.push_back(std::move(ifv));
    return 1;
}

template<size_t N>
size_t extract(const string &/*op*/, const string &arg, Stack &stack, const Modifiers &) {
    const auto &parser = getParser<N>();
    std::regex pattern(arg);
    size_t origSize = stack.size();
    DomainType dtype;
    for (const string &name : parser.scalarFieldNames()) {
        if (regex_match(name, pattern))
            pushScalarField(stack, name, parser.scalarField(name, DomainType::ANY, dtype), dtype);
    }
    for (const string &name : parser.vectorFieldNames()) {
        if (regex_match(name, pattern))
            pushVectorField(stack, name, parser.vectorField(name, DomainType::ANY, dtype), dtype);
    }
    for (const string &name : parser.symmetricMatrixFieldNames()) {
        if (regex_match(name, pattern))
            pushSymmetricMatrixField(stack, name, parser.symmetricMatrixField(name, DomainType::ANY, dtype), dtype);
    }
    for (const string &name : parser.scalarInterpolantFieldNames()) {
        if (regex_match(name, pattern))
            pushInterpolantField< FISValue>(stack, name, parser.         scalarInterpolantField(name, DomainType::ANY, dtype), dtype);
    }
    for (const string &name : parser.vectorInterpolantFieldNames()) {
        if (regex_match(name, pattern))
            pushInterpolantField< FIVValue>(stack, name, parser.         vectorInterpolantField(name, DomainType::ANY, dtype), dtype);
    }
    for (const string &name : parser.symmetricMatrixInterpolantFieldNames()) {
        if (regex_match(name, pattern))
            pushInterpolantField<FISMValue>(stack, name, parser.symmetricMatrixInterpolantField(name, DomainType::ANY, dtype), dtype);
    }

    assert(stack.size() >= origSize);
    if (stack.size() == origSize) throw runtime_error("No fields matched '" + arg + "'");
    return stack.size() - origSize;
}

template<size_t N>
size_t extractAll(const string &/*op*/, const string &/* arg */, Stack &stack, const Modifiers &) {
    size_t origSize = stack.size();
    const auto &parser = getParser<N>();
    DomainType dtype;
    for (const string &name : parser.scalarFieldNames())
        pushScalarField(stack, name, parser.scalarField(name, DomainType::ANY, dtype), dtype);
    for (const string &name : parser.vectorFieldNames())
        pushVectorField(stack, name, parser.vectorField(name, DomainType::ANY, dtype), dtype);
    for (const string &name : parser.symmetricMatrixFieldNames())
        pushSymmetricMatrixField(stack, name, parser.symmetricMatrixField(name, DomainType::ANY, dtype), dtype);

    assert(stack.size() >= origSize);
    return stack.size() - origSize;
}

template<size_t N>
size_t generate(const string &, const string &arg, Stack &stack, const Modifiers &) {
    const auto &parser = getParser<N>();
    const auto &vertices = parser.vertices();
    const auto &elements = parser.elements();
    if (arg == "x") {
        VectorField<Real, N> x(vertices.size());
        for (size_t i = 0; i < vertices.size(); ++i)
            x(i) = truncateFrom3D<VectorND<N>>(vertices[i].point);
        pushVectorField(stack, "x", x, DomainType::PER_NODE);
    }
    else if (arg == "volume") {
        const auto &sampler = getElementSampler<N>();
        ScalarField<Real> vol(elements.size());
        for (size_t i = 0; i < elements.size(); ++i)
            vol[i] = sampler.volume(i);
        pushScalarField(stack, "volume", vol, DomainType::PER_ELEMENT);
    }
    else if (arg == "barycenter") {
        VectorField<Real, N> c(elements.size());
        c.clear();
        for (size_t ei = 0; ei < elements.size(); ++ei) {
            const auto &e = elements[ei];
            for (size_t j : e)
                c(ei) += truncateFrom3D<VectorND<N>>(vertices.at(j).point);
            c(ei) *= 1.0 / e.size();
        }
        pushVectorField(stack, "barycenter", c, DomainType::PER_ELEMENT);
    }
    else throw std::runtime_error("Invalid mesh property name: " + arg);

    return 1;
}

template<size_t N>
size_t expression(const string &, const string &arg, Stack &stack, const Modifiers &) {
    const auto &parser = getParser<N>();
    const auto &vertices = parser.vertices();

    vector<string> components;
    boost::split(components, arg, boost::is_any_of(","));
    string name = "expr(" + arg + ")";

    ExpressionEnvironment env;
    BBox<VectorND<N>> bb(vertices);
    env.setVectorValue("mesh_size_", bb.dimensions());
    env.setVectorValue("mesh_min_",  bb.minCorner);
    env.setVectorValue("mesh_max_",  bb.maxCorner);

    if (components.size() == 1) {
        Expression expr(components[0]);
        ScalarField<Real> sf(vertices.size());
        for (size_t i = 0; i < vertices.size(); ++i) {
            env.setXYZ(vertices[i].point);
            sf[i] = expr.eval(env);
        }
        pushScalarField(stack, name, sf, DomainType::PER_NODE);
    }
    else if (components.size() == N) {
        ExpressionVector expr(components);
        VectorField<Real, N> vf(vertices.size());
        for (size_t i = 0; i < vertices.size(); ++i) {
            env.setXYZ(vertices[i].point);
            vf(i) = expr.eval<N>(env);
        }
        pushVectorField(stack, name, vf, DomainType::PER_NODE);
    }
    else throw std::runtime_error("Invalid number of components in vector-valued expression " + arg);

    return 1;
}

size_t     dup(const string &, const string &   , Stack &stack, const Modifiers &) { stack.emplace_back(getValue(stack)); return 2; } // Copies: NamedValue has value semantics
size_t     pop(const string &, const string &   , Stack &stack, const Modifiers &) { popValue(stack); return 0; }
size_t    push(const string &, const string &arg, Stack &stack, const Modifiers &) { double d = parseRealArg(arg); stack.push_back(TypedNamedValue<SValue>(to_string(d), d)); return 1; }
size_t reverse(const string &, const string &   , Stack &stack, const Modifiers &) { std::reverse(stack.begin(), stack.end()); return stack.size(); }
size_t pull(const string &, const string &arg, Stack &stack, const Modifiers &) {
    for (auto it = stack.begin(); it != stack.end(); ++it) {
        if ((*it).name == arg) {
            NamedValue val(std::move(*it));
            stack.erase(it);
            stack.emplace_back(std::move(val));
            return 1;
        }
    }
    throw runtime_error("Couldn't find '" + arg + "' for pull.");
}

////////////////////////////////////////////////////////////////////////////////
// Symmetric matrix operations
////////////////////////////////////////////////////////////////////////////////
struct Eigenvalues {
    // Returns a vector of eigenvalues.
    using value_type = VValue;
    static constexpr const char *name = "eigenvalues";
    static VValue apply(const SMValue &sm) { return VValue(sm.value.eigenvalues()); }
};

struct VonMises {
    // Returns a symmetric matrix
    using value_type = SMValue;
    static constexpr const char *name = "vonMises";
    static SMValue apply(const SMValue &sm) { return vonMises(sm.value); }
};

struct FrobeniusNorm {
    // Returns a scalar
    using value_type = SValue;
    static constexpr const char *name = "frobeniusNorm";
    static SValue apply(const SMValue &sm) { return sqrt(sm.value.frobeniusNormSq()); }
};

template<class Op>
size_t SMatrixOperation(const string &, const string &/* arg */, Stack &stack, const Modifiers &) {
    using VT = typename Op::value_type;
    auto val = popValue(stack);
    string name = std::string(Op::name) + "(" + val.name + ")";
    if (auto sm = dynamic_cast<SMValue *>(VPtr(val)))
        stack.push_back(TypedNamedValue<VT>(name, Op::apply(sm->value)));
    else if (auto ism = dynamic_cast<ISMValue *>(VPtr(val))) {
        auto result = make_unique<InterpolantValue<VT>>(ism->simplexDimension());
        for (size_t i = 0; i < result->dim(); ++i)
            (*result)[i] = VT(Op::apply((*ism)[i].value));
        stack.emplace_back(name, std::move(result));
    }
    else if (auto fsm = dynamic_cast<FSMValue *>(VPtr(val))) {
        auto result = make_unique<FieldValue<VT>>(fsm->size());
        result->domainType = fsm->domainType;
        for (size_t i = 0; i < result->size(); ++i) {
            (*result)[i] = VT(Op::apply((*fsm)[i].value));
        }
        stack.emplace_back(name, std::move(result));
    }
    else if (/*auto ifsm =*/ dynamic_cast<FISMValue *>(VPtr(val))) {
        throw std::runtime_error("Not yet implemented.");
    }
    else throw runtime_error("called on non-matrix type argument");

    return 1;
}

// // This matrix->vector operator unfortunately must be implemented manually in
// // the current framework...
// size_t eigenvaluesAndEigenvectors(const string &, const string &arg, Stack &stack, const Modifiers &) {
//     auto val = popValue(stack);
//     string name = "eigenvalues(" + val.name + ")";
//     if (auto sm = dynamic_cast<SMValue *>(VPtr(val)))
//         stack.push_back(TypedNamedValue<VValue>(name, sm->value.eigenvalues()));
//     else if (auto  ism = dynamic_cast<ISMValue *>(VPtr(val))) {
//         auto result = make_unique<IVValue>(ism->simplexDimension());
//         for (size_t i = 0; i < result->dim(); ++i) {
//             auto &sm = (*ism)[i];
//             (*result)[i] = VValue(sm.value.eigenvalues());
//         }
//         stack.emplace_back(name, std::move(result));
//     }
//     else if (auto  fsm = dynamic_cast<FSMValue *>(VPtr(val))) {
//         auto result = make_unique<FVValue>(fsm->size());
//         result->domainType = fsm->domainType;
//         for (size_t i = 0; i < result->size(); ++i) {
//             auto &sm = (*fsm)[i];
//             (*result)[i] = VValue(sm.value.eigenvalues());
//         }
//         stack.emplace_back(name, std::move(result));
//     }
//     else if (/*auto ifsm =*/ dynamic_cast<FISMValue *>(VPtr(val))) {
//         throw std::runtime_error("Not yet implemented.");
//     }
//     else throw runtime_error("called on non-matrix type argument");
//
//     return 1;
// }

template<size_t N>
size_t elementBarycenterFieldTransfer(const string &, const string &arg, Stack &stack, const Modifiers &) {
    auto &sampler = getElementSampler<N>();
    sampler.accelerate();

    MSHFieldParser<N> targetMesh(arg);

    PointND<N> center;
    std::vector<ElementSampler::Sample> samplePts; samplePts.reserve(targetMesh.elements().size());
    for (const auto &e : targetMesh.elements()) {
        center.setZero();
        for (size_t vi : e) center += truncateFrom3D<VectorND<N>>(targetMesh.vertices().at(vi));
        center *= 1.0 / e.size();
        samplePts.emplace_back(sampler(center));
    }

    auto &currentMesh = getMutableParser<N>();
    Stack xferStack;

    for (const auto &val : stack) {
        string name = "transfer(" + val.name + ")";
        xferStack.emplace_back(name, val->sample(samplePts,
                    currentMesh.meshDegree(), currentMesh.meshDimension(),
                    DomainType::PER_ELEMENT));
    }

    // Replace mesh/value stack with the new mesh, stack of transferred values.
    // Sampler is also invalidated
    stack = std::move(xferStack);
    currentMesh = std::move(targetMesh);
    g_sampler2D.reset();
    g_sampler3D.reset();

    return stack.size();
}

template<size_t N>
size_t loadNewMSH(const string &, const string &arg, Stack &, const Modifiers &) {
    auto &currentMesh = getMutableParser<N>();
    currentMesh = MSHFieldParser<N>(arg);
    g_sampler2D.reset();
    g_sampler3D.reset();
    return 0;
}

// Filter elements using an indicator scalar field.
// (elements with value > 0 are kept).
template<size_t N>
size_t filterElements(const string &, const string &, Stack &stack, const Modifiers &) {
    auto invalid = std::runtime_error("Invalid argument to filterElements. Argument must be a per-element scalar field.");
    try {
        auto &currentMesh = getMutableParser<N>();
        auto top = popTypedValue<FSValue>(stack);
        if (top->domainType != DomainType::PER_ELEMENT)
            throw invalid;
        auto verts = currentMesh.vertices();
        const auto &oldElems  = currentMesh.elements();
        std::vector<::MeshIO::IOElement> elems;
        for (size_t i = 0; i < oldElems.size(); ++i) {
            if (top->value[i].value > 0) elems.emplace_back(oldElems[i]);
        }
        remove_dangling_vertices(verts, elems);
        currentMesh.replaceMesh(elems, verts);
        stack.clear();
    }
    catch (...) {
        throw invalid;
    }
    return 0;
}

// Sample a field at the point(s) specified by vector list encoded in "arg".
// Nodal fields are interpolated using the mesh's finite element basis functions.
// Element fields are interpolated piecewise constant
// Interpolant fields are sampled at barycentric coordinates.
// Error is thrown for sample points outside the mesh (note: could happen on
// element boundaries if inside/outside check is not robust)
template<size_t N>
size_t sample(const string &, const string &arg, Stack &stack, const Modifiers &) {
    auto pts = parseVectorListArg<N>(arg);
    auto val = popValue(stack);
    // Determine element index, barycentric coordinates, and element node
    // indices of the containing element.
    const auto &sampler = getElementSampler<N>();
    const auto &parser = getParser<N>();

    for (const auto &p : pts) {
        stringstream ss;
        ss << p.format(Eigen::IOFormat(Eigen::FullPrecision, Eigen::DontAlignCols, "", ", ", "", "", "[", "]"));
        string name = "sample(" + val.name + ", " + ss.str() + ")";
        stack.emplace_back(name, val->sample(sampler(p), parser.meshDegree(),
                           parser.meshDimension()));
    }

    return pts.size();
}

// Average a field over each element.
template<size_t N>
size_t elementAverage(const string &, const string &/* arg */, Stack &stack, const Modifiers &) {
    auto val = popValue(stack);
    const auto &parser = getParser<N>();
    stack.emplace_back("elementAverage(" + val.name + ")",
                        val->elementAverage(parser.elements(), parser.meshDegree(), parser.meshDimension()));
    return 1;
}

// Create a per-element field by a volume-weighted averaging over each element's
// neighbors.
template<size_t N>
size_t smoothedElementField(const string &, const string &/* arg */, Stack &stack, const Modifiers &) {
    auto val = popValue(stack);
    const auto &parser = getParser<N>();

    // Compute element volumes
    const auto &sampler = getElementSampler<N>();
    size_t nelems = parser.elements().size();
    std::vector<Real> volumes(nelems);
    for (size_t i = 0; i < nelems; ++i)
        volumes[i] = sampler.volume(i);

    stack.emplace_back("smoothedElementField(" + val.name + ")",
                        val->smoothedElementField(parser.elements(), parser.meshDegree(), parser.meshDimension(),
                                                  volumes, MeshConnectivityImpl<SimplicialMesh<N>>(getMeshDS<N>())));
    return 1;
}

// Report filters
// List all fields parsed
template<size_t N>
size_t listNames(const string &, const string &/* arg */, Stack &, const Modifiers &) {
    const auto &parser = getParser<N>();
    for (const string &name : parser.         scalarFieldNames()) { cout << "s\t"  << name << endl; }
    for (const string &name : parser.         vectorFieldNames()) { cout << "v\t"  << name << endl; }
    for (const string &name : parser.symmetricMatrixFieldNames()) { cout << "sm\t" << name << endl; }

    for (const string &name : parser.         scalarInterpolantFieldNames()) { cout << "si\t"  << name << endl; }
    for (const string &name : parser.         vectorInterpolantFieldNames()) { cout << "vi\t"  << name << endl; }
    for (const string &name : parser.symmetricMatrixInterpolantFieldNames()) { cout << "smi\t" << name << endl; }
    return 0;
}

// Print the top of the stack.
size_t print    (const string &, const string &, Stack &stack, const Modifiers &) { getValue(stack)->print(); cout << endl; return 1; }
size_t printName(const string &, const string &, Stack &stack, const Modifiers &) { cout << getValue(stack).name << endl; return 1; }
size_t noprint  (const string &, const string &, Stack &     , const Modifiers &) { return 1; }

template<size_t N>
size_t importScalarField(const string &/* op */, const string &arg, Stack &stack, const Modifiers &) {
    const auto &parser = getParser<N>();
    const auto &vertices = parser.vertices();
    const auto &elements = parser.elements();
    ifstream inFile(arg);
    if (!inFile.is_open()) throw std::runtime_error("Couldn't open scalar field import file: " + arg);
    Real val;
    std::vector<Real> values;
    while (inFile >> val)
        values.push_back(val);
    if (values.size() == vertices.size())
        pushScalarField(stack, arg, values, DomainType::PER_NODE);
    else if (values.size() == elements.size())
        pushScalarField(stack, arg, values, DomainType::PER_ELEMENT);
    else throw std::runtime_error("Didn't recognize imported field size.");

    return 1;
}

// Import a flattened vector field (x0 y0 z0 x1 ...)
template<size_t N>
size_t importVectorField(const string &/* op */, const string &arg, Stack &stack, const Modifiers &) {
    const auto &parser = getParser<N>();
    const auto &vertices = parser.vertices();
    const auto &elements = parser.elements();
    ifstream inFile(arg);
    if (!inFile.is_open()) throw std::runtime_error("Couldn't open vector field import file: " + arg);
    Real val;
    std::vector<Real> values;
    while (inFile >> val)
        values.push_back(val);
    VectorField<Real, N> vfield(values);

    if (vfield.domainSize() == vertices.size())
        pushVectorField(stack, arg, vfield, DomainType::PER_NODE);
    else if (vfield.domainSize() == elements.size())
        pushVectorField(stack, arg, vfield, DomainType::PER_ELEMENT);
    else throw std::runtime_error("Didn't recognize imported field size.");

    return 1;
}

template<size_t N>
size_t outputMSH(const string &/* op */, const string &arg, Stack &stack, const Modifiers &) {
    const auto &parser = getParser<N>();
    // Note: there will be downsampling of interpolant fields if the output
    // mesh elements are linear. But in these cases, the original extracted
    // interpolant fields were linear as well.
    // TODO: rewrite as template code.
    MSHFieldWriter writer(arg, parser.vertices(), parser.elements(), parser.meshType());
    for (const auto &val : stack) {
        if (auto fs = dynamic_cast<const FSValue *>(CVPtr(val))) {
            ScalarField<Real> sf(fs->value.size());
            for (size_t i = 0; i < sf.domainSize(); ++i)
                sf[i] = fs->value[i].value;
            writer.addField(val.name, sf, fs->domainType);
        }
        else if (auto fv = dynamic_cast<const FVValue *>(CVPtr(val))) {
            VectorField<Real, N> vf(fv->value.size());
            for (size_t i = 0; i < vf.domainSize(); ++i)
                vf(i) = fv->value[i].value;
            writer.addField(val.name, vf, fv->domainType);
        }
        else if (auto fsm = dynamic_cast<const FSMValue *>(CVPtr(val))) {
            SymmetricMatrixField<Real, N> smf(fsm->value.size());
            for (size_t i = 0; i < smf.domainSize(); ++i)
                smf(i) = fsm->value[i].value;
            writer.addField(val.name, smf, fsm->domainType);
        }
        else if (auto fis = dynamic_cast<const FISValue *>(CVPtr(val))) {
            std::vector<typename InterpolantGetter<SValue, N>::storage_backed_type> isf(fis->value.size());
            for (size_t i = 0; i < fis->value.size(); ++i)
                isf[i] = InterpolantGetter<SValue, N>::get(fis->value[i]);
            writer.addField(val.name, isf, fis->domainType);
        }
        else if (auto fiv = dynamic_cast<const FIVValue *>(CVPtr(val))) {
            std::vector<typename InterpolantGetter<VValue, N>::storage_backed_type> ivf(fiv->value.size());
            for (size_t i = 0; i < fiv->value.size(); ++i)
                ivf[i] = InterpolantGetter<VValue, N>::get(fiv->value[i]);
            writer.addField(val.name, ivf, fiv->domainType);
        }
        else if (auto fism = dynamic_cast<const FISMValue *>(CVPtr(val))) {
            std::vector<typename InterpolantGetter<SMValue, N>::storage_backed_type> ismf(fism->value.size());
            for (size_t i = 0; i < fism->value.size(); ++i)
                ismf[i] = InterpolantGetter<SMValue, N>::get(fism->value[i]);
            writer.addField(val.name, ismf, fism->domainType);
        }
        else cout << "WARNING: ignored non-field value on stack: " << val.name << endl;
    }
    return stack.size();
}

size_t rename(const string &/* op */, const string &arg, Stack &stack, const Modifiers &) {
    vector<string> names;
    boost::split(names, arg, boost::is_any_of(","));
    if (names.size() > stack.size()) {
        throw runtime_error("Too many names provided to rename");
    }
    size_t pos = stack.size();
    for(auto &name : names)
        stack[--pos].name = std::move(name);
    return names.size();
}

template<size_t N>
size_t setNodePositions(const string &, const string &/* arg */, Stack &stack, const Modifiers &) {
    const auto &top = popTypedValue<FVValue>(stack);

    size_t nVals = top->size();
    std::vector<PointND<3>> newPositions(nVals);
    for (size_t i = 0; i < nVals; ++i)
        newPositions[i] = top->operator[](i).value;

    getMutableParser<N>().setNodePositions(newPositions);
    return 0;
}

template<class R>
size_t applyReduction(const string &op, const string &arg, Stack &stack, const Modifiers &m) {
    auto top = popValue(stack);
    string name = op + arg + "(" + top.name + ")";
    if (m.outerReduction) name = "outer_" + name;
    R r(arg);
    if (m.outerReduction) stack.emplace_back(name, top->outerReduction(r));
    else                  stack.emplace_back(name, top->innerReduction(r));
    return 1;
}

template<class UOp>
size_t applyUnaryOp(const string &op, const string &arg, Stack &stack, const Modifiers &) {
    auto top = popValue(stack);
    stack.emplace_back(op + arg + "(" + top.name + ")",
                       top->componentwiseUnaryOp(UOp(arg)));
    return 1;
}

template<class BOp>
size_t applyBinaryOp(const string &op, const string &arg, Stack &stack, const Modifiers &) {
    // Top of stack is the second operand, next in stack is the first
    auto b = popValue(stack);
    auto a = popValue(stack);
    if (arg.size() != 0) throw runtime_error("Did not expect binary op argument");
    stack.emplace_back(op + "(" + a.name + ", " + b.name + ")",
                       a->componentwiseBinaryOp(BOp(), b));
    return 1;
}

} // end namespace Filter

template<size_t N>
void execute(vector<FilterInvocation> &filters) {
    map<string, function<size_t(const string &, const string &, Stack &, const Modifiers &)>>
    filterImplementations = {
        // Reductions
        {"min",    Filter::applyReduction<ReductionMin   >},
        {"max",    Filter::applyReduction<ReductionMax   >},
        {"minMag", Filter::applyReduction<ReductionMinMag>},
        {"maxMag", Filter::applyReduction<ReductionMaxMag>},
        {"norm",   Filter::applyReduction<ReductionNorm  >},
        {"sum",    Filter::applyReduction<ReductionSum   >},
        {"mean",   Filter::applyReduction<ReductionMean  >},
        {"index",  Filter::applyReduction<ReductionIndex >},
        // Unary operations
        {"abs",    Filter::applyUnaryOp<AbsOp  >},
        {"scale",  Filter::applyUnaryOp<ScaleOp>},
        {"set",    Filter::applyUnaryOp<SetOp  >},
        // Binary operations
        {"add",    Filter::applyBinaryOp<AddOp>},
        {"sub",    Filter::applyBinaryOp<SubOp>},
        {"mul",    Filter::applyBinaryOp<MulOp>},
        {"div",    Filter::applyBinaryOp<DivOp>},
        // Custom value operations
        {"print",          Filter::print},
        {"noprint",        Filter::noprint},
        {"printName",      Filter::printName},
        {"eigenvalues",    Filter::SMatrixOperation<Filter::Eigenvalues>},
        {"vonMises",       Filter::SMatrixOperation<Filter::VonMises>},
        {"frobeniusNorm",  Filter::SMatrixOperation<Filter::FrobeniusNorm>},
        // {"eigs",           Filter::eigenvaluesAndEigenvectors},
        {"sample",         Filter::sample<N>},
        {"elementAverage", Filter::elementAverage<N>},
        {"smoothedElementField", Filter::smoothedElementField<N>},
        // Stack operations
        {"list",          Filter::listNames<N>},
        {"extract",       Filter::extract<N>},
        {"extractAll",    Filter::extractAll<N>},
        {"generate",      Filter::generate<N>},
        {"expression",    Filter::expression<N>},
        {"dup",           Filter::dup},
        {"pop",           Filter::pop},
        {"push",          Filter::push},
        {"pull",          Filter::pull},
        {"rename",        Filter::rename},
        {"import_sfield", Filter::importScalarField<N>},
        {"import_vfield", Filter::importVectorField<N>},
        {"reverse",       Filter::reverse},
        {"setNodePositions", Filter::setNodePositions<N>},
        {"outMSH",        Filter::outputMSH<N>},

        {"transferFieldsToPerElem", Filter::elementBarycenterFieldTransfer<N>},
        {"loadNewMSH", Filter::loadNewMSH<N>},
        {"filterElements", Filter::filterElements<N>}
    };

    // Classify the operations.
    set<string> reductions = { "min", "max", "minMag", "maxMag",
                               "norm", "sum", "mean", "index" };
    set<string> unaryOps  = { "abs", "scale", "set" };
    set<string> binaryOps = { "add", "sub", "mul", "div" };

    // The following commands suppress automatic output of stack at exit when performed last
    set<string> suppressImplicitPrint = { "noprint", "print", "outMSH", "list" };

    // Apply-all makes sense only for the following operations:
    set<string> acceptsApplyAll = { "print", "printName", "eigenvalues",
                                    "vonMises", "frobeniusNorm", "sample" };
    acceptsApplyAll.insert(reductions.begin(), reductions.end());
    acceptsApplyAll.insert(  unaryOps.begin(),   unaryOps.end());
    acceptsApplyAll.insert( binaryOps.begin(),  binaryOps.end());

    // Implicit list operation when filters are empty
    if (filters.size() == 0) filters.push_back({"list", ""});

    // Add an implicit print operation unless it is supressed
    if (!suppressImplicitPrint.count(filters.back().first)) filters.push_back({"print", ""});

    Stack stack;
    for (size_t fi = 0; fi < filters.size(); ++fi) {
        try {
            Modifiers m; // fresh modifier flags
            runtime_error missingOperation("Modifier specified without an operation.");
            // Note: applyAll should appear *first*
            if (filters[fi].first == "applyAll")       { m.applyAll       = true; ++fi; }
            if (fi >= filters.size()) throw missingOperation;
            if (filters[fi].first == "outerReduction") { m.outerReduction = true; ++fi; }
            if (fi >= filters.size()) throw missingOperation;
            const auto &f = filters[fi];

            // Validate modifiers.
            if (m.outerReduction && (reductions.count(f.first) == 0))
                throw runtime_error("--outerReduction must be followed by reduction");
            if (m.applyAll && (acceptsApplyAll.count(f.first) == 0))
                throw runtime_error("operation does not support apply all");

            // Perform the filter either once or once per stack value.
            if (m.applyAll) {
                Stack newStack;
                while (stack.size()) {
                    size_t n = filterImplementations.at(f.first)(f.first, f.second, stack, m);
                    // Move each result over to the new stack
                    for (size_t r = 0; r < n; ++r)
                        newStack.emplace_back(popValue(stack));
                }
                std::reverse(newStack.begin(), newStack.end());
                stack = std::move(newStack);
            }
            else filterImplementations.at(f.first)(f.first, f.second, stack, m);
        }
        catch (const exception &e) {
            if (fi < filters.size())
                cout << "Filter '" << filters[fi].first << "' failed: " << e.what() << endl;
            else
                cout << "Filter failed: " << e.what() << endl;
            exit(-1);
        }
    }
}

int main(int argc, char *argv[])
{
    cout << std::scientific << std::setprecision(16);
    MeshIO::MeshIO_MSH io;
    vector<MeshIO::IOVertex>  v;
    vector<MeshIO::IOElement> e;

    string mshFile;
    vector<FilterInvocation> filters;
    auto forcedDim = boost::make_optional(false, size_t()); // work around maybe-uninitialized GCC warning bug
    std::tie(mshFile, filters, forcedDim) = parseCmdLine(argc, argv);

    ifstream infile(mshFile);
    if (!infile.is_open()) throw runtime_error("Couldn't open " + mshFile);
    MeshIO::MeshType type = io.load(infile, v, e, MeshIO::MESH_GUESS);
    size_t meshDim = ::MeshIO::meshDimension(type);

    size_t dim = forcedDim ? *forcedDim : meshDim;
    if (dim < 2 || dim > 3) throw std::runtime_error("Unsupported dimension: " + std::to_string(dim));

    if (meshDim != dim)
        cerr << "Warning: some operations won't work properly on non-full-dimension meshes" << endl;

    if (dim == 3) parseMSH<3>(infile, type, std::move(e), std::move(v), io.binary(), meshDim != dim);
    else          parseMSH<2>(infile, type, std::move(e), std::move(v), io.binary(), meshDim != dim);

    if (dim == 3) execute<3>(filters);
    else          execute<2>(filters);

    return 0;
}


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

/** \file ErrorTermWheelTest.cpp
    \brief This file tests the ErrorTermWheel class.
  */

#include <cmath>

#include <boost/make_shared.hpp>

#include <gtest/gtest.h>

#include <aslam/backend/test/ErrorTermTestHarness.hpp>
#include <aslam/backend/EuclideanPoint.hpp>

#include "aslam/calibration/error-terms/ErrorTermGyroscope.h"

using namespace aslam::backend;
using namespace aslam::calibration;

TEST(AslamCalibrationTestSuite, testErrorTermGyroscope) {
  /*ErrorTermGyroscope(const aslam::backend::EuclideanExpression& _i_w_mr,
                           const aslam::backend::EuclideanExpression& _bias,
                           const Input& wm, const Covariance& sigma2);*/
  EuclideanPoint i_w_mr(Eigen::Vector3d::Random());
  EuclideanPoint bias(Eigen::Vector3d::Random());

  ErrorTermGyroscope gerr(i_w_mr.toExpression(),
                          bias.toExpression(),
                          Eigen::Vector3d::Random(), Eigen::Matrix3d::Identity());

  // test the error terms
  try {
    ErrorTermTestHarness<1> harness(&gerr);
    harness.testAll(1e-5);
  }
  catch (const std::exception& e) {
    FAIL() << e.what();
  }

}


#include "TrendCalculator.h"
#include <boost/lexical_cast.hpp>
#include <newbase/NFmiGlobals.h>
#include <regression/tframe.h>
#include <iostream>
#include <string>

using namespace std;
using namespace boost;

namespace TrendCalculatorTest
{
// ----------------------------------------------------------------------
/*!
 * \brief Test TrendCalculator
 */
// ----------------------------------------------------------------------

void trend()
{
  using namespace TextGen;

  TrendCalculator calc;

  if (calc() != kFloatMissing) TEST_FAILED("Should return kFloatMissing after 0 inserts");

  calc(1);
  if (calc() != 0) TEST_FAILED("Should return 0 after 1 insert");

  calc(2);
  if (calc() != 100)
    TEST_FAILED("Failed to return 100 for [1,2], got " + lexical_cast<string>(calc()));

  calc(3);
  if (calc() != 100)
    TEST_FAILED("Failed to return 100 for [1,2,3], got " + lexical_cast<string>(calc()));

  calc(4);
  if (calc() != 100)
    TEST_FAILED("Failed to return 100 for [1,2,3,4], got " + lexical_cast<string>(calc()));

  calc(3);
  if (calc() != 50)
    TEST_FAILED("Failed to return 50 for [1,2,3,4,3], got " + lexical_cast<string>(calc()));

  calc(2);
  if (calc() != 20)
    TEST_FAILED("Failed to return 20 for [1,2,3,4,3,2], got " + lexical_cast<string>(calc()));

  calc(1);
  if (calc() != 0)
    TEST_FAILED("Failed to return 0 for [1,2,3,4,3,2,1], got " + lexical_cast<string>(calc()));

  calc(1);
  if (calc() != 0)
    TEST_FAILED("Failed to return 0 for [1,2,3,4,3,2,1,1], got " + lexical_cast<string>(calc()));

  calc(0);
  if (calc() != -12.5)
    TEST_FAILED("Failed to return -12.5 for [1,2,3,4,3,2,1,1,0], got " +
                lexical_cast<string>(calc()));

  TEST_PASSED();
}

// ----------------------------------------------------------------------
/*!
 * \brief The actual test driver
 */
// ----------------------------------------------------------------------

class tests : public tframe::tests
{
  //! Overridden message separator
  virtual const char* error_message_prefix() const { return "\n\t"; }
  //! Main test suite
  void test(void) { TEST(trend); }
};  // class tests

}  // namespace TrendCalculatorTest

int main(void)
{
  using namespace TrendCalculatorTest;

  cout << endl << "TrendCalculator tests" << endl << "=====================" << endl;

  tests t;
  return t.run();
}


#include <metaSMT/frontend/QF_BV.hpp>
#include <metaSMT/types/TypedSymbol.hpp>
#include <metaSMT/API/SymbolTable.hpp>
#include <boost/test/unit_test.hpp>
#include <string>

using namespace std;
using namespace metaSMT;
using namespace metaSMT::logic;
using namespace metaSMT::logic::QF_BV;
using namespace metaSMT::logic::Array;
using namespace metaSMT::type;
namespace proto = boost::proto;

BOOST_FIXTURE_TEST_SUITE(Types, Solver_Fixture )

BOOST_AUTO_TEST_CASE( init_typed_symbol ) {
  unsigned char const w = 32;

  TypedSymbol<ContextType> p(new_variable());
  TypedSymbol<ContextType> bv(new_bitvector(w), w);

  ContextType::result_type r = bv.eval(ctx);
  TypedSymbol<ContextType> expr(r, type::BitVector(w));

  BOOST_REQUIRE(  p.isBool() );
  BOOST_REQUIRE( !p.isBitVector() );
  BOOST_REQUIRE( !p.isArray() );
  BOOST_REQUIRE(  p.isPrimitiveBool() );
  BOOST_REQUIRE( !p.isPrimitiveBitVector() );
  BOOST_REQUIRE( !p.isPrimitiveArray() );
  BOOST_REQUIRE(  p.isPrimitive() );
  BOOST_REQUIRE( !p.isExpression() );

  BOOST_REQUIRE( !bv.isBool() );
  BOOST_REQUIRE(  bv.isBitVector() );
  BOOST_REQUIRE( !bv.isArray() );
  BOOST_REQUIRE( !bv.isPrimitiveBool() );
  BOOST_REQUIRE(  bv.isPrimitiveBitVector() );
  BOOST_REQUIRE( !bv.isPrimitiveArray() );
  BOOST_REQUIRE(  bv.isPrimitive() );
  BOOST_REQUIRE( !bv.isExpression() );

  BOOST_REQUIRE( !expr.isBool() );
  BOOST_REQUIRE(  expr.isBitVector() );
  BOOST_REQUIRE( !expr.isPrimitiveBool() );
  BOOST_REQUIRE( !expr.isPrimitiveBitVector() );
  BOOST_REQUIRE( !expr.isPrimitiveArray() );
  BOOST_REQUIRE( !expr.isPrimitive() );
  BOOST_REQUIRE(  expr.isExpression() );
}

BOOST_AUTO_TEST_CASE( init_typed_array_symbol ) {
  unsigned char const elem_width = 8;
  unsigned char const index_width = 4;

  TypedSymbol<ContextType> a1(new_array(elem_width, index_width),
                              elem_width, index_width);
  BOOST_REQUIRE( !a1.isBool() );
  BOOST_REQUIRE( !a1.isBitVector() );
  BOOST_REQUIRE(  a1.isArray() );
  BOOST_REQUIRE( !a1.isPrimitiveBool() );
  BOOST_REQUIRE( !a1.isPrimitiveBitVector() );
  BOOST_REQUIRE(  a1.isPrimitiveArray() );
  BOOST_REQUIRE( !a1.isExpression() );

  ContextType::result_type r = evaluate(ctx, new_array(elem_width, index_width));
  TypedSymbol<ContextType> a2(r, type::Array(elem_width, index_width));
  BOOST_REQUIRE( !a2.isBool() );
  BOOST_REQUIRE( !a2.isBitVector() );
  BOOST_REQUIRE(  a2.isArray() );
  BOOST_REQUIRE( !a2.isPrimitiveBool() );
  BOOST_REQUIRE( !a2.isPrimitiveBitVector() );
  BOOST_REQUIRE( !a2.isPrimitiveArray() );
  BOOST_REQUIRE(  a2.isExpression() );
}

BOOST_AUTO_TEST_CASE( check_bitwidth ) {
  unsigned char const w = 32;

  TypedSymbol<ContextType> bv(new_bitvector(w), w);
  BOOST_REQUIRE( w == boost::get<type::BitVector>(bv.type).width );

  ContextType::result_type r = bv.eval(ctx);
  TypedSymbol<ContextType> expr(r, type::BitVector(w));
  BOOST_REQUIRE( expr.isBitVector() );
  BOOST_REQUIRE( expr.isExpression() );
  BOOST_REQUIRE( !expr.isPrimitive() );
  BOOST_REQUIRE( w == boost::get<type::BitVector>(expr.type).width );

  type::BitVector bv_type = expr.getType(type::BitVector());
  BOOST_REQUIRE( w == bv_type.width );
}

BOOST_AUTO_TEST_CASE( check_array_widths ) {
  unsigned char const elem_width = 8;
  unsigned char const index_width = 4;

  TypedSymbol<ContextType> a(new_array(elem_width, index_width),
                             elem_width, index_width);
  BOOST_REQUIRE( elem_width == boost::get<type::Array>(a.type).elem_width );
  BOOST_REQUIRE( index_width == boost::get<type::Array>(a.type).index_width );

  ContextType::result_type r = a.eval(ctx);
  TypedSymbol<ContextType> expr(r, type::Array(elem_width, index_width));
  BOOST_REQUIRE( expr.isArray() );
  BOOST_REQUIRE( expr.isExpression() );
  BOOST_REQUIRE( !expr.isPrimitive() );
  BOOST_REQUIRE( elem_width == boost::get<type::Array>(expr.type).elem_width );
  BOOST_REQUIRE( index_width == boost::get<type::Array>(expr.type).index_width );

  type::Array array_type = expr.getType(type::Array());
  BOOST_REQUIRE( elem_width == array_type.elem_width );
  BOOST_REQUIRE( index_width == array_type.index_width );
}

BOOST_AUTO_TEST_CASE( get_id ) {
  unsigned char const w = 32;

  predicate p_primitive = new_variable();
  bitvector bv_primitive = new_bitvector(w);

  TypedSymbol<ContextType> p(p_primitive);
  TypedSymbol<ContextType> bv(bv_primitive, w);

  ContextType::result_type r = bv.eval(ctx);
  TypedSymbol<ContextType> expr(r, type::BitVector(w));

  BOOST_REQUIRE( p.isPrimitiveBool() );
  if ( p.isPrimitiveBool() ) {
    predicate p_val = boost::get<predicate>(p.value);
    BOOST_REQUIRE( boost::proto::value( p_val ).id == boost::proto::value( p_primitive ).id );
  }

  BOOST_REQUIRE( bv.isPrimitiveBitVector() );
  if ( bv.isPrimitiveBitVector() ) {
    bitvector bv_val = boost::get<bitvector>(bv.value);
    BOOST_REQUIRE( boost::proto::value( bv_val ).id == boost::proto::value( bv_primitive ).id );
  }
}

BOOST_AUTO_TEST_CASE( get_array_id ) {
  unsigned char const elem_width = 8;
  unsigned char const index_width = 4;

  array a_primitive = new_array(elem_width, index_width);

  TypedSymbol<ContextType> a(a_primitive, elem_width, index_width);

  ContextType::result_type r = a.eval(ctx);
  TypedSymbol<ContextType> expr(r, type::Array(elem_width, index_width));

  BOOST_REQUIRE( a.isPrimitiveArray() );
  if ( a.isPrimitiveArray() ) {
    array a_val = boost::get<array>(a.value);
    BOOST_REQUIRE( boost::proto::value( a_val ).id == boost::proto::value( a_primitive ).id );
  }
}

BOOST_AUTO_TEST_CASE( conversion ) {
  unsigned char const w = 32;

  predicate p_primitive = new_variable();
  TypedSymbol<ContextType> p(p_primitive);

  bitvector bv_primitive_zext = new_bitvector(w);
  TypedSymbol<ContextType> bv_zext(bv_primitive_zext, w);
  metaSMT::assertion(ctx, logic::equal(type::detail::to_bool(ctx, bv_zext), p_primitive));
  metaSMT::assertion(ctx, logic::equal(zero_extend(w-1, type::detail::to_bitvector(ctx, p)), bv_primitive_zext));
  metaSMT::assertion(ctx, logic::equal(bv_zext.toBool(ctx), p_primitive));
  metaSMT::assertion(ctx, logic::equal(p.toBV(bv::tag::zero_extend_tag(), ctx, w), bv_primitive_zext));
  solve( ctx );

  bitvector bv_primitive_sext = new_bitvector(w);
  TypedSymbol<ContextType> bv_sext(bv_primitive_sext, w);
  metaSMT::assertion(ctx, logic::equal(type::detail::to_bool(ctx, bv_sext), p_primitive));
  metaSMT::assertion(ctx, logic::equal(zero_extend(w-1, type::detail::to_bitvector(ctx, p)), bv_primitive_sext));
  metaSMT::assertion(ctx, logic::equal(bv_sext.toBool(ctx), p_primitive));
  metaSMT::assertion(ctx, logic::equal(p.toBV(bv::tag::sign_extend_tag(), ctx, w), bv_primitive_sext));
  solve( ctx );
}

BOOST_AUTO_TEST_CASE( array_conversion ) {
  unsigned char const elem_width = 8;
  unsigned char const index_width = 2;
  array a_primitive = new_array(elem_width, index_width);
  TypedSymbol<ContextType> a(a_primitive, elem_width, index_width);

  predicate p = new_variable();
  metaSMT::assertion(ctx, logic::equal(p, a.toBool(ctx)));
  BOOST_REQUIRE( solve( ctx ) );

  bitvector bv1 = new_bitvector(elem_width*(1 << index_width));
  metaSMT::assertion(ctx, logic::equal(bv1, a.toBV(ctx)));
  BOOST_REQUIRE( solve( ctx ) );

  unsigned char const offsett = 32;
  unsigned char const ext_size = (1 << index_width)*elem_width+offsett;
  bitvector bv2 = new_bitvector(ext_size);
  metaSMT::assertion(ctx, logic::equal(bv2, a.toBV(bv::tag::zero_extend_tag(), ctx, ext_size)));
  BOOST_REQUIRE( solve( ctx ) );
}

BOOST_AUTO_TEST_CASE( evaluate_symbol ) {
  predicate p = new_variable();
  TypedSymbol<ContextType> a(new_variable());
  bool ab;

  metaSMT::assumption(ctx, logic::equal(true, a));
  BOOST_REQUIRE( solve( ctx ) );
  ab = read_value(ctx, a.toBool(ctx));
  BOOST_CHECK_EQUAL(true, ab);

  metaSMT::assumption(ctx, logic::equal(false, a));
  BOOST_REQUIRE( solve( ctx ) );
  ab = read_value(ctx, a.toBool(ctx));
  BOOST_CHECK_EQUAL(false, ab);
}

BOOST_AUTO_TEST_SUITE_END() // Types

//  vim: ft=cpp:ts=2:sw=2:expandtab


// Copyright Nick Thompson, 2019
// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_MATH_INTERPOLATORS_WHITAKKER_SHANNON_DETAIL_HPP
#define BOOST_MATH_INTERPOLATORS_WHITAKKER_SHANNON_DETAIL_HPP
#include <boost/assert.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/math/special_functions/sin_pi.hpp>
#include <boost/math/special_functions/cos_pi.hpp>

namespace boost { namespace math { namespace interpolators { namespace detail {

template<class RandomAccessContainer>
class whittaker_shannon_detail {
public:

    using Real = typename RandomAccessContainer::value_type;
    whittaker_shannon_detail(RandomAccessContainer&& y, Real const & t0, Real const & h) : m_y{std::move(y)}, m_t0{t0}, m_h{h}
    {
        for (size_t i = 1; i < m_y.size(); i += 2)
        {
            m_y[i] = -m_y[i];
        }
    }

    inline Real operator()(Real t) const {
        using boost::math::constants::pi;
        using std::isfinite;
        using std::floor;
        Real y = 0;
        Real x = (t - m_t0)/m_h;
        Real z = x;
        auto it = m_y.begin();

        // For some reason, neither clang nor g++ will cache the address of m_y.end() in a register.
        // Hence make a copy of it:
        auto end = m_y.end();
        while(it != end)
        {

            y += *it++/z;
            z -= 1;
        }

        if (!isfinite(y))
        {
            BOOST_ASSERT_MSG(floor(x) == ceil(x), "Floor and ceiling should be equal.\n");
            size_t i = static_cast<size_t>(floor(x));
            if (i & 1)
            {
                return -m_y[i];
            }
            return m_y[i];
        }
        return y*boost::math::sin_pi(x)/pi<Real>();
    }

    Real prime(Real t) const {
        using boost::math::constants::pi;
        using std::isfinite;
        using std::floor;

        Real x = (t - m_t0)/m_h;
        if (ceil(x) == x) {
            Real s = 0;
            long j = static_cast<long>(x);
            long n = m_y.size();
            for (long i = 0; i < n; ++i)
            {
                if (j - i != 0)
                {
                    s += m_y[i]/(j-i);
                }
                // else derivative of sinc at zero is zero.
            }
            if (j & 1) {
                s /= -m_h;
            } else {
                s /= m_h;
            }
            return s;
        }
        Real z = x;
        auto it = m_y.begin();
        Real cospix = boost::math::cos_pi(x);
        Real sinpix_div_pi = boost::math::sin_pi(x)/pi<Real>();

        Real s = 0;
        auto end = m_y.end();
        while(it != end)
        {
            s += (*it++)*(z*cospix - sinpix_div_pi)/(z*z);
            z -= 1;
        }

        return s/m_h;
    }

    Real operator[](size_t i) const {
        if (i & 1)
        {
            return -m_y[i];
        }
        return m_y[i];
    }

    RandomAccessContainer&& return_data() {
        for (size_t i = 1; i < m_y.size(); i += 2)
        {
            m_y[i] = -m_y[i];
        }
        return std::move(m_y);
    }

private:
    RandomAccessContainer m_y;
    Real m_t0;
    Real m_h;
};
}}}}
#endif


/**
 *  @copyright Copyright 2018 The J-PET Framework Authors. All rights reserved.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may find a copy of the License in the LICENCE file.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *  @file JPetSmearingFunctionsTest.cpp
 */

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

#include "JPetSmearingFunctions/JPetSmearingFunctions.h"

#include <TFile.h>
#include <TH1F.h>

using SmearingType = JPetHitExperimentalParametrizer::SmearingType;

BOOST_AUTO_TEST_SUITE(FirstSuite)

BOOST_AUTO_TEST_CASE(testDefaultLimits)
{
  double epsilon = 0.001;
  JPetHitExperimentalParametrizer parametrizer;
  auto limits = parametrizer.getSmearingFunctionLimits();
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kTime].first, -300, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kTime].second, 300, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kEnergy].first, -100, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kEnergy].second, 100, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kZPosition].first, -5, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kZPosition].second, 5, epsilon);
}

BOOST_AUTO_TEST_CASE(testCustomLimits)
{
  double epsilon = 0.001;
  JPetHitExperimentalParametrizer parametrizer;
  parametrizer.setSmearingFunctionLimits({{-1, 1}, {-2, 2}, {-3, 3}});
  auto limits = parametrizer.getSmearingFunctionLimits();
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kTime].first, -1, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kTime].second, 1, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kEnergy].first, -2, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kEnergy].second, 2, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kZPosition].first, -3, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kZPosition].second, 3, epsilon);
}

BOOST_AUTO_TEST_CASE(testCustomLimits2)
{
  double epsilon = 0.001;
  JPetHitExperimentalParametrizer parametrizer;
  /// The second argument has the same upper and lower limit.
  ///  and so the energy smearing function should preserve original values.
  parametrizer.setSmearingFunctionLimits({{-1, 1}, {-2, -2}, {-3, 3}});
  auto limits = parametrizer.getSmearingFunctionLimits();
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kTime].first, -1, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kTime].second, 1, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kEnergy].first, -100, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kEnergy].second, 100, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kZPosition].first, -3, epsilon);
  BOOST_REQUIRE_CLOSE(limits[SmearingType::kZPosition].second, 3, epsilon);
}

BOOST_AUTO_TEST_CASE(testDefaultZFunction)
{
  JPetHitExperimentalParametrizer parametrizer;
  double mean = 1;
  double sigma = 0.976; /// predefined sigma.
  TF1 refFunc("refFunc", "TMath::Gaus(x,[0],[1],1)", -4, 6);
  refFunc.SetParameter(0, mean);
  refFunc.SetParameter(1, sigma);

  const int nTrials = 10000;
  std::vector<double> vals;
  std::vector<double> valsRef;
  vals.reserve(nTrials);
  valsRef.reserve(nTrials);
  for (int i = 0; i < nTrials; i++)
  {
    vals.push_back(parametrizer.addZHitSmearing(0, mean, 0, -99999));
    valsRef.push_back(refFunc.GetRandom());
  }
  std::sort(vals.begin(), vals.end());
  std::sort(valsRef.begin(), valsRef.end());
  auto prob = TMath::KolmogorovTest(vals.size(), &vals[0], valsRef.size(), &valsRef[0], "");
  double alpha = 0.05;
  BOOST_REQUIRE(prob > alpha);
}

BOOST_AUTO_TEST_CASE(testDefaultZFunctionWithCustomSigma)
{
  JPetHitExperimentalParametrizer parametrizer;
  double mean = 1;
  double sigma = 3;

  TF1 refFunc("refFunc2", "TMath::Gaus(x,[0],[1],1)", -4, 6);
  refFunc.SetParameter(0, mean);
  refFunc.SetParameter(1, sigma);

  parametrizer.setSmearingFunctions({{"", {}}, {"", {}}, {"", {sigma}}});

  const int nTrials = 10000;
  std::vector<double> vals;
  std::vector<double> valsRef;
  vals.reserve(nTrials);
  valsRef.reserve(nTrials);
  for (int i = 0; i < nTrials; i++)
  {
    vals.push_back(parametrizer.addZHitSmearing(0, mean, 0, -99999));
    valsRef.push_back(refFunc.GetRandom());
  }
  std::sort(vals.begin(), vals.end());
  std::sort(valsRef.begin(), valsRef.end());
  auto prob = TMath::KolmogorovTest(vals.size(), &vals[0], valsRef.size(), &valsRef[0], "");
  double alpha = 0.05;
  BOOST_REQUIRE(prob > alpha);
}

BOOST_AUTO_TEST_CASE(testCustomZFunction)
{
  JPetHitExperimentalParametrizer parametrizer;

  /// default params are [0] scinId, [1] zIn, [2] eneIn, [3] timeIn
  /// we add here p[4] sigma of Landau
  std::string zSmearing = "[&](double* x, double* p)->double{ return TMath::Landau(x[0],p[1],p[4], 0);};";

  double mpv = 0; /// most probable value of Landau ~~ "mean"
  double sigma = 2;
  /// we pass only the additional parameters
  parametrizer.setSmearingFunctions({{"", {}}, {"", {}}, {zSmearing, {sigma}}});
  parametrizer.setSmearingFunctionLimits({{0, 0}, {0, 0}, {-4, 4}});

  TF1 refFunc("refFunc3", "TMath::Landau(x,[0],[1],0)", -4, 4);
  refFunc.SetParameter(0, mpv);
  refFunc.SetParameter(1, sigma);

  const int nTrials = 100000;
  std::vector<double> vals;
  std::vector<double> valsRef;
  vals.reserve(nTrials);
  valsRef.reserve(nTrials);
  for (int i = 0; i < nTrials; i++)
  {
    double res = parametrizer.addZHitSmearing(0, mpv, 0, -9999);
    double res2 = refFunc.GetRandom();
    vals.push_back(res);
    valsRef.push_back(res2);
  }
  std::sort(vals.begin(), vals.end());
  std::sort(valsRef.begin(), valsRef.end());
  auto prob = TMath::KolmogorovTest(vals.size(), &vals[0], valsRef.size(), &valsRef[0], "");
  double alpha = 0.05;
  BOOST_REQUIRE(prob > alpha);
}

BOOST_AUTO_TEST_SUITE_END()


/*************************************************************************************************/

/*

    ADOBE CONFIDENTIAL
    ___________________

    Copyright 2001 - 2004 Adobe Systems Incorporated
    All Rights Reserved.

    NOTICE: All information contained herein is, and remains the property of
    Adobe Systems Incorporated and its suppliers, if any. The intellectual and
    technical concepts contained herein are proprietary to Adobe Systems Incorporated
    and its suppliers and may be covered by U.S. and Foreign Patents, patents
    in process, and are protected by trade secret or copyright law. Dissemination
    of this information or reproduction of this material is strictly forbidden
    unless prior written permission is obtained from Adobe Systems Incorporated.

*/

/*************************************************************************************************/

// start_of_example

#include <boost/detail/lightweight_test.hpp>
#include <adobe/enum_ops.hpp>

enum Foo
    {
    foo_4 = 1 << 2,
    foo_8 = 1 << 3
    };

enum Bar
    {
    bar_4 = 1 << 2,
    bar_8 = 1 << 3
    };

enum Baz
    {
    baz_4 = 1 << 2,
    baz_8 = 1 << 3
    };

ADOBE_DEFINE_ARITHMETIC_OPS(Baz)

//ADOBE_DEFINE_ARITHMETIC_OPS is not defined for Foo and Bar

int main()
    {
    Foo     a(foo_4);
    Bar     b(bar_8);
    Foo     c(a + b);   // error! a and b are different enum types, so result is integral

    Baz     d(baz_4);
    Baz     e(baz_8);
    Baz     f(d + e);   // Ok, ADOBE_DEFINE_ARITHMETIC_OPS(Baz) is defined, so d + e has type Baz

    BOOST_TEST(c);
    BOOST_TEST(f);

    return boost::report_errors();
    }

// end_of_example


#include "qdtree/listnode.hxx"
#include "qdtree/singlenode.hxx"
#include "qdtree/qdtree.hxx"

#include <vector>
#include <cstdlib>
#include <cstdio>
#include <chrono>

#include <benchmark/benchmark.h>

#include <foonathan/memory/memory_pool.hpp>
#include <foonathan/memory/std_allocator.hpp>

#define BOOST_POOL_NO_MT
#include <boost/pool/pool_alloc.hpp>

// The purpose of this test is:
// - to verify that it is possible to build a QDTree of values,
// references and pointers.
// - to evaluate the impact of allocators.

void _ensure(const char* expression, const char* file, int line)
{
  fprintf(stderr, "Assertion '%s' failed at '%s:%d'.\n", expression, file, line);
  abort();
}

#define ensure(EXPRESSION) ((EXPRESSION) ? (void)0 : _ensure(#EXPRESSION, __FILE__, __LINE__))

namespace my
{
class Point {
public:
  Point(double x, double y)
    : mX(x), mY(y), mTouched(false) {}

  double x() const { return mX; }
  double y() const { return mY; }

  void setX(double x) { mX = x; }
  void setY(double y) { mY = y; }

  void touch() { mTouched = true; }

private:
  double mX, mY;
  bool mTouched;
};

std::ostream& operator<<(std::ostream& out, const Point& p) {
  return out << "(" << p.x() << "; " << p.y() << ")";
}

bool operator==(const Point& lhs, const Point& rhs) {
  return lhs.x() == rhs.x() && lhs.y() == rhs.y();
}
} // namespace my

struct XYPointerAccessor {
  using value_type = double;
  value_type operator()(const my::Point* p, const size_t i) const
  {
    if (i == 0) return p->x();
    else return p->y();
  }
};

struct XYRefAccessor {
  using value_type = double;
  value_type operator()(const my::Point& p, const size_t i) const
  {
    if (i == 0) return p.x();
    else return p.y();
  }
};

using VTree = qdtree::QDTree<qdtree::ListNode<2, my::Point>, XYRefAccessor>;
using RTree = qdtree::QDTree<qdtree::ListNode<2, std::reference_wrapper<my::Point>>, XYRefAccessor>;
using PTree = qdtree::QDTree<qdtree::ListNode<2, const my::Point*>, XYPointerAccessor>;

using FPAllocator = foonathan::memory::std_allocator<
  PTree::node_type,
  foonathan::memory::memory_pool<>>;

using PFATree = qdtree::QDTree<qdtree::ListNode<2, const my::Point*>,
                               XYPointerAccessor,
                               FPAllocator>;

using BPAllocator =  boost::fast_pool_allocator<
    PTree::node_type,
    boost::default_user_allocator_new_delete,
    boost::details::pool::default_mutex
>;

using PBATree = qdtree::QDTree<qdtree::ListNode<2, const my::Point*>,
                               XYPointerAccessor,
                               BPAllocator>;

template<typename T>
T make_tree(size_t N) {
  T t;
  t.cover({0.0, 0.0});
  t.cover({double(N), double(N)});
  return t;
}

std::vector<my::Point> make_points(size_t N) {
  std::vector<my::Point> points;
  points.reserve(N*N);

  for(size_t y = 0; y < N; ++y)
    for(size_t x = 0; x < N; ++x)
      points.emplace_back((double)x, (double)y);

  return points;
}

void bm_value(benchmark::State& state)
{
  size_t N = state.range(0);

  auto points = make_points(N);

  for (auto _ : state)
  {
    VTree t = make_tree<VTree>(N);
    for(const my::Point& p : points)
      t.unsafe_add(p);
  }

  VTree t = make_tree<VTree>(N);
  for(const my::Point& p : points)
    t.unsafe_add(p);

  {
    typename VTree::node_type::data_pointer_type n = t.find({double(N-1), double(N-1)});
    ensure(n != nullptr && (*n).front() == my::Point({double(N-1), double(N-1)}));
    (*n).front().touch();
  }

  {
    const typename VTree::node_type::data_pointer_type n = static_cast<const VTree&>(t).find({0.0, 0.0});
    ensure(n != nullptr && (*n).front() == my::Point({0.0, 0.0}));
    // Compile error, n is a pointer to a _constant Point_.
    //(*n).front().touch();
  }
}
BENCHMARK(bm_value)->Arg(10)->Arg(25)->Arg(50);

void bm_ref(benchmark::State& state)
{
  size_t N = state.range(0);

  auto points = make_points(N);

  for (auto _ : state)
  {
    RTree t = make_tree<RTree>(N);
    for(my::Point& p : points)
      t.unsafe_add(p);
  }

  RTree t = make_tree<RTree>(N);
  for(my::Point& p : points)
    t.unsafe_add(p);

  {
    typename RTree::node_type::data_pointer_type n = t.find({double(N-1), double(N-1)});
    ensure(n != nullptr && &((*n).front().get()) == &points.back());
    (*n).front().get().touch();
    (*n).front() = points.front();
  }

  {
    typename RTree::node_type::data_pointer_type n = static_cast<const RTree&>(t).find({0.0, 0.0});
    ensure(n != nullptr && (*n).front().get() == my::Point({0.0, 0.0}));
    (*n).front().get().touch();
    // Compile error, n is a pointer to a _constant reference_wrapper_.
    //(*n).front() = points.front();
  }

  // In both cases (const and non-const) it's possible to edit the retrieved
  // Point. Use std::reference_wrapper<const Point> to prevent it.
}
BENCHMARK(bm_ref)->Arg(10)->Arg(25)->Arg(50);

void bm_pointer(benchmark::State& state)
{
  size_t N = state.range(0);

  auto points = make_points(N);

  for (auto _ : state)
  {
    PTree t = make_tree<PTree>(N);
    for(my::Point& p : points)
      t.unsafe_add(&p);
  }

  PTree t = make_tree<PTree>(N);
  for(my::Point& p : points)
    t.unsafe_add(&p);

  typename PTree::node_type::data_pointer_type n = t.find({double(N-1), double(N-1)});
  ensure(n != nullptr && (*n).front() == &points.back());

  //(*n)->setX(42);
  // Compile error, n is a pointer to a constant pointer to a _constant Point_.
  // It would compile with QDTree<_, Point*, _>.
}
BENCHMARK(bm_pointer)->Arg(10)->Arg(25)->Arg(50);

void bm_pointer_foo_allocator(benchmark::State& state)
{
  size_t N = state.range(0);

  auto points = make_points(N);

  for (auto _ : state)
  {
    foonathan::memory::memory_pool<> pool(sizeof(PTree::node_type), 4096);
    FPAllocator alloc(pool);
    PFATree t(alloc);
    t.cover({0.0, 0.0});
    t.cover({double(N), double(N)});

    for(my::Point& p : points)
      t.unsafe_add(&p);
  }
}
BENCHMARK(bm_pointer_foo_allocator)->Arg(10)->Arg(25)->Arg(50);

void bm_pointer_boost_allocator(benchmark::State& state)
{
  size_t N = state.range(0);

  auto points = make_points(N);

  for (auto _ : state)
  {
    {
      PBATree t = make_tree<PBATree>(N);

      for(my::Point& p : points)
        t.unsafe_add(&p);
    }

    boost::singleton_pool<
        boost::fast_pool_allocator_tag,
        56u,
        boost::default_user_allocator_new_delete,
        boost::details::pool::null_mutex,
        32u,
        0u>::purge_memory();
  }
}
BENCHMARK(bm_pointer_boost_allocator)->Arg(10)->Arg(25)->Arg(50);

void bm_single_value(benchmark::State& state)
{
  size_t N = state.range(0);

  auto points = make_points(N);

  using Tree = qdtree::QDTree<qdtree::SingleNode<2, my::Point>, XYRefAccessor>;

  for (auto _ : state)
  {
    Tree t = make_tree<Tree>(N);

    for(const my::Point& p : points)
      t.unsafe_add(p);
  }

  Tree t = make_tree<Tree>(N);

  for(const my::Point& p : points)
    t.unsafe_add(p);

  {
    my::Point* n = t.find({double(N-1), double(N-1)});
    ensure(n != nullptr && *n == my::Point({double(N-1), double(N-1)}));
    n->touch();
  }

  {
    const my::Point* n = static_cast<const Tree&>(t).find({0.0, 0.0});
    ensure(n != nullptr && *n == my::Point({0.0, 0.0}));
    // Compile error, n is a pointer to a _constant Point_.
    //n->touch();
  }
}
BENCHMARK(bm_single_value)->Arg(10)->Arg(25)->Arg(50);

template <typename T, typename U>
struct NumericalAccessor
{
  using value_type = U;

  U operator()(const T& v, const size_t) const { return (U)v; }
};

void bm_1d_deletion_queue(benchmark::State& state)
{
  size_t N = state.range(0);

  qdtree::QDTree<qdtree::SingleNode<1, int>, NumericalAccessor<int, double>> t;
  t.cover({{0}});
  t.cover({{double(N)}});
  for(size_t i = 0; i < N; ++i)
    t.unsafe_add(i);

  for (auto _ : state)
  {
    state.PauseTiming();
    qdtree::SingleNode<1, int>* node = t.clone_node(*t.root());
    state.ResumeTiming();
    t.destroy_node_queue(node);
  }
}
BENCHMARK(bm_1d_deletion_queue)->Arg(10)->Arg(100)->Arg(1000)->Arg(10000)->Arg(100000)->Arg(1000000);

void bm_1d_deletion_morris(benchmark::State& state)
{
  size_t N = state.range(0);

  qdtree::QDTree<qdtree::SingleNode<1, int>, NumericalAccessor<int, double>> t;
  t.cover({{0}});
  t.cover({{double(N)}});
  for(size_t i = 0; i < N; ++i)
    t.unsafe_add(i);

  for (auto _ : state)
  {
    state.PauseTiming();
    qdtree::SingleNode<1, int>* node = t.clone_node(*t.root());
    state.ResumeTiming();
    t.destroy_node_morris(node);
  }
}
BENCHMARK(bm_1d_deletion_morris)->Arg(10)->Arg(100)->Arg(1000)->Arg(10000)->Arg(100000)->Arg(1000000);

void bm_1d_deletion_morris_n(benchmark::State& state)
{
  size_t N = state.range(0);

  qdtree::QDTree<qdtree::SingleNode<1, int>, NumericalAccessor<int, double>> t;
  t.cover({{0}});
  t.cover({{double(N)}});
  for(size_t i = 0; i < N; ++i)
    t.unsafe_add(i);

  for (auto _ : state)
  {
    state.PauseTiming();
    qdtree::SingleNode<1, int>* node = t.clone_node(*t.root());
    state.ResumeTiming();
    t.destroy_node_morris_n(node);
  }
}
BENCHMARK(bm_1d_deletion_morris_n)->Arg(10)->Arg(100)->Arg(1000)->Arg(10000)->Arg(100000)->Arg(1000000);

void bm_2d_deletion_queue(benchmark::State& state)
{
  size_t N = state.range(0);
  auto points = make_points(N);

  VTree t = make_tree<VTree>(N);
  for(const my::Point& p : points)
    t.unsafe_add(p);

  for (auto _ : state)
  {
    state.PauseTiming();
    VTree::node_type *node = t.clone_node(*t.root());
    state.ResumeTiming();

    t.destroy_node_queue(node);
  }
}
BENCHMARK(bm_2d_deletion_queue)->Arg(10)->Arg(25)->Arg(50)->Arg(100)->Arg(500)->Arg(1000);

void bm_2d_deletion_morris(benchmark::State& state)
{
  size_t N = state.range(0);
  auto points = make_points(N);

  VTree t = make_tree<VTree>(N);
  for(const my::Point& p : points)
    t.unsafe_add(p);

  for (auto _ : state)
  {
    state.PauseTiming();

    // For some reason, morris destruction might cause the memory manager to loose
    // its mind (Debian 9, GCC 6.3, Kernel 4.9). Nodes appear to be released in a
    // more random order, causing the memory to appear fragmented. In each iteration
    // allocations will take longer and longer
    // This big allocation makes allocation time constant again.
    delete[] new int[100000000];

    // Catching the above issue with this benchmark would have been easy if google-benchmark
    // was able to compute the standard deviation along with the average time.
    // Until this is possible, running the test again with a longer --benchmark_min_time should
    // highlight such issue.

    VTree::node_type *node = t.clone_node(*t.root());

    state.ResumeTiming();

    t.destroy_node_morris_n(node);
  }
}
BENCHMARK(bm_2d_deletion_morris)->Arg(10)->Arg(25)->Arg(50)->Arg(100)->Arg(500)->Arg(1000);

BENCHMARK_MAIN();


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#include <boost/simd/include/functions/stream.hpp>

#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/relation.hpp>

NT2_TEST_CASE_TPL(stream, BOOST_SIMD_TYPES)
{
  using boost::simd::stream;

  T data[5];

  for(boost::simd::int32_t i=0;i<5;++i)
    stream(static_cast<T>(2-i),&data[i]);

  NT2_TEST_EQUAL( data[0], T(2)  );
  NT2_TEST_EQUAL( data[1], T(1)  );
  NT2_TEST_EQUAL( data[2], T(0)  );
  NT2_TEST_EQUAL( data[3], T(-1) );
  NT2_TEST_EQUAL( data[4], T(-2) );
}

NT2_TEST_CASE_TPL(stream_offset, BOOST_SIMD_TYPES)
{
  using boost::simd::stream;

  T data[5];

  for(boost::simd::int32_t i=0;i<5;++i)
    stream(static_cast<T>(2-i),&data[0],i);

  NT2_TEST_EQUAL( data[0], T(2)  );
  NT2_TEST_EQUAL( data[1], T(1)  );
  NT2_TEST_EQUAL( data[2], T(0)  );
  NT2_TEST_EQUAL( data[3], T(-1) );
  NT2_TEST_EQUAL( data[4], T(-2) );
}