text:
/**
 * Copyright (c) 2017 Melown Technologies SE
 *
 * 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 <cstdlib>
#include <memory>
#include <array>
#include <stack>
#include <set>
#include <algorithm>

#include <boost/format.hpp>
#include <boost/filesystem.hpp>
#include <boost/variant.hpp>
#include <boost/range.hpp>

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

#include "utility/streams.hpp"
#include "utility/expect.hpp"
#include "utility/stl-helpers.hpp"

#include "math/math.hpp"
#include "math/transform.hpp"
#include "math/filters.hpp"

#include "imgproc/uvpack.hpp"
#include "imgproc/scanconversion.hpp"
#include "imgproc/const-raster.hpp"
#include "imgproc/reconstruct.hpp"
#include "imgproc/inpaint.hpp"

#include "../opencv/atlas.hpp"
#include "../opencv/texture.hpp"

#include "../meshop.hpp"
#include "../math.hpp"

/** Altas transformation background
 *
 * NB: patches in original textures are intended for OpenGL texturing which uses
 * bilinear interpolation thus we have no extra space around them. We need to
 * keep this in mind when transforming patches from one atlas space to the
 * another.
 *
 * Each patch is transformed from source atlas to destination atlas using map
 * from rotated rectangle to upright rectangle (using eclidean transformation
 * matrix).
 *
 * Trafo matrix is constructed in denormalized texture coordinates (i.e. grid
 * coordinates in the atlases.
 *
 * When mapping texture coordinates they are used as is.
 *
 * When mapping imagery coordinates are first converted from pixel registration
 * to grid registration and after convertion they are converted
 * back. I.e. shifted by 1/2 pixel to the bottom right and back again.
 *
 * Imagery is filtered by Catmul-Rom filter using information about source pixel
 * validity. Validity is established by rendering original texturing 2D mesh
 * into a binary mask which is dilated by one pixel.
 *
 * Since `scanconvert' algorithm is right-bottom bound we intentionally dilate
 * mesh only to the top and left side only.
 *
 * Face vertices are rendered into mask explicitly due to scanconvert's
 * deficiencies as well.
 *
 * NB: Reconstruct treats positive and negative weights from filter differently:
 * if full kernel are is not valid only positives are taken into account.
 */

namespace vtslibs { namespace vts {

namespace {

namespace fs = boost::filesystem;

const char *VTS_MESH_MERGE_DUMP_DIR(std::getenv("VTS_MESH_MERGE_DUMP_DIR"));
const char *NO_ATLAS_INPAINT(std::getenv("NO_ATLAS_INPAINT"));

const std::size_t DefaultMaxVertexCount(1 << 15);
const std::size_t DefaultMaxFaceCount(1 << 15);

typedef std::vector<cv::Mat> MatList;

inline math::Point2d normalize(const imgproc::UVCoord &uv
                               , const cv::Size &texSize)
{
    return { uv.x / texSize.width
            , 1.0 - uv.y / texSize.height };
}

class TextureInfo {
public:
    TextureInfo(const SubMesh &sm, const cv::Mat &texture
                , const SubmeshMergeOptions &options)
        : tc_(opencv::denormalize(sm.tc, texture.size()))
        , faces_(sm.facesTc), texture_(texture)
        , mask_(texture.rows, texture.cols, CV_8U, cv::Scalar(0x00))
    {
        switch (options.atlasPacking) {
        case SubmeshMergeOptions::AtlasPacking::legacy:
            opencv::rasterizeMaskLegacy(mask_, faces_, tc_);
            break;

        case SubmeshMergeOptions::AtlasPacking::progressive:
            opencv::rasterizeMask(mask_, faces_, tc_);
            break;
        }
    }

    const math::Points2d& tc() const { return tc_; }
    const Faces& faces() const { return faces_; }
    const cv::Mat& texture() const { return texture_; }
    const cv::Mat& mask() const { return mask_; }

    const math::Point2d& uv(const Face &face, int index) const {
        return tc_[face(index)];
    }

    imgproc::UVCoord uvCoord(const Face &face, int index) const {
        const auto &p(uv(face, index));
        return imgproc::UVCoord(p(0), p(1));
    }

    const math::Point2d& uv(int index) const {
        return tc_[index];
    }

    typedef std::vector<TextureInfo> list;

    double area(int faceIndex) const {
        const auto &face((faces_)[faceIndex]);
        return triangleArea(tc_[face(0)], tc_[face(1)], tc_[face(2)]);
    }

    /** Face barycenter.
     */
    math::Point2 center(int faceIndex) const {
        const auto &face((faces_)[faceIndex]);
        return ((tc_[face(0)] + tc_[face(1)] + tc_[face(2)]) / 3.0);
    }

    const Face& face(int faceIndex) const {
        return (faces_)[faceIndex];
    }

    Face& ncface(int faceIndex) {
        return (faces_)[faceIndex];
    }

    // Duplicates existing texture coordinates in the systemand returns new
    // index.
    int duplicateTc(int index) {
        auto ni(tc_.size());
        tc_.push_back(tc_[index]);
        return ni;
    }

private:
    math::Points2d tc_;
    Faces faces_;
    cv::Mat texture_;
    cv::Mat mask_;
};

bool validMatPos(int x, int y, const cv::Mat &mat)
{
    return (x >= 0) && (x < mat.cols) && (y >= 0) && (y < mat.rows);
}

void clampMatPos(int &x, int &y, const cv::Mat &mat)
{
    if (x < 0) {
        x = 0;
    } else if (x >= mat.cols) {
        x = mat.cols - 1;
    }

    if (y < 0) {
        y = 0;
    } else if (y >= mat.rows) {
        y = mat.rows - 1;
    }
}

typedef Face::value_type VertexIndex;
typedef std::set<VertexIndex> VertexIndices;

/** Continuous mesh component.
 */
struct Component {
    /** Set of components's indices to faces
     */
    std::set<int> faces;

    /** Set of components's indices to texture coordinates
     */
    VertexIndices indices;

    /** UV rectangle.
     */
    imgproc::UVRect rect;

    /** Best rectangle circumscribed around this component.
     */
    cv::RotatedRect rrect;

    /** Transformation from rrect to output atlas.
     */
    math::Matrix4 rrectTrafo;

    typedef std::shared_ptr<Component> pointer;
    typedef std::vector<pointer> list;
    typedef std::set<pointer> set;

    Component() {}

    Component(int findex, const Face &face)
        : faces{findex}, indices{face(0), face(1), face(2)}
    {}

    void add(int findex, const Face &face) {
        faces.insert(findex);
        indices.insert({ face(0), face(1), face(2) });
    }

    void add(const Component &other) {
        faces.insert(other.faces.begin(), other.faces.end());
        indices.insert(other.indices.begin(), other.indices.end());
    }

    void copy(cv::Mat &tex, const cv::Mat &texture
              , const cv::Mat &mask) const;

    imgproc::UVCoord adjustUV(const math::Point2 &p) const {
        auto np(math::transform(rrectTrafo, p));
        return imgproc::UVCoord(np(0) + rect.packX, np(1) + rect.packY);
    }

    void bestRectangle(const TextureInfo &tx);

    double area(const TextureInfo &tx) const;
};

/** Single submesh broken into components.
 */
struct ComponentInfo {
    /** Mesh broken to continuous components.
     */
    Component::set components;

    /** Mapping between texture coordinate index to owning component.
     */
    Component::list tcMap;

    /** Texturing information, i.e. the context of the problem.
     */
    TextureInfo *tx;

    typedef std::vector<ComponentInfo> list;

    ComponentInfo(const TileId &tileId, int id, TextureInfo &tx);

    void copy(cv::Mat &tex) const;

    void debug(const fs::path &dir, const cv::Mat &outAtlas) const;

private:
    /** Breaks component into best fitting parts.
     */
    void breakComponent(const Component::pointer &c);

    /** Adds new components.
     */
    void addComponent(const Component::pointer &c);

    TileId tileId_;
    int id_;
};

ComponentInfo::ComponentInfo(const TileId &tileId, int id, TextureInfo &tx)
    : tcMap(tx.tc().size()), tx(&tx), tileId_(tileId), id_(id)
{
    const auto &faces(tx.faces());
    Component::list fMap(tx.faces().size());
    Component::list tMap(tx.tc().size());

    typedef std::array<Component::pointer*, 3> FaceComponents;

    // sorts face per-vertex components by number of faces
    // no components are considered empty
    auto sortBySize([](FaceComponents &fc)
    {
        std::sort(fc.begin(), fc.end()
                  , [](const Component::pointer *l, Component::pointer *r)
                  -> bool
        {
            // prefer non-null
            if (!*l) { return false; }
            if (!*r) { return true; }

            // both are non-null; prefer longer
            return ((**l).faces.size() > (**r).faces.size());
        });
    });

    // temporary toplevel components, broken into smalled ones afterwars
    Component::set tlComponents;

    for (std::size_t i(0), e(faces.size()); i != e; ++i) {
        const auto &face(faces[i]);

        FaceComponents fc =
            { { &tMap[face(0)], &tMap[face(1)], &tMap[face(2)] } };
        sortBySize(fc);

        auto assign([&](Component::pointer &owner, Component::pointer &owned)
                    -> void
        {
            // no-op
            if (owned == owner) { return; }

            if (!owned) {
                // no component assigned yet
                owned = owner;
                return;
            }

            // forget this component beforehand
            tlComponents.erase(owned);

            // grab owned by value to prevent overwrite
            const auto old(*owned);

            // merge components
            owner->add(old);

            // move everything to owner
            for (auto index : old.faces) { fMap[index] = owner; }
            for (auto index : old.indices) { tMap[index] = owner; }
        });

        if (*fc[0] || *fc[1] || *fc[2]) {
            auto &owner(*fc[0]);
            fMap[i] = owner;
            owner->add(i, face);
            assign(owner, *fc[1]);
            assign(owner, *fc[2]);
        } else {
            // create new component
            tlComponents.insert
                (fMap[i] = *fc[0] = *fc[1] = *fc[2]
                 = std::make_shared<Component>(i, face));
        }
    }

    // cut components to best fitting subcomponets
    for (const auto &c : tlComponents) {
        breakComponent(c);
    }
}

class BrokenComponent {
public:
    Component::pointer component;
    double margin;

    typedef std::stack<BrokenComponent> stack;

    BrokenComponent(const TextureInfo &tx
                    , const Component::pointer &component
                    , double margin = -1.0)
        : component(component), margin(margin)
        , area_(component->area(tx))
    {
        component->bestRectangle(tx);
    }

    BrokenComponent()
        : component(std::make_shared<Component>())
        , margin(-1.0), area_(0.0)
    {}

    bool cuttable() const {
        return (component->faces.size() > 2);
    }

    const cv::RotatedRect& rrect() const { return component->rrect; }

    void cut(const TextureInfo &tx
             , bool vertical, double distance
             , BrokenComponent &best1, BrokenComponent &best2)
        const;

    bool empty() const { return component->faces.empty(); }

    double marginArea() const {
        const auto &cr(rrect());
        return ((cr.size.width + 2.0 * margin)
                * (cr.size.height + 2.0 * margin));
    }

    double efficiency() const {
        if (empty()) { return 0; }
        return (area_ / marginArea());
    }

    static double efficiency(const BrokenComponent &bc1
                             , const BrokenComponent &bc2)
    {
        if (bc1.empty() || bc2.empty()) { return 0.0; }

        return ((bc1.area_ + bc2.area_) /
                (bc1.marginArea() + bc2.marginArea()));
    }

private:
    double area_;
};

void BrokenComponent::cut(const TextureInfo &tx
                          , bool vertical, double distance
                          , BrokenComponent &best1, BrokenComponent &best2)
    const
{
    const auto &cr(rrect());

    // angle in radians
    const auto a((cr.angle * M_PI / 180.) + (vertical ? 0 : M_PI_2));
    const auto sa(std::sin(a));
    const auto ca(std::cos(a));
    const auto &rc(cr.center);
    const auto &rs(cr.size);
    const double xline(distance - ((vertical ? rs.width : rs.height) / 2.0));

    auto c1(std::make_shared<Component>());
    auto c2(std::make_shared<Component>());

    for (auto findex : component->faces) {
        auto barycenter(tx.center(findex));
        // rotate barycenter and check at which side it resides
        if ((ca * (barycenter(0) - rc.x) - sa * (barycenter(1) - rc.y))
            < xline)
        {
            c1->add(findex, tx.face(findex));
        } else {
            c2->add(findex, tx.face(findex));
        }
    }

    // construct components from cuts
    BrokenComponent cut1(tx, c1, margin);
    BrokenComponent cut2(tx, c2, margin);

    if (efficiency(cut1, cut2) > efficiency(best1, best2)) {
        // better cuts, reassign
        best1 = cut1;
        best2 = cut2;
    }
}

void ComponentInfo::breakComponent(const Component::pointer &start)
{
    const double margin(0.5);
    const double cutDistance(7.0);

    BrokenComponent::stack cuttable;
    cuttable.emplace(*tx, start, margin);

    while (!cuttable.empty()) {
        // get top of the stack
        const auto &top(cuttable.top());
        const auto &cr(top.rrect());

        auto add([&]()
        {
            LOG(info1) << "---- Patch uncuttable";
            addComponent(top.component);
            cuttable.pop();
        });

        if (!top.cuttable()) {
            add();
            continue;
        }

        unsigned int vCuts(std::floor(cr.size.width / cutDistance));
        unsigned int hCuts(std::floor(cr.size.height / cutDistance));
        double vCutDist(cr.size.width / vCuts);
        double hCutDist(cr.size.height / hCuts);

        if (!vCuts && !hCuts) {
            // patch is uncuttable -> move to output
            add();
            continue;
        }

        BrokenComponent best1, best2;

        // vertical cuts
        for (uint i = 1; i < vCuts; ++i) {
            top.cut(*tx, true, i * vCutDist, best1, best2);
        }

        // horizontal cuts
        for (uint i = 1; i < hCuts; ++i) {
            top.cut(*tx, false, i * hCutDist, best1, best2);
        }

        auto ne(BrokenComponent::efficiency(best1, best2));
        if (ne > top.efficiency()) {
            // split patches have better efficiency than original patch ->
            // replace it with them and go on
            LOG(info1) << "---- Patches with better efficiency found: " << ne;
            cuttable.pop();
            cuttable.push(best1);
            cuttable.push(best2);
        } else {
            // patch is uncuttable -> move to output
            add();
            continue;
        }
    }
}

void ComponentInfo::addComponent(const Component::pointer &c)
{
    components.insert(c);

    typedef std::map<int, int> Remap;
    Remap tcRemap;

    // process component's faces
    for (auto findex : c->faces) {
        auto &face(tx->ncface(findex));

        // process all face's vertices
        for (auto &index : face) {
            auto &currentComponent(tcMap[index]);

            if (currentComponent && (currentComponent != c)) {
                // vertex shared with another component

                // check whether we have already remapped it
                int ni;
                auto ftcRemap(tcRemap.find(index));
                if (ftcRemap == tcRemap.end()) {
                    // duplicate and remember
                    ni = tx->duplicateTc(index);
                    tcRemap.insert(Remap::value_type(index, ni));
                    tcMap.push_back(c);
                } else {
                    // alredy remapped -> reuse
                    ni = ftcRemap->second;
                }

                // update (NB: index is a reference!)
                index = ni;
            }

            // map component to texture coordinate
            tcMap[index] = c;
        }
    }

    auto remap([&](VertexIndices &set, const Remap &r)
    {
        for (const auto &item : r) {
            set.erase(item.first);
            set.insert(item.second);
        }
    });

    // remap faces and tc (if any were changed
    remap(c->indices, tcRemap);
}

void ComponentInfo::copy(cv::Mat &tex) const
{
    for (const auto &c : components) {
        c->copy(tex, tx->texture(), tx->mask());
    }
}

void ComponentInfo::debug(const fs::path &dir, const cv::Mat &outAtlas)
    const
{
    int border(16);

    const auto &texture(tx->texture());

    cv::Mat dr(texture.rows + outAtlas.rows + 3 * border
               , std::max(texture.cols, outAtlas.cols) + 2 * border, CV_8UC3
               , cv::Scalar(0x80, 0x80, 0x80));

    cv::Mat drTx(dr, cv::Rect(border, border, texture.cols, texture.rows));

    // combine input texture with its mask and put into drawable
    {
        cv::Mat colorMask;
        const auto &mask(tx->mask());
        cv::cvtColor(mask, colorMask, cv::COLOR_GRAY2BGR);
        addWeighted(texture, 0.7, colorMask, 0.3, 0.0, drTx);
    }

    // put output atlas into drawable
    cv::Mat drAt(dr, cv::Rect
                 (border, texture.rows + 2 * border
                  , outAtlas.cols, outAtlas.rows));
    outAtlas.convertTo(drAt, -1, 0.7);

    // draw both input and output rectangles
    auto &rng(cv::theRNG());

    for (const auto &c : components) {
        cv::Scalar color(rng(223) + 32, rng(223) + 32, rng(223) + 32);

        cv::Point2f pts[4];
        c->rrect.points(pts);

        std::vector<std::vector<cv::Point2i>> contours;
        {
            contours.emplace_back();
            auto &contour(contours.back());
            for (int i : { 0, 1, 2, 3 }) {
                contour.emplace_back(int(pts[i].x), int(pts[i].y));
            }
        }

        cv::drawContours(drTx, contours, 0, color, 1); //CV_FILLED);

        const auto &pr(c->rect);
        cv::Rect r(pr.packX, pr.packY, pr.width(), pr.height());
        cv::rectangle(drAt, r, color, 1); //CV_FILLED);
    }

    fs::create_directories(dir);
    cv::imwrite((dir / str(boost::format("%s-%d-repacked.png")
                           % tileId_ % id_)).string()
                 , dr);
}

imgproc::UVRect uvRect(float width, float height)
{
    imgproc::UVRect r;
    r.update({0.f, 0.f});
    r.update({width, height});
    return r;
}

void Component::bestRectangle(const TextureInfo &tx) {
    // find best rectangle
    if (!indices.empty()) {
        std::vector<cv::Point2f> points;
        points.reserve(indices.size());

        // process all points
        for (const auto i : indices) {
            const auto &p(tx.uv(i));
            points.emplace_back(p(0), p(1));
        }

        // find best rectangle fitting around this patch
        rrect = cv::minAreaRect(points);

        // calculate UV rectangle for this rotated rect
        rect = uvRect(rrect.size.width, rrect.size.height);
    }

    {
        // calculate transformation matrix from original atlas to best
        // rectangle
        rrectTrafo = boost::numeric::ublas::identity_matrix<double>(4);

        const double a(rrect.angle * M_PI / 180.);
        rrectTrafo(0, 0) = rrectTrafo(1, 1) = std::cos(a);
        rrectTrafo(1, 0) = -(rrectTrafo(0, 1) = std::sin(a));

        const auto shift(math::transform
                         (rrectTrafo, math::Point2
                          (rrect.center.x, rrect.center.y)));

        rrectTrafo(0, 3) = -shift(0) + double(rect.width()) / 2.0;
        rrectTrafo(1, 3) = -shift(1) + double(rect.height()) / 2.0;
    }
}

double Component::area(const TextureInfo &tx) const
{
    double area(0.0);
    for (const auto &face : faces) {
        area += tx.area(face);
    }
    return area;
}

/** Const raster for texture with cv::Mat-based mask.
 */
class TextureRaster : public imgproc::CvConstRaster<cv::Vec3b>
{
public:
    TextureRaster(const cv::Mat &texture, const cv::Mat &mask)
        : imgproc::CvConstRaster<cv::Vec3b>(texture), mask_(mask)
    {}

    bool valid(int x, int y) const {
        // queries matrix for bounds and mask for pixel validity
        return (imgproc::CvConstRaster<cv::Vec3b>::valid(x, y)
                && mask_.at<std::uint8_t>(y, x));
    }

private:
    const cv::Mat &mask_;
};

void Component::copy(cv::Mat &tex, const cv::Mat &texture, const cv::Mat &mask)
    const
{
    const math::CatmullRom2 filter(2.0, 2.0);

    TextureRaster raster(texture, mask);

    // and invert: patch to original atlas
    math::Matrix4 trafo(math::matrixInvert(rrectTrafo));

    const math::Size2 size(rect.width(), rect.height());

    // copy texture from source best rectangle to output uv rectangle
    for (int y = 0; y < size.height; ++y) {
        // destination point + bound check
        const int yy(y + rect.packY);
        for (int x = 0; x < size.width; ++x) {
            // destination point + bound check
            const int xx(x + rect.packX);

            // compute source point
            // NB: we have mapped corners of rectanges, not pixel centers
            auto pos(math::transform
                     (trafo, math::Point2(x + 0.5, y + 0.5)));
            pos(0) -= 0.5; pos(1) -= 0.5;

            // reconstruct point using filter
            auto res(imgproc::reconstruct(raster, filter, pos));

            // store result
            tex.at<cv::Vec3b>(yy, xx) = cv::Vec3b(res[0], res[1], res[2]);
        }
    }
}

/** Joins textures into single texture.
 */
std::tuple<cv::Mat, math::Points2d, Faces>
joinTextures(const TileId &tileId, TextureInfo::list texturing);

typedef std::tuple<Mesh::pointer, Atlas::pointer> MeshAtlas;

typedef std::vector<int> Indices;

/** TODO: observe other differences in submeshes (texture mode, external
 *  texture coordinates, texture layer, UV area factor etc.
 *  Not so simple :)
 */
struct Range {
    const Indices *indices;
    std::size_t indexStart;
    std::size_t indexEnd;
    SubMesh::SurfaceReference surface;
    double uvAreaScale;
    SubMesh::ZIndex zIndex;
    bool textured;

    std::size_t size() const { return indexEnd - indexStart; }
    bool empty() const { return !size(); }
    Indices::const_iterator begin() const {
        return indices->begin() + indexStart;
    }
    Indices::const_iterator end() const {
        return indices->begin() + indexEnd;
    }
    int front() const { return (*indices)[indexStart]; }

    typedef std::vector<Range> list;

    Range(const Indices &indices, const SubMesh &sm, bool textured)
        : indices(&indices), surface(sm.surfaceReference)
        , uvAreaScale(sm.uvAreaScale), zIndex(sm.zIndex)
        , textured(textured)
    {}

    bool compatible(const SubMesh &sm) const {
        return ((surface == sm.surfaceReference)
                && (uvAreaScale == sm.uvAreaScale)
                && (zIndex == sm.zIndex));
    }
};

template<typename CharT, typename Traits>
inline std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits> &os, const Range &r)
{
    os << "Range{sr=" << int(r.surface)
       << ", z=" << r.zIndex
       << ", uvas=" << r.uvAreaScale
       << ", tx=" << r.textured
       << ", start=" << r.indexStart << ", end=" << r.indexEnd
       << ", sm=" << utility::join
        (boost::make_iterator_range(r.begin(), r.end()), ",")
       << "}";

    return os;
}

struct EntityCounter {
    std::size_t count;
    std::size_t vertexCount;
    std::size_t tcCount;
    std::size_t faceCount;

    EntityCounter(std::size_t vertexCount = 0, std::size_t faceCount = 0)
        : count(0), vertexCount(vertexCount), tcCount(vertexCount)
        , faceCount(faceCount)
    {}

    EntityCounter(const SubMesh &sm)
        : count(1), vertexCount(sm.vertices.size()), tcCount(sm.tc.size())
        , faceCount(sm.faces.size())
    {}

    void update(const SubMesh &sm) {
        vertexCount += sm.vertices.size();
        tcCount += sm.tc.size();
        faceCount += sm.faces.size();
        ++count;
    }

    bool check(const EntityCounter &limits, bool atLeastOne = false) const {
        if (atLeastOne && (count == 1)) { return true; }
        return ((vertexCount <= limits.vertexCount)
                && (tcCount <= limits.tcCount)
                && (faceCount <= limits.faceCount));
    }

    bool available(const SubMesh &sm) const {
        if ((sm.vertices.size() + 3) > vertexCount) { return false; }
        if ((sm.tc.size() + 3) > tcCount) { return false; }
        if ((sm.faces.size() + 1) > faceCount) { return false; }
        return true;
    }

    void reset() { vertexCount = faceCount = tcCount = count = 0; }
};

/** Precondition: submeshes from the same source are grouped.
 */
Range::list groupSubmeshes(const SubMesh::list &sms, Indices &indices
                           , std::size_t textured
                           , const EntityCounter &meshLimits)
{
    // compare submeshes to group together submeshes with the same
    // surfaceReference, zIndex and uvAreaScale (i.e. compatible ones)
    const auto compareSubmeshes([&](int li, int ri) -> bool
    {
        const auto &l(sms[li]);
        const auto &r(sms[ri]);

        // order: surface reference, z-index, UV area scale
        return (std::tie(l.surfaceReference, l.zIndex, l.uvAreaScale)
                < std::tie(r.surfaceReference, r.zIndex, r.uvAreaScale));
    });

    Range::list out;

    const auto splitToRanges([&](std::size_t begin, std::size_t end
                                 , bool textured)
    {
        bool first(true);
        EntityCounter ec;
        for (auto i(begin); i != end; ++i) {
            const auto &sm(sms[indices[i]]);
            ec.update(sm);
            if (first || !out.back().compatible(sm)
                || !ec.check(meshLimits, true))
            {
                out.emplace_back(indices, sm, textured);
                out.back().indexStart = i;
                ec.reset();
                first = false;
            }
            out.back().indexEnd = i + 1;
        }
    });

    // prepare indices
    indices.reserve(sms.size());
    for (std::size_t i(0), e(sms.size()); i != e; ++i) {
        indices.push_back(i);
    }

    // sanitize input
    if (textured > sms.size()) { textured = sms.size(); }

    // sort both parts
    if (textured) {
        std::sort(indices.begin(), indices.begin() + textured
                  , compareSubmeshes);
        splitToRanges(0, textured, true);
    }

    // non-textured
    if (sms.size() > textured) {
        std::sort(indices.begin() + textured, indices.end(), compareSubmeshes);
        splitToRanges(textured, sms.size(), false);
    }

    return out;
}

typedef opencv::HybridAtlas HybridAtlas;

typedef boost::variant<RawAtlas::pointer
                       , HybridAtlas::pointer> InputAtlas;

struct GetAtlas : public boost::static_visitor<Atlas::pointer>
{
    Atlas::pointer operator()(const RawAtlas::pointer &atlas) const {
        return atlas;
    }

    Atlas::pointer operator()(const HybridAtlas::pointer &atlas) const {
        return atlas;
    }
};

class AsHybridAtlas
    : public boost::static_visitor<HybridAtlas::pointer>
{
public:
    AsHybridAtlas(std::size_t count, int quality)
        : count_(count), quality_(quality)
    {}

    HybridAtlas::pointer operator()(const RawAtlas::pointer &atlas) const {
        return std::make_shared<HybridAtlas>(count_, *atlas, quality_);
    }

    HybridAtlas::pointer operator()(const HybridAtlas::pointer &atlas) const {
        return std::make_shared<HybridAtlas>(count_, *atlas, quality_);
    }

private:
    std::size_t count_;
    int quality_;
};

class GetImage : public boost::static_visitor<cv::Mat>
{
public:
    GetImage(std::size_t index) : index_(index) {}

    cv::Mat operator()(const RawAtlas::pointer &atlas) const {
        return HybridAtlas::imageFromRaw(atlas->get(index_));
    }

    cv::Mat operator()(const HybridAtlas::pointer &atlas) const {
        return atlas->get(index_);
    }

private:
    std::size_t index_;
};

class AddItem : public boost::static_visitor<>
{
public:
    AddItem(const HybridAtlas::pointer &atlas, std::size_t index)
        : atlas_(atlas), index_(index)
    {}

    void operator()(const RawAtlas::pointer &atlas) const {
        atlas_->add(atlas->get(index_));
    }

    void operator()(const HybridAtlas::pointer &atlas) const {
        atlas_->add(atlas->entry(index_));
    }

private:
    HybridAtlas::pointer atlas_;
    std::size_t index_;
};

inline std::size_t applyDefault(std::size_t value, std::size_t dflt)
{
    return value ? value : dflt;
}

class MeshAtlasBuilder {
public:
    MeshAtlasBuilder(const TileId &tileId
                     , const Mesh::pointer &mesh
                     , const InputAtlas &atlas
                     , int textureQuality
                     , const SubmeshMergeOptions &options)
        : tileId_(tileId), textureQuality_(textureQuality), options_(options)
        , originalMesh_(mesh), originalAtlas_(atlas)
        , oSubmeshes_(mesh->submeshes)
        , meshLimits_(applyDefault(options.maxVertexCount
                                   , DefaultMaxVertexCount)
                      , (applyDefault(options.maxFaceCount
                                      , DefaultMaxFaceCount)))
        , meshEnd_(), atlasEnd_()
    {
        merge(groupSubmeshes
              (mesh->submeshes, smIndices_
               , boost::apply_visitor(GetAtlas(), atlas)->size()
               , meshLimits_));
    }

    MeshAtlas result() const {
        return MeshAtlas
            (changedMesh() ? mesh_ : originalMesh_
             , changedAtlas()
             ? atlas_
             : boost::apply_visitor(GetAtlas(), originalAtlas_));
    }

private:
    void merge(const Range::list &ranges);
    void merge(const Range &range);
    void ensureChanged(const Range &range);
    bool changedMesh() const { return bool(mesh_); }
    bool changedAtlas() const { return bool(atlas_); }

    void pass(const Range &range);
    SubMesh& mergeNonTextured(const Range &range);
    void mergeTextured(const Range &range);

    void breakSubmesh();

    void makeSharedFaces();

    TextureInfo::list texturing(const Range &range) const;

    const TileId tileId_;
    const int textureQuality_;
    const SubmeshMergeOptions &options_;
    const Mesh::pointer originalMesh_;
    const InputAtlas originalAtlas_;
    const SubMesh::list oSubmeshes_;
    const EntityCounter meshLimits_;
    Mesh::pointer mesh_;
    HybridAtlas::pointer atlas_;
    std::size_t meshEnd_;
    std::size_t atlasEnd_;
    Indices smIndices_;
};

TextureInfo::list MeshAtlasBuilder::texturing(const Range &range) const
{
    TextureInfo::list tx;
    for (auto i : range) {
        tx.emplace_back
            (originalMesh_->submeshes[i]
             , boost::apply_visitor(GetImage(i), originalAtlas_), options_);
    }
    return tx;
}

void MeshAtlasBuilder::merge(const Range::list &ranges)
{
    // LOG(info4) << "Ranges: " << utility::join(ranges, ", ");
    for (const auto &range : ranges) { merge(range); }
}

void MeshAtlasBuilder::merge(const Range &range)
{
    // sanity check
    if (range.empty()) { return; }

    // textured range - always merge (even if it is only one mesh because we
    // want to repack atlas)
    if (range.textured) { return mergeTextured(range); }

    // non textured submeshes

    // single submes -> pass as is
    if (range.size() == 1) { return pass(range); }

    // merge non-textured submeshes
    mergeNonTextured(range);
}

void MeshAtlasBuilder::ensureChanged(const Range &range)
{
    if (!changedMesh()) {
        // copy mesh and trim submeshes
        mesh_ = std::make_shared<Mesh>(*originalMesh_, meshEnd_);
    }

    if (range.textured && !changedAtlas()) {
        // convert atlas
        atlas_ = boost::apply_visitor
            (AsHybridAtlas(atlasEnd_, textureQuality_), originalAtlas_);
    }
}

void MeshAtlasBuilder::pass(const Range &range)
{
    if (changedMesh()) {
        // copy submeshes
        for (auto i : range) {
            mesh_->submeshes.push_back(originalMesh_->submeshes[i]);
        }
    }

    if (range.textured && changedAtlas()) {
        // copy atlas
        for (auto i : range) {
            boost::apply_visitor(AddItem(atlas_, i), originalAtlas_);
        }
    }

    // move markers
    meshEnd_ += range.size();
    if (range.textured) { atlasEnd_ += range.size(); }
}

template <typename T>
inline void append(std::vector<T> &v1, const std::vector<T> &v2)
{
    v1.insert(v1.end(), v2.begin(), v2.end());
}

void appendFaces(Faces &out, const Faces &in, std::size_t shift)
{
    for (const auto &f : in) {
        out.emplace_back(f(0) + shift, f(1) + shift, f(2) + shift);
    }
}

SubMesh& MeshAtlasBuilder::mergeNonTextured(const Range &range)
{
    // more than one submeshes that can be joined -> join them
    ensureChanged(range);

    // clone first submesh to join
    mesh_->submeshes.push_back(oSubmeshes_[range.front()]);
    auto &sm(mesh_->submeshes.back());

    // drop texturing info (if any)
    sm.tc.clear();
    sm.facesTc.clear();

    for (auto irange(range.begin() + 1), erange(range.end());
         irange != erange; ++irange)
    {
        auto &src(oSubmeshes_[*irange]);

        // append external texture coordinates
        append(sm.etc, src.etc);
        appendFaces(sm.faces, src.faces, sm.vertices.size());
        append(sm.vertices, src.vertices);
    }

    meshEnd_ += range.size();

    return sm;
}

void MeshAtlasBuilder::mergeTextured(const Range &range)
{
    // more than one submeshes that can be joined -> join them

    // join non-texture information (same as above)
    auto &sm(mergeNonTextured(range));

    // and join textures
    cv::Mat texture;
    std::tie(texture, sm.tc, sm.facesTc)
        = joinTextures(tileId_, texturing(range));
    atlas_->add(texture);
    // housekeeping
    atlasEnd_ += range.size();

    if (options_.sharedFaces) {
        makeSharedFaces();
    }

    if (!EntityCounter(sm).check(meshLimits_)) {
        // FIXME: move before joinTextures() call
        // breaks last submesh into compliant submeshes
        breakSubmesh();
    }
}

class MeshBuilder {
public:
    MeshBuilder(const EntityCounter &meshLimits, const SubMesh &src)
        : meshLimits_(meshLimits), src_(src)
        , vertexMap_(src_.vertices.size(), -1)
        , tcMap_(src_.tc.size(), -1)
        , ifaces_(src_.faces.begin())
        , efaces_(src_.faces.end())
        , ifacesTc_(src_.facesTc.begin())
    {}

    std::size_t run(Mesh &mesh);

private:
    bool run(SubMesh &dst);

    void reset() {
        vertexMap_.assign(src_.vertices.size(), -1);
        tcMap_.assign(src_.tc.size(), -1);
    }

    int addVertex(SubMesh &dst, int v) {
        auto& mv(vertexMap_[v]);
        if (mv < 0) {
            mv = dst.vertices.size();
            dst.vertices.push_back(src_.vertices[v]);

            // copy external texture coordinate if present
            if (!src_.etc.empty()) { dst.etc.push_back(src_.etc[v]); }
        }

        return mv;
    }

    int addTc(SubMesh &dst, int v) {
        auto& mv(tcMap_[v]);
        if (mv < 0) {
            mv = dst.tc.size();
            dst.tc.push_back(src_.tc[v]);
        }
        return mv;
    }

    void addFace(SubMesh &dst, const Face &f, const Face &ftc) {
        dst.faces.emplace_back(addVertex(dst, f(0))
                               , addVertex(dst, f(1))
                               , addVertex(dst, f(2)));
        dst.facesTc.emplace_back(addTc(dst, ftc(0))
                                 , addTc(dst, ftc(1))
                                 , addTc(dst, ftc(2)));
    }

    /** TODO: implement capacity check here
     */
    bool check(const SubMesh &dst) const {
        return meshLimits_.available(dst);
    }

    const EntityCounter &meshLimits_;
    const SubMesh &src_;
    std::vector<int> vertexMap_;
    std::vector<int> tcMap_;

    Faces::const_iterator ifaces_;
    Faces::const_iterator efaces_;
    Faces::const_iterator ifacesTc_;
};

std::size_t MeshBuilder::run(Mesh &mesh)
{
    LOG(info1)
        << "Breaking submesh apart ["
        << src_.vertices.size() << "/" << src_.tc.size()
        << "/" << src_.faces.size() << "].";

    const auto start(mesh.submeshes.size());
    while (run(utility::append(mesh.submeshes))) { reset(); }
    return (mesh.submeshes.size() - start);
}

bool MeshBuilder::run(SubMesh &dst)
{
    src_.cloneMetadataInto(dst);

    for (; (ifaces_ != efaces_) && check(dst); ++ifaces_, ++ifacesTc_) {
        addFace(dst, *ifaces_, *ifacesTc_);
    }

    return (ifaces_ != efaces_);
}

void MeshAtlasBuilder::breakSubmesh()
{
    // steal source mesh from the back of the submeshes
    auto src(std::move(mesh_->submeshes.back()));
    mesh_->submeshes.resize(mesh_->submeshes.size() - 1);

    // duplicate texture
    for (auto smCount(MeshBuilder(meshLimits_, src).run(*mesh_));
         smCount > 1; --smCount)
    {
        atlas_->duplicate();
    }
}

void MeshAtlasBuilder::makeSharedFaces()
{
    // get last submesh
    auto &sm(mesh_->submeshes.back());

    math::Points3d vertices;
    math::Points2d tc;
    Faces faces;

    typedef std::pair<int, int> VertexTcPair;
    typedef std::map<VertexTcPair, int> VertexMap;

    VertexMap vmap;

    auto ifaces(sm.faces.begin());
    for (const auto &tface : sm.facesTc) {
        const auto &face(*ifaces++);

        faces.emplace_back();
        auto &oface(faces.back());

        for (int i(0); i < 3; ++i) {
            const VertexTcPair pair(face(i), tface(i));

            auto fvmap(vmap.find(pair));
            if (fvmap == vmap.end()) {
                // unknown vertex/tc pair
                const auto idx(vertices.size());
                vertices.emplace_back(sm.vertices[pair.first]);
                tc.emplace_back(sm.tc[pair.second]);

                vmap.insert(VertexMap::value_type(pair, idx));
                oface(i) = idx;
            } else {
                oface(i) = fvmap->second;
            }
        }
    }

    // fill in new data
    sm.vertices.swap(vertices);
    sm.tc.swap(tc);
    // and use the same data for both 3D and 2D face
    sm.faces.swap(faces);
    sm.facesTc = sm.faces;
}

std::tuple<cv::Mat, math::Points2d, Faces>
joinTextures(const TileId &tileId, TextureInfo::list texturing)
{
    std::tuple<cv::Mat, math::Points2d, Faces> res;
    auto &tex(std::get<0>(res));
    auto &tc(std::get<1>(res));
    auto &faces(std::get<2>(res));

    // break texturing mesh into components
    ComponentInfo::list cinfoList;
    {
        int id(0);
        for (auto &tx : texturing) {
            cinfoList.emplace_back(tileId, id++, tx);
        }
    }

    // pack patches into new atlas
    auto packedSize([&]() -> math::Size2
    {
        // pack the patches
        imgproc::RectPacker packer;
        for (const auto &cinfo : cinfoList) {
            for (auto &c : cinfo.components) {
                packer.addRect(&c->rect);
            }
        }
        packer.pack();
        return math::Size2(packer.width(), packer.height());
    }());

    // background color
    const auto background(0
                          ? cv::Scalar(0x80, 0x80, 0x80)
                          : cv::Scalar(0x0, 0x0, 0x0));

    tex.create(packedSize.height, packedSize.width, CV_8UC3);
    tex.setTo(background);

    // denormalized result texture coordinates
    math::Points2d dnTc;

    // process patches
    {
        auto ts(tex.size());

        for (const auto &cinfo : cinfoList) {
            const auto &tx(*cinfo.tx);

            // copy patches from this texture
            cinfo.copy(tex);

            auto itcMap(cinfo.tcMap.cbegin());
            auto tcOffset(tc.size());

            int idx(0);
            for (const auto &oldUv : tx.tc()) {
                auto uv((*itcMap++)->adjustUV(oldUv));
                dnTc.emplace_back(double(uv.x), double(uv.y));
                tc.push_back(normalize(uv, ts));
                ++idx;
            }

            // add texture faces from this texture
            appendFaces(faces, tx.faces(), tcOffset);
        }

        if (VTS_MESH_MERGE_DUMP_DIR) {
            for (const auto &cinfo : cinfoList) {
                cinfo.debug(VTS_MESH_MERGE_DUMP_DIR, tex);
            }
        }
    }

    if (!NO_ATLAS_INPAINT) {
        // rasterize valid triangles
        cv::Mat mask(tex.rows, tex.cols, CV_8U, cv::Scalar(0x00));
        opencv::rasterizeMask(mask, faces, dnTc);

        // inpaint JPEG blocks
        imgproc::jpegBlockInpaint(tex, mask);
    }

    // done
    return res;
}

} // namespace

MeshAtlas mergeSubmeshes(const TileId &tileId, const Mesh::pointer &mesh
                         , const RawAtlas::pointer &atlas
                         , int textureQuality
                         , const SubmeshMergeOptions &options)
{
    return MeshAtlasBuilder(tileId, mesh, atlas, textureQuality, options)
        .result();
}

MeshAtlas mergeSubmeshes(const TileId &tileId, const Mesh::pointer &mesh
                         , const HybridAtlas::pointer &atlas
                         , int textureQuality
                         , const SubmeshMergeOptions &options)
{
    return MeshAtlasBuilder(tileId, mesh, atlas, textureQuality, options)
        .result();
}

} } // namespace vtslibs::vts

max_stars_repo_name: melowntech/vts-libs

max_issues_repo_name: melowntech/vts-libs

max_forks_repo_name: melowntech/vts-libs


text:
/******************************************************************************
 * $Id$
 *
 * Project:  libLAS - http://liblas.org - A BSD library for LAS format data.
 * Purpose:  Point Partitioning/blocking for OPC
 * Author:   Andrew Bell andrew.bell.ia at gmail.com
 *
 ******************************************************************************
 * Copyright (c) 2010, Andrew Bell
 *
 * 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 Andrew Bell or libLAS 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.
 ****************************************************************************/

#include <liblas/chipper.hpp>
// boost
#include <boost/cstdint.hpp>
// std
#include <cmath>

using namespace std;

/**
The objective is to split the region into non-overlapping blocks, each
containing approximately the same number of points, as specified by the
user.

First, the points are read into arrays - one for the x direction, and one for
the y direction.  The arrays are sorted and are initialized with indices into
the other array of the location of the other coordinate of the same point.

Partitions are created that place the maximum number of points in a
block, subject to the user-defined threshold, using a cumulate and round
procedure.

The distance of the point-space is checked in each direction and the
wider dimension is chosen for splitting at an appropriate partition point.
The points in the narrower direction are copied to locations in the spare
array at one side or the other of the chosen partition, and that portion
of the spare array then becomes the active array for the narrow direction.
This avoids resorting of the arrays, which are already sorted.

This procedure is then recursively applied to the created blocks until
they contains only one or two partitions.  In the case of one partition,
we are done, and we simply store away the contents of the block.  If there are
two partitions in a block, we avoid the recopying the narrow array to the
spare since the wide array already contains the desired points partitioned
into two blocks.  We simply need to locate the maximum and minimum values
from the narrow array so that the approriate extrema of the block can
be stored.
**/

namespace liblas { namespace chipper {

vector<boost::uint32_t> Block::GetIDs() const
{
    vector<boost::uint32_t> ids;

    for (boost::uint32_t i = m_left; i <= m_right; ++i)
        ids.push_back((*m_list_p)[i].m_ptindex);
    return ids;
}

void Chipper::Chip()
{
    Load(m_xvec, m_yvec, m_spare);
    Partition(m_xvec.size());
    DecideSplit(m_xvec, m_yvec, m_spare, 0, m_partitions.size() - 1);
}

void Chipper::Load(RefList& xvec, RefList& yvec, RefList& spare )
{
    PtRef ref;
    boost::uint32_t idx;
    boost::uint32_t count;
    vector<PtRef>::iterator it;

    count = m_reader->GetHeader().GetPointRecordsCount();
    xvec.reserve(count);
    yvec.reserve(count);
    spare.resize(count);

    count = 0;
    while (m_reader->ReadNextPoint()) {
        const liblas::Point& pt = m_reader->GetPoint();

        ref.m_pos = pt.GetX();
        ref.m_ptindex = count;
        xvec.push_back(ref);

        ref.m_pos = pt.GetY();
        yvec.push_back(ref);
        count++;
    }
    // Sort xvec and assign other index in yvec to sorted indices in xvec.
    sort(xvec.begin(), xvec.end());
    for (boost::uint32_t i = 0; i < xvec.size(); ++i) {
        idx = xvec[i].m_ptindex;
        yvec[idx].m_oindex = i;
    }

    // Sort yvec.
    sort(yvec.begin(), yvec.end());

    //Iterate through the yvector, setting the xvector appropriately.
    for (boost::uint32_t i = 0; i < yvec.size(); ++i)
        xvec[yvec[i].m_oindex].m_oindex = i;
}

void Chipper::Partition(boost::uint32_t size)
{
    boost::uint32_t num_partitions;

    num_partitions = size / m_threshold;
    if ( size % m_threshold )
        num_partitions++;
    double total = 0;
    double partition_size = static_cast<double>(size) / num_partitions;
    m_partitions.push_back(0);
    for (boost::uint32_t i = 0; i < num_partitions; ++i) {
        total += partition_size;
        m_partitions.push_back(static_cast<boost::uint32_t>(detail::sround(total)));
    }
}

void Chipper::DecideSplit(RefList& v1, RefList& v2, RefList& spare,
    boost::uint32_t pleft, boost::uint32_t pright)
{
    double v1range;
    double v2range;
    boost::uint32_t left = m_partitions[pleft];
    boost::uint32_t right = m_partitions[pright] - 1;

    // Decide the wider direction of the block, and split in that direction
    // to maintain squareness.
    v1range = v1[right].m_pos - v1[left].m_pos;
    v2range = v2[right].m_pos - v2[left].m_pos;
    if (v1range > v2range)
        Split(v1, v2, spare, pleft, pright);
    else
        Split(v2, v1, spare, pleft, pright);
}

void Chipper::Split(RefList& wide, RefList& narrow, RefList& spare,
    boost::uint32_t pleft, boost::uint32_t pright)
{
    boost::uint32_t lstart;
    boost::uint32_t rstart;
    boost::uint32_t pcenter;
    boost::uint32_t left;
    boost::uint32_t right;
    boost::uint32_t center;

    left = m_partitions[pleft];
    right = m_partitions[pright] - 1;

    // There are two cases in which we are done.
    // 1) We have a distance of two between left and right.
    // 2) We have a distance of three between left and right.

    if (pright - pleft == 1)
        Emit(wide, left, right, narrow, left, right);
    else if (pright - pleft == 2)
        FinalSplit(wide, narrow, pleft, pright);
    else
    {
        pcenter = (pleft + pright) / 2;
        center = m_partitions[pcenter];

        // We are splitting in the wide direction - split elements in the
        // narrow array by copying them to the spare array in the correct
        // partition.  The spare array then becomes the active narrow array
        // for the [left,right] partition.
        lstart = left;
        rstart = center;
        for (boost::uint32_t i = left; i <= right; ++i)
        {
            if (narrow[i].m_oindex < center)
            {
                spare[lstart] = narrow[i];
                wide[narrow[i].m_oindex].m_oindex = lstart;
                lstart++;
            }
            else
            {
                spare[rstart] = narrow[i];
                wide[narrow[i].m_oindex].m_oindex = rstart;
                rstart++;
            }
        }

        // Save away the direction so we know which array is X and which is Y
        // so that when we emit, we can properly label the max/min points.
        Direction dir = narrow.m_dir;
        spare.m_dir = dir;
        DecideSplit(wide, spare, narrow, pleft, pcenter);
        DecideSplit(wide, spare, narrow, pcenter, pright);
        narrow.m_dir = dir;
    }
}

// In this case the wide array is like we want it.  The narrow array is
// ordered, but not for our split, so we have to find the max/min entries
// for each partition in the final split.
void Chipper::FinalSplit(RefList& wide, RefList& narrow,
    boost::uint32_t pleft, boost::uint32_t pright)
{

    boost::int64_t left1 = -1;
    boost::int64_t left2 = -1;
    boost::int64_t right1 = -1;
    boost::int64_t right2 = -1;

    // It appears we're using int64_t here because we're using -1 as
    // an indicator.  I'm not 100% sure that i ends up <0, but I don't
    // think so.  These casts will at least shut up the compiler, but
    // I think this code should be revisited to use std::vector<boost::uint32_t>::const_iterator
    // or std::vector<boost::uint32_t>::size_type instead of this int64_t stuff -- hobu 11/15/10
    boost::int64_t left = static_cast<boost::int64_t>(m_partitions[pleft]);
    boost::int64_t right = static_cast<boost::int64_t>(m_partitions[pright] - 1);
    boost::int64_t center = static_cast<boost::int64_t>(m_partitions[pright - 1]);

    // Find left values for the partitions.
    for (boost::int64_t i = left; i <= right; ++i)
    {
        boost::int64_t idx = static_cast<boost::int64_t>(narrow[static_cast<boost::uint32_t>(i)].m_oindex);
        if (left1 < 0 && (idx < center))
        {
            left1 = i;
            if (left2 >= 0)
                break;
        }
        else if (left2 < 0 && (idx >= center))
        {
            left2 = i;
            if (left1 >= 0)
                break;
        }
    }
    // Find right values for the partitions.
    for (boost::int64_t i = right; i >= left; --i)
    {
        boost::int64_t idx = static_cast<boost::int64_t>(narrow[static_cast<boost::uint32_t>(i)].m_oindex);
        if (right1 < 0 && (idx < center))
        {
            right1 = i;
            if (right2 >= 0)
                break;
        }
        else if (right2 < 0 && (idx >= center))
        {
            right2 = i;
            if (right1 >= 0)
                break;
        }
    }

    // Emit results.
    Emit(wide,
         static_cast<boost::uint32_t>(left),
         static_cast<boost::uint32_t>(center - 1),
         narrow,
         static_cast<boost::uint32_t>(left1),
         static_cast<boost::uint32_t>(right1) );
    Emit(wide,
         static_cast<boost::uint32_t>(center),
         static_cast<boost::uint32_t>(right),
         narrow,
         static_cast<boost::uint32_t>(left2),
         static_cast<boost::uint32_t>(right2) );
}

void Chipper::Emit(RefList& wide, boost::uint32_t widemin, boost::uint32_t widemax,
    RefList& narrow, boost::uint32_t narrowmin, boost::uint32_t narrowmax )
{
    Block b;

    b.m_list_p = &wide;
    if (wide.m_dir == DIR_X) {

        // minx, miny, maxx, maxy
        liblas::Bounds<double> bnd(wide[widemin].m_pos, narrow[narrowmin].m_pos, wide[widemax].m_pos,  narrow[narrowmax].m_pos);
        b.SetBounds(bnd);

        // b.m_xmin = wide[widemin].m_pos;
        // b.m_xmax = wide[widemax].m_pos;
        // b.m_ymin = narrow[narrowmin].m_pos;
        // b.m_ymax = narrow[narrowmax].m_pos;
    }
    else {
        liblas::Bounds<double> bnd(narrow[narrowmin].m_pos, wide[widemin].m_pos, narrow[narrowmax].m_pos, wide[widemax].m_pos);
        b.SetBounds(bnd);

        // b.m_xmin = narrow[narrowmin].m_pos;
        // b.m_xmax = narrow[narrowmax].m_pos;
        // b.m_ymin = wide[widemin].m_pos;
        // b.m_ymax = wide[widemax].m_pos;
    }
    b.m_left = widemin;
    b.m_right = widemax;
    m_blocks.push_back(b);
}

}} // namespace liblas::chipper

ext: cpp


text:
/**
 * @file multicast_receiver_example.cpp
 *
 * @author Lucas Haug <lucas.haug@thuneratz.org>
 * @author Lucas Schneider <lucas.schneider@thuneratz.org>
 *
 * @brief Example on how to use the MulticastReceiver
 *
 * @date 04/2021
 *
 * @copyright MIT License - Copyright (c) 2021 ThundeRatz
 */

#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include "boost/bind.hpp"

#include "travesim_adapters/udp/multicast_receiver.hpp"

/*****************************************
 * Private Constants
 *****************************************/

#define BUFFER_SIZE 1024U

/*****************************************
 * Main Function
 *****************************************/

int main(int argc, char* argv[]) {
    const std::string listen_address_str = "0.0.0.0";
    const std::string multicast_address_str = "224.0.0.1";
    const short multicast_port = 10002;

    char data_buff[BUFFER_SIZE];

    try {
        boost::asio::io_context io_context;
        boost::asio::steady_timer my_timer(io_context);
        travesim::udp::MulticastReceiver my_receiver(multicast_address_str, multicast_port);

        size_t data_size = 0;

        for (long int i = 0;; i++) {
            data_size = my_receiver.receive(data_buff, BUFFER_SIZE);

            if (data_size > 0) {
                std::string received_msg(data_buff, data_size);
                std::cout << received_msg << std::endl;
            }

            if (i % 100000 == 0) {
                std::cout << "Loop count: " << i << std::endl;
            }

            my_timer.expires_after(std::chrono::milliseconds(200));
            my_timer.wait();
        }
    } catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}


text:
#include <boost/config.hpp>

#if defined(BOOST_MSVC)
#pragma warning(disable: 4786)  // identifier truncated in debug info
#pragma warning(disable: 4710)  // function not inlined
#pragma warning(disable: 4711)  // function selected for automatic inline expansion
#pragma warning(disable: 4514)  // unreferenced inline removed
#endif

//
//  bind_rel_test.cpp - ==, !=, <, <=, >, >= operators
//
//  Copyright (c) 2005 Peter Dimov
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/bind/bind.hpp>
#include <boost/core/lightweight_test.hpp>

using namespace boost::placeholders;

//

int f( int x )
{
    return x + x;
}

int g( int x )
{
    return 2 * x;
}

int main()
{
    int x = 4;
    int y = x + x;

    // bind op value

    BOOST_TEST( ( boost::bind( f, _1 ) == y )( x ) );
    BOOST_TEST( !( ( boost::bind( f, _1 ) != y )( x ) ) );

    BOOST_TEST( !( ( boost::bind( f, _1 ) < y )( x ) ) );
    BOOST_TEST( ( boost::bind( f, _1 ) < y + 1 )( x ) );

    BOOST_TEST( !( ( boost::bind( f, _1 ) > y )( x ) ) );
    BOOST_TEST( ( boost::bind( f, _1 ) > y - 1 )( x ) );

    BOOST_TEST( !( ( boost::bind( f, _1 ) <= y - 1 )( x ) ) );
    BOOST_TEST( ( boost::bind( f, _1 ) <= y )( x ) );
    BOOST_TEST( ( boost::bind( f, _1 ) <= y + 1 )( x ) );

    BOOST_TEST( !( ( boost::bind( f, _1 ) >= y + 1 )( x ) ) );
    BOOST_TEST( ( boost::bind( f, _1 ) >= y )( x ) );
    BOOST_TEST( ( boost::bind( f, _1 ) >= y - 1 )( x ) );

    // bind op ref

    BOOST_TEST( ( boost::bind( f, _1 ) == boost::ref( y ) )( x ) );
    BOOST_TEST( !( ( boost::bind( f, _1 ) != boost::ref( y ) )( x ) ) );
    BOOST_TEST( !( ( boost::bind( f, _1 ) < boost::ref( y ) )( x ) ) );
    BOOST_TEST( !( ( boost::bind( f, _1 ) > boost::ref( y ) )( x ) ) );
    BOOST_TEST( ( boost::bind( f, _1 ) <= boost::ref( y ) )( x ) );
    BOOST_TEST( ( boost::bind( f, _1 ) >= boost::ref( y ) )( x ) );

    // bind op placeholder

    BOOST_TEST( ( boost::bind( f, _1 ) == _2 )( x, y ) );
    BOOST_TEST( !( ( boost::bind( f, _1 ) != _2 )( x, y ) ) );
    BOOST_TEST( !( ( boost::bind( f, _1 ) < _2 )( x, y ) ) );
    BOOST_TEST( !( ( boost::bind( f, _1 ) > _2 )( x, y ) ) );
    BOOST_TEST( ( boost::bind( f, _1 ) <= _2 )( x, y ) );
    BOOST_TEST( ( boost::bind( f, _1 ) >= _2 )( x, y ) );

    // bind op bind

    // important: bind( f, _1 ) and bind( g, _1 ) have the same type
    BOOST_TEST( ( boost::bind( f, _1 ) == boost::bind( g, _1 ) )( x ) );
    BOOST_TEST( !( ( boost::bind( f, _1 ) != boost::bind( g, _1 ) )( x ) ) );
    BOOST_TEST( !( ( boost::bind( f, _1 ) < boost::bind( g, _1 ) )( x ) ) );
    BOOST_TEST( ( boost::bind( f, _1 ) <= boost::bind( g, _1 ) )( x ) );
    BOOST_TEST( !( ( boost::bind( f, _1 ) > boost::bind( g, _1 ) )( x ) ) );
    BOOST_TEST( ( boost::bind( f, _1 ) >= boost::bind( g, _1 ) )( x ) );

    return boost::report_errors();
}

max_issues_repo_name: qingkouwei/mediaones

max_forks_repo_name: qingkouwei/mediaones


text:
#ifndef MATHTOOLBOX_MATRIX_INVERSION_HPP
#define MATHTOOLBOX_MATRIX_INVERSION_HPP

#include <Eigen/Core>

namespace mathtoolbox
{
    Eigen::MatrixXd GetInverseUsingUpperLeftBlockInverse(const Eigen::MatrixXd& matrix,
                                                         const Eigen::MatrixXd& upper_left_block_inverse);
} // namespace mathtoolbox

#endif // MATHTOOLBOX_MATRIX_INVERSION_HPP

ext: hpp

max_stars_repo_name: yuki-koyama/mathtoolbox


text:
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2014-2017,  Regents of the University of California,
 *                           Arizona Board of Regents,
 *                           Colorado State University,
 *                           University Pierre & Marie Curie, Sorbonne University,
 *                           Washington University in St. Louis,
 *                           Beijing Institute of Technology,
 *                           The University of Memphis.
 *
 * This file is part of NFD (Named Data Networking Forwarding Daemon).
 * See AUTHORS.md for complete list of NFD authors and contributors.
 *
 * NFD is free software: you can redistribute it and/or modify it under the terms
 * of the GNU General Public License as published by the Free Software Foundation,
 * either version 3 of the License, or (at your option) any later version.
 *
 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "core/logger.hpp"

#include "tests/test-common.hpp"

#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>

#include <iostream>

namespace nfd {
namespace tests {

BOOST_FIXTURE_TEST_SUITE(TestLogger, BaseFixture)

class LoggerFixture : protected BaseFixture
{
public:
  LoggerFixture()
    : m_savedBuf(std::clog.rdbuf())
    , m_savedLevel(LoggerFactory::getInstance().getDefaultLevel())
  {
    std::clog.rdbuf(m_buffer.rdbuf());
  }

  ~LoggerFixture()
  {
    std::clog.rdbuf(m_savedBuf);
    LoggerFactory::getInstance().setDefaultLevel(m_savedLevel);
  }

  std::stringstream m_buffer;
  std::streambuf* m_savedBuf;
  LogLevel m_savedLevel;

};

BOOST_FIXTURE_TEST_CASE(Basic, LoggerFixture)
{
  using namespace ndn::time;
  using std::string;

  const ndn::time::microseconds::rep ONE_SECOND = 1000000;

  NFD_LOG_INIT("BasicTests");
  g_logger.setLogLevel(LOG_ALL);

  const string EXPECTED[] =
    {
      "TRACE:",   "[BasicTests]", "trace-message-JHGFDSR^1\n",
      "DEBUG:",   "[BasicTests]", "debug-message-IGg2474fdksd-fo-151617\n",
      "WARNING:", "[BasicTests]", "warning-message-XXXhdhd111x\n",
      "INFO:",    "[BasicTests]", "info-message-Jjxjshj13\n",
      "ERROR:",   "[BasicTests]", "error-message-!#$&^%$#@\n",
      "FATAL:",   "[BasicTests]", "fatal-message-JJSjaamcng\n",
    };

  const size_t N_EXPECTED = sizeof(EXPECTED) / sizeof(string);

  microseconds::rep before =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  NFD_LOG_TRACE("trace-message-JHGFDSR^1");
  NFD_LOG_DEBUG("debug-message-IGg2474fdksd-fo-" << 15 << 16 << 17);
  NFD_LOG_WARN("warning-message-XXXhdhd11" << 1 <<"x");
  NFD_LOG_INFO("info-message-Jjxjshj13");
  NFD_LOG_ERROR("error-message-!#$&^%$#@");
  NFD_LOG_FATAL("fatal-message-JJSjaamcng");

  microseconds::rep after =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  LoggerFactory::getInstance().flushBackend();

  const string buffer = m_buffer.str();

  std::vector<string> components;
  boost::split(components, buffer, boost::is_any_of(" ,\n"));

  // std::cout << components.size() << " for " << moduleName  << std::endl;
  // for (size_t i = 0; i < components.size(); ++i)
  //   {
  //     std::cout << "-> " << components[i] << std::endl;
  //   }

  // expected + number of timestamps (one per log statement) + trailing newline of last statement
  BOOST_REQUIRE_EQUAL(components.size(), N_EXPECTED + 6 + 1);

  std::vector<std::string>::const_iterator componentIter = components.begin();
  for (size_t i = 0; i < N_EXPECTED; ++i)
    {
      // timestamp LOG_LEVEL: [ModuleName] message\n

      const string& timestamp = *componentIter;
      // std::cout << "timestamp = " << timestamp << std::endl;
      ++componentIter;

      size_t timeDelimiterPosition = timestamp.find(".");

      BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);

      string secondsString = timestamp.substr(0, timeDelimiterPosition);
      string usecondsString = timestamp.substr(timeDelimiterPosition + 1);

      microseconds::rep extractedTime =
        ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
        boost::lexical_cast<microseconds::rep>(usecondsString);

      // std::cout << "before=" << before
      //           << " extracted=" << extractedTime
      //           << " after=" << after << std::endl;

      BOOST_CHECK_LE(before, extractedTime);
      BOOST_CHECK_LE(extractedTime, after);

      // LOG_LEVEL:
      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
      ++componentIter;
      ++i;

      // [ModuleName]
      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
      ++componentIter;
      ++i;

      const string& message = *componentIter;

      // std::cout << "message = " << message << std::endl;

      // add back the newline that we split on
      BOOST_CHECK_EQUAL(message + "\n", EXPECTED[i]);
      ++componentIter;
    }

}

BOOST_FIXTURE_TEST_CASE(ConfigureFactory, LoggerFixture)
{
  using namespace ndn::time;
  using std::string;

  const ndn::time::microseconds::rep ONE_SECOND = 1000000;

  NFD_LOG_INIT("ConfigureFactoryTests");

  const string LOG_CONFIG =
    "log\n"
    "{\n"
    "  default_level INFO\n"
    "}\n";

  LoggerFactory::getInstance().setDefaultLevel(LOG_ALL);

  ConfigFile config;
  LoggerFactory::getInstance().setConfigFile(config);

  config.parse(LOG_CONFIG, false, "LOG_CONFIG");

  BOOST_REQUIRE_EQUAL(LoggerFactory::getInstance().getDefaultLevel(), LOG_INFO);

  const std::string EXPECTED[] =
    {
      "WARNING:", "[ConfigureFactoryTests]", "warning-message-XXXhdhd111x\n",
      "INFO:",    "[ConfigureFactoryTests]", "info-message-Jjxjshj13\n",
      "ERROR:",   "[ConfigureFactoryTests]", "error-message-!#$&^%$#@\n",
      "FATAL:",   "[ConfigureFactoryTests]", "fatal-message-JJSjaamcng\n",
    };

  const size_t N_EXPECTED = sizeof(EXPECTED) / sizeof(std::string);

  microseconds::rep before =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  NFD_LOG_TRACE("trace-message-JHGFDSR^1");
  NFD_LOG_DEBUG("debug-message-IGg2474fdksd-fo-" << 15 << 16 << 17);
  NFD_LOG_WARN("warning-message-XXXhdhd11" << 1 <<"x");
  NFD_LOG_INFO("info-message-Jjxjshj13");
  NFD_LOG_ERROR("error-message-!#$&^%$#@");
  NFD_LOG_FATAL("fatal-message-JJSjaamcng");

  microseconds::rep after =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  LoggerFactory::getInstance().flushBackend();

  const string buffer = m_buffer.str();

  std::vector<string> components;
  boost::split(components, buffer, boost::is_any_of(" ,\n"));

  // std::cout << components.size() << " for " << moduleName  << std::endl;
  // for (size_t i = 0; i < components.size(); ++i)
  //   {
  //     std::cout << "-> " << components[i] << std::endl;
  //   }

  // expected + number of timestamps (one per log statement) + trailing newline of last statement
  BOOST_REQUIRE_EQUAL(components.size(), N_EXPECTED + 4 + 1);

  std::vector<std::string>::const_iterator componentIter = components.begin();
  for (size_t i = 0; i < N_EXPECTED; ++i)
    {
      // timestamp LOG_LEVEL: [ModuleName] message\n

      const string& timestamp = *componentIter;
      // std::cout << "timestamp = " << timestamp << std::endl;
      ++componentIter;

      size_t timeDelimiterPosition = timestamp.find(".");

      BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);

      string secondsString = timestamp.substr(0, timeDelimiterPosition);
      string usecondsString = timestamp.substr(timeDelimiterPosition + 1);

      microseconds::rep extractedTime =
        ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
        boost::lexical_cast<microseconds::rep>(usecondsString);

      // std::cout << "before=" << before
      //           << " extracted=" << extractedTime
      //           << " after=" << after << std::endl;

      BOOST_CHECK_LE(before, extractedTime);
      BOOST_CHECK_LE(extractedTime, after);

      // LOG_LEVEL:
      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
      ++componentIter;
      ++i;

      // [ModuleName]
      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
      ++componentIter;
      ++i;

      const string& message = *componentIter;

      // std::cout << "message = " << message << std::endl;

      // add back the newline that we split on
      BOOST_CHECK_EQUAL(message + "\n", EXPECTED[i]);
      ++componentIter;
    }
}

BOOST_FIXTURE_TEST_CASE(TestNumberLevel, LoggerFixture)
{
  const std::string LOG_CONFIG =
    "log\n"
    "{\n"
    "  default_level 2\n" // equivalent of WARN
    "}\n";

  LoggerFactory::getInstance().setDefaultLevel(LOG_ALL);

  ConfigFile config;
  LoggerFactory::getInstance().setConfigFile(config);

  config.parse(LOG_CONFIG, false, "LOG_CONFIG");

  BOOST_REQUIRE_EQUAL(LoggerFactory::getInstance().getDefaultLevel(), LOG_WARN);
}

static void
testModuleBPrint()
{
  NFD_LOG_INIT("TestModuleB");
  NFD_LOG_DEBUG("debug-message-IGg2474fdksd-fo-" << 15 << 16 << 17);
}

BOOST_FIXTURE_TEST_CASE(LimitModules, LoggerFixture)
{
  using namespace ndn::time;
  using std::string;

  NFD_LOG_INIT("TestModuleA");

  const ndn::time::microseconds::rep ONE_SECOND = 1000000;

  const std::string EXPECTED[] =
    {
      "WARNING:", "[TestModuleA]", "warning-message-XXXhdhd111x\n",
    };

  const size_t N_EXPECTED = sizeof(EXPECTED) / sizeof(std::string);

  const std::string LOG_CONFIG =
    "log\n"
    "{\n"
    "  default_level WARN\n"
    "}\n";

  ConfigFile config;
  LoggerFactory::getInstance().setConfigFile(config);

  config.parse(LOG_CONFIG, false, "LOG_CONFIG");

  microseconds::rep before =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  // this should print
  NFD_LOG_WARN("warning-message-XXXhdhd11" << 1 << "x");

  // this should not because it's level is < WARN
  testModuleBPrint();

  microseconds::rep after =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  LoggerFactory::getInstance().flushBackend();

  const string buffer = m_buffer.str();

  std::vector<string> components;
  boost::split(components, buffer, boost::is_any_of(" ,\n"));

  // expected + number of timestamps (one per log statement) + trailing newline of last statement
  BOOST_REQUIRE_EQUAL(components.size(), N_EXPECTED + 1 + 1);

  std::vector<std::string>::const_iterator componentIter = components.begin();
  for (size_t i = 0; i < N_EXPECTED; ++i)
    {
      // timestamp LOG_LEVEL: [ModuleName] message\n

      const string& timestamp = *componentIter;
      // std::cout << "timestamp = " << timestamp << std::endl;
      ++componentIter;

      size_t timeDelimiterPosition = timestamp.find(".");

      BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);

      string secondsString = timestamp.substr(0, timeDelimiterPosition);
      string usecondsString = timestamp.substr(timeDelimiterPosition + 1);

      microseconds::rep extractedTime =
        ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
        boost::lexical_cast<microseconds::rep>(usecondsString);

      // std::cout << "before=" << before
      //           << " extracted=" << extractedTime
      //           << " after=" << after << std::endl;

      BOOST_CHECK_LE(before, extractedTime);
      BOOST_CHECK_LE(extractedTime, after);

      // LOG_LEVEL:
      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
      ++componentIter;
      ++i;

      // [ModuleName]
      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
      ++componentIter;
      ++i;

      const string& message = *componentIter;

      // std::cout << "message = " << message << std::endl;

      // add back the newline that we split on
      BOOST_CHECK_EQUAL(message + "\n", EXPECTED[i]);
      ++componentIter;
    }
}

BOOST_FIXTURE_TEST_CASE(ExplicitlySetModule, LoggerFixture)
{
  using namespace ndn::time;
  using std::string;

  NFD_LOG_INIT("TestModuleA");

  const ndn::time::microseconds::rep ONE_SECOND = 1000000;

  const std::string LOG_CONFIG =
    "log\n"
    "{\n"
    "  default_level WARN\n"
    "  TestModuleB DEBUG\n"
    "}\n";

  ConfigFile config;
  LoggerFactory::getInstance().setConfigFile(config);

  config.parse(LOG_CONFIG, false, "LOG_CONFIG");

  microseconds::rep before =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

 // this should print
  NFD_LOG_WARN("warning-message-XXXhdhd11" << 1 << "x");

  // this too because its level is explicitly set to DEBUG
  testModuleBPrint();

  microseconds::rep after =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  const std::string EXPECTED[] =
    {
      "WARNING:", "[TestModuleA]", "warning-message-XXXhdhd111x\n",
      "DEBUG:",   "[TestModuleB]", "debug-message-IGg2474fdksd-fo-151617\n",
    };

  const size_t N_EXPECTED = sizeof(EXPECTED) / sizeof(std::string);

  LoggerFactory::getInstance().flushBackend();

  const string buffer = m_buffer.str();

  std::vector<string> components;
  boost::split(components, buffer, boost::is_any_of(" ,\n"));

  // for (size_t i = 0; i < components.size(); ++i)
  //   {
  //     std::cout << "-> " << components[i] << std::endl;
  //   }

  // expected + number of timestamps (one per log statement) + trailing newline of last statement
  BOOST_REQUIRE_EQUAL(components.size(), N_EXPECTED + 2 + 1);

  std::vector<std::string>::const_iterator componentIter = components.begin();
  for (size_t i = 0; i < N_EXPECTED; ++i)
    {
      // timestamp LOG_LEVEL: [ModuleName] message\n

      const string& timestamp = *componentIter;
      // std::cout << "timestamp = " << timestamp << std::endl;
      ++componentIter;

      size_t timeDelimiterPosition = timestamp.find(".");

      BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);

      string secondsString = timestamp.substr(0, timeDelimiterPosition);
      string usecondsString = timestamp.substr(timeDelimiterPosition + 1);

      microseconds::rep extractedTime =
        ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
        boost::lexical_cast<microseconds::rep>(usecondsString);

      // std::cout << "before=" << before
      //           << " extracted=" << extractedTime
      //           << " after=" << after << std::endl;

      BOOST_CHECK_LE(before, extractedTime);
      BOOST_CHECK_LE(extractedTime, after);

      // LOG_LEVEL:
      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
      ++componentIter;
      ++i;

      // [ModuleName]
      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
      ++componentIter;
      ++i;

      const string& message = *componentIter;

      // std::cout << "message = " << message << std::endl;

      // add back the newline that we split on
      BOOST_CHECK_EQUAL(message + "\n", EXPECTED[i]);
      ++componentIter;
    }
}

BOOST_FIXTURE_TEST_CASE(UnknownModule, LoggerFixture)
{
  using namespace ndn::time;
  using std::string;

  const ndn::time::microseconds::rep ONE_SECOND = 1000000;

  const std::string LOG_CONFIG =
    "log\n"
    "{\n"
    "  default_level DEBUG\n"
    "  TestMadeUpModule INFO\n"
    "}\n";

  ConfigFile config;
  LoggerFactory::getInstance().setDefaultLevel(LOG_ALL);
  LoggerFactory::getInstance().setConfigFile(config);

  const std::string EXPECTED = "DEBUG: [LoggerFactory] "
    "Failed to configure logging level for module \"TestMadeUpModule\" (module not found)\n";

  microseconds::rep before =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  config.parse(LOG_CONFIG, false, "LOG_CONFIG");

  microseconds::rep after =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  LoggerFactory::getInstance().flushBackend();

  const string buffer = m_buffer.str();

  const size_t firstSpace = buffer.find(" ");
  BOOST_REQUIRE(firstSpace != string::npos);

  const string timestamp = buffer.substr(0, firstSpace);
  const string message = buffer.substr(firstSpace + 1);

  size_t timeDelimiterPosition = timestamp.find(".");

  BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);

  string secondsString = timestamp.substr(0, timeDelimiterPosition);
  string usecondsString = timestamp.substr(timeDelimiterPosition + 1);

  microseconds::rep extractedTime =
    ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
    boost::lexical_cast<microseconds::rep>(usecondsString);

  // std::cout << "before=" << before
  //           << " extracted=" << extractedTime
  //           << " after=" << after << std::endl;

  BOOST_CHECK_LE(before, extractedTime);
  BOOST_CHECK_LE(extractedTime, after);

  BOOST_CHECK_EQUAL(message, EXPECTED);
}

static bool
checkError(const LoggerFactory::Error& error, const std::string& expected)
{
  return error.what() == expected;
}

BOOST_FIXTURE_TEST_CASE(UnknownLevelString, LoggerFixture)
{
  const std::string LOG_CONFIG =
    "log\n"
    "{\n"
    "  default_level TestMadeUpLevel\n"
    "}\n";

  ConfigFile config;
  LoggerFactory::getInstance().setConfigFile(config);

  BOOST_REQUIRE_EXCEPTION(config.parse(LOG_CONFIG, false, "LOG_CONFIG"),
                          LoggerFactory::Error,
                          bind(&checkError,
                               _1,
                               "Unsupported logging level \"TestMadeUpLevel\""));
}

class InClassLogger : public LoggerFixture
{
public:

  InClassLogger()
  {
    g_logger.setLogLevel(LOG_ALL);
  }

  void
  writeLogs()
  {
    NFD_LOG_TRACE("trace-message-JHGFDSR^1");
    NFD_LOG_DEBUG("debug-message-IGg2474fdksd-fo-" << 15 << 16 << 17);
    NFD_LOG_WARN("warning-message-XXXhdhd11" << 1 <<"x");
    NFD_LOG_INFO("info-message-Jjxjshj13");
    NFD_LOG_ERROR("error-message-!#$&^%$#@");
    NFD_LOG_FATAL("fatal-message-JJSjaamcng");
  }

private:
  NFD_LOG_INCLASS_DECLARE();
};

NFD_LOG_INCLASS_DEFINE(InClassLogger, "InClassLogger");

BOOST_FIXTURE_TEST_CASE(InClass, InClassLogger)
{
  using namespace ndn::time;
  using std::string;

  const ndn::time::microseconds::rep ONE_SECOND = 1000000;

  microseconds::rep before =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  writeLogs();

  microseconds::rep after =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  const string EXPECTED[] =
    {
      "TRACE:",   "[InClassLogger]", "trace-message-JHGFDSR^1\n",
      "DEBUG:",   "[InClassLogger]", "debug-message-IGg2474fdksd-fo-151617\n",
      "WARNING:", "[InClassLogger]", "warning-message-XXXhdhd111x\n",
      "INFO:",    "[InClassLogger]", "info-message-Jjxjshj13\n",
      "ERROR:",   "[InClassLogger]", "error-message-!#$&^%$#@\n",
      "FATAL:",   "[InClassLogger]", "fatal-message-JJSjaamcng\n",
    };

  const size_t N_EXPECTED = sizeof(EXPECTED) / sizeof(string);

  LoggerFactory::getInstance().flushBackend();

  const string buffer = m_buffer.str();

  std::vector<string> components;
  boost::split(components, buffer, boost::is_any_of(" ,\n"));

  // expected + number of timestamps (one per log statement) + trailing newline of last statement
  BOOST_REQUIRE_EQUAL(components.size(), N_EXPECTED + 6 + 1);

  std::vector<std::string>::const_iterator componentIter = components.begin();
  for (size_t i = 0; i < N_EXPECTED; ++i)
    {
      // timestamp LOG_LEVEL: [ModuleName] message\n

      const string& timestamp = *componentIter;
      // std::cout << "timestamp = " << timestamp << std::endl;
      ++componentIter;

      size_t timeDelimiterPosition = timestamp.find(".");

      BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);

      string secondsString = timestamp.substr(0, timeDelimiterPosition);
      string usecondsString = timestamp.substr(timeDelimiterPosition + 1);

      microseconds::rep extractedTime =
        ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
        boost::lexical_cast<microseconds::rep>(usecondsString);

      // std::cout << "before=" << before
      //           << " extracted=" << extractedTime
      //           << " after=" << after << std::endl;

      BOOST_CHECK_LE(before, extractedTime);
      BOOST_CHECK_LE(extractedTime, after);

      // LOG_LEVEL:
      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
      ++componentIter;
      ++i;

      // [ModuleName]
      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
      ++componentIter;
      ++i;

      const string& message = *componentIter;

      // std::cout << "message = " << message << std::endl;

      // add back the newline that we split on
      BOOST_CHECK_EQUAL(message + "\n", EXPECTED[i]);
      ++componentIter;
    }
}

template<class T>
class InClassTemplateLogger : public LoggerFixture
{
public:
  InClassTemplateLogger()
  {
    g_logger.setLogLevel(LOG_ALL);
  }

  void
  writeLogs()
  {
    NFD_LOG_TRACE("trace-message-JHGFDSR^1");
    NFD_LOG_DEBUG("debug-message-IGg2474fdksd-fo-" << 15 << 16 << 17);
    NFD_LOG_WARN("warning-message-XXXhdhd11" << 1 <<"x");
    NFD_LOG_INFO("info-message-Jjxjshj13");
    NFD_LOG_ERROR("error-message-!#$&^%$#@");
    NFD_LOG_FATAL("fatal-message-JJSjaamcng");
  }

private:
  NFD_LOG_INCLASS_DECLARE();
};

NFD_LOG_INCLASS_TEMPLATE_DEFINE(InClassTemplateLogger, "GenericInClassTemplateLogger");
NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(InClassTemplateLogger, int, "IntInClassLogger");

BOOST_FIXTURE_TEST_CASE(GenericInTemplatedClass, InClassTemplateLogger<bool>)
{
  using namespace ndn::time;
  using std::string;

  const ndn::time::microseconds::rep ONE_SECOND = 1000000;

  microseconds::rep before =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  writeLogs();

  microseconds::rep after =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  const string EXPECTED[] =
    {
      "TRACE:",   "[GenericInClassTemplateLogger]", "trace-message-JHGFDSR^1\n",
      "DEBUG:",   "[GenericInClassTemplateLogger]", "debug-message-IGg2474fdksd-fo-151617\n",
      "WARNING:", "[GenericInClassTemplateLogger]", "warning-message-XXXhdhd111x\n",
      "INFO:",    "[GenericInClassTemplateLogger]", "info-message-Jjxjshj13\n",
      "ERROR:",   "[GenericInClassTemplateLogger]", "error-message-!#$&^%$#@\n",
      "FATAL:",   "[GenericInClassTemplateLogger]", "fatal-message-JJSjaamcng\n",
    };

  const size_t N_EXPECTED = sizeof(EXPECTED) / sizeof(string);

  LoggerFactory::getInstance().flushBackend();

  const string buffer = m_buffer.str();

  std::vector<string> components;
  boost::split(components, buffer, boost::is_any_of(" ,\n"));

  // expected + number of timestamps (one per log statement) + trailing newline of last statement
  BOOST_REQUIRE_EQUAL(components.size(), N_EXPECTED + 6 + 1);

  std::vector<std::string>::const_iterator componentIter = components.begin();
  for (size_t i = 0; i < N_EXPECTED; ++i)
    {
      // timestamp LOG_LEVEL: [ModuleName] message\n

      const string& timestamp = *componentIter;
      // std::cout << "timestamp = " << timestamp << std::endl;
      ++componentIter;

      size_t timeDelimiterPosition = timestamp.find(".");

      BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);

      string secondsString = timestamp.substr(0, timeDelimiterPosition);
      string usecondsString = timestamp.substr(timeDelimiterPosition + 1);

      microseconds::rep extractedTime =
        ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
        boost::lexical_cast<microseconds::rep>(usecondsString);

      // std::cout << "before=" << before
      //           << " extracted=" << extractedTime
      //           << " after=" << after << std::endl;

      BOOST_CHECK_LE(before, extractedTime);
      BOOST_CHECK_LE(extractedTime, after);

      // LOG_LEVEL:
      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
      ++componentIter;
      ++i;

      // [ModuleName]
      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
      ++componentIter;
      ++i;

      const string& message = *componentIter;

      // std::cout << "message = " << message << std::endl;

      // add back the newline that we split on
      BOOST_CHECK_EQUAL(message + "\n", EXPECTED[i]);
      ++componentIter;
    }
}

BOOST_FIXTURE_TEST_CASE(SpecializedInTemplatedClass, InClassTemplateLogger<int>)
{
  using namespace ndn::time;
  using std::string;

  const ndn::time::microseconds::rep ONE_SECOND = 1000000;

  microseconds::rep before =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  writeLogs();

  microseconds::rep after =
    duration_cast<microseconds>(system_clock::now().time_since_epoch()).count();

  const string EXPECTED[] =
    {
      "TRACE:",   "[IntInClassLogger]", "trace-message-JHGFDSR^1\n",
      "DEBUG:",   "[IntInClassLogger]", "debug-message-IGg2474fdksd-fo-151617\n",
      "WARNING:", "[IntInClassLogger]", "warning-message-XXXhdhd111x\n",
      "INFO:",    "[IntInClassLogger]", "info-message-Jjxjshj13\n",
      "ERROR:",   "[IntInClassLogger]", "error-message-!#$&^%$#@\n",
      "FATAL:",   "[IntInClassLogger]", "fatal-message-JJSjaamcng\n",
    };

  const size_t N_EXPECTED = sizeof(EXPECTED) / sizeof(string);

  LoggerFactory::getInstance().flushBackend();

  const string buffer = m_buffer.str();

  std::vector<string> components;
  boost::split(components, buffer, boost::is_any_of(" ,\n"));

  // expected + number of timestamps (one per log statement) + trailing newline of last statement
  BOOST_REQUIRE_EQUAL(components.size(), N_EXPECTED + 6 + 1);

  std::vector<std::string>::const_iterator componentIter = components.begin();
  for (size_t i = 0; i < N_EXPECTED; ++i)
    {
      // timestamp LOG_LEVEL: [ModuleName] message\n

      const string& timestamp = *componentIter;
      // std::cout << "timestamp = " << timestamp << std::endl;
      ++componentIter;

      size_t timeDelimiterPosition = timestamp.find(".");

      BOOST_REQUIRE_NE(string::npos, timeDelimiterPosition);

      string secondsString = timestamp.substr(0, timeDelimiterPosition);
      string usecondsString = timestamp.substr(timeDelimiterPosition + 1);

      microseconds::rep extractedTime =
        ONE_SECOND * boost::lexical_cast<microseconds::rep>(secondsString) +
        boost::lexical_cast<microseconds::rep>(usecondsString);

      // std::cout << "before=" << before << " extracted=" << extractedTime << " after=" << after << std::endl;

      BOOST_CHECK_LE(before, extractedTime);
      BOOST_CHECK_LE(extractedTime, after);

      // LOG_LEVEL:
      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
      ++componentIter;
      ++i;

      // [ModuleName]
      BOOST_CHECK_EQUAL(*componentIter, EXPECTED[i]);
      ++componentIter;
      ++i;

      const string& message = *componentIter;

      // std::cout << "message = " << message << std::endl;

      // add back the newline that we split on
      BOOST_CHECK_EQUAL(message + "\n", EXPECTED[i]);
      ++componentIter;
    }
}

BOOST_FIXTURE_TEST_CASE(LoggerFactoryListModules, LoggerFixture)
{
  std::set<std::string> testCaseLoggers{"LoggerFactoryListModules1", "LoggerFactoryListModules2"};

  for (const auto& name : testCaseLoggers) {
    LoggerFactory::create(name);
  }

  auto&& modules = LoggerFactory::getInstance().getModules();
  BOOST_CHECK_GE(modules.size(), 2);

  for (const auto& name : modules) {
    testCaseLoggers.erase(name);
  }

  BOOST_CHECK_EQUAL(testCaseLoggers.size(), 0);
}

BOOST_AUTO_TEST_SUITE_END()

} // namespace tests
} // namespace nfd

// Testing compilation of the logger outside ::nfd namespace

namespace test_logger { // another root namespace

void
Test1()
{
  NFD_LOG_INIT("Test");

  NFD_LOG_TRACE("Trace");
  NFD_LOG_DEBUG("Debug");
  NFD_LOG_INFO("Info");
  NFD_LOG_WARN("Warn");
  NFD_LOG_ERROR("Error");
  NFD_LOG_FATAL("Fatal");
}

class Test2
{
  NFD_LOG_INCLASS_DECLARE();
};

NFD_LOG_INCLASS_DEFINE(Test2, "Test2");

template<class T>
class Test3
{
  NFD_LOG_INCLASS_DECLARE();
};

NFD_LOG_INCLASS_TEMPLATE_DEFINE(Test3, "Test3");

NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(Test3, int, "Test3Int");

template<class X, class Y>
class Test4
{
  NFD_LOG_INCLASS_DECLARE();
};

NFD_LOG_INCLASS_2TEMPLATE_SPECIALIZATION_DEFINE(Test4, int, int, "Test4IntInt");

namespace nfd { // nested nfd namespace, different from ::nfd

void
Test1()
{
  NFD_LOG_INIT("Test");

  NFD_LOG_TRACE("Trace");
  NFD_LOG_DEBUG("Debug");
  NFD_LOG_INFO("Info");
  NFD_LOG_WARN("Warn");
  NFD_LOG_ERROR("Error");
  NFD_LOG_FATAL("Fatal");
}

class Test2
{
  NFD_LOG_INCLASS_DECLARE();
};

NFD_LOG_INCLASS_DEFINE(Test2, "Test2");

template<class T>
class Test3
{
  NFD_LOG_INCLASS_DECLARE();
};

NFD_LOG_INCLASS_TEMPLATE_DEFINE(Test3, "Test3");

NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(Test3, int, "Test3Int");

template<class X, class Y>
class Test4
{
  NFD_LOG_INCLASS_DECLARE();
};

NFD_LOG_INCLASS_2TEMPLATE_SPECIALIZATION_DEFINE(Test4, int, int, "Test4IntInt");

} // namespace nfd
} // namespace test_logger

max_stars_repo_name: NDNLink/NDN-Chord

max_issues_repo_name: NDNLink/NDN-Chord

max_forks_repo_name: NDNLink/NDN-Chord


text:
/**
 * @CopyRight:
 * FISCO-BCOS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FISCO-BCOS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with FISCO-BCOS.  If not, see <http://www.gnu.org/licenses/>
 * (c) 2016-2018 fisco-dev contributors.
 *
 * @brief Construct a new boost auto test case object for FileSystem
 *
 * @file FileSystem.cpp
 * @author: tabsu
 * @date 2018-08-24
 */

#include <libdevcore/FileSystem.h>
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(FileSystem)

BOOST_AUTO_TEST_CASE(testFileSystem)
{
    dev::setDataDir("./data");
    /// test getDataDir
    BOOST_CHECK((dev::getDataDir("ethereum").string() == "./ethereum") == true);
    BOOST_CHECK((dev::getDataDir("fisco-bcos-data").string() == "./data") == true);
    BOOST_CHECK((dev::getDataDir("test").filename() == "test") == true);
    BOOST_CHECK((dev::getDataDir().string() == "./data") == true);
    BOOST_CHECK(dev::getDataDir("/data").string() == "/data");

    /// test getLedgerDir
    BOOST_CHECK(dev::getLedgerDir("ledger1").string() == "./data/ledger1");
    BOOST_CHECK(dev::getLedgerDir("ledger1", "/data").string() == "/data/ledger1");

    dev::setIpcPath("/xxx/geth.ipc");
    BOOST_CHECK((dev::getIpcPath().string() == "/xxx") == true);

    dev::setIpcPath("/xxx/eth.ipc");
    BOOST_CHECK((dev::getIpcPath().string() == "/xxx/eth.ipc") == true);

    dev::setIpcPath("geth.ipc");
}

BOOST_AUTO_TEST_SUITE_END()

max_stars_repo_name: imtypist/FISCO-BCOS

max_issues_repo_name: imtypist/FISCO-BCOS

max_forks_repo_name: imtypist/FISCO-BCOS


text:
// This file is part of snark, a generic and flexible library for robotics research
// Copyright (c) 2011 The University of Sydney
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the name of the University of Sydney nor the
//    names of its contributors may be used to endorse or promote products
//    derived from this software without specific prior written permission.
//
// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
// GRANTED BY THIS LICENSE.  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.

#include <cmath>
#include <Eigen/Eigen>
#include "covariance.h"

namespace snark{

squared_exponential_covariance::squared_exponential_covariance( double length_scale
                                                              , double signal_variance
                                                              , double data_variance )
    : signal_variance_( signal_variance )
    , data_variance_( data_variance )
    , factor_( -1.0 / ( 2.0 * std::sqrt( length_scale ) ) )
    , self_covariance_( signal_variance + data_variance )
{
}

double squared_exponential_covariance::covariance( const Eigen::VectorXd& v, const Eigen::VectorXd& w ) const
{
    const Eigen::VectorXd& diff = v - w;
    return signal_variance_ * std::exp( factor_ * diff.dot( diff ) );
}

double squared_exponential_covariance::self_covariance() const { return self_covariance_; }

} // namespace snark{ { namespace Robotics {

max_stars_repo_name: mission-systems-pty-ltd/snark

max_issues_repo_name: NEU-LC/snark

max_forks_repo_name: NEU-LC/snark


text:
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

#include "QuadProgMm.hpp"
using namespace QuadProgMm;

void expect_linear_expression_(const LinearExpression&) {}
void expect_quadratic_expression_(const QuadraticExpression&) {}
void expect_constraint_(const Constraint&) {}

#define EXPECT_LINEAR_EXPRESSION(x) expect_linear_expression_(x); expect_quadratic_expression_(x);
#define EXPECT_QUADRATIC_EXPRESSION(x) expect_quadratic_expression_(x);
#define EXPECT_CONSTRAINT(x) expect_constraint_(x);

BOOST_AUTO_TEST_CASE(Conversions) {
  double d = 0;
  Variable v;
  LinearExpression l;
  QuadraticExpression q;

  EXPECT_LINEAR_EXPRESSION(d);
  EXPECT_LINEAR_EXPRESSION(v);
  EXPECT_LINEAR_EXPRESSION(l);
  EXPECT_QUADRATIC_EXPRESSION(q);

  // N/A: d = d;
  // N/A: d = v;
  // N/A: d = l;
  // N/A: d = q;

  // N/A: v = d;
  // N/A: v = v;
  // N/A: v = l;
  // N/A: v = q;

  l = d;
  l = v;
  l = l;
  // N/A: l = q;

  q = d;
  q = v;
  q = l;
  q = q;
}

BOOST_AUTO_TEST_CASE(UnarySignOperators) {
  Variable v;
  LinearExpression l;
  QuadraticExpression q;

  EXPECT_LINEAR_EXPRESSION(+v);
  EXPECT_LINEAR_EXPRESSION(-v);
  EXPECT_LINEAR_EXPRESSION(+l);
  EXPECT_LINEAR_EXPRESSION(-l);
  EXPECT_QUADRATIC_EXPRESSION(+q);
  EXPECT_QUADRATIC_EXPRESSION(-q);
}

BOOST_AUTO_TEST_CASE(AdditionOperators) {
  double d = 0;
  Variable v;
  LinearExpression l;
  QuadraticExpression q;

  // N/A: d d
  // d v
  // N/A: d += v;
  // N/A: d -= v;
  EXPECT_LINEAR_EXPRESSION(d + v);
  EXPECT_LINEAR_EXPRESSION(d - v);
  // d l
  // N/A: d += l;
  // N/A: d -= l;
  EXPECT_LINEAR_EXPRESSION(d + l);
  EXPECT_LINEAR_EXPRESSION(d - l);
  // d q
  // N/A: d += q;
  // N/A: d -= q;
  EXPECT_QUADRATIC_EXPRESSION(d + q);
  EXPECT_QUADRATIC_EXPRESSION(d - q);

  // v d
  // N/A: v += d;
  // N/A: v -= d;
  EXPECT_LINEAR_EXPRESSION(v + d);
  EXPECT_LINEAR_EXPRESSION(v - d);
  // v v
  // N/A: v += v;
  // N/A: v -= v;
  EXPECT_LINEAR_EXPRESSION(v + v);
  EXPECT_LINEAR_EXPRESSION(v - v);
  // v l
  // N/A: v += l;
  // N/A: v -= l;
  EXPECT_LINEAR_EXPRESSION(v + l);
  EXPECT_LINEAR_EXPRESSION(v - l);
  // v q
  // N/A: v += q;
  // N/A: v -= q;
  EXPECT_QUADRATIC_EXPRESSION(v + q);
  EXPECT_QUADRATIC_EXPRESSION(v - q);

  // l d
  l += d;
  l -= d;
  EXPECT_LINEAR_EXPRESSION(l + d);
  EXPECT_LINEAR_EXPRESSION(l - d);
  // l v
  l += v;
  l -= v;
  EXPECT_LINEAR_EXPRESSION(l + v);
  EXPECT_LINEAR_EXPRESSION(l - v);
  // l l
  l += l;
  l -= l;
  EXPECT_LINEAR_EXPRESSION(l + l);
  EXPECT_LINEAR_EXPRESSION(l - l);
  // l q
  // N/A: l += q;
  // N/A: l -= q;
  EXPECT_QUADRATIC_EXPRESSION(l + q);
  EXPECT_QUADRATIC_EXPRESSION(l - q);

  // q d
  q += d;
  q -= d;
  EXPECT_QUADRATIC_EXPRESSION(q + d);
  EXPECT_QUADRATIC_EXPRESSION(q - d);
  // q v
  q += v;
  q -= v;
  EXPECT_QUADRATIC_EXPRESSION(q + v);
  EXPECT_QUADRATIC_EXPRESSION(q - v);
  // q l
  q += l;
  q -= l;
  EXPECT_QUADRATIC_EXPRESSION(q + l);
  EXPECT_QUADRATIC_EXPRESSION(q - l);
  // q q
  q += q;
  q -= q;
  EXPECT_QUADRATIC_EXPRESSION(q + q);
  EXPECT_QUADRATIC_EXPRESSION(q - q);
}

BOOST_AUTO_TEST_CASE(MultiplicationOperators) {
  double d = 0;
  Variable v;
  LinearExpression l;
  QuadraticExpression q;

  // N/A: d d
  // d v
  // N/A: d *= v;
  // N/A: d /= v;
  EXPECT_LINEAR_EXPRESSION(d * v);
  // N/A: d / v;
  // d l
  // N/A: d *= l;
  // N/A: d /= l;
  EXPECT_LINEAR_EXPRESSION(d * l);
  // N/A: d / l;
  // d q
  // N/A: d *= q;
  // N/A: d /= q;
  EXPECT_QUADRATIC_EXPRESSION(d * q);
  // N/A: d / q;

  // v d
  // N/A: v *= d;
  // N/A: v /= d;
  EXPECT_LINEAR_EXPRESSION(v * d);
  EXPECT_LINEAR_EXPRESSION(v / d);
  // v v
  // N/A: v *= v;
  // N/A: v /= v;
  EXPECT_QUADRATIC_EXPRESSION(v * v);
  // N/A: v / v;
  // v l
  // N/A: v *= l;
  // N/A: v /= l;
  EXPECT_QUADRATIC_EXPRESSION(v * l);
  // N/A: v / l;
  // v q
  // N/A: v *= q;
  // N/A: v /= q;
  // N/A: v * q;
  // N/A: v / q;

  // l d
  l *= d;
  l /= d;
  EXPECT_LINEAR_EXPRESSION(l * d);
  EXPECT_LINEAR_EXPRESSION(l / d);
  // l v
  // N/A: l *= v;
  // N/A: l /= v;
  EXPECT_QUADRATIC_EXPRESSION(l * v);
  // N/A: l / v;
  // l l
  // N/A: l *= l;
  // N/A: l /= l;
  EXPECT_QUADRATIC_EXPRESSION(l * l);
  // N/A: l / l;
  // l q
  // N/A: l *= q;
  // N/A: l /= q;
  // N/A: l * q;
  // N/A: l / q;

  // q d
  q *= d;
  q /= d;
  EXPECT_QUADRATIC_EXPRESSION(q * d);
  EXPECT_QUADRATIC_EXPRESSION(q / d);
  // q v
  // N/A: q *= v;
  // N/A: q /= v;
  // N/A: q * v;
  // N/A: q / v;
  // q l
  // N/A: q *= l;
  // N/A: q /= l;
  // N/A: q * l;
  // N/A: q / l;
  // q q
  // N/A: q *= q;
  // N/A: q /= q;
  // N/A: q * q;
  // N/A: q / q;
}

BOOST_AUTO_TEST_CASE(ComparisonOperators) {
  double d = 0;
  Variable v;
  LinearExpression l;

  // N/A: d d
  // d v
  EXPECT_CONSTRAINT(d == v)
  EXPECT_CONSTRAINT(d >= v)
  EXPECT_CONSTRAINT(d <= v)
  // d l
  EXPECT_CONSTRAINT(d == l)
  EXPECT_CONSTRAINT(d >= l)
  EXPECT_CONSTRAINT(d <= l)

  // v d
  EXPECT_CONSTRAINT(v == d)
  EXPECT_CONSTRAINT(v >= d)
  EXPECT_CONSTRAINT(v <= d)
  // v v
  EXPECT_CONSTRAINT(v == v)
  EXPECT_CONSTRAINT(v >= v)
  EXPECT_CONSTRAINT(v <= v)
  // v l
  EXPECT_CONSTRAINT(v == l)
  EXPECT_CONSTRAINT(v >= l)
  EXPECT_CONSTRAINT(v <= l)

  // l d
  EXPECT_CONSTRAINT(l == d)
  EXPECT_CONSTRAINT(l >= d)
  EXPECT_CONSTRAINT(l <= d)
  // l v
  EXPECT_CONSTRAINT(l == v)
  EXPECT_CONSTRAINT(l >= v)
  EXPECT_CONSTRAINT(l <= v)
  // l l
  EXPECT_CONSTRAINT(l == l)
  EXPECT_CONSTRAINT(l >= l)
  EXPECT_CONSTRAINT(l <= l)
}

ext: cc