#include <gtest/gtest.h>

#include <type_traits>
#include <algorithm>
#include <string>
#include <fstream>
#include <iostream>
#include <tuple>
#include <vector>
#include <boost/filesystem.hpp>
#include "DocumentationPath.h"
#include "TestRequiringOptions.h"

#include <noise/noise.h>
#include <noise/module/codecs/ModuleCodecs.h>

template<typename T>
class TestModuleDecode : public TestRequiringOptions
{
public:
    TestModuleDecode() :
        TestRequiringOptions()
    {
        exampleModules.emplace_back();
        exampleSources.emplace_back();
    }
    const static std::string type;
    typedef noise::module::Wrapper<T> Wrapper;
    std::vector<Wrapper> exampleModules;
    std::vector<noise::module::NoiseSources> exampleSources;
    void compareModules(const T& expected, const T& actual) {};
    void compareSources(const noise::module::NoiseSources& expected, const noise::module::NoiseSources& actual)
    {
        EXPECT_EQ(bool(expected.sourceModules), bool(actual.sourceModules));
        EXPECT_EQ(bool(expected.controlModule), bool(actual.controlModule));
        EXPECT_EQ(bool(expected.displaceModules), bool(actual.displaceModules));
        if (expected.sourceModules)
            EXPECT_EQ(expected.sourceModules.get(), actual.sourceModules.get());
        if (expected.controlModule)
            EXPECT_EQ(expected.controlModule.get(), actual.controlModule.get());
        if (expected.displaceModules)
            EXPECT_EQ(expected.displaceModules.get(), actual.displaceModules.get());
    }
};

template<> const std::string TestModuleDecode<noise::module::Abs>::type = "Abs";
template<> const std::string TestModuleDecode<noise::module::Add>::type = "Add";
template<> const std::string TestModuleDecode<noise::module::Billow>::type = "Billow";
template<> const std::string TestModuleDecode<noise::module::Bitmap>::type = "Bitmap";
template<> const std::string TestModuleDecode<noise::module::Blend>::type = "Blend";
template<> const std::string TestModuleDecode<noise::module::Cache>::type = "Cache";
template<> const std::string TestModuleDecode<noise::module::Checkerboard>::type = "Checkerboard";
template<> const std::string TestModuleDecode<noise::module::Clamp>::type = "Clamp";
template<> const std::string TestModuleDecode<noise::module::Const>::type = "Const";
template<> const std::string TestModuleDecode<noise::module::CornerCombinerBase>::type = "CornerCombinerBase";
template<> const std::string TestModuleDecode<noise::module::Curve>::type = "Curve";
template<> const std::string TestModuleDecode<noise::module::Cylinders>::type = "Cylinders";
template<> const std::string TestModuleDecode<noise::module::Displace>::type = "Displace";
template<> const std::string TestModuleDecode<noise::module::Divide>::type = "Divide";
template<> const std::string TestModuleDecode<noise::module::Exp>::type = "Exp";
template<> const std::string TestModuleDecode<noise::module::Exponent>::type = "Exponent";
template<> const std::string TestModuleDecode<noise::module::Forward>::type = "Forward";
template<> const std::string TestModuleDecode<noise::module::GradientX>::type = "GradientX";
template<> const std::string TestModuleDecode<noise::module::GradientY>::type = "GradientY";
template<> const std::string TestModuleDecode<noise::module::GradientZ>::type = "GradientZ";
template<> const std::string TestModuleDecode<noise::module::Invert>::type = "Invert";
template<> const std::string TestModuleDecode<noise::module::Logarithm>::type = "Logarithm";
template<> const std::string TestModuleDecode<noise::module::Max>::type = "Max";
template<> const std::string TestModuleDecode<noise::module::Min>::type = "Min";
template<> const std::string TestModuleDecode<noise::module::Multiply>::type = "Multiply";
template<> const std::string TestModuleDecode<noise::module::NormLPQ>::type = "NormLPQ";
template<> const std::string TestModuleDecode<noise::module::Perlin>::type = "Perlin";
template<> const std::string TestModuleDecode<noise::module::Pow>::type = "Pow";
template<> const std::string TestModuleDecode<noise::module::Power>::type = "Power";
template<> const std::string TestModuleDecode<noise::module::QuadrantSelector>::type = "QuadrantSelector";
template<> const std::string TestModuleDecode<noise::module::Reciprocal>::type = "Reciprocal";
template<> const std::string TestModuleDecode<noise::module::RidgedMulti>::type = "RidgedMulti";
template<> const std::string TestModuleDecode<noise::module::RotatePoint>::type = "RotatePoint";
template<> const std::string TestModuleDecode<noise::module::ScaleBias>::type = "ScaleBias";
template<> const std::string TestModuleDecode<noise::module::ScalePoint>::type = "ScalePoint";
template<> const std::string TestModuleDecode<noise::module::Select>::type = "Select";
template<> const std::string TestModuleDecode<noise::module::Spheres>::type = "Spheres";
template<> const std::string TestModuleDecode<noise::module::Terrace>::type = "Terrace";
template<> const std::string TestModuleDecode<noise::module::TranslatePoint>::type = "TranslatePoint";
template<> const std::string TestModuleDecode<noise::module::Turbulence>::type = "Turbulence";
template<> const std::string TestModuleDecode<noise::module::Voronoi>::type = "Voronoi";

template<>
TestModuleDecode<noise::module::Abs>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{"mySourceModule"});
}

template<>
TestModuleDecode<noise::module::Add>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{
        "mySourceModule",
        "myOtherSourceModule"});
}

template<>
TestModuleDecode<noise::module::Billow>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetSeed(123);
    this->exampleModules[0].module->SetFrequency(2.2);
    this->exampleModules[0].module->SetPersistence(0.5);
    this->exampleModules[0].module->SetLacunarity(1.8);
    this->exampleModules[0].module->SetOctaveCount(14);
    this->exampleModules[0].module->SetNoiseQuality(noise::QUALITY_BEST);
    this->exampleSources.emplace_back();
}

template<>
TestModuleDecode<noise::module::Bitmap>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetFilename("../../tests/bitmap.png");
    this->exampleModules[0].module->SetDefaultValue(0.0);
    this->exampleModules[0].module->SetRegion(sf::Rect<double>(1.,2.,3.,4.));
    this->exampleModules[0].module->SetMaxScale(true);
    this->exampleSources.emplace_back();
}

template<>
TestModuleDecode<noise::module::Blend>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{
        "mySourceModule",
            "myOtherSourceModule"});
    this->exampleSources[0].controlModule.emplace("myControlModule");
}

template<>
TestModuleDecode<noise::module::Cache>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{ "mySourceModule" });
}

template<>
TestModuleDecode<noise::module::Checkerboard>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleSources.emplace_back();
}

template<>
TestModuleDecode<noise::module::Clamp>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetBounds(-0.5, 1.5);
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{ "mySourceModule" });
}

template<>
TestModuleDecode<noise::module::Const>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetConstValue(0.7);
    this->exampleSources.emplace_back();
}

template<>
TestModuleDecode<noise::module::Curve>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->AddControlPoint(-0.5, 0.3);
    this->exampleModules[0].module->AddControlPoint(1.1, 2.2);
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{ "mySourceModule" });
}

template<>
TestModuleDecode<noise::module::Cylinders>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetFrequency(2.2);
    this->exampleSources.emplace_back();
}

template<>
TestModuleDecode<noise::module::Displace>::TestModuleDecode()
{
    for (int i = 0; i < 2; i++)
    {
        this->exampleModules.emplace_back();
        this->exampleSources.emplace_back();
        this->exampleSources[i].sourceModules.emplace(std::initializer_list<std::string>{ "mySourceModule" });
        this->exampleSources[i].displaceModules.emplace(std::initializer_list<std::string>{
            "mySourceModuleX",
                "mySourceModuleY",
                "mySourceModuleZ" });
    }
}

template<>
TestModuleDecode<noise::module::Divide>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{
        "mySourceModule",
            "myOtherSourceModule"});
}

template<>
TestModuleDecode<noise::module::Exponent>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetExponent(1.1);
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{ "mySourceModule" });
}

template<>
TestModuleDecode<noise::module::Invert>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{ "mySourceModule" });
}

template<>
TestModuleDecode<noise::module::Max>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{
        "mySourceModule",
            "myOtherSourceModule" });
}

template<>
TestModuleDecode<noise::module::Min>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{
        "mySourceModule",
            "myOtherSourceModule" });
}

template<>
TestModuleDecode<noise::module::Multiply>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{
        "mySourceModule",
            "myOtherSourceModule" });
}

template<>
TestModuleDecode<noise::module::Perlin>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetSeed(123);
    this->exampleModules[0].module->SetFrequency(2.2);
    this->exampleModules[0].module->SetPersistence(0.5);
    this->exampleModules[0].module->SetLacunarity(1.8);
    this->exampleModules[0].module->SetOctaveCount(14);
    this->exampleModules[0].module->SetNoiseQuality(noise::QUALITY_BEST);
    this->exampleSources.emplace_back();
}

template<>
TestModuleDecode<noise::module::Power>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{
        "mySourceModule",
            "myOtherSourceModule" });
}

template<>
TestModuleDecode<noise::module::Reciprocal>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{ "mySourceModule" });
}

template<>
TestModuleDecode<noise::module::RidgedMulti>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetSeed(123);
    this->exampleModules[0].module->SetFrequency(2.2);
    this->exampleModules[0].module->SetLacunarity(1.8);
    this->exampleModules[0].module->SetOctaveCount(14);
    this->exampleModules[0].module->SetNoiseQuality(noise::QUALITY_BEST);
    this->exampleSources.emplace_back();
}

template<>
TestModuleDecode<noise::module::RotatePoint>::TestModuleDecode()
{
    for (int i = 0; i < 2; i++)
    {
        this->exampleModules.emplace_back();
        this->exampleModules[i].module->SetAngles(10.0, 25.5, -45.4);
        this->exampleSources.emplace_back();
        this->exampleSources[i].sourceModules.emplace(std::initializer_list<std::string>{ "mySourceModule" });
    }

}

template<>
TestModuleDecode<noise::module::ScaleBias>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetBias(-0.75);
    this->exampleModules[0].module->SetScale(1.1);
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{ "mySourceModule" });
}

template<>
TestModuleDecode<noise::module::ScalePoint>::TestModuleDecode()
{
    for (int i = 0; i < 3; i++)
    {
        this->exampleModules.emplace_back();
        this->exampleModules[i].module->SetScale(2.0, 3.5, -1.4);
        this->exampleSources.emplace_back();
        this->exampleSources[i].sourceModules.emplace(std::initializer_list<std::string>{ "mySourceModule" });
    }
    this->exampleModules[2].module->SetScale(1.4);
}

template<>
TestModuleDecode<noise::module::Voronoi>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetSeed(123);
    this->exampleModules[0].module->SetFrequency(2.2);
    this->exampleModules[0].module->SetDisplacement(0.5);
    this->exampleModules[0].module->EnableDistance(true);
    this->exampleSources.emplace_back();
}

template<>
TestModuleDecode<noise::module::Turbulence>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetFrequency(2.2);
    this->exampleModules[0].module->SetPower(1.3);
    this->exampleModules[0].module->SetRoughness(3);
    this->exampleModules[0].module->SetSeed(133);
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{ "mySourceModule" });
}

template<>
TestModuleDecode<noise::module::TranslatePoint>::TestModuleDecode()
{
    for (int i = 0; i < 3; i++)
    {
        this->exampleModules.emplace_back();
        this->exampleModules[i].module->SetTranslation(2.0, 3.5, -1.4);
        this->exampleSources.emplace_back();
        this->exampleSources[i].sourceModules.emplace(std::initializer_list<std::string>{ "mySourceModule" });
    }
    this->exampleModules[2].module->SetTranslation(4.3);
}

template<>
TestModuleDecode<noise::module::Terrace>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->AddControlPoint(-0.5);
    this->exampleModules[0].module->AddControlPoint(0.25);
    this->exampleModules[0].module->AddControlPoint(0.7);
    this->exampleModules[0].module->AddControlPoint(0.9);
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{ "mySourceModule" });
}

template<>
TestModuleDecode<noise::module::Spheres>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetFrequency(2.2);
    this->exampleSources.emplace_back();
}

template<>
TestModuleDecode<noise::module::Select>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetBounds(0.2, 0.87);
    this->exampleModules[0].module->SetEdgeFalloff(0.25);
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{
        "mySourceModule",
            "myOtherSourceModule" });
    this->exampleSources[0].controlModule.emplace("myControlModule");
}

template<>
TestModuleDecode<noise::module::CornerCombinerBase>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetPower(3.5);
    this->exampleSources.emplace_back();
}

template<>
TestModuleDecode<noise::module::Exp>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetBase(2.2);
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{
        "mySourceModule"});
}

template<>
TestModuleDecode<noise::module::Forward>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{
        "mySourceModule"});
}

template<>
TestModuleDecode<noise::module::Logarithm>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetBase(3.3);
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{
        "mySourceModule"});
}

template<>
TestModuleDecode<noise::module::NormLPQ>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetP(2.3);
    this->exampleModules[0].module->SetQ(1.4);
    this->exampleSources.emplace_back();
}

template<>
TestModuleDecode<noise::module::Pow>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetExponent(3.3);
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{
        "mySourceModule"});
}

template<>
TestModuleDecode<noise::module::QuadrantSelector>::TestModuleDecode()
{
    this->exampleModules.emplace_back();
    this->exampleModules[0].module->SetTranslateIgnored(false, false, true);
    this->exampleSources.emplace_back();
    this->exampleSources[0].sourceModules.emplace(std::initializer_list<std::string>{
        "mySourceModule"});
}

template<>
void TestModuleDecode<noise::module::Bitmap>::compareModules(const noise::module::Bitmap& expected, const noise::module::Bitmap& actual)
{
    EXPECT_EQ(expected.GetFilename(), actual.GetFilename());
    EXPECT_EQ(expected.GetDefaultValue(), actual.GetDefaultValue());
    EXPECT_EQ(expected.GetRegion(), actual.GetRegion());
    EXPECT_EQ(expected.GetMaxScale(), actual.GetMaxScale());

}

template<>
void TestModuleDecode<noise::module::Billow>::compareModules(const noise::module::Billow& expected, const noise::module::Billow& actual)
{
    EXPECT_EQ(expected.GetFrequency(), actual.GetFrequency());
    EXPECT_EQ(expected.GetLacunarity(), actual.GetLacunarity());
    EXPECT_EQ(expected.GetNoiseQuality(), actual.GetNoiseQuality());
    EXPECT_EQ(expected.GetOctaveCount(), actual.GetOctaveCount());
    EXPECT_EQ(expected.GetPersistence(), actual.GetPersistence());
    EXPECT_EQ(expected.GetSeed(), actual.GetSeed());
}

template<>
void TestModuleDecode<noise::module::Clamp>::compareModules(const noise::module::Clamp& expected, const noise::module::Clamp& actual)
{
    EXPECT_EQ(expected.GetLowerBound(), actual.GetLowerBound());
    EXPECT_EQ(expected.GetUpperBound(), actual.GetUpperBound());
}

template<>
void TestModuleDecode<noise::module::CornerCombinerBase>::compareModules(const noise::module::CornerCombinerBase& expected, const noise::module::CornerCombinerBase& actual)
{
    EXPECT_EQ(expected.GetPower(), actual.GetPower());
}

bool operator==(noise::module::ControlPoint lhs, noise::module::ControlPoint rhs)
{
    return std::tie(lhs.inputValue, lhs.outputValue) == std::tie(rhs.inputValue, rhs.outputValue);
}

template<>
void TestModuleDecode<noise::module::Curve>::compareModules(const noise::module::Curve& expected, const noise::module::Curve& actual)
{
    EXPECT_EQ(expected.GetControlPointCount(), actual.GetControlPointCount());
    for (int i = 0; i < expected.GetControlPointCount(); i++)
    {
        EXPECT_TRUE(expected.GetControlPointArray()[i] == actual.GetControlPointArray()[i]);
    }
}

template<>
void TestModuleDecode<noise::module::Cylinders>::compareModules(const noise::module::Cylinders& expected, const noise::module::Cylinders& actual)
{
    EXPECT_EQ(expected.GetFrequency(), actual.GetFrequency());
}

template<>
void TestModuleDecode<noise::module::Exp>::compareModules(const noise::module::Exp& expected, const noise::module::Exp& actual)
{
    EXPECT_EQ(expected.GetBase(), actual.GetBase());
}

template<>
void TestModuleDecode<noise::module::Exponent>::compareModules(const noise::module::Exponent& expected, const noise::module::Exponent& actual)
{
    EXPECT_EQ(expected.GetExponent(), actual.GetExponent());
}

template<>
void TestModuleDecode<noise::module::Logarithm>::compareModules(const noise::module::Logarithm& expected, const noise::module::Logarithm& actual)
{
    EXPECT_EQ(expected.GetBase(), actual.GetBase());
}

template<>
void TestModuleDecode<noise::module::Perlin>::compareModules(const noise::module::Perlin& expected, const noise::module::Perlin& actual)
{
    EXPECT_EQ(expected.GetFrequency(), actual.GetFrequency());
    EXPECT_EQ(expected.GetLacunarity(), actual.GetLacunarity());
    EXPECT_EQ(expected.GetNoiseQuality(), actual.GetNoiseQuality());
    EXPECT_EQ(expected.GetOctaveCount(), actual.GetOctaveCount());
    EXPECT_EQ(expected.GetPersistence(), actual.GetPersistence());
    EXPECT_EQ(expected.GetSeed(), actual.GetSeed());
}

template<>
void TestModuleDecode<noise::module::QuadrantSelector>::compareModules(const noise::module::QuadrantSelector& expected, const noise::module::QuadrantSelector& actual)
{
    EXPECT_EQ(expected.GetTranslate(0), actual.GetTranslate(0));
    EXPECT_EQ(expected.GetTranslate(1), actual.GetTranslate(1));
    EXPECT_EQ(expected.GetTranslate(2), actual.GetTranslate(2));
    EXPECT_EQ(expected.GetTranslateIgnored(0), actual.GetTranslateIgnored(0));
    EXPECT_EQ(expected.GetTranslateIgnored(1), actual.GetTranslateIgnored(1));
    EXPECT_EQ(expected.GetTranslateIgnored(2), actual.GetTranslateIgnored(2));
}

template<>
void TestModuleDecode<noise::module::RidgedMulti>::compareModules(const noise::module::RidgedMulti& expected, const noise::module::RidgedMulti& actual)
{
    EXPECT_EQ(expected.GetFrequency(), actual.GetFrequency());
    EXPECT_EQ(expected.GetLacunarity(), actual.GetLacunarity());
    EXPECT_EQ(expected.GetNoiseQuality(), actual.GetNoiseQuality());
    EXPECT_EQ(expected.GetOctaveCount(), actual.GetOctaveCount());
    EXPECT_EQ(expected.GetSeed(), actual.GetSeed());
}

template<>
void TestModuleDecode<noise::module::RotatePoint>::compareModules(const noise::module::RotatePoint& expected, const noise::module::RotatePoint& actual)
{
    EXPECT_EQ(expected.GetXAngle(), actual.GetXAngle());
    EXPECT_EQ(expected.GetYAngle(), actual.GetYAngle());
    EXPECT_EQ(expected.GetZAngle(), actual.GetZAngle());
}

template<>
void TestModuleDecode<noise::module::ScaleBias>::compareModules(const noise::module::ScaleBias& expected, const noise::module::ScaleBias& actual)
{
    EXPECT_EQ(expected.GetScale(), actual.GetScale());
    EXPECT_EQ(expected.GetBias(), actual.GetBias());
}

template<>
void TestModuleDecode<noise::module::ScalePoint>::compareModules(const noise::module::ScalePoint& expected, const noise::module::ScalePoint& actual)
{
    EXPECT_EQ(expected.GetXScale(), actual.GetXScale());
    EXPECT_EQ(expected.GetYScale(), actual.GetYScale());
    EXPECT_EQ(expected.GetZScale(), actual.GetZScale());
}

template<>
void TestModuleDecode<noise::module::Select>::compareModules(const noise::module::Select& expected, const noise::module::Select& actual)
{
    EXPECT_EQ(expected.GetLowerBound(), actual.GetLowerBound());
    EXPECT_EQ(expected.GetUpperBound(), actual.GetUpperBound());
    EXPECT_EQ(expected.GetEdgeFalloff(), actual.GetEdgeFalloff());
}

template<>
void TestModuleDecode<noise::module::Terrace>::compareModules(const noise::module::Terrace& expected, const noise::module::Terrace& actual)
{
    EXPECT_EQ(expected.GetControlPointCount(), actual.GetControlPointCount());
    EXPECT_EQ(expected.IsTerracesInverted(), actual.IsTerracesInverted());
    for (int i = 0; i < expected.GetControlPointCount(); i++)
    {
        EXPECT_EQ(expected.GetControlPointArray()[i], actual.GetControlPointArray()[i]);
    }
}

template<>
void TestModuleDecode<noise::module::TranslatePoint>::compareModules(const noise::module::TranslatePoint& expected, const noise::module::TranslatePoint& actual)
{
    EXPECT_EQ(expected.GetXTranslation(), actual.GetXTranslation());
    EXPECT_EQ(expected.GetYTranslation(), actual.GetYTranslation());
    EXPECT_EQ(expected.GetZTranslation(), actual.GetZTranslation());
}

template<>
void TestModuleDecode<noise::module::Turbulence>::compareModules(const noise::module::Turbulence& expected, const noise::module::Turbulence& actual)
{
    EXPECT_EQ(expected.GetFrequency(), actual.GetFrequency());
    EXPECT_EQ(expected.GetPower(), actual.GetPower());
    EXPECT_EQ(expected.GetRoughnessCount(), actual.GetRoughnessCount());
    EXPECT_EQ(expected.GetSeed(), actual.GetSeed());
}

template<>
void TestModuleDecode<noise::module::Voronoi>::compareModules(const noise::module::Voronoi& expected, const noise::module::Voronoi& actual)
{
    EXPECT_EQ(expected.GetFrequency(), actual.GetFrequency());
    EXPECT_EQ(expected.IsDistanceEnabled(), actual.IsDistanceEnabled());
}

typedef ::testing::Types<
    noise::module::Abs,
    noise::module::Add,
    noise::module::Billow,
    noise::module::Bitmap,
    noise::module::Blend,
    noise::module::Cache,
    noise::module::Checkerboard,
    noise::module::Clamp,
    noise::module::Const,
    noise::module::CornerCombinerBase,
    noise::module::Curve,
    noise::module::Cylinders,
    noise::module::Displace,
    noise::module::Divide,
    noise::module::Exp,
    noise::module::Exponent,
    noise::module::Forward,
    noise::module::GradientX,
    noise::module::GradientY,
    noise::module::GradientZ,
    noise::module::Invert,
    noise::module::Logarithm,
    noise::module::Max,
    noise::module::Min,
    noise::module::Multiply,
    noise::module::NormLPQ,
    noise::module::Perlin,
    noise::module::Pow,
    noise::module::Power,
    noise::module::QuadrantSelector,
    noise::module::Reciprocal,
    noise::module::RidgedMulti,
    noise::module::RotatePoint,
    noise::module::ScaleBias,
    noise::module::ScalePoint,
    noise::module::Select,
    noise::module::Spheres,
    noise::module::Terrace,
    noise::module::TranslatePoint,
    noise::module::Turbulence,
    noise::module::Voronoi
>
ModuleDecodeArgs;

TYPED_TEST_CASE(TestModuleDecode, ModuleDecodeArgs);

TYPED_TEST(TestModuleDecode, TestModuleDecode)
{
    int count = 0;
    EXPECT_EQ(this->exampleModules.size(), this->exampleSources.size());
    auto examples_dir = boost::filesystem::path(getDocumentationPath()) / "tests";
    for (size_t i = 0; i < this->exampleModules.size(); i++)
    {
        std::string filename = (examples_dir / (this->type + std::to_string(i + 1) + ".json")).string();
        std::ifstream ifs(filename);
        if (!ifs.good())
        {
            throw std::runtime_error("Could not open example JSON module file:" + filename);
        }

        std::shared_ptr<noise::module::Wrapper<TypeParam>> decoded_module;
        noise::module::NoiseSources decoded_sources;
        std::string str{std::istreambuf_iterator<char>(ifs),
            std::istreambuf_iterator<char>()};
        try
        {
            decoded_module = spotify::json::decode<std::shared_ptr<noise::module::Wrapper<TypeParam>>>(str.c_str());
            decoded_sources = spotify::json::decode<noise::module::NoiseSources>(str.c_str());
        }
        catch (const spotify::json::decode_exception& e)
        {
            std::cout << "spotify::json::decode_exception encountered at "
                << e.offset()
                << ": "
                << e.what();
            throw;
        }
        this->compareModules(*this->exampleModules[i].module, *decoded_module->module);
        this->compareSources(this->exampleSources[i], decoded_sources);
    }
}


//
//=======================================================================
// Copyright 2012
// Author: Alex Hagen-Zanker
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
//
// A class that allow interrupting and resuming the dijkstra algorithm.
//
//=======================================================================
//

#ifndef BLINK_GRAPH_RESUMABLE_DIJKSTRA_HPP
#define BLINK_GRAPH_RESUMABLE_DIJKSTRA_HPP

#include <blink/graph/dijkstra_visitor/joined_visitor.hpp>
#include <blink/graph/dijkstra_state.hpp>
#include <blink/graph/dijkstra_shortest_paths.hpp> //dijkstra_shortest_paths_no_init_at_all
#include <blink/graph/breadth_first_search.hpp> //default_interruptor
#include <blink/graph/relax.hpp> //relax_target

#include <boost/graph/named_function_params.hpp>
#include <boost/range.hpp>
#include <boost/shared_ptr.hpp>

namespace blink {

template<typename DijkstraState>
class resumable_dijkstra : public dijkstra_state_mixin<DijkstraState>
{
public:
  resumable_dijkstra(const DijkstraState& state)
    : dijkstra_state_mixin(boost::shared_ptr<DijkstraState>(new DijkstraState(state) ) )
  {}

  // Expand the shortest path search until the Interruptor returns true on
  // do_interrupt(). The callback functions of both the FirstVisitor and
  // SecondVisitor are applied.
  template<typename Interruptor, typename SecondVisitor>
  bool expand(Interruptor interruptor, SecondVisitor visitor)
  {
    typedef joined_visitor<graph_visitor_type, SecondVisitor> visitor_type;
    visitor_type vis(m_graph_visitor, visitor);

    dijkstra_shortest_paths_no_init_at_all(m_graph, m_vertex_predecessor,
      m_vertex_distance, m_edge_weight, m_vertex_index, m_distance_compare,
      m_distance_combine, m_distance_zero, vis, m_vertex_color,
      m_max_priority_queue,interruptor);

    return m_max_priority_queue.empty();
  }

  // Expand the shortest path search until the Interruptor return true on
  // do_interrupt(). The callback functions of the FirstVisitor are applied,
  // as no second visitor is provided.
  template<typename Interruptor>
  bool expand(Interruptor interruptor)
  {
    return expand(interruptor, boost::default_dijkstra_visitor());
  }

  // Expand the shortest path search exhhaustively. The callback functions of
  // the FirstVisitor are applied, as no second visitor is provided.
  bool expand()
  {
    return expand(default_interruptor(), boost::default_dijkstra_visitor());
  }

  // Initialize the vertices in the color map, predecessor map and distance
  // map, as well as the FirstVisitor and SecondVisitor
  template<typename SecondVisitor>
  void init_all(SecondVisitor vis)
  {
    typedef joined_visitor<graph_visitor_type, SecondVisitor> visitor_type;
    visitor_type visitor = make_joined_visitor(m_graph_visitor, vis);

    vertex_iterator ui, ui_end;
    for (boost::tie(ui, ui_end) = vertices(m_graph); ui != ui_end; ++ui) {
      visitor.initialize_vertex(*ui, m_graph);
      put(m_vertex_color, *ui, color_traits::white() );
      put(m_vertex_predecessor, *ui, *ui);
      put(m_vertex_distance, *ui, m_distance_inf);
    }
    clear(m_max_priority_queue);
      //get(max_priority_queue_t()).clear();
  }

  // Set the source Vertex
  template<typename SecondVisitor>
  void put_source(vertex_descriptor source, SecondVisitor vis)
  {
    typedef joined_visitor<graph_visitor_type, SecondVisitor> visitor_type;
    visitor_type visitor = make_joined_visitor(m_graph_visitor, vis);

    put(m_vertex_color, source, color_traits::gray() );
    put(m_vertex_distance, source, m_distance_zero);
    m_max_priority_queue.push(source);
    visitor.discover_vertex(source, m_graph);
  }

  void put_source(vertex_descriptor source)
  {
    put_source(source, boost::default_dijkstra_visitor());
  }

  // Set multiple source Vertices
  template<typename VerticesRange, typename SecondVisitor>
  void put_sources(const VerticesRange& r, SecondVisitor vis)
  {
    boost::range_iterator<const VerticesRange>::type i = boost::begin(r);
    const boost::range_iterator<const VerticesRange>::type end = boost::end(r);
    for(; i != end; ++i) {
      put_source(*i, vis);
    }
  }

  // Initialize maps and visitors and set source vertex
  template<typename SecondVisitor>
  void init_from_source(vertex_descriptor source, SecondVisitor vis)
  {
    init_all(vis);
    put_source(source, vis);
  }

  // Initialize maps and first visitor and set source vertex, don't provide
  // second visitor
  void init_from_source(vertex_descriptor source)
  {
    init_all(boost::default_dijkstra_visitor() );
    put_source(source, boost::default_dijkstra_visitor() );
  }

  // Initialize maps and visitors and multiple source vertices
  template<typename VerticesRange, typename SecondVisitor>
  void init_from_sources(const VerticesRange& r, SecondVisitor vis)
  {
    init_all(vis);
    put_sources(r, vis);
  }

  // Initialize maps and first visitor and multiple source vertices, don't
  // provide second visitor
  template<typename VerticesRange>
  void init_from_sources(const VerticesRange& r)
  {
    init_all(boost::default_dijkstra_visitor());
    put_sources(r, boost::default_dijkstra_visitor());
  }

};// class resumable_dijkstra

// extends dijkstra_state_helper with the resumable_dijktra type
template<typename Graph, typename Params>
struct resumable_dijkstra_helper
  : protected dijkstra_state_helper<Graph, Params>
{
  typedef typename dijkstra_state_helper<Graph, Params> parent;
  typedef typename parent::type state_type;

  template<typename Tag>
  struct param : parent::param<Tag>
  {};

  typedef typename resumable_dijkstra<state_type> type;

  static type make(const Graph& g, const Params& params)
  {
    state_type state = parent::make(g, params);
    return type(state);
  }
};

  // Make a resumable_dijkstra object
template <typename Graph, typename Params>
typename resumable_dijkstra_helper<Graph, Params>::type
  make_resumable_dijkstra(const Graph& g, const Params& params)
{
  return resumable_dijkstra_helper<Graph, Params>::make(g, params);
}

// Make a resumable_dijkstra, using only default parameters
template <typename Graph>
typename resumable_dijkstra_helper<Graph, boost::no_named_parameters>::type
  make_resumable_dijkstra(const Graph& g)
{
  return make_resumable_dijkstra(g, boost::no_named_parameters() );
}

}// namespace blink

#endif //BLINK_GRAPH_RESUMABLE_DIJKSTRA_HPP


/*
 * PCGSolver.cpp
 *
 *  Created on: Feb 14, 2012
 *      Author: ydjian
 */

#include <gtsam/linear/GaussianFactorGraph.h>
//#include <gtsam/inference/FactorGraph-inst.h>
//#include <gtsam/linear/FactorGraphUtil-inl.h>
//#include <gtsam/linear/JacobianFactorGraph.h>
//#include <gtsam/linear/LSPCGSolver.h>
#include <gtsam/linear/PCGSolver.h>
#include <gtsam/linear/Preconditioner.h>
//#include <gtsam/linear/SuiteSparseUtil.h>
//#include <gtsam/linear/ConjugateGradientMethod-inl.h>
//#include <gsp2/gtsam-interface-sbm.h>
//#include <ydjian/tool/ThreadSafeTimer.h>
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <stdexcept>

using namespace std;

namespace gtsam {

/*****************************************************************************/
void PCGSolverParameters::print(ostream &os) const {
  Base::print(os);
  os << "PCGSolverParameters:" <<  endl;
  preconditioner_->print(os);
}

/*****************************************************************************/
PCGSolver::PCGSolver(const PCGSolverParameters &p) {
  preconditioner_ = createPreconditioner(p.preconditioner_);
}

/*****************************************************************************/
VectorValues PCGSolver::optimize (
  const GaussianFactorGraph &gfg,
  const KeyInfo &keyInfo,
  const std::map<Key, Vector> &lambda,
  const VectorValues &initial)
{
  /* build preconditioner */
  preconditioner_->build(gfg, keyInfo, lambda);

  /* apply pcg */
  const Vector sol = preconditionedConjugateGradient<GaussianFactorGraphSystem, Vector>(
        GaussianFactorGraphSystem(gfg, *preconditioner_, keyInfo, lambda),
        initial.vector(keyInfo.ordering()), parameters_);

  return buildVectorValues(sol, keyInfo);
}

/*****************************************************************************/
GaussianFactorGraphSystem::GaussianFactorGraphSystem(
    const GaussianFactorGraph &gfg,
    const Preconditioner &preconditioner,
    const KeyInfo &keyInfo,
    const std::map<Key, Vector> &lambda)
  : gfg_(gfg), preconditioner_(preconditioner), keyInfo_(keyInfo), lambda_(lambda) {}

/*****************************************************************************/
void GaussianFactorGraphSystem::residual(const Vector &x, Vector &r) const {
  /* implement b-Ax, assume x and r are pre-allocated */

  /* reset r to b */
  getb(r);

  /* substract A*x */
  Vector Ax = Vector::Zero(r.rows(), 1);
  multiply(x, Ax);
  r -= Ax ;
}

/*****************************************************************************/
void GaussianFactorGraphSystem::multiply(const Vector &x, Vector& Ax) const {
  /* implement Ax, assume x and Ax are pre-allocated */

  /* reset y */
  Ax.setZero();

  BOOST_FOREACH ( const GaussianFactor::shared_ptr &gf, gfg_ ) {
    if ( JacobianFactor::shared_ptr jf = boost::dynamic_pointer_cast<JacobianFactor>(gf) ) {
      /* accumulate At A x*/
      for ( JacobianFactor::const_iterator it = jf->begin() ; it != jf->end() ; ++it ) {
        const Matrix Ai = jf->getA(it);
        /* this map lookup should be replaced */
        const KeyInfoEntry &entry = keyInfo_.find(*it)->second;
        Ax.segment(entry.colstart(), entry.dim())
            += Ai.transpose() * (Ai * x.segment(entry.colstart(), entry.dim()));
      }
    }
    else if ( HessianFactor::shared_ptr hf = boost::dynamic_pointer_cast<HessianFactor>(gf) ) {
      /* accumulate H x */

      /* use buffer to avoid excessive table lookups */
      const size_t sz = hf->size();
      vector<Vector> y;
      y.reserve(sz);
      for (HessianFactor::const_iterator it = hf->begin(); it != hf->end(); it++) {
        /* initialize y to zeros */
        y.push_back(zero(hf->getDim(it)));
      }

      for (HessianFactor::const_iterator j = hf->begin(); j != hf->end(); j++ ) {
        /* retrieve the key mapping */
        const KeyInfoEntry &entry = keyInfo_.find(*j)->second;
        // xj is the input vector
        const Vector xj = x.segment(entry.colstart(), entry.dim());
        size_t idx = 0;
        for (HessianFactor::const_iterator i = hf->begin(); i != hf->end(); i++, idx++ ) {
          if ( i == j ) y[idx] += hf->info(j, j).selfadjointView() * xj;
          else y[idx] += hf->info(i, j).knownOffDiagonal() * xj;
        }
      }

      /* accumulate to r */
      for(DenseIndex i = 0; i < (DenseIndex) sz; ++i) {
        /* retrieve the key mapping */
        const KeyInfoEntry &entry = keyInfo_.find(hf->keys()[i])->second;
        Ax.segment(entry.colstart(), entry.dim()) += y[i];
      }
    }
    else {
      throw invalid_argument("GaussianFactorGraphSystem::multiply gfg contains a factor that is neither a JacobianFactor nor a HessianFactor.");
    }
  }
}

/*****************************************************************************/
void GaussianFactorGraphSystem::getb(Vector &b) const {
  /* compute rhs, assume b pre-allocated */

  /* reset */
  b.setZero();

  BOOST_FOREACH ( const GaussianFactor::shared_ptr &gf, gfg_ ) {
    if ( JacobianFactor::shared_ptr jf = boost::dynamic_pointer_cast<JacobianFactor>(gf) ) {
      const Vector rhs = jf->getb();
      /* accumulate At rhs */
      for ( JacobianFactor::const_iterator it = jf->begin() ; it != jf->end() ; ++it ) {
        /* this map lookup should be replaced */
        const KeyInfoEntry &entry = keyInfo_.find(*it)->second;
        b.segment(entry.colstart(), entry.dim()) += jf->getA(it).transpose() * rhs ;
      }
    }
    else if ( HessianFactor::shared_ptr hf = boost::dynamic_pointer_cast<HessianFactor>(gf) ) {
      /* accumulate g */
      for (HessianFactor::const_iterator it = hf->begin(); it != hf->end(); it++) {
        const KeyInfoEntry &entry = keyInfo_.find(*it)->second;
        b.segment(entry.colstart(), entry.dim()) += hf->linearTerm(it);
      }
    }
    else {
      throw invalid_argument("GaussianFactorGraphSystem::getb gfg contains a factor that is neither a JacobianFactor nor a HessianFactor.");
    }
  }
}

/**********************************************************************************/
void GaussianFactorGraphSystem::leftPrecondition(const Vector &x, Vector &y) const
{ preconditioner_.transposeSolve(x, y); }

/**********************************************************************************/
void GaussianFactorGraphSystem::rightPrecondition(const Vector &x, Vector &y) const
{ preconditioner_.solve(x, y); }

/**********************************************************************************/
VectorValues buildVectorValues(const Vector &v,
                               const Ordering &ordering,
                               const map<Key, size_t>  & dimensions) {
  VectorValues result;

  DenseIndex offset = 0;
  for ( size_t i = 0 ; i < ordering.size() ; ++i ) {
    const Key &key = ordering[i];
    map<Key, size_t>::const_iterator it = dimensions.find(key);
    if ( it == dimensions.end() ) {
      throw invalid_argument("buildVectorValues: inconsistent ordering and dimensions");
    }
    const size_t dim = it->second;
    result.insert(key, v.segment(offset, dim));
    offset += dim;
  }

  return result;
}

/**********************************************************************************/
VectorValues buildVectorValues(const Vector &v, const KeyInfo &keyInfo) {
  VectorValues result;
  BOOST_FOREACH ( const KeyInfo::value_type &item, keyInfo ) {
    result.insert(item.first, v.segment(item.second.colstart(), item.second.dim()));
  }
  return result;
}

}


#include <Eigen/Dense>
#include <vector>
#include <fstream>
#include <iostream>
#include <cmath>

#include "../include/KF.h"
#include "../include/dsho.h"
#include "../include/matern32.h"
#include "../include/ndsho.h"

using namespace Eigen;
using namespace std;

// function to read a space separated file

std::vector<double> load_csv (const std::string & path) {
    std::ifstream indata;
    indata.open(path);
    std::string line;
    std::vector<double> values;
    uint rows = 0;
    while (std::getline(indata, line)) {
        std::stringstream lineStream(line);
        std::string cell;
        while (std::getline(lineStream, cell, ' ')) {
            //cout << std::stod(cell) << endl;
            values.push_back(std::stod(cell));
        }
        ++rows;
    }
    return values;
}

int main() {

	// read file with two component damped simple harmonic oscillator simulated data
	//std::vector<double> values = load_csv("two_comp_dsho.txt");
	//cout << values.size() << endl;

	// map data to VectorXds
	//VectorXd times = Map<VectorXd, 0, InnerStride<2> > (values.data(), 10000);
	//VectorXd yi = Map<VectorXd, 0, InnerStride<2> > (values.data()+1, 10000);

  int vecsize = 10000;
	// map data to VectorXds
	VectorXd yi(vecsize);
  VectorXd times = Eigen::VectorXd::Random(vecsize);
  times.array() += 1.0;
  times.array() *= (100*0.5);
  std::sort(times.data(), times.data() + times.size());

	// this is the observational error vector
	VectorXd yerr = VectorXd::Ones(yi.size());
	yerr.array() *= 0.05;

	VectorXd y_sim;

	double varf_list[2] = {1.0, 0.1*0.1};
	for (int k=0; k<2; k++){
		for(int j=0; j < 6; j++) {
			double varf = varf_list[k];
			//auto const seed = std::random_device()();
			int seed = 123;
			std::mt19937 rng(seed);
			cout << seed << endl;
			double lambda = std::pow(2,j);

			// Simulate a Matern 3/2 model
			gpstate::matern32::Matern32Solver m32(times,yi,yerr, lambda, varf);
			m32.simulate_Matern32(y_sim, rng);

			double mean = y_sim.mean();
			Eigen::VectorXd tmp = y_sim.array()-mean;
			double variance  = tmp.dot(tmp) / y_sim.rows();
			std::cout << "var:" << variance << " " << std::to_string(k*6 + j+1) << std::endl;

			// write simulated vector to file GPtest.txt
			std::ofstream file("GPtest" + std::to_string(k*6 + j+1) + "_m32.txt");
			assert(file.is_open());
			file << "# Matern 3/2 simulated light curve" << endl;
			file << "# input parameters: " << endl;
			file << "# omega: " << lambda << endl;
			file << "# varf: " << varf  << endl;
			file << "#time y_sim" << endl;
			for(int i=0; i<times.rows(); i++)
				file <<  times(i) <<" "<< y_sim(i) << endl;

		}
	}

}


/**
 * @file envelope_model.cc
 * Reverberation envelope time series for a single combination of
 * receiver azimuth, source beam number, receiver beam number.
 */
#include <boost/foreach.hpp>
#include <boost/numeric/ublas/vector_proxy.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <usml/eigenverb/envelope_model.h>

using namespace usml::eigenverb;

//#define DEBUG_ENVELOPE

/**
 * Reserve the memory used to store the results of this calculation.
 */
envelope_model::envelope_model(
	const seq_vector* envelope_freq,
	size_t src_freq_first,
	const seq_vector* travel_time,
	double initial_time,
	double pulse_length,
	double threshold
) :
	_envelope_freq(envelope_freq),
	_src_freq_first(src_freq_first),
	_travel_time( travel_time->clone() ),
	_initial_time(initial_time),
	_pulse_length(pulse_length),
	_threshold(threshold),
	_level(travel_time->size()),
	_power(envelope_freq->size()),
	_duration(envelope_freq->size()),
	_intensity(envelope_freq->size(), travel_time->size())
{
}

/**
 * Reserve the memory used to store the results of this calculation.
 */
envelope_model::~envelope_model() {
	delete _travel_time ;
}

/**
 * Adds a single combination of source and receiver eigenverbs
 * to this time series.
 */
bool envelope_model::compute_intensity(
		const eigenverb& src_verb, const eigenverb& rcv_verb,
		const vector<double>& scatter, double xs2, double ys2 )
{
	bool ok = compute_overlap( src_verb, rcv_verb, scatter, xs2, ys2 );
	if ( !ok ) return false ;

	compute_time_series( src_verb.time, rcv_verb.time ) ;
	return true ;
}

/**
 * Compute the total power of the overlap between two eigenverbs.
 */
bool envelope_model::compute_overlap(
	const eigenverb& src_verb, const eigenverb& rcv_verb,
	const vector<double>& scatter, double xs2, double ys2 )
{
	#ifdef DEBUG_ENVELOPE
		cout << "wave_queue::compute_overlap() " << endl
			<< "\txs2=" << xs2
			<< " ys2=" << ys2
			<< " scatter=" << scatter << endl
			<< "\tsrc_verb"
			<< " t=" << src_verb.time
			<< " de=" << to_degrees(src_verb.source_de)
			<< " az=" << to_degrees(src_verb.source_az)
			<< " direction=" << to_degrees(src_verb.direction)
			<< " grazing=" << to_degrees(src_verb.grazing) << endl
			<< "\tpower=" << 10.0 * log10(src_verb.power)
			<< " length=" << sqrt(src_verb.length2)
			<< " width=" << sqrt(src_verb.width2) << endl
			<< "\tsurface=" << src_verb.surface << " bottom=" << src_verb.bottom
			<< " caustic=" << src_verb.caustic << endl
			<< "\trcv_verb"
			<< " t=" << rcv_verb.time
			<< " de=" << to_degrees(rcv_verb.source_de)
			<< " az=" << to_degrees(rcv_verb.source_az)
			<< " direction=" << to_degrees(rcv_verb.direction)
			<< " grazing=" << to_degrees(rcv_verb.grazing) << endl
			<< "\tpower=" << 10.0 * log10(rcv_verb.power)
			<< " length=" << sqrt(rcv_verb.length2)
			<< " width=" << sqrt(rcv_verb.width2) << endl
			<< "\tsurface=" << rcv_verb.surface << " bottom=" << rcv_verb.bottom
			<< " caustic=" << rcv_verb.caustic << endl;
	#endif

	// determine the relative tilt between the projected Gaussians

	const double alpha = src_verb.direction - rcv_verb.direction;
	const double cos2alpha = cos(2.0 * alpha);
	const double sin2alpha = sin(2.0 * alpha);

	// define subset of frequency dependent terms in source
    // Although the use of const_cast<> allows us to ignore the read-only
    // nature of src_verb, we are *very careful* to not write anything to it.

	range window( _src_freq_first, _src_freq_first + _envelope_freq->size() ) ;
	eigenverb& verb = const_cast<eigenverb&>( src_verb ) ;
	const vector_range< vector<double> > src_verb_power( verb.power, window ) ;

    // compute commonly used terms in the intersection of the Gaussian profiles

	const double src_sum = src_verb.length2 + src_verb.width2 ;
	const double src_diff = src_verb.length2 - src_verb.width2 ;
	const double src_prod = src_verb.length2 * src_verb.width2 ;

	const double rcv_sum = rcv_verb.length2 + rcv_verb.width2 ;
	const double rcv_diff = rcv_verb.length2 - rcv_verb.width2 ;
	const double rcv_prod = rcv_verb.length2 * rcv_verb.width2 ;

    // compute the scaling of the exponential
    // equations (26) and (28) from the paper

    double det_sr = 0.5 * ( 2.0 * ( src_prod + rcv_prod )
    		+ ( src_sum * rcv_sum ) - ( src_diff * rcv_diff ) * cos2alpha ) ;
    noalias(_power) = 0.25 * 0.5 * _pulse_length
    		* src_verb_power * rcv_verb.power * scatter ;

    // compute the power of the exponential
    // equation (28) from the paper

    const double new_prod = src_diff * cos2alpha ;
    const double kappa = -0.25 * (
  		  xs2 * ( src_sum + new_prod + 2.0 * rcv_verb.length2 )
		+ ys2 * ( src_sum - new_prod + 2.0 * rcv_verb.width2 )
		- 2.0 * sqrt( xs2 * ys2 ) * src_diff * sin2alpha )
		/ det_sr ;
	#ifdef DEBUG_ENVELOPE
		cout << "\tsrc_verb_power=" << src_verb_power
			 << " rcv_verb.power=" << rcv_verb.power << endl
			 << "\tdet_sr=" << det_sr
			 << " kappa=" << kappa
			 << " power=" << (10.0*log10(_power)) << endl ;
	#endif
	_power *= exp( kappa ) / sqrt( det_sr ) ;

    // compute the square of the duration of the overlap
    // equation (41) from the paper

    det_sr = det_sr / ( src_prod * rcv_prod ) ;
	_duration = 0.5 * (
			( 1.0 / src_verb.width2 + 1.0 / src_verb.length2 )
			+ ( 1.0 / src_verb.width2 - 1.0 / src_verb.length2 ) * cos2alpha
			+ 2.0 / rcv_verb.width2
			) / det_sr ;

	// combine duration of the overlap with pulse length
	// equation (33) from the paper

	const double factor = cos( rcv_verb.grazing ) / rcv_verb.sound_speed ;
	_duration = 0.5 * sqrt( _pulse_length * _pulse_length
			+ factor * factor * _duration ) ;
	#ifdef DEBUG_ENVELOPE
		cout << "\tcontribution"
			<< " duration=" << _duration
			<< " power=" << (10.0*log10(_power)) << endl ;
	#endif

	// check threshold to avoid calculations for weak signals

	BOOST_FOREACH( double level, _power ) {
		if ( level / _duration > _threshold ) return true ;
	}
	return false ;
}

/**
 * Computes Gaussian time series contribution given delay, duration, and
 * total power.
 */
void envelope_model::compute_time_series(
		double src_verb_time, double rcv_verb_time )
{
	_intensity.clear() ;

	for ( size_t f = 0 ;f < _envelope_freq->size(); ++f ) {

		// compute the peak time and intensity

		const double delay = src_verb_time + rcv_verb_time + _duration - _initial_time;
		const double scale = _power[f] / _duration;

		// compute Gaussian intensity as a function of time

		matrix_row< matrix<double> > intensity( _intensity, f ) ;

		// use uBLAS vector proxies to only compute the portion of the
		// time series within +/- five (5) times the duration
		// speeds up the computation by over a factor of 3

		_level.clear() ;
		size_t first = _travel_time->find_index(delay - 5.0 * _duration);
		size_t last = _travel_time->find_index(delay + 5.0 * _duration) + 1;
		range window(first, last);
		vector_range< seq_vector > time(*_travel_time, window);
		vector_range< vector<double> > level(_level, window);
		level = scale * exp(-0.5 * abs2((time - delay) / _duration)) ;
		intensity = _level ;
	}
}


#include "pch.h"
#include "StreamReader.h"
#include "StreamWriter.h"
#include <GLTFSDK/Deserialize.h>
#include <GLTFSDK/Serialize.h>
#include <GLTFSDK/GLTFResourceWriter.h>
#include <GLTFSDK/GLTFResourceReader.h>
#include <GLTFSDK/BufferBuilder.h>
#include <Eigen/Dense>
#include <iostream>

using namespace std;
using namespace Microsoft::glTF;
using namespace Eigen;

int main()
{
    auto sourcePath = experimental::filesystem::current_path() / "Data";

    string filename = "buggy.gltf";
    //string filename = "Node_Attribute_08.gltf";
    string TargetFilename = "MergedMesh.gltf";

    // Load the glTF model
    auto streamReader = make_unique<StreamReader>(sourcePath);
    auto gltfStream = streamReader->GetInputStream(filename);
    auto gltfResourceReader = make_unique<GLTFResourceReader>(move(streamReader));

    stringstream manifestStream;
    manifestStream << gltfStream->rdbuf();
    string manifestSource = manifestStream.str();

    Document documentSource;
    try
    {
        documentSource = Deserialize(manifestSource);
    }
    catch (const GLTFException& ex)
    {
        stringstream ss;
        ss << "Microsoft::glTF::Deserialize failed: ";
        ss << ex.what();
        throw runtime_error(ss.str());
    }

    cout << "glTF loaded!\n";

    auto streamWriter = make_unique<StreamWriter>(sourcePath);
    auto resourceWriter = make_unique<GLTFResourceWriter>(move(streamWriter));

    Document documentTarget;
    BufferBuilder bufferBuilder(move(resourceWriter));

    const char* bufferId = "MergedMesh";
    bufferBuilder.AddBuffer(bufferId);

    // Build a mutable structure to track an manipulate the node transforms.
    // The index is the node ID, while the matrix is the transformation matrix.
    vector<Matrix4f> modifiedMatrices;
    for (const auto& nodeMain : documentSource.nodes.Elements())
    {
        Matrix4f matrix;
        int index = 0;
        for (auto it = nodeMain.matrix.values.begin(); it != nodeMain.matrix.values.end(); it++)
        {
            int row = index / 4;
            int col = index % 4;
            matrix((row), (col)) = *it;
            index++;
        }

        modifiedMatrices.push_back(matrix);
    }
    cout << "Transforms loaded!\n";

    // Flatten the matrix transforms, so every node's matrix also includes the transforms of its parents.
    int index = 0;
    for (const auto& node : documentSource.nodes.Elements())
    {
        for (auto it = node.children.begin(); it != node.children.end(); it++)
        {
            modifiedMatrices[stoi(*it)] = modifiedMatrices[stoi(*it)] * modifiedMatrices[index];
        }
        index++;
    }
    cout << "Transforms flattened!\n";

    // Material
    Material material;
    material.emissiveFactor = Color3(0.0f, 0.0f, 0.0f);
    material.metallicRoughness.baseColorFactor = Color4(1.0f, 1.0f, 1.0f, 1.0f);
    material.metallicRoughness.metallicFactor = 0.0f;
    auto materialId = documentTarget.materials.Append(move(material), AppendIdPolicy::GenerateOnEmpty).id;

    // Rebuild the meshes as a single mesh.
    Mesh meshTarget;
    string nodeWithCameraId = "";
    for (const auto& node : documentSource.nodes.Elements())
    {
        // Note that this means a mesh can be called more than once, if it is instanced by more than one node.
        if (node.meshId != "")
        {
            auto mesh = documentSource.meshes.Get(node.meshId);
            for (const auto& meshPrimitive : mesh.primitives)
            {
                // Normal
                string accessorIdNormal = "";
                if (meshPrimitive.TryGetAttributeAccessorId(ACCESSOR_NORMAL, accessorIdNormal))
                {
                    // Load normals
                    const Accessor& accessorNormal = documentSource.accessors.Get(accessorIdNormal);
                    const auto dataNormal = gltfResourceReader->ReadBinaryData<float>(documentSource, accessorNormal);

                    // Save positions to buffer, bufferview, and accessor.
                    bufferBuilder.AddBufferView(BufferViewTarget::ARRAY_BUFFER);
                    vector<float> minValues(3U, numeric_limits<float>::max());
                    vector<float> maxValues(3U, numeric_limits<float>::lowest());
                    const size_t countNormal = dataNormal.size();
                    for (size_t i = 0U, j = 0U; i < countNormal; ++i, j = (i % 3U))
                    {
                        minValues[j] = min(dataNormal[i], minValues[j]);
                        maxValues[j] = max(dataNormal[i], maxValues[j]);
                    }
                    accessorIdNormal = bufferBuilder.AddAccessor(dataNormal, { accessorNormal.type, accessorNormal.componentType, false, move(minValues), move(maxValues) }).id;
                }

                // Position
                string accessorIdPositon = "";
                if (meshPrimitive.TryGetAttributeAccessorId(ACCESSOR_POSITION, accessorIdPositon))
                {
                    // Load positions
                    const Accessor& accessorPosition = documentSource.accessors.Get(accessorIdPositon);
                    const auto dataPosition = gltfResourceReader->ReadBinaryData<float>(documentSource, accessorPosition);
                    vector<float> dataNewPosition;
                    // Transform positions
                    for (auto it = dataPosition.begin(); it != dataPosition.end(); it++)
                    {
                        // Rebuild the positions as a Vector4.
                        auto x = *it;
                        it++;
                        auto y = *it;
                        it++;
                        auto z = *it;
                        Vector4f vec(x, y, z, 1);

                        auto matrix = modifiedMatrices[stoi(node.id)];
                        vec = vec.transpose() * matrix;

                        dataNewPosition.push_back(move(vec.x()));
                        dataNewPosition.push_back(move(vec.y()));
                        dataNewPosition.push_back(move(vec.z()));
                    }

                    // Save positions to buffer, bufferview, and accessor.
                    bufferBuilder.AddBufferView(BufferViewTarget::ARRAY_BUFFER);
                    vector<float> minValues(3U, numeric_limits<float>::max());
                    vector<float> maxValues(3U, numeric_limits<float>::lowest());
                    const size_t countPosition = dataNewPosition.size();
                    for (size_t i = 0U, j = 0U; i < countPosition; ++i, j = (i % 3U))
                    {
                        minValues[j] = min(dataNewPosition[i], minValues[j]);
                        maxValues[j] = max(dataNewPosition[i], maxValues[j]);
                    }
                    accessorIdPositon = bufferBuilder.AddAccessor(dataNewPosition, { accessorPosition.type, accessorPosition.componentType, false, move(minValues), move(maxValues) }).id;
                }

                // Indices
                const Accessor& accessorIndices = documentSource.accessors.Get(meshPrimitive.indicesAccessorId);
                const auto dataIndices = gltfResourceReader->ReadBinaryData<uint16_t>(documentSource, accessorIndices);
                bufferBuilder.AddBufferView(BufferViewTarget::ELEMENT_ARRAY_BUFFER);
                string accessorIdIndices = bufferBuilder.AddAccessor(dataIndices, { accessorIndices.type, accessorIndices.componentType }).id;

                // Mesh Primitive (assemble attributes)
                MeshPrimitive meshPrimitiveTarget;
                meshPrimitiveTarget.indicesAccessorId = move(accessorIdIndices);
                meshPrimitiveTarget.attributes[ACCESSOR_NORMAL] = move(accessorIdNormal);
                meshPrimitiveTarget.attributes[ACCESSOR_POSITION] = move(accessorIdPositon);
                meshPrimitiveTarget.materialId = materialId;
                meshPrimitiveTarget.mode = move(meshPrimitive.mode);
                meshTarget.primitives.push_back(move(meshPrimitiveTarget));
            }
        }
        else if (node.cameraId != "")
        {
            // If a camera is attached to a node, it needs to be preserved.
            // This code won't be called if a mesh is attached to the same node. Multiple cameras would cause issues.
            // Preserves the camera.
            documentTarget.cameras.Append(move(documentSource.cameras.Get(node.cameraId)));

            auto matrix = modifiedMatrices[stoi(node.id)];
            Matrix4 matrixTarget;
            int index = 0;
            for (auto it = matrixTarget.values.begin(); it != matrixTarget.values.end(); it++)
            {
                int row = index / 4;
                int col = index % 4;
                *it = matrix((row), (col));
                index++;
            }

            Node nodeTarget;
            nodeTarget.cameraId = node.cameraId;
            nodeTarget.matrix = move(matrixTarget);
            nodeWithCameraId = documentTarget.nodes.Append(move(nodeTarget), AppendIdPolicy::GenerateOnEmpty).id;
        }
    }

    // Buffer
    bufferBuilder.Output(documentTarget);

    // Mesh
    auto meshId = documentTarget.meshes.Append(move(meshTarget), AppendIdPolicy::GenerateOnEmpty).id;

    // Node
    Node node;
    node.meshId = meshId;
    auto nodeId = documentTarget.nodes.Append(move(node), AppendIdPolicy::GenerateOnEmpty).id;

    // Scene
    Scene scene;
    scene.nodes.push_back(nodeId);
    if (nodeWithCameraId != "")
    {
        scene.nodes.push_back(nodeWithCameraId);
    }
    documentTarget.SetDefaultScene(move(scene), AppendIdPolicy::GenerateOnEmpty);

    cout << "glTF rebuilt!\n";

    // Write the translated model to file.
    string manifestTarget;
    try
    {
        manifestTarget = Serialize(documentTarget, SerializeFlags::Pretty);
    }
    catch (const GLTFException& ex)
    {
        stringstream ss;
        ss << "Microsoft::glTF::Serialize failed: ";
        ss << ex.what();
        throw std::runtime_error(ss.str());
    }

    auto& gltfResourceWriter = bufferBuilder.GetResourceWriter();
    gltfResourceWriter.WriteExternal(TargetFilename, manifestTarget);

    cout << "glTF written to new file!\n";
}


#include <algorithm>
#include <cfloat>
#include <vector>
#include <boost/pointer_cast.hpp>
#include <boost/shared_ptr.hpp>

#include "caffe/layers/distance_weighted_valid_pair_loss_V5_layer.hpp"
#include "caffe/util/math_functions.hpp"

namespace caffe {

template <typename Dtype>
void DistanceWeightedValidPairLossV5Layer<Dtype>::LayerSetUp(
    const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {

  LossLayer<Dtype>::LayerSetUp(bottom, top);
  //auxiliary layer
  LayerParameter contrastive_loss_param(this->layer_param_);
  contrastive_loss_param.set_type("ContrastiveLoss");
  contrastive_loss_layer_ = LayerRegistry<Dtype>::CreateLayer(contrastive_loss_param);

}

template <typename Dtype>
void DistanceWeightedValidPairLossV5Layer<Dtype>::Reshape(
    const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {

  LossLayer<Dtype>::Reshape(bottom, top);
  //initialization of  pair data and pair lables, and auxiliary parameters
  sample_num = bottom[0]->num();
  feature_dim = bottom[0]->channels();
  //Valid construction of pairs
  pair_num = 0;
  pair_index_a.clear();
  pair_index_b.clear();
  pair_score.clear();
  for (int i = 0; i < sample_num - 1; i++){
    for(int j = i + 1; j < sample_num; j++){
      pair_num++;
      pair_index_a.push_back(i);
      pair_index_b.push_back(j);
      pair_score.push_back(Dtype(1.0));
    }
  }
  //
  vector<int> shape = bottom[0]->shape();
  shape[0] = pair_num;
  pair_data_a.Reshape(shape);
  pair_data_b.Reshape(shape);
  pair_label.Reshape(pair_num, 1, 1, 1);

  contrastive_loss_bottom.clear();
  contrastive_loss_bottom.push_back(&pair_data_a);
  contrastive_loss_bottom.push_back(&pair_data_b);
  contrastive_loss_bottom.push_back(&pair_label);

  contrastive_loss_top.clear();
  contrastive_loss_top.push_back(top[0]);

  contrastive_loss_layer_->SetUp(contrastive_loss_bottom, contrastive_loss_top);

  contrastive_propogate.clear();
  contrastive_propogate.push_back(true);
  contrastive_propogate.push_back(true);
  contrastive_propogate.push_back(false);

}

template <typename Dtype>
void DistanceWeightedValidPairLossV5Layer<Dtype>::Forward_cpu(
    const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {

  //set the input data
  for (int i = 0; i < pair_num; i++){
    //The pair label
    if( bottom[1]->cpu_data()[pair_index_a[i]] == bottom[1]->cpu_data()[pair_index_b[i]] ){
      pair_label.mutable_cpu_data()[i] = Dtype(1);
    }
    else{
      pair_label.mutable_cpu_data()[i] = Dtype(0);
    }
    //the pair data
    const Dtype* source_a = bottom[0]->cpu_data() + pair_index_a[i] * feature_dim;
    const Dtype* source_b = bottom[0]->cpu_data() + pair_index_b[i] * feature_dim;
    Dtype* dest_a = pair_data_a.mutable_cpu_data() + i*feature_dim;
    Dtype* dest_b = pair_data_b.mutable_cpu_data() + i*feature_dim;

    caffe_copy(feature_dim, source_a, dest_a);
    caffe_copy(feature_dim, source_b, dest_b);
    //the pair score
    //pair_score[i] = bottom[2]->cpu_data()[pair_index_a[i]] * bottom[2]->cpu_data()[pair_index_b[i]];
    pair_score[i] = std::min(bottom[2]->cpu_data()[pair_index_a[i]] , bottom[2]->cpu_data()[pair_index_b[i]]);
  }
  // The forward pass of contrastive loss.
  contrastive_loss_layer_->Forward(contrastive_loss_bottom, contrastive_loss_top);
}

template <typename Dtype>
void DistanceWeightedValidPairLossV5Layer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
    const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {

  if (propagate_down[0]) {
    // The backward pass of contrastive loss.
    contrastive_loss_layer_->Backward(contrastive_loss_top, contrastive_propogate, contrastive_loss_bottom);
    // set diff to zero
    caffe_set(bottom[0]->count(), Dtype(0), bottom[0]->mutable_cpu_diff());
    //dynamic cast
    shared_ptr<ContrastiveLossLayer<Dtype> > auxiLayer = boost::dynamic_pointer_cast<ContrastiveLossLayer<Dtype> >(contrastive_loss_layer_);
    if (!auxiLayer) {
      throw std::runtime_error("ContrastiveLossLayer dynamic pointer_cast failed");
    }

    //reweighting
    Dtype sum_similar_dw = Dtype(0);
    Dtype sum_dissimilar_dw = Dtype(0);
    Dtype margin = this->layer_param_.contrastive_loss_param().margin();

    for(int i = 0; i < pair_num; i++){
      //distance based weighting
      if( static_cast<int>(pair_label.cpu_data()[i]) ){
        //similar pairs
        Dtype dist = sqrt(auxiLayer->dist_sq_.cpu_data()[i]);

        //transformation to weight
        Dtype sigma = this->layer_param_.gaussian_param().sigma();
        Dtype mean = this->layer_param_.gaussian_param().mean();
        Dtype weight = gaussianfunc(dist, sigma, mean);
        //

        sum_similar_dw += (weight * pair_score[i]);
      }
      else{
        //dissimilar pairs
        Dtype dist = sqrt(auxiLayer->dist_sq_.cpu_data()[i]);
        Dtype m_dist = std::max(Dtype(0), margin - dist);

        sum_dissimilar_dw += (m_dist * pair_score[i]);
      }
    }
    sum_similar_dw += static_cast<Dtype>(1e-12);
    sum_dissimilar_dw += static_cast<Dtype>(1e-12);

    //copy gradients and scale the gradients with normalized score
    for(int i = 0; i < pair_num; i++){
      //separate process for similar pairs and dissimilar pairs
      if( static_cast<int>(pair_label.cpu_data()[i]) ){
        //similar pairs
        Dtype dist = sqrt(auxiLayer->dist_sq_.cpu_data()[i]);

        //transformation to weight
        Dtype sigma = this->layer_param_.gaussian_param().sigma();
        Dtype mean = this->layer_param_.gaussian_param().mean();
        Dtype weight = gaussianfunc(dist, sigma, mean);
        //

        pair_score[i] = 0.5 * (weight * pair_score[i]) / sum_similar_dw;
      }
      else{
        //dissimilar pairs
        Dtype dist = sqrt(auxiLayer->dist_sq_.cpu_data()[i]);
        Dtype m_dist = std::max(Dtype(0), margin - dist);

        pair_score[i] = 0.5 * (m_dist * pair_score[i]) / sum_dissimilar_dw;
      }
      //LOG(INFO) << "pair_score[" << i <<"]: " << pair_score[i];

      const Dtype* source_a = pair_data_a.cpu_diff() + i * feature_dim;
      const Dtype* source_b = pair_data_b.cpu_diff() + i * feature_dim;
      Dtype* dest_a = bottom[0]->mutable_cpu_diff() + pair_index_a[i] * feature_dim;
      Dtype* dest_b = bottom[0]->mutable_cpu_diff() + pair_index_b[i] * feature_dim;
      //dest[ind] = pair_score[i] * source[ind] + dest[ind]
      caffe_axpy(feature_dim, pair_score[i], source_a, dest_a);
      caffe_axpy(feature_dim, pair_score[i], source_b, dest_b);
    }

  }
  if (propagate_down[1] || propagate_down[2]) {
    LOG(FATAL) << this->type()
               << " Layer cannot backpropagate to label and score inputs.";
  }

}

#ifdef CPU_ONLY
STUB_GPU(DistanceWeightedValidPairLossV5Layer);
#endif

INSTANTIATE_CLASS(DistanceWeightedValidPairLossV5Layer);
REGISTER_LAYER_CLASS(DistanceWeightedValidPairLossV5);

}  // namespace caffe


// -*- C++ -*-
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//                                   Jiao Lin
//                      California Institute of Technology
//                      (C) 2007-2010 All Rights Reserved
//
// {LicenseText}
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

#include <sstream>
#include <boost/python.hpp>
#include "mccomponents/math/rootfinding.h"
#include "mccomponents/kernels/sample/phonon/TargetCone.h"
#include "mccomponents/kernels/sample/phonon/AbstractDispersion_3D.h"
#include "mccomponents/kernels/sample/phonon/AbstractDebyeWallerFactor.h"
#include "mccomponents/kernels/sample/phonon/CoherentInelastic_SingleXtal.h"
#include "mccomponents/boostpython_binding/wrap_kernel.h"

namespace wrap_mccomponents {

  void wrap_Phonon_CoherentInelastic_SingleXtal_kernel()
  {
    using namespace boost::python;
    using namespace mccomponents::boostpython_binding;

    typedef mccomponents::kernels::phonon::CoherentInelastic_SingleXtal w_t;

    kernel_wrapper<w_t>::wrap
      ( "Phonon_CoherentInelastic_SingleXtal_kernel",
	init<
	const w_t::dispersion_t &,
	const w_t::atoms_t &,
	w_t::float_t, // uc vol
	w_t::dwcalculator_t & ,
	w_t::float_t, // temperature
	w_t::float_t, // deltaV_Jacobi
	const w_t::rootsfinder_t &,
	const w_t::target_region_t &,
	w_t::float_t  // epsilon
	> ()
	[with_custodian_and_ward<1,2,
	 with_custodian_and_ward<1,3,
	 with_custodian_and_ward<1,5,
	 with_custodian_and_ward<1,8,
	 with_custodian_and_ward<1,9> > > > > () ]
	)
      ;

  }

}

// version
// $Id$

// End of file


#include <iostream>
#include <iomanip>
#include <map>
#include <fstream>

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

#include "../Core/Utils/BoundingBox.h"

using namespace cv;
using namespace std;

map<unsigned char, BoundingBox> computeBoundingBoxes(Mat image) {
    if(image.type() != CV_8UC1 || !image.isContinuous()){
        std::cerr << "Invalid input format" << std::endl;
    }
    map<unsigned char, BoundingBox> result;
    for(int r = 0; r < image.rows; r++){
        for(int c = 0; c < image.cols; c++){
            unsigned char v = image.at<unsigned char>(r,c);
            BoundingBox& bb = result[v];
            bb.include(r,c);
        }
    }
    return result;
}

int main(int argc, char * argv[]){
    if(argc < 2) {
        cerr << "Missing parameters." << endl;
        return 1;
    }
    const int index_width = 4;
    for(int i = 0; ; i++){

        // Input
        stringstream path;
        path << argv[1] << "/" << setw(index_width) << setfill('0') << i;
        cv::Mat labels = imread(path.str() + ".png");
        if(labels.total()==0){
            cout << "Stopping at index " << i << "(path not found: " << path.str() << ")" << endl;
            return 0;
        }

        // Only keep single channel image
        if(labels.type() == CV_8UC3){
            Mat t[3];
            split(labels,t);
            labels = t[0];
        }

        // Get bbs
        map<unsigned char, BoundingBox> boxes = computeBoundingBoxes(labels);
        auto l0 = boxes.find(0);
        if(l0 != boxes.end()) boxes.erase(l0);

        // Visualise boxes
        Mat vis;
        equalizeHist(labels, vis);
        for (auto& kv : boxes) kv.second.draw(vis, Scalar(255));
        imshow("boxes", vis);
        waitKey(1);

        // Write output txt files
        ofstream tf(path.str() + ".txt");
        if(argc > 2){
            for(int p=2; p<argc; p++){
                tf << argv[p] << " ";
            }
            tf << "\n";
        }
        for (auto& kv : boxes) {
            BoundingBox& bb = kv.second;
            tf << bb.top << " " << bb.left << " " << bb.bottom << " " << bb.right << endl;
        }
        tf.close();
    }
}


//==================================================================================================
/*!
  @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_MAXIMUM_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_MAXIMUM_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{
  /*!
    @ingroup group-reduction
   This function object returns the greatest element of the argument

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

    @see minimum

    @par Example:

      @snippet maximum.cpp maximum

    @par Possible output:

      @snippet maximum.txt maximum

  **/
  scalar_of_t<Value> maximum(Value const& x);
} }
#endif

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

#endif