#include <iostream>
#include <fstream>
#include <sstream>

#include <boost/program_options.hpp>

#include <itkImage.h>
#include <itkImageFileReader.h>
#include <itkImageFileWriter.h>
#include <itkImageRegionIterator.h>
#include <itkImageRegionConstIterator.h>
#include <itkMaskImageFilter.h>
#include <itkBinaryImageToLabelMapFilter.h>
#include <itkLabelStatisticsImageFilter.h>

int main(int argc,char **argv)
{
    std::string     labelTableFile, labelImageFile, outImageFile, fileToModify;
    bool modifyExisting;

    boost::program_options::options_description optionsDescription("create an image from a label image and a lable table.");
    optionsDescription.add_options()
	("help,h", "display help message")
	("table,t", boost::program_options::value< std::string >(&labelTableFile), "label table with two columns: labels and corresponding pixel values to be assigned to the output image.")
	("labelImage,l", boost::program_options::value< std::string >(&labelImageFile), "input label image file from which the regions matching the labels in the table will be extracted"
	 "New pixel values will be put to only these extracted regions.")
	("outputImage,o",boost::program_options::value< std::string >(&outImageFile), "Output image")
	("fileToModify,m",boost::program_options::value< std::string >(&fileToModify), " The image that is to be modified to produce output image."
	 " If not provided, creates a new image with zero value everywhere"
	 " and then updates the extracted regions.")
	;

    boost::program_options::variables_map options;
    boost::program_options::store(boost::program_options::parse_command_line(argc,argv,optionsDescription),options);
    boost::program_options::notify(options);

//If help is asked!
    if(options.count("help")) {
	std::cout<<optionsDescription<<std::endl;
	return EXIT_SUCCESS;
    }

//Confirm all the options required are given.
    if(!options.count("table") || !options.count("labelImage")
       || !options.count("outputImage")
	) {
	std::cerr<<"invalid options! run with --help or -h to see the proper options."<<std::endl;
	return EXIT_FAILURE;
    }

// Check if file to be modified is given or not.
    if(!options.count("fileToModify"))
	modifyExisting = false;
    else
	modifyExisting = true;
    typedef itk::Image<int, 3> LabelImageType;
    typedef itk::Image<float, 3> OutImageType;

// Open label table file for reading
    std::ifstream labelTable(labelTableFile.c_str(),std::ios::in);
    if (!labelTable.is_open()) {
	std::cerr<<"could not open file: "<<labelTableFile<<std::endl;
	return EXIT_FAILURE;
    }

// Read first line of the table and check if it is in the desired format i.e. labels newValues
    {
	std::string line;
	std::getline(labelTable, line);
	std::istringstream is(line);
	std::string checkString;
	is >> checkString;
	if(checkString.compare("labels")) {
	    std::cout<<"incorrect table format. 1st col- labels; 2nd col- newValues"<<std::endl<<"The first word must be: labels"<<std::endl;
	    return EXIT_FAILURE;
	}
	is >> checkString;
	if (checkString.compare("newValues")) {
	    std::cout<<"incorrect table format. 1st col- labels; 2nd col- newValues"<<std::endl<<"The second word must be: newValues"<<std::endl;
	    return EXIT_FAILURE;
	}
    }

// From the second line, read the values and put it into a map.
    typedef std::map< LabelImageType::PixelType, OutImageType::PixelType > LabelWithValueType;
    typedef std::pair< LabelImageType::PixelType, OutImageType::PixelType > LabelValuePairType;
    typedef LabelWithValueType::iterator LabelWithValueIteratorType;

    LabelWithValueType labelWithValue;
    while(!labelTable.eof()) {
	std::string line;
	std::getline(labelTable, line);
	std::istringstream is(line);
	LabelImageType::PixelType label;
	is >> label;
	OutImageType::PixelType outValue;
	is >> outValue;
	labelWithValue.insert(LabelValuePairType(label, outValue));
    }
    labelTable.close();

    for (LabelWithValueIteratorType labelWithValueIt = labelWithValue.begin(); labelWithValueIt != labelWithValue.end(); ++labelWithValueIt) {
	std::cout<<labelWithValueIt->first<<" ** "<<labelWithValueIt->second<<std::endl;
    }

// Read label image
    typedef itk::ImageFileReader< LabelImageType > LabelImageReaderType;
    LabelImageReaderType::Pointer labelImageReader = LabelImageReaderType::New();
    labelImageReader->SetFileName(labelImageFile);
    labelImageReader->Update();
    LabelImageType::Pointer labelImage = labelImageReader->GetOutput();

// Output image.
    OutImageType::Pointer outImage = OutImageType::New();
    if (modifyExisting) {
	typedef itk::ImageFileReader< OutImageType > OutImageReaderType;
	OutImageReaderType::Pointer outImageReader = OutImageReaderType::New();
	outImageReader->SetFileName(fileToModify);
	outImageReader->Update();
	outImage = outImageReader->GetOutput();
    } else { // If not modifying create new image of the same size as label image and fill it with zero.
	outImage->SetRegions(labelImage->GetLargestPossibleRegion());
	outImage->CopyInformation(labelImage);
	outImage->Allocate();
	outImage->FillBuffer(0.);
    }

    //L:labelImage A:outImage  out(I):out value for the corresponding label I read from the table.
    //if ( L(x) != 0 ) then A(x) = out(L(x)) or just A(x) = out(L(x)) without if; when you want for label 0 also.
    typedef itk::ImageRegionConstIterator< LabelImageType > LabelIteratorType;
    typedef itk::ImageRegionIterator< OutImageType > ValueIteratorType;
    LabelIteratorType labelIt(labelImage, labelImage->GetLargestPossibleRegion());
    ValueIteratorType outIt(outImage, outImage->GetLargestPossibleRegion());
    labelIt.GoToBegin();
    outIt.GoToBegin();
    while(!labelIt.IsAtEnd())
    {
	LabelImageType::PixelType currLabel = labelIt.Get();
	//std::cout<<"LabelImage[x]="<<currLabel<<std::endl;
	// if(currLabel) //for all non-zero labels
	// {
	for (LabelWithValueIteratorType labelWithValueIt = labelWithValue.begin(); labelWithValueIt != labelWithValue.end(); ++labelWithValueIt)
	{
	    //std::cout<<"LabelImage[x]="<<currLabel<<",  labelTable[current]="<<labelWithValueIt->first<<std::endl;
	    if (currLabel == labelWithValueIt->first)
	    {
		//std::cout<<"LabelImage[x]="<<currLabel<<",  labelTable[current]="<<labelWithValueIt->first<<std::endl;
		outIt.Set(labelWithValueIt->second);
		break;
	    }
	}
	//}
	++labelIt;
	++outIt;
    }

// //Label Statistics Filter to write to only those labels which are present in the input label image.
// typedef itk::BinaryImageToLabelMapFilter< LabelImageType > ImageToLabelMapFilterType;
// ImageToLabelMapFilterType::Pointer labelMap = ImageToLabelMapFilterType::New();
// labelMap->SetInput(labelImage);
// labelMap->Update();
// std::cout<<"There are originally "<< labelMap->GetOutput()->GetNumberOfLabelObjects() << " objects"<<std::endl;

// //Label Statistics Filter to compute mean of each regions:
// typedef itk::LabelStatisticsImageFilter< OutImageType, LabelImageType > LabelStatsFilterType;
// LabelStatsFilterType::Pointer labelStats = LabelStatsFilterType::New();
// labelStats->SetLabelInput( labelImage);
// labelStats->SetInput(outImage);
// labelStats->Update();
// std::cout << "Number of labels in the input label image: " << labelStats->GetNumberOfLabels() << std::endl;

// typedef itk::MaskImageFilter<OutImageType, LabelImageType, OutImageType> MaskImageFilterType;
// MaskImageFilterType::Pointer maskImageFilter = MaskImageFilterType::New();
// maskImageFilter->SetMaskImage(labelImage);
// for (LabelWithValueIteratorType labelWithValueIt = labelWithValue.begin(); labelWithValueIt != labelWithValue.end(); ++labelWithValueIt)
// {
// 	LabelImageType::PixelType label = labelWithValueIt->first;
// 	OutImageType::PixelType val = labelWithValueIt->second;
// 	std::cout<<"Testing for label "<<label<<" with value "<<val<<std::endl;
// 	if(labelStats->HasLabel(label))//if(labelMap->GetOutput()->HasLabel(label))
// 	{
// 	    maskImageFilter->SetInput(outImage);
// 	    //if (pixel_from_mask_image != masking_value)     pixel_output_image = pixel_input_image
// 	    //else pixel_output_image = outside_value
// 	    maskImageFilter->SetMaskingValue(label);
// 	    maskImageFilter->SetOutsideValue(val);
// 	    maskImageFilter->Update();
// 	    outImage = maskImageFilter->GetOutput();
// 	    outImage->DisconnectPipeline();
// 	}
// 	else {
// 	    std::cout<<" Label Id "<<label<<" not present in the label image"<<std::endl;
// 	}
// }

// Write the out output.
    typedef itk::ImageFileWriter< OutImageType > ValueImageWriterType;
    ValueImageWriterType::Pointer outImageWriter = ValueImageWriterType::New();
    outImageWriter->SetFileName(outImageFile);
    outImageWriter->SetInput(outImage);
    outImageWriter->Update();
    return EXIT_SUCCESS;
}


#ifndef STAN_MATH_PRIM_MAT_PROB_CATEGORICAL_LOG_HPP
#define STAN_MATH_PRIM_MAT_PROB_CATEGORICAL_LOG_HPP

#include <stan/math/prim/mat/fun/Eigen.hpp>
#include <stan/math/prim/mat/prob/categorical_lpmf.hpp>
#include <boost/math/tools/promotion.hpp>
#include <vector>

namespace stan {
  namespace math {

    /**
     * @deprecated use categorical_lpmf
     */
    template <bool propto, typename T_prob>
    typename boost::math::tools::promote_args<T_prob>::type
    categorical_log(int n,
                    const Eigen::Matrix<T_prob, Eigen::Dynamic, 1>& theta) {
      return categorical_lpmf<propto, T_prob>(n, theta);
    }

    /**
     * @deprecated use categorical_lpmf
     */
    template <typename T_prob>
    typename boost::math::tools::promote_args<T_prob>::type
    categorical_log(const typename math::index_type<Eigen::VectorXd>::type n,
                    const Eigen::Matrix<T_prob, Eigen::Dynamic, 1>& theta) {
      return categorical_lpmf<T_prob>(n, theta);
    }

    /**
     * @deprecated use categorical_lpmf
     */
    template <bool propto, typename T_prob>
    typename boost::math::tools::promote_args<T_prob>::type
    categorical_log(const std::vector<int>& ns,
                    const Eigen::Matrix<T_prob, Eigen::Dynamic, 1>& theta) {
      return categorical_lpmf<propto, T_prob>(ns, theta);
    }

    /**
     * @deprecated use categorical_lpmf
     */
    template <typename T_prob>
    inline
    typename boost::math::tools::promote_args<T_prob>::type
    categorical_log(const std::vector<int>& ns,
                    const Eigen::Matrix<T_prob, Eigen::Dynamic, 1>& theta) {
      return categorical_lpmf<false>(ns, theta);
    }

  }
}
#endif


/*
 * Copyright 2009-2018 The VOTCA Development Team (http://www.votca.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#define BOOST_TEST_MAIN

#define BOOST_TEST_MODULE test_hdf5
#include <cassert>
#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <votca/xtp/orbitals.h>
#include <votca/xtp/qmatom.h>

BOOST_AUTO_TEST_SUITE(test_hdf5)
using namespace votca::xtp;
BOOST_AUTO_TEST_CASE(checkpoint_file_test) {

    int basisSetSize = 17;
    int occupiedLevels = 4;
    int unoccupiedLevels = 13;
    int numElectrons = 12;

    Eigen::VectorXd moeTest = Eigen::VectorXd::Random(17);
    Eigen::MatrixXd mocTest = Eigen::MatrixXd::Random(17,17);

    std::vector<QMAtom> atomsTest(1000);

    double qmEnergy = -2.1025e-3;

    std::string qmPackage = "NOPE";
    double selfEnergy = 3.14159e23;

    std::string dftBasis = "AWESOME basis*,, 2/.8";
    std::string auxBasis = "cos(theta) = pretty okay basis";

    int rpaMin = '?';
    int rpaMax = 1e3;

    unsigned int bseVmin = -6019386;
    unsigned int bseVmax = 1092581;

    unsigned int bseCmin = 2718L;
    unsigned int bseCmax = 42;

    double scaHfx = 3.14159;

    std::string bseType = "A+";

    Eigen::MatrixXd vxcTest = Eigen::MatrixXd::Random(200,200);
    std::string someECP = "aye aye Cap'n";

    Eigen::MatrixXd QPpertEnergiesTest = Eigen::MatrixXd::Random(31, 42);
    Eigen::MatrixXd QPdiagEnergiesTest = Eigen::VectorXd::Random(21);
    Eigen::MatrixXd QPdiagCoefficientsTest = Eigen::MatrixXd::Identity(42, 42);

    MatrixXfd eh_dTest = MatrixXfd::Random(32, 290);
    MatrixXfd eh_xTest = MatrixXfd::Random(3, 22);
    VectorXfd BSESingletEnergiesTest = VectorXfd::Random(25);
    MatrixXfd BSESingletCoefficientsTest = MatrixXfd::Random(25, 38);
    MatrixXfd BSESingletCoefficientsARTest = MatrixXfd::Random(42, 42);

    VectorXfd BSETripletEnergiesTest = VectorXfd::Random(33);
    MatrixXfd BSETripletCoefficientsTest = MatrixXfd::Random(33,31);

    std::vector <votca::tools::vec> transitionDipolesTest;
    for (size_t i =0; i < 1000; ++i){
        transitionDipolesTest.push_back(votca::tools::vec(1,2,3));
    }

    {
        // Write orbitals
        Orbitals orbWrite;

        orbWrite.setBasisSetSize(basisSetSize);
        orbWrite.setNumberOfLevels(occupiedLevels, unoccupiedLevels);
        orbWrite.setNumberOfElectrons(numElectrons);
        orbWrite.MOEnergies() = moeTest;
        orbWrite.MOCoefficients() = mocTest;

        for (auto const& qma:atomsTest){
            orbWrite.AddAtom(qma);
        }

        orbWrite.setQMEnergy(qmEnergy);
        orbWrite.setQMpackage(qmPackage);
        orbWrite.setSelfEnergy(selfEnergy);
        orbWrite.setDFTbasis(dftBasis);
        orbWrite.setAuxbasis(auxBasis);
        orbWrite.setRPAindices(rpaMin, rpaMax);
        // no need to write qpmin, qpmax
        orbWrite.setBSEindices(bseVmin, bseVmax, bseCmin, bseCmax, 3);
        orbWrite.setScaHFX(scaHfx);
        orbWrite.setBSEtype(bseType);
        orbWrite.setECP(someECP);
        orbWrite.QPpertEnergies() = QPpertEnergiesTest;
        orbWrite.QPdiagEnergies() = QPdiagEnergiesTest;
        orbWrite.QPdiagCoefficients() = QPdiagCoefficientsTest;
        orbWrite.eh_t() = eh_dTest;
        orbWrite.eh_s() = eh_xTest;
        orbWrite.BSESingletEnergies() = BSESingletEnergiesTest;
        orbWrite.BSESingletCoefficients() = BSESingletCoefficientsTest;
        orbWrite.BSESingletCoefficientsAR() = BSESingletCoefficientsARTest;
        orbWrite.TransitionDipoles() = transitionDipolesTest;
        orbWrite.BSETripletEnergies() = BSETripletEnergiesTest;
        orbWrite.BSETripletCoefficients() = BSETripletCoefficientsTest;
        orbWrite.WriteToCpt("xtp_testing.hdf5");
    }
    // Read Orbitals
    Orbitals orbRead;

    orbRead.ReadFromCpt("xtp_testing.hdf5");
    double tol = 1e-6;

    // Test the read values
    BOOST_CHECK_EQUAL(orbRead.getBasisSetSize() , basisSetSize);
    BOOST_CHECK_EQUAL(orbRead.getNumberOfLevels() , occupiedLevels + unoccupiedLevels);
    BOOST_CHECK_EQUAL(orbRead.getNumberOfElectrons() , numElectrons);
    BOOST_CHECK(orbRead.MOEnergies().isApprox(moeTest, tol));

    BOOST_CHECK(orbRead.MOCoefficients().isApprox(mocTest, tol));
    BOOST_CHECK_CLOSE(orbRead.getQMEnergy(), qmEnergy, tol);
    BOOST_CHECK_EQUAL(orbRead.getQMpackage(), qmPackage);
    BOOST_CHECK_CLOSE(orbRead.getSelfEnergy(), selfEnergy, tol);
    BOOST_CHECK_EQUAL(orbRead.getDFTbasis(), dftBasis);
    BOOST_CHECK_EQUAL(orbRead.getAuxbasis(), auxBasis);
    BOOST_CHECK_EQUAL(orbRead.getRPAmin(), rpaMin);
    BOOST_CHECK_EQUAL(orbRead.getRPAmax(), rpaMax);

    BOOST_CHECK_EQUAL(orbRead.getBSEvmin(), bseVmin);
    BOOST_CHECK_EQUAL(orbRead.getBSEvmax(), bseVmax);
    BOOST_CHECK_EQUAL(orbRead.getBSEcmin(), bseCmin);
    BOOST_CHECK_EQUAL(orbRead.getBSEcmax(), bseCmax);

    BOOST_CHECK_CLOSE(orbRead.getScaHFX(), scaHfx, tol);
    BOOST_CHECK_EQUAL(orbRead.getBSEtype(), bseType);
    BOOST_CHECK_EQUAL(orbRead.getECP(), someECP);
    BOOST_CHECK(orbRead.QPpertEnergies().isApprox(QPpertEnergiesTest, tol));
    BOOST_CHECK(orbRead.QPdiagEnergies().isApprox(QPdiagEnergiesTest, tol));
    BOOST_CHECK(orbRead.QPdiagCoefficients().isApprox(QPdiagCoefficientsTest));
    BOOST_CHECK(orbRead.eh_t().isApprox(eh_dTest, tol));
    BOOST_CHECK(orbRead.eh_s().isApprox(eh_xTest, tol));
    BOOST_CHECK(orbRead.BSESingletEnergies().isApprox(BSESingletEnergiesTest, tol));
    BOOST_CHECK(orbRead.BSESingletCoefficients().isApprox(BSESingletCoefficientsTest, tol));
    BOOST_CHECK(orbRead.BSESingletCoefficientsAR().isApprox(BSESingletCoefficientsARTest, tol));
    BOOST_CHECK(orbRead.BSETripletEnergies().isApprox(BSETripletEnergiesTest, tol));
    BOOST_CHECK(orbRead.BSETripletCoefficients().isApprox(BSETripletCoefficientsTest, tol));

    BOOST_REQUIRE_EQUAL(orbRead.TransitionDipoles().size(), transitionDipolesTest.size());

    for (size_t c = 0; c<transitionDipolesTest.size(); ++c){
        BOOST_CHECK(
            orbRead.TransitionDipoles()[c].isClose(transitionDipolesTest[c], tol));

    }

    BOOST_REQUIRE_EQUAL(orbRead.QMAtoms().size(), atomsTest.size());

    for (size_t i = 0; i<atomsTest.size(); ++i){
        auto atomRead = *(orbRead.QMAtoms()[i]);
        auto atomTest = atomsTest[i];
        BOOST_CHECK_EQUAL(atomRead.getAtomID(), atomTest.getAtomID());
        BOOST_CHECK(atomRead.getPos().isClose(atomTest.getPos(), tol));
        BOOST_CHECK_EQUAL(atomRead.getNuccharge(), atomTest.getNuccharge());
        BOOST_CHECK_EQUAL(atomRead.getPartialcharge(), atomTest.getPartialcharge());
        // no way to get qmatom index
    }

    BOOST_AUTO_TEST_SUITE_END()}


#define BOOST_TEST_DYN_LINK

#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>

#include "trikSound/angleDetector.h"
#include "trikSound/splitFilter.h"
#include "trikSound/digitalAudioFilter.h"
#include "trikSound/audioPipe.h"
#include "trikSound/wavFile.h"

using namespace std;
using namespace boost;
using namespace trikSound;

class AngleDetectorFixture
{
public:

    typedef sample_type* iterator_type;

    AngleDetectorFixture();

    void readAudioData(const QString& filename);

protected:

    StereoAudioPipe<iterator_type> audioPipe;
    std::shared_ptr<AngleDetector<iterator_type>> detector;
    AngleDetector<iterator_type>::range_type channel1;
    AngleDetector<iterator_type>::range_type channel2;
    QAudioFormat format;

    AudioBuffer bufl;
    AudioBuffer bufr;

    const int ord = 100;
};

AngleDetectorFixture::AngleDetectorFixture():
    detector(new AngleDetector<iterator_type>())
{
    std::shared_ptr<AudioFilter<iterator_type>> filter =
            make_shared<DigitalAudioFilter<iterator_type>>();

    std::shared_ptr<AudioPipe<iterator_type>> pipe =
            make_shared<AudioPipe<iterator_type>>();
    pipe->insertFilter(pipe->end(), filter);

    std::shared_ptr<StereoAudioFilter<iterator_type>> split =
            make_shared<SplitFilter<iterator_type>>(pipe);

    detector->setMicrDist(10.2);
    audioPipe.insertFilter(audioPipe.end(), split);
    audioPipe.insertFilter(audioPipe.end(), detector);
}

void AngleDetectorFixture::readAudioData(const QString& filename)
{
    WavFile file(filename);
    file.open(WavFile::ReadOnly);

    format = file.audioFormat();
    detector->setSampleRate(format.sampleRate());
    const size_t offset = 1000;
    file.seek(offset);
    AudioBuffer buf = file.readAll();

    bufl = buf.leftChannel();
    bufr = buf.rightChannel();

    channel1.first = (sample_type*) bufl.data();
    channel1.second = (sample_type*) bufl.data() + bufl.sampleCount();

    channel2.first = (sample_type*) bufr.data();
    channel2.second = (sample_type*) bufr.data() + bufr.sampleCount();
}

BOOST_FIXTURE_TEST_SUITE(testAngleDetector, AngleDetectorFixture)

BOOST_AUTO_TEST_CASE(testLeft)
{
    readAudioData("wav/left.wav");
    audioPipe.handleWindow(channel1, channel2);
    int angle = round(ord * detector->getAngle());
    BOOST_CHECK_EQUAL(angle, -90 * ord);
}

BOOST_AUTO_TEST_CASE(testCenter)
{
    readAudioData("wav/center.wav");
    audioPipe.handleWindow(channel1, channel2);
    int angle = round(ord * detector->getAngle());
    BOOST_CHECK_EQUAL(angle, 0 * ord);
}

BOOST_AUTO_TEST_CASE(testRight)
{
    readAudioData("wav/right.wav");
    audioPipe.handleWindow(channel1, channel2);
    int angle = round(ord * detector->getAngle());
    BOOST_CHECK_EQUAL(angle, 90 * ord);
}

BOOST_AUTO_TEST_SUITE_END()


// ------------------------------------------------------------------------
//  Copyright (C)
//  Jordi Pont-Tuset <jponttuset@vision.ee.ethz.ch>
//  July 2015
// ------------------------------------------------------------------------
#ifndef CONTAINERS_HPP
#define CONTAINERS_HPP

#include <Eigen/Dense>
#include <vector>
#include <set>
#include <map>
#include <list>
#include <fstream>

#include <clipper/clipper.hpp>

namespace PolyCont
{
    /* Our main container for double matrices */
    typedef Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> MatrixType;

    /* Container for boolean masks */
    typedef Eigen::Matrix<bool,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> MaskType;

    /* Type to avoid copies when reading from Mat */
    typedef Eigen::Map<Eigen::MatrixXd> eigen_map;

    /* Container of a coordinate */
    typedef ClipperLib::IntPoint Point;

    /* Container of a junction */
    struct Junction
    {
        Junction(std::size_t new_id, std::size_t new_x,
                 std::size_t new_y)
        : id(new_id), pos(new_x,new_y), cont_pos()
        {};

        /* Label */
        std::size_t id;

        /* Position */
        Point pos;

        /* Set of coordinate of the contours that form the junction */
        std::vector<Point> cont_pos;

        /* Set of ids of the incident contour pieces */
        std::set<double> cont_pieces;
    };

    /* Container of a piece of contour (from junction to junction) */
    struct Path : public ClipperLib::Path
    {
        Path()
        : orig_junc(), end_junc(), ClipperLib::Path()
        {};

        Path(std::size_t new_orig_junc, Point first_point)
        : orig_junc(new_orig_junc), end_junc(), ClipperLib::Path(1,first_point)
        {};

        /* Labels of the junctions */
        std::size_t orig_junc;
        std::size_t end_junc;
    };

    /* Container of a full contour (set of contour pieces) */
    struct ContContainer : public std::vector<Path>
    {
        ContContainer(std::size_t size=0) : std::vector<Path>(size)
        {}

        std::size_t im_sx;
        std::size_t im_sy;
        std::vector<bool> is_hole;

        friend std::ostream& operator<<(std::ostream& stream, const ContContainer& conts)
        {
            for(const Path& cont: conts)
            {
                for(const Point& pt: cont)
                    stream << pt;
                stream << std::endl;
            }

            return stream;
        }
    };
}
#endif