/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\
|  Phycas: Python software for phylogenetic analysis                          |
|  Copyright (C) 2006 Mark T. Holder, Paul O. Lewis and David L. Swofford     |
|                                                                             |
|  This program is free software; you can redistribute it and/or modify       |
|  it under the terms of the GNU General Public License as published by       |
|  the Free Software Foundation; either version 2 of the License, or          |
|  (at your option) any later version.                                        |
|                                                                             |
|  This program is distributed in the hope that it will be useful,            |
|  but WITHOUT ANY WARRANTY; without even the implied warranty of             |
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              |
|  GNU General Public License for more details.                               |
|                                                                             |
|  You should have received a copy of the GNU General Public License along    |
|  with this program; if not, write to the Free Software Foundation, Inc.,    |
|  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.                |
\~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

#if defined(_MSC_VER)
#	pragma warning(disable : 4267) // boost's builtin_converters.hpp casts size_t to int rather than unsigned
#endif

#include <boost/python.hpp>

//#include "phycas/force_include.h"
#include "model.hpp"
#include "gtr.hpp"
#include "jc.hpp"
#include "hky.hpp"
#include "codon_model.hpp"
#include "varcov.hpp"

using namespace boost::python;
using namespace phycas;

void model_pymod()
	{
	class_<QMatrix, boost::noncopyable>("QMatrixBase")
		.def("getDimension", &QMatrix::getDimension)
		.def("setRelativeRates", &QMatrix::setRelativeRates)
		.def("setStateFreqs", &QMatrix::setStateFreqs)
		.def("getPMatrix", &QMatrix::getPMatrix)
		.def("getQMatrix", &QMatrix::getQMatrix)
		.def("getEigenVectors", &QMatrix::getEigenVectors)
		.def("getEigenValues", &QMatrix::getEigenValues)
		;
	class_<VarCovMatrix, boost::noncopyable>("VarCovBase", init<std::string,unsigned,unsigned>())
        //.def("copyNewick", &VarCovMatrix::copyNewick)
        //.def("copyParamTypes", &VarCovMatrix::copyParamTypes)
        .def("setParamNames", &VarCovMatrix::setParamNames)
        .def("copyParamVector", &VarCovMatrix::copyParamVector)
        .def("standardizeSamples", &VarCovMatrix::standardizeSamples)
        .def("getLogRatioSum", &VarCovMatrix::getLogRatioSum)
        .def("getLogRatioVect", &VarCovMatrix::getLogRatioVect)
        .def("getRepresentativeLogKernel", &VarCovMatrix::getRepresentativeLogKernel)
        .def("getRepresentativeLogLikelihood", &VarCovMatrix::getRepresentativeLogLikelihood)
        .def("getRepresentativeLogPrior", &VarCovMatrix::getRepresentativeLogPrior)
        .def("getRepresentativeLogJacobianEdgelen", &VarCovMatrix::getRepresentativeLogJacobianEdgelen)
        .def("getRepresentativeLogJacobianSubstmodel", &VarCovMatrix::getRepresentativeLogJacobianSubstmodel)
        .def("getRepresentativeLogJacobianStandardization", &VarCovMatrix::getRepresentativeLogJacobianStandardization)
        .def("getRepresentativeParamVect", &VarCovMatrix::getRepresentativeParamVect)
        .def("calcRepresentativeForShell", &VarCovMatrix::calcRepresentativeForShell)
        .def("getRepresentativesForShell", &VarCovMatrix::getRepresentativesForShell)
        .def("debugTestStandardization", &VarCovMatrix::debugTestStandardization)
        .def("logJacobianForStandardization", &VarCovMatrix::logJacobianForStandardization)
        .def("destandardizeSample", &VarCovMatrix::destandardizeSample)
		;
	class_<phycas::Model, boost::noncopyable, boost::shared_ptr<phycas::Model> >("Model", no_init)
		.def("fixEdgeLenHyperprior", &phycas::Model::fixEdgeLenHyperprior)
		.def("freeEdgeLenHyperprior", &phycas::Model::freeEdgeLenHyperprior)
		.def("fixEdgeLengths", &phycas::Model::fixEdgeLengths)
		.def("freeEdgeLengths", &phycas::Model::freeEdgeLengths)
		.def("fixStateFreqs", &phycas::Model::fixStateFreqs)
		.def("freeStateFreqs", &phycas::Model::freeStateFreqs)
		.def("fixShape", &phycas::Model::fixShape)
		.def("freeShape", &phycas::Model::freeShape)
		.def("shapeFixed", &phycas::Model::shapeFixed)
		.def("pinvarFixed", &phycas::Model::pinvarFixed)
		.def("stateFreqsFixed", &phycas::Model::stateFreqsFixed)
		.def("edgeLenHyperParamFixed", &phycas::Model::edgeLenHyperParamFixed)
		.def("edgeLengthsFixed", &phycas::Model::edgeLengthsFixed)
		.def("isPinvarModel", &phycas::Model::isPinvarModel)
		.def("setPinvarModel", &phycas::Model::setPinvarModel)
		.def("setNotPinvarModel", &phycas::Model::setNotPinvarModel)
		.def("fixPinvar", &phycas::Model::fixPinvar)
		.def("freePinvar", &phycas::Model::freePinvar)
		.def("getPinvarPrior", &phycas::Model::getPinvarPrior)
		.def("setPinvarPrior", &phycas::Model::setPinvarPrior)
		.def("getDiscreteGammaShapePrior", &phycas::Model::getDiscreteGammaShapePrior)
		.def("setDiscreteGammaShapePrior", &phycas::Model::setDiscreteGammaShapePrior)
		.def("getTreeLengthPrior", &phycas::Model::getTreeLengthPrior)
		.def("setTreeLengthPrior", &phycas::Model::setTreeLengthPrior)
		.def("getExternalEdgeLenPrior", &phycas::Model::getExternalEdgeLenPrior)
		.def("setExternalEdgeLenPrior", &phycas::Model::setExternalEdgeLenPrior)
		.def("getInternalEdgeLenPrior", &phycas::Model::getInternalEdgeLenPrior)
		.def("setInternalEdgeLenPrior", &phycas::Model::setInternalEdgeLenPrior)
		.def("isSeparateInternalExternalEdgeLenPriors", &phycas::Model::isSeparateInternalExternalEdgeLenPriors)
		.def("setEdgeSpecificParams", &phycas::Model::setEdgeSpecificParams)
		.def("separateInternalExternalEdgeLenPriors", &phycas::Model::separateInternalExternalEdgeLenPriors)
		.def("hasEdgeLenHyperPrior", &phycas::Model::hasEdgeLenHyperPrior)
		.def("getInternalEdgelenHyperparam", &phycas::Model::getInternalEdgelenHyperparam)
		.def("getExternalEdgelenHyperparam", &phycas::Model::getExternalEdgelenHyperparam)
		.def("getEdgeLenHyperPrior", &phycas::Model::getEdgeLenHyperPrior)
		.def("setEdgeLenHyperPrior", &phycas::Model::setEdgeLenHyperPrior)
		.def("getModelName", &phycas::Model::getModelName)
		.def("getPinvar", &phycas::Model::getPinvar)
		.def("setPinvar", &phycas::Model::setPinvar)
		.def("getShape", &phycas::Model::getShape)
		.def("setShape", &phycas::Model::setShape)
		.def("getNGammaRates", &phycas::Model::getNGammaRates)
		.def("setNGammaRates", &phycas::Model::setNGammaRates)
		.def("getGammaRateProbs", &phycas::Model::getGammaRateProbs, return_value_policy<copy_const_reference>())
		.def("setAllGammaRateProbsEqual", &phycas::Model::setAllGammaRateProbsEqual)
		.def("getPMatrix", &phycas::Model::getPMatrix)
        .def("setStateFreqsUnnorm", &phycas::Model::setStateFreqsUnnorm)
        .def("setStateFreqUnnorm", &phycas::Model::setStateFreqUnnorm)
        .def("getTimeStamp", &phycas::Model::getTimeStamp)
		;
    class_<phycas::Irreversible, bases<phycas::Model> >("IrreversibleModelBase")
		.def("getModelName", &phycas::Irreversible::getModelName)
		.def("getNStates", &phycas::Irreversible::getNumStates)
		.def("getStateFreqs", &phycas::Irreversible::getStateFreqs, return_value_policy<copy_const_reference>())
		.def("fixScalingFactor", &phycas::Irreversible::fixScalingFactor)
		.def("freeScalingFactor", &phycas::Irreversible::freeScalingFactor)
		.def("getScalingFactor", &phycas::Irreversible::getScalingFactor)
		.def("setScalingFactor", &phycas::Irreversible::setScalingFactor)
		.def("getScalingFactorPrior", &phycas::Irreversible::getScalingFactorPrior)
		.def("setScalingFactorPrior", &phycas::Irreversible::setScalingFactorPrior)
		.def("setGainOnly", &phycas::Irreversible::setGainOnly)
		.def("setLossOnly", &phycas::Irreversible::setLossOnly)
		.def("paramHeader", &phycas::Irreversible::paramHeader)
		.def("paramReport", &phycas::Irreversible::paramReport)
		;
    class_<phycas::Binary, bases<phycas::Model> >("BinaryModelBase")
		.def("getModelName", &phycas::Binary::getModelName)
		.def("getNStates", &phycas::Binary::getNumStates)
		.def("getStateFreqs", &phycas::Binary::getStateFreqs, return_value_policy<copy_const_reference>())
		.def("setAllFreqsEqual", &phycas::Binary::setAllFreqsEqual)
		.def("fixScalingFactor", &phycas::Binary::fixScalingFactor)
		.def("freeScalingFactor", &phycas::Binary::freeScalingFactor)
		.def("getScalingFactor", &phycas::Binary::getScalingFactor)
		.def("setScalingFactor", &phycas::Binary::setScalingFactor)
		.def("getScalingFactorPrior", &phycas::Binary::getScalingFactorPrior)
		.def("setScalingFactorPrior", &phycas::Binary::setScalingFactorPrior)
		.def("fixKappa", &phycas::Binary::fixKappa)
		.def("freeKappa", &phycas::Binary::freeKappa)
		.def("getKappa", &phycas::Binary::getKappa)
		.def("setKappa", &phycas::Binary::setKappa)
		.def("getKappaPrior", &phycas::Binary::getKappaPrior)
		.def("setKappaPrior", &phycas::Binary::setKappaPrior)
		.def("paramHeader", &phycas::Binary::paramHeader)
		.def("paramReport", &phycas::Binary::paramReport)
		;
    class_<phycas::JC, bases<phycas::Model> >("JCModelBase")
		.def("getModelName", &phycas::JC::getModelName)
		.def("getNStates", &phycas::JC::getNumStates)
		.def("getStateFreqs", &phycas::JC::getStateFreqs, return_value_policy<copy_const_reference>())
		.def("setAllFreqsEqual", &phycas::JC::setAllFreqsEqual)
		.def("paramHeader", &phycas::JC::paramHeader)
		.def("paramReport", &phycas::JC::paramReport)
		;
	class_<phycas::HKY, bases<phycas::Model> >("HKYModelBase")
		.def("getModelName", &phycas::HKY::getModelName)
		.def("fixKappa", &phycas::HKY::fixKappa)
		.def("freeKappa", &phycas::HKY::freeKappa)
		.def("getKappa", &phycas::HKY::getKappa)
		.def("setKappa", &phycas::HKY::setKappa)
		.def("getKappaPrior", &phycas::HKY::getKappaPrior)
		.def("setKappaPrior", &phycas::HKY::setKappaPrior)
        .def("getStateFreqPrior", &phycas::HKY::getStateFreqPrior)
		.def("setStateFreqPrior", &phycas::HKY::setStateFreqPrior)
        .def("getStateFreqParamPrior", &phycas::HKY::getStateFreqParamPrior)
		.def("setStateFreqParamPrior", &phycas::HKY::setStateFreqParamPrior)
		.def("setKappaFromTRatio", &phycas::HKY::setKappaFromTRatio)
		.def("calcTRatio", &phycas::HKY::calcTRatio)
		.def("getNStates", &phycas::HKY::getNumStates)
		.def("getStateFreqs", &phycas::HKY::getStateFreqs, return_value_policy<copy_const_reference>())
		.def("setAllFreqsEqual", &phycas::HKY::setAllFreqsEqual)
		.def("setNucleotideFreqs", &phycas::HKY::setNucleotideFreqs)
		.def("paramHeader", &phycas::HKY::paramHeader)
		.def("paramReport", &phycas::HKY::paramReport)
		;
	class_<phycas::GTR, bases<phycas::Model> >("GTRModelBase")
		.def("getModelName", &phycas::GTR::getModelName)
		//.def("createParameters", &phycas::GTR::createParameters)
		//.def("calcPMat", &phycas::GTR::calcPMat)
		.def("fixRelRates", &phycas::GTR::fixRelRates)
		.def("freeRelRates", &phycas::GTR::freeRelRates)
		.def("getRelRates", &phycas::GTR::getRelRates)
		.def("setRelRates", &phycas::GTR::setRelRates)
		.def("setRelRateUnnorm", &phycas::GTR::setRelRateUnnorm)
		.def("setRelRateParamPrior", &phycas::GTR::setRelRateParamPrior)
		.def("getRelRateParamPrior", &phycas::GTR::getRelRateParamPrior)
		.def("setRelRatePrior", &phycas::GTR::setRelRatePrior)
		.def("getRelRatePrior", &phycas::GTR::getRelRatePrior)
		.def("setNucleotideFreqs", &phycas::GTR::setNucleotideFreqs)
		.def("getStateFreqs", &phycas::GTR::getStateFreqs, return_value_policy<copy_const_reference>())
		.def("setAllFreqsEqual", &phycas::GTR::setAllFreqsEqual)
        .def("getStateFreqPrior", &phycas::GTR::getStateFreqPrior)
		.def("setStateFreqPrior", &phycas::GTR::setStateFreqPrior)
		.def("setStateFreqParamPrior", &phycas::GTR::setStateFreqParamPrior)
		.def("getStateFreqParamPrior", &phycas::GTR::getStateFreqParamPrior)
		.def("paramHeader", &phycas::GTR::paramHeader)
		.def("paramReport", &phycas::GTR::paramReport)
		.def("calcTRatio", &phycas::GTR::calcTRatio)
		;
	class_<phycas::Codon, bases<phycas::Model> >("CodonModelBase")
		.def("getModelName", &phycas::Codon::getModelName)
		.def("getNStates", &phycas::Codon::getNumStates)
		.def("getStateFreqs", &phycas::Codon::getStateFreqs, return_value_policy<copy_const_reference>())
		.def("setStateFreqUnnorm", &phycas::Codon::setStateFreqUnnorm)
		.def("setAllFreqsEqual", &phycas::Codon::setAllFreqsEqual)
		.def("setNucleotideFreqs", &phycas::Codon::setNucleotideFreqs)
		.def("fixKappa", &phycas::Codon::fixKappa)
		.def("freeKappa", &phycas::Codon::freeKappa)
		.def("fixOmega", &phycas::Codon::fixOmega)
		.def("freeOmega", &phycas::Codon::freeOmega)
		.def("getKappa", &phycas::Codon::getKappa)
		.def("setKappa", &phycas::Codon::setKappa)
		.def("getOmega", &phycas::Codon::getOmega)
		.def("setOmega", &phycas::Codon::setOmega)
		.def("getNGammaRates", &phycas::Codon::getNGammaRates)
		.def("setNGammaRates", &phycas::Codon::setNGammaRates)
		.def("getKappaPrior", &phycas::Codon::getKappaPrior)
		.def("setKappaPrior", &phycas::Codon::setKappaPrior)
		.def("getOmegaPrior", &phycas::Codon::getOmegaPrior)
		.def("setOmegaPrior", &phycas::Codon::setOmegaPrior)
        .def("getStateFreqPrior", &phycas::Codon::getStateFreqPrior)
		.def("setStateFreqPrior", &phycas::Codon::setStateFreqPrior)
		.def("setStateFreqParamPrior", &phycas::Codon::setStateFreqParamPrior)
		.def("getStateFreqParamPrior", &phycas::Codon::getStateFreqParamPrior)
		.def("paramHeader", &phycas::Codon::paramHeader)
		.def("paramReport", &phycas::Codon::paramReport)
		;
	}


// Copyright 2015 Christina Teflioudi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 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.
/*
 * runNaive.cc
 *
 * Created on: Sep 21, 2012
 * Author: chteflio
 */
#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>

#include <iostream>
#include <mips/mips.h>

using namespace std;
using namespace mips;
using namespace boost::program_options;

int main(int argc, char *argv[]) {
    int k, depth, r, m, n, threads;
    string usersFile;
    string itemsFile;
    string logFile, resultsFile;
    bool querySideLeft = true, isTransformed = false;

    // read command line
    options_description desc("Options");
    desc.add_options()
            ("help", "produce help message")
            ("Q^T", value<string>(&usersFile), "file containing the query matrix (left side)")
            ("P", value<string>(&itemsFile), "file containing the probe matrix (right side)")
            ("k", value<int>(&k)->default_value(5), "top k (default = 5)")
            ("querySideLeft", value<bool>(&querySideLeft)->default_value(true), "1 if Q^T contains the queries (default). Interesting for Row-Top-k")
            ("isTransformed", value<bool>(&isTransformed)->default_value(false), "1 if the input files contain the tranformed vectors. If 0 the program will transform them")
            ("logFile", value<string>(&logFile)->default_value(""), "output File (contains runtime information)")
            ("resultsFile", value<string>(&resultsFile)->default_value(""), "output File (contains the results)")
            ("d", value<int>(&depth)->default_value(0), "depth of the tree (default = 0)")
            ("t", value<int>(&threads)->default_value(1), "num of threads (default 1)")
            ("r", value<int>(&r)->default_value(0), "num of coordinates in each vector (needed when reading from csv files)")
            ("m", value<int>(&m)->default_value(0), "num of vectors in Q^T (needed when reading from csv files)")
            ("n", value<int>(&n)->default_value(0), "num of vectors in P (needed when reading from csv files)")
            ;

    positional_options_description pdesc;
    pdesc.add("Q^T", 1);
    pdesc.add("P", 2);

    variables_map vm;
    store(command_line_parser(argc, argv).options(desc).positional(pdesc).run(), vm);
    notify(vm);

    if (vm.count("help") || vm.count("Q^T") == 0 || vm.count("P") == 0) {
        cout << "runPcaTree [options] <Q^T> <P>" << endl << endl;
        cout << desc << endl;
        return 1;
    }

    InputArguments args;
    args.logFile = logFile;
    args.k = k;
    args.threads = threads;

    VectorMatrix leftMatrix, rightMatrix;

    if (querySideLeft) {
        leftMatrix.readFromFile(usersFile, r, m, true);
        rightMatrix.readFromFile(itemsFile, r, n, false);
    } else {
        leftMatrix.readFromFile(itemsFile, r, n, false);
        rightMatrix.readFromFile(usersFile, r, m, true);
    }

    mips::PcaTree algo(args, depth, isTransformed);

    algo.initialize(rightMatrix);

    Results results;
    algo.runTopK(leftMatrix, results);

    if (resultsFile != "") {
        results.writeToFile(resultsFile);
    }

    return 0;
}


#include "pch.hxx"

#include <cryptox/symmetric/iostreams/basic_endec_filter.hxx>
#include <cryptox/symmetric/iostreams/evp_encryptor.hxx>
#include <cryptox/symmetric/iostreams/evp_decryptor.hxx>
#include <cryptox/symmetric/symmetric_algorithm.hxx>
#include <cryptox/symmetric/encryptor.hxx>
#include <cryptox/symmetric/decryptor.hxx>
#include <cryptox/detail/make_random_vector.hxx>

#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/copy.hpp>
#include <iostream>

typedef cryptox::aes_256_cbc cipher_type;
static std::vector<std::uint8_t> iv  = cryptox::detail::make_random_vector(32);
static std::vector<std::uint8_t> key = cryptox::detail::make_random_vector(32);

template <typename Input, typename Output>
static void encrypt_using_filter(Input first, Input last, Output d_first) {
	boost::iostreams::filtering_ostream output;
	output.push(cryptox::create_filter(
		new cryptox::encryptor<cipher_type> (
			key.begin(), key.end(),
			iv.begin(),  iv.end()
		)
	));

	output.push(d_first);

	// Ugly, but effective for this test module.
	const char* input = (const char*)&(*first);

	boost::iostreams::write(output, input, std::distance(first, last));
	output.reset();
}

template <typename Input, typename Output>
static void decrypt_using_filter(Input first, Input last, Output d_first) {
	boost::iostreams::filtering_istream input;
	input.push(cryptox::create_filter(
		new cryptox::decryptor<cipher_type> (
			key.begin(), key.end(),
			iv.begin(),  iv.end()
		)
	));

	// Ugly, but effective for this test module.
	const char* input_ = (const char*)&(*first);

 	input.push(boost::iostreams::array_source(input_, std::distance(first, last)));

 	boost::iostreams::copy(input, d_first);
	input.reset();
}

// ----------------------------------------------------------------------------

template <typename Input, typename Output>
static Output encrypt_using_direct(Input first, Input last, Output d_first) {
	cryptox::encryptor<cipher_type> encryptor(key.begin(), key.end(), iv.begin(), iv.end());
	return encryptor(first, last, d_first);
}

template <typename Input, typename Output>
static Output decrypt_using_direct(Input first, Input last, Output d_first) {
	cryptox::decryptor<cipher_type> decryptor(key.begin(), key.end(), iv.begin(), iv.end());
	return decryptor(first, last, d_first);
}

// ----------------------------------------------------------------------------

template <typename Input, typename Output>
static Output perform_encryptions(Input first, Input last, Output d_first) {
	std::vector<std::uint8_t> filter;
	std::vector<std::uint8_t> direct;

	encrypt_using_filter(first, last, std::back_inserter(filter));
	encrypt_using_direct(first, last, std::back_inserter(direct));

	BOOST_CHECK_EQUAL_COLLECTIONS(filter.begin(), filter.end(), direct.begin(), direct.end());

	return std::copy(filter.begin(), filter.end(), d_first);
}

template <typename Input, typename Output>
static Output perform_decryptions(Input first, Input last, Output d_first) {
	std::vector<std::uint8_t> filter;
	std::vector<std::uint8_t> direct;

	decrypt_using_filter(first, last, std::back_inserter(filter));
	decrypt_using_direct(first, last, std::back_inserter(direct));

	BOOST_CHECK_EQUAL_COLLECTIONS(filter.begin(), filter.end(), direct.begin(), direct.end());

	return std::copy(filter.begin(), filter.end(), d_first);
}

// ----------------------------------------------------------------------------

template <typename Input>
static void roundtrip_test(Input first, Input last) {
	std::cerr << "RT" << std::distance(first, last) << std::endl;

	std::vector<std::uint8_t> encrypted;
	std::vector<std::uint8_t> decrypted;

	perform_encryptions(first,             last,            std::back_inserter(encrypted));
	perform_decryptions(encrypted.begin(), encrypted.end(), std::back_inserter(decrypted));

	BOOST_CHECK_EQUAL_COLLECTIONS(first, last, decrypted.begin(), decrypted.end());
}

BOOST_AUTO_TEST_CASE(basic_endec_filter_test) {
	std::vector<std::uint8_t> input = cryptox::detail::make_random_vector(9000);

	//
	// Slice input and test along...
	//
	const size_t initial = 0;
	size_t increment = 1;
	for (size_t i=initial; i<input.size(); i+=increment)
		roundtrip_test(input.begin(), input.begin() + i);
}


// BSD 3-Clause License
//
// Copyright (c) 2022, Map IV, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
//    contributors may be used to endorse or promote products derived from
//    this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <height_converter/gsigeo.hpp>

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

#include <boost/algorithm/string.hpp>

namespace height_converter
{

std::vector<std::string> split(std::string input, char delimiter)
{
  std::istringstream stream(input);
  std::string field;
  std::vector<std::string> result;

  while (std::getline(stream, field, delimiter))
  {
    if (field != std::string(" ") && !field.empty() && field != "\n" && field != "\r")
      result.push_back(field);
  }
  return result;
}

GSIGEO2011::GSIGEO2011()
{
}

GSIGEO2011::~GSIGEO2011()
{
}

void GSIGEO2011::loadGeoidMap(const std::string& geoid_file)
{
  std::ifstream ifs(geoid_file);
  if (!ifs)
  {
    std::cerr << "Error: Cannot open Geoid data file: " << geoid_file << std::endl;
    exit(2);
  }

  geoid_map_.reserve(row_size_);
  for (int i = 0; i < row_size_; i++)
  {
    geoid_map_[i].reserve(column_size_);
  }

  std::string curr_line;
  // Skip header line
  std::getline(ifs, curr_line);

  int i = 0, j = 0;

  while (std::getline(ifs, curr_line))
  {
    std::vector<std::string> str_vec = split(curr_line, ' ');

    for (int k = 0; k < str_vec.size(); k++)
    {
      geoid_map_[i][j] = std::stod(str_vec[k]);
      j++;

    }
    if (j == column_size_)
    {
      j = 0;
      i++;
    }
  }
  is_geoid_loaded_ = true;
}

double GSIGEO2011::getGeoid(const double& lat, const double& lon)
{
  if (!is_geoid_loaded_)
  {
    std::cerr << "Error: Geoid map is not loaded" << std::endl;
    exit(1);
  }
  const double lat_min = 20;
  const double lon_min = 120;
  const double d_lat = 1.0 / 60.0;
  const double d_lon = 1.5 / 60.0;

  const int i_lat = std::floor((lat - lat_min) / d_lat);
  const int i_lon = std::floor((lon - lon_min) / d_lon);
  const int j_lat = i_lat + 1;
  const int j_lon = i_lon + 1;

  // const double t = (((lat - lat_min) / d_lat) - i_lat) / d_lat;
  // const double u = (((lon - lon_min) / d_lon) - i_lon) / d_lon;
  const double t = (lat - (lat_min + i_lat * d_lat)) / d_lat;
  const double u = (lon - (lon_min + i_lon * d_lon)) / d_lon;

  if (i_lat < 0 || i_lat >= row_size_ - 1 || i_lon < 0 || i_lon >= column_size_ - 1)
  {
    std::cerr << "Error: latitude/longitude is out of range (20~50, 120~150)" << std::endl;
    std::cerr << (lat, lon) << std::endl;
    exit(1);
  }

  if (geoid_map_[i_lat][i_lon] == 999 || geoid_map_[i_lat][j_lon] == 999 || geoid_map_[j_lat][i_lon] == 999 || geoid_map_[j_lat][j_lon] == 999)
  {
    std::cerr << "Error: Not supported area" << std::endl;
    exit(1);
  }

  double geoid = (1-t)*(1-u)*geoid_map_[i_lat][i_lon] + (1-t)*u*geoid_map_[i_lat][j_lon] + t*(1-u)*geoid_map_[j_lat][i_lon] + t*u*geoid_map_[j_lat][j_lon];

  return geoid;
}
}   // namespace height_converter


#include <algorithm>
#include <boost/optional.hpp>
#include <climits>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <functional>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#define REP(i, n) for(int i = 0, i##_MACRO = (n); i < i##_MACRO; i++)
#define RANGE(i, a, b) for(int i = (a), i##_MACRO = (b); i < i##_MACRO; i++)
#define EACH(e, a) for(auto&& e : a)
#define ALL(a) std::begin(a), std::end(a)
#define RALL(a) std::rbegin(a), std::rend(a)
#define FILL(a, n) memset((a), n, sizeof(a))
#define FILLZ(a) FILL(a, 0)
#define INT(x) (static_cast<int>(x))

using namespace std;

using ll = long long;
using VI = vector<int>;
using VI2D = vector<vector<int>>;

constexpr int INF = 2e9;
constexpr double EPS = 1e-10;
constexpr double PI = acos(-1.0);

constexpr int dx[] = {-1, 0, 1, 0};
constexpr int dy[] = {0, -1, 0, 1};

template <typename T>
constexpr int sign(T x) {
	return x < 0 ? -1 : x > 0 ? 1 : 0;
}

template <>
constexpr int sign(double x) {
	return x < -EPS ? -1 : x > EPS ? 1 : 0;
}

template <typename T, typename U>
constexpr void chmax(T& m, U x) {
	m = max(m, x);
}

template <typename T, typename U>
constexpr void chmin(T& m, U x) {
	m = min(m, x);
}

template <typename T>
constexpr T square(T x) {
	return x * x;
}

// UnionFind(disjoint set)
class UnionFind {
	std::vector<size_t> parents;
	std::vector<size_t> rank;
	std::vector<size_t> tree_size;

	public:
	UnionFind(size_t size) : parents(size), rank(size, 0), tree_size(size, 1) {
		std::iota(this->parents.begin(), this->parents.end(), 0);
	}

	// 併合
	bool merge(size_t a, size_t b) {
		size_t ar = this->root(a);
		size_t br = this->root(b);
		if(ar == br) {
			return false;
		}
		if(this->rank[ar] < this->rank[br]) {
			std::swap(ar, br);
		}
		if(this->rank[ar] == this->rank[br]) {
			this->rank[ar]++;
		}
		this->tree_size[ar] = this->tree_size[br] =
			this->tree_size[ar] + this->tree_size[br];
		return this->parents[br] = ar;
	}
	bool unite(size_t a, size_t b) { return this->merge(a, b); }

	// 同集合か判定
	bool is_same(size_t a, size_t b) { return this->root(a) == this->root(b); }
	bool is_union(size_t a, size_t b) { return this->is_same(a, b); }
	size_t size(size_t n) {
		this->root(n);
		return this->tree_size[n];
	}

	private:
	size_t root(int n) {
		if(this->parents[n] == n) {
			return n;
		}

		size_t r = this->root(this->parents[n]);
		this->parents[n] = r;
		this->tree_size[n] = this->tree_size[r];
		return r;
	}
};

int main() {
	int n, m, k;
	cin >> n >> m >> k;
	VI a(m);
	VI b(m);
	VI2D friends(n);
	REP(i, m) {
		cin >> a[i] >> b[i];
		friends[a[i] - 1].push_back(b[i] - 1);
		friends[b[i] - 1].push_back(a[i] - 1);
	}
	VI c(k);
	VI d(k);
	VI2D blocks(n);
	REP(i, k) {
		cin >> c[i] >> d[i];
		blocks[c[i] - 1].push_back(d[i] - 1);
		blocks[d[i] - 1].push_back(c[i] - 1);
	}
	UnionFind uf(n);
	REP(i, m) { uf.merge(a[i] - 1, b[i] - 1); }
	REP(i, n) {
		int size = uf.size(i) - 1;
		EACH(e, friends[i]) {
			if(uf.is_same(i, e)) {
				size--;
			}
		}
		EACH(e, blocks[i]) {
			if(uf.is_same(i, e)) {
				size--;
			}
		}
		cout << size << ' ';
	}
	cout << endl;
	return 0;
}


#include "norm_test.hpp"
#include <boost/numeric/ublas/matrix.hpp>

int main()
{

  ///testing float norm1
  bench_norm<float, 10, 10> b1;

  ///testing double norm1
  bench_norm<double, 10, 10> b2;

  b1.run();
  b2.run();

  return 0;

}


/***************************************************************************************************************************
* formation_control.cpp
*
* Author: Qyp
*
* Update Time: 2020.4.2
*                           采用虚拟领机-从机结构。地面站为主节点，每一个飞机都为从机
***************************************************************************************************************************/
#include <ros/ros.h>
#include <iostream>
#include <command_to_mavros.h>
#include <geometry_msgs/Point.h>
#include <mavros_msgs/CommandBool.h>
#include <mavros_msgs/SetMode.h>
#include <mavros_msgs/State.h>
#include <nav_msgs/Odometry.h>
#include <mavros_msgs/PositionTarget.h>
#include <geometry_msgs/PoseStamped.h>
#include <geometry_msgs/TwistStamped.h>
#include <mavros_msgs/ActuatorControl.h>
#include <sensor_msgs/Imu.h>
#include <bitset>
#include <Eigen/Eigen>

using namespace std;
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>变量声明<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
string uav_name;
string neighbour_name1,neighbour_name2;
int num_neighbour = 2;
ros::Publisher setpoint_raw_local_pub;

mavros_msgs::State current_state;
Eigen::Vector3d pos_drone;
Eigen::Vector3d vel_drone;

Eigen::Vector3d pos_leader;
Eigen::Vector3d relative_pos_to_leader;

Eigen::Vector3d state_sp(0,0,0);
float yaw_sp = 0;
bool get_new_cmd = false;

float k_p;
float k_aij;
ros::Publisher vision_pub;
//mavros_msgs::State current_state_nei[2];
Eigen::Vector3d pos_nei[2];
Eigen::Vector3d vel_nei[2];
//2020.4.2 这里有个十分神奇的bug： 这个语句后面不要再放任何变量声明了，不然变量的值会各种变化。。
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>函数声明<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>回调函数<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
void pos_leader_cb(const geometry_msgs::Point::ConstPtr &msg)
{
    pos_leader = Eigen::Vector3d(msg->x,msg->y,msg->z);
    get_new_cmd = true;
}
void formation_cb(const geometry_msgs::Point::ConstPtr &msg)
{
    relative_pos_to_leader  = Eigen::Vector3d(msg->x,msg->y,msg->z);
}
void state_cb(const mavros_msgs::State::ConstPtr &msg)
{
    current_state = *msg;
}
void odom_cb(const nav_msgs::Odometry::ConstPtr &msg)
{
    pos_drone  = Eigen::Vector3d(msg->pose.pose.position.x,msg->pose.pose.position.y,msg->pose.pose.position.z);
    vel_drone  = Eigen::Vector3d(msg->twist.twist.linear.x,msg->twist.twist.linear.y,msg->twist.twist.linear.z);
}
void odom_nei_cb(const nav_msgs::Odometry::ConstPtr &msg, int nei_id)
{
    pos_nei[nei_id]  = Eigen::Vector3d(msg->pose.pose.position.x,msg->pose.pose.position.y,msg->pose.pose.position.z);
    vel_nei[nei_id]  = Eigen::Vector3d(msg->twist.twist.linear.x,msg->twist.twist.linear.y,msg->twist.twist.linear.z);
}
void gazebo_cb(const nav_msgs::Odometry::ConstPtr& msg)
{

    geometry_msgs::PoseStamped vision;

    vision.pose.position.x = msg->pose.pose.position.x;
    vision.pose.position.y = msg->pose.pose.position.y;
    vision.pose.position.z = msg->pose.pose.position.z;

    vision.pose.orientation.x = msg->pose.pose.orientation.x;
    vision.pose.orientation.y = msg->pose.pose.orientation.y;
    vision.pose.orientation.z = msg->pose.pose.orientation.z;
    vision.pose.orientation.w = msg->pose.pose.orientation.w;

    vision.header.stamp = ros::Time::now();
    vision_pub.publish(vision);
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>主 函 数<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
int main(int argc, char **argv)
{
    ros::init(argc, argv, "formation_control");
    ros::NodeHandle nh("~");
    ros::Rate rate(20.0);

    //无人机编号 1号无人机则为1
    nh.param<string>("uav_name", uav_name, "/uav0");
    nh.param<float>("k_p", k_p, 0.95);
    nh.param<float>("k_aij", k_aij, 0.1);
    //可监听到的无人机编号，目前设定为可监听到两台无人机，后期考虑可通过数组传递参数，监听任意ID的无人机
    nh.param<string>("neighbour_name1", neighbour_name1, "/uav0");
    nh.param<string>("neighbour_name2", neighbour_name2, "/uav0");

    //订阅虚拟主机的位置，及阵型
    ros::Subscriber leader_sub = nh.subscribe<geometry_msgs::Point>("/prometheus/formation/leader_pos", 10, pos_leader_cb);
    ros::Subscriber formation_sub = nh.subscribe<geometry_msgs::Point>("/prometheus/formation" + uav_name, 10, formation_cb);

    // 【订阅】gazebo仿真真值
    ros::Subscriber gazebo_sub = nh.subscribe<nav_msgs::Odometry>("/prometheus/ground_truth"+ uav_name, 100, gazebo_cb);
    vision_pub = nh.advertise<geometry_msgs::PoseStamped>(uav_name + "/mavros/vision_pose/pose", 100);

    //订阅本台飞机的状态
    ros::Subscriber state_sub    = nh.subscribe<mavros_msgs::State>(uav_name + "/mavros/state", 10, state_cb);
    ros::Subscriber odom_sub = nh.subscribe<nav_msgs::Odometry>(uav_name + "/mavros/local_position/odom", 100, odom_cb);

    //订阅邻居飞机的状态信息
    ros::Subscriber odom_nei_sub_1 = nh.subscribe<nav_msgs::Odometry>(neighbour_name1 + "/mavros/local_position/odom", 100, boost::bind(&odom_nei_cb,_1, 0));
    ros::Subscriber odom_nei_sub_2 = nh.subscribe<nav_msgs::Odometry>(neighbour_name2 + "/mavros/local_position/odom", 100, boost::bind(&odom_nei_cb,_1, 1));

    // 用于与mavros通讯的类，通过mavros发送控制指令至飞控【本程序->mavros->飞控】
    command_to_mavros _command_to_mavros;

    //固定的浮点显示
    cout.setf(ios::fixed);
    //setprecision(n) 设显示小数精度为n位
    cout<<setprecision(2);
    //左对齐
    cout.setf(ios::left);
    // 强制显示小数点
    cout.setf(ios::showpoint);
    // 强制显示符号
    cout.setf(ios::showpos);

    // Waiting for input
    cout << "Waiting for start command......"<< endl;

    int start_flag = 0;
    while(!get_new_cmd)
    {
        _command_to_mavros.idle();
        ros::spinOnce();
        rate.sleep();
    }

    get_new_cmd = false;

    cout << "Switch to OFFBOARD and arm ..."<<endl;
    while(current_state.mode != "OFFBOARD")
    {
        _command_to_mavros.mode_cmd.request.custom_mode = "OFFBOARD";
        _command_to_mavros.set_mode_client.call(_command_to_mavros.mode_cmd);
        _command_to_mavros.idle();
        //执行回调函数
        ros::spinOnce();
        ros::Duration(0.5).sleep();

        if (_command_to_mavros.mode_cmd.response.mode_sent)
        {
            cout <<"[formation_flight, UAV_name: "<<uav_name<<"]: "<< "Set to OFFBOARD Mode Susscess! "<< endl;
        }
    }

    while(!current_state.armed)
    {
        _command_to_mavros.arm_cmd.request.value = true;
        _command_to_mavros.arming_client.call(_command_to_mavros.arm_cmd);
        _command_to_mavros.idle();
        //执行回调函数
        ros::spinOnce();
        ros::Duration(0.5).sleep();

        if (_command_to_mavros.arm_cmd.response.success)
        {
            cout <<"[formation_flight, UAV_name: "<<uav_name<<"]: "<< "Arm Susscess! "<< endl;
        }
    }

    //起飞位置为飞机所在位置上方1米
    cout<<"[formation_flight, UAV_name: "<<uav_name<<"]: "<<"Takeoff to predefined position."<<endl;
    Eigen::Vector3d takeoff_pos = pos_drone + Eigen::Vector3d(0,0,1);
    yaw_sp = 0.0;
    while(!get_new_cmd)
    {
        _command_to_mavros.send_pos_setpoint(takeoff_pos, yaw_sp);
        rate.sleep();
        ros::spinOnce();
    }

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>主  循  环<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    while(ros::ok())
    {
        ros::spinOnce();

        //formation control， 平面阵型，xy控制速度，z轴高度定高
        state_sp[0] = k_p * (pos_leader[0] + relative_pos_to_leader[0] - pos_drone[0]) + k_aij*(pos_nei[0][0] - pos_drone[0]) + k_aij*(pos_nei[1][0] - pos_drone[0]);
        state_sp[1] = k_p * (pos_leader[1] + relative_pos_to_leader[1] - pos_drone[1]) + k_aij*(pos_nei[0][1] - pos_drone[1]) + k_aij*(pos_nei[1][1] - pos_drone[1]);
        state_sp[2] = 1.0;
        yaw_sp = 0.0;
        _command_to_mavros.send_vel_xy_pos_z_setpoint(state_sp, yaw_sp);

        cout << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Formation Flight Mission<<<<<<<<<<<<<<<<<<<<<<<<<<< "<< endl;
        cout << "UAV_name : " << uav_name <<"   UAV_mode: "<< current_state.mode <<"   arm or not: "<< current_state.armed << endl;
        cout << "Position [X Y Z] : " << pos_drone[0] << " [ m ] "<< pos_drone[1]<<" [ m ] "<<pos_drone[2]<<" [ m ] "<<endl;
        cout << "Velocity [X Y Z] : " << vel_drone[0] << " [m/s] "<< vel_drone[1]<<" [m/s] "<<vel_drone[2]<<" [m/s] "<<endl;
        cout << "P_Leader [X Y Z] : " << pos_leader[0] << " [ m ] "<< pos_leader[1]<<" [ m ] "<<pos_leader[2]<<" [ m ] "<<endl;
        cout << "Pos_Nei1 [X Y Z] : " << pos_nei[0][0] << " [ m ] "<< pos_nei[0][1]<<" [ m ] "<<pos_nei[0][2]<<" [ m ] "<<endl;
        cout << "Pos_Nei2 [X Y Z] : " << pos_nei[1][0] << " [ m ] "<< pos_nei[1][1]<<" [ m ] "<<pos_nei[1][2]<<" [ m ] "<<endl;
        cout << "Relative [X Y Z] : " << relative_pos_to_leader[0] << " [ m ] "<< relative_pos_to_leader[1]<<" [ m ] "<<relative_pos_to_leader[2]<<" [ m ] "<<endl;
        cout << "state_sp [X Y Z] : " << state_sp[0]  << " [m/s] "<< state_sp[1] <<" [m/s] "<<state_sp[2] <<" [m/s] "<<endl;

        rate.sleep();
    }

}


/****************************************************************************
 * Copyright (c) 2012-2020 by the DataTransferKit authors                   *
 * All rights reserved.                                                     *
 *                                                                          *
 * This file is part of the DataTransferKit library. DataTransferKit is     *
 * distributed under a BSD 3-clause license. For the licensing terms see    *
 * the LICENSE file in the top-level directory.                             *
 *                                                                          *
 * SPDX-License-Identifier: BSD-3-Clause                                    *
 ****************************************************************************/
/*!
 * \file
 * \brief C adapter to DTK Maps.
 */
#ifndef DTK_C_API_MAP_HPP
#define DTK_C_API_MAP_HPP

#include <DTK_C_API.h>
#include <DTK_C_API.hpp>
#include <DTK_DBC.hpp>
#include <DTK_MovingLeastSquaresOperator.hpp>
#include <DTK_NearestNeighborOperator.hpp>
#include <DTK_ParallelTraits.hpp>
#include <DTK_PointCloudOperator.hpp>
#include <DTK_UserApplication.hpp>

#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>

#include <mpi.h>

#include <memory>
#include <string>
#include <tuple>

namespace DataTransferKit
{
//---------------------------------------------------------------------------//
// Map interface base class. This allows us to hide the device template
// parameter in MapImpl when we do the casting in the C interface
// implementation. We won't know what device type we have in a template when
// calling the C interface and we don't need to - the apply function
// implementation details will dispatch to the correct device type based on
// the construction.
struct DTK_Map
{
    virtual ~DTK_Map() = default;

    virtual void apply( const std::string &source_field_name,
                        const std::string &target_field_name ) = 0;
};

//---------------------------------------------------------------------------//
template <class MapExecSpace, class SourceMemSpace, class TargetMemSpace>
struct DTK_MapImpl : public DTK_Map
{
    using map_device_type = typename MapExecSpace::device_type;

    DTK_MapImpl( MPI_Comm comm, DTK_UserApplicationHandle source,
                 DTK_UserApplicationHandle target,
                 boost::property_tree::ptree const &ptree )
        : _source( reinterpret_cast<DTK_Registry *>( source )->_registry )
        , _target( reinterpret_cast<DTK_Registry *>( target )->_registry )
    {
        // FOR NOW JUST CREATE A NEAREST NEIGHBOR OPERATOR FOR DEMONSTRATION
        // PURPOSES. THIS WILL BE REPLACED BY A PROPER FACTORY.

        // Get coordinates from the source and target.
        auto source_nodes = _source.getNodeList().coordinates;
        auto target_nodes = _target.getNodeList().coordinates;

        // For now things are layout left in the interface so copy to a
        // matching layout that is compatible with the operator.
        Kokkos::View<Coordinate **, map_device_type> source_nodes_copy(
            "src_nodes_copy", source_nodes.extent( 0 ),
            source_nodes.extent( 1 ) );
        Kokkos::deep_copy( source_nodes_copy, source_nodes );
        Kokkos::View<Coordinate **, map_device_type> target_nodes_copy(
            "tgt_nodes_copy", target_nodes.extent( 0 ),
            target_nodes.extent( 1 ) );
        Kokkos::deep_copy( target_nodes_copy, target_nodes );

        auto const which_map =
            ptree.get<std::string>( "Map Type", "Undefined" );
        if ( which_map == "Undefined" )
            throw DataTransferKitException(
                R"(Field "Map Type" is not defined in options string argument for map creation)" );
        else if ( which_map == "Nearest Neighbor" || which_map == "NN" )
            _map = std::unique_ptr<NearestNeighborOperator<map_device_type>>(
                new NearestNeighborOperator<map_device_type>(
                    comm, source_nodes_copy, target_nodes_copy ) );
        else if ( which_map == "Moving Least Squares" || which_map == "MLS" )
        {
            // NOTE if field "Order" is misspelled (for instance first letter
            // not capitalized), the default value (linear polynomials) will be
            // picked up without a warning or an error being raised.
            auto const order = ptree.get<std::string>( "Order", "Linear" );
            if ( order == "Linear" || order == "1" )
                _map = std::unique_ptr<MovingLeastSquaresOperator<
                    map_device_type, Wendland<0>,
                    MultivariatePolynomialBasis<Linear, 3>>>(
                    new MovingLeastSquaresOperator<
                        map_device_type, Wendland<0>,
                        MultivariatePolynomialBasis<Linear, 3>>(
                        comm, source_nodes_copy, target_nodes_copy ) );
            else if ( order == "Quadratic" || order == "2" )
                _map = std::unique_ptr<MovingLeastSquaresOperator<
                    map_device_type, Wendland<0>,
                    MultivariatePolynomialBasis<Quadratic, 3>>>(
                    new MovingLeastSquaresOperator<
                        map_device_type, Wendland<0>,
                        MultivariatePolynomialBasis<Quadratic, 3>>(
                        comm, source_nodes_copy, target_nodes_copy ) );
            else
                throw DataTransferKitException(
                    "Invalid order \"" + order +
                    "\" for creating a moving least squares map" );
        }
        else
            throw DataTransferKitException( "Invalid map type \"" + which_map +
                                            "\"" );
    }

    void apply( const std::string &source_field_name,
                const std::string &target_field_name ) override
    {
        // Create fields.
        auto source_field = _source.getField( source_field_name );
        auto target_field = _target.getField( target_field_name );

        // Pull the data from the source.
        _source.pullField( source_field_name, source_field );

        // Copy to a compatible layout. Nearest neighbor operator needs a
        // compatible layout and only 1 dimension
        int num_src = source_field.dofs.extent( 0 );
        Kokkos::View<double *, map_device_type> source_field_copy(
            "source_field_copy", num_src );
        Kokkos::deep_copy(
            source_field_copy,
            Kokkos::subview( source_field.dofs, Kokkos::ALL, 0 ) );
        int num_tgt = target_field.dofs.extent( 0 );
        Kokkos::View<double *, map_device_type> target_field_copy(
            "target_field_copy", num_tgt );

        // Apply the map.
        _map->apply( source_field_copy, target_field_copy );

        // Copy the transferred field back to the original target layout.
        Kokkos::deep_copy( Kokkos::subview( target_field.dofs, Kokkos::ALL, 0 ),
                           target_field_copy );

        // Push the data to the target.
        _target.pushField( target_field_name, target_field );
    }

    UserApplication<double, SourceMemSpace> _source;
    UserApplication<double, TargetMemSpace> _target;
    std::unique_ptr<PointCloudOperator<map_device_type>> _map;
};

//---------------------------------------------------------------------------//
// Execution space validation.
bool validExecutionSpace( DTK_ExecutionSpace space )
{
    switch ( space )
    {
    case DTK_SERIAL:
#if defined( KOKKOS_ENABLE_SERIAL )
        return true;
        break;
#else
        return false;
        break;
#endif

    case DTK_OPENMP:
#if defined( KOKKOS_ENABLE_OPENMP )
        return true;
        break;
#else
        return false;
        break;
#endif

    case DTK_CUDA:
#if defined( KOKKOS_ENABLE_CUDA )
        return true;
        break;
#else
        return false;
        break;
#endif
    default:
        return false;
        break;
    }
}

// Memory space validation.
bool validMemorySpace( DTK_MemorySpace space )
{
    switch ( space )
    {
    case DTK_HOST_SPACE:
#if defined( KOKKOS_ENABLE_SERIAL ) || defined( KOKKOS_ENABLE_OPENMP )
        return true;
        break;
#else
        return false;
        break;
#endif
    case DTK_CUDAUVM_SPACE:
#if defined( KOKKOS_ENABLE_CUDA )
        return true;
        break;
#else
        return false;
        break;
#endif
    default:
        return false;
        break;
    }
}

// Map space validation.
bool validMapSpaces( DTK_ExecutionSpace map_space, DTK_MemorySpace source_space,
                     DTK_MemorySpace target_space )
{
    return validExecutionSpace( map_space ) &&
           validMemorySpace( source_space ) && validMemorySpace( target_space );
}

//---------------------------------------------------------------------------//
// Create a map.
DTK_Map *createMap( DTK_ExecutionSpace map_space, MPI_Comm comm,
                    DTK_UserApplicationHandle source,
                    DTK_UserApplicationHandle target, const char *options )
{
    // Parse options.
    std::stringstream ss;
    ss.str( options );
    boost::property_tree::ptree ptree;
    // read_json calls seq_index_node which is not compatible with cuda 10 (see
    // https://stackoverflow.com/questions/55143135/error-cannot-call-member-function-impl-of-impl-pointer-in-boost)
    // So we disable reading json file for now
    //  try
    //  {
    //      boost::property_tree::read_json( ss, ptree );
    //  }
    //  catch ( boost::property_tree::json_parser_error )
    //  {
    //      throw DataTransferKitException(
    //          "Error while parsing JSON format in options string argument "
    //          "for map creation" );
    //  }

    // Get the user source and target memory spaces.
    DTK_MemorySpace src_space =
        reinterpret_cast<DataTransferKit::DTK_Registry *>( source )->_space;
    DTK_MemorySpace tgt_space =
        reinterpret_cast<DataTransferKit::DTK_Registry *>( target )->_space;

    // Check up front that we have been asked for execution and memory spaces
    // that are available in the kokkos build. This lets use a little cleaner
    // macro logic below because we know we will get a valid map space
    // already.
    DTK_INSIST( validMapSpaces( map_space, src_space, tgt_space ) );

    DTK_Map *map = nullptr;
    switch ( map_space )
    {
    case DTK_SERIAL:
#if defined( KOKKOS_ENABLE_SERIAL )
        switch ( src_space )
        {
        case DTK_HOST_SPACE:
            switch ( tgt_space )
            {
            case DTK_HOST_SPACE:
                map = new DTK_MapImpl<Serial, HostSpace, HostSpace>(
                    comm, source, target, ptree );
                break;

            case DTK_CUDAUVM_SPACE:
#if defined( KOKKOS_ENABLE_CUDA )
                map = new DTK_MapImpl<Serial, HostSpace, CudaUVMSpace>(
                    comm, source, target, ptree );
#endif
                break;
            }
            break;

        case DTK_CUDAUVM_SPACE:
#if defined( KOKKOS_ENABLE_CUDA )
            switch ( tgt_space )
            {
            case DTK_HOST_SPACE:
                map = new DTK_MapImpl<Serial, CudaUVMSpace, HostSpace>(
                    comm, source, target, ptree );
                break;

            case DTK_CUDAUVM_SPACE:
                map = new DTK_MapImpl<Serial, CudaUVMSpace, CudaUVMSpace>(
                    comm, source, target, ptree );
                break;
            }
#endif
            break;
        }
#endif
        break;

    case DTK_OPENMP:
#if defined( KOKKOS_ENABLE_OPENMP )
        switch ( src_space )
        {
        case DTK_HOST_SPACE:
            switch ( tgt_space )
            {
            case DTK_HOST_SPACE:
                map = new DTK_MapImpl<OpenMP, HostSpace, HostSpace>(
                    comm, source, target, ptree );
                break;

            case DTK_CUDAUVM_SPACE:
#if defined( KOKKOS_ENABLE_CUDA )
                map = new DTK_MapImpl<OpenMP, HostSpace, CudaUVMSpace>(
                    comm, source, target, ptree );
#endif
                break;
            }
            break;

        case DTK_CUDAUVM_SPACE:
#if defined( KOKKOS_ENABLE_CUDA )
            switch ( tgt_space )
            {
            case DTK_HOST_SPACE:
                map = new DTK_MapImpl<OpenMP, CudaUVMSpace, HostSpace>(
                    comm, source, target, ptree );
                break;

            case DTK_CUDAUVM_SPACE:
                map = new DTK_MapImpl<OpenMP, CudaUVMSpace, CudaUVMSpace>(
                    comm, source, target, ptree );
                break;
            }
#endif
            break;
        }
#endif
        break;

    case DTK_CUDA:
#if defined( KOKKOS_ENABLE_CUDA )
        switch ( src_space )
        {
        case DTK_HOST_SPACE:
#if defined( KOKKOS_ENABLE_SERIAL ) || defined( KOKKOS_ENABLE_OPENMP )
            switch ( tgt_space )
            {
            case DTK_HOST_SPACE:
                map = new DTK_MapImpl<Cuda, HostSpace, HostSpace>(
                    comm, source, target, ptree );
                break;

            case DTK_CUDAUVM_SPACE:
                map = new DTK_MapImpl<Cuda, HostSpace, CudaUVMSpace>(
                    comm, source, target, ptree );
                break;
            }
#endif
            break;

        case DTK_CUDAUVM_SPACE:
            switch ( tgt_space )
            {
            case DTK_HOST_SPACE:
#if defined( KOKKOS_ENABLE_SERIAL ) || defined( KOKKOS_ENABLE_OPENMP )
                map = new DTK_MapImpl<Cuda, CudaUVMSpace, HostSpace>(
                    comm, source, target, ptree );
#endif
                break;
            case DTK_CUDAUVM_SPACE:
                map = new DTK_MapImpl<Cuda, CudaUVMSpace, CudaUVMSpace>(
                    comm, source, target, ptree );
                break;
            }
            break;
        }
#endif
        break;
    }

    return map;
}

//---------------------------------------------------------------------------//

} // namespace DataTransferKit

#endif // DTK_C_API_MAP_HPP


// Copyright (c) 2016 by Sony Computer Entertainment Inc.
// This file is subject to the terms and conditions defined in file
// 'LICENSE.txt', which is part of this source code package.

#ifndef SCANLIB_PROGRESS_HPP
#define SCANLIB_PROGRESS_HPP

#include <atomic>
#include <chrono>
#include <functional>
#include <iosfwd>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
//#include <vector>
#include <boost/circular_buffer.hpp>

namespace details {
    // ****************
    // * progress_bar *
    // ****************
    class progress_bar {
    public:
        explicit progress_bar (unsigned width = 40);
        void set_value (float value);
        std::ostream & write (std::ostream & os) const;

    private:
        unsigned const width_;
        float value_ = 0.0;
    };

    std::ostream & operator << (std::ostream & os, progress_bar const & prog);

    // *************************
    // * hours_minutes_seconds *
    // *************************
    class hours_minutes_seconds {
    public:
        hours_minutes_seconds (float secs);

        std::string to_string () const;
        std::ostream & write (std::ostream & os) const;

        unsigned hours () const { return hours_; }
        unsigned minutes () const { return minutes_; }
        unsigned seconds () const { return seconds_; }

    private:
        unsigned hours_;
        unsigned minutes_;
        unsigned seconds_;

        static constexpr auto const minute_threshold_for_no_seconds = 2U;
        static constexpr auto const seconds_in_minute = 60U;
        static constexpr auto const minutes_in_hour = 60U;
        static constexpr auto const seconds_in_hour = 60U * 60U;
    };

    std::ostream & operator << (std::ostream & os, hours_minutes_seconds const & hms);

    // *****************************
    // * time_remaining_calculator *
    // *****************************
    class time_remaining_calculator {
    public:
        explicit time_remaining_calculator (std::size_t size);

        float update (float speed);

        float mean () const { return stats_.mean (); }
        std::size_t samples () const { return stats_.samples (); }
        float standard_deviation () const { return stats_.stddev (); }

        class stats {
        public:
            // Adds 'x' to the sample collection.
            void push (float x);
            // Removes 'x' from the sample collection.
            void pop (float x);

            std::size_t samples () const { return samples_; }
            float mean () const { return mean_; }
            float stddev () const;

        private:
            float variance () const;

            std::size_t samples_ = 0U;
            float mean_ = 0.0F;
            float s_ = 0.0F;
        };

    private:
        boost::circular_buffer<float> buffer_;
        stats stats_;
    };
}

// *********
// * timer *
// *********
class timer {
public:
    timer (std::function <void ()> proc, std::chrono::milliseconds interval);
    ~timer () noexcept;
    void clear ();

private:
    void run (std::function <void ()> proc, std::chrono::milliseconds interval) noexcept;

    std::atomic <bool> running_ {true};
    std::thread thread_;
};

typedef std::shared_ptr <timer>  timer_ptr;

timer_ptr setInterval (std::function <void ()> proc, std::chrono::milliseconds interval);
void clearInterval (timer_ptr timer) noexcept;

// ****************
// * updater_intf *
// ****************
class updater_intf {
public:
    virtual ~updater_intf () noexcept = 0;

    virtual updater_intf & completed (unsigned /*c*/) { return *this; }
    virtual updater_intf & completed_incr (unsigned /*by*/ = 1) { return *this; }

    virtual updater_intf & total (unsigned /*value*/) { return *this; }
    virtual updater_intf & total_incr (unsigned /*by*/ = 1) { return *this; }

    virtual updater_intf & run () { return *this; }
};

// ******************
// * silent_updater *
// ******************
class silent_updater final : public updater_intf {
public:
    explicit silent_updater (char const * /*message*/ = nullptr);
    ~silent_updater () noexcept;
};

// ***********
// * updater *
// ***********
class updater final : public updater_intf {
public:
    explicit updater (char const * message = nullptr, unsigned total = 0);
    ~updater () noexcept override;

    unsigned completed () const;
    updater & completed (unsigned c) override;
    updater & completed_incr (unsigned by = 1) override;

    unsigned total () const;
    updater & total (unsigned value) override;
    updater & total_incr (unsigned by = 1) override;

    updater & run () override;

private:
    std::ostream & time_left (float const average, std::ostream & os) const;
    bool have_reliable_estimate (float const average) const;

    void update ();

    mutable std::mutex mutex_;

    details::progress_bar bar_;
    // time remaining estimates will be based on the completion rate that we've achieved
    // over the last 30 seconds.
    details::time_remaining_calculator time_remaining_;
    unsigned completed_ = 0;
    unsigned total_ = 0;

    unsigned old_completed_ = 0;
    std::size_t prev_width_ = 0;

    timer_ptr timer_;
};
#endif // SCANLIB_PROGRESS_HPP
// eof progress.hpp


/*=========================================================================
 *
 *  Copyright David Doria 2011 daviddoria@gmail.com
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  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.
 *
 *=========================================================================*/

// Custom
#include "Helpers/HelpersOutput.h"

// Pixel descriptors
#include "PixelDescriptors/ImagePatchPixelDescriptor.h"
#include "PixelDescriptors/FeatureVectorPixelDescriptor.h"

// Descriptor visitors
#include "Visitors/DescriptorVisitors/ImagePatchDescriptorVisitor.hpp"
#include "Visitors/DescriptorVisitors/FeatureVectorPrecomputedStructuredGridDescriptorVisitor.hpp"
#include "Visitors/DescriptorVisitors/CompositeDescriptorVisitor.hpp"

// Inpainting visitors
#include "Visitors/InpaintingVisitor.hpp"

// Nearest neighbors
#include "NearestNeighbor/LinearSearchBestProperty.hpp"
#include "NearestNeighbor/LinearSearchKNNProperty.hpp"
#include "NearestNeighbor/TwoStepNearestNeighbor.hpp"

// Initializers
#include "Initializers/InitializeFromMaskImage.hpp"
#include "Initializers/InitializePriority.hpp"

// Inpainters
#include "Inpainters/MaskedGridPatchInpainter.hpp"
#include "Inpainters/HoleListPatchInpainter.hpp"

// Difference functions
#include "DifferenceFunctions/ImagePatchDifference.hpp"
#include "DifferenceFunctions/FeatureVectorDifference.hpp"

// Inpainting
#include "Algorithms/InpaintingAlgorithm.hpp"

// Priority
#include "Priority/PriorityRandom.h"

// ITK
#include "itkImageFileReader.h"

// VTK
#include <vtkPolyData.h>
#include <vtkSmartPointer.h>
#include <vtkXMLPolyDataReader.h>
#include <vtkXMLStructuredGridReader.h>

// Boost
#include <boost/graph/grid_graph.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/detail/d_ary_heap.hpp>

// Debug
#include "Helpers/HelpersOutput.h"

// Run with: trashcan.mha trashcan_mask.mha 15 trashcan.vts Intensity filled.mha
int main(int argc, char *argv[])
{
  // Verify arguments
  if(argc != 7)
    {
    std::cerr << "Required arguments: image.mha imageMask.mha patch_half_width structuredGrid.vts featureName output.mha" << std::endl;
    std::cerr << "Input arguments: ";
    for(int i = 1; i < argc; ++i)
      {
      std::cerr << argv[i] << " ";
      }
    return EXIT_FAILURE;
    }

  // Parse arguments
  std::string imageFilename = argv[1];
  std::string maskFilename = argv[2];

  std::stringstream ssPatchRadius;
  ssPatchRadius << argv[3];
  unsigned int patch_half_width = 0;
  ssPatchRadius >> patch_half_width;

  std::string structuredGridFileName = argv[4];
  std::string featureName = argv[5];

  std::string outputFilename = argv[6];

  // Output arguments
  std::cout << "Reading image: " << imageFilename << std::endl;
  std::cout << "Reading mask: " << maskFilename << std::endl;
  std::cout << "Patch half width: " << patch_half_width << std::endl;
  std::cout << "Reading structured grid: " << structuredGridFileName << std::endl;
  std::cout << "Feature name: " << featureName << std::endl;
  std::cout << "Output: " << outputFilename << std::endl;

  vtkSmartPointer<vtkXMLStructuredGridReader> structuredGridReader = vtkSmartPointer<vtkXMLStructuredGridReader>::New();
  structuredGridReader->SetFileName(structuredGridFileName.c_str());
  structuredGridReader->Update();

  typedef FloatVectorImageType ImageType;

  typedef  itk::ImageFileReader<ImageType> ImageReaderType;
  ImageReaderType::Pointer imageReader = ImageReaderType::New();
  imageReader->SetFileName(imageFilename);
  imageReader->Update();

  ImageType::Pointer image = ImageType::New();
  ITKHelpers::DeepCopy(imageReader->GetOutput(), image.GetPointer());

  Mask::Pointer mask = Mask::New();
  mask->Read(maskFilename);

  std::cout << "hole pixels: " << mask->CountHolePixels() << std::endl;
  std::cout << "valid pixels: " << mask->CountValidPixels() << std::endl;

  typedef ImagePatchPixelDescriptor<ImageType> ImagePatchPixelDescriptorType;
  typedef FeatureVectorPixelDescriptor FeatureVectorPixelDescriptorType;

  // Create the graph
  typedef boost::grid_graph<2> VertexListGraphType;
  boost::array<std::size_t, 2> graphSideLengths = { { imageReader->GetOutput()->GetLargestPossibleRegion().GetSize()[0],
                                                      imageReader->GetOutput()->GetLargestPossibleRegion().GetSize()[1] } };
  VertexListGraphType graph(graphSideLengths);
  typedef boost::graph_traits<VertexListGraphType>::vertex_descriptor VertexDescriptorType;

  // Get the index map
  typedef boost::property_map<VertexListGraphType, boost::vertex_index_t>::const_type IndexMapType;
  IndexMapType indexMap(get(boost::vertex_index, graph));

  // Create the priority map
  typedef boost::vector_property_map<float, IndexMapType> PriorityMapType;
  PriorityMapType priorityMap(num_vertices(graph), indexMap);

  // Create the node fill status map. Each pixel is either filled (true) or not filled (false).
  typedef boost::vector_property_map<bool, IndexMapType> FillStatusMapType;
  FillStatusMapType fillStatusMap(num_vertices(graph), indexMap);

  // Create the boundary status map. A node is on the current boundary if this property is true.
  // This property helps the boundaryNodeQueue because we can mark here if a node has become no longer
  // part of the boundary, so when the queue is popped we can check this property to see if it should
  // actually be processed.
  typedef boost::vector_property_map<bool, IndexMapType> BoundaryStatusMapType;
  BoundaryStatusMapType boundaryStatusMap(num_vertices(graph), indexMap);

  // Create the descriptor map. This is where the data for each pixel is stored.
  typedef boost::vector_property_map<ImagePatchPixelDescriptorType, IndexMapType> ImagePatchDescriptorMapType;
  ImagePatchDescriptorMapType imagePatchDescriptorMap(num_vertices(graph), indexMap);

  // Create the descriptor map. This is where the data for each pixel is stored.
  typedef boost::vector_property_map<FeatureVectorPixelDescriptorType, IndexMapType> FeatureVectorDescriptorMapType;
  FeatureVectorDescriptorMapType featureVectorDescriptorMap(num_vertices(graph), indexMap);

  // Create the patch inpainter. The inpainter needs to know the status of each pixel to determine if they should be inpainted.
  typedef MaskedGridPatchInpainter<FillStatusMapType> InpainterType;
  InpainterType patchInpainter(patch_half_width, fillStatusMap);

  // Create the priority function
  typedef PriorityRandom PriorityType;
  PriorityType priorityFunction;

  // Create the boundary node queue. The priority of each node is used to order the queue.
  typedef boost::vector_property_map<std::size_t, IndexMapType> IndexInHeapMap;
  IndexInHeapMap index_in_heap(indexMap);

  // Create the priority compare functor
  typedef std::less<float> PriorityCompareType;
  PriorityCompareType lessThanFunctor;

  typedef boost::d_ary_heap_indirect<VertexDescriptorType, 4, IndexInHeapMap, PriorityMapType, PriorityCompareType> BoundaryNodeQueueType;
  BoundaryNodeQueueType boundaryNodeQueue(priorityMap, index_in_heap, lessThanFunctor);

  // Create the descriptor visitors
  typedef FeatureVectorPrecomputedStructuredGridDescriptorVisitor<VertexListGraphType, FeatureVectorDescriptorMapType> FeatureVectorPrecomputedStructuredGridDescriptorVisitorType;
  FeatureVectorPrecomputedStructuredGridDescriptorVisitorType featureVectorPrecomputedStructuredGridDescriptorVisitor(featureVectorDescriptorMap, structuredGridReader->GetOutput(), featureName);

  typedef ImagePatchDescriptorVisitor<VertexListGraphType, ImageType, ImagePatchDescriptorMapType> ImagePatchDescriptorVisitorType;
  ImagePatchDescriptorVisitorType imagePatchDescriptorVisitor(image, mask, imagePatchDescriptorMap, patch_half_width);

  typedef CompositeDescriptorVisitor<VertexListGraphType> CompositeDescriptorVisitorType;
  CompositeDescriptorVisitorType compositeDescriptorVisitor;
  compositeDescriptorVisitor.AddVisitor(&imagePatchDescriptorVisitor);
  compositeDescriptorVisitor.AddVisitor(&featureVectorPrecomputedStructuredGridDescriptorVisitor);

  // Create the inpainting visitor
  typedef InpaintingVisitor<VertexListGraphType, ImageType, BoundaryNodeQueueType, FillStatusMapType,
                            CompositeDescriptorVisitorType, PriorityType, PriorityMapType, BoundaryStatusMapType> InpaintingVisitorType;
  InpaintingVisitorType inpaintingVisitor(image, mask, boundaryNodeQueue, fillStatusMap,
                                          compositeDescriptorVisitor, priorityMap, &priorityFunction, patch_half_width, boundaryStatusMap);

  InitializePriority(mask, boundaryNodeQueue, priorityMap, &priorityFunction, boundaryStatusMap);

  // Initialize the boundary node queue from the user provided mask image.
  InitializeFromMaskImage(mask, &inpaintingVisitor, graph, fillStatusMap);
  std::cout << "PatchBasedInpaintingNonInteractive: There are " << boundaryNodeQueue.size()
            << " nodes in the boundaryNodeQueue" << std::endl;

  // Create the nearest neighbor finder
  typedef LinearSearchKNNProperty<FeatureVectorDescriptorMapType, FeatureVectorDifference> KNNSearchType;
  KNNSearchType linearSearchKNN(featureVectorDescriptorMap, 1000);

  typedef LinearSearchBestProperty<ImagePatchDescriptorMapType, ImagePatchDifference<ImagePatchPixelDescriptorType> > BestSearchType;
  BestSearchType linearSearchBest(imagePatchDescriptorMap);

  TwoStepNearestNeighbor<KNNSearchType, BestSearchType> twoStepNearestNeighbor(linearSearchKNN, linearSearchBest);

  // Perform the inpainting
  inpainting_loop(graph, inpaintingVisitor, boundaryStatusMap, boundaryNodeQueue, twoStepNearestNeighbor, patchInpainter);

  HelpersOutput::WriteImage<ImageType>(image, outputFilename);

  return EXIT_SUCCESS;
}


#pragma once

#include <algorithm>
#include <vector>
#include <Eigen/Dense>
#include "genetic_code.hpp"
#include "xlorad.hpp"

namespace lorad {

    class QMatrix {

        public:
            typedef std::vector<double>             freq_xchg_t;
            typedef std::shared_ptr<freq_xchg_t>    freq_xchg_ptr_t;
            typedef double                          omega_t;
            typedef std::shared_ptr<omega_t>        omega_ptr_t;
            typedef boost::shared_ptr<QMatrix>      SharedPtr;

                                                    QMatrix();
            virtual                                 ~QMatrix();

            virtual void                            clear() = 0;

            virtual void                            setEqualStateFreqs(freq_xchg_ptr_t freq_ptr) = 0;
            virtual void                            setStateFreqsSharedPtr(freq_xchg_ptr_t freq_ptr) = 0;
            virtual void                            setStateFreqs(freq_xchg_t & freq) = 0;
            virtual freq_xchg_ptr_t                 getStateFreqsSharedPtr() = 0;
            virtual const double *                  getStateFreqs() const = 0;
            void                                    fixStateFreqs(bool is_fixed);
            bool                                    isFixedStateFreqs() const;

            virtual void                            setEqualExchangeabilities(freq_xchg_ptr_t xchg_ptr) = 0;
            virtual void                            setExchangeabilitiesSharedPtr(freq_xchg_ptr_t xchg) = 0;
            virtual void                            setExchangeabilities(freq_xchg_t & xchg) = 0;
            virtual freq_xchg_ptr_t                 getExchangeabilitiesSharedPtr() = 0;
            virtual const double *                  getExchangeabilities() const = 0;
            void                                    fixExchangeabilities(bool is_fixed);
            bool                                    isFixedExchangeabilities() const;

            virtual void                            setOmegaSharedPtr(omega_ptr_t omega) = 0;
            virtual void                            setOmega(omega_t omega) = 0;
            virtual omega_ptr_t                     getOmegaSharedPtr() = 0;
            virtual double                          getOmega() const = 0;
            void                                    fixOmega(bool is_fixed);
            bool                                    isFixedOmega() const;

#if defined(POLGSS)
            virtual void                            setStateFreqRefDistParamsSharedPtr(QMatrix::freq_xchg_ptr_t freq_params_ptr) = 0;
            virtual std::vector<double>             getStateFreqRefDistParamsVect() const = 0;
            virtual void                            setExchangeabilityRefDistParamsSharedPtr(QMatrix::freq_xchg_ptr_t xchg_params_ptr) = 0;
            virtual std::vector<double>             getExchangeabilityRefDistParamsVect() const = 0;
#endif

            virtual const double *                  getEigenvectors() const = 0;
            virtual const double *                  getInverseEigenvectors() const = 0;
            virtual const double *                  getEigenvalues() const = 0;

            void                                    setActive(bool activate);

        protected:

            virtual void                            recalcRateMatrix() = 0;
            void                                    normalizeFreqsOrExchangeabilities(freq_xchg_ptr_t v);

            bool                                    _is_active;
            bool                                    _state_freqs_fixed;
            bool                                    _exchangeabilities_fixed;
            bool                                    _omega_fixed;

#if defined(POLGSS)
            freq_xchg_ptr_t                         _state_freq_refdist;
            freq_xchg_ptr_t                         _exchangeability_refdist;
#endif
    };

    inline QMatrix::QMatrix() {
    }

    inline QMatrix::~QMatrix() {
    }

    inline void QMatrix::setActive(bool activate) {
        _is_active = activate;
        recalcRateMatrix();
    }

    inline void QMatrix::clear() {
        _is_active = false;
        _state_freqs_fixed = false;
        _exchangeabilities_fixed = false;
        _omega_fixed = false;
    }

    inline void QMatrix::fixStateFreqs(bool is_fixed) {
        _state_freqs_fixed = is_fixed;
    }

    inline void QMatrix::fixExchangeabilities(bool is_fixed) {
        _exchangeabilities_fixed = is_fixed;
    }

    inline void QMatrix::fixOmega(bool is_fixed) {
        _omega_fixed = is_fixed;
    }

    inline bool QMatrix::isFixedStateFreqs() const {
        return _state_freqs_fixed;
    }

    inline bool QMatrix::isFixedExchangeabilities() const {
        return _exchangeabilities_fixed;
    }

    inline bool QMatrix::isFixedOmega() const {
        return _omega_fixed;
    }

    inline void QMatrix::normalizeFreqsOrExchangeabilities(QMatrix::freq_xchg_ptr_t v) {
        // Be sure elements of v sum to 1.0 and assert that they are all positive
        double sum_v = std::accumulate(v->begin(), v->end(), 0.0);
        for (auto & x : *v) {
            assert(x > 0.0);
            x /= sum_v;
        }
    }

    class QMatrixNucleotide : public QMatrix {

        public:
            typedef Eigen::Matrix<double, 4, 4, Eigen::RowMajor>    eigenMatrix4d_t;
            typedef Eigen::Vector4d                                 eigenVector4d_t;

                                        QMatrixNucleotide();
                                        ~QMatrixNucleotide();

            void                        clear();

            void                        setEqualStateFreqs(freq_xchg_ptr_t freq_ptr);
            void                        setStateFreqsSharedPtr(freq_xchg_ptr_t freq_ptr);
            void                        setStateFreqs(freq_xchg_t & freqs);
            freq_xchg_ptr_t             getStateFreqsSharedPtr();
            const double *              getStateFreqs() const;

            void                        setEqualExchangeabilities(freq_xchg_ptr_t xchg_ptr);
            void                        setExchangeabilitiesSharedPtr(freq_xchg_ptr_t xchg_ptr);
            void                        setExchangeabilities(freq_xchg_t & xchg);
            freq_xchg_ptr_t             getExchangeabilitiesSharedPtr();
            const double *              getExchangeabilities() const;

            void                        setOmegaSharedPtr(omega_ptr_t omega_ptr);
            void                        setOmega(omega_t omega);
            omega_ptr_t                 getOmegaSharedPtr();
            double                      getOmega() const;

#if defined(POLGSS)
            void                        setStateFreqRefDistParamsSharedPtr(QMatrix::freq_xchg_ptr_t freq_params_ptr);
            std::vector<double>         getStateFreqRefDistParamsVect() const;
            void                        setExchangeabilityRefDistParamsSharedPtr(QMatrix::freq_xchg_ptr_t xchg_params_ptr);
            std::vector<double>         getExchangeabilityRefDistParamsVect() const;
#endif

            const double *              getEigenvectors() const;
            const double *              getInverseEigenvectors() const;
            const double *              getEigenvalues() const;

            EIGEN_MAKE_ALIGNED_OPERATOR_NEW

        protected:

            virtual void                recalcRateMatrix();

        private:

            // workspaces for computing eigenvectors/eigenvalues
            eigenMatrix4d_t             _sqrtPi;
            eigenMatrix4d_t             _sqrtPiInv;
            eigenMatrix4d_t             _Q;
            eigenMatrix4d_t             _eigenvectors;
            eigenMatrix4d_t             _inverse_eigenvectors;
            eigenVector4d_t             _eigenvalues;

            freq_xchg_ptr_t             _state_freqs;
            freq_xchg_ptr_t             _exchangeabilities;
    };

    inline QMatrixNucleotide::QMatrixNucleotide() {
        clear();
    }

    inline QMatrixNucleotide::~QMatrixNucleotide() {
    }

    inline void QMatrixNucleotide::clear() {
        QMatrix::clear();

        QMatrix::freq_xchg_t xchg = {1,1,1,1,1,1};
        _exchangeabilities = std::make_shared<QMatrix::freq_xchg_t>(xchg);

        QMatrix::freq_xchg_t freq_vect = {0.25, 0.25, 0.25, 0.25};
        _state_freqs = std::make_shared<QMatrix::freq_xchg_t>(freq_vect);

#if defined(POLGSS)
        QMatrix::freq_xchg_t freq_param_vect = {1.0, 1.0, 1.0, 1.0};
        _state_freq_refdist = std::make_shared<QMatrix::freq_xchg_t>(freq_param_vect);

        QMatrix::freq_xchg_t xchg_param_vect = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
        _exchangeability_refdist = std::make_shared<QMatrix::freq_xchg_t>(xchg_param_vect);
#endif

        recalcRateMatrix();
    }

    inline QMatrix::freq_xchg_ptr_t QMatrixNucleotide::getExchangeabilitiesSharedPtr() {
        return _exchangeabilities;
    }

    inline QMatrix::freq_xchg_ptr_t QMatrixNucleotide::getStateFreqsSharedPtr() {
        return _state_freqs;
    }

    inline QMatrix::omega_ptr_t QMatrixNucleotide::getOmegaSharedPtr() {
        assert(false);
        return nullptr;
    }

    inline const double * QMatrixNucleotide::getEigenvectors() const {
        return _eigenvectors.data();
    }

    inline const double * QMatrixNucleotide::getInverseEigenvectors() const {
        return _inverse_eigenvectors.data();
    }

    inline const double * QMatrixNucleotide::getEigenvalues() const {
        return _eigenvalues.data();
    }

    inline const double * QMatrixNucleotide::getExchangeabilities() const {
        return &(*_exchangeabilities)[0];
    }

    inline const double * QMatrixNucleotide::getStateFreqs() const {
        return &(*_state_freqs)[0];
    }

    inline double QMatrixNucleotide::getOmega() const {
        assert(false);
        return 0.0;
    }

    inline void QMatrixNucleotide::setEqualExchangeabilities(QMatrix::freq_xchg_ptr_t xchg_ptr) {
        _exchangeabilities = xchg_ptr;
        _exchangeabilities->assign(6, 1.0/6.0);
        recalcRateMatrix();
    }

    inline void QMatrixNucleotide::setExchangeabilitiesSharedPtr(QMatrix::freq_xchg_ptr_t xchg_ptr) {
        if (xchg_ptr->size() != 6)
            throw XLorad(boost::format("Expecting 6 exchangeabilities and got %d: perhaps you meant to specify a subset data type other than nucleotide") % xchg_ptr->size());
        _exchangeabilities = xchg_ptr;
        normalizeFreqsOrExchangeabilities(_exchangeabilities);
        recalcRateMatrix();
    }

    inline void QMatrixNucleotide::setExchangeabilities(QMatrix::freq_xchg_t & xchg) {
        if (xchg.size() != 6)
            throw XLorad(boost::format("Expecting 6 exchangeabilities and got %d: perhaps you meant to specify a subset data type other than nucleotide") % xchg.size());
        std::copy(xchg.begin(), xchg.end(), _exchangeabilities->begin());
        recalcRateMatrix();
    }

    inline void QMatrixNucleotide::setEqualStateFreqs(QMatrix::freq_xchg_ptr_t freq_ptr) {
        _state_freqs = freq_ptr;
        _state_freqs->assign(4, 0.25);
        recalcRateMatrix();
    }

    inline void QMatrixNucleotide::setStateFreqsSharedPtr(QMatrix::freq_xchg_ptr_t freq_ptr) {
        if (freq_ptr->size() != 4)
            throw XLorad(boost::format("Expecting 4 state frequencies and got %d: perhaps you meant to specify a subset data type other than nucleotide") % freq_ptr->size());
        double sum_of_freqs = std::accumulate(freq_ptr->begin(), freq_ptr->end(), 0.0);
        if (std::fabs(sum_of_freqs - 1.0) > 0.001)
            throw XLorad(boost::format("Expecting sum of 4 state frequencies to be 1, but instead got %g") % sum_of_freqs);
        _state_freqs = freq_ptr;
        recalcRateMatrix();
    }

    inline void QMatrixNucleotide::setStateFreqs(QMatrix::freq_xchg_t & freqs) {
        if (freqs.size() != 4)
            throw XLorad(boost::format("Expecting 4 state frequencies and got %d: perhaps you meant to specify a subset data type other than nucleotide") % freqs.size());
        std::copy(freqs.begin(), freqs.end(), _state_freqs->begin());
        recalcRateMatrix();
    }

    inline void QMatrixNucleotide::setOmegaSharedPtr(QMatrix::omega_ptr_t omega_ptr) {
        assert(false);
    }

    inline void QMatrixNucleotide::setOmega(QMatrix::omega_t omega) {
        assert(false);
    }

#if defined(POLGSS)
    inline void QMatrixNucleotide::setStateFreqRefDistParamsSharedPtr(QMatrix::freq_xchg_ptr_t freq_params_ptr) {
        if (freq_params_ptr->size() != 4)
            throw XLorad(boost::format("Expecting 4 state frequency reference distribution parameters and got %d: perhaps you meant to specify a subset data type other than nucleotide") % freq_params_ptr->size());
        _state_freq_refdist = freq_params_ptr;
    }

    inline std::vector<double> QMatrixNucleotide::getStateFreqRefDistParamsVect() const {
        return std::vector<double>(_state_freq_refdist->begin(), _state_freq_refdist->end());
    }

    inline void QMatrixNucleotide::setExchangeabilityRefDistParamsSharedPtr(QMatrix::freq_xchg_ptr_t xchg_params_ptr) {
        if (xchg_params_ptr->size() != 6)
            throw XLorad(boost::format("Expecting 6 exchangeability reference distribution parameters and got %d: perhaps you meant to specify a subset data type other than nucleotide") % xchg_params_ptr->size());
        _exchangeability_refdist = xchg_params_ptr;
    }

    inline std::vector<double> QMatrixNucleotide::getExchangeabilityRefDistParamsVect() const {
        return std::vector<double>(_exchangeability_refdist->begin(), _exchangeability_refdist->end());
    }
#endif

    inline void QMatrixNucleotide::recalcRateMatrix() {
        // Must have assigned both _state_freqs and _exchangeabilities to recalculate rate matrix
        if (!_is_active || !(_state_freqs && _exchangeabilities))
            return;

        double piA = (*_state_freqs)[0];
        double piC = (*_state_freqs)[1];
        double piG = (*_state_freqs)[2];
        double piT = (*_state_freqs)[3];

        Eigen::Map<const Eigen::Array4d> tmp(_state_freqs->data());
        _sqrtPi = tmp.sqrt().matrix().asDiagonal();
        _sqrtPiInv = _sqrtPi.inverse();

        assert(_exchangeabilities->size() == 6);
        double rAC = (*_exchangeabilities)[0];
        double rAG = (*_exchangeabilities)[1];
        double rAT = (*_exchangeabilities)[2];
        double rCG = (*_exchangeabilities)[3];
        double rCT = (*_exchangeabilities)[4];
        double rGT = (*_exchangeabilities)[5];

        double inverse_scaling_factor = piA*(rAC*piC + rAG*piG + rAT*piT) + piC*(rAC*piA + rCG*piG + rCT*piT) + piG*(rAG*piA + rCG*piC + rGT*piT) + piT*(rAT*piA + rCT*piC + rGT*piG);
        double scaling_factor = 1.0/inverse_scaling_factor;

        _Q(0,0) = -scaling_factor*(rAC*piC + rAG*piG + rAT*piT);
        _Q(0,1) = scaling_factor*rAC*piC;
        _Q(0,2) = scaling_factor*rAG*piG;
        _Q(0,3) = scaling_factor*rAT*piT;

        _Q(1,0) = scaling_factor*rAC*piA;
        _Q(1,1) = -scaling_factor*(rAC*piA + rCG*piG + rCT*piT);
        _Q(1,2) = scaling_factor*rCG*piG;
        _Q(1,3) = scaling_factor*rCT*piT;

        _Q(2,0) = scaling_factor*rAG*piA;
        _Q(2,1) = scaling_factor*rCG*piC;
        _Q(2,2) = -scaling_factor*(rAG*piA + rCG*piC + rGT*piT);
        _Q(2,3) = scaling_factor*rGT*piT;

        _Q(3,0) = scaling_factor*rAT*piA;
        _Q(3,1) = scaling_factor*rCT*piC;
        _Q(3,2) = scaling_factor*rGT*piG;
        _Q(3,3) = -scaling_factor*(rAT*piA + rCT*piC + rGT*piG);

        // S is a symmetric matrix
        eigenMatrix4d_t S = eigenMatrix4d_t(_sqrtPi*_Q*_sqrtPiInv);

        // Can use efficient eigensystem solver because S is symmetric
        Eigen::SelfAdjointEigenSolver<Eigen::Matrix4d> solver(S);
        if (solver.info() != Eigen::Success) {
            throw XLorad("Error in the calculation of eigenvectors and eigenvalues of the GTR rate matrix");
        }

        _eigenvectors           = _sqrtPiInv*solver.eigenvectors();
        _inverse_eigenvectors   = solver.eigenvectors().transpose()*_sqrtPi;
        _eigenvalues            = solver.eigenvalues();
    }

    class QMatrixCodon : public QMatrix {

        public:
            typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>      eigenMatrixXd_t;
            typedef Eigen::VectorXd                                                             eigenVectorXd_t;

                                        QMatrixCodon(GeneticCode::SharedPtr gcode);
                                        ~QMatrixCodon();

            void                        clear();

            void                        setEqualStateFreqs(freq_xchg_ptr_t freq_ptr);
            void                        setStateFreqsSharedPtr(freq_xchg_ptr_t freq_ptr);
            void                        setStateFreqs(freq_xchg_t & freqs);
            freq_xchg_ptr_t             getStateFreqsSharedPtr();
            const double *              getStateFreqs() const;

            void                        setEqualExchangeabilities(freq_xchg_ptr_t xchg_ptr);
            void                        setExchangeabilitiesSharedPtr(freq_xchg_ptr_t xchg_ptr);
            void                        setExchangeabilities(freq_xchg_t & xchg);
            freq_xchg_ptr_t             getExchangeabilitiesSharedPtr();
            const double *              getExchangeabilities() const;

            void                        setOmegaSharedPtr(omega_ptr_t omega_ptr);
            void                        setOmega(omega_t omega);
            omega_ptr_t                 getOmegaSharedPtr();
            double                      getOmega() const;

#if defined(POLGSS)
            void                        setStateFreqRefDistParamsSharedPtr(QMatrix::freq_xchg_ptr_t freq_params_ptr);
            std::vector<double>         getStateFreqRefDistParamsVect() const;
            void                        setExchangeabilityRefDistParamsSharedPtr(QMatrix::freq_xchg_ptr_t xchg_params_ptr);
            std::vector<double>         getExchangeabilityRefDistParamsVect() const;
#endif

            const double *              getEigenvectors() const;
            const double *              getInverseEigenvectors() const;
            const double *              getEigenvalues() const;

        protected:

            virtual void                recalcRateMatrix();

        private:

            // workspaces for computing eigenvectors/eigenvalues
            eigenMatrixXd_t             _sqrtPi;
            eigenMatrixXd_t             _sqrtPiInv;
            eigenMatrixXd_t             _Q;
            eigenMatrixXd_t             _eigenvectors;
            eigenMatrixXd_t             _inverse_eigenvectors;
            eigenVectorXd_t             _eigenvalues;

            freq_xchg_ptr_t             _state_freqs;
            omega_ptr_t                 _omega;

            std::vector<std::string>    _codons;
            std::vector<unsigned>       _amino_acids;

            GeneticCode::SharedPtr      _genetic_code;
    };

    inline QMatrixCodon::QMatrixCodon(GeneticCode::SharedPtr gcode) {
        assert(gcode);
        _genetic_code = gcode;
        clear();
    }

    inline QMatrixCodon::~QMatrixCodon() {
    }

    inline void QMatrixCodon::clear() {
        QMatrix::clear();

        unsigned nstates = _genetic_code->getNumNonStopCodons();
        _genetic_code->copyCodons(_codons);
        _genetic_code->copyAminoAcids(_amino_acids);

        QMatrix::omega_t omega = 0.1;
        _omega = std::make_shared<QMatrix::omega_t>(omega);

        QMatrix::freq_xchg_t freq_vect(nstates, 1./nstates);
        _state_freqs = std::make_shared<QMatrix::freq_xchg_t>(freq_vect);

        _sqrtPi.resize(nstates, nstates);
        _sqrtPiInv.resize(nstates, nstates);
        _Q.resize(nstates, nstates);
        _eigenvectors.resize(nstates, nstates);
        _inverse_eigenvectors.resize(nstates, nstates);
        _eigenvalues.resize(nstates);

        recalcRateMatrix();
    }

    inline QMatrix::freq_xchg_ptr_t QMatrixCodon::getExchangeabilitiesSharedPtr() {
        assert(false);
        return nullptr;
    }

    inline QMatrix::freq_xchg_ptr_t QMatrixCodon::getStateFreqsSharedPtr() {
        return _state_freqs;
    }

    inline QMatrix::omega_ptr_t QMatrixCodon::getOmegaSharedPtr() {
        return _omega;
    }

    inline const double * QMatrixCodon::getEigenvectors() const {
        return _eigenvectors.data();
    }

    inline const double * QMatrixCodon::getInverseEigenvectors() const {
        return _inverse_eigenvectors.data();
    }

    inline const double * QMatrixCodon::getEigenvalues() const {
        return _eigenvalues.data();
    }

    inline const double * QMatrixCodon::getExchangeabilities() const {
        assert(false);
        return 0;
    }

    inline const double * QMatrixCodon::getStateFreqs() const {
        return &(*_state_freqs)[0];
    }

    inline double QMatrixCodon::getOmega() const {
        return *_omega;
    }

    inline void QMatrixCodon::setEqualExchangeabilities(QMatrix::freq_xchg_ptr_t xchg_ptr) {
        assert(false);
    }

    inline void QMatrixCodon::setExchangeabilitiesSharedPtr(QMatrix::freq_xchg_ptr_t xchg_ptr) {
        assert(false);
    }

    inline void QMatrixCodon::setExchangeabilities(QMatrix::freq_xchg_t & xchg) {
        assert(false);
    }

    inline void QMatrixCodon::setEqualStateFreqs(QMatrix::freq_xchg_ptr_t freq_ptr) {
        _state_freqs = freq_ptr;
        unsigned nstates = _genetic_code->getNumNonStopCodons();
        _state_freqs->assign(nstates, 1./nstates);
        recalcRateMatrix();
    }

    inline void QMatrixCodon::setStateFreqsSharedPtr(QMatrix::freq_xchg_ptr_t freq_ptr) {
        unsigned nstates = _genetic_code->getNumNonStopCodons();
        if (freq_ptr->size() != nstates)
            throw XLorad(boost::format("Expecting %d state frequencies and got %d: perhaps you meant to specify a subset data type other than codon") % nstates % freq_ptr->size());
        double sum_of_freqs = std::accumulate(freq_ptr->begin(), freq_ptr->end(), 0.0);
        if (std::fabs(sum_of_freqs - 1.0) > 0.001)
            throw XLorad(boost::format("Expecting sum of codon frequencies to be 1, but instead got %g") % sum_of_freqs);
        _state_freqs = freq_ptr;
        normalizeFreqsOrExchangeabilities(_state_freqs);
        recalcRateMatrix();
    }

    inline void QMatrixCodon::setStateFreqs(QMatrix::freq_xchg_t & freqs) {
        unsigned nstates = _genetic_code->getNumNonStopCodons();
        if (freqs.size() != nstates)
            throw XLorad(boost::format("Expecting %d state frequencies and got %d: perhaps you meant to specify a subset data type other than codon") % nstates % freqs.size());
        std::copy(freqs.begin(), freqs.end(), _state_freqs->begin());
        recalcRateMatrix();
    }

    inline void QMatrixCodon::setOmegaSharedPtr(QMatrix::omega_ptr_t omega_ptr) {
        _omega = omega_ptr;
        recalcRateMatrix();
    }

    inline void QMatrixCodon::setOmega(QMatrix::omega_t omega) {
        *_omega = omega;
        recalcRateMatrix();
    }

    inline void QMatrixCodon::recalcRateMatrix() {
        // Must have assigned both _state_freqs and _omega to recalculate rate matrix
        if (!_is_active || !(_state_freqs && _omega))
            return;

        unsigned nstates = _genetic_code->getNumNonStopCodons();
        assert(_state_freqs->size() == nstates);
        const double * pi = getStateFreqs();
        double omega = getOmega();

        Eigen::Map<const Eigen::ArrayXd> tmp(_state_freqs->data(), nstates);
        _sqrtPi = tmp.sqrt().matrix().asDiagonal();
        _sqrtPiInv = _sqrtPi.inverse();

        // Calculate (unscaled) instantaneous rate matrix
        _Q = Eigen::MatrixXd::Zero(nstates,nstates);

        for (unsigned i = 0; i < nstates-1; i++) {
            for (unsigned j = i+1; j < nstates; j++) {
                unsigned diffs = 0;
                if (_codons[i][0] != _codons[j][0])
                    diffs++;
                if (_codons[i][1] != _codons[j][1])
                    diffs++;
                if (_codons[i][2] != _codons[j][2])
                    diffs++;
                if (diffs == 1) {
                    bool synonymous = _amino_acids[i] == _amino_acids[j];
                    _Q(i,j) = (synonymous ? 1.0 : omega)*pi[j];
                    _Q(j,i) = (synonymous ? 1.0 : omega)*pi[i];
                    _Q(i,i) -= _Q(i,j);
                    _Q(j,j) -= _Q(j,i);
                }
            }
        }

        double average_rate = 0.0;
        for (unsigned i = 0; i < nstates; i++)
            average_rate -= pi[i]*_Q(i,i);
        double scaling_factor = 3.0/average_rate;

        _Q *= scaling_factor;

        // S is a symmetric matrix
        eigenMatrixXd_t S = eigenMatrixXd_t(_sqrtPi*_Q*_sqrtPiInv);

        // Can use efficient eigensystem solver because S is symmetric
        Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> solver(S);
        if (solver.info() != Eigen::Success) {
            throw XLorad("Error in the calculation of eigenvectors and eigenvalues of the codon model rate matrix");
        }

        _eigenvectors           = _sqrtPiInv*solver.eigenvectors();
        _inverse_eigenvectors   = solver.eigenvectors().transpose()*_sqrtPi;
        _eigenvalues            = solver.eigenvalues();
    }

#if defined(POLGSS)
    inline void QMatrixCodon::setStateFreqRefDistParamsSharedPtr(QMatrix::freq_xchg_ptr_t freq_params_ptr) {
        if (freq_params_ptr->size() != 61)
            throw XLorad(boost::format("Expecting 61 state frequency reference distribution parameters and got %d: perhaps you meant to specify a subset data type other than codon") % freq_params_ptr->size());
        _state_freq_refdist = freq_params_ptr;
    }

    inline std::vector<double> QMatrixCodon::getStateFreqRefDistParamsVect() const {
        return std::vector<double>(_state_freq_refdist->begin(), _state_freq_refdist->end());
    }

    inline void QMatrixCodon::setExchangeabilityRefDistParamsSharedPtr(QMatrix::freq_xchg_ptr_t xchg_params_ptr) {
        throw XLorad("Not expecting exchangeability reference distribution to be specified for a codon model");
    }

    inline std::vector<double> QMatrixCodon::getExchangeabilityRefDistParamsVect() const {
        throw XLorad("Not expecting to copy exchangeability reference distribution parameters for a codon model");
        return std::vector<double>();
    }
#endif

} // namespace lorad


//==================================================================================================
/**
  Copyright 2017 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_CONSTANT_SCALAR_LOGSQRT2PI_HPP_INCLUDED
#define BOOST_SIMD_CONSTANT_SCALAR_LOGSQRT2PI_HPP_INCLUDED

#include <boost/simd/constant/definition/logsqrt2pi.hpp>
#include <boost/simd/arch/common/scalar/constant/constant_value.hpp>

#endif


#pragma once

#include <ysu/lib/config.hpp>
#include <ysu/lib/locks.hpp>
#include <ysu/lib/numbers.hpp>
#include <ysu/lib/utility.hpp>

#include <boost/optional.hpp>
#include <boost/thread/thread.hpp>

#include <atomic>
#include <memory>

namespace ysu
{
enum class work_version
{
	unspecified,
	work_1
};
std::string to_string (ysu::work_version const version_a);

class block;
class block_details;
enum class block_type : uint8_t;
bool work_validate_entry (ysu::block const &);
bool work_validate_entry (ysu::work_version const, ysu::root const &, uint64_t const);

uint64_t work_difficulty (ysu::work_version const, ysu::root const &, uint64_t const);

uint64_t work_threshold_base (ysu::work_version const);
uint64_t work_threshold_entry (ysu::work_version const, ysu::block_type const);
// Ledger threshold
uint64_t work_threshold (ysu::work_version const, ysu::block_details const);

namespace work_v1
{
	uint64_t value (ysu::root const & root_a, uint64_t work_a);
	uint64_t threshold_base ();
	uint64_t threshold_entry ();
	uint64_t threshold (ysu::block_details const);
}

double normalized_multiplier (double const, uint64_t const);
double denormalized_multiplier (double const, uint64_t const);
class opencl_work;
class work_item final
{
public:
	work_item (ysu::work_version const version_a, ysu::root const & item_a, uint64_t difficulty_a, std::function<void(boost::optional<uint64_t> const &)> const & callback_a) :
	version (version_a), item (item_a), difficulty (difficulty_a), callback (callback_a)
	{
	}
	ysu::work_version const version;
	ysu::root const item;
	uint64_t const difficulty;
	std::function<void(boost::optional<uint64_t> const &)> const callback;
};
class work_pool final
{
public:
	work_pool (unsigned, std::chrono::nanoseconds = std::chrono::nanoseconds (0), std::function<boost::optional<uint64_t> (ysu::work_version const, ysu::root const &, uint64_t, std::atomic<int> &)> = nullptr);
	~work_pool ();
	void loop (uint64_t);
	void stop ();
	void cancel (ysu::root const &);
	void generate (ysu::work_version const, ysu::root const &, uint64_t, std::function<void(boost::optional<uint64_t> const &)>);
	boost::optional<uint64_t> generate (ysu::work_version const, ysu::root const &, uint64_t);
	// For tests only
	boost::optional<uint64_t> generate (ysu::root const &);
	boost::optional<uint64_t> generate (ysu::root const &, uint64_t);
	size_t size ();
	ysu::network_constants network_constants;
	std::atomic<int> ticket;
	bool done;
	std::vector<boost::thread> threads;
	std::list<ysu::work_item> pending;
	std::mutex mutex;
	ysu::condition_variable producer_condition;
	std::chrono::nanoseconds pow_rate_limiter;
	std::function<boost::optional<uint64_t> (ysu::work_version const, ysu::root const &, uint64_t, std::atomic<int> &)> opencl;
	ysu::observer_set<bool> work_observers;
};

std::unique_ptr<container_info_component> collect_container_info (work_pool & work_pool, const std::string & name);
}


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

  @copyright 2016 NumScale SAS
  @copyright 2016 J.T. Lapreste

  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_LOGICAL_NOT_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_LOGICAL_NOT_HPP_INCLUDED

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

 /*!

    @ingroup group-operator
    Function object implementing logical_not capabilities

    return the logical negation of the parameter,
    i.e. True or False of the logical type associated entry type according that
    the input is @ref Zero or non @ref Zero (False or True).

    Infix notation can be used with operator '!'

    @par Semantic:

    For every parameter of type T0

    @code
    as_logical_t<T0> r = logical_not(x);
    @endcode

    is similar to:

    @code
    as_logical_t<T0> r =!x;
    @endcode

    @par Alias:
    @c l_not

    @see  logical_or, logical_xor, logical_notand,
    logical_andnot, logical_notor, logical_ornot

  **/
  const boost::dispatch::functor<tag::logical_not_> logical_not = {};
} }
#endif

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

#endif


#include <boost/icl/interval_set.hpp>