#include <cmath>
#include <algorithm>

#include "boost/geometry/geometry.hpp"
#include <boost/container_hash/hash.hpp>
#include <memory>

#include "lsst/pex/exceptions.h"
#include "lsst/geom/Extent.h"
#include "lsst/afw/geom/polygon/Polygon.h"

#include "lsst/afw/table/io/OutputArchive.h"
#include "lsst/afw/table/io/InputArchive.h"
#include "lsst/afw/table/io/CatalogVector.h"
#include "lsst/afw/table/aggregates.h"
#include "lsst/afw/table/io/Persistable.hpp"

typedef lsst::afw::geom::polygon::Polygon::Point LsstPoint;
typedef lsst::afw::geom::polygon::Polygon::Box LsstBox;
typedef std::vector<LsstPoint> LsstRing;
typedef boost::geometry::model::polygon<LsstPoint> BoostPolygon;
typedef boost::geometry::model::box<LsstPoint> BoostBox;
typedef boost::geometry::model::linestring<LsstPoint> BoostLineString;

namespace boost {
namespace geometry {
namespace traits {

// Setting up LsstPoint
template <>
struct tag<LsstPoint> {
    typedef point_tag type;
};
template <>
struct coordinate_type<LsstPoint> {
    typedef LsstPoint::Element type;
};
template <>
struct coordinate_system<LsstPoint> {
    typedef cs::cartesian type;
};
template <>
struct dimension<LsstPoint> : boost::mpl::int_<2> {};
template <std::size_t dim>
struct access<LsstPoint, dim> {
    static double get(LsstPoint const& p) { return p[dim]; }
    static void set(LsstPoint& p, LsstPoint::Element const& value) { p[dim] = value; }
};

// Setting up LsstBox
//
// No setters, because it's inefficient (can't set individual elements of lsst::geom::Box2D directly).
// For box outputs from boost::geometry we'll use BoostBox and then convert.
template <>
struct tag<LsstBox> {
    typedef box_tag type;
};
template <>
struct point_type<LsstBox> {
    typedef LsstPoint type;
};
template <>
struct indexed_access<LsstBox, 0, 0> {
    static double get(LsstBox const& box) { return box.getMinX(); }
};
template <>
struct indexed_access<LsstBox, 1, 0> {
    static double get(LsstBox const& box) { return box.getMaxX(); }
};
template <>
struct indexed_access<LsstBox, 0, 1> {
    static double get(LsstBox const& box) { return box.getMinY(); }
};
template <>
struct indexed_access<LsstBox, 1, 1> {
    static double get(LsstBox const& box) { return box.getMaxY(); }
};

// Setting up LsstRing
template <>
struct tag<LsstRing> {
    typedef ring_tag type;
};
// template<> struct range_value<LsstRing> { typedef LsstPoint type; };
}  // namespace traits
}  // namespace geometry
}  // namespace boost

namespace {

/// @internal Convert BoostBox to LsstBox
LsstBox boostBoxToLsst(BoostBox const& box) { return LsstBox(box.min_corner(), box.max_corner()); }

/// @internal Convert box to corners
std::vector<LsstPoint> boxToCorners(LsstBox const& box) {
    std::vector<LsstPoint> corners;
    corners.reserve(4);
    corners.push_back(box.getMin());
    corners.push_back(LsstPoint(box.getMaxX(), box.getMinY()));
    corners.push_back(box.getMax());
    corners.push_back(LsstPoint(box.getMinX(), box.getMaxY()));
    return corners;
}

/**
 * @internal Sub-sample a line
 *
 * Add `num` points to `vector` between `first` and `second`
 */
void addSubSampledEdge(std::vector<LsstPoint>& vertices,  // Vector of points to which to add
                       LsstPoint const& first,            // First vertex defining edge
                       LsstPoint const& second,           // Second vertex defining edge
                       size_t const num                   // Number of parts to divide edge into
) {
    lsst::geom::Extent2D const delta = (second - first) / num;
    vertices.push_back(first);
    for (size_t i = 1; i < num; ++i) {
        vertices.push_back(first + delta * i);
    }
}

/// @internal Calculate area of overlap between polygon and pixel
double pixelOverlap(BoostPolygon const& poly, int const x, int const y) {
    std::vector<BoostPolygon> overlap;  // Overlap between pixel and polygon
    LsstBox const pixel(lsst::geom::Point2D(x - 0.5, y - 0.5), lsst::geom::Point2D(x + 0.5, y + 0.5));
    boost::geometry::intersection(poly, pixel, overlap);
    double area = 0.0;
    for (std::vector<BoostPolygon>::const_iterator i = overlap.begin(); i != overlap.end(); ++i) {
        double const polyArea = boost::geometry::area(*i);
        area += std::min(polyArea, 1.0);  // remove any rounding error
    }
    return area;
}

/// @internal Set each pixel in a row to the amount of overlap with polygon
void pixelRowOverlap(std::shared_ptr<lsst::afw::image::Image<float>> const image, BoostPolygon const& poly,
                     int const xStart, int const xStop, int const y) {
    int x = xStart;
    for (lsst::afw::image::Image<float>::x_iterator i = image->x_at(x - image->getX0(), y - image->getY0());
         x <= xStop; ++i, ++x) {
        *i = pixelOverlap(poly, x, y);
    }
}

}  // anonymous namespace

namespace lsst {
namespace afw {

template std::shared_ptr<geom::polygon::Polygon> table::io::PersistableFacade<
        geom::polygon::Polygon>::dynamicCast(std::shared_ptr<table::io::Persistable> const&);

namespace geom {
namespace polygon {

/// @internal Stream vertices
std::ostream& operator<<(std::ostream& os, std::vector<LsstPoint> const& vertices) {
    os << "[";
    size_t num = vertices.size();
    for (size_t i = 0; i < num - 1; ++i) {
        os << vertices[i] << ",";
    }
    os << vertices[vertices.size() - 1] << "]";
    return os;
}

/// @internal Stream BoostPolygon
std::ostream& operator<<(std::ostream& os, BoostPolygon const& poly) {
    return os << "BoostPolygon(" << poly.outer() << ")";
}

std::ostream& operator<<(std::ostream& os, Polygon const& poly) {
    os << poly.toString();
    return os;
}

struct Polygon::Impl {
    Impl() : poly() {}
    explicit Impl(Polygon::Box const& box) : poly() {
        boost::geometry::assign(poly, box);
        // Assignment from a box is correctly handled by BoostPolygon, so doesn't need a "check()"
    }
    explicit Impl(std::vector<LsstPoint> const& vertices) : poly() {
        boost::geometry::assign(poly, vertices);
        check();  // because the vertices might not have the correct orientation (CW vs CCW) or be open
    }
    explicit Impl(BoostPolygon const& _poly) : poly(_poly) {}

    void check() { boost::geometry::correct(poly); }

    /// @internal Convert collection of Boost polygons to our own
    static std::vector<std::shared_ptr<Polygon>> convertBoostPolygons(
            std::vector<BoostPolygon> const& boostPolygons);

    template <class PolyT>
    bool overlaps(PolyT const& other) const {
        return !boost::geometry::disjoint(poly, other);
    }

    template <class PolyT>
    std::shared_ptr<Polygon> intersectionSingle(PolyT const& other) const;

    template <class PolyT>
    std::vector<std::shared_ptr<Polygon>> intersection(PolyT const& other) const;

    template <class PolyT>
    std::shared_ptr<Polygon> unionSingle(PolyT const& other) const;

    template <class PolyT>
    std::vector<std::shared_ptr<Polygon>> union_(PolyT const& other) const;

    template <class PolyT>
    std::vector<std::shared_ptr<Polygon>> symDifference(PolyT const& other) const;

    BoostPolygon poly;
};

std::vector<std::shared_ptr<Polygon>> Polygon::Impl::convertBoostPolygons(
        std::vector<BoostPolygon> const& boostPolygons) {
    std::vector<std::shared_ptr<Polygon>> lsstPolygons;
    lsstPolygons.reserve(boostPolygons.size());
    for (std::vector<BoostPolygon>::const_iterator i = boostPolygons.begin(); i != boostPolygons.end(); ++i) {
        std::shared_ptr<Polygon> tmp(new Polygon(std::shared_ptr<Polygon::Impl>(new Polygon::Impl(*i))));
        lsstPolygons.push_back(tmp);
    }
    return lsstPolygons;
}

template <class PolyT>
std::shared_ptr<Polygon> Polygon::Impl::intersectionSingle(PolyT const& other) const {
    std::vector<BoostPolygon> result;
    boost::geometry::intersection(poly, other, result);
    if (result.size() == 0) {
        throw LSST_EXCEPT(SinglePolygonException, "Polygons have no intersection");
    }
    if (result.size() > 1) {
        throw LSST_EXCEPT(
                SinglePolygonException,
                (boost::format("Multiple polygons (%d) created by intersection()") % result.size()).str());
    }
    return std::shared_ptr<Polygon>(new Polygon(std::shared_ptr<Impl>(new Impl(result[0]))));
}

template <class PolyT>
std::vector<std::shared_ptr<Polygon>> Polygon::Impl::intersection(PolyT const& other) const {
    std::vector<BoostPolygon> boostResult;
    boost::geometry::intersection(poly, other, boostResult);
    return convertBoostPolygons(boostResult);
}

template <class PolyT>
std::shared_ptr<Polygon> Polygon::Impl::unionSingle(PolyT const& other) const {
    std::vector<BoostPolygon> result;
    boost::geometry::union_(poly, other, result);
    if (result.size() != 1) {
        throw LSST_EXCEPT(
                SinglePolygonException,
                (boost::format("Multiple polygons (%d) created by union_()") % result.size()).str());
    }
    return std::shared_ptr<Polygon>(new Polygon(std::shared_ptr<Impl>(new Impl(result[0]))));
}

template <class PolyT>
std::vector<std::shared_ptr<Polygon>> Polygon::Impl::union_(PolyT const& other) const {
    std::vector<BoostPolygon> boostResult;
    boost::geometry::union_(poly, other, boostResult);
    return convertBoostPolygons(boostResult);
}

template <class PolyT>
std::vector<std::shared_ptr<Polygon>> Polygon::Impl::symDifference(PolyT const& other) const {
    std::vector<BoostPolygon> boostResult;
    boost::geometry::sym_difference(poly, other, boostResult);
    return convertBoostPolygons(boostResult);
}

Polygon::Polygon(Polygon const&) = default;
Polygon::Polygon(Polygon&&) = default;
Polygon& Polygon::operator=(Polygon const&) = default;
Polygon& Polygon::operator=(Polygon&&) = default;

Polygon::~Polygon() = default;

Polygon::Polygon(Polygon::Box const& box) : _impl(new Polygon::Impl(box)) {}

Polygon::Polygon(std::vector<LsstPoint> const& vertices) : _impl(new Polygon::Impl(vertices)) {}

Polygon::Polygon(Polygon::Box const& box, afw::geom::TransformPoint2ToPoint2 const& transform)
        : _impl(new Polygon::Impl()) {
    auto corners = transform.applyForward(boxToCorners(box));
    boost::geometry::assign(_impl->poly, corners);
    _impl->check();
}

Polygon::Polygon(Polygon::Box const& box, lsst::geom::AffineTransform const& transform)
        : _impl(new Polygon::Impl()) {
    std::vector<LsstPoint> corners = boxToCorners(box);
    for (std::vector<LsstPoint>::iterator p = corners.begin(); p != corners.end(); ++p) {
        *p = transform(*p);
    }
    boost::geometry::assign(_impl->poly, corners);
    _impl->check();
}

size_t Polygon::getNumEdges() const {
    // boost::geometry::models::polygon uses a "closed" polygon: the start/end point is included twice
    return boost::geometry::num_points(_impl->poly) - 1;
}

Polygon::Box Polygon::getBBox() const {
    return boostBoxToLsst(boost::geometry::return_envelope<BoostBox>(_impl->poly));
}

LsstPoint Polygon::calculateCenter() const {
    return boost::geometry::return_centroid<LsstPoint>(_impl->poly);
}

double Polygon::calculateArea() const { return boost::geometry::area(_impl->poly); }

double Polygon::calculatePerimeter() const { return boost::geometry::perimeter(_impl->poly); }

std::vector<std::pair<LsstPoint, LsstPoint>> Polygon::getEdges() const {
    std::vector<LsstPoint> const vertices = getVertices();
    std::vector<std::pair<LsstPoint, LsstPoint>> edges;
    edges.reserve(getNumEdges());
    for (std::vector<LsstPoint>::const_iterator i = vertices.begin(), j = vertices.begin() + 1;
         j != vertices.end(); ++i, ++j) {
        edges.push_back(std::make_pair(*i, *j));
    }
    return edges;
}

std::vector<LsstPoint> Polygon::getVertices() const { return _impl->poly.outer(); }

std::vector<LsstPoint>::const_iterator Polygon::begin() const { return _impl->poly.outer().begin(); }

std::vector<LsstPoint>::const_iterator Polygon::end() const {
    return _impl->poly.outer().end() - 1;  // Note removal of final "closed" point
}

bool Polygon::operator==(Polygon const& other) const {
    return boost::geometry::equals(_impl->poly, other._impl->poly);
}

std::size_t Polygon::hash_value() const noexcept {
    // boost::hash allows hash functions to throw, but the container hashes throw
    // only if the element [geom::Point] has a throwing hash
    static boost::hash<BoostPolygon::ring_type> polygonHash;
    return polygonHash(_impl->poly.outer());
}

bool Polygon::contains(LsstPoint const& point) const { return boost::geometry::within(point, _impl->poly); }

bool Polygon::overlaps(Polygon const& other) const { return _impl->overlaps(other._impl->poly); }

bool Polygon::overlaps(Box const& box) const { return _impl->overlaps(box); }

std::shared_ptr<Polygon> Polygon::intersectionSingle(Polygon const& other) const {
    return _impl->intersectionSingle(other._impl->poly);
}

std::shared_ptr<Polygon> Polygon::intersectionSingle(Box const& box) const {
    return _impl->intersectionSingle(box);
}

std::vector<std::shared_ptr<Polygon>> Polygon::intersection(Polygon const& other) const {
    return _impl->intersection(other._impl->poly);
}

std::vector<std::shared_ptr<Polygon>> Polygon::intersection(Box const& box) const {
    return _impl->intersection(box);
}

std::shared_ptr<Polygon> Polygon::unionSingle(Polygon const& other) const {
    return _impl->unionSingle(other._impl->poly);
}

std::shared_ptr<Polygon> Polygon::unionSingle(Box const& box) const { return _impl->unionSingle(box); }

std::vector<std::shared_ptr<Polygon>> Polygon::union_(Polygon const& other) const {
    return _impl->union_(other._impl->poly);
}

std::vector<std::shared_ptr<Polygon>> Polygon::union_(Box const& box) const { return _impl->union_(box); }

std::vector<std::shared_ptr<Polygon>> Polygon::symDifference(Polygon const& other) const {
    return _impl->symDifference(other._impl->poly);
}

std::vector<std::shared_ptr<Polygon>> Polygon::symDifference(Box const& box) const {
    return _impl->symDifference(box);
}

std::shared_ptr<Polygon> Polygon::simplify(double const distance) const {
    BoostPolygon result;
    boost::geometry::simplify(_impl->poly, result, distance);
    return std::shared_ptr<Polygon>(new Polygon(std::shared_ptr<Impl>(new Impl(result))));
}

std::shared_ptr<Polygon> Polygon::convexHull() const {
    BoostPolygon hull;
    boost::geometry::convex_hull(_impl->poly, hull);
    return std::shared_ptr<Polygon>(new Polygon(std::shared_ptr<Impl>(new Impl(hull))));
}

std::shared_ptr<Polygon> Polygon::transform(TransformPoint2ToPoint2 const& transform) const {
    auto newVertices = transform.applyForward(getVertices());
    return std::shared_ptr<Polygon>(new Polygon(std::shared_ptr<Impl>(new Impl(newVertices))));
}

std::shared_ptr<Polygon> Polygon::transform(lsst::geom::AffineTransform const& transform) const {
    std::vector<LsstPoint> vertices;  // New vertices
    vertices.reserve(getNumEdges());
    for (std::vector<LsstPoint>::const_iterator i = _impl->poly.outer().begin();
         i != _impl->poly.outer().end(); ++i) {
        vertices.push_back(transform(*i));
    }
    return std::shared_ptr<Polygon>(new Polygon(std::shared_ptr<Impl>(new Impl(vertices))));
}

std::shared_ptr<Polygon> Polygon::subSample(size_t num) const {
    std::vector<LsstPoint> vertices;  // New vertices
    vertices.reserve(getNumEdges() * num);
    std::vector<std::pair<Point, Point>> edges = getEdges();
    for (std::vector<std::pair<Point, Point>>::const_iterator i = edges.begin(); i != edges.end(); ++i) {
        addSubSampledEdge(vertices, i->first, i->second, num);
    }
    return std::shared_ptr<Polygon>(new Polygon(std::shared_ptr<Impl>(new Impl(vertices))));
}

std::shared_ptr<Polygon> Polygon::subSample(double maxLength) const {
    std::vector<LsstPoint> vertices;  // New vertices
    vertices.reserve(getNumEdges() + static_cast<size_t>(::ceil(calculatePerimeter() / maxLength)));
    std::vector<std::pair<Point, Point>> edges = getEdges();
    for (std::vector<std::pair<Point, Point>>::const_iterator i = edges.begin(); i != edges.end(); ++i) {
        Point const &p1 = i->first, p2 = i->second;
        double const dist = ::sqrt(p1.distanceSquared(p2));
        addSubSampledEdge(vertices, p1, p2, static_cast<size_t>(::ceil(dist / maxLength)));
    }
    return std::shared_ptr<Polygon>(new Polygon(std::shared_ptr<Impl>(new Impl(vertices))));
}

std::shared_ptr<afw::image::Image<float>> Polygon::createImage(lsst::geom::Box2I const& bbox) const {
    typedef afw::image::Image<float> Image;
    std::shared_ptr<Image> image = std::make_shared<Image>(bbox);
    image->setXY0(bbox.getMin());
    *image = 0.0;
    lsst::geom::Box2D bounds = getBBox();  // Polygon bounds
    int xMin = std::max(static_cast<int>(bounds.getMinX()), bbox.getMinX());
    int xMax = std::min(static_cast<int>(::ceil(bounds.getMaxX())), bbox.getMaxX());
    int yMin = std::max(static_cast<int>(bounds.getMinY()), bbox.getMinY());
    int yMax = std::min(static_cast<int>(::ceil(bounds.getMaxY())), bbox.getMaxY());
    for (int y = yMin; y <= yMax; ++y) {
        double const yPixelMin = (double)y - 0.5, yPixelMax = (double)y + 0.5;
        BoostPolygon row;  // A polygon of row y
        boost::geometry::assign(
                row, LsstBox(lsst::geom::Point2D(xMin, yPixelMin), lsst::geom::Point2D(xMax, yPixelMax)));
        std::vector<BoostPolygon> intersections;
        boost::geometry::intersection(_impl->poly, row, intersections);

        if (intersections.size() == 1 && boost::geometry::num_points(intersections[0]) == 5) {
            // This row is fairly tame, and should have a long run of pixels within the polygon
            BoostPolygon const& row = intersections[0];
            std::vector<double> top, bottom;
            top.reserve(2);
            bottom.reserve(2);
            bool failed = false;
            for (std::vector<Point>::const_iterator i = row.outer().begin(); i != row.outer().end() - 1;
                 ++i) {
                double const xCoord = i->getX(), yCoord = i->getY();
                if (yCoord == yPixelMin) {
                    bottom.push_back(xCoord);
                } else if (yCoord == yPixelMax) {
                    top.push_back(xCoord);
                } else {
                    failed = true;
                    break;
                }
            }
            if (!failed && top.size() == 2 && bottom.size() == 2) {
                std::sort(top.begin(), top.end());
                std::sort(bottom.begin(), bottom.end());
                int const xMin = std::min(top[0], bottom[0]);
                int const xStart = ::ceil(std::max(top[0], bottom[0])) + 1;
                int const xStop = std::min(top[1], bottom[1]) - 1;
                int const xMax = ::ceil(std::max(top[1], bottom[1]));
                pixelRowOverlap(image, _impl->poly, std::max(xMin, bbox.getMinX()),
                                std::min(xStart, bbox.getMaxX()), y);
                int x = xStart;
                for (Image::x_iterator i = image->x_at(std::max(xStart, bbox.getMinX()) - image->getX0(),
                                                       y - image->getY0());
                     x <= std::min(xStop, bbox.getMaxX()); ++i, ++x) {
                    *i = 1.0;
                }
                pixelRowOverlap(image, _impl->poly, std::max(xStop, bbox.getMinX()),
                                std::min(xMax, bbox.getMaxX()), y);
                continue;
            }
        }

        // Last resort: do each pixel independently...
        for (std::vector<BoostPolygon>::const_iterator p = intersections.begin(); p != intersections.end();
             ++p) {
            double xMinRow = xMax, xMaxRow = xMin;
            std::vector<LsstPoint> const vertices = p->outer();
            for (std::vector<LsstPoint>::const_iterator q = vertices.begin(); q != vertices.end(); ++q) {
                double const x = q->getX();
                if (x < xMinRow) xMinRow = x;
                if (x > xMaxRow) xMaxRow = x;
            }

            pixelRowOverlap(image, _impl->poly, std::max(static_cast<int>(xMinRow), bbox.getMinX()),
                            std::min(static_cast<int>(::ceil(xMaxRow)), bbox.getMaxX()), y);
        }
    }
    return image;
}

// -------------- Table-based Persistence -------------------------------------------------------------------

/*
 *
 */
namespace {

struct PolygonSchema {
    afw::table::Schema schema;
    afw::table::PointKey<double> vertices;

    static PolygonSchema const& get() {
        static PolygonSchema instance;
        return instance;
    }

    // No copying
    PolygonSchema(const PolygonSchema&) = delete;
    PolygonSchema& operator=(const PolygonSchema&) = delete;

    // No moving
    PolygonSchema(PolygonSchema&&) = delete;
    PolygonSchema& operator=(PolygonSchema&&) = delete;

private:
    PolygonSchema()
            : schema(),
              vertices(afw::table::PointKey<double>::addFields(schema, "vertices", "list of vertex points",
                                                               "")) {}
};

class PolygonFactory : public table::io::PersistableFactory {
public:
    explicit PolygonFactory(std::string const& name) : table::io::PersistableFactory(name) {}

    std::shared_ptr<table::io::Persistable> read(InputArchive const& archive,
                                                 CatalogVector const& catalogs) const override {
        static PolygonSchema const& keys = PolygonSchema::get();

        LSST_ARCHIVE_ASSERT(catalogs.size() == 1u);
        afw::table::BaseCatalog const& cat = catalogs.front();

        std::vector<LsstPoint> vertices;
        for (afw::table::BaseCatalog::const_iterator iter = cat.begin(); iter != cat.end(); ++iter) {
            vertices.push_back(iter->get(keys.vertices));
        }
        std::shared_ptr<Polygon> result(new Polygon(vertices));
        return result;
    }
};

std::string getPolygonPersistenceName() { return "Polygon"; }

PolygonFactory registration(getPolygonPersistenceName());

}  // anonymous namespace

std::string Polygon::getPersistenceName() const { return getPolygonPersistenceName(); }

void Polygon::write(OutputArchiveHandle& handle) const {
    static PolygonSchema const& keys = PolygonSchema::get();
    afw::table::BaseCatalog catalog = handle.makeCatalog(keys.schema);

    std::vector<LsstPoint> vertices = this->getVertices();
    for (std::vector<LsstPoint>::const_iterator i = vertices.begin(); i != vertices.end(); ++i) {
        std::shared_ptr<afw::table::BaseRecord> record = catalog.addNew();
        record->set(keys.vertices, *i);
    }

    handle.saveCatalog(catalog);
}

std::shared_ptr<typehandling::Storable> Polygon::cloneStorable() const {
    return std::make_unique<Polygon>(*this);
}

std::string Polygon::toString() const {
    std::stringstream buffer;
    buffer << "Polygon(" << this->getVertices() << ")";
    return buffer.str();
}

bool Polygon::equals(typehandling::Storable const& other) const noexcept {
    return singleClassEquals(*this, other);
}

}  // namespace polygon
}  // namespace geom
}  // namespace afw
}  // namespace lsst


//=============================================================================================================
/**
 * @file     filterkernel.cpp
 * @author   Lorenz Esch <lesch@mgh.harvard.edu>;
 *           Ruben Doerfel <Ruben.Doerfel@tu-ilmenau.de>;
 *           Christoph Dinh <chdinh@nmr.mgh.harvard.edu>
 * @since    0.1.0
 * @date     February, 2014
 *
 * @section  LICENSE
 *
 * Copyright (C) 2014, Lorenz Esch, Ruben Doerfel, Christoph Dinh. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 * the following conditions are met:
 *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *       following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 *       the following disclaimer in the documentation and/or other materials provided with the distribution.
 *     * Neither the name of MNE-CPP authors nor the names of its contributors may be used
 *       to endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * @brief    Contains all FilterKernel.
 *
 */

//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include "filterkernel.h"

#include <utils/mnemath.h>

#include "parksmcclellan.h"
#include "cosinefilter.h"

#include <iostream>

//=============================================================================================================
// QT INCLUDES
//=============================================================================================================

#include <QDebug>

//=============================================================================================================
// EIGEN INCLUDES
//=============================================================================================================

#include <Eigen/SparseCore>
//#ifndef EIGEN_FFTW_DEFAULT
//#define EIGEN_FFTW_DEFAULT
//#endif
#include <unsupported/Eigen/FFT>

//=============================================================================================================
// USED NAMESPACES
//=============================================================================================================

using namespace RTPROCESSINGLIB;
using namespace Eigen;
using namespace UTILSLIB;

//=============================================================================================================
// INIT STATIC MEMBERS
//=============================================================================================================

QVector<RTPROCESSINGLIB::FilterParameter> FilterKernel::m_designMethods ({
    FilterParameter(QString("Cosine"), QString("A cosine filter")),
    FilterParameter(QString("Tschebyscheff"), QString("A tschebyscheff filter"))
//    FilterParameter(QString("External"), QString("An external filter"))
});
QVector<RTPROCESSINGLIB::FilterParameter> FilterKernel::m_filterTypes ({
    FilterParameter(QString("LPF"), QString("An LPF filter")),
    FilterParameter(QString("HPF"), QString("An HPF filter")),
    FilterParameter(QString("BPF"), QString("A BPF filter")),
    FilterParameter(QString("NOTCH"), QString("A NOTCH filter")),
    FilterParameter(QString("UNKNOWN"), QString("An UNKNOWN filter"))
});

//=============================================================================================================
// DEFINE MEMBER METHODS
//=============================================================================================================

FilterKernel::FilterKernel()
: m_iFilterType(m_filterTypes.indexOf(FilterParameter("BPF")))
, m_iFilterOrder(80)
, m_sFilterName("Unknown")
, m_dParksWidth(0.1)
, m_iDesignMethod(m_designMethods.indexOf(FilterParameter("Cosine")))
, m_dCenterFreq(0.5)
, m_dBandwidth(0.1)
, m_sFreq(1000)
, m_dLowpassFreq(40)
, m_dHighpassFreq(4)
{
    designFilter();
}

//=============================================================================================================

FilterKernel::FilterKernel(const QString& sFilterName,
                           int iFilterType,
                           int iOrder,
                           double dCenterfreq,
                           double dBandwidth,
                           double dParkswidth,
                           double dSFreq,
                           int iDesignMethod)
: m_iDesignMethod(iDesignMethod)
, m_iFilterType(iFilterType)
, m_sFreq(dSFreq)
, m_dCenterFreq(dCenterfreq)
, m_dBandwidth(dBandwidth)
, m_dParksWidth(dParkswidth)
, m_iFilterOrder(iOrder)
, m_sFilterName(sFilterName)
{
    if(iOrder < 9) {
       qWarning() << "[FilterKernel::FilterKernel] Less than 9 taps were provided. Setting number of taps to 9.";
    }

    designFilter();
}

//=============================================================================================================

void FilterKernel::prepareFilter(int iDataSize)
{
    int iFftLength, exp;

    iFftLength = iDataSize + m_vecCoeff.cols();
    exp = ceil(MNEMath::log2(iFftLength));
    iFftLength = pow(2, exp);

    // Transform coefficients anew if needed
    if(m_vecCoeff.cols() != (iFftLength/2+1)) {
        fftTransformCoeffs(iFftLength);
    }
}

//=============================================================================================================

RowVectorXd FilterKernel::applyConvFilter(const RowVectorXd& vecData,
                                          bool bKeepOverhead) const
{
    //Do zero padding or mirroring depending on user input
    RowVectorXd vecDataZeroPad = RowVectorXd::Zero(2*m_vecCoeff.cols() + vecData.cols());
    RowVectorXd vecFilteredTime = RowVectorXd::Zero(2*m_vecCoeff.cols() + vecData.cols());

    vecDataZeroPad.segment(m_vecCoeff.cols(), vecData.cols()) = vecData;

    //Do the convolution
    for(int i = m_vecCoeff.cols(); i < vecFilteredTime.cols(); i++) {
        vecFilteredTime(i-m_vecCoeff.cols()) = vecDataZeroPad.segment(i-m_vecCoeff.cols(),m_vecCoeff.cols()) * m_vecCoeff.transpose();
    }

    //Return filtered data
    if(!bKeepOverhead) {
        return vecFilteredTime.segment(m_vecCoeff.cols()/2, vecData.cols());
    }

    return vecFilteredTime.head(vecData.cols()+m_vecCoeff.cols());
}

//=============================================================================================================

void FilterKernel::applyFftFilter(RowVectorXd& vecData,
                                  bool bKeepOverhead)
{
    #ifdef EIGEN_FFTW_DEFAULT
    fftw_make_planner_thread_safe();
    #endif

    // Make sure we always have the correct FFT length for the given input data and filter overlap
    int iFftLength = vecData.cols() + m_vecCoeff.cols();
    int exp = ceil(MNEMath::log2(iFftLength));
    iFftLength = pow(2, exp);

    // Transform coefficients anew if needed
    if(m_vecFftCoeff.cols() != (iFftLength/2+1)) {
        fftTransformCoeffs(iFftLength);
    }

    //generate fft object
    Eigen::FFT<double> fft;
    fft.SetFlag(fft.HalfSpectrum);

    // Zero padd if necessary. Please note: The zero padding in Eigen's FFT is only working for column vectors -> We have to zero pad manually here
    int iOriginalSize = vecData.cols();
    if (vecData.cols() < iFftLength) {
        int iResidual = iFftLength - vecData.cols();
        vecData.conservativeResize(iFftLength);
        vecData.tail(iResidual).setZero();
    }

    //fft-transform data sequence
    RowVectorXcd vecFreqData;
    fft.fwd(vecFreqData, vecData, iFftLength);

    //perform frequency-domain filtering
    vecFreqData = m_vecFftCoeff.array() * vecFreqData.array();

    //inverse-FFT
    fft.inv(vecData, vecFreqData);

    //Return filtered data
    if(!bKeepOverhead) {
        vecData = vecData.segment(m_vecCoeff.cols()/2, iOriginalSize).eval();
    } else {
        vecData = vecData.head(iOriginalSize + m_vecCoeff.cols()).eval();
    }
}

//=============================================================================================================

QString FilterKernel::getName() const
{
    return m_sFilterName;
}

//=============================================================================================================

void FilterKernel::setName(const QString& sFilterName)
{
    m_sFilterName = sFilterName;
}

//=============================================================================================================

double FilterKernel::getSamplingFrequency() const
{
    return m_sFreq;
}

//=============================================================================================================

void FilterKernel::setSamplingFrequency(double dSFreq)
{
    m_sFreq = dSFreq;
}

//=============================================================================================================

int FilterKernel::getFilterOrder() const
{
    return m_iFilterOrder;
}

//=============================================================================================================

void FilterKernel::setFilterOrder(int iOrder)
{
    m_iFilterOrder = iOrder;
}

//=============================================================================================================

double FilterKernel::getCenterFrequency() const
{
    return m_dCenterFreq;
}

//=============================================================================================================

void FilterKernel::setCenterFrequency(double dCenterFreq)
{
    m_dCenterFreq = dCenterFreq;
}

//=============================================================================================================

double FilterKernel::getBandwidth() const
{
    return m_dBandwidth;
}

//=============================================================================================================

void FilterKernel::setBandwidth(double dBandwidth)
{
    m_dBandwidth = dBandwidth;
}

//=============================================================================================================

double FilterKernel::getParksWidth() const
{
    return m_dParksWidth;
}

//=============================================================================================================

void FilterKernel::setParksWidth(double dParksWidth)
{
    m_dParksWidth = dParksWidth;
}

//=============================================================================================================

double FilterKernel::getHighpassFreq() const
{
    return m_dHighpassFreq;
}

//=============================================================================================================

void FilterKernel::setHighpassFreq(double dHighpassFreq)
{
    m_dHighpassFreq = dHighpassFreq;
}

//=============================================================================================================

double FilterKernel::getLowpassFreq() const
{
    return m_dLowpassFreq;
}

//=============================================================================================================

void FilterKernel::setLowpassFreq(double dLowpassFreq)
{
    m_dLowpassFreq = dLowpassFreq;
}

//=============================================================================================================

Eigen::RowVectorXd FilterKernel::getCoefficients() const
{
    return m_vecCoeff;
}

//=============================================================================================================

void FilterKernel::setCoefficients(const Eigen::RowVectorXd& vecCoeff)
{
    m_vecCoeff = vecCoeff;
}

//=============================================================================================================

Eigen::RowVectorXcd FilterKernel::getFftCoefficients() const
{
    return m_vecFftCoeff;
}

//=============================================================================================================

void FilterKernel::setFftCoefficients(const Eigen::RowVectorXcd& vecFftCoeff)
{
    m_vecFftCoeff = vecFftCoeff;
}

//=============================================================================================================

bool FilterKernel::fftTransformCoeffs(int iFftLength)
{
    #ifdef EIGEN_FFTW_DEFAULT
        fftw_make_planner_thread_safe();
    #endif

    if(m_vecCoeff.cols() > iFftLength) {
        std::cout <<"[FilterKernel::fftTransformCoeffs] The number of filter taps is bigger than the FFT length."<< std::endl;
        return false;
    }

    //generate fft object
    Eigen::FFT<double> fft;
    fft.SetFlag(fft.HalfSpectrum);

    // Zero padd if necessary. Please note: The zero padding in Eigen's FFT is only working for column vectors -> We have to zero pad manually here
    RowVectorXd vecInputFft;
    if (m_vecCoeff.cols() < iFftLength) {
        vecInputFft.setZero(iFftLength);
        vecInputFft.block(0,0,1,m_vecCoeff.cols()) = m_vecCoeff;
    } else {
        vecInputFft = m_vecCoeff;
    }

    //fft-transform filter coeffs
    RowVectorXcd vecFreqData;
    fft.fwd(vecFreqData, vecInputFft, iFftLength);
    m_vecFftCoeff = vecFreqData;;

    return true;
}

//=============================================================================================================

void FilterKernel::designFilter()
{
    // Make sure we only use a minimum needed FFT size
    int iFftLength = m_iFilterOrder;
    int exp = ceil(MNEMath::log2(iFftLength));
    iFftLength = pow(2, exp);

    switch(m_iDesignMethod) {
        case 1: {
            ParksMcClellan filter(m_iFilterOrder,
                                  m_dCenterFreq,
                                  m_dBandwidth,
                                  m_dParksWidth,
                                  static_cast<ParksMcClellan::TPassType>(m_iFilterType));
            m_vecCoeff = filter.FirCoeff;

            //fft-transform m_vecCoeff in order to be able to perform frequency-domain filtering
            fftTransformCoeffs(iFftLength);

            break;
        }

        case 0: {
            CosineFilter filtercos;

            switch(m_iFilterType) {
                case 0:
                    filtercos = CosineFilter(iFftLength,
                                             (m_dCenterFreq)*(m_sFreq/2.),
                                             m_dParksWidth*(m_sFreq/2),
                                             (m_dCenterFreq)*(m_sFreq/2),
                                             m_dParksWidth*(m_sFreq/2),
                                             m_sFreq,
                                             static_cast<CosineFilter::TPassType>(m_iFilterType));

                    break;

                case 1:
                    filtercos = CosineFilter(iFftLength,
                                             (m_dCenterFreq)*(m_sFreq/2),
                                             m_dParksWidth*(m_sFreq/2),
                                             (m_dCenterFreq)*(m_sFreq/2),
                                             m_dParksWidth*(m_sFreq/2),
                                             m_sFreq,
                                             static_cast<CosineFilter::TPassType>(m_iFilterType));

                    break;

                case 2:
                    filtercos = CosineFilter(iFftLength,
                                             (m_dCenterFreq + m_dBandwidth/2)*(m_sFreq/2),
                                             m_dParksWidth*(m_sFreq/2),
                                             (m_dCenterFreq - m_dBandwidth/2)*(m_sFreq/2),
                                             m_dParksWidth*(m_sFreq/2),
                                             m_sFreq,
                                             static_cast<CosineFilter::TPassType>(m_iFilterType));

                    break;
            }

            //This filter is designed in the frequency domain, hence the time domain impulse response need to be shortend by the users dependent number of taps
            m_vecCoeff.resize(m_iFilterOrder);

            m_vecCoeff.head(m_iFilterOrder/2) = filtercos.m_vecCoeff.tail(m_iFilterOrder/2);
            m_vecCoeff.tail(m_iFilterOrder/2) = filtercos.m_vecCoeff.head(m_iFilterOrder/2);

            //Now generate the fft version of the shortened impulse response
            fftTransformCoeffs(iFftLength);

            break;
        }
    }

    switch(m_iFilterType) {
        case 0:
            m_dLowpassFreq = 0;
            m_dHighpassFreq = m_dCenterFreq*(m_sFreq/2);
        break;

        case 1:
            m_dLowpassFreq = m_dCenterFreq*(m_sFreq/2);
            m_dHighpassFreq = 0;
        break;

        case 2:
            m_dLowpassFreq = (m_dCenterFreq + m_dBandwidth/2)*(m_sFreq/2);
            m_dHighpassFreq = (m_dCenterFreq - m_dBandwidth/2)*(m_sFreq/2);
        break;
    }
}

//=============================================================================================================

RTPROCESSINGLIB::FilterParameter FilterKernel::getDesignMethod() const
{
    if(m_iDesignMethod < 0){
        return m_designMethods.at(0);
    }
    return m_designMethods.at(m_iDesignMethod);
}

//=============================================================================================================

RTPROCESSINGLIB::FilterParameter FilterKernel::getFilterType() const
{
    if(m_iFilterType < 0){
        return m_filterTypes.at(0);
    }
    return m_filterTypes.at(m_iFilterType);
}

//=============================================================================================================

void FilterKernel::setDesignMethod(int iDesignMethod)
{
    if(iDesignMethod < 0){
        m_iDesignMethod = 0;
    } else {
        m_iDesignMethod = iDesignMethod;
    }
}

//=============================================================================================================

void FilterKernel::setFilterType(int iFilterType)
{
    if(iFilterType < 0){
        m_iFilterType = 0;
    } else {
        m_iFilterType = iFilterType;
    }
}

//=============================================================================================================

FilterParameter::FilterParameter()
:FilterParameter("Unknown", "")
{
}

//=============================================================================================================

FilterParameter::FilterParameter(QString sName)
:FilterParameter(sName,"")
{
}

//=============================================================================================================

FilterParameter::FilterParameter(QString sName,
                           QString sDescription)
: m_sName(sName)
, m_sDescription(sDescription)
{

}

//=============================================================================================================

QString FilterParameter::getName() const
{
    return m_sName;
}


/*
 * Copyright (c) 2013, Christian Gehring, Hannes Sommer, Paul Furgale, Remo Diethelm
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Autonomous Systems Lab, ETH Zurich 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 Christian Gehring, Hannes Sommer, Paul Furgale,
 * Remo Diethelm 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.
 *
*/

#pragma once

#include <Eigen/Core>

#include "kindr/math/LinearAlgebra.hpp"
#include "kindr/common/common.hpp"
#include "kindr/common/assert_macros.hpp"
#include "kindr/common/assert_macros_eigen.hpp"
#include "kindr/rotations/RotationDiffBase.hpp"
#include "kindr/rotations/Rotation.hpp"

namespace kindr {

/*! \class RotationMatrixDiff
 * \brief Time derivative of a rotation matrix.
 *
 * This class implements the time derivative of a rotation matrix using a Eigen::Matrix<Scalar, 3, 3> as data storage.
 *
 * \tparam PrimType_  Primitive data type of the coordinates.
 * \ingroup rotations
 */
template<typename PrimType_>
class RotationMatrixDiff : public RotationDiffBase<RotationMatrixDiff<PrimType_>>, private Eigen::Matrix<PrimType_, 3, 3> {
 private:
  /*! \brief The base type.
   */
  typedef Eigen::Matrix<PrimType_, 3, 3> Base;

 public:
  /*! \brief The implementation type.
   *  The implementation type is always an Eigen object.
   */
  typedef Base Implementation;

  typedef Base Matrix3x3;

  /*! \brief The primitive type.
   *  Float/Double
   */
  typedef PrimType_ Scalar;

  RotationMatrixDiff()
    : Base(Base::Zero()) {
  }

  /*! \brief Constructor using Eigen::Matrix<Scalar, 3, 3>.
   *  \param other   Eigen::Matrix<Scalar, 3, 3>
   */
  explicit RotationMatrixDiff(const Base& other) // explicit on purpose
    : Base(other) {
  }

  /*! \brief Constructor using nine scalars.
   *  \param r11     entry in row 1, col 1
   *  \param r12     entry in row 1, col 2
   *  \param r13     entry in row 1, col 3
   *  \param r21     entry in row 2, col 1
   *  \param r22     entry in row 2, col 2
   *  \param r23     entry in row 2, col 3
   *  \param r31     entry in row 3, col 1
   *  \param r32     entry in row 3, col 2
   *  \param r33     entry in row 3, col 3
   */
  RotationMatrixDiff(Scalar r11, Scalar r12, Scalar r13,
                     Scalar r21, Scalar r22, Scalar r23,
                     Scalar r31, Scalar r32, Scalar r33) {
    *this << r11,r12,r13,r21,r22,r23,r31,r32,r33;
  }

  /*! \brief Constructor using a time derivative with a different parameterization
   *
   * \param rotation  rotation
   * \param other     other time derivative
   */
  template<typename RotationDerived_, typename OtherDerived_>
  inline explicit RotationMatrixDiff(const RotationBase<RotationDerived_>& rotation, const RotationDiffBase<OtherDerived_>& other)
    : Base(internal::RotationDiffConversionTraits<RotationMatrixDiff, OtherDerived_, RotationDerived_>::convert(rotation.derived(), other.derived()).toImplementation()){
  }

  /*! \brief Cast to another representation of the time derivative of a rotation
   *  \param other   other rotation
   *  \returns reference
   */
  template<typename OtherDerived_, typename RotationDerived_>
  OtherDerived_ cast(const RotationBase<RotationDerived_>& rotation) const {
    return internal::RotationDiffConversionTraits<OtherDerived_, RotationMatrixDiff, RotationDerived_>::convert(rotation.derived(), *this);
  }

  /*! \brief Cast to the implementation type.
   *  \returns the implementation (recommended only for advanced users)
   */
  inline Implementation& toImplementation() {
    return static_cast<Implementation&>(*this);
  }

  /*! \brief Cast to the implementation type.
   *  \returns the implementation (recommended only for advanced users)
   */
  inline const Implementation& toImplementation() const {
    return static_cast<const Implementation&>(*this);
  }

  /*! \brief Reading access to the time derivative of the rotation matrix.
   *  \returns rotation matrix (matrix) with reading access
   */
  inline const Matrix3x3& matrix() const {
    return this->toImplementation();
  }

  /*! \brief Writing access to the time derivative of the rotation matrix.
   *  \returns rotation matrix (matrix) with writing access
   */
  inline Matrix3x3& matrix() {
    return this->toImplementation();
  }

  /*! \brief Sets all time derivatives to zero.
   *  \returns reference
   */
  RotationMatrixDiff& setZero() {
    this->toImplementation().setZero();
    return *this;
  }

  /*! \brief Used for printing the object with std::cout.
   *  \returns std::stream object
   */
  friend std::ostream& operator << (std::ostream& out, const RotationMatrixDiff& diff) {
    out << diff.toImplementation();
    return out;
  }
};

//! \brief Time derivative of a rotation quaternion with primitive type double
typedef RotationMatrixDiff<double> RotationMatrixDiffPD;
//! \brief Time derivative of a rotation quaternion with primitive type float
typedef RotationMatrixDiff<float> RotationMatrixDiffPF;
//! \brief Time derivative of a rotation quaternion with primitive type double
typedef RotationMatrixDiff<double> RotationMatrixDiffD;
//! \brief Time derivative of a rotation quaternion with primitive type float
typedef RotationMatrixDiff<float> RotationMatrixDiffF;

namespace internal {

template<typename PrimType_>
class RotationDiffConversionTraits<RotationMatrixDiff<PrimType_>, LocalAngularVelocity<PrimType_>, RotationMatrix<PrimType_>> {
 public:
  inline static RotationMatrixDiff<PrimType_> convert(const RotationMatrix<PrimType_>& rotationMatrix, const LocalAngularVelocity<PrimType_>& angularVelocity) {
    return RotationMatrixDiff<PrimType_>(rotationMatrix.matrix()*getSkewMatrixFromVector(angularVelocity.vector()));
  }
};

template<typename PrimType_>
class RotationDiffConversionTraits<RotationMatrixDiff<PrimType_>, GlobalAngularVelocity<PrimType_>, RotationMatrix<PrimType_>> {
 public:
  inline static RotationMatrixDiff<PrimType_> convert(const RotationMatrix<PrimType_>& rotationMatrix, const GlobalAngularVelocity<PrimType_>& angularVelocity) {
    return RotationMatrixDiff<PrimType_>(getSkewMatrixFromVector(angularVelocity.vector())*rotationMatrix.matrix());
  }
};

} // namespace internal
} // namespace kindr


// g2o - General Graph Optimization
// Copyright (C) 2011 R. Kuemmerle, G. Grisetti, W. Burgard
// 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.
//
// 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.

#include "vertex_ellipse.h"

#include "g2o/stuff/macros.h"
#include "g2o/stuff/misc.h"

#ifdef G2O_HAVE_OPENGL
#include "g2o/EXTERNAL/freeglut/freeglut_minimal.h"
#include "g2o/stuff/opengl_primitives.h"
#include "g2o/stuff/opengl_wrapper.h"
#endif

#include <Eigen/Eigenvalues>
#include <iomanip>

using namespace std;

namespace g2o {

VertexEllipse::VertexEllipse()
    : RobotData(),
      _covariance(Matrix3F::Zero()),
      _UMatrix(Matrix2F::Zero()),
      _singularValues(Vector2F::Zero()) {}

VertexEllipse::~VertexEllipse() {}

void VertexEllipse::_updateSVD() const {
  Eigen::SelfAdjointEigenSolver<Matrix2F> eigenSolver(
      _covariance.block<2, 2>(0, 0));
  _UMatrix = eigenSolver.eigenvectors();
  _singularValues = eigenSolver.eigenvalues();
}

bool VertexEllipse::read(std::istream& is) {
  float cxx, cxy, cxt, cyy, cyt, ctt;
  is >> cxx >> cxy >> cxt >> cyy >> cyt >> ctt;
  _covariance(0, 0) = cxx;
  _covariance(0, 1) = cxy;
  _covariance(0, 2) = cxt;
  _covariance(1, 0) = cxy;
  _covariance(1, 1) = cyy;
  _covariance(1, 2) = cyt;
  _covariance(2, 0) = cxt;
  _covariance(2, 1) = cyt;
  _covariance(2, 2) = ctt;

  _updateSVD();

  int size;
  is >> size;
  for (int i = 0; i < size; i++) {
    float x, y;
    is >> x >> y;
    addMatchingVertex(x, y);
  }

  return true;
}

bool VertexEllipse::write(std::ostream& os) const {
  os << _covariance(0, 0) << " " << _covariance(0, 1) << " "
     << _covariance(0, 2) << " " << _covariance(1, 1) << " "
     << _covariance(1, 2) << " " << _covariance(2, 2) << " ";

  os << _matchingVertices.size() << " ";
  for (size_t i = 0; i < _matchingVertices.size(); i++) {
    os << _matchingVertices[i].x() << " " << _matchingVertices[i].y() << " ";
  }

  return os.good();
}

#ifdef G2O_HAVE_OPENGL
VertexEllipseDrawAction::VertexEllipseDrawAction()
    : DrawAction(typeid(VertexEllipse).name()) {
  _scaleFactor = 0;
}

bool VertexEllipseDrawAction::refreshPropertyPtrs(
    HyperGraphElementAction::Parameters* params_) {
  if (!DrawAction::refreshPropertyPtrs(params_)) return false;
  if (_previousParams) {
    _scaleFactor =
        _previousParams->makeProperty<DoubleProperty>(_typeName + "::", 1);
  } else {
    _scaleFactor = 0;
  }
  return true;
}

HyperGraphElementAction* VertexEllipseDrawAction::operator()(
    HyperGraph::HyperGraphElement* element,
    HyperGraphElementAction::Parameters* params_) {
  if (typeid(*element).name() != _typeName) return nullptr;

  refreshPropertyPtrs(params_);
  if (!_previousParams) {
    return this;
  }
  if (_show && !_show->value()) return this;

  VertexEllipse* that = dynamic_cast<VertexEllipse*>(element);

  glPushMatrix();

  float sigmaTheta = sqrt(that->covariance()(2, 2));
  float x = 0.1f * cosf(sigmaTheta);
  float y = 0.1f * sinf(sigmaTheta);

  glColor3f(1.f, 0.7f, 1.f);
  glBegin(GL_LINE_STRIP);
  glVertex3f(x, y, 0);
  glVertex3f(0, 0, 0);
  glVertex3f(x, -y, 0);
  glEnd();

  glColor3f(0.f, 1.f, 0.f);
  for (size_t i = 0; i < that->matchingVertices().size(); i++) {
    glBegin(GL_LINES);
    glVertex3f(0, 0, 0);
    glVertex3f(that->matchingVertices()[i].x(), that->matchingVertices()[i].y(),
               0);
    glEnd();
  }

  Matrix2F rot = that->U();
  float angle = std::atan2(rot(1, 0), rot(0, 0));
  glRotatef(angle * 180.0 / const_pi(), 0., 0., 1.);
  Vector2F sv = that->singularValues();
  glScalef(sqrt(sv(0)), sqrt(sv(1)), 1);

  glColor3f(1.f, 0.7f, 1.f);
  glBegin(GL_LINE_LOOP);
  for (int i = 0; i < 36; i++) {
    float rad = i * const_pi() / 18.0;
    glVertex2f(std::cos(rad), std::sin(rad));
  }
  glEnd();

  glPopMatrix();
  return this;
}
#endif

}  // namespace g2o


/*
 * _build_in.cpp
 *
 *  Created on: 2013-2-14
 *      Author: fasiondog
 */

#include <boost/python.hpp>
#include <hikyuu/indicator/build_in.h>

using namespace hku;
using namespace boost::python;

Indicator (*PRICELIST2)(const PriceList&, int) = PRICELIST;
Indicator (*PRICELIST3)(const Indicator&, int) = PRICELIST;
Indicator (*PRICELIST4)(int) = PRICELIST;

Indicator (*KDATA1)(const KData&) = KDATA;
Indicator (*KDATA3)() = KDATA;

Indicator (*OPEN1)(const KData&) = OPEN;
Indicator (*OPEN3)() = OPEN;

Indicator (*HIGH1)(const KData&) = HIGH;
Indicator (*HIGH3)() = HIGH;

Indicator (*LOW1)(const KData&) = LOW;
Indicator (*LOW3)() = LOW;

Indicator (*CLOSE1)(const KData&) = CLOSE;
Indicator (*CLOSE3)() = CLOSE;

Indicator (*AMO1)(const KData&) = AMO;
Indicator (*AMO3)() = AMO;

Indicator (*VOL1)(const KData&) = VOL;
Indicator (*VOL3)() = VOL;

Indicator (*KDATA_PART1)(const KData& kdata, const string& part) = KDATA_PART;
Indicator (*KDATA_PART3)(const string& part) = KDATA_PART;

Indicator (*AMA_1)(int, int, int) = AMA;
Indicator (*AMA_2)(const Indicator&, int, int, int) = AMA;

Indicator (*ATR_1)(int) = ATR;
Indicator (*ATR_2)(const Indicator&, int) = ATR;

Indicator (*DIFF_1)() = DIFF;
Indicator (*DIFF_2)(const Indicator&) = DIFF;

Indicator (*MA_1)(int) = MA;
Indicator (*MA_2)(const IndParam&) = MA;
Indicator (*MA_3)(const Indicator&, const IndParam&) = MA;
Indicator (*MA_4)(const Indicator&, const Indicator&) = MA;
Indicator (*MA_5)(const Indicator&, int) = MA;

Indicator (*SMA_1)(int, double) = SMA;
Indicator (*SMA_2)(const Indicator&, int, double) = SMA;

Indicator (*EMA_1)(int) = EMA;
Indicator (*EMA_2)(const Indicator&, int) = EMA;

Indicator (*MACD_1)(int, int, int) = MACD;
Indicator (*MACD_2)(const Indicator&, int, int, int) = MACD;
// BOOST_PYTHON_FUNCTION_OVERLOADS(MACD_1_overload, MACD, 0, 3);
// BOOST_PYTHON_FUNCTION_OVERLOADS(MACD_2_overload, MACD, 1, 4);

Indicator (*REF_1)(int) = REF;
Indicator (*REF_2)(const Indicator&, int) = REF;

Indicator (*SAFTYLOSS_1)(int n1, int n2, double p) = SAFTYLOSS;
Indicator (*SAFTYLOSS_2)(const Indicator&, int n1, int n2, double p) = SAFTYLOSS;
// BOOST_PYTHON_FUNCTION_OVERLOADS(SAFTYLOSS_1_overload, SAFTYLOSS, 0, 3);
// BOOST_PYTHON_FUNCTION_OVERLOADS(SAFTYLOSS_2_overload, SAFTYLOSS, 1, 4);

Indicator (*STDEV_1)(int) = STDEV;
Indicator (*STDEV_2)(const IndParam&) = STDEV;
Indicator (*STDEV_3)(const Indicator&, const IndParam&) = STDEV;
Indicator (*STDEV_4)(const Indicator&, const Indicator&) = STDEV;
Indicator (*STDEV_5)(const Indicator&, int) = STDEV;

Indicator (*STDP_1)(int) = STDP;
Indicator (*STDP_2)(const IndParam&) = STDP;
Indicator (*STDP_3)(const Indicator&, const IndParam&) = STDP;
Indicator (*STDP_4)(const Indicator&, const Indicator&) = STDP;
Indicator (*STDP_5)(const Indicator&, int) = STDP;

Indicator (*HHV_1)(int) = HHV;
Indicator (*HHV_2)(const IndParam&) = HHV;
Indicator (*HHV_3)(const Indicator&, const Indicator&) = HHV;
Indicator (*HHV_4)(const Indicator&, const IndParam&) = HHV;
Indicator (*HHV_5)(const Indicator&, int) = HHV;

Indicator (*LLV_1)(int) = LLV;
Indicator (*LLV_2)(const IndParam&) = LLV;
Indicator (*LLV_3)(const Indicator&, const IndParam&) = LLV;
Indicator (*LLV_4)(const Indicator&, const Indicator&) = LLV;
Indicator (*LLV_5)(const Indicator&, int) = LLV;

Indicator (*VIGOR_1)(const KData&, int) = VIGOR;
Indicator (*VIGOR_2)(int) = VIGOR;

Indicator (*CVAL_1)(double, size_t) = CVAL;
Indicator (*CVAL_2)(const Indicator&, double, int) = CVAL;

Indicator (*LIUTONGPAN_1)() = LIUTONGPAN;
Indicator (*LIUTONGPAN_2)(const KData&) = LIUTONGPAN;

Indicator (*HSL_1)() = HSL;
Indicator (*HSL_2)(const KData&) = HSL;

Indicator (*IF_1)(const Indicator&, const Indicator&, const Indicator&) = IF;
Indicator (*IF_2)(const Indicator&, price_t, const Indicator&) = IF;
Indicator (*IF_3)(const Indicator&, const Indicator&, price_t) = IF;
Indicator (*IF_4)(const Indicator&, price_t, price_t) = IF;

Indicator (*COUNT_1)(int) = COUNT;
Indicator (*COUNT_2)(const IndParam&) = COUNT;
Indicator (*COUNT_3)(const Indicator&, const Indicator&) = COUNT;
Indicator (*COUNT_4)(const Indicator&, const IndParam&) = COUNT;
Indicator (*COUNT_5)(const Indicator&, int) = COUNT;

Indicator (*SUM_1)(int) = SUM;
Indicator (*SUM_2)(const IndParam&) = SUM;
Indicator (*SUM_3)(const Indicator&, const IndParam&) = SUM;
Indicator (*SUM_4)(const Indicator&, const Indicator&) = SUM;
Indicator (*SUM_5)(const Indicator&, int) = SUM;

Indicator (*ABS_1)() = ABS;
Indicator (*ABS_2)(price_t) = ABS;
Indicator (*ABS_3)(const Indicator&) = ABS;

Indicator (*NOT_1)() = NOT;
Indicator (*NOT_2)(const Indicator&) = NOT;

Indicator (*SGN_1)() = SGN;
Indicator (*SGN_2)(price_t) = SGN;
Indicator (*SGN_3)(const Indicator&) = SGN;

Indicator (*EXP_1)() = EXP;
Indicator (*EXP_2)(price_t) = EXP;
Indicator (*EXP_3)(const Indicator&) = EXP;

Indicator (*MAX_1)(const Indicator&, const Indicator&) = MAX;
Indicator (*MAX_2)(const Indicator&, price_t) = MAX;
Indicator (*MAX_3)(price_t, const Indicator&) = MAX;

Indicator (*MIN_1)(const Indicator&, const Indicator&) = MIN;
Indicator (*MIN_2)(const Indicator&, price_t) = MIN;
Indicator (*MIN_3)(price_t, const Indicator&) = MIN;

Indicator (*BETWEEN_1)(const Indicator&, const Indicator&, const Indicator&) = BETWEEN;
Indicator (*BETWEEN_2)(const Indicator&, const Indicator&, price_t) = BETWEEN;
Indicator (*BETWEEN_3)(const Indicator&, price_t, const Indicator&) = BETWEEN;
Indicator (*BETWEEN_4)(const Indicator&, price_t, price_t) = BETWEEN;
Indicator (*BETWEEN_5)(price_t, const Indicator&, const Indicator&) = BETWEEN;
Indicator (*BETWEEN_6)(price_t, const Indicator&, price_t) = BETWEEN;
Indicator (*BETWEEN_7)(price_t, price_t, const Indicator&) = BETWEEN;
Indicator (*BETWEEN_8)(price_t, price_t, price_t) = BETWEEN;

Indicator (*LN_1)() = LN;
Indicator (*LN_2)(price_t) = LN;
Indicator (*LN_3)(const Indicator&) = LN;

Indicator (*LOG_1)() = LOG;
Indicator (*LOG_2)(price_t) = LOG;
Indicator (*LOG_3)(const Indicator&) = LOG;

Indicator (*HHVBARS_1)(int) = HHVBARS;
Indicator (*HHVBARS_2)(const IndParam&) = HHVBARS;
Indicator (*HHVBARS_3)(const Indicator&, const IndParam&) = HHVBARS;
Indicator (*HHVBARS_4)(const Indicator&, const Indicator&) = HHVBARS;
Indicator (*HHVBARS_5)(const Indicator&, int) = HHVBARS;

Indicator (*LLVBARS_1)(int) = LLVBARS;
Indicator (*LLVBARS_2)(const IndParam&) = LLVBARS;
Indicator (*LLVBARS_3)(const Indicator&, const IndParam&) = LLVBARS;
Indicator (*LLVBARS_4)(const Indicator&, const Indicator&) = LLVBARS;
Indicator (*LLVBARS_5)(const Indicator&, int) = LLVBARS;

Indicator (*POW_1)(int) = POW;
Indicator (*POW_2)(const IndParam&) = POW;
Indicator (*POW_3)(const Indicator&, int) = POW;
Indicator (*POW_4)(const Indicator&, const IndParam&) = POW;
Indicator (*POW_5)(const Indicator&, const Indicator&) = POW;
Indicator (*POW_6)(price_t, int) = POW;

Indicator (*SQRT_1)() = SQRT;
Indicator (*SQRT_2)(const Indicator&) = SQRT;
Indicator (*SQRT_3)(price_t) = SQRT;

Indicator (*ROUND_1)(int) = ROUND;
Indicator (*ROUND_2)(const Indicator&, int) = ROUND;
Indicator (*ROUND_3)(price_t, int) = ROUND;

Indicator (*ROUNDUP_1)(int) = ROUNDUP;
Indicator (*ROUNDUP_2)(const Indicator&, int) = ROUNDUP;
Indicator (*ROUNDUP_3)(price_t, int) = ROUNDUP;

Indicator (*ROUNDDOWN_1)(int) = ROUNDDOWN;
Indicator (*ROUNDDOWN_2)(const Indicator&, int) = ROUNDDOWN;
Indicator (*ROUNDDOWN_3)(price_t, int) = ROUNDDOWN;

Indicator (*FLOOR_1)() = FLOOR;
Indicator (*FLOOR_2)(const Indicator&) = FLOOR;
Indicator (*FLOOR_3)(price_t) = FLOOR;

Indicator (*CEILING_1)() = CEILING;
Indicator (*CEILING_2)(const Indicator&) = CEILING;
Indicator (*CEILING_3)(price_t) = CEILING;

Indicator (*INTPART_1)() = INTPART;
Indicator (*INTPART_2)(const Indicator&) = INTPART;
Indicator (*INTPART_3)(price_t) = INTPART;

Indicator (*EXIST_1)(int) = EXIST;
Indicator (*EXIST_2)(const IndParam&) = EXIST;
Indicator (*EXIST_3)(const Indicator&, const IndParam&) = EXIST;
Indicator (*EXIST_4)(const Indicator&, const Indicator&) = EXIST;
Indicator (*EXIST_5)(const Indicator&, int) = EXIST;

Indicator (*EVERY_1)(int) = EVERY;
Indicator (*EVERY_2)(const IndParam&) = EVERY;
Indicator (*EVERY_3)(const Indicator&, const IndParam&) = EVERY;
Indicator (*EVERY_4)(const Indicator&, const Indicator&) = EVERY;
Indicator (*EVERY_5)(const Indicator&, int) = EVERY;

Indicator (*LAST_1)(int, int) = LAST;
Indicator (*LAST_2)(const Indicator&, int, int) = LAST;

Indicator (*SIN_1)() = SIN;
Indicator (*SIN_2)(const Indicator&) = SIN;
Indicator (*SIN_3)(price_t) = SIN;

Indicator (*ASIN_1)() = ASIN;
Indicator (*ASIN_2)(const Indicator&) = ASIN;
Indicator (*ASIN_3)(price_t) = ASIN;

Indicator (*COS_1)() = COS;
Indicator (*COS_2)(const Indicator&) = COS;
Indicator (*COS_3)(price_t) = COS;

Indicator (*ACOS_1)() = ACOS;
Indicator (*ACOS_2)(const Indicator&) = ACOS;
Indicator (*ACOS_3)(price_t) = ACOS;

Indicator (*TAN_1)() = TAN;
Indicator (*TAN_2)(const Indicator&) = TAN;
Indicator (*TAN_3)(price_t) = TAN;

Indicator (*ATAN_1)() = ATAN;
Indicator (*ATAN_2)(const Indicator&) = ATAN;
Indicator (*ATAN_3)(price_t) = ATAN;

Indicator (*REVERSE_1)() = REVERSE;
Indicator (*REVERSE_2)(const Indicator&) = REVERSE;
Indicator (*REVERSE_3)(price_t) = REVERSE;

Indicator (*MOD_1)(const Indicator&, const Indicator&) = MOD;
Indicator (*MOD_2)(const Indicator&, price_t) = MOD;
Indicator (*MOD_3)(price_t, const Indicator&) = MOD;
Indicator (*MOD_4)(price_t, price_t) = MOD;

Indicator (*VAR_1)(int) = VAR;
Indicator (*VAR_2)(const IndParam&) = VAR;
Indicator (*VAR_3)(const Indicator&, const IndParam&) = VAR;
Indicator (*VAR_4)(const Indicator&, const Indicator&) = VAR;
Indicator (*VAR_5)(const Indicator&, int) = VAR;

Indicator (*VARP_1)(int) = VARP;
Indicator (*VARP_2)(const IndParam&) = VARP;
Indicator (*VARP_3)(const Indicator&, const IndParam&) = VARP;
Indicator (*VARP_4)(const Indicator&, const Indicator&) = VARP;
Indicator (*VARP_5)(const Indicator&, int) = VARP;

Indicator (*CROSS_1)(const Indicator&, const Indicator&) = CROSS;
Indicator (*CROSS_2)(const Indicator&, price_t) = CROSS;
Indicator (*CROSS_3)(price_t, const Indicator&) = CROSS;
Indicator (*CROSS_4)(price_t, price_t) = CROSS;

Indicator (*LONGCROSS_1)(const Indicator&, const Indicator&, int) = LONGCROSS;
Indicator (*LONGCROSS_2)(const Indicator&, price_t, int) = LONGCROSS;
Indicator (*LONGCROSS_3)(price_t, const Indicator&, int) = LONGCROSS;
Indicator (*LONGCROSS_4)(price_t, price_t, int) = LONGCROSS;

Indicator (*FILTER_1)(int) = FILTER;
Indicator (*FILTER_2)(const IndParam&) = FILTER;
Indicator (*FILTER_3)(const Indicator&, const IndParam&) = FILTER;
Indicator (*FILTER_4)(const Indicator&, const Indicator&) = FILTER;
Indicator (*FILTER_5)(const Indicator&, int) = FILTER;

Indicator (*BARSSINCE_1)() = BARSSINCE;
Indicator (*BARSSINCE_2)(const Indicator&) = BARSSINCE;
Indicator (*BARSSINCE_3)(price_t) = BARSSINCE;

Indicator (*BARSLAST_1)() = BARSLAST;
Indicator (*BARSLAST_2)(const Indicator&) = BARSLAST;
Indicator (*BARSLAST_3)(price_t) = BARSLAST;

Indicator (*SUMBARS_1)(double) = SUMBARS;
Indicator (*SUMBARS_2)(const IndParam&) = SUMBARS;
Indicator (*SUMBARS_3)(const Indicator&, const IndParam&) = SUMBARS;
Indicator (*SUMBARS_4)(const Indicator&, const Indicator&) = SUMBARS;
Indicator (*SUMBARS_5)(const Indicator&, double) = SUMBARS;

Indicator (*BARSCOUNT_1)() = BARSCOUNT;
Indicator (*BARSCOUNT_2)(const Indicator&) = BARSCOUNT;

Indicator (*BACKSET_1)(int) = BACKSET;
Indicator (*BACKSET_2)(const IndParam&) = BACKSET;
Indicator (*BACKSET_3)(const Indicator&, const IndParam&) = BACKSET;
Indicator (*BACKSET_4)(const Indicator&, const Indicator&) = BACKSET;
Indicator (*BACKSET_5)(const Indicator&, int) = BACKSET;

Indicator (*TIMELINE_1)() = TIMELINE;
Indicator (*TIMELINE_2)(const KData&) = TIMELINE;

Indicator (*TIMELINEVOL_1)() = TIMELINEVOL;
Indicator (*TIMELINEVOL_2)(const KData&) = TIMELINEVOL;

Indicator (*DEVSQ_1)(int) = DEVSQ;
Indicator (*DEVSQ_2)(const IndParam&) = DEVSQ;
Indicator (*DEVSQ_3)(const Indicator&, const Indicator&) = DEVSQ;
Indicator (*DEVSQ_4)(const Indicator&, const IndParam&) = DEVSQ;
Indicator (*DEVSQ_5)(const Indicator&, int) = DEVSQ;

Indicator (*ROC_1)(int) = ROC;
Indicator (*ROC_2)(const IndParam&) = ROC;
Indicator (*ROC_3)(const Indicator&, const IndParam&) = ROC;
Indicator (*ROC_4)(const Indicator&, const Indicator&) = ROC;
Indicator (*ROC_5)(const Indicator&, int) = ROC;

Indicator (*ROCP_1)(int) = ROCP;
Indicator (*ROCP_2)(const IndParam&) = ROCP;
Indicator (*ROCP_3)(const Indicator&, const IndParam&) = ROCP;
Indicator (*ROCP_4)(const Indicator&, const Indicator&) = ROCP;
Indicator (*ROCP_5)(const Indicator&, int) = ROCP;

Indicator (*ROCR_1)(int) = ROCR;
Indicator (*ROCR_2)(const IndParam&) = ROCR;
Indicator (*ROCR_3)(const Indicator&, const IndParam&) = ROCR;
Indicator (*ROCR_4)(const Indicator&, const Indicator&) = ROCR;
Indicator (*ROCR_5)(const Indicator&, int) = ROCR;

Indicator (*ROCR100_1)(int) = ROCR100;
Indicator (*ROCR100_2)(const IndParam&) = ROCR100;
Indicator (*ROCR100_3)(const Indicator&, const IndParam&) = ROCR100;
Indicator (*ROCR100_4)(const Indicator&, const Indicator&) = ROCR100;
Indicator (*ROCR100_5)(const Indicator&, int) = ROCR100;

Indicator (*AD_1)() = AD;
Indicator (*AD_2)(const KData&) = AD;

Indicator (*COST_1)(double x) = COST;
Indicator (*COST_2)(const KData&, double x) = COST;

Indicator (*ALIGN_1)(const DatetimeList&) = ALIGN;
Indicator (*ALIGN_2)(const Indicator&, const DatetimeList&) = ALIGN;
Indicator (*ALIGN_3)(const Indicator&, const Indicator&) = ALIGN;
Indicator (*ALIGN_4)(const Indicator&, const KData&) = ALIGN;

Indicator (*DROPNA_1)() = DROPNA;
Indicator (*DROPNA_2)(const Indicator&) = DROPNA;

Indicator (*AVEDEV_1)(const Indicator&, int) = AVEDEV;
Indicator (*AVEDEV_2)(const Indicator&, const IndParam&) = AVEDEV;
Indicator (*AVEDEV_3)(const Indicator&, const Indicator&) = AVEDEV;

Indicator (*DOWNNDAY_1)(const Indicator&, int) = DOWNNDAY;
Indicator (*DOWNNDAY_2)(const Indicator&, const IndParam&) = DOWNNDAY;
Indicator (*DOWNNDAY_3)(const Indicator&, const Indicator&) = DOWNNDAY;

Indicator (*UPNDAY_1)(const Indicator&, int) = UPNDAY;
Indicator (*UPNDAY_2)(const Indicator&, const IndParam&) = UPNDAY;
Indicator (*UPNDAY_3)(const Indicator&, const Indicator&) = UPNDAY;

Indicator (*NDAY_1)(const Indicator&, const Indicator&, int) = NDAY;
Indicator (*NDAY_2)(const Indicator&, const Indicator&, const Indicator&) = NDAY;
Indicator (*NDAY_3)(const Indicator&, const Indicator&, const IndParam&) = NDAY;

void export_Indicator_build_in() {
    def("KDATA", KDATA1);
    def("KDATA", KDATA3, R"(KDATA([data])

    包装KData成Indicator，用于其他指标计算

    :param data: KData 或 具有6个返回结果的Indicator（如KDATA生成的Indicator）
    :rtype: Indicator)");

    def("CLOSE", CLOSE1);
    def("CLOSE", CLOSE3, R"(CLOSE([data])

    获取收盘价，包装KData的收盘价成Indicator

    :param data: 输入数据（KData 或 Indicator）
    :rtype: Indicator)");

    def("OPEN", OPEN1);
    def("OPEN", OPEN3, R"(OPEN([data])

    获取开盘价，包装KData的开盘价成Indicator

    :param data: 输入数据（KData 或 Indicator）
    :rtype: Indicator)");

    def("HIGH", HIGH1);
    def("HIGH", HIGH3, R"(HIGH([data])

    获取最高价，包装KData的最高价成Indicator

    :param data: 输入数据（KData 或 Indicator）
    :rtype: Indicator)");

    def("LOW", LOW1);
    def("LOW", LOW3, R"(LOW([data])

    获取最低价，包装KData的最低价成Indicator

    :param data: 输入数据（KData 或 Indicator）
    :rtype: Indicator)");

    def("AMO", AMO1);
    def("AMO", AMO3, R"(AMO([data])

    获取成交金额，包装KData的成交金额成Indicator

    :param data: 输入数据（KData 或 Indicator）
    :rtype: Indicator)");

    def("VOL", VOL1);
    def("VOL", VOL3, R"(VOL([data])

    获取成交量，包装KData的成交量成Indicator

    :param data: 输入数据（KData 或 Indicator）
    :rtype: Indicator)");

    def("KDATA_PART", KDATA_PART1, (arg("data"), arg("kpart")));
    def("KDATA_PART", KDATA_PART3, (arg("kpart")), R"(KDATA_PART([data, kpart])

    根据字符串选择返回指标KDATA/OPEN/HIGH/LOW/CLOSE/AMO/VOL，如:KDATA_PART("CLOSE")等同于CLOSE()

    :param data: 输入数据（KData 或 Indicator）
    :param string kpart: KDATA|OPEN|HIGH|LOW|CLOSE|AMO|VOL
    :rtype: Indicator)");

    def("PRICELIST", PRICELIST2, (arg("data"), arg("discard") = 0));
    def("PRICELIST", PRICELIST3, (arg("data"), arg("result_index") = 0));
    def("PRICELIST", PRICELIST4, (arg("result_index") = 0));

    def("SMA", SMA_1, (arg("n") = 22, arg("m") = 2.0));
    def("SMA", SMA_2, (arg("data"), arg("n") = 22, arg("m") = 2.0), R"(SMA([data, n=22, m=2])

    求移动平均

    用法：若Y=SMA(X,N,M) 则 Y=[M*X+(N-M)*Y')/N,其中Y'表示上一周期Y值

    :param Indicator data: 输入数据
    :param int n: 时间窗口
    :param float m: 系数
    :rtype: Indicator)");

    def("EMA", EMA_1, (arg("n") = 22));
    def("EMA", EMA_2, (arg("data"), arg("n") = 22), R"(EMA([data, n=22])

    指数移动平均线(Exponential Moving Average)

    :param data: 输入数据
    :param int n: 计算均值的周期窗口，必须为大于0的整数
    :rtype: Indicator)");

    def("MA", MA_1, (arg("n") = 22));
    def("MA", MA_2, (arg("n")));
    def("MA", MA_3, (arg("data"), arg("n")));
    def("MA", MA_4, (arg("data"), arg("n")));
    def("MA", MA_5, (arg("data"), arg("n") = 22), R"(MA([data, n=22])

    简单移动平均

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("AMA", AMA_1, (arg("n") = 10, arg("fast_n") = 2, arg("slow_n") = 30));
    def("AMA", AMA_2, (arg("data"), arg("n") = 10, arg("fast_n") = 2, arg("slow_n") = 30),
        R"(AMA([data, n=10, fast_n=2, slow_n=30])

    佩里.J 考夫曼（Perry J.Kaufman）自适应移动平均 [BOOK1]_

    :param Indicator data: 输入数据
    :param int n: 计算均值的周期窗口，必须为大于2的整数
    :param int fast_n: 对应快速周期N
    :param int slow_n: 对应慢速EMA线的N值
    :rtype: Indicator

    * result(0): AMA
    * result(1): ER)");

    def("ATR", ATR_1, (arg("n") = 14));
    def("ATR", ATR_2, (arg("data"), arg("n") = 14), R"(ATR([data, n=14])

    平均真实波幅(Average True Range)

    :param Indicator data 待计算的源数据
    :param int n: 计算均值的周期窗口，必须为大于1的整数
    :rtype: Indicator)");

    def("MACD", MACD_1, (arg("n1") = 12, arg("n2") = 26, arg("n3") = 9));
    def("MACD", MACD_2, (arg("data"), arg("n1") = 12, arg("n2") = 26, arg("n3") = 9),
        R"(MACD([data, n1=12, n2=26, n3=9])

    平滑异同移动平均线

    :param Indicator data: 输入数据
    :param int n1: 短期EMA时间窗
    :param int n2: 长期EMA时间窗
    :param int n3: （短期EMA-长期EMA）EMA平滑时间窗
    :rtype: 具有三个结果集的 Indicator

    * result(0): MACD_BAR：MACD直柱，即MACD快线－MACD慢线
    * result(1): DIFF: 快线,即（短期EMA-长期EMA）
    * result(2): DEA: 慢线，即快线的n3周期EMA平滑)");

    def("VIGOR", VIGOR_1, (arg("kdata"), arg("n") = 2));
    def("VIGOR", VIGOR_2, (arg("n") = 2), R"(VIGOR([kdata, n=2])

    亚历山大.艾尔德力度指数 [BOOK2]_

    计算公式：（收盘价今－收盘价昨）＊成交量今

    :param KData data: 输入数据
    :param int n: EMA平滑窗口
    :rtype: Indicator)");

    def("SAFTYLOSS", SAFTYLOSS_1, (arg("n1") = 10, arg("n2") = 3, arg("p") = 2.0));
    def("SAFTYLOSS", SAFTYLOSS_2, (arg("data"), arg("n1") = 10, arg("n2") = 3, arg("p") = 2.0),
        R"(SAFTYLOSS([data, n1=10, n2=3, p=2.0])

    亚历山大 艾尔德安全地带止损线，参见 [BOOK2]_

    计算说明：在回溯周期内（一般为10到20天），将所有向下穿越的长度相加除以向下穿越的次数，得到噪音均值（即回溯期内所有最低价低于前一日最低价的长度除以次数），并用今日最低价减去（前日噪音均值乘以一个倍数）得到该止损线。为了抵消波动并且保证止损线的上移，在上述结果的基础上再取起N日（一般为3天）内的最高值

    :param Indicator data: 输入数据
    :param int n1: 计算平均噪音的回溯时间窗口
    :param int n2: 对初步止损线去n2日内的最高值
    :param float p: 噪音系数
    :rtype: Indicator)");

    def("DIFF", DIFF_1);
    def("DIFF", DIFF_2, R"(DIFF([data])

    差分指标，即data[i] - data[i-1]

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("REF", REF_1, (arg("n")));
    def("REF", REF_2, (arg("data"), arg("n")), R"(REF([data, n])

    向前引用 （即右移），引用若干周期前的数据。

    用法：REF(X，A)　引用A周期前的X值。

    :param Indicator data: 输入数据
    :param int n: 引用n周期前的值，即右移n位
    :rtype: Indicator)");

    def("STDEV", STDEV_1, (arg("n") = 10));
    def("STDEV", STDEV_2, (arg("n")));
    def("STDEV", STDEV_3, (arg("data"), arg("n")));
    def("STDEV", STDEV_4, (arg("data"), arg("n")));
    def("STDEV", STDEV_5, (arg("data"), arg("n") = 10), R"(STDEV([data, n=10])

    计算N周期内样本标准差

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("STDP", STDP_1, (arg("n") = 10));
    def("STDP", STDP_2, (arg("n")));
    def("STDP", STDP_3, (arg("data"), arg("n")));
    def("STDP", STDP_4, (arg("data"), arg("n")));
    def("STDP", STDP_5, (arg("data"), arg("n") = 10), R"(STDP([data, n=10])

    总体标准差，STDP(X,N)为X的N日总体标准差

    :param data: 输入数据
    :param int n: 时间窗口
    :rtype: Indicator)");

    def("POS", POS, (arg("block"), arg("query"), arg("sg")));

    def("HHV", HHV_1, (arg("n") = 20));
    def("HHV", HHV_2, (arg("n")));
    def("HHV", HHV_3, (arg("data"), arg("n")));
    def("HHV", HHV_4, (arg("data"), arg("n")));
    def("HHV", HHV_5, (arg("data"), arg("n") = 20), R"(HHV([data, n=20])

    N日内最高价，N=0则从第一个有效值开始。

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: N日时间窗口
    :rtype: Indicator)");

    def("LLV", LLV_1, (arg("n") = 20));
    def("LLV", LLV_2, (arg("n")));
    def("LLV", LLV_3, (arg("data"), arg("n")));
    def("LLV", LLV_4, (arg("data"), arg("n")));
    def("LLV", LLV_5, (arg("data"), arg("n") = 20), R"(LLV([data, n=20])

    N日内最低价，N=0则从第一个有效值开始。

    :param data: 输入数据
    :param int|Indicator|IndParam n: N日时间窗口
    :rtype: Indicator)");

    def("CVAL", CVAL_1, (arg("value") = 0.0, arg("discard") = 0));
    def("CVAL", CVAL_2, (arg("data"), arg("value") = 0.0, arg("discard") = 0),
        R"(CVAL([data, value=0.0, discard=0])

    data 为 Indicator 实例，创建和 data 等长的常量指标，其值和为value，抛弃长度discard和data一样

    :param Indicator data: Indicator实例
    :param float value: 常数值
    :param int discard: 抛弃数量
    :rtype: Indicator)");

    def("LIUTONGPAN", LIUTONGPAN_1);
    def("LIUTONGPAN", LIUTONGPAN_2, R"(LIUTONGPAN(kdata)

   获取流通盘（单位：万股），同 CAPITAL

   :param KData kdata: k线数据
   :rtype: Indicator)");

    def("HSL", HSL_1);
    def("HSL", HSL_2, R"(HSL(kdata)

    获取换手率，等于 VOL(k) / CAPITAL(k)

    :param KData kdata: k线数据
    :rtype: Indicator)");

    def("WEAVE", WEAVE, R"(WEAVE(ind1, ind2)

    将ind1和ind2的结果组合在一起放在一个Indicator中。如ind = WEAVE(ind1, ind2), 则此时ind包含多个结果，按ind1、ind2的顺序存放。

    :param Indicator ind1: 指标1
    :param Indicator ind2: 指标2
    :rtype: Indicator)");

    def("IF", IF_1);
    def("IF", IF_2);
    def("IF", IF_3);
    def("IF", IF_4, R"(IF(x, a, b)

    条件函数, 根据条件求不同的值。

    用法：IF(X,A,B)若X不为0则返回A,否则返回B

    例如：IF(CLOSE>OPEN,HIGH,LOW)表示该周期收阳则返回最高值,否则返回最低值

    :param Indicator x: 条件指标
    :param Indicator a: 待选指标 a
    :param Indicator b: 待选指标 b
    :rtype: Indicator)");

    def("COUNT", COUNT_1, (arg("n") = 20));
    def("COUNT", COUNT_2, (arg("n")));
    def("COUNT", COUNT_3, (arg("data"), arg("n")));
    def("COUNT", COUNT_4, (arg("data"), arg("n")));
    def("COUNT", COUNT_5, (arg("data"), arg("n") = 20), R"(COUNT([data, n=20])

    统计满足条件的周期数。

    用法：COUNT(X,N),统计N周期中满足X条件的周期数,若N=0则从第一个有效值开始。

    例如：COUNT(CLOSE>OPEN,20)表示统计20周期内收阳的周期数

    :param Indicator data: 条件
    :param int|Indicator|IndParam n: 周期
    :rtype: Indicator)");

    def("SUM", SUM_1, (arg("n") = 20));
    def("SUM", SUM_2, (arg("n")));
    def("SUM", SUM_3, (arg("data"), arg("n")));
    def("SUM", SUM_4, (arg("data"), arg("n")));
    def("SUM", SUM_5, (arg("data"), arg("n") = 20), R"(SUM([data, n=20])

    求总和。SUM(X,N),统计N周期中X的总和,N=0则从第一个有效值开始。

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("ABS", ABS_1);
    def("ABS", ABS_2);
    def("ABS", ABS_3, R"(ABS([data])

    求绝对值

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("NOT", NOT_1);
    def("NOT", NOT_2, R"(NOT([data])

    求逻辑非。NOT(X)返回非X,即当X=0时返回1，否则返回0。

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("SGN", SGN_1);
    def("SGN", SGN_2);
    def("SGN", SGN_3, R"(SGN([data])

    求符号值, SGN(X)，当 X>0, X=0, X<0分别返回 1, 0, -1。

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("EXP", EXP_1);
    def("EXP", EXP_2);
    def("EXP", EXP_3, R"(EXP([data])

    EXP(X)为e的X次幂

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("MAX", MAX_1);
    def("MAX", MAX_2);
    def("MAX", MAX_3, R"(MAX(ind1, ind2)

    求最大值, MAX(A,B)返回A和B中的较大值。

    :param Indicator ind1: A
    :param Indicator ind2: B
    :rtype: Indicator)");

    def("MIN", MIN_1);
    def("MIN", MIN_2);
    def("MIN", MIN_3, R"(MIN(ind1, ind2)

    求最小值, MIN(A,B)返回A和B中的较小值。

    :param Indicator ind1: A
    :param Indicator ind2: B
    :rtype: Indicator)");

    def("BETWEEN", BETWEEN_1);
    def("BETWEEN", BETWEEN_2);
    def("BETWEEN", BETWEEN_3);
    def("BETWEEN", BETWEEN_4);
    def("BETWEEN", BETWEEN_5);
    def("BETWEEN", BETWEEN_6);
    def("BETWEEN", BETWEEN_7);
    def("BETWEEN", BETWEEN_8, R"(BETWEEN(a, b, c)

    介于(介于两个数之间)

    用法：BETWEEN(A,B,C)表示A处于B和C之间时返回1，否则返回0

    例如：BETWEEN(CLOSE,MA(CLOSE,10),MA(CLOSE,5))表示收盘价介于5日均线和10日均线之间

    :param Indicator a: A
    :param Indicator b: B
    :param Indicator c: C
    :rtype: Indicator)");

    def("LN", LN_1);
    def("LN", LN_2);
    def("LN", LN_3, R"(LN([data])

    求自然对数, LN(X)以e为底的对数

    :param data: 输入数据
    :rtype: Indicator)");

    def("LOG", LOG_1);
    def("LOG", LOG_2);
    def("LOG", LOG_3, R"(LOG([data])

    以10为底的对数

    :param data: 输入数据
    :rtype: Indicator)");

    def("HHVBARS", HHVBARS_1, (arg("n") = 20));
    def("HHVBARS", HHVBARS_2, (arg("n")));
    def("HHVBARS", HHVBARS_3, (arg("data"), arg("n")));
    def("HHVBARS", HHVBARS_4, (arg("data"), arg("n")));
    def("HHVBARS", HHVBARS_5, (arg("data"), arg("n") = 20), R"(HHVBARS([data, n=20])

    上一高点位置 求上一高点到当前的周期数。

    用法：HHVBARS(X,N):求N周期内X最高值到当前周期数N=0表示从第一个有效值开始统计

    例如：HHVBARS(HIGH,0)求得历史新高到到当前的周期数

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: N日时间窗口
    :rtype: Indicator)");

    def("LLVBARS", LLVBARS_1, (arg("n") = 20));
    def("LLVBARS", LLVBARS_2, (arg("n")));
    def("LLVBARS", LLVBARS_3, (arg("data"), arg("n")));
    def("LLVBARS", LLVBARS_4, (arg("data"), arg("n")));
    def("LLVBARS", LLVBARS_5, (arg("data"), arg("n") = 20), R"(LLVBARS([data, n=20])

    上一低点位置 求上一低点到当前的周期数。

    用法：LLVBARS(X,N):求N周期内X最低值到当前周期数N=0表示从第一个有效值开始统计

    例如：LLVBARS(HIGH,20)求得20日最低点到当前的周期数

    :param data: 输入数据
    :param int|Indicator|IndParam n: N日时间窗口
    :rtype: Indicator)");

    def("POW", POW_1, (arg("n")));
    def("POW", POW_2, (arg("n")));
    def("POW", POW_3, (arg("data"), arg("n")));
    def("POW", POW_4, (arg("data"), arg("n")));
    def("POW", POW_5, (arg("data"), arg("n")));
    def("POW", POW_6), (arg("data"), arg("n"), R"(POW(data, n)

    乘幂

    用法：POW(A,B)返回A的B次幂

    例如：POW(CLOSE,3)求得收盘价的3次方

    :param data: 输入数据
    :param int|Indicator|IndParam n: 幂
    :rtype: Indicator)");

    def("SQRT", SQRT_1);
    def("SQRT", SQRT_2);
    def("SQRT", SQRT_3, R"(SQRT([data])

    开平方

    用法：SQRT(X)为X的平方根

    例如：SQRT(CLOSE)收盘价的平方根

    :param data: 输入数据
    :rtype: Indicator)");

    def("ROUND", ROUND_1, (arg("ndigits") = 2));
    def("ROUND", ROUND_2, (arg("data"), arg("ndigits") = 2));
    def("ROUND", ROUND_3, (arg("data"), arg("ndigits") = 2), R"(ROUND([data, ndigits=2])

    四舍五入

    :param data: 输入数据
    :param int ndigits: 保留的小数点后位数
    :rtype: Indicator)");

    def("ROUNDUP", ROUNDUP_1, (arg("ndigits") = 2));
    def("ROUNDUP", ROUNDUP_2, (arg("data"), arg("ndigits") = 2));
    def("ROUNDUP", ROUNDUP_3, (arg("data"), arg("ndigits") = 2), R"(ROUNDUP([data, ndigits=2])

    向上截取，如10.1截取后为11

    :param data: 输入数据
    :param int ndigits: 保留的小数点后位数
    :rtype: Indicator)");

    def("ROUNDDOWN", ROUNDDOWN_1, (arg("ndigits") = 2));
    def("ROUNDDOWN", ROUNDDOWN_2, (arg("data"), arg("ndigits") = 2));
    def("ROUNDDOWN", ROUNDDOWN_3, (arg("data"), arg("ndigits") = 2), R"(ROUND([data, ndigits=2])

    四舍五入

    :param data: 输入数据
    :param int ndigits: 保留的小数点后位数
    :rtype: Indicator)");

    def("FLOOR", FLOOR_1);
    def("FLOOR", FLOOR_2);
    def("FLOOR", FLOOR_3, R"(FLOOR([data])

    向下舍入(向数值减小方向舍入)取整

    用法：FLOOR(A)返回沿A数值减小方向最接近的整数

    例如：FLOOR(12.3)求得12

    :param data: 输入数据
    :rtype: Indicator)");

    def("CEILING", CEILING_1);
    def("CEILING", CEILING_2);
    def("CEILING", CEILING_3, R"(CEILING([data])

    向上舍入(向数值增大方向舍入)取整

    用法：CEILING(A)返回沿A数值增大方向最接近的整数

    例如：CEILING(12.3)求得13；CEILING(-3.5)求得-3

    :param data: 输入数据
    :rtype: Indicator)");

    def("INTPART", INTPART_1);
    def("INTPART", INTPART_2);
    def("INTPART", INTPART_3, R"(INTPART([data])

    取整(绝对值减小取整，即取得数据的整数部分)

    :param data: 输入数据
    :rtype: Indicator)");

    def("EXIST", EXIST_1, (arg("n") = 20));
    def("EXIST", EXIST_2, (arg("n")));
    def("EXIST", EXIST_3, (arg("data"), arg("n")));
    def("EXIST", EXIST_4, (arg("data"), arg("n")));
    def("EXIST", EXIST_5, (arg("data"), arg("n") = 20), R"(EXIST([data, n=20])

    存在, EXIST(X,N) 表示条件X在N周期有存在

    :param data: 输入数据
    :param int|Indicator|IndParam n: 计算均值的周期窗口，必须为大于0的整数
    :rtype: Indicator)");

    def("EVERY", EVERY_1, (arg("n") = 20));
    def("EVERY", EVERY_2, (arg("n")));
    def("EVERY", EVERY_3, (arg("data"), arg("n")));
    def("EVERY", EVERY_4, (arg("data"), arg("n")));
    def("EVERY", EVERY_5, (arg("data"), arg("n") = 20), R"(EVERY([data, n=20])

    一直存在

    用法：EVERY (X,N) 表示条件X在N周期一直存在

    例如：EVERY(CLOSE>OPEN,10) 表示前10日内一直是阳线

    :param data: 输入数据
    :param int|Indicator|IndParam n: 计算均值的周期窗口，必须为大于0的整数
    :rtype: Indicator)");

    def("LAST", LAST_1, (arg("m") = 10, arg("n") = 5));
    def("LAST", LAST_2, (arg("data"), arg("m") = 10, arg("n") = 5), R"(LAST([data, m=10, n=5])

    区间存在。

    用法：LAST (X,M,N) 表示条件 X 在前 M 周期到前 N 周期存在。

    例如：LAST(CLOSE>OPEN,10,5) 表示从前10日到前5日内一直阳线。

    :param data: 输入数据
    :param int m: m周期
    :param int n: n周期
    :rtype: Indicator)");

    def("SIN", SIN_1);
    def("SIN", SIN_2);
    def("SIN", SIN_3, R"(SIN([data])

    正弦值

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("ASIN", ASIN_1);
    def("ASIN", ASIN_2);
    def("ASIN", ASIN_3, R"(ASIN([data])

    反正弦值

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("COS", COS_1);
    def("COS", COS_2);
    def("COS", COS_3, R"(COS([data])

    余弦值

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("ACOS", ACOS_1);
    def("ACOS", ACOS_2);
    def("ACOS", ACOS_3, R"(ACOS([data])

    反余弦值

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("TAN", TAN_1);
    def("TAN", TAN_2);
    def("TAN", TAN_3, R"(TAN([data])

    正切值

    :param Indicator data: 输入数据
    :rtype: Indicato)");

    def("ATAN", ATAN_1);
    def("ATAN", ATAN_2);
    def("ATAN", ATAN_3, R"(ATAN([data])

    反正切值

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("REVERSE", REVERSE_1);
    def("REVERSE", REVERSE_2);
    def("REVERSE", REVERSE_3, R"(REVERSE([data])

    求相反数，REVERSE(X)返回-X

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("MOD", MOD_1);
    def("MOD", MOD_2);
    def("MOD", MOD_3);
    def("MOD", MOD_4, R"(MOD(ind1, ind2)

    取整后求模。该函数仅为兼容通达信。实际上，指标求模可直接使用 % 操作符

    用法：MOD(A,B)返回A对B求模

    例如：MOD(26,10) 返回 6

    :param Indicator ind1:
    :param Indicator ind2:
    :rtype: Indicator)");

    def("VAR", VAR_1, (arg("n") = 10));
    def("VAR", VAR_2, (arg("n")));
    def("VAR", VAR_3, (arg("data"), arg("n")));
    def("VAR", VAR_4, (arg("data"), arg("n")));
    def("VAR", VAR_5, (arg("data"), arg("n") = 10), R"(VAR([data, n=10])

    估算样本方差, VAR(X,N)为X的N日估算样本方差

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("VARP", VARP_1, (arg("n") = 10));
    def("VARP", VARP_2, (arg("n")));
    def("VARP", VARP_3, (arg("data"), arg("n")));
    def("VARP", VARP_4, (arg("data"), arg("n")));
    def("VARP", VARP_5, (arg("data"), arg("n") = 10), R"(VARP([data, n=10])

    总体样本方差, VARP(X,N)为X的N日总体样本方差

    :param Indicator data: 输入数据
    :param int n|Indicator|IndParam: 时间窗口
    :rtype: Indicator)");

    def("UPNDAY", UPNDAY_1, (arg("data"), arg("n") = 3));
    def("UPNDAY", UPNDAY_2, (arg("data"), arg("n")));
    def("UPNDAY", UPNDAY_3, (arg("data"), arg("n")), R"(UPNDAY(data[, n=3])

    连涨周期数, UPNDAY(CLOSE,M)表示连涨M个周期

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("DOWNNDAY", DOWNNDAY_1, (arg("data"), arg("n") = 3));
    def("DOWNNDAY", DOWNNDAY_2, (arg("data"), arg("n")));
    def("DOWNNDAY", DOWNNDAY_3, (arg("data"), arg("n")), R"(DOWNNDAY(data[, n=3])

    连跌周期数, DOWNNDAY(CLOSE,M)表示连涨M个周期

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("NDAY", NDAY_1, (arg("x"), arg("y"), arg("n") = 3));
    def("NDAY", NDAY_1, (arg("x"), arg("y"), arg("n")));
    def("NDAY", NDAY_3, (arg("x"), arg("y"), arg("n")), R"(NDAY(x, y[, n=3])

    连大, NDAY(X,Y,N)表示条件X>Y持续存在N个周期

    :param Indicator x:
    :param Indicator y:
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("CROSS", CROSS_1);
    def("CROSS", CROSS_2);
    def("CROSS", CROSS_3);
    def("CROSS", CROSS_4, R"(CROSS(x, y)

    交叉函数

    :param x: 变量或常量，判断交叉的第一条线
    :param y: 变量或常量，判断交叉的第二条线
    :rtype: Indicator)");

    def("LONGCROSS", LONGCROSS_1, (arg("a"), arg("b"), arg("n") = 3));
    def("LONGCROSS", LONGCROSS_2, (arg("a"), arg("b"), arg("n") = 3));
    def("LONGCROSS", LONGCROSS_3, (arg("a"), arg("b"), arg("n") = 3));
    def("LONGCROSS", LONGCROSS_4, (arg("a"), arg("b"), arg("n") = 3), R"(LONGCROSS(a, b[, n=3])

    两条线维持一定周期后交叉

    用法：LONGCROSS(A,B,N)表示A在N周期内都小于B，本周期从下方向上穿过B时返 回1，否则返回0

    例如：LONGCROSS(MA(CLOSE,5),MA(CLOSE,10),5)表示5日均线维持5周期后与10日均线交金叉

    :param Indicator a:
    :param Indicator b:
    :param int n:
    :rtype: Indicator)");

    def("FILTER", FILTER_1, (arg("n") = 5));
    def("FILTER", FILTER_2, (arg("n")));
    def("FILTER", FILTER_3, (arg("data"), arg("n")));
    def("FILTER", FILTER_4, (arg("data"), arg("n")));
    def("FILTER", FILTER_5, (arg("data"), arg("n") = 5), R"(FILTER([data, n=5])

    信号过滤, 过滤连续出现的信号。

    用法：FILTER(X,N): X 满足条件后，删除其后 N 周期内的数据置为 0。

    例如：FILTER(CLOSE>OPEN,5) 查找阳线，5 天内再次出现的阳线不被记录在内。

    :param Indicator data: 输入数据
    :param int|Indicaot|IndParam n: 过滤周期
    :rtype: Indicator)");

    def("BARSSINCE", BARSSINCE_1);
    def("BARSSINCE", BARSSINCE_2);
    def("BARSSINCE", BARSSINCE_3, R"(BARSSINCE([data])

    第一个条件成立位置到当前的周期数。

    用法：BARSSINCE(X):第一次X不为0到现在的天数。

    例如：BARSSINCE(HIGH>10)表示股价超过10元时到当前的周期数

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("BARSLAST", BARSLAST_1);
    def("BARSLAST", BARSLAST_2);
    def("BARSLAST", BARSLAST_3, R"(BARSLAST([data])

    上一次条件成立位置 上一次条件成立到当前的周期数。

    用法：BARSLAST(X): 上一次 X 不为 0 到现在的天数。

    例如：BARSLAST(CLOSE/REF(CLOSE,1)>=1.1) 表示上一个涨停板到当前的周期数

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("SUMBARS", SUMBARS_1, (arg("a")));
    def("SUMBARS", SUMBARS_2, (arg("a")));
    def("SUMBARS", SUMBARS_3, (arg("data"), arg("a")));
    def("SUMBARS", SUMBARS_4, (arg("data"), arg("a")));
    def("SUMBARS", SUMBARS_5, (arg("data"), arg("a")), R"(SUMBARS([data,] a)

    累加到指定周期数, 向前累加到指定值到现在的周期数

    用法：SUMBARS(X,A):将X向前累加直到大于等于A,返回这个区间的周期数

    例如：SUMBARS(VOL,CAPITAL)求完全换手到现在的周期数

    :param Indicator data: 输入数据
    :param float a|Indicator|IndParam: 指定累加和
    :rtype: Indicator)");

    def("BARSCOUNT", BARSCOUNT_1);
    def("BARSCOUNT", BARSCOUNT_2, R"(BARSCOUNT([data])

    有效值周期数, 求总的周期数。

    用法：BARSCOUNT(X)第一个有效数据到当前的天数。

    例如：BARSCOUNT(CLOSE)对于日线数据取得上市以来总交易日数，对于1分钟线取得当日交易分钟数。

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("BACKSET", BACKSET_1, (arg("n") = 2));
    def("BACKSET", BACKSET_2, (arg("n")));
    def("BACKSET", BACKSET_3, (arg("data"), arg("n")));
    def("BACKSET", BACKSET_4, (arg("data"), arg("n")));
    def("BACKSET", BACKSET_5, (arg("data"), arg("n") = 2), R"(BACKSET([data, n=2])

    向前赋值将当前位置到若干周期前的数据设为1。

    用法：BACKSET(X,N),X非0,则将当前位置到N周期前的数值设为1。

    例如：BACKSET(CLOSE>OPEN,2)若收阳则将该周期及前一周期数值设为1,否则为0

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: N周期
    :rtype: Indicator)");

    def("TIMELINE", TIMELINE_1);
    def("TIMELINE", TIMELINE_2, R"(TIMELINE([k])

    分时价格数据

    :param KData k: 上下文
    :rtype: Indicator)");

    def("TIMELINEVOL", TIMELINEVOL_1);
    def("TIMELINEVOL", TIMELINEVOL_2, R"(TIMELINEVOL([k])

    分时成交量数据

    :param KData k: 上下文
    :rtype: Indicator)");

    def("DMA", DMA, R"(DMA(ind, a)

    动态移动平均

    用法：DMA(X,A),求X的动态移动平均。

    算法：若Y=DMA(X,A) 则 Y=A*X+(1-A)*Y',其中Y'表示上一周期Y值。

    例如：DMA(CLOSE,VOL/CAPITAL)表示求以换手率作平滑因子的平均价

    :param Indicator ind: 输入数据
    :param Indicator a: 动态系数
    :rtype: Indicator)");

    def("AVEDEV", AVEDEV_1, (arg("data"), arg("n") = 22));
    def("AVEDEV", AVEDEV_2, (arg("data"), arg("n")));
    def("AVEDEV", AVEDEV_3, (arg("data"), arg("n")), R"(AVEDEV(data[, n=22])

    平均绝对偏差，求X的N日平均绝对偏差

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("DEVSQ", DEVSQ_1, (arg("n") = 10));
    def("DEVSQ", DEVSQ_2, (arg("n")));
    def("DEVSQ", DEVSQ_3, (arg("data"), arg("n")));
    def("DEVSQ", DEVSQ_4, (arg("data"), arg("n")));
    def("DEVSQ", DEVSQ_5, (arg("data"), arg("n") = 10), R"(DEVSQ([data, n=10])

    数据偏差平方和，求X的N日数据偏差平方和

    :param Indicator data: 输入数据
    :param int|Indicator n: 时间窗口
    :rtype: Indicator)");

    def("ROC", ROC_1, (arg("n") = 10));
    def("ROC", ROC_2, (arg("n")));
    def("ROC", ROC_3, (arg("data"), arg("n")));
    def("ROC", ROC_4, (arg("data"), arg("n")));
    def("ROC", ROC_5, (arg("data"), arg("n") = 10), R"(ROC([data, n=10])

    变动率指标: ((price / prevPrice)-1)*100

    :param data: 输入数据
    :param int n: 时间窗口
    :rtype: Indicator)");

    def("ROCP", ROCP_1, (arg("n") = 10));
    def("ROCP", ROCP_2, (arg("n")));
    def("ROCP", ROCP_3, (arg("data"), arg("n")));
    def("ROCP", ROCP_4, (arg("data"), arg("n")));
    def("ROCP", ROCP_5, (arg("data"), arg("n") = 10), R"(ROCP([data, n=10])

    变动率指标: (price - prevPrice) / prevPrice

    :param data: 输入数据
    :param int n: 时间窗口
    :rtype: Indicator)");

    def("ROCR", ROCR_1, (arg("n") = 10));
    def("ROCR", ROCR_2, (arg("n")));
    def("ROCR", ROCR_3, (arg("data"), arg("n")));
    def("ROCR", ROCR_4, (arg("data"), arg("n")));
    def("ROCR", ROCR_5, (arg("data"), arg("n") = 10), R"(ROCR([data, n=10])

    变动率指标: (price / prevPrice)

    :param data: 输入数据
    :param int n|Indicator|IndParam: 时间窗口
    :rtype: Indicator)");

    def("ROCR100", ROCR100_1, (arg("n") = 10));
    def("ROCR100", ROCR100_2, (arg("n")));
    def("ROCR100", ROCR100_3, (arg("data"), arg("n")));
    def("ROCR100", ROCR100_4, (arg("data"), arg("n")));
    def("ROCR100", ROCR100_5, (arg("data"), arg("n") = 10), R"(ROCR100([data, n=10])

    变动率指标: (price / prevPrice) * 100

    :param data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("AD", AD_1);
    def("AD", AD_2, R"(AD(kdata)

   累积/派发线

   :param KData kdata: k线数据
   :rtype: Indicator)");

    def("COST", COST_1, (arg("x") = 10.0));
    def("COST", COST_2, (arg("k"), arg("x") = 10.0), R"(COST(k[, x=10.0])

    成本分布

    用法：COST(k, X) 表示X%获利盘的价格是多少

    例如：COST(k, 10),表示10%获利盘的价格是多少，即有10%的持仓量在该价格以下，其余90%在该价格以上，为套牢盘 该函数仅对日线分析周期有效

    :param KData k: 关联的K线数据
    :param float x: x%获利价格, 0~100
    :rtype: Indicator)");

    def("ALIGN", ALIGN_1);
    def("ALIGN", ALIGN_2);
    def("ALIGN", ALIGN_3);
    def("ALIGN", ALIGN_4, R"(ALIGN(data, ref):

    按指定的参考日期对齐

    :param Indicator data: 输入数据
    :param ref: 指定做为日期参考的 DatetimeList、Indicator 或 KData
    :retype: Indicator)");

    def("DROPNA", DROPNA_1);
    def("DROPNA", DROPNA_2, R"(DROPNA([data])

    删除 nan 值

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("ADVANCE", ADVANCE,
        (arg("query") = KQueryByIndex(-100), arg("market") = "SH", arg("stk_type") = STOCKTYPE_A,
         arg("ignore_context") = false),
        R"(ADVANCE([query=Query(-100), market='SH', stk_type='constant.STOCKTYPE_A'])

    上涨家数。当存在指定上下文且 ignore_context 为 false 时，将忽略 query, market, stk_type 参数。

    :param Query query: 查询条件
    :param str market: 所属市场，等于 "" 时，获取所有市场
    :param int stk_type: 证券类型, 大于 constant.STOCKTYPE_TMP 时，获取所有类型证券
    :param bool ignore_context: 是否忽略上下文。忽略时，强制使用 query, market, stk_type 参数。
    :rtype: Indicator)");

    def("DECLINE", DECLINE,
        (arg("query") = KQueryByIndex(-100), arg("market") = "SH", arg("stk_type") = STOCKTYPE_A,
         arg("ignore_context") = false),
        R"(DECLINE([query=Query(-100), market='SH', stk_type='constant.STOCKTYPE_A'])

    下跌家数。当存在指定上下文且 ignore_context 为 false 时，将忽略 query, market, stk_type 参数。

    :param Query query: 查询条件
    :param str market: 所属市场，等于 "" 时，获取所有市场
    :param int stk_type: 证券类型, 大于 constant.STOCKTYPE_TMP 时，获取所有类型证券
    :param bool ignore_context: 是否忽略上下文。忽略时，强制使用 query, market, stk_type 参数。
    :rtype: Indicator)");
}


//  (C) 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_CARDINAL_TRIGONOMETRIC_HPP
#define BOOST_MATH_INTERPOLATORS_CARDINAL_TRIGONOMETRIC_HPP
#include <memory>
#include <boost/math/interpolators/detail/cardinal_trigonometric_detail.hpp>

namespace boost { namespace math { namespace interpolators {

template<class RandomAccessContainer>
class cardinal_trigonometric
{
public:
    using Real = typename RandomAccessContainer::value_type;
    cardinal_trigonometric(RandomAccessContainer const & v, Real t0, Real h)
    {
        m_impl = std::make_shared<interpolators::detail::cardinal_trigonometric_detail<Real>>(v.data(), v.size(), t0, h);
    }

    Real operator()(Real t) const
    {
        return m_impl->operator()(t);
    }

    Real prime(Real t) const
    {
        return m_impl->prime(t);
    }

    Real double_prime(Real t) const
    {
        return m_impl->double_prime(t);
    }

    Real period() const
    {
        return m_impl->period();
    }

    Real integrate() const
    {
        return m_impl->integrate();
    }

    Real squared_l2() const
    {
        return m_impl->squared_l2();
    }

private:
    std::shared_ptr<interpolators::detail::cardinal_trigonometric_detail<Real>> m_impl;
};

}}}
#endif