// Copyright (c) 2012 Christopher Lux <christopherlux@gmail.com>
// Distributed under the Modified BSD License, see license.txt.

#include "quad.h"

#include <cassert>

#include <boost/assign/list_of.hpp>

#include <scm/core/memory.h>

#include <scm/gl_core/log.h>
#include <scm/gl_core/render_device.h>
#include <scm/gl_core/buffer_objects/buffer.h>
#include <scm/gl_core/buffer_objects/scoped_buffer_map.h>
#include <scm/gl_core/render_device/context.h>
#include <scm/gl_core/buffer_objects/vertex_array.h>
#include <scm/gl_core/buffer_objects/vertex_format.h>

namespace {
    struct vertex {
        scm::math::vec3f pos;
        scm::math::vec2f tex;
    };
} // namespace

namespace scm {
namespace gl {

quad_geometry::quad_geometry(const render_device_ptr& in_device,
                             const math::vec2f& in_min_vertex,
                             const math::vec2f& in_max_vertex,
                             const math::vec2f& in_min_texcoord,
                             const math::vec2f& in_max_texcoord)
  : geometry(in_device),
    _min_vertex(in_min_vertex),
    _max_vertex(in_max_vertex)
{
    using namespace scm::math;

    int num_vertices            = 4;

    scoped_array<unsigned short>    ind_s(new unsigned short[4]);
    scoped_array<unsigned short>    ind_w(new unsigned short[4]);

    _vertices = in_device->create_buffer(BIND_VERTEX_BUFFER, USAGE_STATIC_DRAW, num_vertices * sizeof(vertex), 0);

    render_context_ptr ctx = in_device->main_context();
    vertex* data = static_cast<vertex*>(ctx->map_buffer(_vertices, ACCESS_WRITE_INVALIDATE_BUFFER));

    const vec2f& o = in_min_vertex;
    const vec2f& l = in_max_vertex;

    const vec2f& ot = in_min_texcoord;
    const vec2f& lt = in_max_texcoord;

    data[0].pos = vec3f(o.x, o.y, 0.0f); data[0].tex = vec2f(ot.x, ot.y);
    data[1].pos = vec3f(l.x, o.y, 0.0f); data[1].tex = vec2f(lt.x, ot.y);
    data[2].pos = vec3f(o.x, l.y, 0.0f); data[2].tex = vec2f(ot.x, lt.y);
    data[3].pos = vec3f(l.x, l.y, 0.0f); data[3].tex = vec2f(lt.x, lt.y);

    ctx->unmap_buffer(_vertices);

    // solid indices
    ind_s[0]  =  0;
    ind_s[1]  =  1;
    ind_s[2]  =  2;
    ind_s[3]  =  3;

    // wire indices
    ind_w[0]  =  0;
    ind_w[1]  =  1;
    ind_w[2]  =  3;
    ind_w[3]  =  2;

    using namespace scm::gl;
    using boost::assign::list_of;

    _solid_indices = in_device->create_buffer(BIND_INDEX_BUFFER, USAGE_STATIC_DRAW, num_vertices * sizeof(unsigned short), ind_s.get());
    _wire_indices  = in_device->create_buffer(BIND_INDEX_BUFFER, USAGE_STATIC_DRAW, num_vertices * sizeof(unsigned short), ind_w.get());

    _vertex_array = in_device->create_vertex_array(vertex_format(0, 0, TYPE_VEC3F, sizeof(vertex))
                                                                (0, 2, TYPE_VEC2F, sizeof(vertex)),
                                                   list_of(_vertices));
}

quad_geometry::~quad_geometry()
{
    _vertex_array.reset();
    _solid_indices.reset();
    _wire_indices.reset();
    _vertices.reset();
}

void
quad_geometry::update(const render_context_ptr& in_context,
                      const math::vec2f&        in_min_vertex,
                      const math::vec2f&        in_max_vertex,
                      const math::vec2f&        in_min_texcoord,
                      const math::vec2f&        in_max_texcoord)
{
    using namespace scm::math;

    scoped_buffer_map vb_map(in_context, _vertices, ACCESS_WRITE_INVALIDATE_BUFFER);

    if (!vb_map) {
        glerr() << log::error
                << "quad_geometry::update(): unable to map vertex buffer." << log::end;
        return;
    }

    vertex*const    data = reinterpret_cast<vertex*const>(vb_map.data_ptr());

    const vec2f& o = in_min_vertex;
    const vec2f& l = in_max_vertex;

    const vec2f& ot = in_min_texcoord;
    const vec2f& lt = in_max_texcoord;

    data[0].pos = vec3f(o.x, o.y, 0.0f); data[0].tex = vec2f(ot.x, ot.y);
    data[1].pos = vec3f(l.x, o.y, 0.0f); data[1].tex = vec2f(lt.x, ot.y);
    data[2].pos = vec3f(o.x, l.y, 0.0f); data[2].tex = vec2f(ot.x, lt.y);
    data[3].pos = vec3f(l.x, l.y, 0.0f); data[3].tex = vec2f(lt.x, lt.y);
}

void
quad_geometry::draw(const render_context_ptr& in_context,
                    const draw_mode in_draw_mode) const
{
    context_vertex_input_guard vig(in_context);

    in_context->bind_vertex_array(_vertex_array);

    if (in_draw_mode == MODE_SOLID) {
        in_context->bind_index_buffer(_solid_indices, PRIMITIVE_TRIANGLE_STRIP, TYPE_USHORT);
    }
    else if (in_draw_mode == MODE_WIRE_FRAME) {
        in_context->bind_index_buffer(_wire_indices, PRIMITIVE_LINE_LOOP, TYPE_USHORT);
    }
    else {
        return;
    }

    in_context->apply();
    in_context->draw_elements(4);
}

void
quad_geometry::draw_instanced(const render_context_ptr& in_context,
                              const int instance_count,
                              const draw_mode in_draw_mode
                             ) const
{
    context_vertex_input_guard vig(in_context);

    in_context->bind_vertex_array(_vertex_array);

    if (in_draw_mode == MODE_SOLID) {
        in_context->bind_index_buffer(_solid_indices, PRIMITIVE_TRIANGLE_STRIP, TYPE_USHORT);
    }
    else if (in_draw_mode == MODE_WIRE_FRAME) {
        in_context->bind_index_buffer(_wire_indices, PRIMITIVE_LINE_LOOP, TYPE_USHORT);
    }
    else {
        return;
    }

    in_context->apply();
    in_context->draw_elements_instanced(4,0, instance_count);
}

} // namespace gl
} // namespace scm


//
// Created by przestaw on 11.12.2020.
//

#ifndef NBC4GPU_GPUCALCULATECLASSP_HPP
#define NBC4GPU_GPUCALCULATECLASSP_HPP

#include <Defines.h>
#include <error/Execeptions.h>
DIAGNOSTIC_PUSH
#include <boost/compute/algorithm/accumulate.hpp>
#include <boost/compute/algorithm/transform.hpp>
#include <boost/compute/container/vector.hpp>
#include <boost/compute/core.hpp>
#include <boost/compute/lambda.hpp>
DIAGNOSTIC_POP
#include <gpuclassifier/GPULearnClass.hpp>

namespace nbc4gpu {
  template <typename ValueType> class GPUCalculateClassP {
  public:
    using Statistics = typename GPULearnClass<ValueType>::Statistics;
    using Record     = std::vector<ValueType>;

    /**
     * Constructor
     * @param statistics - statistics of a class values
     * @param classId - class identifer value
     * @param queue - device queue
     */
    GPUCalculateClassP(Statistics statistics,
                       ValueType classId,
                       boost::compute::command_queue& queue);

    /**
     * Calculates propability of record given in a constructor
     * @return propability
     */
    ValueType operator()(const Record& record);

    /**
     * Getter for classId
     * @return class identifier
     */
    inline const ValueType getClassId() const { return classId_; }

  private:
    ValueType calcPropability(const Record& record);
    ValueType classId_;
    Statistics statistics_;
    boost::compute::command_queue& queue_;
  };

  template <typename ValueType>
  GPUCalculateClassP<ValueType>::GPUCalculateClassP(
      GPUCalculateClassP::Statistics statistics,
      ValueType classId,
      boost::compute::command_queue& queue)
      : classId_(classId), statistics_(statistics), queue_(queue) {
    if (statistics_.empty()) {
      throw nbc4gpu::error::ZeroValuesProvided(
          " class Propability calculation");
    }
  }

  template <typename ValueType>
  ValueType GPUCalculateClassP<ValueType>::operator()(const Record& record) {
    if (record.size() != statistics_.size()) {
      throw nbc4gpu::error::MismatchedSize(
          " record and dataset have diffrent sizes of columns");
    }
    return calcPropability(record);
  }

  template <typename ValueType>
  ValueType
  GPUCalculateClassP<ValueType>::calcPropability(const Record& record) {
    static const ValueType sqr2pi = sqrt(2 * 3.14159265);
    // exponent = exp(-((x-avg)^ 2 / (2 * variance )))
    // base =  (1 / (sqrt(2 * pi) * sqrt(variance)))
    // return e1*b1*e2*b2.... : e in exponent, b in base

    // 1. transfer record, avg & means to device
    boost::compute::vector<ValueType> recordVector(record.size(),
                                                   queue_.get_context());
    boost::compute::future<void> fRec = boost::compute::copy_async(
        record.begin(), record.end(), recordVector.begin(), queue_);

    std::vector<ValueType> avgs;
    std::vector<ValueType> variances;
    for (const auto& iter : statistics_) {
      avgs.emplace_back(iter.first);
      variances.emplace_back(iter.second);
    }

    boost::compute::vector<ValueType> avgVector(avgs.size(),
                                                queue_.get_context());
    boost::compute::future<void> fAvg = boost::compute::copy_async(
        avgs.begin(), avgs.end(), avgVector.begin(), queue_);

    boost::compute::vector<ValueType> varianceVector(variances.size(),
                                                     queue_.get_context());
    boost::compute::future<void> fVar = boost::compute::copy_async(
        variances.begin(), variances.end(), varianceVector.begin(), queue_);

    // 1. Calculate exponent
    fRec.wait();
    fAvg.wait();
    boost::compute::transform(recordVector.begin(),
                              recordVector.end(),
                              avgVector.begin(),
                              recordVector.begin(),
                              boost::compute::lambda::_1
                                  - boost::compute::lambda::_2,
                              queue_);
    // Note : x - avg

    boost::compute::transform(
        recordVector.begin(),
        recordVector.end(),
        recordVector.begin(),
        -1.0 * (boost::compute::lambda::_1 * boost::compute::lambda::_1),
        queue_);
    // Note : -(x - avg)^2

    fVar.wait();
    boost::compute::transform(
        recordVector.begin(),
        recordVector.end(),
        varianceVector.begin(),
        recordVector.begin(),
        exp(boost::compute::lambda::_1 / (2.0 * boost::compute::lambda::_2)),
        queue_);
    // Note : uses builtin function boost::compute::exp<ValueType(ValueType)>()
    //        wrapped using BOOST_COMPUTE_LAMBDA_WRAP_UNARY_FUNCTION_T

    boost::compute::transform(recordVector.begin(),
                              recordVector.end(),
                              varianceVector.begin(),
                              recordVector.begin(),
                              boost::compute::lambda::_1
                                  / (sqrt(boost::compute::lambda::_2) * sqr2pi),
                              queue_);

    // 3. summarize
    ValueType propability = 1.0;
    boost::compute::reduce(recordVector.begin(),
                           recordVector.end(),
                           &propability,
                           //(boost::compute::multiplies<ValueType>()),
                           boost::compute::lambda::_1
                               * boost::compute::lambda::_2,
                           queue_);

    // 4. return propability
    return propability;
  }
} // namespace nbc4gpu

#endif // NBC4GPU_GPUCALCULATECLASSP_HPP


/*
 Copyright (C) 2016 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

#include <ored/portfolio/builders/capfloor.hpp>
#include <ored/portfolio/builders/swap.hpp>
#include <ored/portfolio/builders/yoycapfloor.hpp>
#include <ored/portfolio/capfloor.hpp>
#include <ored/portfolio/legdata.hpp>
#include <ored/utilities/log.hpp>
#include <ored/portfolio/fixingdates.hpp>

#include <ql/instruments/capfloor.hpp>
#include <ql/instruments/inflationcapfloor.hpp>
#include <qle/indexes/inflationindexwrapper.hpp>

#include <boost/make_shared.hpp>

using namespace QuantLib;

namespace ore {
namespace data {
void CapFloor::build(const boost::shared_ptr<EngineFactory>& engineFactory) {

    // Make sure the leg is floating or CMS
    QL_REQUIRE((legData_.legType() == "Floating") || (legData_.legType() == "CMS") || (legData_.legType() == "CPI") ||
                   (legData_.legType() == "YY"),
               "CapFloor build error, LegType must be Floating, CMS, CPI or YY");

    // Determine if we have a cap, a floor or a collar
    QL_REQUIRE(caps_.size() > 0 || floors_.size() > 0, "CapFloor build error, no cap rates or floor rates provided");
    QuantLib::CapFloor::Type capFloorType;
    if (floors_.size() == 0) {
        capFloorType = QuantLib::CapFloor::Cap;
    } else if (caps_.size() == 0) {
        capFloorType = QuantLib::CapFloor::Floor;
    } else {
        capFloorType = QuantLib::CapFloor::Collar;
    }

    // Clear legs before building
    legs_.clear();

    // Make sure that the floating leg section does not have caps or floors
    boost::shared_ptr<EngineBuilder> builder;

    if (legData_.legType() == "Floating") {
        builder = engineFactory->builder(tradeType_);

        boost::shared_ptr<FloatingLegData> floatData =
            boost::dynamic_pointer_cast<FloatingLegData>(legData_.concreteLegData());
        QL_REQUIRE(floatData, "Wrong LegType, expected Floating, got " << legData_.legType());
        QL_REQUIRE(floatData->caps().empty() && floatData->floors().empty(),
                   "CapFloor build error, Floating leg section must not have caps and floors");

        underlyingIndex_ = floatData->index();
        Handle<IborIndex> hIndex =
            engineFactory->market()->iborIndex(underlyingIndex_, builder->configuration(MarketContext::pricing));
        QL_REQUIRE(!hIndex.empty(), "Could not find ibor index " << underlyingIndex_ << " in market.");
        boost::shared_ptr<IborIndex> index = hIndex.currentLink();

        // Do not support caps/floors involving overnight indices
        boost::shared_ptr<OvernightIndex> ois = boost::dynamic_pointer_cast<OvernightIndex>(index);
        QL_REQUIRE(!ois, "CapFloor trade type does not support overnight indices.");
        legs_.push_back(makeIborLeg(legData_, index, engineFactory));

        // If a vector of cap/floor rates are provided, ensure they align with the number of schedule periods
        if (floors_.size() > 1) {
            QL_REQUIRE(floors_.size() == legs_[0].size(),
                       "The number of floor rates provided does not match the number of schedule periods");
        }

        if (caps_.size() > 1) {
            QL_REQUIRE(caps_.size() == legs_[0].size(),
                       "The number of cap rates provided does not match the number of schedule periods");
        }

        // If one cap/floor rate is given, extend the vector to align with the number of schedule periods
        if (floors_.size() == 1)
            floors_.resize(legs_[0].size(), floors_[0]);

        if (caps_.size() == 1)
            caps_.resize(legs_[0].size(), caps_[0]);

        // Create QL CapFloor instrument
        boost::shared_ptr<QuantLib::CapFloor> capFloor =
            boost::make_shared<QuantLib::CapFloor>(capFloorType, legs_[0], caps_, floors_);

        boost::shared_ptr<CapFloorEngineBuilder> capFloorBuilder =
            boost::dynamic_pointer_cast<CapFloorEngineBuilder>(builder);
        capFloor->setPricingEngine(capFloorBuilder->engine(parseCurrency(legData_.currency())));

        // Wrap the QL instrument in a vanilla instrument
        Real multiplier = (parsePositionType(longShort_) == Position::Long ? 1.0 : -1.0);
        instrument_ = boost::make_shared<VanillaInstrument>(capFloor, multiplier);

        maturity_ = capFloor->maturityDate();

    } else if (legData_.legType() == "CMS") {
        builder = engineFactory->builder("Swap");

        boost::shared_ptr<CMSLegData> cmsData = boost::dynamic_pointer_cast<CMSLegData>(legData_.concreteLegData());
        QL_REQUIRE(cmsData, "Wrong LegType, expected CMS");

        underlyingIndex_ = cmsData->swapIndex();
        Handle<SwapIndex> hIndex =
            engineFactory->market()->swapIndex(underlyingIndex_, builder->configuration(MarketContext::pricing));
        QL_REQUIRE(!hIndex.empty(), "Could not find swap index " << underlyingIndex_ << " in market.");

        boost::shared_ptr<SwapIndex> index = hIndex.currentLink();

        bool payer = (parsePositionType(longShort_) == Position::Long ? false : true);
        vector<bool> legPayers_;
        legs_.push_back(makeCMSLeg(legData_, index, engineFactory, caps_, floors_));
        legPayers_.push_back(!payer);
        legs_.push_back(makeCMSLeg(legData_, index, engineFactory));
        legPayers_.push_back(payer);

        boost::shared_ptr<QuantLib::Swap> capFloor(new QuantLib::Swap(legs_, legPayers_));
        boost::shared_ptr<SwapEngineBuilderBase> cmsCapFloorBuilder =
            boost::dynamic_pointer_cast<SwapEngineBuilderBase>(builder);
        capFloor->setPricingEngine(cmsCapFloorBuilder->engine(parseCurrency(legData_.currency())));

        instrument_.reset(new VanillaInstrument(capFloor));
        maturity_ = capFloor->maturityDate();

    } else if (legData_.legType() == "CPI") {
        // Add CPI Cap/Floors
    } else if (legData_.legType() == "YY") {
        builder = engineFactory->builder("YYCapFloor");

        boost::shared_ptr<YoYLegData> yyData = boost::dynamic_pointer_cast<YoYLegData>(legData_.concreteLegData());
        QL_REQUIRE(yyData, "Wrong LegType, expected YY");

        underlyingIndex_ = yyData->index();
        Handle<YoYInflationIndex> yoyIndex;
        // look for yoy inflation index
        yoyIndex = engineFactory->market()->yoyInflationIndex(underlyingIndex_, builder->configuration(MarketContext::pricing));

        // we must have either an yoy or a zero inflation index in the market, if no yoy curve, get teh zero
        // and create a yoy index from it
        if (yoyIndex.empty()) {
            Handle<ZeroInflationIndex> zeroIndex =
                engineFactory->market()->zeroInflationIndex(underlyingIndex_, builder->configuration(MarketContext::pricing));
            QL_REQUIRE(!zeroIndex.empty(),
                "Could not find inflation index (of type either zero or yoy) " << underlyingIndex_ << " in market.");
            yoyIndex = Handle<YoYInflationIndex>(
                boost::make_shared<QuantExt::YoYInflationIndexWrapper>(zeroIndex.currentLink(), zeroIndex->interpolated()));
        }

        legs_.push_back(makeYoYLeg(legData_, yoyIndex.currentLink(), engineFactory));

        // If a vector of cap/floor rates are provided, ensure they align with the number of schedule periods
        if (floors_.size() > 1) {
            QL_REQUIRE(floors_.size() == legs_[0].size(),
                       "The number of floor rates provided does not match the number of schedule periods");
        }

        if (caps_.size() > 1) {
            QL_REQUIRE(caps_.size() == legs_[0].size(),
                       "The number of cap rates provided does not match the number of schedule periods");
        }

        // If one cap/floor rate is given, extend the vector to align with the number of schedule periods
        if (floors_.size() == 1)
            floors_.resize(legs_[0].size(), floors_[0]);

        if (caps_.size() == 1)
            caps_.resize(legs_[0].size(), caps_[0]);

        // Create QL YoY Inflation CapFloor instrument
        boost::shared_ptr<QuantLib::YoYInflationCapFloor> yoyCapFloor;
        if (capFloorType == QuantLib::CapFloor::Cap) {
            yoyCapFloor = boost::shared_ptr<YoYInflationCapFloor>(new YoYInflationCap(legs_[0], caps_));
        } else if (capFloorType == QuantLib::CapFloor::Floor) {
            yoyCapFloor = boost::shared_ptr<YoYInflationCapFloor>(new YoYInflationFloor(legs_[0], floors_));
        } else {
            QL_FAIL("unknown YoYInflation cap/floor type");
        }

        boost::shared_ptr<YoYCapFloorEngineBuilder> capFloorBuilder =
            boost::dynamic_pointer_cast<YoYCapFloorEngineBuilder>(builder);
        yoyCapFloor->setPricingEngine(capFloorBuilder->engine(underlyingIndex_));

        // Wrap the QL instrument in a vanilla instrument
        Real multiplier = (parsePositionType(longShort_) == Position::Long ? 1.0 : -1.0);
        instrument_ = boost::make_shared<VanillaInstrument>(yoyCapFloor, multiplier);

        maturity_ = yoyCapFloor->maturityDate();

    } else {
        QL_FAIL("Invalid legType for CapFloor");
    }

    // Fill in remaining Trade member data
    legCurrencies_.push_back(legData_.currency());
    legPayers_.push_back(legData_.isPayer());
    npvCurrency_ = legData_.currency();
    notional_ = currentNotional(legs_[0]);
}

map<string, set<Date>> CapFloor::fixings(const Date& settlementDate) const {

    map<string, set<Date>> result;

    if (legs_.empty() || underlyingIndex_.empty()) {
        WLOG("Need to build CapFloor before asking for fixings");
        return result;
    }

    result[underlyingIndex_] = fixingDates(legs_[0], settlementDate);

    return result;
}

void CapFloor::fromXML(XMLNode* node) {
    Trade::fromXML(node);
    XMLNode* capFloorNode = XMLUtils::getChildNode(node, "CapFloorData");
    longShort_ = XMLUtils::getChildValue(capFloorNode, "LongShort", true);
    legData_.fromXML(XMLUtils::getChildNode(capFloorNode, "LegData"));
    caps_ = XMLUtils::getChildrenValuesAsDoubles(capFloorNode, "Caps", "Cap");
    floors_ = XMLUtils::getChildrenValuesAsDoubles(capFloorNode, "Floors", "Floor");
}

XMLNode* CapFloor::toXML(XMLDocument& doc) {
    XMLNode* node = Trade::toXML(doc);
    XMLNode* capFloorNode = doc.allocNode("CapFloorData");
    XMLUtils::appendNode(node, capFloorNode);
    XMLUtils::addChild(doc, capFloorNode, "LongShort", longShort_);
    XMLUtils::appendNode(capFloorNode, legData_.toXML(doc));
    XMLUtils::addChildren(doc, capFloorNode, "Caps", "Cap", caps_);
    XMLUtils::addChildren(doc, capFloorNode, "Floors", "Floor", floors_);
    return node;
}
} // namespace data
} // namespace ore


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdexcept>
#include <cmath>
#include <string>
#include <map>
#include <deque>
#include <iostream>
#include <fstream>
#include <unordered_map>
#include <boost/format.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include "utils.h"
#include "matrix.h"
#include "map.h"
#include "pdslabel.h"
#include "pds.h"
#include "pdsset.h"
#include "../../povray/include/povray_plugin.h"
#include "noise.h"

const double MAXHEIGHT = 22; // maximum height of any object on the surface

PDSSet geoidpds;
PDSSet terrainpds;

#include "variables.h"

class PluginVariables : public Variables
{
public:
	PluginVariables()
	{
		parse(0, NULL);
		for (auto s : terrain)
			terrainpds.add(s);

		for (auto s : geoid)
			geoidpds.add(s);
	}
};

PluginVariables vars;

#include "functions.h"
#include "texture.h"
#include "writer.h"
#include "sphericalroam.h"
#include "povpluginwriter.h"
#include "terrain.h"
#include "sea.h"

class Initialiser
{
public:
	Initialiser()
	{
		initCalculon();
	}
};

Sea sea;
Terrain terrain;
Initialiser init;

#define SPHERE          (10000.000)
#define AVERAGE_TERRAIN  (1737.400)
#define SEALEVEL           (-1.850)

#define ATMOSPHERE_BASE    (-2.000)
#define ATMOSPHERE_DEPTH  (200.000)
#define ATMOSPHERE_SCALE  (ATMOSPHERE_DEPTH * 2.0)

const double bottom_of_atmosphere = AVERAGE_TERRAIN + ATMOSPHERE_BASE;
const double top_of_atmosphere = bottom_of_atmosphere + ATMOSPHERE_DEPTH;

const double g = 1.622;     // m/s^2 --- note METRES
const double L = 0.0065/6;  // K/m --- temperature lapse rate. Adjusted, crudely
const double M = 0.0289644; // kg/mol --- molar mass of dry air
const double T0 = 293.0;    // kelvin --- STP
const double R = 8.3144;    // joules / mole kelvin --- gas constant
const double gM_RL = (g*M) / (R*L);
const double L_T0 = L / T0;

const double dscale = 1.0;  // from atmospheres to whatever Povray uses

static double pressure(double height)
{
	return pow((1 - L_T0*height), gM_RL);
}

static double rayleigh_density(const Point& p)
{
	const double height_from_centre = p.length();
	const double height_from_bottom = height_from_centre - bottom_of_atmosphere;

	return dscale * pressure(height_from_bottom);
}

extern "C" povray_scalar_fn povray_scalar_function_terrain_altitude;
double povray_scalar_function_terrain_altitude(double* data)
{
	Point p = Point(data);
	return terrain.at(p) - sea.at(p);
}

extern "C" povray_scalar_fn povray_scalar_function_terrain_gradient;
double povray_scalar_function_terrain_gradient(double* data)
{
	Point p = Point(data);
	return terrain.slope(p);
}

extern "C" povray_scalar_fn povray_scalar_function_rayleigh_density;
double povray_scalar_function_rayleigh_density(double* data)
{
	Point scaled = Point(data);
	return rayleigh_density(scaled);
}

struct povray_mesh* mesh_function(XYZMap& shape)
{
	Transform world;

	/* Set up the camera position. */

	world = world.lookAt(Point::ORIGIN, Vector::Y, Vector::Z);
	world = world.rotate(Vector::Y, -vars.longitude);
	world = world.rotate(Vector::X, -vars.latitude);
	world = world.rotate(Vector::X, -90);
	world = world.translate(Vector(0, vars.radius, 0));

	/* world is now relative to a point on a normalised sphere. Find out
	 * what sealevel is at this point and adjust the camera so it's
	 * relative to that. */

	Point camera = world.untransform(Point::ORIGIN);
	double sealevel = sea.at(camera);

	std::cerr << "sealevel at camera is " << sealevel << "km\n";

	world = world.translate(Vector(0, sealevel-vars.radius+vars.altitude, 0));

	/* Adjust for pointing direction. */

	world = world.rotate(Vector::Y, -vars.bearing);
	world = world.rotate(Vector::X, 90 + vars.azimuth);

	PovPluginWriter writer;
	SphericalRoam(shape, world, sealevel, vars.fov / vars.shmixels).writeTo(writer);
	//writer.applyTextureData(texture);
	writer.calculateNormals();

	return writer.writeToMesh(world);
}

extern "C" povray_mesh_fn povray_mesh_function_moon;
struct povray_mesh* povray_mesh_function_moon(void)
{
	return mesh_function(terrain);
}

extern "C" povray_mesh_fn povray_mesh_function_sea;
struct povray_mesh* povray_mesh_function_sea(void)
{
	return mesh_function(sea);
}


//---------------------------------------------------------------------------//
//!
//! \file   tstDeltaDistribution.cpp
//! \author Alex Robinson
//! \brief  Delta distribution unit tests.
//!
//---------------------------------------------------------------------------//

// Std Lib Includes
#include <iostream>
#include <limits>

// Boost Includes
#include <boost/units/systems/si.hpp>
#include <boost/units/systems/cgs.hpp>
#include <boost/units/io.hpp>

// FRENSIE Includes
#include "Utility_TabularUnivariateDistribution.hpp"
#include "Utility_DeltaDistribution.hpp"
#include "Utility_PhysicalConstants.hpp"
#include "Utility_UnitTraits.hpp"
#include "Utility_QuantityTraits.hpp"
#include "Utility_ElectronVoltUnit.hpp"
#include "Utility_UnitTestHarnessWithMain.hpp"
#include "ArchiveTestHelpers.hpp"

//---------------------------------------------------------------------------//
// Testing types
//---------------------------------------------------------------------------//
using boost::units::quantity;
using namespace Utility::Units;
namespace si = boost::units::si;
namespace cgs = boost::units::cgs;

typedef TestArchiveHelper::TestArchives TestArchives;

typedef std::tuple<
  std::tuple<si::energy,si::amount,cgs::energy,si::amount>,
  std::tuple<cgs::energy,si::amount,si::energy,si::amount>,
  std::tuple<si::energy,si::length,cgs::energy,cgs::length>,
  std::tuple<cgs::energy,cgs::length,si::energy,si::length>,
  std::tuple<si::energy,si::mass,cgs::energy,cgs::mass>,
  std::tuple<cgs::energy,cgs::mass,si::energy,si::mass>,
  std::tuple<si::energy,si::dimensionless,cgs::energy,cgs::dimensionless>,
  std::tuple<cgs::energy,cgs::dimensionless,si::energy,si::dimensionless>,
  std::tuple<cgs::energy,void*,si::energy,void*>,
  std::tuple<ElectronVolt,si::amount,si::energy,si::amount>,
  std::tuple<ElectronVolt,si::amount,cgs::energy,si::amount>,
  std::tuple<ElectronVolt,si::amount,KiloElectronVolt,si::amount>,
  std::tuple<ElectronVolt,si::amount,MegaElectronVolt,si::amount>,
  std::tuple<KiloElectronVolt,si::amount,si::energy,si::amount>,
  std::tuple<KiloElectronVolt,si::amount,cgs::energy,si::amount>,
  std::tuple<KiloElectronVolt,si::amount,ElectronVolt,si::amount>,
  std::tuple<KiloElectronVolt,si::amount,MegaElectronVolt,si::amount>,
  std::tuple<MegaElectronVolt,si::amount,si::energy,si::amount>,
  std::tuple<MegaElectronVolt,si::amount,cgs::energy,si::amount>,
  std::tuple<MegaElectronVolt,si::amount,ElectronVolt,si::amount>,
  std::tuple<MegaElectronVolt,si::amount,KiloElectronVolt,si::amount>,
  std::tuple<void*,MegaElectronVolt,void*,KiloElectronVolt>
  > TestUnitTypeQuads;

//---------------------------------------------------------------------------//
// Testing Variables
//---------------------------------------------------------------------------//

std::shared_ptr<Utility::TabularUnivariateDistribution>
  tab_distribution( new Utility::DeltaDistribution );

std::shared_ptr<Utility::UnivariateDistribution>
  distribution( tab_distribution );

std::shared_ptr<Utility::UnitAwareTabularUnivariateDistribution<si::time,si::length> >
  unit_aware_tab_distribution( new Utility::UnitAwareDeltaDistribution<si::time,si::length>( 3.0*si::seconds ) );

std::shared_ptr<Utility::UnitAwareUnivariateDistribution<si::time,si::length> >
  unit_aware_distribution( unit_aware_tab_distribution );

//---------------------------------------------------------------------------//
// Tests.
//---------------------------------------------------------------------------//
// Check that the distribution can be evaluated
FRENSIE_UNIT_TEST( DeltaDistribution, evaluate )
{
  FRENSIE_CHECK_EQUAL( distribution->evaluate( 1.0 ), 0.0 );
  FRENSIE_CHECK_EQUAL( distribution->evaluate( -1.0 ), 0.0 );
  FRENSIE_CHECK_EQUAL( distribution->evaluate( 0.0 ), 1.0 );
}

//---------------------------------------------------------------------------//
// Check that the unit-aware distribution can be evaluated
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, evaluate )
{
  FRENSIE_CHECK_EQUAL( unit_aware_distribution->evaluate( 0.0*si::seconds ),
		       0.0*si::meters );
  FRENSIE_CHECK_EQUAL( unit_aware_distribution->evaluate( 3.0*si::seconds ),
		       1.0*si::meters );
  FRENSIE_CHECK_EQUAL( unit_aware_distribution->evaluate( 6.0*si::seconds ),
		       0.0*si::meters );
}

//---------------------------------------------------------------------------//
// Check that the PDF can be evaluated
FRENSIE_UNIT_TEST( DeltaDistribution, evaluatePDF )
{
  FRENSIE_CHECK_EQUAL( distribution->evaluatePDF( 1.0 ), 0.0 );
  FRENSIE_CHECK_EQUAL( distribution->evaluatePDF( -1.0 ), 0.0 );
  FRENSIE_CHECK_EQUAL( distribution->evaluatePDF( 0.0 ), 1.0 );
}

//---------------------------------------------------------------------------//
// Check that the unit-aware PDF can be evaluated
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, evaluatePDF )
{
  FRENSIE_CHECK_EQUAL( unit_aware_distribution->evaluatePDF( 0.0*si::seconds ),
		       0.0/si::seconds );
  FRENSIE_CHECK_EQUAL( unit_aware_distribution->evaluatePDF( 3.0*si::seconds ),
		       1.0/si::seconds );
  FRENSIE_CHECK_EQUAL( unit_aware_distribution->evaluatePDF( 6.0*si::seconds ),
		       0.0/si::seconds );
}

//---------------------------------------------------------------------------//
// Check that the CDF can be evaluated
FRENSIE_UNIT_TEST( DeltaDistribution, evaluateCDF )
{
  FRENSIE_CHECK_EQUAL( tab_distribution->evaluateCDF( -1.0 ), 0.0 );
  FRENSIE_CHECK_EQUAL( tab_distribution->evaluateCDF( 0.0 ), 1.0 );
  FRENSIE_CHECK_EQUAL( tab_distribution->evaluateCDF( 1.0 ), 1.0 );
}

//---------------------------------------------------------------------------//
// Check that the unit-aware CDF can be evaluated
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, evaluateCDF )
{
  FRENSIE_CHECK_EQUAL(
		   unit_aware_tab_distribution->evaluateCDF( 0.0*si::seconds ),
		   0.0 );
  FRENSIE_CHECK_EQUAL(
		   unit_aware_tab_distribution->evaluateCDF( 3.0*si::seconds ),
		   1.0 );
  FRENSIE_CHECK_EQUAL(
		   unit_aware_tab_distribution->evaluateCDF( 6.0*si::seconds ),
		   1.0 );
}

//---------------------------------------------------------------------------//
// Check that the distribution can be sampled
FRENSIE_UNIT_TEST( DeltaDistribution, sample )
{
  FRENSIE_CHECK_EQUAL( distribution->sample(), 0.0 );
}

//---------------------------------------------------------------------------//
// Check that the unit-aware distribution can be sampled
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, sample )
{
  FRENSIE_CHECK_EQUAL( unit_aware_distribution->sample(), 3.0*si::seconds );
}

//---------------------------------------------------------------------------//
// Check that the distribution can be sampled
FRENSIE_UNIT_TEST( DeltaDistribution, sampleAndRecordTrials )
{
  Utility::DistributionTraits::Counter trials = 0;
  double sample = distribution->sampleAndRecordTrials( trials );

  FRENSIE_CHECK_EQUAL( sample, 0.0 );
  FRENSIE_CHECK_EQUAL( trials, 1 );
}

//---------------------------------------------------------------------------//
// Check that the unit-aware distribution can be sampled
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, sampleAndRecordTrials )
{
  Utility::DistributionTraits::Counter trials = 0;

  quantity<si::time> sample =
    unit_aware_distribution->sampleAndRecordTrials( trials );

  FRENSIE_CHECK_EQUAL( sample, 3.0*si::seconds );
  FRENSIE_CHECK_EQUAL( trials, 1 );
}

//---------------------------------------------------------------------------//
// Check that the distribution can be sampled
FRENSIE_UNIT_TEST( DeltaDistribution, sampleAndRecordBinIndex )
{
  size_t bin_index = 0;
  double sample = tab_distribution->sampleAndRecordBinIndex( bin_index );

  FRENSIE_CHECK_EQUAL( sample, 0.0 );
  FRENSIE_CHECK_EQUAL( bin_index, 0 );
}

//---------------------------------------------------------------------------//
// Check that the unit-aware distribution can be sampled
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, sampleAndRecordBinIndex )
{
  size_t bin_index = 0;

  quantity<si::time> sample =
    unit_aware_tab_distribution->sampleAndRecordBinIndex( bin_index );

  FRENSIE_CHECK_EQUAL( sample, 3.0*si::seconds );
  FRENSIE_CHECK_EQUAL( bin_index, 0 );
}

//---------------------------------------------------------------------------//
// Check that the distribution can be sampled
FRENSIE_UNIT_TEST( DeltaDistribution, sampleWithRandomNumber )
{
  double sample = tab_distribution->sampleWithRandomNumber( 0.0 );

  FRENSIE_CHECK_EQUAL( sample, 0.0 );

  sample = tab_distribution->sampleWithRandomNumber( 0.5 );

  FRENSIE_CHECK_EQUAL( sample, 0.0 );
}

//---------------------------------------------------------------------------//
// Check that the unit-aware distribution can be sampled
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, sampleWithRandomNumber )
{
  quantity<si::time> sample =
    unit_aware_tab_distribution->sampleWithRandomNumber( 0.0 );

  FRENSIE_CHECK_EQUAL( sample, 3.0*si::seconds );

  sample = unit_aware_tab_distribution->sampleWithRandomNumber( 0.5 );

  FRENSIE_CHECK_EQUAL( sample, 3.0*si::seconds );
}

//---------------------------------------------------------------------------//
// Check that the distribution can be sampled
FRENSIE_UNIT_TEST( DeltaDistribution, sampleInSubrange )
{
  double sample = tab_distribution->sampleInSubrange( 1.0 );

  FRENSIE_CHECK_EQUAL( sample, 0.0 );

  sample = tab_distribution->sampleInSubrange( 0.1 );

  FRENSIE_CHECK_EQUAL( sample, 0.0 );
}

//---------------------------------------------------------------------------//
// Check that the unit-aware distribution can be sampled
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, sampleInSubrange )
{
  quantity<si::time> sample =
    unit_aware_tab_distribution->sampleInSubrange( 4.0*si::seconds );

  FRENSIE_CHECK_EQUAL( sample, 3.0*si::seconds );

  sample = unit_aware_tab_distribution->sampleInSubrange( 3.1*si::seconds );

  FRENSIE_CHECK_EQUAL( sample, 3.0*si::seconds );
}

//---------------------------------------------------------------------------//
// Check that the distribution can be sampled
FRENSIE_UNIT_TEST( DeltaDistribution, sampleWithRandomNumberInSubrange )
{
  double sample =
    tab_distribution->sampleWithRandomNumberInSubrange( 0.0, 1.0 );

  FRENSIE_CHECK_EQUAL( sample, 0.0 );

  sample = tab_distribution->sampleWithRandomNumberInSubrange( 0.5, 2.0 );

  FRENSIE_CHECK_EQUAL( sample, 0.0 );
}

//---------------------------------------------------------------------------//
// Check that the unit-aware distribution can be sampled
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution,
		   sampleWithRandomNumberInSubrange )
{
  quantity<si::time> sample =
    unit_aware_tab_distribution->sampleWithRandomNumberInSubrange( 0.0, 4.0*si::seconds );

  FRENSIE_CHECK_EQUAL( sample, 3.0*si::seconds );

  sample = unit_aware_tab_distribution->sampleWithRandomNumberInSubrange( 0.5, 3.1*si::seconds );

  FRENSIE_CHECK_EQUAL( sample, 3.0*si::seconds );
}

//---------------------------------------------------------------------------//
// Check that the upper bound of the distribution independent variable can
// be returned
FRENSIE_UNIT_TEST( DeltaDistribution, getUpperBoundOfIndepVar )
{
  FRENSIE_CHECK_EQUAL( distribution->getUpperBoundOfIndepVar(), 0.0 );
}

//---------------------------------------------------------------------------//
// Check that the upper bound of the unit-aware distribution independent
// variable can be returned
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, getUpperBoundOfIndepVar )
{
  FRENSIE_CHECK_EQUAL( unit_aware_distribution->getUpperBoundOfIndepVar(),
		       3.0*si::seconds );
}

//---------------------------------------------------------------------------//
// Check that the lower bound of the distribution independent variable can
// be returned
FRENSIE_UNIT_TEST( DeltaDistribution, getLowerBoundOfIndepVar )
{
  FRENSIE_CHECK_EQUAL( distribution->getLowerBoundOfIndepVar(), 0.0 );
}

//---------------------------------------------------------------------------//
// Check that the lower bound of the unit-aware distribution independent
// variable can be returned
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, getLowerBoundOfIndepVar )
{
  FRENSIE_CHECK_EQUAL( unit_aware_distribution->getLowerBoundOfIndepVar(),
		       3.0*si::seconds );
}

//---------------------------------------------------------------------------//
// Check that the distribution type can be returned
FRENSIE_UNIT_TEST( DeltaDistribution, getDistributionType )
{
  FRENSIE_CHECK_EQUAL( distribution->getDistributionType(),
		       Utility::DELTA_DISTRIBUTION );
}

//---------------------------------------------------------------------------//
// Check that the unit-aware distribution type can be returned
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, getDistributionType )
{
  FRENSIE_CHECK_EQUAL( unit_aware_distribution->getDistributionType(),
		       Utility::DELTA_DISTRIBUTION );
}

//---------------------------------------------------------------------------//
// Check if the distribution is tabular
FRENSIE_UNIT_TEST( DeltaDistribution, isTabular )
{
  FRENSIE_CHECK( distribution->isTabular() );
}

//---------------------------------------------------------------------------//
// Check if the distribution is tabular
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, isTabular )
{
  FRENSIE_CHECK( unit_aware_distribution->isTabular() );
}

//---------------------------------------------------------------------------//
// Check if the distribution is continuous
FRENSIE_UNIT_TEST( DeltaDistribution, isContinuous )
{
  FRENSIE_CHECK( !distribution->isContinuous() );
}

//---------------------------------------------------------------------------//
// Check if the distribution is continuous
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, isContinuous )
{
  FRENSIE_CHECK( !unit_aware_distribution->isContinuous() );
}

//---------------------------------------------------------------------------//
// Check if the distribution is compatible with the interpolation type
FRENSIE_UNIT_TEST( DeltaDistribution, isCompatibleWithInterpType )
{
  FRENSIE_CHECK( !distribution->isCompatibleWithInterpType<Utility::LinLin>() );
  FRENSIE_CHECK( !distribution->isCompatibleWithInterpType<Utility::LinLog>() );
  FRENSIE_CHECK( !distribution->isCompatibleWithInterpType<Utility::LogLin>() );
  FRENSIE_CHECK( !distribution->isCompatibleWithInterpType<Utility::LogLog>() );
}

//---------------------------------------------------------------------------//
// Check if the distribution is compatible with the interpolation type
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, isCompatibleWithInterpType )
{
  FRENSIE_CHECK( !unit_aware_distribution->isCompatibleWithInterpType<Utility::LinLin>() );
  FRENSIE_CHECK( !unit_aware_distribution->isCompatibleWithInterpType<Utility::LinLog>() );
  FRENSIE_CHECK( !unit_aware_distribution->isCompatibleWithInterpType<Utility::LogLin>() );
  FRENSIE_CHECK( !unit_aware_distribution->isCompatibleWithInterpType<Utility::LogLog>() );
}

//---------------------------------------------------------------------------//
// Check that the distribution can be placed in a stream
FRENSIE_UNIT_TEST( DeltaDistribution, ostream_operator )
{
  std::ostringstream oss;

  oss << Utility::DeltaDistribution();

  Utility::VariantMap dist_data =
    Utility::fromString<Utility::VariantMap>( oss.str() );

  FRENSIE_CHECK_EQUAL( dist_data["type"].toString(), "Delta Distribution" );
  FRENSIE_CHECK_EQUAL( dist_data["independent unit"].toString(), "void" );
  FRENSIE_CHECK_EQUAL( dist_data["dependent unit"].toString(), "void" );
  FRENSIE_CHECK_EQUAL( dist_data["location"].toDouble(), 0.0 );
  FRENSIE_CHECK_EQUAL( dist_data["multiplier"].toDouble(), 1.0 );

  oss.str( "" );
  oss.clear();

  oss << Utility::DeltaDistribution( 1.0 );

  dist_data = Utility::fromString<Utility::VariantMap>( oss.str() );

  FRENSIE_CHECK_EQUAL( dist_data["type"].toString(), "Delta Distribution" );
  FRENSIE_CHECK_EQUAL( dist_data["independent unit"].toString(), "void" );
  FRENSIE_CHECK_EQUAL( dist_data["dependent unit"].toString(), "void" );
  FRENSIE_CHECK_EQUAL( dist_data["location"].toDouble(), 1.0 );
  FRENSIE_CHECK_EQUAL( dist_data["multiplier"].toDouble(), 1.0 );

  oss.str( "" );
  oss.clear();

  oss << Utility::DeltaDistribution( 1.0, 2.0 );

  dist_data = Utility::fromString<Utility::VariantMap>( oss.str() );

  FRENSIE_CHECK_EQUAL( dist_data["type"].toString(), "Delta Distribution" );
  FRENSIE_CHECK_EQUAL( dist_data["independent unit"].toString(), "void" );
  FRENSIE_CHECK_EQUAL( dist_data["dependent unit"].toString(), "void" );
  FRENSIE_CHECK_EQUAL( dist_data["location"].toDouble(), 1.0 );
  FRENSIE_CHECK_EQUAL( dist_data["multiplier"].toDouble(), 2.0 );

  oss.str( "" );
  oss.clear();

  oss << *distribution;

  dist_data = Utility::fromString<Utility::VariantMap>( oss.str() );

  FRENSIE_CHECK_EQUAL( dist_data["type"].toString(), "Delta Distribution" );
  FRENSIE_CHECK_EQUAL( dist_data["independent unit"].toString(), "void" );
  FRENSIE_CHECK_EQUAL( dist_data["dependent unit"].toString(), "void" );
  FRENSIE_CHECK_EQUAL( dist_data["location"].toDouble(), 0.0 );
  FRENSIE_CHECK_EQUAL( dist_data["multiplier"].toDouble(), 1.0 );
}

//---------------------------------------------------------------------------//
// Check that the unit-aware distribution can be placed in a stream
FRENSIE_UNIT_TEST( UnitAwareDeltaDistribution, ostream_operator )
{
  std::ostringstream oss;

  oss << Utility::UnitAwareDeltaDistribution<si::time,si::length>();

  Utility::VariantMap dist_data =
    Utility::fromString<Utility::VariantMap>( oss.str() );

  FRENSIE_CHECK_EQUAL( dist_data["type"].toString(), "Delta Distribution" );
  FRENSIE_CHECK_EQUAL( dist_data["independent unit"].toString(),
                       Utility::UnitTraits<si::time>::name() );
  FRENSIE_CHECK_EQUAL( dist_data["dependent unit"].toString(),
                       Utility::UnitTraits<si::length>::name() );
  FRENSIE_CHECK_EQUAL( dist_data["location"].toType<quantity<si::time> >(),
                       0.0*si::seconds );
  FRENSIE_CHECK_EQUAL( dist_data["multiplier"].toType<quantity<si::length> >(),
                       1.0*si::meter );

  oss.str( "" );
  oss.clear();

  oss << Utility::UnitAwareDeltaDistribution<si::time,si::length>( 1.0*si::seconds );

  dist_data = Utility::fromString<Utility::VariantMap>( oss.str() );

  FRENSIE_CHECK_EQUAL( dist_data["type"].toString(), "Delta Distribution" );
  FRENSIE_CHECK_EQUAL( dist_data["independent unit"].toString(),
                       Utility::UnitTraits<si::time>::name() );
  FRENSIE_CHECK_EQUAL( dist_data["dependent unit"].toString(),
                       Utility::UnitTraits<si::length>::name() );
  FRENSIE_CHECK_EQUAL( dist_data["location"].toType<quantity<si::time> >(),
                       1.0*si::second );
  FRENSIE_CHECK_EQUAL( dist_data["multiplier"].toType<quantity<si::length> >(),
                       1.0*si::meter );

  oss.str( "" );
  oss.clear();

  oss << Utility::UnitAwareDeltaDistribution<si::time,si::length>( 1.0*si::seconds, 2.0*si::meters );

  dist_data = Utility::fromString<Utility::VariantMap>( oss.str() );

  FRENSIE_CHECK_EQUAL( dist_data["type"].toString(), "Delta Distribution" );
  FRENSIE_CHECK_EQUAL( dist_data["independent unit"].toString(),
                       Utility::UnitTraits<si::time>::name() );
  FRENSIE_CHECK_EQUAL( dist_data["dependent unit"].toString(),
                       Utility::UnitTraits<si::length>::name() );
  FRENSIE_CHECK_EQUAL( dist_data["location"].toType<quantity<si::time> >(),
                       1.0*si::second );
  FRENSIE_CHECK_EQUAL( dist_data["multiplier"].toType<quantity<si::length> >(),
                       2.0*si::meters );

  oss.str( "" );
  oss.clear();

  oss << *unit_aware_distribution;

  dist_data = Utility::fromString<Utility::VariantMap>( oss.str() );

  FRENSIE_CHECK_EQUAL( dist_data["type"].toString(), "Delta Distribution" );
  FRENSIE_CHECK_EQUAL( dist_data["independent unit"].toString(),
                       Utility::UnitTraits<si::time>::name() );
  FRENSIE_CHECK_EQUAL( dist_data["dependent unit"].toString(),
                       Utility::UnitTraits<si::length>::name() );
  FRENSIE_CHECK_EQUAL( dist_data["location"].toType<quantity<si::time> >(),
                       3.0*si::seconds );
  FRENSIE_CHECK_EQUAL( dist_data["multiplier"].toType<quantity<si::length> >(),
                       1.0*si::meter );
}

//---------------------------------------------------------------------------//
// Check that a distribution can be archived
FRENSIE_UNIT_TEST_TEMPLATE_EXPAND( DeltaDistribution, archive, TestArchives )
{
  FETCH_TEMPLATE_PARAM( 0, RawOArchive );
  FETCH_TEMPLATE_PARAM( 1, RawIArchive );

  typedef typename std::remove_pointer<RawOArchive>::type OArchive;
  typedef typename std::remove_pointer<RawIArchive>::type IArchive;

  std::string archive_base_name( "test_delta_dist" );
  std::ostringstream archive_ostream;

  // Create and archive some delta distributions
  {
    std::unique_ptr<OArchive> oarchive;

    createOArchive( archive_base_name, archive_ostream, oarchive );

    Utility::DeltaDistribution delta_dist_a( 0.0, 1.0 );

    std::shared_ptr<Utility::UnivariateDistribution> delta_dist_b( new Utility::DeltaDistribution( 1.0, 2.0 ) );

    FRENSIE_REQUIRE_NO_THROW( (*oarchive) << boost::serialization::make_nvp( "delta_dist_a", delta_dist_a ) );
    FRENSIE_REQUIRE_NO_THROW( (*oarchive) << boost::serialization::make_nvp( "delta_dist_b", delta_dist_b ) );
  }

  // Copy the archive ostream to an istream
  std::istringstream archive_istream( archive_ostream.str() );

  // Load the archived distributions
  std::unique_ptr<IArchive> iarchive;

  createIArchive( archive_istream, iarchive );

  Utility::DeltaDistribution delta_dist_a;

  FRENSIE_REQUIRE_NO_THROW( (*iarchive) >> boost::serialization::make_nvp( "delta_dist_a", delta_dist_a ) );
  FRENSIE_CHECK_EQUAL( delta_dist_a.getUpperBoundOfIndepVar(), 0.0 );
  FRENSIE_CHECK_EQUAL( delta_dist_a.getLowerBoundOfIndepVar(), 0.0 );
  FRENSIE_CHECK_EQUAL( delta_dist_a.evaluate( 0.0 ), 1.0 );

  std::shared_ptr<Utility::UnivariateDistribution> delta_dist_b;

  FRENSIE_REQUIRE_NO_THROW( (*iarchive) >> boost::serialization::make_nvp( "delta_dist_b", delta_dist_b ) );
  FRENSIE_CHECK_EQUAL( delta_dist_b->getUpperBoundOfIndepVar(), 1.0 );
  FRENSIE_CHECK_EQUAL( delta_dist_b->getLowerBoundOfIndepVar(), 1.0 );
  FRENSIE_CHECK_EQUAL( delta_dist_b->evaluate( 1.0 ), 2.0 );
}

//---------------------------------------------------------------------------//
// Check that a unit-aware distribution can be archived
FRENSIE_UNIT_TEST_TEMPLATE_EXPAND( UnitAwareDeltaDistribution,
                                   archive,
                                   TestArchives )
{
  FETCH_TEMPLATE_PARAM( 0, RawOArchive );
  FETCH_TEMPLATE_PARAM( 1, RawIArchive );

  typedef typename std::remove_pointer<RawOArchive>::type OArchive;
  typedef typename std::remove_pointer<RawIArchive>::type IArchive;

  std::string archive_base_name( "test_unit_aware_delta_dist" );
  std::ostringstream archive_ostream;

  // Create and archive some delta distributions
  {
    std::unique_ptr<OArchive> oarchive;

    createOArchive( archive_base_name, archive_ostream, oarchive );

    Utility::UnitAwareDeltaDistribution<si::time,si::length>
      delta_dist_a( 0.0*si::seconds, 1.0*si::meters );

    std::shared_ptr<Utility::UnitAwareUnivariateDistribution<si::time,si::length> > delta_dist_b( new Utility::UnitAwareDeltaDistribution<si::time,si::length>( 1.0*si::seconds, 2.0*si::meters ) );

    FRENSIE_REQUIRE_NO_THROW( (*oarchive) << boost::serialization::make_nvp( "delta_dist_a", delta_dist_a ) );
    FRENSIE_REQUIRE_NO_THROW( (*oarchive) << boost::serialization::make_nvp( "delta_dist_b", delta_dist_b ) );
  }

  // Copy the archive ostream to an istream
  std::istringstream archive_istream( archive_ostream.str() );

  // Load the archived distributions
  std::unique_ptr<IArchive> iarchive;

  createIArchive( archive_istream, iarchive );

  Utility::UnitAwareDeltaDistribution<si::time,si::length> delta_dist_a;

  FRENSIE_REQUIRE_NO_THROW( (*iarchive) >> boost::serialization::make_nvp( "delta_dist_a", delta_dist_a ) );
  FRENSIE_CHECK_EQUAL( delta_dist_a.getUpperBoundOfIndepVar(), 0.0*si::seconds );
  FRENSIE_CHECK_EQUAL( delta_dist_a.getLowerBoundOfIndepVar(), 0.0*si::seconds );
  FRENSIE_CHECK_EQUAL( delta_dist_a.evaluate( 0.0*si::seconds ), 1.0*si::meters );

  std::shared_ptr<Utility::UnitAwareUnivariateDistribution<si::time,si::length> >
    delta_dist_b;

  FRENSIE_REQUIRE_NO_THROW( (*iarchive) >> boost::serialization::make_nvp( "delta_dist_b", delta_dist_b ) );
  FRENSIE_CHECK_EQUAL( delta_dist_b->getUpperBoundOfIndepVar(), 1.0*si::seconds );
  FRENSIE_CHECK_EQUAL( delta_dist_b->getLowerBoundOfIndepVar(), 1.0*si::seconds );
  FRENSIE_CHECK_EQUAL( delta_dist_b->evaluate( 1.0*si::seconds ), 2.0*si::meters );
}

//---------------------------------------------------------------------------//
// Check that the distribution can be scaled
FRENSIE_UNIT_TEST_TEMPLATE_EXPAND( UnitAwareDeltaDistribution,
				   explicit_conversion,
                                   TestUnitTypeQuads )
{
  FETCH_TEMPLATE_PARAM( 0, RawIndepUnitA );
  FETCH_TEMPLATE_PARAM( 1, RawDepUnitA );
  FETCH_TEMPLATE_PARAM( 2, RawIndepUnitB );
  FETCH_TEMPLATE_PARAM( 3, RawDepUnitB );

  typedef typename std::remove_pointer<RawIndepUnitA>::type IndepUnitA;
  typedef typename std::remove_pointer<RawDepUnitA>::type DepUnitA;
  typedef typename std::remove_pointer<RawIndepUnitB>::type IndepUnitB;
  typedef typename std::remove_pointer<RawDepUnitB>::type DepUnitB;

  typedef typename Utility::UnitTraits<IndepUnitA>::template GetQuantityType<double>::type IndepQuantityA;
  typedef typename Utility::UnitTraits<typename Utility::UnitTraits<IndepUnitA>::InverseUnit>::template GetQuantityType<double>::type InverseIndepQuantityA;

  typedef typename Utility::UnitTraits<IndepUnitB>::template GetQuantityType<double>::type IndepQuantityB;
  typedef typename Utility::UnitTraits<typename Utility::UnitTraits<IndepUnitB>::InverseUnit>::template GetQuantityType<double>::type InverseIndepQuantityB;

  typedef typename Utility::UnitTraits<DepUnitA>::template GetQuantityType<double>::type DepQuantityA;
  typedef typename Utility::UnitTraits<DepUnitB>::template GetQuantityType<double>::type DepQuantityB;

  // Copy from unitless distribution to distribution type A
  Utility::UnitAwareDeltaDistribution<IndepUnitA,DepUnitA>
    unit_aware_dist_a_copy = Utility::UnitAwareDeltaDistribution<IndepUnitA,DepUnitA>::fromUnitlessDistribution( *dynamic_cast<Utility::DeltaDistribution*>( distribution.get() ) );

  // Copy from distribution type A to distribution type B
  Utility::UnitAwareDeltaDistribution<IndepUnitB,DepUnitB>
    unit_aware_dist_b_copy( unit_aware_dist_a_copy );

  IndepQuantityA indep_quantity_a =
    Utility::QuantityTraits<IndepQuantityA>::initializeQuantity( 0.0 );
  InverseIndepQuantityA inv_indep_quantity_a =
    Utility::QuantityTraits<InverseIndepQuantityA>::initializeQuantity( 1.0 );
  DepQuantityA dep_quantity_a =
    Utility::QuantityTraits<DepQuantityA>::initializeQuantity( 1.0 );

  IndepQuantityB indep_quantity_b( indep_quantity_a );
  InverseIndepQuantityB inv_indep_quantity_b =
    Utility::QuantityTraits<InverseIndepQuantityB>::initializeQuantity( 1.0 );
  DepQuantityB dep_quantity_b( dep_quantity_a );

  FRENSIE_CHECK_FLOATING_EQUALITY(
			   unit_aware_dist_a_copy.evaluate( indep_quantity_a ),
			   dep_quantity_a,
			   1e-15 );
  FRENSIE_CHECK_FLOATING_EQUALITY(
			unit_aware_dist_a_copy.evaluatePDF( indep_quantity_a ),
			inv_indep_quantity_a,
			1e-15 );
  FRENSIE_CHECK_FLOATING_EQUALITY(
			   unit_aware_dist_b_copy.evaluate( indep_quantity_b ),
			   dep_quantity_b,
			   1e-15 );
  FRENSIE_CHECK_FLOATING_EQUALITY(
			unit_aware_dist_b_copy.evaluatePDF( indep_quantity_b ),
			inv_indep_quantity_b,
			1e-15 );

  Utility::setQuantity( indep_quantity_a, 1.0 );
  Utility::setQuantity( inv_indep_quantity_a, 0.0 );
  Utility::setQuantity( dep_quantity_a, 0.0 );

  indep_quantity_b = IndepQuantityB( indep_quantity_a );
  Utility::setQuantity( inv_indep_quantity_b, 0.0 );
  dep_quantity_b = DepQuantityB( dep_quantity_a );

  FRENSIE_CHECK_FLOATING_EQUALITY(
			   unit_aware_dist_a_copy.evaluate( indep_quantity_a ),
			   dep_quantity_a,
			   1e-15 );
  FRENSIE_CHECK_FLOATING_EQUALITY(
			unit_aware_dist_a_copy.evaluatePDF( indep_quantity_a ),
			inv_indep_quantity_a,
			1e-6 );
  FRENSIE_CHECK_FLOATING_EQUALITY(
			   unit_aware_dist_b_copy.evaluate( indep_quantity_b ),
			   dep_quantity_b,
			   1e-15 );
  FRENSIE_CHECK_FLOATING_EQUALITY(
			unit_aware_dist_b_copy.evaluatePDF( indep_quantity_b ),
			inv_indep_quantity_b,
			1e-6 );
}

//---------------------------------------------------------------------------//
// end tstDeltaDistribution.cpp
//---------------------------------------------------------------------------//


// This file is part of OpenMVG, an Open Multiple View Geometry C++ library.

// Copyright (c) 2012, 2013 Pierre MOULON.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef OPENMVG_NUMERIC_LM_HPP
#define OPENMVG_NUMERIC_LM_HPP

// Levenberg Marquardt Non Linear Optimization
#include <Eigen/Core>

namespace openMVG
{
using namespace Eigen;

/**
 * @brief Generic functor Levenberg-Marquardt minimization
 * @tparam _Scalar Type of internal computation
 * @tparam NX Number of values per sample at compile time
 * @tparam NY Number of samples at compile time
 */
template<typename _Scalar, int NX = Dynamic, int NY = Dynamic>
struct Functor
{
  using Scalar = _Scalar;
  enum
  {
    InputsAtCompileTime = NX,
    ValuesAtCompileTime = NY
  };
  using InputType = Matrix<Scalar, InputsAtCompileTime, 1>;
  using ValueType = Matrix<Scalar, ValuesAtCompileTime, 1>;
  using JacobianType = Matrix<Scalar, ValuesAtCompileTime, InputsAtCompileTime>;

  /// Number of values per sample
  const int m_inputs;

  // Number of sample
  const int m_values;

  /**
  * @brief Default constructor
  */
  Functor()
    : m_inputs( InputsAtCompileTime ),
      m_values( ValuesAtCompileTime )
  {

  }

  /**
  * @brief Constructor
  * @param inputs Number of column per sample
  * @param values Number of sample
  */
  Functor( int inputs, int values ) : m_inputs( inputs ), m_values( values ) {}

  /**
  * @brief Get number of samples
  * @return Number of samples
  */
  int inputs() const
  {
    return m_inputs;
  }

  /**
  * @brief Get number of samples
  * @return Number of samples
  */
  int values() const
  {
    return m_values;
  }

  // you should define that in the subclass :
  //  void operator() (const InputType& x, ValueType* v, JacobianType* _j=0) const;
};

}; // namespace openMVG

#endif // OPENMVG_NUMERIC_LM_HPP


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2008-2012 Barend Gehrels, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_PROJECTIONS_PARAMETERS_HPP
#define BOOST_GEOMETRY_PROJECTIONS_PARAMETERS_HPP

#include <string>
#include <vector>

#include <boost/geometry/extensions/gis/projections/impl/pj_init.hpp>
#include <boost/geometry/extensions/gis/projections/impl/projects.hpp>

namespace boost { namespace geometry { namespace projections {

template <typename R>
inline parameters init(const R& arguments)
{
    return detail::pj_init(arguments);
}

/*!
\ingroup projection
\brief Initializes a projection as a string, using the format with + and =
\details The projection can be initialized with a string (with the same format as the PROJ4 package) for
  convenient initialization from, for example, the command line
\par Example
    <tt>+proj=labrd +ellps=intl +lon_0=46d26'13.95E +lat_0=18d54S +azi=18d54 +k_0=.9995 +x_0=400000 +y_0=800000</tt>
    for the Madagascar projection.
\note Parameters are described in the group
*/
inline parameters init(const std::string& arguments)
{
    return detail::pj_init_plus(arguments);
}

/*!
\ingroup projection
\brief Overload using a const char*
*/
inline parameters init(const char* arguments)
{
    return detail::pj_init_plus(arguments);
}

// todo:
/*
parameters init(const std::map<std::string, std::string>& arguments)
{
    return detail::pj_init_plus(arguments);
}
*/

}}} // namespace boost::geometry::projections
#endif


/*
 * Copyright (C) 2015, Nils Moehrle
 * TU Darmstadt - Graphics, Capture and Massively Parallel Computing
 * All rights reserved.
 *
 * This software may be modified and distributed under the terms
 * of the BSD 3-Clause license. See the LICENSE.txt file for details.
 */

#include <set>
#include <list>

#include <util/timer.h>
#include <mve/image_tools.h>
#include <Eigen/SparseCore>
#include <Eigen/SparseLU>

#include "texturing.h"

TEX_NAMESPACE_BEGIN

void merge_vertex_projection_infos(std::vector<std::vector<VertexProjectionInfo> > * vertex_projection_infos) {
    /* Merge vertex infos within the same texture patch. */
    #pragma omp parallel for
    for (std::size_t i = 0; i < vertex_projection_infos->size(); ++i) {

        // vertex projection infomation of each vertex
        std::vector<VertexProjectionInfo> & infos = vertex_projection_infos->at(i);

        std::map<std::size_t, VertexProjectionInfo> info_map;
        std::map<std::size_t, VertexProjectionInfo>::iterator it;

        for (VertexProjectionInfo const & info : infos) {
            std::size_t texture_patch_id = info.texture_patch_id;
            if((it = info_map.find(texture_patch_id)) == info_map.end()) {
                info_map[texture_patch_id] = info;
            } else {
                it->second.faces.insert(it->second.faces.end(),
                    info.faces.begin(), info.faces.end());
            }
        }

        infos.clear();
        infos.reserve(info_map.size());
        for (it = info_map.begin(); it != info_map.end(); ++it) {
            infos.push_back(it->second);
        }
    }
}

/** Struct representing a TexturePatch candidate - final texture patches are obtained by merging candiates. */
struct TexturePatchCandidate {
    Rect<int> bounding_box;
    TexturePatch::Ptr texture_patch;
};

/** Create a TexturePatchCandidate by calculating
  * the faces' bounding box projected into the view,
  *  relative texture coordinates
  *  and extacting the texture views relevant part
  */
TexturePatchCandidate
generate_candidate(int label, TextureView const & texture_view,
    std::vector<std::size_t> const & faces, mve::TriangleMesh::ConstPtr mesh) {

    // view image
    mve::ByteImage::Ptr view_image = texture_view.get_image();
    int min_x = view_image->width(), min_y = view_image->height();
    int max_x = 0, max_y = 0;

    // all the faces in the mesh
    mve::TriangleMesh::FaceList const & mesh_faces = mesh->get_faces();
    // all the vertices in the mesh
    mve::TriangleMesh::VertexList const & vertices = mesh->get_vertices();

    // compute texture coordinates
    std::vector<math::Vec2f> texcoords;
    for (std::size_t i = 0; i < faces.size(); ++i) {
        for (std::size_t j = 0; j < 3; ++j) {
            math::Vec3f vertex = vertices[mesh_faces[faces[i] * 3 + j]];
            math::Vec2f pixel = texture_view.get_pixel_coords(vertex);

            texcoords.push_back(pixel);

            min_x = std::min(static_cast<int>(std::floor(pixel[0])) - texture_patch_border, min_x);
            min_y = std::min(static_cast<int>(std::floor(pixel[1])) - texture_patch_border, min_y);
            max_x = std::max(static_cast<int>(std::ceil(pixel[0])) + texture_patch_border, max_x);
            max_y = std::max(static_cast<int>(std::ceil(pixel[1])) + texture_patch_border, max_y);
        }
    }

    /* Calculate the relative texcoords. */
    math::Vec2f min(min_x, min_y);
    for (std::size_t i = 0; i < texcoords.size(); ++i) {
        texcoords[i] = texcoords[i] - min;
    }

    int const width = max_x - min_x;
    int const height = max_y - min_y;

    /* Check for valid projections/erroneous labeling files. */
    assert(min_x >= 0 - texture_patch_border);
    assert(min_y >= 0 - texture_patch_border);
    assert(max_x < view_image->width() + texture_patch_border);
    assert(max_y < view_image->height() + texture_patch_border);

    mve::ByteImage::Ptr image;
    image = mve::image::crop(view_image, width, height, min_x, min_y, *math::Vec3uc(255, 0, 255));

    // gamma corrections of an image
    mve::image::gamma_correct(image, 2.2f);

    TexturePatchCandidate texture_patch_candidate =
        {Rect<int>(min_x, min_y, max_x, max_y),
            TexturePatch::create(label, faces, texcoords, image)};
    return texture_patch_candidate;
}

void
generate_texture_patches(UniGraph const & graph, mve::TriangleMesh::ConstPtr mesh,
    mve::VertexInfoList::ConstPtr vertex_infos,
    std::vector<TextureView> * texture_views,
    std::vector<std::vector<VertexProjectionInfo> > * vertex_projection_infos,
    std::vector<TexturePatch::Ptr> * texture_patches) {

    util::WallTimer timer;

    // Face List
    mve::TriangleMesh::FaceList const & mesh_faces = mesh->get_faces();
    // Vertices
    mve::TriangleMesh::VertexList const & vertices = mesh->get_vertices();
    // Projection Infomation
    vertex_projection_infos->resize(vertices.size());

    //==============================generate patches ================================================//
    std::size_t num_patches = 0;
    std::cout << "\tRunning... " << std::flush;
    #pragma omp parallel for schedule(dynamic)
    for (std::size_t i = 0; i < texture_views->size(); ++i) {

        // get all the patches that are seedn from view i+1
        std::vector<std::vector<std::size_t> > subgraphs;
        int const label = i + 1;
        graph.get_subgraphs(label, &subgraphs);

        // get the texture view and related image
        TextureView * texture_view = &texture_views->at(i);
        texture_view->load_image();

        // each subgraph is a patch candidate
        std::list<TexturePatchCandidate> candidates;
        for (std::size_t j = 0; j < subgraphs.size(); ++j) {
            candidates.push_back(generate_candidate(label, *texture_view, subgraphs[j], mesh));
        }
        texture_view->release_image();

        /* Merge candidates which contain the same image content. */
        std::list<TexturePatchCandidate>::iterator it, sit;
        for (it = candidates.begin(); it != candidates.end(); ++it) {
            for (sit = candidates.begin(); sit != candidates.end();) {
                Rect<int> bounding_box = sit->bounding_box;
                if (it != sit && bounding_box.is_inside(&it->bounding_box)) {
                    TexturePatch::Faces & faces = it->texture_patch->get_faces();
                    TexturePatch::Faces & ofaces = sit->texture_patch->get_faces();
                    faces.insert(faces.end(), ofaces.begin(), ofaces.end());

                    TexturePatch::Texcoords & texcoords = it->texture_patch->get_texcoords();
                    TexturePatch::Texcoords & otexcoords = sit->texture_patch->get_texcoords();
                    math::Vec2f offset;
                    offset[0] = sit->bounding_box.min_x - it->bounding_box.min_x;
                    offset[1] = sit->bounding_box.min_y - it->bounding_box.min_y;
                    for (std::size_t i = 0; i < otexcoords.size(); ++i) {
                        texcoords.push_back(otexcoords[i] + offset);
                    }

                    sit = candidates.erase(sit);
                } else {
                    ++sit;
                }
            }
        }

        it = candidates.begin();
        for (; it != candidates.end(); ++it) {
            std::size_t texture_patch_id;

            #pragma omp critical
            {
                texture_patches->push_back(it->texture_patch);
                texture_patch_id = num_patches++;
            }

            std::vector<std::size_t> const & faces = it->texture_patch->get_faces();
            std::vector<math::Vec2f> const & texcoords = it->texture_patch->get_texcoords();
            for (std::size_t i = 0; i < faces.size(); ++i) {
                std::size_t const face_id = faces[i];
                std::size_t const face_pos = face_id * 3;
                for (std::size_t j = 0; j < 3; ++j) {
                    std::size_t const vertex_id = mesh_faces[face_pos  + j];
                    math::Vec2f const projection = texcoords[i * 3 + j];

                    VertexProjectionInfo info = {texture_patch_id, projection, {face_id}};

                    #pragma omp critical
                    vertex_projection_infos->at(vertex_id).push_back(info);
                }
            }
        }
    }

    //  merge vertex projection information
    merge_vertex_projection_infos(vertex_projection_infos);

    //================================= process (fill) holes ============================================//
    std::size_t num_holes = 0;
    std::size_t num_hole_faces = 0;

    //if (!settings.skip_hole_filling) {
    {
        std::vector<std::vector<std::size_t> > subgraphs;
        graph.get_subgraphs(0, &subgraphs);

        #pragma omp parallel for schedule(dynamic)
        for (std::size_t i = 0; i < subgraphs.size(); ++i) {

            // for each subgraph  // each subgraph is a hole????
            std::vector<std::size_t> const & subgraph = subgraphs[i];

            // neighboring facet of each vertex
            std::map<std::size_t, std::set<std::size_t> > tmp;
            for (std::size_t const face_id : subgraph) {
                std::size_t const v0 = mesh_faces[face_id * 3];
                std::size_t const v1 = mesh_faces[face_id * 3 + 1];
                std::size_t const v2 = mesh_faces[face_id * 3 + 2];
                tmp[v0].insert(face_id);
                tmp[v1].insert(face_id);
                tmp[v2].insert(face_id);
            }

            std::size_t const num_vertices = tmp.size();
            /* Only fill small holes. */
            if (num_vertices > 100) {
                //std::cerr << "Hole to large" << std::endl;
                continue;
            }

            /* Calculate 2D parameterization using the technique from libremesh/patch2d,
             * which was published as sourcecode accompanying the following paper:
             *
             * Isotropic Surface Remeshing
             * Simon Fuhrmann, Jens Ackermann, Thomas Kalbe, Michael Goesele
             */

            std::size_t seed = -1;
            std::vector<bool> is_border(num_vertices, false);
            std::vector<std::vector<std::size_t> > adj_verts_via_border(num_vertices);
            /* Index structures to map from local <-> global vertex id. */
            std::map<std::size_t, std::size_t> g2l;
            std::vector<std::size_t> l2g(num_vertices);
            /* Index structure to determine column in matrix/vector. */
            std::vector<std::size_t> idx(num_vertices);
            std::size_t num_border_vertices = 0;

            bool disk_topology = true;
            std::map<std::size_t, std::set<std::size_t> >::iterator it = tmp.begin();
            for (std::size_t j = 0; j < num_vertices; ++j, ++it) {
                // index of the vertex
                std::size_t vertex_id = it->first;
                g2l[vertex_id] = j;
                l2g[j] = vertex_id;

                /* Check topology in original mesh. */
                if (vertex_infos->at(vertex_id).vclass != mve::VERTEX_CLASS_SIMPLE) {
                    //std::cerr << "Complex/Border vertex in original mesh" << std::endl;
                    disk_topology = false;
                    break;
                }

                /* Check new topology and determine if vertex is now at the border. */
                std::vector<std::size_t> const & adj_faces = vertex_infos->at(vertex_id).faces;
                std::set<std::size_t> const & adj_hole_faces = it->second;
                std::vector<std::pair<std::size_t, std::size_t> > fan;

                for (std::size_t k = 0; k < adj_faces.size(); ++k) {
                    std::size_t adj_face = adj_faces[k];
                    if (graph.get_label(adj_faces[k]) == 0 && adj_hole_faces.find(adj_face) != adj_hole_faces.end()) {
                        std::size_t curr = adj_faces[k];
                        std::size_t next = adj_faces[(k + 1) % adj_faces.size()];
                        std::pair<std::size_t, std::size_t> pair(curr, next);
                        fan.push_back(pair);
                    }
                }

                std::size_t gaps = 0;
                for (std::size_t k = 0; k < fan.size(); k++) {
                    std::size_t curr = fan[k].first;
                    std::size_t next = fan[(k + 1) % fan.size()].first;
                    if (fan[k].second != next) {
                        ++gaps;

                        for (std::size_t l = 0; l < 3; ++l) {
                            if(mesh_faces[curr * 3 + l] == vertex_id) {
                                std::size_t second = mesh_faces[curr * 3 + (l + 2) % 3];
                                adj_verts_via_border[j].push_back(second);
                            }
                            if(mesh_faces[next * 3 + l] == vertex_id) {
                                std::size_t first = mesh_faces[next * 3 + (l + 1) % 3];
                                adj_verts_via_border[j].push_back(first);
                            }
                        }
                    }
                }

                is_border[j] = gaps == 1;

                /* Check if vertex is now complex. */
                if (gaps > 1) {
                    //std::cerr << "Complex vertex in hole" << std::endl;
                    disk_topology = false;
                    break;
                }

                if (is_border[j]) {
                    idx[j] = num_border_vertices++;
                    seed = vertex_id;
                } else {
                    idx[j] = j - num_border_vertices;
                }
            }
            tmp.clear();

            if (!disk_topology) continue;
            if (num_border_vertices == 0) {
                //std::cerr << "Genus zero topology" << std::endl;
                continue;
            }

            //===================== get the border of this subgraph =====================================//
            std::vector<std::size_t> border;
            border.reserve(num_border_vertices);
            std::size_t prev = seed;
            std::size_t curr = seed;
            while (prev == seed || curr != seed) {
                std::size_t next = std::numeric_limits<std::size_t>::max();
                std::vector<std::size_t> const & adj_verts = adj_verts_via_border[g2l[curr]];
                for (std::size_t adj_vert : adj_verts) {
                    assert(is_border[g2l[adj_vert]]);
                    if (adj_vert != prev && adj_vert != curr) {
                        next = adj_vert;
                        break;
                    }
                }
                if (next != std::numeric_limits<std::size_t>::max()) {
                    prev = curr;
                    curr = next;
                    border.push_back(next);
                } else {
                    //std::cerr << "No new border vertex" << std::endl;
                    border.clear();
                    break;
                }

                if (border.size() > num_border_vertices) {
                    //std::cerr << "Loop within border" << std::endl;
                    break;
                }
            }

            if (border.size() != num_border_vertices) {
                continue;
            }

            //============================compute the coordinates of the vertices of holes=========================//
            // compute total length and total projection length
            float total_length = 0.0f;
            float total_projection_length = 0.0f;
            for (std::size_t j = 0; j < border.size(); ++j) {
                std::size_t vi0 = border[j];
                std::size_t vi1 = border[(j + 1) % border.size()];
                std::vector<VertexProjectionInfo> const & vpi0 = vertex_projection_infos->at(vi0);
                std::vector<VertexProjectionInfo> const & vpi1 = vertex_projection_infos->at(vi0);
                /* According to the previous checks (vertex class within the origial
                 * mesh and boundary) there already has to be at least one projection
                 * of each border vertex. */
                assert(!vpi0.empty() && !vpi1.empty());
                math::Vec2f vp0(0.0f), vp1(0.0f);
                for (VertexProjectionInfo const & info0 : vpi0) {
                    for (VertexProjectionInfo const & info1 : vpi1) {
                        if (info0.texture_patch_id == info1.texture_patch_id) {
                            vp0 = info0.projection;
                            vp1 = info1.projection;
                            break;
                        }
                    }
                }
                total_projection_length += (vp0 - vp1).norm();
                math::Vec3f const & v0 = vertices[vi0];
                math::Vec3f const & v1 = vertices[vi1];
                total_length += (v0 - v1).norm();
            }
            float radius = total_projection_length / (2.0f * MATH_PI);

            float length = 0.0f;
            std::vector<math::Vec2f> projections(num_vertices);
            for (std::size_t j = 0; j < border.size(); ++j) {
                float angle = 2.0f * MATH_PI * (length / total_length);
                projections[g2l[border[j]]] = math::Vec2f(std::cos(angle), std::sin(angle));
                math::Vec3f const & v0 = vertices[border[j]];
                math::Vec3f const & v1 = vertices[border[(j + 1) % border.size()]];
                length += (v0 - v1).norm();
            }

            typedef Eigen::Triplet<float, int> Triplet;
            std::vector<Triplet> coeff;
            std::size_t matrix_size = num_vertices - border.size();

            Eigen::VectorXf xx(matrix_size), xy(matrix_size);

            if (matrix_size != 0) {
                Eigen::VectorXf bx(matrix_size);
                Eigen::VectorXf by(matrix_size);
                for (std::size_t j = 0; j < num_vertices; ++j) {
                    if (is_border[j]) continue;

                    std::size_t const vertex_id = l2g[j];

                    /* Calculate "Mean Value Coordinates" as proposed by Michael S. Floater */
                    std::map<std::size_t, float> weights;

                    // adjacent facets of each vertex
                    std::vector<std::size_t> const & adj_faces = vertex_infos->at(vertex_id).faces;
                    for (std::size_t adj_face : adj_faces) {
                        std::size_t v0 = mesh_faces[adj_face * 3];
                        std::size_t v1 = mesh_faces[adj_face * 3 + 1];
                        std::size_t v2 = mesh_faces[adj_face * 3 + 2];
                        if (v1 == vertex_id) std::swap(v1, v0);
                        if (v2 == vertex_id) std::swap(v2, v0);

                        math::Vec3f v01 = vertices[v1] - vertices[v0];
                        float v01n = v01.norm();
                        math::Vec3f v02 = vertices[v2] - vertices[v0];
                        float v02n = v02.norm();
                        float alpha = std::acos(v01.dot(v02) / (v01n * v02n));
                        weights[g2l[v1]] += std::tan(alpha / 2.0f) / v01n;
                        weights[g2l[v2]] += std::tan(alpha / 2.0f) / v02n;
                    }

                    // normalization of the weights
                    std::map<std::size_t, float>::iterator it;
                    float sum = 0.0f;
                    for (it = weights.begin(); it != weights.end(); ++it)
                        sum += it->second;
                    for (it = weights.begin(); it != weights.end(); ++it)
                        it->second /= sum;

                    bx[idx[j]] = 0.0f;
                    by[idx[j]] = 0.0f;
                    for (it = weights.begin(); it != weights.end(); ++it) {
                        if (is_border[it->first]) {
                            std::size_t border_vertex_id = border[idx[it->first]];
                            bx[idx[j]] += projections[g2l[border_vertex_id]][0] * it->second;
                            by[idx[j]] += projections[g2l[border_vertex_id]][1] * it->second;
                        } else {
                            coeff.push_back(Triplet(idx[j], idx[it->first], -it->second));
                        }
                    }
                }
                for (std::size_t j = 0; j < matrix_size; ++j) {
                    coeff.push_back(Triplet(j, j, 1.0f));
                }

                typedef Eigen::SparseMatrix<float> SpMat;
                SpMat A(matrix_size, matrix_size);
                A.setFromTriplets(coeff.begin(), coeff.end());

                Eigen::SparseLU<SpMat> solver;
                solver.analyzePattern(A);
                solver.factorize(A);
                xx = solver.solve(bx);
                xy = solver.solve(by);
            }

            int image_size = std::floor(radius * 1.1f) * 2 + 4;
            int scale = image_size / 2 - texture_patch_border;
            for (std::size_t j = 0, k = 0; j < num_vertices; ++j) {
                if (!is_border[j]) {
                    projections[j] = math::Vec2f(xx[k], xy[k]) * scale + image_size / 2;
                    ++k;
                } else {
                    projections[j] = projections[j] * scale + image_size / 2;
                }
            }

            mve::ByteImage::Ptr image = mve::ByteImage::create(image_size, image_size, 3);
            //DEBUG image->fill_color(*math::Vec4uc(0, 255, 0, 255));
            std::vector<math::Vec2f> texcoords; texcoords.reserve(subgraph.size());
            for (std::size_t const face_id : subgraph) {
                for (std::size_t j = 0; j < 3; ++j) {
                    std::size_t const vertex_id = mesh_faces[face_id * 3 + j];
                    math::Vec2f const & projection = projections[g2l[vertex_id]];
                    texcoords.push_back(projection);
                }
            }
            TexturePatch::Ptr texture_patch = TexturePatch::create(0, subgraph, texcoords, image);
            std::size_t texture_patch_id;
            #pragma omp critical
            {
                texture_patches->push_back(texture_patch);
                texture_patch_id = num_patches++;

                num_hole_faces += subgraph.size();
                ++num_holes;
            }

            for (std::size_t j = 0; j < num_vertices; ++j) {
                std::size_t const vertex_id = l2g[j];
                std::vector<std::size_t> const & adj_faces = vertex_infos->at(vertex_id).faces;
                std::vector<std::size_t> faces;
                faces.reserve(adj_faces.size());
                for (std::size_t adj_face : adj_faces) {
                    if (graph.get_label(adj_face) == 0) {
                        faces.push_back(adj_face);
                    }
                }
                VertexProjectionInfo info = {texture_patch_id, projections[j], faces};
                #pragma omp critical
                vertex_projection_infos->at(vertex_id).push_back(info);
            }
        }
    }

    merge_vertex_projection_infos(vertex_projection_infos);

    std::cout << "done. (Took " << timer.get_elapsed_sec() << "s)" << std::endl;
    std::cout << "\t" << num_patches << " texture patches." << std::endl;
    std::cout << "\t" << num_holes << " holes (" << num_hole_faces << " faces)." << std::endl;
}

TEX_NAMESPACE_END


#include "../config.hpp"
#include <boost/type_index.hpp>
TEMPLATE_TEST_CASE("meter_t addition negative tests",
                   "[quantity][arithmetic]",
                   ampere_t, candela_t, kelvin_t, kilogram_t, mol_t, second_t)
{
    REQUIRE( IS_INVALID_EXPR(DECLVAL(0) + DECLVAL(1))(meter_t, TestType) );
}

TEMPLATE_TEST_CASE("ampere_t addition negative tests",
                   "[quantity][arithmetic]",
                   meter_t, candela_t, kelvin_t, kilogram_t, mol_t, second_t)
{
    REQUIRE( IS_INVALID_EXPR(DECLVAL(0) + DECLVAL(1))(ampere_t, TestType) );
}

TEMPLATE_TEST_CASE("candela_t addition negative tests",
                   "[quantity][arithmetic]",
                   meter_t, ampere_t, kelvin_t, kilogram_t, mol_t, second_t)
{
    REQUIRE( IS_INVALID_EXPR(DECLVAL(0) + DECLVAL(1))(candela_t, TestType) );
}

TEMPLATE_TEST_CASE("kelvin_t addition negative tests",
                   "[quantity][arithmetic]",
                   meter_t, ampere_t, candela_t, kilogram_t, mol_t, second_t)
{
    REQUIRE( IS_INVALID_EXPR(DECLVAL(0) + DECLVAL(1))(kelvin_t, TestType) );
}

TEMPLATE_TEST_CASE("kilogram_t addition negative tests",
                   "[quantity][arithmetic]",
                   meter_t, ampere_t, candela_t, kelvin_t, mol_t, second_t)
{
    REQUIRE( IS_INVALID_EXPR(DECLVAL(0) + DECLVAL(1))(kilogram_t, TestType) );
}

TEMPLATE_TEST_CASE("mol_t addition negative tests",
                   "[quantity][arithmetic]",
                   meter_t, ampere_t, candela_t, kelvin_t, kilogram_t, second_t)
{
    REQUIRE( IS_INVALID_EXPR(DECLVAL(0) + DECLVAL(1))(mol_t, TestType) );
}

TEMPLATE_TEST_CASE("second_t addition negative tests",
                   "[quantity][arithmetic]",
                   meter_t, ampere_t, candela_t, kelvin_t, kilogram_t, mol_t)
{
    REQUIRE( IS_INVALID_EXPR(DECLVAL(0) + DECLVAL(1))(second_t, TestType) );
}

TEMPLATE_TEST_CASE("add quantity and value tests",
                   "[quantity][arithmetic]",
                   int, unsigned, double, float, long long, long double)
{
    REQUIRE( std::is_same_v<std::decay_t<decltype(std::declval<TestType>() + std::declval<quantity_t<dimless_t, TestType>>())>, TestType> );
}


#include <boost/bind.hpp>
#include <gazebo/gazebo.hh>
#include <gazebo/physics/physics.hh>
#include <gazebo/common/common.hh>
#include <gazebo/math/gzmath.hh>
#include <stdio.h>
#include "ros/ros.h"
#include "nav_msgs/Odometry.h"
#include <stdlib.h>

namespace gazebo
{
class CatGPS : public ModelPlugin
{
public:
        void Load(physics::ModelPtr _parent, sdf::ElementPtr /*_sdf*/);
        void OnUpdate(const common::UpdateInfo & /*_info*/);
private:
        void CatVehicleSimROSThread();
        void Callback(const nav_msgs::Odometry::ConstPtr& msg);

        //ROS
        ros::Subscriber sub_;
        boost::thread ros_spinner_thread_;
        ros::NodeHandle* rosnode_;

	//gazebo
        event::ConnectionPtr updateConnection;
        physics::ModelPtr model;

	//variables
	int k;
	double x , y , z , x_ang , y_ang , z_ang , omega , x_new , y_new , offset_x , offset_y , car_height;
	math::Vector3 vector;
	math::Pose _pose;
	math::Quaternion quaternion;
};
}


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//         Copyright 2011 - 2012   MetaScale SAS
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef NT2_CORE_FUNCTIONS_MEAN_HPP_INCLUDED
#define NT2_CORE_FUNCTIONS_MEAN_HPP_INCLUDED

#include <boost/simd/include/functor.hpp>
#include <boost/dispatch/include/functor.hpp>

namespace nt2
{
  namespace tag
  {
    /*!
      @brief Tag for the mean functor
    **/
    struct mean_ : ext::abstract_<mean_>
    {
      /// @brief Parent hierarchy
      typedef ext::abstract_<mean_> parent;
      template<class... Args>
      static BOOST_FORCEINLINE BOOST_AUTO_DECLTYPE dispatch(Args&&... args)
      BOOST_AUTO_DECLTYPE_BODY( dispatching_mean_( ext::adl_helper(), static_cast<Args&&>(args)... ) )
    };
  }
  namespace ext
  {
    template<class Site, class... Ts>
    BOOST_FORCEINLINE generic_dispatcher<tag::mean_, Site> dispatching_mean_(adl_helper, boost::dispatch::meta::unknown_<Site>, boost::dispatch::meta::unknown_<Ts>...)
    {
      return generic_dispatcher<tag::mean_, Site>();
    }
    template<class... Args>
    struct impl_mean_;
  }
  /*!
   @brief mean of a table along a given dimension

    Compute the mean of the  elements of a table expression
    along a given dimension.

    @par Semantic

    For any table  expression @c t and any integer @c n:

    @code
    auto r = mean(t,n);
    @endcode

    is equivalent to:

    @code
    auto r = sum(t,n)/size(a, n);
    @endcode

    @par Note:
    n default to firstnonsingleton(t)

    @see @funcref{firstnonsingleton}, @funcref{sum}, @funcref{size},

    @param a0 Table to process
    @param a1 Dimension along which to process a0

    @return An expression eventually evaluated to the result
   */
  NT2_FUNCTION_IMPLEMENTATION(nt2::tag::mean_       , mean, 2)
  /// @overload
  NT2_FUNCTION_IMPLEMENTATION(nt2::tag::mean_       , mean, 1)
}

#endif


/*
* TS Elements
* Copyright 2015-2018 M. Newhouse
* Released under the MIT license.
*/

#pragma once

#include "graphics/geometry.hpp"

#include "utility/vector2.hpp"
#include "utility/color.hpp"

#include <boost/utility/string_ref.hpp>

#include <vector>
#include <cstdint>

namespace ts
{
  namespace fonts
  {
    class BitmapFont;

    namespace text_style
    {
      enum : std::uint32_t
      {
        center_horizontal = 1,
        center_vertical = 2,
        word_wrap = 4,
      };
    }

    class TextGeometry
      : private graphics::Geometry
    {
    public:
      void add_text(boost::string_ref text, const BitmapFont& font, FloatRect area, Colorb color,
                    std::uint32_t text_flags);

      using graphics::Geometry::draw;
      using graphics::Geometry::clear;

    private:
      struct CacheComponent
      {
        std::vector<vertex_type> vertices;
        const graphics::Texture* texture;
      };

      std::vector<CacheComponent> vertex_cache_;
    };
  }
}


#ifndef UTILS_H_
#define UTILS_H_

// basic
#include <iostream>
#include <fstream>
#include <stdint.h>
#include <math.h>
#include <time.h>
#include <string>
#include <vector>
#include <algorithm>
#include <random>

// ros
#include <ros/ros.h>
// cv
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <cv_bridge/cv_bridge.h>
#include <cv.h>
#include <highgui.h>
#include <opencv2/imgproc/imgproc.hpp>
// messages & services
#include <std_srvs/Empty.h>
#include <std_msgs/Float32MultiArray.h>
#include <std_msgs/Int32MultiArray.h>
#include <sensor_msgs/PointCloud2.h>
#include <sensor_msgs/point_cloud2_iterator.h>
#include <sensor_msgs/LaserScan.h>
#include <sensor_msgs/Image.h>
#include <geometry_msgs/PoseStamped.h>
#include <visualization_msgs/Marker.h>
#include <visualization_msgs/MarkerArray.h>
// pcl
#include <pcl_conversions/pcl_conversions.h>
#include <pcl/io/pcd_io.h>
#include <pcl/io/ply_io.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/common/common.h>
#include <pcl/common/transforms.h>
// tf
#include <tf/transform_broadcaster.h>
// bag
#include <rosbag/bag.h>
#include <rosbag/view.h>
#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

// Eigen
#include <Eigen/Core>
#include <Eigen/Geometry>

// namespace
using namespace std;

namespace renderer{

struct state {
    int                action;
    sensor_msgs::Image color;
    float              reward;
    bool               terminal;
};

bool static quaternionMsgToRPY(const geometry_msgs::Quaternion& quaternion,
                               double& roll,
                               double& pitch,
                               double& yaw){
    tf::Quaternion tmp_quaternion(quaternion.x,
                                  quaternion.y,
                                  quaternion.z,
                                  quaternion.w);
    tf::Matrix3x3 m(tmp_quaternion);
    m.getRPY(roll, pitch, yaw);

    return true;
}

bool static applyTranslation(Eigen::Matrix4f& transform_trans,
                             const double& x,
                             const double& y,
                             const double& z,
                             bool reset=true){
    if (reset){transform_trans = Eigen::Matrix4f::Identity();}
    transform_trans(0, 3) = x;
    transform_trans(1, 3) = y;
    transform_trans(2, 3) = z;

    return true;
}

bool static applyRotation(Eigen::Matrix4f& transform_rotat,
                          double theta,
                          int l,
                          int m,
                          int n,
                          bool reset=true){
    if (reset){transform_rotat = Eigen::Matrix4f::Identity();}
    transform_rotat(0, 0) = l*l*(1-cos(theta)) +   cos(theta);
    transform_rotat(0, 1) = m*l*(1-cos(theta)) - n*sin(theta);
    transform_rotat(0, 2) = n*l*(1-cos(theta)) + m*sin(theta);
    transform_rotat(1, 0) = l*m*(1-cos(theta)) + n*sin(theta);
    transform_rotat(1, 1) = m*m*(1-cos(theta)) +   cos(theta);
    transform_rotat(1, 2) = n*m*(1-cos(theta)) - l*sin(theta);
    transform_rotat(2, 0) = l*n*(1-cos(theta)) - m*sin(theta);
    transform_rotat(2, 1) = m*n*(1-cos(theta)) + l*sin(theta);
    transform_rotat(2, 2) = n*n*(1-cos(theta)) +   cos(theta);

    return true;
}

double static randomRadian(){
    return rand() / double(RAND_MAX) * 2. * M_PI;
}

// NOTE: navive version w/o wrapping around
double static deg2rad(const double& deg){
    return deg * M_PI / 180.;
}

// NOTE: navive version w/o wrapping around
double static rad2deg(const double& rad){
    return rad / M_PI * 180.;
}

double static getDistance(const double& y_0,
                          const double& x_0,
                          const double& y_1,
                          const double& x_1){
    return sqrt(pow(y_0 - y_1, 2.0) + pow(x_0 - x_1, 2.0));
}

double static getRelativeRadian(){

}

} // end of namespace

#endif


#include <boost/test/unit_test.hpp>
#include <iostream>
#include "objects/numerics/float.hpp"
#include "objects/numerics/integer.hpp"
#include "objects/invokable/generic_function.hpp"
#include "globals/globals.hpp"

using obj::Object;

BOOST_AUTO_TEST_SUITE( add_suite )

BOOST_AUTO_TEST_CASE ( mod_add_genfn_specs ) {
  using obj::GenericFunction;

  GenericFunction* gadd = dynamic_cast<GenericFunction*>(num::add);

  BOOST_TEST(gadd != nullptr);

  BOOST_TEST(gadd->functions.size() == 2);
}

BOOST_AUTO_TEST_CASE( mod_int_add ) {
  using obj::Integer;
  Integer i1(2);
  Integer i2(3);

  Object* out = std::get<Object*>((*num::add)({&i1, &i2}, *context::empty));
  Integer* i3 = dynamic_cast<Integer*>(out);

  BOOST_TEST(i3 != nullptr);
  BOOST_TEST(i3->val == 5);
}

BOOST_AUTO_TEST_CASE( mod_float_add ) {
  using obj::Float;
  Float f1(2.5);
  Float f2(3.2);

  Object* out = std::get<Object*>((*num::add)({&f1, &f2}, *context::empty));
  Float* f3 = dynamic_cast<Float*>(out);
  Float* f4 = new Float(f1.val + f2.val);

  BOOST_TEST(f3 != nullptr);
  BOOST_TEST(f3->val == f4->val);
}

BOOST_AUTO_TEST_SUITE_END()


//==================================================================================================
/*!
  @file

    @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_GENMASKC_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_GENMASKC_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

  /*!
    @ingroup group-bitwise
    This function object returns a mask of bits. This mask is full of ones if the
    input element is @ref Zero else full of zeros.

    @par Header <boost/simd/function/genmaskc.hpp>

    @see genmask

    @par Example:

      @snippet genmaskc.cpp genmaskc

    @par Possible output:

      @snippet genmaskc.txt genmaskc

    @par Alias:
    @c if_zero_else_allbits

    @see if_else_allbits

  **/
  Value genmaskc(Value const& x);
} }
#endif

#include <boost/simd/function/scalar/genmaskc.hpp>
#include <boost/simd/function/simd/genmaskc.hpp>

#endif


//
// Copyright © 2017 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//

#include <boost/test/unit_test.hpp>
#include "ParserFlatbuffersFixture.hpp"
#include "../TfLiteParser.hpp"

#include <string>
#include <iostream>

BOOST_AUTO_TEST_SUITE(TensorflowLiteParser)

struct SubFixture : public ParserFlatbuffersFixture
{
    explicit SubFixture(const std::string & inputShape1,
                        const std::string & inputShape2,
                        const std::string & outputShape,
                        const std::string & activation="NONE")
    {
        m_JsonString = R"(
            {
                "version": 3,
                "operator_codes": [ { "builtin_code": "SUB" } ],
                "subgraphs": [ {
                    "tensors": [
                        {
                            "shape": )" + inputShape1 + R"(,
                            "type": "UINT8",
                            "buffer": 0,
                            "name": "inputTensor1",
                            "quantization": {
                                "min": [ 0.0 ],
                                "max": [ 255.0 ],
                                "scale": [ 1.0 ],
                                "zero_point": [ 0 ],
                            }
                        },
                        {
                            "shape": )" + inputShape2 + R"(,
                            "type": "UINT8",
                            "buffer": 1,
                            "name": "inputTensor2",
                            "quantization": {
                                "min": [ 0.0 ],
                                "max": [ 255.0 ],
                                "scale": [ 1.0 ],
                                "zero_point": [ 0 ],
                            }
                        },
                        {
                            "shape": )" + outputShape + R"( ,
                            "type": "UINT8",
                            "buffer": 2,
                            "name": "outputTensor",
                            "quantization": {
                                "min": [ 0.0 ],
                                "max": [ 255.0 ],
                                "scale": [ 1.0 ],
                                "zero_point": [ 0 ],
                            }
                        }
                    ],
                    "inputs": [ 0, 1 ],
                    "outputs": [ 2 ],
                    "operators": [
                        {
                            "opcode_index": 0,
                            "inputs": [ 0, 1 ],
                            "outputs": [ 2 ],
                            "builtin_options_type": "SubOptions",
                            "builtin_options": {
                                "fused_activation_function": )" + activation + R"(
                            },
                            "custom_options_format": "FLEXBUFFERS"
                        }
                    ],
                } ],
                "buffers" : [
                    { },
                    { }
                ]
            }
        )";
        Setup();
    }
};

struct SimpleSubFixture : SubFixture
{
    SimpleSubFixture() : SubFixture("[ 1, 4 ]",
                                    "[ 1, 4 ]",
                                    "[ 1, 4 ]") {}
};

BOOST_FIXTURE_TEST_CASE(SimpleSub, SimpleSubFixture)
{
  RunTest<2, armnn::DataType::QAsymmU8>(
      0,
      {{"inputTensor1", { 4, 5, 6, 7 }},
      {"inputTensor2", { 3, 2, 1, 0 }}},
      {{"outputTensor", { 1, 3, 5, 7 }}});
}

struct DynamicSubFixture : SubFixture
{
    DynamicSubFixture() : SubFixture("[ 1, 4 ]",
                                     "[ 1, 4 ]",
                                     "[  ]") {}
};

BOOST_FIXTURE_TEST_CASE(DynamicSub, DynamicSubFixture)
{
    RunTest<2, armnn::DataType::QAsymmU8, armnn::DataType::QAsymmU8>(
        0,
        {{"inputTensor1", { 4, 5, 6, 7 }},
         {"inputTensor2", { 3, 2, 1, 0 }}},
        {{"outputTensor", { 1, 3, 5, 7 }}},
        true);
}

BOOST_AUTO_TEST_SUITE_END()