/*
 * Copyright (c) 2011-2021, The DART development contributors
 * All rights reserved.
 *
 * The list of contributors can be found at:
 *   https://github.com/dartsim/dart/blob/master/LICENSE
 *
 * This file is provided under the following "BSD-style" License:
 *   Redistribution and use in source and binary forms, with or
 *   without modification, are permitted provided that the following
 *   conditions are met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 *   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 *   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *   POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef DART_OPTIMIZATION_MULTIOBJECTIVESOLVER_HPP_
#define DART_OPTIMIZATION_MULTIOBJECTIVESOLVER_HPP_

#include <iostream>
#include <memory>
#include <vector>

#include <Eigen/Dense>

#include "dart/optimization/Population.hpp"

namespace dart {
namespace optimization {

class MultiObjectiveProblem;

/// Abstract class that provides a common interface for different
/// multi-objective optimization solvers.
///
/// The different MultiObjectiveSolver implementations each use a different
/// Pareto-optimization library, which could lead to differences in performance
/// for various problem types. This base class allows the different
/// MultiObjectiveSolver implementations to be swapped out with each other
/// quickly and easily to help with testing, benchmarking, and experimentation.
class MultiObjectiveSolver
{
public:
  /// The MultiObjectiveSolver::Properties class contains Solver parameters that
  /// are common to all MultiObjectiveSolver types. Most (but not necessarily
  /// all) Solvers will make use of these parameters, and these parameters can
  /// be directly copied or transferred between all Solver types.
  struct Properties
  {
    /// Multi-objective optimization problem to be solved
    std::shared_ptr<MultiObjectiveProblem> mProblem;

    /// Number of populations
    std::size_t mNumPopulations;

    /// Number of decision vectors in one population
    std::size_t mPopulationSize;

    /// The maximum step size allowed for the Problem to be considered converged
    double mTolerance;

    /// How many iterations per evolution.
    std::size_t mIterationsPerEvolution;

    /// How many iterations between printing the Solver's progress to the
    /// terminal. Use 0 for no printing.
    std::size_t mIterationsPerPrint;

    /// Stream for printing the Solver's progress. Default is std::cout.
    std::ostream* mOutStream;

    /// Set to true if the final result should be printed to the terminal.
    bool mPrintFinalResult;

    /// Constructor
    Properties(
        std::shared_ptr<MultiObjectiveProblem> problem = nullptr,
        std::size_t numPopulations = 1u,
        std::size_t populationSize = 100u,
        double tolerance = 1e-9,
        std::size_t numMaxIterations = 500u,
        std::size_t iterationsPerPrint = 0u,
        std::ostream* ostream = &std::cout,
        bool printFinalResult = false);
  };

  /// Default constructor
  explicit MultiObjectiveSolver(const Properties& properties = Properties());

  /// Destructor
  virtual ~MultiObjectiveSolver() = default;

  /// Solve optimization problem
  virtual bool solve(std::size_t numEvolutions = 1u) = 0;

  /// Returns the type (implementation) of this Solver
  virtual std::string getType() const = 0;

  /// Creates an identical clone of this Solver
  virtual std::shared_ptr<MultiObjectiveSolver> clone() const = 0;

  /// Set the generic Properties of this Solver
  void setProperties(const Properties& properties);

  /// Get the generic Properties of this Solver
  const Properties& getSolverProperties() const;

  virtual void setProblem(std::shared_ptr<MultiObjectiveProblem> problem);

  /// Get nonlinear optimization problem
  std::shared_ptr<MultiObjectiveProblem> getProblem() const;

  /// \{ \name Population

  /// Sets the number of decision vectors in one population
  void setPopulationSize(std::size_t size);

  /// Return sthe number of decision vectors in one population
  std::size_t getPopulationSize() const;

  /// Sets the number of populations.
  void setNumPopulations(std::size_t size);

  /// Returns the number of populations.
  std::size_t getNumPopulations() const;

  /// Returns a population at \c index.
  const Population& getPopulation(std::size_t index) const;

  /// Returns all the populations.
  const std::vector<Population>& getPopulations() const;

  /// \}

  /// Sets the number of iterations per evolution
  void setNumIterationsPerEvolution(std::size_t maxIterations);

  /// Returns the number of iterations per evolution
  std::size_t getNumIterationsPerEvolution() const;

protected:
  /// Properties
  Properties mProperties;

  /// Populations
  std::vector<Population> mPopulations;
};

} // namespace optimization
} // namespace dart

#endif // DART_OPTIMIZATION_MULTIOBJECTIVESOLVER_HPP_


//! This file does nothing but stick everything together in a usable command line executable
//!
//! It's a tiny bit hacky, but all UI stuff is...

#include <rubbishrsa/attack.hpp>
#include <rubbishrsa/keys.hpp>
#include <rubbishrsa/log.hpp>

#include <boost/program_options.hpp>

#include <fstream>
#include <iostream>

namespace po = boost::program_options;

// A class that handles where the output goes
class output_handler {
private:
  std::unique_ptr<std::ofstream> maybe_outfile;
  std::ostream* out;

public:
  std::ostream& get() { return *out; }

  output_handler() { out = &std::cout; }
  output_handler(const std::string& path) {
    maybe_outfile = std::make_unique<std::ofstream>(path);
    if (!*maybe_outfile) {
      // Since we are only used in main, we can just do the error handling here
      std::cerr << "ERROR: Could not open private key output file!" << std::endl;
      exit(-1);
    }
    out = maybe_outfile.get();
  }
};

rubbishrsa::bigint read_hex_input(const std::string& not_file_indicator, const po::variables_map& args2) {
  rubbishrsa::bigint data;

  if (args2.count(not_file_indicator)) {
    data = rubbishrsa::hex2bigint(args2.at(not_file_indicator).as<std::string>());
  }
  else {
    std::ifstream in{args2.at("in").as<std::string>()};
    if (!in) {
      std::cerr << "ERROR: Cannot open input file!" << std::endl;
      exit(1);
    }
    in >> std::hex >> data;
  }

  return data;
}

rubbishrsa::bigint read_message(const po::variables_map& args2) {
  rubbishrsa::bigint data;

  if (args2.count("message")){
    if (args2.count("hex"))
      data = rubbishrsa::hex2bigint(args2.at("message").as<std::string>());
    else
      data = rubbishrsa::ascii2bigint(args2.at("message").as<std::string>());
  }
  else {
    std::ifstream in{args2.at("in").as<std::string>()};
    if (!in) {
      std::cerr << "ERROR: Cannot open input file!" << std::endl;
      exit(1);
    }
    if (args2.count("hex"))
      in >> std::hex >> data;
    else
      data = rubbishrsa::ascii2bigint(in);
  }

  return data;
}

rubbishrsa::public_key read_pubkey(const po::variables_map& args2) {
  std::ifstream ifs{args2.at("pubkey").as<std::string>()};
  if (!ifs) {
    std::cerr << "ERROR: Could not open RSA public key" << std::endl;
    exit(1);
  }
  return rubbishrsa::public_key::deserialise(ifs);
}

rubbishrsa::private_key read_privkey(const po::variables_map& args2) {
  std::ifstream ifs{args2.at("privkey").as<std::string>()};
  if (!ifs) {
    std::cerr << "ERROR: Could not open RSA private key" << std::endl;
    exit(1);
  }
  return rubbishrsa::private_key::deserialise(ifs);
}

int main(int argc, char** argv) {
  // Here we will set up our options
  uint_fast16_t keysize;
  std::string outfile_path;
  std::string inkey_path;
  // A common variable used by the different options to represent how to obtain the target data
  std::string target;
  std::string min, max;
  std::string candidates_path;

  po::options_description common_options, gen_options, enc_options, dec_options, crack_options, brute_options, sign_options, verify_options, forge_options;
  {
    common_options.add_options()
        ("help,h", "Prints a help message")
        ("out,o", po::value(&outfile_path)->value_name("path"), "The file in which the result should be placed instead of printed to the terminal");

    gen_options.add_options()
        ("keysize,s", po::value(&keysize)->default_value(2048)->value_name("bits"), "Sets the RSA keysize")
        ("pubkey,p", po::value(&inkey_path)->value_name("path"), "An optional path to place a generated public key");

    enc_options.add_options()
        ("hex,x", po::value(&target)->value_name("num"), "Indicates that the message is in hexadecimal, not text")
        ("pubkey,p", po::value(&inkey_path)->value_name("path")->required(), "The path to the public key")
        ("message,m", po::value(&target)->value_name("str"), "A text (or hexadecimal) string that will be used as the RSA message")
        ("in,i", po::value(&target)->value_name("path"), "The path to the message file");

    dec_options.add_options()
        ("hex,x", "Indicates that the output should be in hexadecimal")
        ("privkey,k", po::value(&inkey_path)->value_name("path")->required(), "The path to the private key")
        ("ctext,c", po::value(&target)->value_name("num"), "The cyphertext created by encrypt")
        ("in,i", po::value(&target)->value_name("path"), "The path to the cyphertext file created by encrypt");

    sign_options.add_options()
        ("hex,x", "Indicates that the message is in hexadecimal, not text")
        ("privkey,k", po::value(&inkey_path)->value_name("path")->required(), "The path to the private key")
        ("message,m", po::value(&target)->value_name("str"), "A text (or hexadecimal) string that will be used as the RSA message")
        ("in,i", po::value(&target)->value_name("path"), "The path to the message file");

    verify_options.add_options()
        ("hex,x", "Indicates that the output should be in hexadecimal. Without this options, invisible characters can be added to the end of the string to fake signatures")
        ("pubkey,p", po::value(&inkey_path)->value_name("path")->required(), "The path to the public key")
        ("sig,s", po::value(&target)->value_name("num"), "The cyphertext created by encrypt")
        ("in,i", po::value(&target)->value_name("path"), "The path to the cyphertext file created by encrypt");

    crack_options.add_options()
        ("hex,x", "Indicates that the two factors should be returned (in decimal), instead of incorporated into a private key")
        ("pubkey,p", po::value(&inkey_path)->value_name("path")->required(), "The path to the public key");

    brute_options.add_options()
        ("hex,x", "Indicates the output should be in hexadecimal, not as text")
        ("pubkey,p", po::value(&inkey_path)->value_name("path")->required(), "The path to the public key")
        ("ctext,c", po::value(&target)->value_name("num"), "The cyphertext created by encrypt")
        ("in,i", po::value(&target)->value_name("path"), "The path to the cyphertext file created by encrypt")
        ("list,l", po::value(&candidates_path)->value_name("path"), "A file containing all the candidate plaintexts, with newlines between them")
        ("num,n", "Indicates that the lines in the file are hexadecimal numbers, not text")
        ("min", po::value(&min)->value_name("num")->default_value("0"), "In the context of a range search, gives the lowest candidate value")
        ("max", po::value(&max)->value_name("num"), "In the context of a range search, gives the largest candidate value. If missing, we use the modulus");

    forge_options.add_options()
        ("hex,x",  "Indicates that the message is in hexadecimal, not text")
        ("pubkey,p", po::value(&inkey_path)->value_name("path")->required(), "The path to the public key")
        ("invisible,u", "Indicates that invisible characters are allowed")
        ("message,m", po::value(&target)->value_name("str"), "A text (or hexadecimal) string that will be used as the RSA message")
        ("in,i", po::value(&target)->value_name("path"), "The path to the message file");
  }

  // We use a copy capture so that our hidden options go unnoticed
  auto print_help = [=]() {
    std::cout << "Usage: " << argv[0] << " <mode> <options>" << std::endl
              << std::endl
              << "This program takes in a mode (listed below), and options for that mode."
              << std::endl
              << "Please note that, due to the nature of the encoding used, private keys can be used in the place of public keys, but (obviously) not the other way around"
              << std::endl
              << "Common options: " << std::endl
              << common_options << std::endl
              << "gen: Generates a RSA key" << std::endl
              << gen_options << std::endl
              << "enc: Encrypts some data" << std::endl
              << enc_options << std::endl
              << "dec: Decrypts some data" << std::endl
              << dec_options << std::endl
              << "sign: Signs some data" << std::endl
              << enc_options << std::endl
              << "verify: Verifies as signature" << std::endl
              << dec_options << std::endl
              << "crack: Factorises public keys" << std::endl
              << crack_options << std::endl
              << "brute: Brute forces plaintexts" << std::endl
              << brute_options << std::endl
              << "forge: Forges signatures for small moduli" << std::endl
              << forge_options << std::endl
              << std::endl;
  };

  // Add in the common_options option to each mode so it doesn't complain
  for (auto* desc : {&gen_options, &enc_options, &dec_options, &crack_options, &brute_options, &sign_options, &verify_options})
    for (auto& i : common_options.options())
      desc->add(i);

  // Actually parse the arguments
  po::variables_map args;
  po::store(po::command_line_parser(argc, argv)
                                .options(common_options)
                                .allow_unregistered()
                                .run(), args);
  po::notify(args);

  if (args.count("help") || argc == 1) {
    print_help();
    return 0;
  }

  output_handler out = args.count("out") ? output_handler{outfile_path} : output_handler{};

  std::string_view mode{argv[1]};

  if (mode == "gen") {
    po::variables_map args2;
    po::store(po::command_line_parser(argc - 1, argv + 1)
                                      .options(gen_options)
                                      .run(), args2);
    po::notify(args2);

    if (keysize < 16) {
      std::cerr << "ERROR: RSA needs a few digits difference in length to be secure, and < 16 bits may ask for a negative number of bits. Sorry" << std::endl;
      return 1;
    }

    auto key = rubbishrsa::private_key::generate(keysize);

    key.serialise(out.get()); // TODO: impl this as json
    if (inkey_path.size()) {
      std::ofstream pubkey_out{inkey_path};
      if (!pubkey_out)
        // Don't crash, this is only a warning, as the public key could be generated later
        std::cerr << "WARNING: Could not open public key output file!" << std::endl;
      else
        static_cast<rubbishrsa::public_key>(key).serialise(pubkey_out);
    }
  }
  else if (mode == "enc") {
    po::variables_map args2;
    po::store(po::command_line_parser(argc - 1, argv + 1)
                                      .options(enc_options)
                                      .run(), args2);
    po::notify(args2);

    if (args2.count("hex-message") + args2.count("message") + args2.count("in") != 1) {
      std::cerr << "ERROR: Exactly one of --hex-message, --message, --in must be specified!" << std::endl;
      return 1;
    }

    rubbishrsa::public_key key = read_pubkey(args2);
    rubbishrsa::bigint data = read_message(args2);

    RUBBISHRSA_LOG_INFO(std::cerr << "Encrypting " << std::hex << data << std::endl);

    if (data >= key.n) {
      std::cout << "ERROR: Message is too big to be encrypted with a modulus this small!" << std::endl;
      return 1;
    }

    out.get() << std::hex << key.raw_encrypt(data) << std::endl;
  }
  else if (mode == "dec") {
    po::variables_map args2;
    po::store(po::command_line_parser(argc - 1, argv + 1)
                                      .options(dec_options)
                                      .run(), args2);
    po::notify(args2);

    if (args2.count("ctext") + args2.count("in") != 1) {
      std::cerr << "ERROR: Exactly one of --ctext, --in must be specified!" << std::endl;
      return 1;
    }

    rubbishrsa::private_key key = read_privkey(args2);
    rubbishrsa::bigint data = read_hex_input("ctext", args2);

    RUBBISHRSA_LOG_INFO(std::cerr << "Decrypting " << std::hex << data << std::endl);

    if (data >= key.n) {
      std::cout << "ERROR: Cyphertext too large! Maybe you used the wrong key?" << std::endl;
      return 1;
    }

    auto result = key.raw_decrypt(data);

    if (args2.count("hex"))
      out.get() << std::hex << result << std::endl;
    else
      out.get() << rubbishrsa::bigint2ascii(result) << std::endl;
  }
  else if (mode == "sign") {
    po::variables_map args2;
    po::store(po::command_line_parser(argc - 1, argv + 1)
                                      .options(sign_options)
                                      .run(), args2);
    po::notify(args2);

    rubbishrsa::private_key key = read_privkey(args2);
    rubbishrsa::bigint data = read_message(args2);

    RUBBISHRSA_LOG_INFO(std::cerr << "Signing " << std::hex << data << std::endl);

    if (data >= key.n) {
      std::cout << "ERROR: Message is too big to be signed with a modulus this small!" << std::endl;
      return 1;
    }

    auto result = key.raw_sign(data);

    out.get() << std::hex << result << std::endl;
  }
  else if (mode == "verify") {
    po::variables_map args2;
    po::store(po::command_line_parser(argc - 1, argv + 1)
                                      .options(verify_options)
                                      .run(), args2);
    po::notify(args2);

    rubbishrsa::public_key key = read_pubkey(args2);
    rubbishrsa::bigint data = read_hex_input("sig", args2);

    if (data >= key.n) {
      std::cout << "ERROR: Signature too large! Maybe you used the wrong key?" << std::endl;
      return 1;
    }

    rubbishrsa::bigint result = key.raw_verify(data);

    if (args2.count("hex"))
      out.get() << std::hex << result << std::endl;
    else
      out.get() << rubbishrsa::bigint2ascii(result) << std::endl;
  }
  else if (mode == "crack") {
    po::variables_map args2;
    po::store(po::command_line_parser(argc - 1, argv + 1)
                                      .options(crack_options)
                                      .run(), args2);
    po::notify(args2);

    rubbishrsa::public_key key = read_pubkey(args2);

    if (args2.count("hex")) {
      auto fact = rubbishrsa::factorise_semiprime(key.n);
      out.get() << std::hex << fact.first << std::endl << std::hex << fact.second << std::endl;
    }
    else {
      auto k = rubbishrsa::attack::crack_key(key);
      k.serialise(out.get());
    }
  }
  else if (mode == "brute") {
    po::variables_map args2;
    po::store(po::command_line_parser(argc - 1, argv + 1)
                                      .options(brute_options)
                                      .run(), args2);
    po::notify(args2);

    if (args2.count("ctext") + args2.count("in") != 1) {
      std::cerr << "ERROR: Exactly one of --ctext, --in must be specified!" << std::endl;
      return 1;
    }
    // For various API reasons, we cannot check if a defaulted argument was given
    if ((args2.count("max")) && (args2.count("list") || args2.count("num"))) {
      std::cerr << "ERROR: Invalid option combination!" << std::endl;
      return 1;
    }

    rubbishrsa::public_key key = read_pubkey(args2);

    rubbishrsa::bigint data = read_hex_input("ctext", args2);

    std::optional<rubbishrsa::bigint> result;

    // Are we in range mode?
    if (args2.count("list")) {
      std::ifstream ifs{candidates_path};
      if (!ifs) {
        std::cerr << "ERROR: Could not open candidates file!" << std::endl;
        return 1;
      }
      // XXX: may not work on Windows due to CRLF bs
      result = rubbishrsa::attack::brute_force_ptext(key, data, ifs, '\n', args2.count("num"));
    }
    else
      result = rubbishrsa::attack::brute_force_ptext(key, data, rubbishrsa::hex2bigint(min),
                                                     args2.count("max") ? rubbishrsa::hex2bigint(max) : key.n);

    if (result) {
      if (args.count("hex"))
        out.get() << std::hex << *result << std::endl;
      else
        out.get() << rubbishrsa::bigint2ascii(*result) << std::endl;
    }
    else {
      std::cerr << "ERROR: Could not crack the cyphertext!" << std::endl;
      return 1;
    }
  }
  else if (mode == "forge") {
    po::variables_map args2;
    po::store(po::command_line_parser(argc - 1, argv + 1)
                                      .options(forge_options)
                                      .run(), args2);
    po::notify(args2);

    rubbishrsa::public_key key = read_pubkey(args2);

    rubbishrsa::bigint data = read_message(args2);

    if (data >= key.n) {
      std::cout << "ERROR: Message is too big to be signed with a modulus this small!" << std::endl;
      return 1;
    }

    std::optional<rubbishrsa::bigint> result;

    if (args2.count("invisible"))
      result = rubbishrsa::attack::brute_force_sig_invis(key, data);
    else
      result = rubbishrsa::attack::brute_force_sig(key, [&](const auto& i) {return i == data;});

    if (!result)
      std::cerr << "ERROR: Could not find a conforming signature!" << std::endl;

    out.get() << std::hex << *result << std::endl;
  }
  else {
    std::cerr << "ERROR: Unknown mode '" << argv[1] << '\'' << std::endl << std::endl;
    print_help();
  }

  return 0;
}


// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2015 Tiago de Paula Peixoto <tiago@skewed.de>
//
// 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 3
// 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, see <http://www.gnu.org/licenses/>.

#include "graph_filtering.hh"
#include "graph.hh"
#include "graph_properties.hh"

#include <boost/graph/boyer_myrvold_planar_test.hpp>

using namespace std;
using namespace boost;
using namespace graph_tool;

struct get_planar_embedding
{
    template <class EdgeMap>
    class edge_inserter
    {
    public:
        edge_inserter(EdgeMap edge_map): _edge_map(edge_map) {}

        edge_inserter& operator++() { return *this; }
        edge_inserter& operator++(int) { return *this; }
        edge_inserter& operator*() { return *this; }

        template <class Key>
        edge_inserter& operator=(const Key& e)
        {
            _edge_map[e] = 1;
            return *this;
        }

    private:
        EdgeMap _edge_map;
    };

    template <class Graph, class VertexIndex, class EdgeIndex, class EmbedMap,
              class KurMap>
    void operator()(Graph& g, VertexIndex vertex_index, EdgeIndex edge_index,
                    EmbedMap embed_map, KurMap kur_map, bool& is_planar) const
    {
        edge_inserter<KurMap> kur_insert(kur_map);
        unchecked_vector_property_map
            <vector<typename graph_traits<Graph>::edge_descriptor>, VertexIndex>
            embedding(vertex_index, num_vertices(g));
        is_planar = boyer_myrvold_planarity_test
            (boyer_myrvold_params::graph = g,
             boyer_myrvold_params::edge_index_map = edge_index,
             boyer_myrvold_params::embedding = embedding,
             boyer_myrvold_params::kuratowski_subgraph = kur_insert);

        int i, N = num_vertices(g);
        #pragma omp parallel for default(shared) private(i) schedule(runtime) if (N > 100)
        for (i = 0; i < N; ++i)
        {
            typename graph_traits<Graph>::vertex_descriptor v = vertex(i, g);
            if (v == graph_traits<Graph>::null_vertex())
                continue;
            embed_map[v].resize(embedding[v].size());
            for (size_t j = 0; j < embedding[v].size(); ++j)
                embed_map[v][j] = edge_index[embedding[v][j]];
        }
    }

    template <class Graph, class VertexIndex, class EdgeIndex, class KurMap>
    void operator()(Graph& g, VertexIndex, EdgeIndex edge_index,
                    dummy_property_map, KurMap kur_map,
                    bool& is_planar) const
    {
        edge_inserter<KurMap> kur_insert(kur_map);
        is_planar = boyer_myrvold_planarity_test
            (boyer_myrvold_params::graph = g,
             boyer_myrvold_params::edge_index_map = edge_index,
             boyer_myrvold_params::kuratowski_subgraph = kur_insert);
    }

};

bool is_planar(GraphInterface& gi, boost::any embed_map, boost::any kur_map)
{
    bool is_planar;

    if (embed_map.empty())
        embed_map = dummy_property_map();
    if (kur_map.empty())
        kur_map = dummy_property_map();

    typedef mpl::push_back<writable_edge_scalar_properties,
                           dummy_property_map>::type edge_map_types;
    typedef mpl::push_back<vertex_scalar_vector_properties,
                           dummy_property_map>::type vertex_map_types;

    run_action<graph_tool::detail::never_directed>()
        (gi, std::bind(get_planar_embedding(), placeholders::_1, gi.GetVertexIndex(),
                       gi.GetEdgeIndex(), placeholders::_2, placeholders::_3,
                       std::ref(is_planar)),
         vertex_map_types(), edge_map_types())
        (embed_map, kur_map);
    return is_planar;
}


// Copyright (c) 2020 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <avalanche/peermanager.h>

#include <avalanche/test/util.h>

#include <test/util/setup_common.h>

#include <boost/test/unit_test.hpp>

using namespace avalanche;

BOOST_FIXTURE_TEST_SUITE(peermanager_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(select_peer_linear) {
    // No peers.
    BOOST_CHECK_EQUAL(selectPeerImpl({}, 0, 0), NO_PEER);
    BOOST_CHECK_EQUAL(selectPeerImpl({}, 1, 3), NO_PEER);

    // One peer
    const std::vector<Slot> oneslot = {{100, 100, 23}};

    // Undershoot
    BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 0, 300), NO_PEER);
    BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 42, 300), NO_PEER);
    BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 99, 300), NO_PEER);

    // Nailed it
    BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 100, 300), 23);
    BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 142, 300), 23);
    BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 199, 300), 23);

    // Overshoot
    BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 200, 300), NO_PEER);
    BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 242, 300), NO_PEER);
    BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 299, 300), NO_PEER);

    // Two peers
    const std::vector<Slot> twoslots = {{100, 100, 69}, {300, 100, 42}};

    // Undershoot
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 0, 500), NO_PEER);
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 42, 500), NO_PEER);
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 99, 500), NO_PEER);

    // First entry
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 100, 500), 69);
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 142, 500), 69);
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 199, 500), 69);

    // In betwenn
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 200, 500), NO_PEER);
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 242, 500), NO_PEER);
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 299, 500), NO_PEER);

    // Second entry
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 300, 500), 42);
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 342, 500), 42);
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 399, 500), 42);

    // Overshoot
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 400, 500), NO_PEER);
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 442, 500), NO_PEER);
    BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 499, 500), NO_PEER);
}

BOOST_AUTO_TEST_CASE(select_peer_dichotomic) {
    std::vector<Slot> slots;

    // 100 peers of size 1 with 1 empty element apart.
    uint64_t max = 1;
    for (int i = 0; i < 100; i++) {
        slots.emplace_back(max, 1, i);
        max += 2;
    }

    BOOST_CHECK_EQUAL(selectPeerImpl(slots, 4, max), NO_PEER);

    // Check that we get what we expect.
    for (int i = 0; i < 100; i++) {
        BOOST_CHECK_EQUAL(selectPeerImpl(slots, 2 * i, max), NO_PEER);
        BOOST_CHECK_EQUAL(selectPeerImpl(slots, 2 * i + 1, max), i);
    }

    BOOST_CHECK_EQUAL(selectPeerImpl(slots, max, max), NO_PEER);

    // Update the slots to be heavily skewed toward the last element.
    slots[99] = slots[99].withScore(101);
    max = slots[99].getStop();
    BOOST_CHECK_EQUAL(max, 300);

    for (int i = 0; i < 100; i++) {
        BOOST_CHECK_EQUAL(selectPeerImpl(slots, 2 * i, max), NO_PEER);
        BOOST_CHECK_EQUAL(selectPeerImpl(slots, 2 * i + 1, max), i);
    }

    BOOST_CHECK_EQUAL(selectPeerImpl(slots, 200, max), 99);
    BOOST_CHECK_EQUAL(selectPeerImpl(slots, 256, max), 99);
    BOOST_CHECK_EQUAL(selectPeerImpl(slots, 299, max), 99);
    BOOST_CHECK_EQUAL(selectPeerImpl(slots, 300, max), NO_PEER);

    // Update the slots to be heavily skewed toward the first element.
    for (int i = 0; i < 100; i++) {
        slots[i] = slots[i].withStart(slots[i].getStart() + 100);
    }

    slots[0] = Slot(1, slots[0].getStop() - 1, slots[0].getPeerId());
    slots[99] = slots[99].withScore(1);
    max = slots[99].getStop();
    BOOST_CHECK_EQUAL(max, 300);

    BOOST_CHECK_EQUAL(selectPeerImpl(slots, 0, max), NO_PEER);
    BOOST_CHECK_EQUAL(selectPeerImpl(slots, 1, max), 0);
    BOOST_CHECK_EQUAL(selectPeerImpl(slots, 42, max), 0);

    for (int i = 0; i < 100; i++) {
        BOOST_CHECK_EQUAL(selectPeerImpl(slots, 100 + 2 * i + 1, max), i);
        BOOST_CHECK_EQUAL(selectPeerImpl(slots, 100 + 2 * i + 2, max), NO_PEER);
    }
}

BOOST_AUTO_TEST_CASE(select_peer_random) {
    for (int c = 0; c < 1000; c++) {
        size_t size = InsecureRandBits(10) + 1;
        std::vector<Slot> slots;
        slots.reserve(size);

        uint64_t max = InsecureRandBits(3);
        auto next = [&]() {
            uint64_t r = max;
            max += InsecureRandBits(3);
            return r;
        };

        for (size_t i = 0; i < size; i++) {
            const uint64_t start = next();
            const uint32_t score = InsecureRandBits(3);
            max += score;
            slots.emplace_back(start, score, i);
        }

        for (int k = 0; k < 100; k++) {
            uint64_t s = max > 0 ? InsecureRandRange(max) : 0;
            auto i = selectPeerImpl(slots, s, max);
            // /!\ Because of the way we construct the vector, the peer id is
            // always the index. This might not be the case in practice.
            BOOST_CHECK(i == NO_PEER || slots[i].contains(s));
        }
    }
}

BOOST_AUTO_TEST_CASE(peer_probabilities) {
    // No peers.
    PeerManager pm;
    BOOST_CHECK_EQUAL(pm.selectNode(), NO_NODE);

    const NodeId node0 = 42, node1 = 69, node2 = 37;

    // One peer, we always return it.
    Proof proof0 = buildRandomProof(100);
    pm.addNode(node0, buildRandomProof(100), CPubKey());
    BOOST_CHECK_EQUAL(pm.selectNode(), node0);

    // Two peers, verify ratio.
    pm.addNode(node1, buildRandomProof(200), CPubKey());

    std::unordered_map<PeerId, int> results = {};
    for (int i = 0; i < 10000; i++) {
        size_t n = pm.selectNode();
        BOOST_CHECK(n == node0 || n == node1);
        results[n]++;
    }

    BOOST_CHECK(abs(2 * results[0] - results[1]) < 500);

    // Three peers, verify ratio.
    pm.addNode(node2, buildRandomProof(100), CPubKey());

    results.clear();
    for (int i = 0; i < 10000; i++) {
        size_t n = pm.selectNode();
        BOOST_CHECK(n == node0 || n == node1 || n == node2);
        results[n]++;
    }

    BOOST_CHECK(abs(results[0] - results[1] + results[2]) < 500);
}

BOOST_AUTO_TEST_CASE(remove_peer) {
    // No peers.
    PeerManager pm;
    BOOST_CHECK_EQUAL(pm.selectPeer(), NO_PEER);

    // Add 4 peers.
    std::array<PeerId, 8> peerids;
    for (int i = 0; i < 4; i++) {
        peerids[i] = pm.getPeer(buildRandomProof(100));
    }

    BOOST_CHECK_EQUAL(pm.getSlotCount(), 400);
    BOOST_CHECK_EQUAL(pm.getFragmentation(), 0);

    for (int i = 0; i < 100; i++) {
        PeerId p = pm.selectPeer();
        BOOST_CHECK(p == peerids[0] || p == peerids[1] || p == peerids[2] ||
                    p == peerids[3]);
    }

    // Remove one peer, it nevers show up now.
    BOOST_CHECK(pm.removePeer(peerids[2]));
    BOOST_CHECK_EQUAL(pm.getSlotCount(), 400);
    BOOST_CHECK_EQUAL(pm.getFragmentation(), 100);

    // Make sure we compact to never get NO_PEER.
    BOOST_CHECK_EQUAL(pm.compact(), 100);
    BOOST_CHECK(pm.verify());
    BOOST_CHECK_EQUAL(pm.getSlotCount(), 300);
    BOOST_CHECK_EQUAL(pm.getFragmentation(), 0);

    for (int i = 0; i < 100; i++) {
        PeerId p = pm.selectPeer();
        BOOST_CHECK(p == peerids[0] || p == peerids[1] || p == peerids[3]);
    }

    // Add 4 more peers.
    for (int i = 0; i < 4; i++) {
        peerids[i + 4] = pm.getPeer(buildRandomProof(100));
    }

    BOOST_CHECK_EQUAL(pm.getSlotCount(), 700);
    BOOST_CHECK_EQUAL(pm.getFragmentation(), 0);

    BOOST_CHECK(pm.removePeer(peerids[0]));
    BOOST_CHECK_EQUAL(pm.getSlotCount(), 700);
    BOOST_CHECK_EQUAL(pm.getFragmentation(), 100);

    // Removing the last entry do not increase fragmentation.
    BOOST_CHECK(pm.removePeer(peerids[7]));
    BOOST_CHECK_EQUAL(pm.getSlotCount(), 600);
    BOOST_CHECK_EQUAL(pm.getFragmentation(), 100);

    // Make sure we compact to never get NO_PEER.
    BOOST_CHECK_EQUAL(pm.compact(), 100);
    BOOST_CHECK(pm.verify());
    BOOST_CHECK_EQUAL(pm.getSlotCount(), 500);
    BOOST_CHECK_EQUAL(pm.getFragmentation(), 0);

    for (int i = 0; i < 100; i++) {
        PeerId p = pm.selectPeer();
        BOOST_CHECK(p == peerids[1] || p == peerids[3] || p == peerids[4] ||
                    p == peerids[5] || p == peerids[6]);
    }

    // Removing non existent peers fails.
    BOOST_CHECK(!pm.removePeer(peerids[0]));
    BOOST_CHECK(!pm.removePeer(peerids[2]));
    BOOST_CHECK(!pm.removePeer(peerids[7]));
    BOOST_CHECK(!pm.removePeer(NO_PEER));
}

BOOST_AUTO_TEST_CASE(compact_slots) {
    PeerManager pm;

    // Add 4 peers.
    std::array<PeerId, 4> peerids;
    for (int i = 0; i < 4; i++) {
        peerids[i] = pm.getPeer(buildRandomProof(100));
    }

    // Remove all peers.
    for (auto p : peerids) {
        pm.removePeer(p);
    }

    BOOST_CHECK_EQUAL(pm.getSlotCount(), 300);
    BOOST_CHECK_EQUAL(pm.getFragmentation(), 300);

    for (int i = 0; i < 100; i++) {
        BOOST_CHECK_EQUAL(pm.selectPeer(), NO_PEER);
    }

    BOOST_CHECK_EQUAL(pm.compact(), 300);
    BOOST_CHECK(pm.verify());
    BOOST_CHECK_EQUAL(pm.getSlotCount(), 0);
    BOOST_CHECK_EQUAL(pm.getFragmentation(), 0);
}

BOOST_AUTO_TEST_CASE(node_crud) {
    PeerManager pm;

    // Create one peer.
    Proof proof = buildRandomProof(100000000);
    BOOST_CHECK_EQUAL(pm.selectNode(), NO_NODE);

    // Add 4 nodes.
    for (int i = 0; i < 4; i++) {
        BOOST_CHECK(pm.addNode(i, proof, CPubKey()));
    }

    for (int i = 0; i < 100; i++) {
        NodeId n = pm.selectNode();
        BOOST_CHECK(n >= 0 && n < 4);
        BOOST_CHECK(
            pm.updateNextRequestTime(n, std::chrono::steady_clock::now()));
    }

    // Remove a node, check that it doesn't show up.
    BOOST_CHECK(pm.removeNode(2));

    for (int i = 0; i < 100; i++) {
        NodeId n = pm.selectNode();
        BOOST_CHECK(n == 0 || n == 1 || n == 3);
        BOOST_CHECK(
            pm.updateNextRequestTime(n, std::chrono::steady_clock::now()));
    }

    // Push a node's timeout in the future, so that it doesn't show up.
    BOOST_CHECK(pm.updateNextRequestTime(1, std::chrono::steady_clock::now() +
                                                std::chrono::hours(24)));

    for (int i = 0; i < 100; i++) {
        NodeId n = pm.selectNode();
        BOOST_CHECK(n == 0 || n == 3);
        BOOST_CHECK(
            pm.updateNextRequestTime(n, std::chrono::steady_clock::now()));
    }

    // Move a node from a peer to another. This peer has a very low score such
    // as chances of being picked are 1 in a billion.
    Proof altproof = buildRandomProof(1);
    BOOST_CHECK(pm.addNode(3, altproof, CPubKey()));

    int node3selected = 0;
    for (int i = 0; i < 100; i++) {
        NodeId n = pm.selectNode();
        if (n == 3) {
            // Selecting this node should be exceedingly unlikely.
            BOOST_CHECK(node3selected++ < 1);
        } else {
            BOOST_CHECK_EQUAL(n, 0);
        }
        BOOST_CHECK(
            pm.updateNextRequestTime(n, std::chrono::steady_clock::now()));
    }
}

BOOST_AUTO_TEST_SUITE_END()


/***
   HiCapTools.
   Copyright (c) 2017 Pelin Sahlén <pelin.akan@scilifelab.se>

	Permission is hereby granted, free of charge, to any person obtaining a
	copy of this software and associated documentation files (the "Software"),
	to deal in the Software with some restriction, including without limitation
	the rights to use, copy, modify, merge, publish, distribute the Software,
	and to permit persons to whom the Software is furnished to do so, subject to
	the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software. The Software shall not be used
	for commercial purposes.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
	PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
	CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
	OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
***/

//
//  BackgroundInteractionFrequency.cpp
//  HiCapTools
//
//  Created by Pelin Sahlen and Anandashankar Anil.
//

#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/rolling_mean.hpp>
using namespace boost::accumulators;

#include "BackgroundInteractionFrequency.h"
#include "Global.h"
#include "linear.h"
#include <fstream>
#include <cmath>

///For Probe-Distal and Probe-Probe
void DetermineBackgroundLevels::CalculateMeanandStdRegress(std::string eName, int ExperimentNo, std::string DesignName, BG_signals& bglevelsloc, int binsize, std::string whichProx, int MinimumJunctionDistance, OutStream& log, int WindowSizeloc){

	expName= eName; //set experiment name

	std::map< int, int > nofentries_perBin; // required for calculating mean
	std::map< int, int > signal_square; // required for calculating stdev
    double sum_square, variance;
	int distance;
    std::string feature_id;

    std::map< int, Junction >::const_iterator iter;
	for(int i = 0; i < Design_NegCtrl[DesignName].Probes.size(); i++){
        feature_id = Design_NegCtrl[DesignName].Probes[i].feature_id;
        //////////Probe-distal
        if(whichProx=="ProbeDistal"){
			for (iter = Features[feature_id].proximities.junctions.begin(); iter != Features[feature_id].proximities.junctions.end(); ++iter){

				distance = iter->first - Design_NegCtrl[DesignName].Probes[i].end;

				int bin = abs(distance) / binsize;

				if(iter->second.paircount[ExperimentNo] > 0){

					if(bglevelsloc.mean.find(bin) == bglevelsloc.mean.end())
						bglevelsloc.mean[bin] = iter->second.paircount[ExperimentNo];
					else
						bglevelsloc.mean[bin] = bglevelsloc.mean[bin] + iter->second.paircount[ExperimentNo];

					if(nofentries_perBin.find(bin) == nofentries_perBin.end()){
						nofentries_perBin[bin] = 1;
						signal_square[bin] = (iter->second.paircount[ExperimentNo])*(iter->second.paircount[ExperimentNo]);
					}
					else{
						nofentries_perBin[bin] = nofentries_perBin[bin] + 1;
						signal_square[bin] = (signal_square[bin] + ((iter->second.paircount[ExperimentNo])*(iter->second.paircount[ExperimentNo])));
					}
				}
			}
		}
		///////////Probe-Probe
        else if(whichProx=="ProbeProbe"){
			for (auto iter = Features[feature_id].Inter_feature_ints.begin(); iter != Features[feature_id].Inter_feature_ints.end(); ++iter){

				distance = Features[feature_id].start - Features[(*iter).interacting_feature_id].start;

				if((Features[feature_id].FeatureType == 3 && Features[(*iter).interacting_feature_id].FeatureType == 3) && Features[feature_id].TranscriptName != Features[(*iter).interacting_feature_id].TranscriptName && (abs(distance) >= MinimumJunctionDistance)){

					int bin = abs(distance) / binsize;
					if((*iter).signal[ExperimentNo] > 0){
						if(bglevelsloc.mean.find(bin) == bglevelsloc.mean.end())
							bglevelsloc.mean[bin] = (*iter).signal[ExperimentNo];
						else
							bglevelsloc.mean[bin] = bglevelsloc.mean[bin] + (*iter).signal[ExperimentNo];

						if(nofentries_perBin.find(bin) == nofentries_perBin.end()){
							nofentries_perBin[bin] = 1;
							signal_square[bin] = ((*iter).signal[ExperimentNo])*((*iter).signal[ExperimentNo]);
						}
						else{
							nofentries_perBin[bin] = nofentries_perBin[bin] + 1;
							signal_square[bin] = (signal_square[bin] + (((*iter).signal[ExperimentNo])*((*iter).signal[ExperimentNo])));
						}
					}
				}
			}
		}
    }

// Calculate Mean and stdev
	std::map< int, double>::iterator it; // iterator for bin signals
	for (it = bglevelsloc.mean.begin(); it != bglevelsloc.mean.end(); ++it){
        bglevelsloc.samplesize[it->first] = nofentries_perBin[it->first];
        if(bglevelsloc.samplesize[it->first] == 0){
            it->second = 1; //Mean
        }
        else{
            it->second /= nofentries_perBin[it->first]; //Mean
        }
        if(bglevelsloc.samplesize[it->first] <= 1){
            bglevelsloc.stdev[it->first] = 0;
        }
        else{
            sum_square = ((it->second)*(it->second));
            sum_square /= (double(nofentries_perBin[it->first])); // Average Sum Squared
            variance = signal_square[it->first] - sum_square;
            variance /= (nofentries_perBin[it->first] -1);
            bglevelsloc.stdev[it->first] = sqrt(variance); //stdev
        }
	}

    int first25kb = ceil(25000/(double)binsize); // to exclude bins of first 25 kb from smoothing

    boost::accumulators::accumulator_set<double, stats<tag::rolling_mean> > acc(tag::rolling_window::window_size = (WindowSizeloc*2));
    boost::accumulators::accumulator_set<double, stats<tag::rolling_mean> > acc2(tag::rolling_window::window_size = (WindowSizeloc*2));

    int w = 0, z = 0, s = 0, a=0;
    std::deque< double > dm, ds, db, dwm, dws;

    // Put values into a queue
    for (it = bglevelsloc.mean.begin(); it != bglevelsloc.mean.end(); ++it){

        dm.push_back(it->second);
        ds.push_back(bglevelsloc.stdev[it->first]);
        db.push_back(it->first);

    }
    //Push the first WindowSize-1 elements into a queue
	if(dm.empty())
		log<<"No Element in dm Outloop"<<std::endl;

	bool firstpass=true;
    for(w = 0; w < (dm.size()); ++w){
		if(w < first25kb){
			bglevelsloc.smoothed[db[w]] = dm[w];
			bglevelsloc.smoothed_stdev[db[w]] = ds[w];
		}
		else{
			boost::accumulators::accumulator_set<double, stats<tag::rolling_mean> > acc(tag::rolling_window::window_size = (WindowSizeloc));
			boost::accumulators::accumulator_set<double, stats<tag::rolling_mean> > acc2(tag::rolling_window::window_size = (WindowSizeloc));

			if(w >= first25kb && w < first25kb + WindowSizeloc - 1){

				/**
				if(dwm.size()==WindowSizeloc){
					for(z = 0; z < WindowSizeloc;++z){
						std::cout<<dwm[z]<<std::endl;
					}
					dwm.pop_front();
				}
			**/
				//change to rolling mean if needed
				bglevelsloc.smoothed[db[w]] = dm[w];
				bglevelsloc.smoothed_stdev[db[w]] = ds[w];

			}
			else {
			    if(firstpass){
			        for(auto it=w-(WindowSizeloc/2); it<w+WindowSizeloc/2;++it){ //for the first bin that is to be smoothed
			            dwm.push_back(dm[it]);
			            dws.push_back(ds[it]);
			        }
			        firstpass=false;
			    }
				bool ifrolling=false;

				//To check that the window stops at the last bin
				if(w+WindowSizeloc/2 < dm.size()){
				    dwm.push_back(dm.at(w+WindowSizeloc/2));
				    dws.push_back(ds.at(w+WindowSizeloc/2));
				}

				if(dwm.size()==WindowSizeloc){
					ifrolling=true;
					for(z = 0; z < WindowSizeloc;++z){
						acc(dwm[z]);
						acc2(dws[z]);
					}
				}
				if(ifrolling){
					bglevelsloc.smoothed[db[w]] = rolling_mean(acc);
					bglevelsloc.smoothed_stdev[db[w]] = rolling_mean(acc2);

				}
				else{
					bglevelsloc.smoothed[db[w]] = dm[w];
					bglevelsloc.smoothed_stdev[db[w]] = ds[w];

				}

				dwm.pop_front();
				dws.pop_front();

			}
		}
	}
}

void DetermineBackgroundLevels::PrintBackgroundFrequency(int bSize, int bSizePP, PrDes::RENFileInfo& reInfo){

	std::string FileNamePE, FileNamePP;

	FileNamePE.append(expName);
	FileNamePE.append(".");
	FileNamePE.append(reInfo.genomeAssembly.substr(0, reInfo.genomeAssembly.find_first_of(',')));
	FileNamePE.append(".Probe_Distal.BackgroundLevels.");
	FileNamePE.append(reInfo.currTime);
	FileNamePE.append(".txt");

	FileNamePP.append(expName);
	FileNamePP.append(".");
	FileNamePP.append(reInfo.genomeAssembly.substr(0, reInfo.genomeAssembly.find_first_of(',')));
	FileNamePP.append(".Probe_Probe.BackgroundLevels.");
	FileNamePP.append(reInfo.currTime);
	FileNamePP.append(".txt");

	std::ofstream outfPE(FileNamePE.c_str());
	std::ofstream outfPP(FileNamePP.c_str());
    outfPE << "Distance Bin" << '\t' << "Interaction Distance" << '\t' << "Mean " << '\t' << "Stdev" << '\t' << "Sample Size" << '\t' << "Mean (Smoothed)" << '\t' << "StDev (Smoothed)" <<  std::endl;

	for (auto it = bglevels.smoothed.begin(); it != bglevels.smoothed.end(); ++it){
        outfPE << (it->first) << '\t'<< (it->first)*bSize << '\t' << bglevels.mean[it->first] << '\t' << bglevels.stdev[it->first] << '\t' << bglevels.samplesize[it->first] << '\t' << it->second << '\t' << bglevels.smoothed_stdev[it->first] << std::endl;
	}

	outfPP << "Distance Bin" << '\t' << "Interaction Distance" << '\t' << "Mean " << '\t' << "Stdev" << '\t' << "Sample Size" << '\t' << "Mean (Smoothed)" << '\t' << "StDev (Smoothed)" <<  std::endl;

	for (auto it = bglevelsProbeProbe.smoothed.begin(); it != bglevelsProbeProbe.smoothed.end(); ++it){
        outfPP << (it->first) << '\t' << (it->first)*bSizePP << '\t' << bglevelsProbeProbe.mean[it->first] << '\t' << bglevelsProbeProbe.stdev[it->first] << '\t' << bglevelsProbeProbe.samplesize[it->first] << '\t' << it->second << '\t' << bglevelsProbeProbe.smoothed_stdev[it->first] << std::endl;
	}

}


#pragma once

#include <chrono>

#include <boost/bind.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/asio/steady_timer.hpp>

#include "HandlerAllocator.hpp"

namespace enyx {
namespace net_tester {

class BandwidthThrottle
{
public:
    BandwidthThrottle(boost::asio::io_service & io_service,
                      std::size_t bandwidth,
                      std::size_t sampling_frequency);

    template<typename Functor>
    void
    delay(Functor && f)
    {
        timer_.expires_at(next_slice_start_);

        auto handler = [this, f](boost::system::error_code const& failure) {
            if (failure)
                return;

            next_slice_start_ += slice_duration_;
            f(slice_bytes_count_);
        };

        timer_.async_wait(make_handler(handler_memory_,
                                       std::move(handler)));
    }

    void
    reset();

private:
    static std::chrono::steady_clock::duration
    to_slice_duration(std::size_t sampling_frequency);

    static std::size_t
    to_slice_bytes_count(std::size_t bandwidth,
                         std::size_t sampling_frequency);

private:
    boost::asio::steady_timer timer_;
    HandlerMemory handler_memory_;
    std::size_t slice_bytes_count_;
    std::chrono::steady_clock::duration slice_duration_;
    std::chrono::steady_clock::time_point next_slice_start_;
};

} // namespace net_tester
} // namespace enyx


//==============================================================================
//         Copyright 2009 - 2013 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_SWAR_FUNCTIONS_SIMD_SSE_SSE2_SPLIT_HIGH_HPP_INCLUDED
#define BOOST_SIMD_SWAR_FUNCTIONS_SIMD_SSE_SSE2_SPLIT_HIGH_HPP_INCLUDED
#ifdef BOOST_SIMD_HAS_SSE2_SUPPORT

#include <boost/simd/swar/functions/split_high.hpp>
#include <boost/simd/include/constants/zero.hpp>
#include <boost/dispatch/meta/upgrade.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT          ( split_high_
                                    , boost::simd::tag::sse2_
                                    , (A0)
                                    , ((simd_ < single_<A0>
                                              , boost::simd::tag::sse_
                                              >
                                      ))
                                    )
  {
    typedef typename dispatch::meta::upgrade<A0>::type  result_type;

    BOOST_FORCEINLINE result_type operator()(const A0& a0) const
    {
      return _mm_cvtps_pd(_mm_movehl_ps(a0, a0));
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( split_high_
                                    , boost::simd::tag::sse2_
                                    , (A0)
                                    , ((simd_ < int8_<A0>
                                              , boost::simd::tag::sse_
                                              >
                                      ))
                                    )
  {
    typedef typename dispatch::meta::upgrade<A0>::type  result_type;

    BOOST_FORCEINLINE result_type operator()(const A0& a0) const
    {
      return _mm_unpackhi_epi8(a0, is_ltz(a0));
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( split_high_
                                    , boost::simd::tag::sse2_
                                    , (A0)
                                    , ((simd_ < uint8_<A0>
                                              , boost::simd::tag::sse_
                                              >
                                      ))
                                    )
  {
    typedef typename dispatch::meta::upgrade<A0>::type  result_type;

    BOOST_FORCEINLINE result_type operator()(const A0& a0) const
    {
      return _mm_unpackhi_epi8(a0, Zero<A0>());
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( split_high_
                                    , boost::simd::tag::sse2_
                                    , (A0)
                                    , ((simd_ < int16_<A0>
                                              , boost::simd::tag::sse_
                                              >
                                      ))
                                    )
  {
    typedef typename dispatch::meta::upgrade<A0>::type  result_type;

    BOOST_FORCEINLINE result_type operator()(const A0& a0) const
    {
      return _mm_unpackhi_epi16(a0, is_ltz(a0));
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( split_high_
                                    , boost::simd::tag::sse2_
                                    , (A0)
                                    , ((simd_ < uint16_<A0>
                                              , boost::simd::tag::sse_
                                              >
                                      ))
                                    )
  {
    typedef typename dispatch::meta::upgrade<A0>::type  result_type;

    BOOST_FORCEINLINE result_type operator()(const A0& a0) const
    {
      return _mm_unpackhi_epi16(a0, Zero<A0>());
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( split_high_
                                    , boost::simd::tag::sse2_
                                    , (A0)
                                    , ((simd_ < int32_<A0>
                                              , boost::simd::tag::sse_
                                              >
                                      ))
                                    )
  {
    typedef typename dispatch::meta::upgrade<A0>::type  result_type;

    BOOST_FORCEINLINE result_type operator()(const A0& a0) const
    {
      return _mm_unpackhi_epi32(a0, is_ltz(a0));
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( split_high_
                                    , boost::simd::tag::sse2_
                                    , (A0)
                                    , ((simd_ < uint32_<A0>
                                              , boost::simd::tag::sse_
                                              >
                                      ))
                                    )
  {
    typedef typename dispatch::meta::upgrade<A0>::type  result_type;

    BOOST_FORCEINLINE result_type operator()(const A0& a0) const
    {
      return _mm_unpackhi_epi32(a0, Zero<A0>());
    }
  };
} } }

#endif
#endif


// Copyright (C) 2011-2012 by the BEM++ Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#ifndef bempp_laplace_3d_boundary_operator_base_hpp
#define bempp_laplace_3d_boundary_operator_base_hpp

#include "elementary_singular_integral_operator.hpp"

#include <boost/scoped_ptr.hpp>

namespace Bempp
{

/** \ingroup laplace_3d
 *  \brief Base class for boundary operators for the Laplace equation in 3D.
 *
 *  \tparam Impl
 *    Type of the internal implementation object.
 *  \tparam BasisFunctionType_
 *    Type of the values of the basis functions into
 *    which functions acted upon by the operator are expanded.
 *  \tparam ResultType_
 *    Type used to represent elements of the weak form form of the operator.
 *
 *  The latter two template parameters can take the following values: \c float, \c
 *  double, <tt>std::complex<float></tt> and <tt>std::complex<double></tt>.
 *  Both types must have the same precision: for instance, mixing \c float with
 *  <tt>std::complex<double></tt> is not allowed. The parameter \p ResultType_
 *  is by default set to \p BasisFunctionType_. You should override that only if
 *  you set \p BasisFunctionType_ to a real type, but you want the entries of
 *  the operator's weak form to be stored as complex numbers.
 *
 *  \see laplace_3d */
template <typename Impl,
          typename BasisFunctionType_, typename ResultType_ = BasisFunctionType_>
class Laplace3dBoundaryOperatorBase :
        public ElementarySingularIntegralOperator<
        BasisFunctionType_,
        typename ScalarTraits<ResultType_>::RealType,
        ResultType_>
{
    typedef ElementarySingularIntegralOperator<
    BasisFunctionType_,
    typename ScalarTraits<ResultType_>::RealType,
    ResultType_>
    Base;
public:
    /** \brief Type of the values of the basis functions into which functions
     *  acted upon by the operator are expanded. */
    typedef typename Base::BasisFunctionType BasisFunctionType;
    /** \brief Type of the values of kernel functions. */
    typedef typename Base::KernelType KernelType;
    /** \copydoc ElementaryIntegralOperator::ResultType */
    typedef typename Base::ResultType ResultType;
    /** \copydoc ElementaryIntegralOperator::CoordinateType */
    typedef typename Base::CoordinateType CoordinateType;
    /** \copydoc ElementaryIntegralOperator::CollectionOfBasisTransformations */
    typedef typename Base::CollectionOfBasisTransformations
    CollectionOfBasisTransformations;
    /** \copydoc ElementaryIntegralOperator::CollectionOfKernels */
    typedef typename Base::CollectionOfKernels CollectionOfKernels;
    /** \copydoc ElementaryIntegralOperator::TestKernelTrialIntegral */
    typedef typename Base::TestKernelTrialIntegral TestKernelTrialIntegral;

    /** \copydoc AbstractBoundaryOperator::AbstractBoundaryOperator */
    Laplace3dBoundaryOperatorBase(
            const shared_ptr<const Space<BasisFunctionType> >& domain,
            const shared_ptr<const Space<BasisFunctionType> >& range,
            const shared_ptr<const Space<BasisFunctionType> >& dualToRange,
            const std::string& label = "",
            int symmetry = NO_SYMMETRY);
    /** \brief Copy constructor. */
    Laplace3dBoundaryOperatorBase(
            const Laplace3dBoundaryOperatorBase& other);
    virtual ~Laplace3dBoundaryOperatorBase();
    Laplace3dBoundaryOperatorBase& operator=(
            const Laplace3dBoundaryOperatorBase& rhs);

    /** \brief Return the identifier of this operator.
     *
     *  Two boundary operators related to the Laplace equation are treated as
     *  identical, and hence having the same weak form, if they have the same
     *  C++ type (e.g.
     *  <tt>Laplace3dDoubleLayerBoundaryOperator<double, double></tt>),
     *  domain space, range space and space dual to range.
     *
     *  \deprecated This function is deprecated and will be removed in a future
     *  version of BEM++. */
    BEMPP_DEPRECATED virtual shared_ptr<const AbstractBoundaryOperatorId> id() const;

private:
    virtual const CollectionOfKernels& kernels() const;
    virtual const CollectionOfBasisTransformations&
    testTransformations() const;
    virtual const CollectionOfBasisTransformations&
    trialTransformations() const;
    virtual const TestKernelTrialIntegral& integral() const;

private:
    /** \cond PRIVATE */
    boost::scoped_ptr<Impl> m_impl;
    shared_ptr<AbstractBoundaryOperatorId> m_id;
    /** \endcond */
};

} // namespace Bempp

#endif


#include <cstdint>
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <tuple>
#include <cmath>
#include <boost/program_options.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/optional.hpp>
#include <boost/format.hpp>

#include "config.h"

#include "midgard/encoded.h"
#include "baldr/graphreader.h"
#include "baldr/tilehierarchy.h"
#include "baldr/pathlocation.h"
#include "baldr/connectivity_map.h"
#include "loki/search.h"
#include "sif/costfactory.h"
#include "odin/directionsbuilder.h"
#include "odin/util.h"
#include "proto/trippath.pb.h"
#include "proto/tripdirections.pb.h"
#include "proto/directions_options.pb.h"
#include "midgard/logging.h"
#include "midgard/distanceapproximator.h"
#include "thor/astar.h"
#include "thor/bidirectional_astar.h"
#include "thor/multimodal.h"
#include "thor/trippathbuilder.h"
#include "thor/attributes_controller.h"
#include "thor/route_matcher.h"

using namespace valhalla::midgard;
using namespace valhalla::baldr;
using namespace valhalla::loki;
using namespace valhalla::odin;
using namespace valhalla::sif;
using namespace valhalla::thor;
using namespace valhalla::meili;

namespace bpo = boost::program_options;

namespace {
  class PathStatistics {
    std::pair<float, float> origin;
    std::pair<float, float> destination;
    std::string success;
    uint32_t passes;
    uint32_t runtime;
    uint32_t trip_time;
    float trip_dist;
    float arc_dist;
    uint32_t manuevers;

  public:
    PathStatistics (std::pair<float, float> p1, std::pair<float, float> p2)
      : origin(p1), destination(p2), success("false"),
        passes(0), runtime(), trip_time(),
        trip_dist(), arc_dist(), manuevers() { }

    void setSuccess(std::string s) { success = s; }
    void incPasses(void) { ++passes; }
    void addRuntime(uint32_t msec) { runtime += msec; }
    void setTripTime(uint32_t t) { trip_time = t; }
    void setTripDist(float d) { trip_dist = d; }
    void setArcDist(float d) { arc_dist = d; }
    void setManuevers(uint32_t n) { manuevers = n; }
    void log() {
      valhalla::midgard::logging::Log(
        (boost::format("%f,%f,%f,%f,%s,%d,%d,%d,%f,%f,%d")
          % origin.first % origin.second % destination.first % destination.second
          % success % passes % runtime % trip_time % trip_dist % arc_dist % manuevers).str(),
        " [STATISTICS] ");
    }
  };
}

/**
 * Test a single path from origin to destination.
 */
TripPath PathTest(GraphReader& reader, PathLocation& origin,
                  PathLocation& dest, PathAlgorithm* pathalgorithm,
                  const std::shared_ptr<DynamicCost>* mode_costing,
                  const TravelMode mode, PathStatistics& data,
                  bool multi_run, uint32_t iterations,
                  bool using_astar, bool match_test,
                  const std::string& routetype) {
  auto t1 = std::chrono::high_resolution_clock::now();
  std::vector<PathInfo> pathedges;
  pathedges = pathalgorithm->GetBestPath(origin, dest, reader, mode_costing, mode);
  cost_ptr_t cost = mode_costing[static_cast<uint32_t>(mode)];
  cost->set_pass(0);
  data.incPasses();
  if (pathedges.size() == 0 ||
      (routetype == "pedestrian" && pathalgorithm->has_ferry())) {
    if (cost->AllowMultiPass()) {
      LOG_INFO("Try again with relaxed hierarchy limits");
      cost->set_pass(1);
      pathalgorithm->Clear();
      float relax_factor = (using_astar) ? 16.0f : 8.0f;
      float expansion_within_factor = (using_astar) ? 4.0f : 2.0f;
      cost->RelaxHierarchyLimits(using_astar, expansion_within_factor);
      pathedges = pathalgorithm->GetBestPath(origin, dest, reader, mode_costing, mode);
      data.incPasses();
    }
  }
  if (pathedges.size() == 0) {
    // Return an empty trip path
    return TripPath();
  }

  auto t2 = std::chrono::high_resolution_clock::now();
  uint32_t msecs = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
  LOG_INFO("PathAlgorithm GetBestPath took " + std::to_string(msecs) + " ms");

  // Form trip path
  t1 = std::chrono::high_resolution_clock::now();
  AttributesController controller;
  TripPath trip_path = TripPathBuilder::Build(controller, reader, mode_costing,
                                              pathedges, origin, dest,
                                              std::list<PathLocation>{});
  t2 = std::chrono::high_resolution_clock::now();
  msecs = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
  LOG_INFO("TripPathBuilder took " + std::to_string(msecs) + " ms");

  // Time how long it takes to clear the path
  t1 = std::chrono::high_resolution_clock::now();
  pathalgorithm->Clear();
  t2 = std::chrono::high_resolution_clock::now();
  msecs = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
  LOG_INFO("PathAlgorithm Clear took " + std::to_string(msecs) + " ms");

  // Test RouteMatcher
  if (match_test) {
    LOG_INFO("Testing RouteMatcher");

    // Get shape
    std::vector<PointLL> shape = decode<std::vector<PointLL>>(trip_path.shape());
    std::vector<Measurement> trace; trace.reserve(shape.size());
    std::transform(shape.begin(), shape.end(), std::back_inserter(trace), [](const PointLL& p){ return Measurement{p, 0, 0}; });

    // Use the shape to form a single edge correlation at the start and end of
    // the shape (using heading).
    std::vector<valhalla::baldr::Location> locations{shape.front(), shape.back()};
    locations.front().heading_ = std::round(PointLL::HeadingAlongPolyline(shape, 30.f));
    locations.back().heading_ = std::round(PointLL::HeadingAtEndOfPolyline(shape, 30.f));

    std::shared_ptr<DynamicCost> cost = mode_costing[static_cast<uint32_t>(mode)];
    const auto projections = Search(locations, reader, cost->GetEdgeFilter(), cost->GetNodeFilter());
    std::vector<PathLocation> path_location;
    for (auto loc : locations) {
      path_location.push_back(projections.at(loc));
    }
    std::vector<PathInfo> path;
    bool ret = RouteMatcher::FormPath(mode_costing, mode, reader, trace,
                     path_location, path);
    if (ret) {
      LOG_INFO("RouteMatcher succeeded");
    } else {
      LOG_ERROR("RouteMatcher failed");
    }
  }

  // Run again to see benefits of caching
  if (multi_run) {
    uint32_t totalms = 0;
    for (uint32_t i = 0; i < iterations; i++) {
      t1 = std::chrono::high_resolution_clock::now();
      pathedges = pathalgorithm->GetBestPath(origin, dest, reader, mode_costing, mode);
      t2 = std::chrono::high_resolution_clock::now();
      totalms += std::chrono::duration_cast<std::chrono::milliseconds>(t2-t1).count();
      pathalgorithm->Clear();
    }
    msecs = totalms / iterations;
    LOG_INFO("PathAlgorithm GetBestPath average: " + std::to_string(msecs) + " ms");
  }
  return trip_path;
}

namespace std {

//TODO: maybe move this into location.h if its actually useful elsewhere than here?
std::string to_string(const valhalla::baldr::Location& l) {
  std::string s;
  for (auto address : { &l.name_, &l.street_, &l.city_, &l.state_, &l.zip_, &l
      .country_ }) {
    s.append(*address);
    s.push_back(',');
  }
  s.erase(s.end() - 1);
  return s;
}

//TODO: maybe move this into location.h if its actually useful elsewhere than here?
std::string to_json(const valhalla::baldr::Location& l) {
  std::string json = "{";
  json += "\"lat\":";
  json += std::to_string(l.latlng_.lat());

  json += ",\"lon\":";
  json += std::to_string(l.latlng_.lng());

  json += ",\"type\":\"";
  json +=
      (l.stoptype_ == valhalla::baldr::Location::StopType::THROUGH) ?
          "through" : "break";
  json += "\"";

  if (l.heading_) {
    json += ",\"heading\":";
    json += *l.heading_;
  }

  if (!l.name_.empty()) {
    json += ",\"name\":\"";
    json += l.name_;
    json += "\"";
  }

  if (!l.street_.empty()) {
    json += ",\"street\":\"";
    json += l.street_;
    json += "\"";
  }

  if (!l.city_.empty()) {
    json += ",\"city\":\"";
    json += l.city_;
    json += "\"";
  }

  if (!l.state_.empty()) {
    json += ",\"state\":\"";
    json += l.state_;
    json += "\"";
  }

  if (!l.zip_.empty()) {
    json += ",\"postal_code\":\"";
    json += l.zip_;
    json += "\"";
  }

  if (!l.country_.empty()) {
    json += ",\"country\":\"";
    json += l.country_;
    json += "\"";
  }

  json += "}";

  return json;
}

}

std::string GetFormattedTime(uint32_t seconds) {
  uint32_t hours = (uint32_t) seconds / 3600;
  uint32_t minutes = ((uint32_t) (seconds / 60)) % 60;
  std::string formattedTime = "";
  // Hours
  if (hours > 0) {
    formattedTime += std::to_string(hours);
    formattedTime += (hours == 1) ? " hour" : " hours";
    if (minutes > 0) {
      formattedTime += ", ";
    }
  }
  // Minutes
  if (minutes > 0) {
    formattedTime += std::to_string(minutes);
    formattedTime += (minutes == 1) ? " minute" : " minutes";
  }
  return formattedTime;

}

TripDirections DirectionsTest(const DirectionsOptions& directions_options,
                              TripPath& trip_path, const PathLocation& origin,
                              const PathLocation& destination, PathStatistics& data) {
  DirectionsBuilder directions;
  TripDirections trip_directions = directions.Build(directions_options,
                                                    trip_path);
  std::string units = (
      directions_options.units()
          == DirectionsOptions::Units::DirectionsOptions_Units_kKilometers ?
          "km" : "mi");
  int m = 1;
  valhalla::midgard::logging::Log("From: " + std::to_string(origin),
                                  " [NARRATIVE] ");
  valhalla::midgard::logging::Log("To: " + std::to_string(destination),
                                  " [NARRATIVE] ");
  valhalla::midgard::logging::Log(
      "==============================================", " [NARRATIVE] ");
  for (int i = 0; i < trip_directions.maneuver_size(); ++i) {
    const auto& maneuver = trip_directions.maneuver(i);

    // Depart instruction
    if (maneuver.has_depart_instruction()) {
      valhalla::midgard::logging::Log(
          (boost::format("   %s")
              % maneuver.depart_instruction()).str(),
          " [NARRATIVE] ");
    }

    // Verbal depart instruction
    if (maneuver.has_verbal_depart_instruction()) {
      valhalla::midgard::logging::Log(
          (boost::format("   VERBAL_DEPART: %s")
              % maneuver.verbal_depart_instruction()).str(),
          " [NARRATIVE] ");
    }

    // Instruction
    valhalla::midgard::logging::Log(
        (boost::format("%d: %s | %.1f %s") % m++ % maneuver.text_instruction()
            % maneuver.length() % units).str(),
        " [NARRATIVE] ");

    // Verbal transition alert instruction
    if (maneuver.has_verbal_transition_alert_instruction()) {
      valhalla::midgard::logging::Log(
          (boost::format("   VERBAL_ALERT: %s")
              % maneuver.verbal_transition_alert_instruction()).str(),
          " [NARRATIVE] ");
    }

    // Verbal pre transition instruction
    if (maneuver.has_verbal_pre_transition_instruction()) {
      valhalla::midgard::logging::Log(
          (boost::format("   VERBAL_PRE: %s")
              % maneuver.verbal_pre_transition_instruction()).str(),
          " [NARRATIVE] ");
    }

    // Verbal post transition instruction
    if (maneuver.has_verbal_post_transition_instruction()) {
      valhalla::midgard::logging::Log(
          (boost::format("   VERBAL_POST: %s")
              % maneuver.verbal_post_transition_instruction()).str(),
          " [NARRATIVE] ");
    }

    // Arrive instruction
    if (maneuver.has_arrive_instruction()) {
      valhalla::midgard::logging::Log(
          (boost::format("   %s")
              % maneuver.arrive_instruction()).str(),
          " [NARRATIVE] ");
    }

    // Verbal arrive instruction
    if (maneuver.has_verbal_arrive_instruction()) {
      valhalla::midgard::logging::Log(
          (boost::format("   VERBAL_ARRIVE: %s")
              % maneuver.verbal_arrive_instruction()).str(),
          " [NARRATIVE] ");
    }

    if (i < trip_directions.maneuver_size() - 1)
      valhalla::midgard::logging::Log(
          "----------------------------------------------", " [NARRATIVE] ");
  }
  valhalla::midgard::logging::Log(
      "==============================================", " [NARRATIVE] ");
  valhalla::midgard::logging::Log(
      "Total time: " + GetFormattedTime(trip_directions.summary().time()),
      " [NARRATIVE] ");
  valhalla::midgard::logging::Log(
      (boost::format("Total length: %.1f %s")
          % trip_directions.summary().length() % units).str(),
      " [NARRATIVE] ");
  if(origin.date_time_) {
    valhalla::midgard::logging::Log(
        "Departed at: " + *origin.date_time_,
        " [NARRATIVE] ");
  }
  if(destination.date_time_) {
    valhalla::midgard::logging::Log(
        "Arrived at: " + *destination.date_time_,
        " [NARRATIVE] ");
  }
  data.setTripTime(trip_directions.summary().time());
  data.setTripDist(trip_directions.summary().length());
  data.setManuevers(trip_directions.maneuver_size());

  return trip_directions;
}

// Returns the costing method (created from the dynamic cost factory).
// Get the costing options. Merge in any request costing options that
// override those in the config.
//Armin H.
valhalla::sif::cost_ptr_t get_costing(CostFactory<DynamicCost> factory,
                                      boost::property_tree::ptree& request,
                                      const std::string& costing,
                                      GraphReader& graphreader) {
  std::string method_options = "costing_options." + costing;
  auto costing_options = request.get_child(method_options, {});
  return factory.Create(costing, costing_options, graphreader);
}

// Main method for testing a single path
int main(int argc, char *argv[]) {
  bpo::options_description options("valhalla_run_route " VERSION "\n"
  "\n"
  " Usage: valhalla_run_route [options]\n"
  "\n"
  "valhalla_run_route is a simple command line test tool for shortest path routing. "
  "\n"
  "Use the -o and -d options OR the -j option for specifying the locations. "
  "\n"
  "\n");

  std::string origin, destination, routetype, json, config;
  bool connectivity, multi_run, match_test;
  connectivity = multi_run = match_test = false;
  uint32_t iterations;

  options.add_options()("help,h", "Print this help message.")(
      "version,v", "Print the version of this software.")(
      "origin,o",
      boost::program_options::value<std::string>(&origin),
      "Origin: lat,lng,[through|stop],[name],[street],[city/town/village],[state/province/canton/district/region/department...],[zip code],[country].")(
      "destination,d",
      boost::program_options::value<std::string>(&destination),
      "Destination: lat,lng,[through|stop],[name],[street],[city/town/village],[state/province/canton/district/region/department...],[zip code],[country].")(
      "type,t", boost::program_options::value<std::string>(&routetype),
      "Route Type: auto|bicycle|pedestrian|auto-shorter")(
      "json,j",
      boost::program_options::value<std::string>(&json),
      "JSON Example: '{\"locations\":[{\"lat\":40.748174,\"lon\":-73.984984,\"type\":\"break\",\"heading\":200,\"name\":\"Empire State Building\",\"street\":\"350 5th Avenue\",\"city\":\"New York\",\"state\":\"NY\",\"postal_code\":\"10118-0110\",\"country\":\"US\"},{\"lat\":40.749231,\"lon\":-73.968703,\"type\":\"break\",\"name\":\"United Nations Headquarters\",\"street\":\"405 East 42nd Street\",\"city\":\"New York\",\"state\":\"NY\",\"postal_code\":\"10017-3507\",\"country\":\"US\"}],\"costing\":\"auto\",\"directions_options\":{\"units\":\"miles\"}}'")
      ("connectivity", "Generate a connectivity map before testing the route.")
      ("match-test", "Test RouteMatcher with resulting shape.")
      ("multi-run", bpo::value<uint32_t>(&iterations), "Generate the route N additional times before exiting.")
      // positional arguments
      ("config", bpo::value<std::string>(&config), "Valhalla configuration file");

  bpo::positional_options_description pos_options;
  pos_options.add("config", 1);

  bpo::variables_map vm;

  try {
    bpo::store(
        bpo::command_line_parser(argc, argv).options(options).positional(
            pos_options).run(),
        vm);
    bpo::notify(vm);

  } catch (std::exception &e) {
    std::cerr << "Unable to parse command line options because: " << e.what()
              << "\n" << "This is a bug, please report it at " PACKAGE_BUGREPORT
              << "\n";
    return EXIT_FAILURE;
  }

  if (vm.count("help")) {
    std::cout << options << "\n";
    return EXIT_SUCCESS;
  }

  if (vm.count("version")) {
    std::cout << "valhalla_run_route " << VERSION << "\n";
    return EXIT_SUCCESS;
  }

  if (vm.count("connectivity")) {
    connectivity = true;
  }

  if (vm.count("match-test")) {
    match_test = true;
  }

  if (vm.count("multi-run")) {
    multi_run = true;
  }

  // Directions options - set defaults
  DirectionsOptions directions_options;
  directions_options.set_units(
      DirectionsOptions::Units::DirectionsOptions_Units_kMiles);
  directions_options.set_language("en-US");

  // Locations
  std::vector<valhalla::baldr::Location> locations;

  // argument checking and verification
  boost::property_tree::ptree json_ptree;
  if (vm.count("json") == 0) {
    for (auto arg : std::vector<std::string> { "origin", "destination", "type",
        "config" }) {
      if (vm.count(arg) == 0) {
        std::cerr
            << "The <"
            << arg
            << "> argument was not provided, but is mandatory when json is not provided\n\n";
        std::cerr << options << "\n";
        return EXIT_FAILURE;
      }
    }
    locations.push_back(valhalla::baldr::Location::FromCsv(origin));
    locations.push_back(valhalla::baldr::Location::FromCsv(destination));
  }
  ////////////////////////////////////////////////////////////////////////////
  // Process json input
  else {
    std::stringstream stream;
    stream << json;
    boost::property_tree::read_json(stream, json_ptree);

    try {
      for (const auto& location : json_ptree.get_child("locations"))
        locations.emplace_back(std::move(valhalla::baldr::Location::FromPtree(location.second)));
      if (locations.size() < 2)
        throw;
    } catch (...) {
      throw std::runtime_error(
          "insufficiently specified required parameter 'locations'");
    }

    // Parse out the type of route - this provides the costing method to use
    std::string costing;
    try {
      routetype = json_ptree.get<std::string>("costing");
    } catch (...) {
      throw std::runtime_error("No edge/node costing provided");
    }

    // Grab the directions options, if they exist
    auto directions_options_ptree_ptr = json_ptree.get_child_optional(
        "directions_options");
    if (directions_options_ptree_ptr) {
      directions_options = valhalla::odin::GetDirectionsOptions(
          *directions_options_ptree_ptr);
    }

    // Grab the date_time, if is exists
    auto date_time_ptr = json_ptree.get_child_optional("date_time");
    if (date_time_ptr) {
      auto date_time_type = (*date_time_ptr).get<int>("type");
      auto date_time_value = (*date_time_ptr).get_optional<std::string>("value");

      if (date_time_type == 0) // current
        locations.front().date_time_ = "current";
      else if (date_time_type == 1) // depart at
        locations.front().date_time_ = date_time_value;
      else if (date_time_type == 2) // arrive by
        locations.back().date_time_ = date_time_value;
    }

  }

  //parse the config
  boost::property_tree::ptree pt;
  boost::property_tree::read_json(config.c_str(), pt);

  //configure logging
  boost::optional<boost::property_tree::ptree&> logging_subtree = pt
      .get_child_optional("thor.logging");
  if (logging_subtree) {
    auto logging_config = valhalla::midgard::ToMap<
        const boost::property_tree::ptree&,
        std::unordered_map<std::string, std::string> >(logging_subtree.get());
    valhalla::midgard::logging::Configure(logging_config);
  }

  // Something to hold the statistics
  uint32_t n = locations.size() - 1;
  PathStatistics data({locations[0].latlng_.lat(), locations[0].latlng_.lng()},
                      {locations[n].latlng_.lat(), locations[n].latlng_.lng()});

  // Crow flies distance between locations (km)
  float d1 = 0.0f;
  for (uint32_t i = 0; i < n; i++) {
    d1 += locations[i].latlng_.Distance(locations[i+1].latlng_) * kKmPerMeter;
  }

  // Get something we can use to fetch tiles
  valhalla::baldr::GraphReader reader(pt.get_child("mjolnir"));

  auto t0 = std::chrono::high_resolution_clock::now();

  // Construct costing
  CostFactory<DynamicCost> factory;
  //Armin H.
  factory.Register("auto", CreateAutoCost);
  factory.Register("auto_shorter", CreateAutoShorterCost);
  factory.Register("bus", CreateBusCost);
  factory.Register("bicycle", CreateBicycleCost);
  factory.Register("pedestrian", CreatePedestrianCost);
  factory.Register("truck", CreateTruckCost);
  factory.Register("transit", CreateTransitCost);

  // Figure out the route type
  for (auto & c : routetype)
    c = std::tolower(c);
  LOG_INFO("routetype: " + routetype);

  // Get the costing method - pass the JSON configuration
  TripPath trip_path;
  TravelMode mode;
  std::shared_ptr<DynamicCost> mode_costing[4];
  if (routetype == "multimodal") {
    // Create array of costing methods per mode and set initial mode to
    // pedestrian
    //Armin H.
    mode_costing[0] = get_costing(factory, json_ptree, "auto", reader);
    mode_costing[1] = get_costing(factory, json_ptree, "pedestrian", reader);
    mode_costing[2] = get_costing(factory, json_ptree, "bicycle", reader);
    mode_costing[3] = get_costing(factory, json_ptree, "transit", reader);
    mode = TravelMode::kPedestrian;
  } else {
    // Assign costing method, override any config options that are in the
    // json request
    //Armin H.
    std::shared_ptr<DynamicCost> cost = get_costing(factory,
                          json_ptree, routetype, reader);
    mode = cost->travel_mode();
    mode_costing[static_cast<uint32_t>(mode)] = cost;
  }

  // Find locations
  auto t1 = std::chrono::high_resolution_clock::now();
  std::shared_ptr<DynamicCost> cost = mode_costing[static_cast<uint32_t>(mode)];
  const auto projections = Search(locations, reader, cost->GetEdgeFilter(), cost->GetNodeFilter());
  std::vector<PathLocation> path_location;
  for (auto loc : locations) {
    try {
      path_location.push_back(projections.at(loc));
      //TODO: get transit level for transit costing
      //TODO: if transit send a non zero radius
    } catch (...) {
      data.setSuccess("fail_invalid_origin");
      data.log();
      return EXIT_FAILURE;
    }
  }
  // If we are testing connectivity
  if (connectivity) {
    std::unordered_map<size_t, size_t> color_counts;
    connectivity_map_t connectivity_map(pt.get_child("mjolnir"));
    auto colors = connectivity_map.get_colors(TileHierarchy::levels().rbegin()->first,
                                              path_location.back(), 0);
    for(auto color : colors){
      auto itr = color_counts.find(color);
      if(itr == color_counts.cend())
        color_counts[color] = 1;
      else
        ++itr->second;
    }

    //are all the locations in the same color regions
    bool connected = false;
    for(const auto& c : color_counts) {
      if(c.second == locations.size()) {
        connected = true;
        break;
      }
    }
    if(!connected) {
      LOG_INFO("No tile connectivity between locations");
      data.setSuccess("fail_no_connectivity");
      data.log();
      return EXIT_FAILURE;
    }
  }

  // Normalize the edge scores
  for (auto& correlated : path_location) {
    auto minScoreEdge = *std::min_element(correlated.edges.begin(), correlated.edges.end(),
       [](PathLocation::PathEdge i, PathLocation::PathEdge j)->bool {
         return i.score < j.score;
       });

    for(auto& e : correlated.edges) {
      e.score -= minScoreEdge.score;
    }
  }
  auto t2 = std::chrono::high_resolution_clock::now();
  uint32_t msecs = std::chrono::duration_cast<std::chrono::milliseconds>(
                  t2 - t1).count();
  LOG_INFO("Location Processing took " + std::to_string(msecs) + " ms");

  // Get the route
  AStarPathAlgorithm astar;
  BidirectionalAStar bd;
  MultiModalPathAlgorithm mm;
  for (uint32_t i = 0; i < n; i++) {
    // Choose path algorithm
    PathAlgorithm* pathalgorithm;
    if (routetype == "multimodal") {
      pathalgorithm = &mm;
    } else {
      // Use bidirectional except for possible trivial cases
      pathalgorithm = &bd;
      for (auto& edge1 : path_location[i].edges) {
        for (auto& edge2 : path_location[i+1].edges) {
          if (edge1.id == edge2.id) {
            pathalgorithm = &astar;
          }
        }
      }
    }
    bool using_astar = (pathalgorithm == &astar);

    // Get the best path
    try {
      trip_path = PathTest(reader, path_location[i], path_location[i + 1],
                           pathalgorithm, mode_costing, mode, data, multi_run,
                           iterations, using_astar, match_test, routetype);
    } catch (std::runtime_error& rte) {
      LOG_ERROR("trip_path not found");
    }

    // If successful get directions
    if (trip_path.node().size() > 0) {
      // Try the the directions
      t1 = std::chrono::high_resolution_clock::now();
      TripDirections trip_directions = DirectionsTest(directions_options, trip_path,
                        path_location[i], path_location[i+1], data);
      t2 = std::chrono::high_resolution_clock::now();
      msecs = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();

      auto trip_time = trip_directions.summary().time();
      auto trip_length = trip_directions.summary().length() * 1609.344f;
      LOG_INFO("trip_processing_time (ms)::" + std::to_string(msecs));
      LOG_INFO("trip_time (secs)::" + std::to_string(trip_time));
      LOG_INFO("trip_length (meters)::" + std::to_string(trip_length));
      data.setSuccess("success");
    } else {
      // Check if origins are unreachable
      bool unreachable_origin = false;
      for (auto& edge : path_location[i].edges) {
        const GraphTile* tile = reader.GetGraphTile(edge.id);
        const DirectedEdge* directededge = tile->directededge(edge.id);
        auto ei = tile->edgeinfo(directededge->edgeinfo_offset());
        if (directededge->unreachable()) {
          LOG_INFO("Origin edge is unconnected: wayid = " + std::to_string(ei.wayid()));
          unreachable_origin = true;
        }
        LOG_INFO("Origin wayId = " + std::to_string(ei.wayid()));
      }

      // Check if destinations are unreachable
      bool unreachable_dest = false;
      for (auto& edge : path_location[i+1].edges) {
        const GraphTile* tile = reader.GetGraphTile(edge.id);
        const DirectedEdge* directededge = tile->directededge(edge.id);
        auto ei = tile->edgeinfo(directededge->edgeinfo_offset());
        if (directededge->unreachable()) {
          LOG_INFO("Destination edge is unconnected: wayid = " + std::to_string(ei.wayid()));
          unreachable_dest = true;
        }
        LOG_INFO("Destination wayId = " + std::to_string(ei.wayid()));
      }

      // Route was unsuccessful
      if (unreachable_origin && unreachable_dest) {
        data.setSuccess("fail_unreachable_locations");
      } else if (unreachable_origin) {
        data.setSuccess("fail_unreachable_origin");
      } else if (unreachable_dest) {
        data.setSuccess("fail_unreachable_dest");
      } else {
        data.setSuccess("fail_no_route");
      }
    }
  }

  // Set the arc distance. Convert to miles if needed
  if (directions_options.units() == DirectionsOptions::Units::DirectionsOptions_Units_kMiles) {
    d1 *= kMilePerKm;
  }
  data.setArcDist(d1);

  // Time all stages for the stats file: location processing,
  // path computation, trip path building, and directions
  t2 = std::chrono::high_resolution_clock::now();
  msecs = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t0).count();
  LOG_INFO("Total time= " + std::to_string(msecs) + " ms");
  data.addRuntime(msecs);
  data.log();

  return EXIT_SUCCESS;
}