#include <El.hpp>

#include <boost/program_options.hpp>
#include <boost/mpi.hpp>
#include <boost/format.hpp>

#define SKYLARK_NO_ANY
#include <skylark.hpp>

#include <iostream>

namespace bpo = boost::program_options;

enum file_types {
    LIBSVM,
    ARC_LIST
};

template<typename InputType, typename FactorType, typename UType = FactorType,
         typename YType = FactorType>
void execute(bool directory, const std::string &fname,
    const std::string &hdfs, int port, const std::vector<int> &profile, int k,
    const skylark::nla::approximate_svd_params_t &params,
    const std::string &prefix,
    skylark::base::context_t &context) {

    boost::mpi::communicator world;
    int rank = world.rank();

    InputType A;
    FactorType S, V;
    UType U;
    YType Y;

    boost::mpi::timer timer;

    if (profile.empty()) {
        // Load A and Y (Y is thrown away)
        if (rank == 0) {
            std::cout << "Reading the matrix... ";
            std::cout.flush();
            timer.restart();
        }

        if (!hdfs.empty()) {
#       if SKYLARK_HAVE_LIBHDFS

            hdfsFS fs;
            if (rank == 0)
                fs = hdfsConnect(hdfs.c_str(), port);

            if (directory)
                SKYLARK_THROW_EXCEPTION(skylark::base::io_exception() <<
                    skylark::base::error_msg(
                                             "HDFS directory reading not yet supported."))
                else
                    skylark::utility::io::ReadLIBSVM(
                                                     fs, fname, A, Y, skylark::base::ROWS);

#       else

            SKYLARK_THROW_EXCEPTION(skylark::base::io_exception() <<
                skylark::base::error_msg("Install libhdfs for HDFS support!"));

#       endif
        } else {
            if (directory)
                skylark::utility::io::ReadDirLIBSVM(
                                                    fname, A, Y, skylark::base::ROWS);
            else
                skylark::utility::io::ReadLIBSVM(fname, A, Y, skylark::base::ROWS);
        }

        Y.Empty();
    } else {

        if (rank == 0) {
            std::cout << "Generating random matrix... ";
            std::cout.flush();
            timer.restart();
        }

        skylark::base::UniformMatrix(A, profile[0], profile[1], context);
    }

    if (rank == 0)
        std::cout <<"took " << boost::format("%.2e") % timer.elapsed()
                  << " sec\n";

    /* Compute approximate SVD */
    if (rank == 0) {
        std::cout << "Computing approximate SVD...";
        std::cout.flush();
        timer.restart();
    }

    skylark::nla::ApproximateSVD(A, U, S, V, k, context, params);

    if (rank == 0)
        std::cout <<"Took " << boost::format("%.2e") % timer.elapsed()
                  << " sec\n";

    /* Write results */
    if (rank == 0) {
        std::cout << "Writing results...";
        std::cout.flush();
        timer.restart();
    }

    El::Write(U, prefix + ".U", El::ASCII);
    El::Write(S, prefix + ".S", El::ASCII);
    El::Write(V, prefix + ".V", El::ASCII);

    if (rank == 0)
        std::cout <<"took " << boost::format("%.2e") % timer.elapsed()
                  << " sec\n";
}

template<typename InputType, typename FactorType, typename UType = FactorType,
         typename YType = FactorType>
void execute_sym(bool directory, const std::string &fname,
    const std::string& ftype,
    const std::string &hdfs, int port,
    const std::vector<int> &profile, bool lower, int k,
    const skylark::nla::approximate_svd_params_t &params,
    const std::string &prefix,
    skylark::base::context_t &context) {

    boost::mpi::communicator world;
    int rank = world.rank();

    InputType A;
    FactorType S, V;
    YType Y;

    boost::mpi::timer timer;

    if (profile.empty()) {
        // Load A and Y (Y is thrown away)
        if (rank == 0) {
            std::cout << "Reading the matrix... ";
            std::cout.flush();
            timer.restart();
        }

        if (!hdfs.empty()) {
#       if SKYLARK_HAVE_LIBHDFS

            hdfsFS fs;
            if (rank == 0)
                fs = hdfsConnect(hdfs.c_str(), port);

            if (directory)
                SKYLARK_THROW_EXCEPTION(skylark::base::io_exception() <<
                    skylark::base::error_msg(
                                             "HDFS directory reading not yet supported."))
                else
                    skylark::utility::io::ReadLIBSVM(
                                                     fs, fname, A, Y, skylark::base::ROWS);

#       else

            SKYLARK_THROW_EXCEPTION(skylark::base::io_exception() <<
                skylark::base::error_msg("Install libhdfs for HDFS support!"));

#       endif
        } else {
            // FIXME: ugly, fix options
            if (ftype.compare("ARC_LIST") == 0) {
                skylark::utility::io::ReadArcList(fname, A, world, true);
            } else {
                if (directory)
                    skylark::utility::io::ReadDirLIBSVM(
                                                        fname, A, Y, skylark::base::ROWS);
                else
                    skylark::utility::io::ReadLIBSVM(
                                                     fname, A, Y, skylark::base::ROWS);
            }
        }

        Y.Empty();

    } else {

        SKYLARK_THROW_EXCEPTION(skylark::base::unsupported_base_operation() <<
            skylark::base::error_msg("Uniform symmetric matrix generating not supported yet."));
    }

    if (rank == 0)
        std::cout << "took " << boost::format("%.2e") % timer.elapsed()
                  << " sec\n";

    /* Compute approximate SVD */
    if (rank == 0) {
        std::cout << "Computing approximate SVD...";
        std::cout.flush();
        timer.restart();
    }

    skylark::nla::ApproximateSymmetricSVD(lower ? El::LOWER : El::UPPER,
        A, V, S, k, context, params);

    if (rank == 0)
        std::cout <<"Took " << boost::format("%.2e") % timer.elapsed()
                  << " sec\n";

    /* Write results */
    if (rank == 0) {
        std::cout << "Writing results...";
        std::cout.flush();
        timer.restart();
    }

    El::Write(S, prefix + ".S", El::ASCII);
    El::Write(V, prefix + ".V", El::ASCII);

    if (rank == 0)
        std::cout <<"took " << boost::format("%.2e") % timer.elapsed()
                  << " sec\n";

    world.barrier();
}

int main(int argc, char* argv[]) {

    El::Initialize(argc, argv);

    boost::mpi::communicator world;
    int rank = world.rank();
    int size = world.size();

    int seed, k, powerits, port;
    std::string fname, ftype, prefix, hdfs;
    bool as_symmetric, as_sparse, skipqr, use_single, lower, directory;
    int oversampling_ratio, oversampling_additive;
    std::vector<int> profile;

    // Parse options
    bpo::options_description desc("Options");
    desc.add_options()
        ("help,h", "produce a help message")
        ("inputfile",
            bpo::value<std::string>(&fname),
            "Input file to run approximate SVD on (default libsvm format).")
        ("filetype",
            bpo::value<std::string>(&ftype),
            "Input file type (LIBSVM or ARC_LIST).")
        ("directory,d", "Whether inputfile is a directory of files whose"
            " concatination is the input.")
        ("seed,s",
            bpo::value<int>(&seed)->default_value(38734),
            "Seed for random number generation. OPTIONAL.")
        ("hdfs",
            bpo::value<std::string>(&hdfs)->default_value(""),
            "If not empty, will assume file is in an HDFS. "
            "Parameter is filesystem name.")
        ("port",
            bpo::value<int>(&port)->default_value(0),
            "For HDFS: port to use.")
        ("rank,k",
            bpo::value<int>(&k)->default_value(6),
            "Target rank. OPTIONAL.")
        ("powerits,i",
            bpo::value<int>(&powerits)->default_value(2),
            "Number of power iterations. OPTIONAL.")
        ("skipqr", "Whether to skip QR in each iteration. Higher than one power"
            " iterations is not recommended in this mode.")
        ("ratio,r",
            bpo::value<int>(&oversampling_ratio)->default_value(2),
            "Ratio of oversampling of rank. OPTIONAL.")
        ("additive,a",
            bpo::value<int>(&oversampling_additive)->default_value(0),
            "Additive factor for oversampling of rank. OPTIONAL.")
        ("symmetric", "Whether to treat the matrix as symmetric. "
         "Only upper part will be accessed, unless --lower is used.")
        ("lower", "For symmetric matrix, access only lower part (upper is default).")
        ("sparse", "Whether to load the matrix as a sparse one.")
        ("single", "Whether to use single precision instead of double.")
        ("profile",
            bpo::value<std::vector<int> >()->multitoken(),
            "Generate random matrix and run on it (for profiling)."
            "Requires specification of height and width. OPTIONAL.")
        ("prefix",
            bpo::value<std::string>(&prefix)->default_value("out"),
            "Prefix for output files (prefix.U.txt, prefix.S.txt"
            " and prefix.V.txt. OPTIONAL.");

    bpo::positional_options_description positional;
    positional.add("inputfile", 1);

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

        if (vm.count("help")) {
            if (rank == 0) {
                std::cout << "Usage: " << argv[0]
                          << " [options] input-file-name" << std::endl;
                std::cout << desc;
            }
            world.barrier();
            return 0;
        }

        if (vm["profile"].empty() && !vm.count("inputfile")) {
            if (rank == 0)
                std::cout << "Input file is required." << std::endl;
            world.barrier();
            return -1;
        }

        if (!vm["profile"].empty()) {
            if (vm["profile"].as<std::vector<int> >().size() < 2) {
                if (rank == 0)
                    std::cout << "Please specify height and width for --profile."
                              << std::endl;
                world.barrier();
                return -1;
            } else
                profile = vm["profile"].as<std::vector<int> >();
        }

        bpo::notify(vm);

        as_symmetric = vm.count("symmetric");
        as_sparse = vm.count("sparse");
        skipqr = vm.count("skipqr");
        use_single = vm.count("single");
        directory = vm.count("directory");
        lower = vm.count("lower");

    } catch(bpo::error& e) {
        if (rank == 0) {
            std::cerr << e.what() << std::endl;
            std::cerr << desc << std::endl;
        }
        world.barrier();
        return -1;
    }

    skylark::base::context_t context(seed);

    skylark::nla::approximate_svd_params_t params;
    params.skip_qr = skipqr;
    params.num_iterations = powerits;
    params.oversampling_ratio = oversampling_ratio;
    params.oversampling_additive = oversampling_additive;

    SKYLARK_BEGIN_TRY()

        if (size == 1) {
            if (!as_symmetric) {

                if (use_single) {
                    if (as_sparse)
                        execute<skylark::base::sparse_matrix_t<float>,
                                El::Matrix<float> >(
                                    directory, fname, hdfs,
                                    port, profile, k, params, prefix, context);
                    else
                        execute<El::Matrix<float>,
                                El::Matrix<float> >(directory, fname, hdfs,
                                    port, profile, k, params, prefix, context);

                } else {
                    if (as_sparse)
                        execute<skylark::base::sparse_matrix_t<double>,
                                El::Matrix<double> >(
                                     directory, fname, hdfs,
                                     port, profile, k, params, prefix, context);
                    else
                        execute<El::Matrix<double>,
                                El::Matrix<double> >(directory, fname, hdfs,
                                    port, profile, k, params, prefix, context);
                }

            } else {

                if (use_single) {
                    if (as_sparse)
                        execute_sym<skylark::base::sparse_matrix_t<float>,
                                    El::Matrix<float> >(
                                       directory, fname, ftype,
                                       hdfs, port, profile, lower, k, params, prefix,
                                        context);
                    else
                        execute_sym<El::Matrix<float>,
                                    El::Matrix<float> >(directory, fname, ftype,
                                        hdfs, port, profile, lower, k, params, prefix,
                                        context);

                } else {
                    if (as_sparse)
                        execute_sym<skylark::base::sparse_matrix_t<double>,
                                    El::Matrix<double>,
                                    El::Matrix<double> >(
                                        directory, fname, ftype, hdfs, port,
                                        profile, lower, k, params, prefix, context);
                    else
                        execute_sym<El::Matrix<double>,
                                    El::Matrix<double> >(directory, fname,
                                        ftype, hdfs, port, profile, lower, k, params,
                                        prefix, context);
                }
            }

        } else {
            if (!as_symmetric) {

                if (use_single) {
                    if (as_sparse)
                        execute<skylark::base::sparse_vc_star_matrix_t<float>,
                                El::DistMatrix<float, El::STAR, El::STAR>,
                                El::DistMatrix<float, El::VC, El::STAR>,
                                El::DistMatrix<float, El::VC, El::STAR> >(
                                    directory, fname, hdfs,
                                    port, profile, k, params, prefix, context);
                    else
                        execute<El::DistMatrix<float>,
                                El::DistMatrix<float> >(directory, fname, hdfs,
                                    port, profile, k, params, prefix, context);

                } else {
                    if (as_sparse)
                        execute<skylark::base::sparse_vc_star_matrix_t<double>,
                                El::DistMatrix<double, El::STAR, El::STAR>,
                                El::DistMatrix<double, El::VC, El::STAR>,
                                El::DistMatrix<double, El::VC, El::STAR> >(
                                    directory, fname, hdfs,
                                    port, profile, k, params, prefix, context);
                    else
                        execute<El::DistMatrix<double>,
                                El::DistMatrix<double> >(directory, fname, hdfs,
                                    port, profile, k, params, prefix, context);
                }

            } else {

                if (use_single) {
                    if (as_sparse)
                        execute_sym<skylark::base::sparse_vc_star_matrix_t<float>,
                                    El::DistMatrix<float, El::VC, El::STAR>,
                                    El::DistMatrix<float, El::VC, El::STAR> >(
                                        directory, fname, ftype,
                                        hdfs, port, profile, lower, k, params, prefix,
                                        context);
                    else
                        execute_sym<El::DistMatrix<float>,
                                    El::DistMatrix<float> >(directory, fname, ftype,
                                        hdfs, port, profile, lower, k, params, prefix,
                                        context);

                } else {
                    if (as_sparse)
                        execute_sym<skylark::base::sparse_vc_star_matrix_t<double>,
                                    El::DistMatrix<double, El::VC, El::STAR>,
                                    El::DistMatrix<double, El::VC, El::STAR> >(
                                        directory, fname, ftype, hdfs, port,
                                        profile, lower,
                                        k, params, prefix, context);
                    else
                        execute_sym<El::DistMatrix<double>,
                                    El::DistMatrix<double> >(directory, fname,
                                        ftype, hdfs, port, profile, lower, k, params,
                                        prefix, context);
                }
            }
        }

    SKYLARK_END_TRY() SKYLARK_CATCH_AND_PRINT((rank == 0))

    El::Finalize();
    return 0;
}


#include <pcl/common/time.h>
#include <pcl/console/print.h>
#include <pcl/features/fpfh_omp.h>
#include <pcl/features/normal_3d_omp.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/registration/icp.h>
#include <pcl/registration/sample_consensus_prerejective.h>
#include <Eigen/Core>
#include <mil_vision_lib/pcl_tools.hpp>

#define _USE_MATH_DEFINES
#include <cmath>

namespace sub
{
typedef pcl::FPFHSignature33 FeatureT;
typedef pcl::FPFHEstimationOMP<PointNT, PointNT, FeatureT> FeatureEstimationT;
typedef pcl::PointCloud<FeatureT> FeatureCloudT;

float feature_radius = 0.02;
float normal_radius = 0.05;

void compute_features(PointCloudNT::Ptr& cloud, FeatureCloudT::Ptr& feature_cloud)
{
  // Estimate features
  pcl::console::print_highlight("Estimating features...\n");
  FeatureEstimationT feature_estimator;
  feature_estimator.setRadiusSearch(feature_radius);
  feature_estimator.setInputCloud(cloud);
  feature_estimator.setInputNormals(cloud);
  feature_estimator.compute(*feature_cloud);
}
}


// Copyright (c) 2019 Graphcore Ltd. All rights reserved.

#include "poputil/VertexTemplates.hpp"
#include <boost/multi_array.hpp>
#include <boost/program_options.hpp>
#include <boost/test/tools/floating_point_comparison.hpp>
#include <popfloat/experimental/CastToGfloat.hpp>
#include <popfloat/experimental/codelets.hpp>
#include <poplar/Engine.hpp>
#include <popops/codelets.hpp>
#include <poprand/RandomGen.hpp>
#include <poprand/codelets.hpp>

#include "cast_to_gfloat.hpp"
#include "poputil/TileMapping.hpp"

#include <poplibs_support/Compiler.hpp>
#include <poplibs_support/TestDevice.hpp>
#include <poplibs_test/Pass.hpp>
#include <poplibs_test/Util.hpp>

#include <cassert>
#include <iomanip>
#include <iostream>
#include <memory>
#include <random>

#undef ENABLE_GF16_VERTEX

const float sr_tolerance = 0.03;
#include <poplar/CSRFunctions.hpp>

using namespace poplar;
using namespace poplar::program;
using namespace poplibs_test::util;
using namespace poputil;
using namespace popfloat::experimental;
using namespace poplibs_support;
using poplibs_test::Pass;

const OptionFlags simDebugOptions{{"debug.trace", "false"}};

SRDensityType convertStringToSRDensity(const std::string &dist) {
  if (dist == "Uniform") {
    return SRDensityType::UNIFORM;
  } else if (dist == "Normal") {
    return SRDensityType::NORMAL;
  } else if (dist == "TruncatedNormal") {
    return SRDensityType::TRUNCATED_NORMAL;
  } else if (dist == "Bernoulli") {
    return SRDensityType::BERNOULLI;
  } else if (dist == "TruncatedLogistic") {
    return SRDensityType::TRUNCATED_LOGISTIC;
  } else if (dist == "Logistic") {
    return SRDensityType::LOGISTIC;
  } else if (dist == "Laplace") {
    return SRDensityType::LAPLACE;
  } else if (dist == "TruncatedLaplace") {
    return SRDensityType::TRUNCATED_LAPLACE;
  } else if (dist == "LogitNormal") {
    return SRDensityType::LOGIT_NORMAL;
  } else if (dist == "TruncatedLogitNormal") {
    return SRDensityType::TRUNCATED_LOGIT_NORMAL;
  }
  throw poputil::poplibs_error("Gfloat Sr Distribution not supported");
}

int main(int argc, char **argv) {
  namespace po = boost::program_options;

  unsigned man;
  unsigned exp;
  int bias;
  bool enableDenorm;
  bool enableInfsAndNans;
  bool enableNanoo;
  std::string srDensity;
  std::string calcType;
  int numberSRBits;
  float srNoiseMin;
  float srNoiseMax;
  float srNoiseOffset;
  float srNoiseScale;
  float probDown;
  Type inType = FLOAT;
  unsigned inSize;
  DeviceType deviceType = DeviceType::Cpu;
  boost::optional<unsigned> tilesPerIPU;
  unsigned seed;
  unsigned seedModifier;
  float inValue;

  po::options_description desc("Options");
  // clang-format off
  desc.add_options()
    ("help", "Produce help message")
    ("compile-only", "Stop after compilation; don't run the program")
    ("device-type",
     po::value<DeviceType>(&deviceType)->default_value(deviceType),
     deviceTypeHelp)
    ("tiles-per-ipu",
      po::value(&tilesPerIPU),
     "Number of tiles per IPU")
    ("seed", po::value<unsigned>(&seed)->default_value(12352345), "prng seed")
    ("seed-modifier", po::value<unsigned>(&seedModifier)->default_value(785439),
     "prng seedModifier")("profile", "Output profiling report")
    ("man", po::value<unsigned>(&man)->default_value(10),
     "Mantissa size of the Gfloat format")
    ("exp", po::value<unsigned>(&exp)->default_value(5),
     "Exponent size of the Gfloat format")
    ("bias", po::value<int>(&bias)->default_value(15),
     "Exponent bias of the Gfloat format")
    ("enable-denorms",
     po::value<bool>(&enableDenorm)->default_value(true),
     "Enable Denorms")
    ("enable-infs-and-nans",
     po::value<bool>(&enableInfsAndNans)->default_value(true),
     "Enable Infs and Nans")
     ("enable-nanoo-mode",
      po::value<bool>(&enableNanoo)->default_value(true),
     "Enable Nans on overflow ")
     ("sr-noise-density",
     po::value<std::string>(&srDensity)->default_value("Uniform"),
     "SR noise density function: Uniform | Normal | TruncatedNormal | Laplace |"
     " Logistic | LogitNormal | LogitTruncatedNormal")
     ("number-sr-bits",
     po::value<int>(&numberSRBits)->default_value(23),
     "Number of bits for stochastic rounding")
    ("sr-noise-min",
     po::value<float>(&srNoiseMin)->default_value(-0.5),
     "Min value for prng values used for stochastic rounding")
    ("sr-noise-max",
     po::value<float>(&srNoiseMax)->default_value(0.5),
     "Max value for prng values used for stochastic rounding")
    ("sr-noise-offset",
     po::value<float>(&srNoiseOffset)->default_value(0.0),
     "Mean of prng values used for stochastic rounding")
    ("sr-noise-scale",
     po::value<float>(&srNoiseScale)->default_value(1.0),
     "Standard deviation of prng values used for stochastic rounding")
    ("sr-prob-truncate",
     po::value<float>(&probDown)->default_value(0.5),
     "Probability of rounding down (Bernoulli)")
    ("in-type", po::value<Type>(&inType)->default_value(inType),
     "Type of the data")
    ("calc-type",
     po::value<std::string>(&calcType)->default_value("AUTO"),
     "Native type used for gfloat calculation: AUTO | FP32 | FP16")
    ("input-value", po::value<float>(&inValue)->default_value(0.1),
     "Input value")
    ("input-size",
     po::value<unsigned>(&inSize)->default_value(1024),
     "Vector size");
  // clang-format on

  po::variables_map vm;
  try {
    po::store(po::parse_command_line(argc, argv, desc), vm);
    if (vm.count("help")) {
      std::cout << desc << "\n";
      return 1;
    }
    po::notify(vm);
  } catch (std::exception &e) {
    std::cerr << "error: " << e.what() << "\n";
    return 1;
  }

  auto dev = [&]() -> TestDevice {
    if (isIpuModel(deviceType)) {
      // When running on the IPU model we apply global exchange constraints,
      // which is why we create the device from the model here and not using
      // the normal createTestDevice factory function.
      IPUModel ipuModel(deviceTypeToIPUName(deviceType));
      if (tilesPerIPU)
        ipuModel.tilesPerIPU = *tilesPerIPU;
      addGlobalExchangeConstraints(ipuModel);
      setGlobalSyncLatency(ipuModel);
      return ipuModel.createDevice();
    } else {
      if (tilesPerIPU)
        return createTestDevice(deviceType, 1, *tilesPerIPU);
      else
        return createTestDeviceFullSize(deviceType);
    }
  }();

  poplar::Device::createCPUDevice();
  const auto &target = dev.getTarget();
  Graph graph(target);
  poprand::addCodelets(graph);
  popops::addCodelets(graph);
  popfloat::experimental::addCodelets(graph);

  auto gfCastProg = Sequence();

  bool enableNanooMode = enableNanoo && enableInfsAndNans && (exp > 0);
  poplar::FloatingPointBehaviour fpBehaviour(false, false, false, false,
                                             enableNanooMode);

  poplar::setFloatingPointBehaviour(graph, gfCastProg, fpBehaviour,
                                    "setFpBehaviour");
  poplar::setStochasticRounding(graph, gfCastProg, false, "setSR");

#if 0 // def ENABLE_PARAM_PRINT
  std::cout
  << "Cast to Gfloat Params:\n"
  "man       :" << man << "\n"
  "exp       :" << exp << "\n"
  "bias      :" << bias << "\n"
  "enDenorm  :" << enableDenorm << "\n"
  "enInf     :" << enableInfsAndNans << "\n"
  "enNanoo   :" << enableNanooMode << "\n"
  "srDensity :" << srDensity << "\n"
  "srBits    :" << numberSRBits << "\n"
  "randMin   :" << srNoiseMin << "\n"
  "randMax   :" << srNoiseMax << "\n"
  "stdDev    :" << srNoiseScale << "\n"
  "inType    :" << inType << "\n"
  "inSize    :" << inSize << "\n";
#endif
  auto noiseDensity = convertStringToSRDensity(srDensity);

  auto calculationType = convertStringToSpecType(calcType);
  auto gfFormatCfg = GfloatCast::FormatConfig(
      man, exp, bias, enableDenorm, enableInfsAndNans, calculationType);

  Tensor input = graph.addVariable(inType, {inSize}, "input");
  mapTensorLinearly(graph, input);

  auto hInput = std::unique_ptr<float[]>(new float[inSize]);
  for (std::size_t i = 0; i != inSize; ++i) {
    hInput.get()[i] = inValue;
  }

  boost::multi_array<double, 1> hostInput(boost::extents[inSize]);

  std::mt19937 randomEngine;
  writeRandomValues(target, inType, hostInput, -5.0, +5.0, randomEngine);

  auto flpCastOut = std::unique_ptr<float[]>(new float[inSize]);

  // Create stream for input data
  auto inStreamV = graph.addHostToDeviceFIFO("InputVector", inType, inSize);

  gfCastProg = Sequence(Copy(inStreamV, input));

  uint32_t hSeed[2];
  hSeed[0] = seed;
  hSeed[1] = ~seed;

  auto tSeed = graph.addVariable(poplar::UNSIGNED_INT, {2}, "tSeed");
  graph.setTileMapping(tSeed, 0);

  // Create stream for input data
  auto seedStreamV = graph.addHostToDeviceFIFO("seed", UNSIGNED_INT, 2);
  gfCastProg.add(Copy(seedStreamV, tSeed));

  poprand::setSeed(graph, tSeed, seedModifier, gfCastProg, "setSeed");

  auto roundCfg = GfloatCast::RoundConfig(
      RoundType::SX, numberSRBits, gfFormatCfg.getCalculationType(),
      noiseDensity, srNoiseOffset, srNoiseScale, srNoiseMax, srNoiseMin,
      probDown);

  auto gfCast =
      GfloatCast(gfFormatCfg, roundCfg, enableNanooMode,
                 convertTypeToGfloatSpecType(gfFormatCfg.getCalculationType()));

  gfCast.createCastOpParamsTensor(graph, gfCastProg);

  auto castOutput = gfCast.castNativeToGfloat(graph, input, gfCastProg);

  graph.createHostRead("castOutput", castOutput);

  Engine engine(graph, gfCastProg,
                OptionFlags{{"debug.allowOutOfMemory", "true"},
                            {"debug.outputAllSymbols", "true"},
                            {"debug.instrumentCompute", "true"},
                            {"debug.floatPointOpException", "false"},
                            {"debug.nanOverflowException", "false"},
                            {"prng.enableStochasticRounding", "true"},
                            {"prng.seed", std::to_string(seed)}});

  if (vm.count("compile-only"))
    return 0;

  engine.connectStream("InputVector", hInput.get());
  engine.connectStream("seed", hSeed);

  // Run the forward pass.
  if (gfFormatCfg.getCalculationType() == poplar::FLOAT) {
    dev.bind([&](const Device &d) {
      engine.load(d);
      engine.run();
      readAndConvertTensor<float, false>(
          graph.getTarget(), engine, "castOutput", flpCastOut.get(), inSize);
    });
  } else if (gfFormatCfg.getCalculationType() == poplar::HALF) {
    dev.bind([&](const Device &d) {
      engine.load(d);
      engine.run();
      readAndConvertTensor<float, true>(graph.getTarget(), engine, "castOutput",
                                        flpCastOut.get(), inSize);
    });
  }

  unsigned inValueMem;
  std::memcpy(&inValueMem, &inValue, sizeof(inValueMem));
  int e_single;
  float inMan = std::frexp(inValue, &e_single);
  e_single -= 1;
  inMan *= 2.0;

  int m_single = (std::abs(inMan) * (1 << manSizeFp32));

  int maskLen;
  maskLen = ((1 - bias) - e_single);
  maskLen = (maskLen < 0) ? 0 : maskLen;
  maskLen += manSizeFp32 - man;
  maskLen = std::min<uint32_t>(maskLen, manSizeFp32 + 1);

  int castAtomNumMantissaBits =
      (gfFormatCfg.getCalculationType() == poplar::FLOAT) ? manSizeFp32
                                                          : manSizeFp16;
  numberSRBits = std::min<int>(castAtomNumMantissaBits, numberSRBits);

  float normMan = 0.0;
  numberSRBits = std::min<int>(maskLen, numberSRBits);

  int srMask;
  srMask = (1 << numberSRBits) - 1;
  m_single >>= (maskLen - numberSRBits);
  m_single &= srMask;
  normMan = (float)m_single / ((float)(1 << numberSRBits));

  float floorRate = 0;
  for (unsigned idx = 0; idx < inSize; ++idx) {
    if (std::abs(flpCastOut.get()[idx]) <= std::abs(inValue)) {
      ++floorRate;
    }
  }
  floorRate = floorRate / inSize;
  float probFloor = probDown;

  if (noiseDensity != SRDensityType::BERNOULLI) {
    probFloor = 1 - normMan;
    if ((noiseDensity == SRDensityType::NORMAL) ||
        (noiseDensity == SRDensityType::TRUNCATED_NORMAL) ||
        (noiseDensity == SRDensityType::LOGISTIC) ||
        (noiseDensity == SRDensityType::TRUNCATED_LOGISTIC) ||
        (noiseDensity == SRDensityType::LAPLACE) ||
        (noiseDensity == SRDensityType::TRUNCATED_LAPLACE)) {
      probFloor -= 0.5;
    }

    if (probFloor < srNoiseMin) {
      probFloor = 0.0;
    } else if (probFloor > srNoiseMax) {
      probFloor = 1.0;
    } else {
      float probMin = 0.0;
      float probMax = 1.0;
      if (noiseDensity == SRDensityType::UNIFORM) {
        probFloor = (probFloor - srNoiseMin) / (srNoiseMax - srNoiseMin);
      } else if (noiseDensity == SRDensityType::NORMAL) {
        probFloor = 0.5 * (1.0 + std::erf((probFloor - srNoiseOffset) /
                                          (std::sqrt(2.0) * srNoiseScale)));
      } else if (noiseDensity == SRDensityType::TRUNCATED_NORMAL) {
        probFloor = (1.0 + std::erf((probFloor - srNoiseOffset) /
                                    (std::sqrt(2.0) * srNoiseScale)));
        probMin = (1.0 + std::erf((srNoiseMin - srNoiseOffset) /
                                  (std::sqrt(2.0) * srNoiseScale)));
        probMax = (1.0 + std::erf((srNoiseMax - srNoiseOffset) /
                                  (std::sqrt(2.0) * srNoiseScale)));
      } else if (noiseDensity == SRDensityType::LAPLACE) {
        probFloor -= srNoiseOffset;
        if (probFloor != 0.0) {
          probFloor =
              0.5 + (std::abs(probFloor) / probFloor) *
                        (1.0 - std::exp(-std::abs(probFloor) / srNoiseScale)) /
                        2.0;
        } else {
          probFloor = 0.5;
        }
      } else if (noiseDensity == SRDensityType::TRUNCATED_LAPLACE) {
        probFloor -= srNoiseOffset;
        if (probFloor != 0) {
          probFloor =
              0.5 + (std::abs(probFloor) / probFloor) *
                        (1.0 - std::exp(-std::abs(probFloor) / srNoiseScale)) /
                        2.0;
        } else {
          probFloor = 0.5;
        }

        probMin = 0.5;
        if (srNoiseMin != 0.0) {
          probMin += (std::abs(srNoiseMin - srNoiseOffset) /
                      (srNoiseMin - srNoiseOffset)) *
                     (1.0 - std::exp(-std::abs(srNoiseMin - srNoiseOffset) /
                                     srNoiseScale)) /
                     2.0;
        }

        probMax = 0.5;
        if (srNoiseMax != 0.0) {
          probMax += (std::abs(srNoiseMax - srNoiseOffset) /
                      (srNoiseMax - srNoiseOffset)) *
                     (1.0 - std::exp(-std::abs(srNoiseMax - srNoiseOffset) /
                                     srNoiseScale)) /
                     2.0;
        }
      } else if (noiseDensity == SRDensityType::LOGISTIC) {
        probFloor =
            1.0 / (1.0 + std::exp(-(probFloor - srNoiseOffset) / srNoiseScale));
      } else if (noiseDensity == SRDensityType::TRUNCATED_LOGISTIC) {
        probFloor =
            1.0 / (1.0 + std::exp(-(probFloor - srNoiseOffset) / srNoiseScale));
        probMin =
            1.0 /
            (1.0 + std::exp(-(srNoiseMin - srNoiseOffset) / srNoiseScale));
        probMax =
            1.0 /
            (1.0 + std::exp(-(srNoiseMax - srNoiseOffset) / srNoiseScale));
      } else if (noiseDensity == SRDensityType::LOGIT_NORMAL) {
        probFloor = std::log(probFloor / (1.0 - probFloor));
        probFloor = 0.5 * (1.0 + std::erf((probFloor - srNoiseOffset) /
                                          (std::sqrt(2.0) * srNoiseScale)));
      } else if (noiseDensity == SRDensityType::TRUNCATED_LOGIT_NORMAL) {
        probFloor = std::log(probFloor / (1.0 - probFloor));
        probFloor = (1.0 + std::erf((probFloor - srNoiseOffset) /
                                    (std::sqrt(2.0) * srNoiseScale)));

        probMin = std::log(srNoiseMin / (1.0 - srNoiseMin));
        probMin = (1.0 + std::erf((probMin - srNoiseOffset) /
                                  (std::sqrt(2.0) * srNoiseScale)));

        probMax = std::log(srNoiseMax / (1.0 - srNoiseMax));
        probMax = (1.0 + std::erf((probMax - srNoiseOffset) /
                                  (std::sqrt(2.0) * srNoiseScale)));
      }
      probFloor = (probFloor - probMin) / (probMax - probMin);
    }
  }
  probFloor = ((int)(probFloor * ((float)(1 << numberSRBits)))) /
              ((float)(1 << numberSRBits));

  bool pass = (std::abs(probFloor - floorRate) <= sr_tolerance);
  if (pass) {
    std::cout << "Test Passed\n";
  }
  return !pass;
}


#define BOOST_TEST_MODULE "test_bond_length_interaction"

#ifdef BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif

#include <test/util/utility.hpp>

#include <mjolnir/core/BoundaryCondition.hpp>
#include <mjolnir/core/SimulatorTraits.hpp>
#include <mjolnir/forcefield/MultipleBasin/MultipleBasinForceField.hpp>
#include <mjolnir/forcefield/MultipleBasin/MultipleBasin2BasinUnit.hpp>
#include <mjolnir/forcefield/MultipleBasin/MultipleBasin3BasinUnit.hpp>
#include <mjolnir/forcefield/local/BondLengthInteraction.hpp>
#include <mjolnir/forcefield/local/HarmonicPotential.hpp>
#include <mjolnir/util/make_unique.hpp>

#include <random>

BOOST_AUTO_TEST_CASE(MultipleBasin_2Basin_numerical_difference)
{
    namespace test = mjolnir::test;

    mjolnir::LoggerManager::set_default_logger("test_multiple_basin_forcefield.log");

    using traits_type      = mjolnir::SimulatorTraits<double, mjolnir::UnlimitedBoundary>;
    using real_type        = traits_type::real_type;
    using coord_type       = traits_type::coordinate_type;
    using boundary_type    = traits_type::boundary_type;
    using system_type      = mjolnir::System<traits_type>;
    using potential_type   = mjolnir::HarmonicPotential<real_type>;
    using interaction_type = mjolnir::BondLengthInteraction<traits_type, potential_type>;

    const real_type k(10.0);
    const real_type native1(1.5);
    const real_type native2(2.0);

    mjolnir::LocalForceField<traits_type>    loc1, loc2, locc;
    mjolnir::GlobalForceField<traits_type>   glo1, glo2, gloc;
    mjolnir::ExternalForceField<traits_type> ext1, ext2, extc;

    std::vector<std::pair<std::array<std::size_t, 2>, potential_type>> param1, param2;
    param1.emplace_back(std::array<std::size_t, 2>{{0,1}}, potential_type(k, native1));
    param2.emplace_back(std::array<std::size_t, 2>{{0,1}}, potential_type(k, native2));

    loc1.emplace(mjolnir::make_unique<interaction_type>("none", std::move(param1)));
    loc2.emplace(mjolnir::make_unique<interaction_type>("none", std::move(param2)));

    std::unique_ptr<mjolnir::MultipleBasinUnitBase<traits_type>> unit1 =
        mjolnir::make_unique<mjolnir::MultipleBasin2BasinUnit<traits_type>>(
            -10.0, "short", "long", 0.0, 0.0,
            std::make_tuple(std::move(loc1), std::move(glo1), std::move(ext1)),
            std::make_tuple(std::move(loc2), std::move(glo2), std::move(ext2)),
            0.0, 0.0);

    std::vector<std::unique_ptr<mjolnir::MultipleBasinUnitBase<traits_type>>> units;
    units.push_back(std::move(unit1));

    std::mt19937 mt(123456789);

    mjolnir::MultipleBasinForceField<traits_type> forcefield(
            std::make_tuple(std::move(locc), std::move(gloc), std::move(extc)),
            std::move(units));

    for(std::size_t i = 0; i < 1000; ++i)
    {
        system_type sys(2, boundary_type{});

        test::clear_everything(sys);

        sys.position(0) = coord_type( 0.0, 0.0, 0.0);
        sys.position(1) = coord_type( 1.0, 1.0, 1.0);

        test::apply_random_rotation(sys, mt);
        test::apply_random_perturbation(sys, mt, 0.01);

        forcefield.initialize(sys); // don't forget this

        constexpr real_type tol = 1e-4;
        constexpr real_type dr  = 1e-5;

        test::check_force(sys, forcefield, tol, dr);
        test::check_virial(sys, forcefield, tol);
    }
}

BOOST_AUTO_TEST_CASE(MultipleBasin_2Basin_numerical_difference_bias)
{
    namespace test = mjolnir::test;

    mjolnir::LoggerManager::set_default_logger("test_multiple_basin_forcefield.log");

    using traits_type      = mjolnir::SimulatorTraits<double, mjolnir::UnlimitedBoundary>;
    using real_type        = traits_type::real_type;
    using coord_type       = traits_type::coordinate_type;
    using boundary_type    = traits_type::boundary_type;
    using system_type      = mjolnir::System<traits_type>;
    using potential_type   = mjolnir::HarmonicPotential<real_type>;
    using interaction_type = mjolnir::BondLengthInteraction<traits_type, potential_type>;

    const real_type k(10.0);
    const real_type native1(1.5);
    const real_type native2(2.0);

    mjolnir::LocalForceField<traits_type>    loc1, loc2, locc;
    mjolnir::GlobalForceField<traits_type>   glo1, glo2, gloc;
    mjolnir::ExternalForceField<traits_type> ext1, ext2, extc;

    std::vector<std::pair<std::array<std::size_t, 2>, potential_type>> param1, param2;
    param1.emplace_back(std::array<std::size_t, 2>{{0,1}}, potential_type(k, native1));
    param2.emplace_back(std::array<std::size_t, 2>{{0,1}}, potential_type(k, native2));

    loc1.emplace(mjolnir::make_unique<interaction_type>("none", std::move(param1)));
    loc2.emplace(mjolnir::make_unique<interaction_type>("none", std::move(param2)));

    std::unique_ptr<mjolnir::MultipleBasinUnitBase<traits_type>> unit1 =
        mjolnir::make_unique<mjolnir::MultipleBasin2BasinUnit<traits_type>>(
            -10.0, "short", "long", 0.0, 0.0,
            std::make_tuple(std::move(loc1), std::move(glo1), std::move(ext1)),
            std::make_tuple(std::move(loc2), std::move(glo2), std::move(ext2)),
            /*k_chi = */1.0, /*chi_0 = */ 0.5);

    std::vector<std::unique_ptr<mjolnir::MultipleBasinUnitBase<traits_type>>> units;
    units.push_back(std::move(unit1));

    std::mt19937 mt(123456789);

    mjolnir::MultipleBasinForceField<traits_type> forcefield(
            std::make_tuple(std::move(locc), std::move(gloc), std::move(extc)),
            std::move(units));

    for(const real_type len : {1.5, 1.75, 2.0})
    {
        for(std::size_t i = 0; i < 1000; ++i)
        {
            system_type sys(2, boundary_type{});

            test::clear_everything(sys);

            sys.position(0) = coord_type( 0.0, 0.0, 0.0);
            sys.position(1) = coord_type( len, 0.0, 0.0);

            test::apply_random_rotation(sys, mt);
            test::apply_random_perturbation(sys, mt, 0.01);

            forcefield.initialize(sys); // don't forget this

            constexpr real_type tol = 1e-4;
            constexpr real_type dr  = 1e-5;

            // Here we need to take sum of `chi_bias` and `V_MB`.
            // So we don't use test::check_force.

            const auto init = sys; // save current system state as the initial state
            for(std::size_t idx=0; idx<sys.size(); ++idx)
            {
                {
                    // ----------------------------------------------------------------
                    // reset positions
                    sys = init;

                    // calc U(x-dx) and U(x+dx)
                    auto E0 = forcefield.calc_force_and_energy(sys);
                    E0 += sys.attribute("chi_bias");

                    // calc U(x+dx)
                    mjolnir::math::X(sys.position(idx)) += 2 * dr;
                    auto E1 = forcefield.calc_force_and_energy(sys);
                    E1 += sys.attribute("chi_bias");

                    // take the center
                    sys = init;
                    mjolnir::math::X(sys.position(idx)) += dr;

                    // calc F(x)
                    sys.preprocess_forces();
                    forcefield.calc_force(sys);
                    sys.postprocess_forces();

                    // central difference
                    const auto dE = (E1 - E0) * 0.5;

                    BOOST_TEST(-dE == dr * mjolnir::math::X(sys.force(idx)),
                               boost::test_tools::tolerance(tol));
                }
                {
                    // ----------------------------------------------------------------
                    // reset positions
                    sys = init;

                    // calc U(x-dx) and U(x+dx)
                    auto E0 = forcefield.calc_force_and_energy(sys);
                    E0 += sys.attribute("chi_bias");

                    // calc U(x+dx)
                    mjolnir::math::Y(sys.position(idx)) += 2 * dr;
                    auto E1 = forcefield.calc_force_and_energy(sys);
                    E1 += sys.attribute("chi_bias");

                    // take the center
                    sys = init;
                    mjolnir::math::Y(sys.position(idx)) += dr;

                    // calc F(x)
                    sys.preprocess_forces();
                    forcefield.calc_force(sys);
                    sys.postprocess_forces();

                    // central difference
                    const auto dE = (E1 - E0) * 0.5;

                    BOOST_TEST(-dE == dr * mjolnir::math::Y(sys.force(idx)),
                               boost::test_tools::tolerance(tol));
                }
                {
                    // ----------------------------------------------------------------
                    // reset positions
                    sys = init;

                    // calc U(x-dx) and U(x+dx)
                    auto E0 = forcefield.calc_force_and_energy(sys);
                    E0 += sys.attribute("chi_bias");

                    // calc U(x+dx)
                    mjolnir::math::Z(sys.position(idx)) += 2 * dr;
                    auto E1 = forcefield.calc_force_and_energy(sys);
                    E1 += sys.attribute("chi_bias");

                    // take the center
                    sys = init;
                    mjolnir::math::Z(sys.position(idx)) += dr;

                    // calc F(x)
                    sys.preprocess_forces();
                    forcefield.calc_force(sys);
                    sys.postprocess_forces();

                    // central difference
                    const auto dE = (E1 - E0) * 0.5;

                    BOOST_TEST(-dE == dr * mjolnir::math::Z(sys.force(idx)),
                               boost::test_tools::tolerance(tol));
                }
            }
        }
    }
}

BOOST_AUTO_TEST_CASE(MultipleBasin_3Basin_numerical_difference)
{
    namespace test = mjolnir::test;

    mjolnir::LoggerManager::set_default_logger("test_multiple_basin_forcefield.log");

    using traits_type      = mjolnir::SimulatorTraits<double, mjolnir::UnlimitedBoundary>;
    using real_type        = traits_type::real_type;
    using coord_type       = traits_type::coordinate_type;
    using boundary_type    = traits_type::boundary_type;
    using system_type      = mjolnir::System<traits_type>;
    using potential_type   = mjolnir::HarmonicPotential<real_type>;
    using interaction_type = mjolnir::BondLengthInteraction<traits_type, potential_type>;

    const real_type k(10.0);
    const real_type native1(1.6);
    const real_type native2(1.8);
    const real_type native3(1.9);

    mjolnir::LocalForceField<traits_type>    loc1, loc2, loc3, locc;
    mjolnir::GlobalForceField<traits_type>   glo1, glo2, glo3, gloc;
    mjolnir::ExternalForceField<traits_type> ext1, ext2, ext3, extc;

    std::vector<std::pair<std::array<std::size_t, 2>, potential_type>> param1, param2, param3;
    param1.emplace_back(std::array<std::size_t, 2>{{0,1}}, potential_type(k, native1));
    param2.emplace_back(std::array<std::size_t, 2>{{0,1}}, potential_type(k, native2));
    param3.emplace_back(std::array<std::size_t, 2>{{0,1}}, potential_type(k, native3));

    loc1.emplace(mjolnir::make_unique<interaction_type>("none", std::move(param1)));
    loc2.emplace(mjolnir::make_unique<interaction_type>("none", std::move(param2)));
    loc3.emplace(mjolnir::make_unique<interaction_type>("none", std::move(param3)));

    std::unique_ptr<mjolnir::MultipleBasinUnitBase<traits_type>> unit1 =
        mjolnir::make_unique<mjolnir::MultipleBasin3BasinUnit<traits_type>>(
            "short", "middle", "long", -10.0, -10.0, -10.0, 0.0, 0.0, 0.0,
            std::make_tuple(std::move(loc1), std::move(glo1), std::move(ext1)),
            std::make_tuple(std::move(loc2), std::move(glo2), std::move(ext2)),
            std::make_tuple(std::move(loc3), std::move(glo3), std::move(ext3)));

    std::vector<std::unique_ptr<mjolnir::MultipleBasinUnitBase<traits_type>>> units;
    units.push_back(std::move(unit1));

    std::mt19937 mt(123456789);
    std::uniform_real_distribution<real_type> uni(-1.0, 1.0);

    mjolnir::MultipleBasinForceField<traits_type> forcefield(
            std::make_tuple(std::move(locc), std::move(gloc), std::move(extc)),
            std::move(units));

    for(std::size_t i = 0; i < 1000; ++i)
    {
        system_type sys(2, boundary_type{});

        test::clear_everything(sys);

        sys.position(0) = coord_type( 0.0, 0.0, 0.0);
        sys.position(1) = coord_type( 1.0, 1.0, 1.0);

        test::apply_random_rotation(sys, mt);
        test::apply_random_perturbation(sys, mt, 0.01);

        forcefield.initialize(sys); // don't forget this

        constexpr real_type tol = 1e-4;
        constexpr real_type dr  = 1e-5;
        test::check_force(sys, forcefield, tol, dr);
        test::check_virial(sys, forcefield, tol);
    }
}


#include "graphwidget.h"
#include "edge.h"
#include "node.h"

#include <math.h>
#include <random>
#include <QKeyEvent>
#include <QSizePolicy>

#include <boost/graph/fruchterman_reingold.hpp>
#include <boost/graph/random_layout.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topology.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/random/linear_congruential.hpp>
#include <boost/progress.hpp>
#include <boost/shared_ptr.hpp>

namespace uipf{
namespace gui{

using namespace boost;

typedef boost::rectangle_topology<> topology_type;
typedef topology_type::point_type point_type;

typedef adjacency_list<listS, vecS, undirectedS,
                       property<vertex_name_t, std::string> > Graph;

typedef graph_traits<Graph>::vertex_descriptor Vertex;

typedef std::map<std::string, Vertex> NameToVertex;

Vertex get_vertex(const std::string& name, Graph& g, NameToVertex& names)
{
  NameToVertex::iterator i = names.find(name);
  if (i == names.end())
    i = names.insert(std::make_pair(name, add_vertex(name, g))).first;
  return i->second;
}

GraphWidget::GraphWidget(QWidget *parent)
    : QGraphicsView(parent),currentScale_(1.0f)
{
    QGraphicsScene *scene = new QGraphicsScene(this);
    scene->setItemIndexMethod(QGraphicsScene::NoIndex);
    //scene->setSceneRect(0, 0, 400, 400);
    setScene(scene);
    setCacheMode(CacheBackground);
    setViewportUpdateMode(BoundingRectViewportUpdate);
    setRenderHint(QPainter::Antialiasing);
    setTransformationAnchor(AnchorUnderMouse);
    scale(currentScale_,currentScale_);
    setMinimumSize(200, 400);
    setDragMode(ScrollHandDrag);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);

    //setInteractive(false);
    this->mapToScene( this->viewport()->geometry() );

}

void GraphWidget::clearSelectionInGraphView()
{
	for (auto pair : nodes_)
	{
		auto node = pair.second;
		node->unselect();
	}
}

void GraphWidget::selectNodesInGraphView(const std::vector<std::string>& vcNodeNames,uipf::gui::GraphViewSelectionType eType,bool bUnselectOthers)
{
	for (auto pair : nodes_)
	{
		auto node = pair.second;
		for (auto name : vcNodeNames)
		{
			if (node->processingStep_.name == name)
				node->select(eType);
			else if (bUnselectOthers)
				node->unselect();
		}

	}
}

void GraphWidget::selectNodeByName(const QString name, bool bUnselectOthers)
{
	for (auto pair : nodes_)
	{
		auto node = pair.second;
		if (node->name_ == name)
			node->select(uipf::gui::GraphViewSelectionType::CURRENT);
		else if (bUnselectOthers)
			node->unselect();
	}
}

void GraphWidget::triggerNodeSelected(const uipf::gui::Node* node)
{
	emit nodeSelected(node);
}

//takes a configuration and outputs a graph layout
void GraphWidget::renderConfig(uipf::ProcessingChain& config)
{
	QGraphicsScene* scene = this->scene();
	scene->clear();
	nodes_.clear();

	using namespace std;
	using namespace uipf;
	map<string, ProcessingStep> chain = config.getProcessingSteps();

	double width = this->width();//scene->width();
	double height = this->height();//scene->height();

	srand (0);//same seed to get invariant positions

	//create all nodes first
	for (auto it = chain.begin(); it!=chain.end(); ++it)
	{
		  Node *node = new Node(this,QString(it->first.c_str()),it->second);
		  node->setPos(rand()%static_cast<int>(width),rand()%static_cast<int>(height));//initial random position to prevent clashes

		  scene->addItem(node);
		  nodes_.insert(std::pair<string,Node*>(it->first,node));
	}

	Graph g;
	NameToVertex names;

	//connect them in second pass:
	for (auto it = chain.begin(); it!=chain.end(); ++it)
	{
		auto inputs = it->second.inputs;
		std::string thisname = it->first;
		for (auto inputIt = inputs.begin();inputIt!=inputs.end();++inputIt)
		{
			std::string othername = inputIt->second.sourceStep;
			if (nodes_.count(othername) != 0) { // only create edge if referenced node exists
				auto edge =new Edge(nodes_[it->first], nodes_[othername]);
				scene->addItem(edge);
				//add to calculation graph
				add_edge(get_vertex(thisname, g, names), get_vertex(othername, g, names), g);
			}
		}
	}

	//calculate graph layout with boosts fruchterman_reingold algo

	typedef std::vector<point_type> PositionVec;
	PositionVec position_vec(num_vertices(g));
	typedef iterator_property_map<PositionVec::iterator,
			property_map<Graph, vertex_index_t>::type>
	PositionMap;
	PositionMap position(position_vec.begin(), get(vertex_index, g));

	boost::minstd_rand gen;
	topology_type topo(gen, -width/2, -height/2, width/2, height/2);
	random_graph_layout(g, position, topo);
	fruchterman_reingold_force_directed_layout	(g, position, topo);

	//apply calculated positions to nodes
	graph_traits<Graph>::vertex_iterator vi, vi_end;
	for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) {
		std::string strName = get(vertex_name, g, *vi) ;

		nodes_[strName]->setPos(position[*vi][0],  position[*vi][1]);
	}

	QGraphicsView::fitInView( scene->itemsBoundingRect(), Qt::KeepAspectRatio );

}

void GraphWidget::wheelEvent(QWheelEvent *event)
{
    scaleView(pow((double)2, event->delta() / 240.0));
}

void GraphWidget::scaleView(qreal scaleFactor)
{
    qreal factor = transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width();
    if (factor < 0.07 || factor > 100)
        return;

    scale(scaleFactor, scaleFactor);

    currentScale_ = scaleFactor;
}

}//gui
}//uipf


/*
 * Software License Agreement (BSD License)
 *
 *  Point Cloud Library (PCL) - www.pointclouds.org
 *  Copyright (c) 2012-, Open Perception, 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:
 *
 *   * 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.
 *   * Neither the name of Willow Garage, Inc. 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 OWNER 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 PCL_SEGMENTATION_IMPL_RANDOM_WALKER_HPP
#define PCL_SEGMENTATION_IMPL_RANDOM_WALKER_HPP

#include <boost/bimap.hpp>
#include <boost/graph/subgraph.hpp>

#include <Eigen/Sparse>

namespace pcl
{

  namespace segmentation
  {

    namespace detail
    {

      /** \brief Multilabel graph segmentation using random walks.
        *
        * This is an implementation of the algorithm described in "Random Walks
        * for Image Segmentation" by Leo Grady.
        *
        * See the documentation of the randomWalker() function for details.
        *
        * \author Sergey Alexandrov
        * \ingroup segmentation
        */
      template <class Graph, class EdgeWeightMap, class VertexColorMap>
      class RandomWalker
      {

        public:

          typedef typename boost::property_traits<VertexColorMap>::value_type Color;
          typedef typename boost::property_traits<EdgeWeightMap>::value_type Weight;
          typedef boost::graph_traits<Graph> GraphTraits;
          typedef typename GraphTraits::edge_descriptor EdgeDescriptor;
          typedef typename GraphTraits::vertex_descriptor VertexDescriptor;
          typedef typename GraphTraits::edge_iterator EdgeIterator;
          typedef typename GraphTraits::out_edge_iterator OutEdgeIterator;
          typedef typename GraphTraits::vertex_iterator VertexIterator;
          typedef typename boost::property_map<Graph, boost::vertex_index_t>::type VertexIndexMap;
          typedef boost::iterator_property_map<typename std::vector<Weight>::iterator, VertexIndexMap> VertexDegreeMap;
          typedef Eigen::SparseMatrix<Weight> SparseMatrix;
          typedef Eigen::Matrix<Weight, Eigen::Dynamic, Eigen::Dynamic> Matrix;
          typedef Eigen::Matrix<Weight, Eigen::Dynamic, 1> Vector;

          RandomWalker (Graph& g, EdgeWeightMap weights, VertexColorMap colors)
          : g_ (g)
          , weight_map_ (weights)
          , color_map_ (colors)
          , index_map_ (boost::get (boost::vertex_index, g_))
          , degree_storage_ (boost::num_vertices (g_), 0)
          , degree_map_ (boost::make_iterator_property_map (degree_storage_.begin (), index_map_))
          {
          }

          bool
          segment ()
          {
            computeVertexDegrees ();
            buildLinearSystem ();
            return solveLinearSystem ();
          }

          void
          computeVertexDegrees ()
          {
            using namespace boost;
            EdgeIterator ei, e_end;
            for (tie (ei, e_end) = edges (g_); ei != e_end; ++ei)
            {
              Weight w = weight_map_[*ei];
              degree_map_[source (*ei, g_)] += w;
              degree_map_[target (*ei, g_)] += w;
            }
          }

          void
          buildLinearSystem ()
          {
            using namespace boost;

            typedef Eigen::Triplet<float> T;
            typedef std::vector<T> Triplets;
            Triplets L_triplets;
            Triplets B_triplets;

            VertexIterator vi, v_end;
            for (tie (vi, v_end) = vertices (g_); vi != v_end; ++vi)
            {
              // If this is a labeled vertex add it to the seeds list and register its color
              if (color_map_[*vi])
              {
                seeds_.push_back (*vi);
                colors_.insert (color_map_[*vi]);
              }
              // Skip seeds and vertices with zero connectivity
              if (color_map_[*vi] || std::fabs (degree_map_[*vi]) < std::numeric_limits<Weight>::epsilon ())
                continue;
              // Create a row in L matrix for the vertex
              size_t current_row = insertInBimap (L_vertex_bimap, *vi);
              // Add diagonal degree entry for the vertex
              L_triplets.push_back (T (current_row, current_row, degree_map_[*vi]));
              // Iterate over incident vertices and add entries on corresponding columns of L or B
              OutEdgeIterator ei, e_end;
              for (tie (ei, e_end) = out_edges (*vi, g_); ei != e_end; ++ei)
              {
                Weight w = weight_map_[*ei];
                VertexDescriptor tgt = target (*ei, g_);
                Color color = color_map_[tgt];
                if (color)
                {
                  // This is a seed and will go to B matrix
                  size_t column;
                  if (B_color_bimap.right.count (color) == 0)
                  {
                    // This is the first time we encountered this color, create a new column in B
                    column = insertInBimap (B_color_bimap, color);
                  }
                  else
                  {
                    column = B_color_bimap.right.at (color);
                  }
                  B_triplets.push_back (T (current_row, column, w));
                }
                else
                {
                  // This is a non-seed and will go to L matrix,
                  // but only if a row for this vertex already exists
                  if (L_vertex_bimap.right.count (tgt) && L_vertex_bimap.right.at (tgt) != current_row)
                  {
                    L_triplets.push_back (T (current_row, L_vertex_bimap.right.at (tgt), -w));
                  }
                }
              }
            }

            size_t num_equations = L_vertex_bimap.size ();
            size_t num_colors = B_color_bimap.size ();
            L.resize (num_equations, num_equations);
            B.resize (num_equations, num_colors);
            if (L_triplets.size ())
              L.setFromTriplets(L_triplets.begin(), L_triplets.end());
            if (B_triplets.size ())
              B.setFromTriplets(B_triplets.begin(), B_triplets.end());
          }

          bool solveLinearSystem()
          {
            X.resize (L.rows (), B.cols ());

            // Nothing to solve
            if (L.rows () == 0 || B.cols () == 0)
              return true;

            Eigen::SimplicialCholesky<SparseMatrix, Eigen::Lower> cg;
            cg.compute (L);
            bool succeeded = true;
            for (int i = 0; i < B.cols (); ++i)
            {
              Vector b = B.col (i);
              X.col (i) = cg.solve (b);
              if (cg.info () != Eigen::Success)
                succeeded = false;
            }

            assignColors ();
            return succeeded;
          }

          void
          assignColors ()
          {
            using namespace boost;
            if (X.cols ())
              for (int i = 0; i < X.rows (); ++i)
              {
                size_t max_column;
                X.row (i).maxCoeff (&max_column);
                VertexDescriptor vertex = L_vertex_bimap.left.at (i);
                Color color = B_color_bimap.left.at (max_column);
                color_map_[vertex] = color;
              }
          }

          void
          getPotentials (Matrix& potentials, std::map<Color, size_t>& color_to_column_map)
          {
            using namespace boost;
            potentials = Matrix::Zero (num_vertices (g_), colors_.size ());
            // Copy over rows from X
            for (int i = 0; i < X.rows (); ++i)
              potentials.row (L_vertex_bimap.left.at (i)).head (X.cols ()) = X.row (i);
            // In rows that correspond to seeds put ones in proper columns
            for (size_t i = 0; i < seeds_.size (); ++i)
            {
              VertexDescriptor v = seeds_[i];
              insertInBimap (B_color_bimap, color_map_[v]);
              potentials (seeds_[i], B_color_bimap.right.at (color_map_[seeds_[i]])) = 1;
            }
            // Fill in a map that associates colors with columns in potentials matrix
            color_to_column_map.clear ();
            for (int i = 0; i < potentials.cols (); ++i)
              color_to_column_map[B_color_bimap.left.at (i)] = i;
          }

          template <typename T> static inline size_t
          insertInBimap (boost::bimap<size_t, T>& bimap, T value)
          {
            if (bimap.right.count (value) != 0)
            {
              return bimap.right.at (value);
            }
            else
            {
              size_t s = bimap.size ();
              bimap.insert (typename boost::bimap<size_t, T>::value_type (s, value));
              return s;
            }
          }

          Graph& g_;
          EdgeWeightMap weight_map_;
          VertexColorMap color_map_;
          VertexIndexMap index_map_;

          std::vector<VertexDescriptor> seeds_;
          std::set<Color> colors_;

          std::vector<Weight> degree_storage_;
          VertexDegreeMap degree_map_;
          SparseMatrix L;
          SparseMatrix B;
          Matrix X;

          // Map vertex identifiers to the rows/columns of L and vice versa
          boost::bimap<size_t, VertexDescriptor> L_vertex_bimap;
          // Map colors to the columns of B and vice versa
          boost::bimap<size_t, Color> B_color_bimap;

      };

    }

    template <class Graph> bool
    randomWalker (Graph& graph)
    {
      return randomWalker (graph,
                           boost::get (boost::edge_weight, graph),
                           boost::get (boost::vertex_color, graph));
    }

    template <class Graph, class EdgeWeightMap, class VertexColorMap> bool
    randomWalker (Graph& graph,
                  EdgeWeightMap weights,
                  VertexColorMap colors)
    {
      using namespace boost;

      typedef typename graph_traits<Graph>::edge_descriptor EdgeDescriptor;
      typedef typename graph_traits<Graph>::vertex_descriptor VertexDescriptor;

      BOOST_CONCEPT_ASSERT ((VertexListGraphConcept<Graph>));                                 // to have vertices(), num_vertices()
      BOOST_CONCEPT_ASSERT ((EdgeListGraphConcept<Graph>));                                   // to have edges()
      BOOST_CONCEPT_ASSERT ((IncidenceGraphConcept<Graph>));                                  // to have source(), target() and out_edges()
      BOOST_CONCEPT_ASSERT ((ReadablePropertyMapConcept<EdgeWeightMap, EdgeDescriptor>));     // read weight-values from edges
      BOOST_CONCEPT_ASSERT ((ReadWritePropertyMapConcept<VertexColorMap, VertexDescriptor>)); // read and write color-values from vertices

      ::pcl::segmentation::detail::RandomWalker
      <
        Graph,
        EdgeWeightMap,
        VertexColorMap
      >
      rw (graph, weights, colors);
      return rw.segment ();
    }

    template <class Graph, class EdgeWeightMap, class VertexColorMap> bool
    randomWalker (Graph& graph,
                  EdgeWeightMap weights,
                  VertexColorMap colors,
                  Eigen::Matrix<typename boost::property_traits<EdgeWeightMap>::value_type, Eigen::Dynamic, Eigen::Dynamic>& potentials,
                  std::map<typename boost::property_traits<VertexColorMap>::value_type, size_t>& colors_to_columns_map)
    {
      using namespace boost;

      typedef typename graph_traits<Graph>::edge_descriptor EdgeDescriptor;
      typedef typename graph_traits<Graph>::vertex_descriptor VertexDescriptor;

      BOOST_CONCEPT_ASSERT ((VertexListGraphConcept<Graph>));                                 // to have vertices(), num_vertices()
      BOOST_CONCEPT_ASSERT ((EdgeListGraphConcept<Graph>));                                   // to have edges()
      BOOST_CONCEPT_ASSERT ((IncidenceGraphConcept<Graph>));                                  // to have source(), target() and out_edges()
      BOOST_CONCEPT_ASSERT ((ReadablePropertyMapConcept<EdgeWeightMap, EdgeDescriptor>));     // read weight-values from edges
      BOOST_CONCEPT_ASSERT ((ReadWritePropertyMapConcept<VertexColorMap, VertexDescriptor>)); // read and write color-values from vertices

      ::pcl::segmentation::detail::RandomWalker
      <
        Graph,
        EdgeWeightMap,
        VertexColorMap
      >
      rw (graph, weights, colors);
      bool result = rw.segment ();
      rw.getPotentials (potentials, colors_to_columns_map);
      return result;
    }

  }

}

#endif /* PCL_SEGMENTATION_IMPL_RANDOM_WALKER_HPP */


#pragma once

#include <Eigen/Core>

#include <vector>

int ei_test_init_cuda();

double dot_cuda(const std::vector<Eigen::Vector3d>& v1, const std::vector<Eigen::Vector3d>& v2);


// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "main.h"

#include <numeric>
#include <Eigen/CXX11/Tensor>

using Eigen::Tensor;

template <int DataLayout>
static void test_trivial_reductions() {
  {
    Tensor<float, 0, DataLayout> tensor;
    tensor.setRandom();
    array<ptrdiff_t, 0> reduction_axis;

    Tensor<float, 0, DataLayout> result = tensor.sum(reduction_axis);
    VERIFY_IS_EQUAL(result(), tensor());
  }

  {
    Tensor<float, 1, DataLayout> tensor(7);
    tensor.setRandom();
    array<ptrdiff_t, 0> reduction_axis;

    Tensor<float, 1, DataLayout> result = tensor.sum(reduction_axis);
    VERIFY_IS_EQUAL(result.dimension(0), 7);
    for (int i = 0; i < 7; ++i) {
      VERIFY_IS_EQUAL(result(i), tensor(i));
    }
  }

  {
    Tensor<float, 2, DataLayout> tensor(2, 3);
    tensor.setRandom();
    array<ptrdiff_t, 0> reduction_axis;

    Tensor<float, 2, DataLayout> result = tensor.sum(reduction_axis);
    VERIFY_IS_EQUAL(result.dimension(0), 2);
    VERIFY_IS_EQUAL(result.dimension(1), 3);
    for (int i = 0; i < 2; ++i) {
      for (int j = 0; j < 3; ++j) {
        VERIFY_IS_EQUAL(result(i, j), tensor(i, j));
      }
    }
  }
}

template <int DataLayout>
static void test_simple_reductions() {
  Tensor<float, 4, DataLayout> tensor(2, 3, 5, 7);
  tensor.setRandom();
  array<ptrdiff_t, 2> reduction_axis2;
  reduction_axis2[0] = 1;
  reduction_axis2[1] = 3;

  Tensor<float, 2, DataLayout> result = tensor.sum(reduction_axis2);
  VERIFY_IS_EQUAL(result.dimension(0), 2);
  VERIFY_IS_EQUAL(result.dimension(1), 5);
  for (int i = 0; i < 2; ++i) {
    for (int j = 0; j < 5; ++j) {
      float sum = 0.0f;
      for (int k = 0; k < 3; ++k) {
        for (int l = 0; l < 7; ++l) {
          sum += tensor(i, k, j, l);
        }
      }
      VERIFY_IS_APPROX(result(i, j), sum);
    }
  }

  {
    Tensor<float, 0, DataLayout> sum1 = tensor.sum();
    VERIFY_IS_EQUAL(sum1.rank(), 0);

    array<ptrdiff_t, 4> reduction_axis4;
    reduction_axis4[0] = 0;
    reduction_axis4[1] = 1;
    reduction_axis4[2] = 2;
    reduction_axis4[3] = 3;
    Tensor<float, 0, DataLayout> sum2 = tensor.sum(reduction_axis4);
    VERIFY_IS_EQUAL(sum2.rank(), 0);

    VERIFY_IS_APPROX(sum1(), sum2());
  }

  reduction_axis2[0] = 0;
  reduction_axis2[1] = 2;
  result = tensor.prod(reduction_axis2);
  VERIFY_IS_EQUAL(result.dimension(0), 3);
  VERIFY_IS_EQUAL(result.dimension(1), 7);
  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 7; ++j) {
      float prod = 1.0f;
      for (int k = 0; k < 2; ++k) {
        for (int l = 0; l < 5; ++l) {
          prod *= tensor(k, i, l, j);
        }
      }
      VERIFY_IS_APPROX(result(i, j), prod);
    }
  }

  {
    Tensor<float, 0, DataLayout> prod1 = tensor.prod();
    VERIFY_IS_EQUAL(prod1.rank(), 0);

    array<ptrdiff_t, 4> reduction_axis4;
    reduction_axis4[0] = 0;
    reduction_axis4[1] = 1;
    reduction_axis4[2] = 2;
    reduction_axis4[3] = 3;
    Tensor<float, 0, DataLayout> prod2 = tensor.prod(reduction_axis4);
    VERIFY_IS_EQUAL(prod2.rank(), 0);

    VERIFY_IS_APPROX(prod1(), prod2());
  }

  reduction_axis2[0] = 0;
  reduction_axis2[1] = 2;
  result = tensor.maximum(reduction_axis2);
  VERIFY_IS_EQUAL(result.dimension(0), 3);
  VERIFY_IS_EQUAL(result.dimension(1), 7);
  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 7; ++j) {
      float max_val = std::numeric_limits<float>::lowest();
      for (int k = 0; k < 2; ++k) {
        for (int l = 0; l < 5; ++l) {
          max_val = (std::max)(max_val, tensor(k, i, l, j));
        }
      }
      VERIFY_IS_APPROX(result(i, j), max_val);
    }
  }

  {
    Tensor<float, 0, DataLayout> max1 = tensor.maximum();
    VERIFY_IS_EQUAL(max1.rank(), 0);

    array<ptrdiff_t, 4> reduction_axis4;
    reduction_axis4[0] = 0;
    reduction_axis4[1] = 1;
    reduction_axis4[2] = 2;
    reduction_axis4[3] = 3;
    Tensor<float, 0, DataLayout> max2 = tensor.maximum(reduction_axis4);
    VERIFY_IS_EQUAL(max2.rank(), 0);

    VERIFY_IS_APPROX(max1(), max2());
  }

  reduction_axis2[0] = 0;
  reduction_axis2[1] = 1;
  result = tensor.minimum(reduction_axis2);
  VERIFY_IS_EQUAL(result.dimension(0), 5);
  VERIFY_IS_EQUAL(result.dimension(1), 7);
  for (int i = 0; i < 5; ++i) {
    for (int j = 0; j < 7; ++j) {
      float min_val = (std::numeric_limits<float>::max)();
      for (int k = 0; k < 2; ++k) {
        for (int l = 0; l < 3; ++l) {
          min_val = (std::min)(min_val, tensor(k, l, i, j));
        }
      }
      VERIFY_IS_APPROX(result(i, j), min_val);
    }
  }

  {
    Tensor<float, 0, DataLayout> min1 = tensor.minimum();
    VERIFY_IS_EQUAL(min1.rank(), 0);

    array<ptrdiff_t, 4> reduction_axis4;
    reduction_axis4[0] = 0;
    reduction_axis4[1] = 1;
    reduction_axis4[2] = 2;
    reduction_axis4[3] = 3;
    Tensor<float, 0, DataLayout> min2 = tensor.minimum(reduction_axis4);
    VERIFY_IS_EQUAL(min2.rank(), 0);

    VERIFY_IS_APPROX(min1(), min2());
  }

  reduction_axis2[0] = 0;
  reduction_axis2[1] = 1;
  result = tensor.mean(reduction_axis2);
  VERIFY_IS_EQUAL(result.dimension(0), 5);
  VERIFY_IS_EQUAL(result.dimension(1), 7);
  for (int i = 0; i < 5; ++i) {
    for (int j = 0; j < 7; ++j) {
      float sum = 0.0f;
      int count = 0;
      for (int k = 0; k < 2; ++k) {
        for (int l = 0; l < 3; ++l) {
          sum += tensor(k, l, i, j);
          ++count;
        }
      }
      VERIFY_IS_APPROX(result(i, j), sum / count);
    }
  }

  {
    Tensor<float, 0, DataLayout> mean1 = tensor.mean();
    VERIFY_IS_EQUAL(mean1.rank(), 0);

    array<ptrdiff_t, 4> reduction_axis4;
    reduction_axis4[0] = 0;
    reduction_axis4[1] = 1;
    reduction_axis4[2] = 2;
    reduction_axis4[3] = 3;
    Tensor<float, 0, DataLayout> mean2 = tensor.mean(reduction_axis4);
    VERIFY_IS_EQUAL(mean2.rank(), 0);

    VERIFY_IS_APPROX(mean1(), mean2());
  }

  {
    Tensor<int, 1> ints(10);
    std::iota(ints.data(), ints.data() + ints.dimension(0), 0);

    TensorFixedSize<bool, Sizes<> > all;
    all = ints.all();
    VERIFY(!all());
    all = (ints >= ints.constant(0)).all();
    VERIFY(all());

    TensorFixedSize<bool, Sizes<> > any;
    any = (ints > ints.constant(10)).any();
    VERIFY(!any());
    any = (ints < ints.constant(1)).any();
    VERIFY(any());
  }
}

template <int DataLayout>
static void test_reductions_in_expr() {
  Tensor<float, 4, DataLayout> tensor(2, 3, 5, 7);
  tensor.setRandom();
  array<ptrdiff_t, 2> reduction_axis2;
  reduction_axis2[0] = 1;
  reduction_axis2[1] = 3;

  Tensor<float, 2, DataLayout> result(2, 5);
  result = result.constant(1.0f) - tensor.sum(reduction_axis2);
  VERIFY_IS_EQUAL(result.dimension(0), 2);
  VERIFY_IS_EQUAL(result.dimension(1), 5);
  for (int i = 0; i < 2; ++i) {
    for (int j = 0; j < 5; ++j) {
      float sum = 0.0f;
      for (int k = 0; k < 3; ++k) {
        for (int l = 0; l < 7; ++l) {
          sum += tensor(i, k, j, l);
        }
      }
      VERIFY_IS_APPROX(result(i, j), 1.0f - sum);
    }
  }
}

template <int DataLayout>
static void test_full_reductions() {
  Tensor<float, 2, DataLayout> tensor(2, 3);
  tensor.setRandom();
  array<ptrdiff_t, 2> reduction_axis;
  reduction_axis[0] = 0;
  reduction_axis[1] = 1;

  Tensor<float, 0, DataLayout> result = tensor.sum(reduction_axis);
  VERIFY_IS_EQUAL(result.rank(), 0);

  float sum = 0.0f;
  for (int i = 0; i < 2; ++i) {
    for (int j = 0; j < 3; ++j) {
      sum += tensor(i, j);
    }
  }
  VERIFY_IS_APPROX(result(0), sum);

  result = tensor.square().sum(reduction_axis).sqrt();
  VERIFY_IS_EQUAL(result.rank(), 0);

  sum = 0.0f;
  for (int i = 0; i < 2; ++i) {
    for (int j = 0; j < 3; ++j) {
      sum += tensor(i, j) * tensor(i, j);
    }
  }
  VERIFY_IS_APPROX(result(), sqrtf(sum));
}

struct UserReducer {
  static const bool PacketAccess = false;
  UserReducer(float offset) : offset_(offset) {}
  void reduce(const float val, float* accum) { *accum += val * val; }
  float initialize() const { return 0; }
  float finalize(const float accum) const { return 1.0f / (accum + offset_); }

 private:
  const float offset_;
};

template <int DataLayout>
static void test_user_defined_reductions() {
  Tensor<float, 2, DataLayout> tensor(5, 7);
  tensor.setRandom();
  array<ptrdiff_t, 1> reduction_axis;
  reduction_axis[0] = 1;

  UserReducer reducer(10.0f);
  Tensor<float, 1, DataLayout> result = tensor.reduce(reduction_axis, reducer);
  VERIFY_IS_EQUAL(result.dimension(0), 5);
  for (int i = 0; i < 5; ++i) {
    float expected = 10.0f;
    for (int j = 0; j < 7; ++j) {
      expected += tensor(i, j) * tensor(i, j);
    }
    expected = 1.0f / expected;
    VERIFY_IS_APPROX(result(i), expected);
  }
}

template <int DataLayout>
static void test_tensor_maps() {
  int inputs[2 * 3 * 5 * 7];
  TensorMap<Tensor<int, 4, DataLayout> > tensor_map(inputs, 2, 3, 5, 7);
  TensorMap<Tensor<const int, 4, DataLayout> > tensor_map_const(inputs, 2, 3, 5,
                                                                7);
  const TensorMap<Tensor<const int, 4, DataLayout> > tensor_map_const_const(
      inputs, 2, 3, 5, 7);

  tensor_map.setRandom();
  array<ptrdiff_t, 2> reduction_axis;
  reduction_axis[0] = 1;
  reduction_axis[1] = 3;

  Tensor<int, 2, DataLayout> result = tensor_map.sum(reduction_axis);
  Tensor<int, 2, DataLayout> result2 = tensor_map_const.sum(reduction_axis);
  Tensor<int, 2, DataLayout> result3 =
      tensor_map_const_const.sum(reduction_axis);

  for (int i = 0; i < 2; ++i) {
    for (int j = 0; j < 5; ++j) {
      int sum = 0;
      for (int k = 0; k < 3; ++k) {
        for (int l = 0; l < 7; ++l) {
          sum += tensor_map(i, k, j, l);
        }
      }
      VERIFY_IS_EQUAL(result(i, j), sum);
      VERIFY_IS_EQUAL(result2(i, j), sum);
      VERIFY_IS_EQUAL(result3(i, j), sum);
    }
  }
}

template <int DataLayout>
static void test_static_dims() {
  Tensor<float, 4, DataLayout> in(72, 53, 97, 113);
  Tensor<float, 2, DataLayout> out(72, 97);
  in.setRandom();

#if !EIGEN_HAS_CONSTEXPR
  array<int, 2> reduction_axis;
  reduction_axis[0] = 1;
  reduction_axis[1] = 3;
#else
  Eigen::IndexList<Eigen::type2index<1>, Eigen::type2index<3> > reduction_axis;
#endif

  out = in.maximum(reduction_axis);

  for (int i = 0; i < 72; ++i) {
    for (int j = 0; j < 97; ++j) {
      float expected = -1e10f;
      for (int k = 0; k < 53; ++k) {
        for (int l = 0; l < 113; ++l) {
          expected = (std::max)(expected, in(i, k, j, l));
        }
      }
      VERIFY_IS_APPROX(out(i, j), expected);
    }
  }
}

template <int DataLayout>
static void test_innermost_last_dims() {
  Tensor<float, 4, DataLayout> in(72, 53, 97, 113);
  Tensor<float, 2, DataLayout> out(97, 113);
  in.setRandom();

// Reduce on the innermost dimensions.
#if !EIGEN_HAS_CONSTEXPR
  array<int, 2> reduction_axis;
  reduction_axis[0] = 0;
  reduction_axis[1] = 1;
#else
  // This triggers the use of packets for ColMajor.
  Eigen::IndexList<Eigen::type2index<0>, Eigen::type2index<1> > reduction_axis;
#endif

  out = in.maximum(reduction_axis);

  for (int i = 0; i < 97; ++i) {
    for (int j = 0; j < 113; ++j) {
      float expected = -1e10f;
      for (int k = 0; k < 53; ++k) {
        for (int l = 0; l < 72; ++l) {
          expected = (std::max)(expected, in(l, k, i, j));
        }
      }
      VERIFY_IS_APPROX(out(i, j), expected);
    }
  }
}

template <int DataLayout>
static void test_innermost_first_dims() {
  Tensor<float, 4, DataLayout> in(72, 53, 97, 113);
  Tensor<float, 2, DataLayout> out(72, 53);
  in.setRandom();

// Reduce on the innermost dimensions.
#if !EIGEN_HAS_CONSTEXPR
  array<int, 2> reduction_axis;
  reduction_axis[0] = 2;
  reduction_axis[1] = 3;
#else
  // This triggers the use of packets for RowMajor.
  Eigen::IndexList<Eigen::type2index<2>, Eigen::type2index<3>> reduction_axis;
#endif

  out = in.maximum(reduction_axis);

  for (int i = 0; i < 72; ++i) {
    for (int j = 0; j < 53; ++j) {
      float expected = -1e10f;
      for (int k = 0; k < 97; ++k) {
        for (int l = 0; l < 113; ++l) {
          expected = (std::max)(expected, in(i, j, k, l));
        }
      }
      VERIFY_IS_APPROX(out(i, j), expected);
    }
  }
}

template <int DataLayout>
static void test_reduce_middle_dims() {
  Tensor<float, 4, DataLayout> in(72, 53, 97, 113);
  Tensor<float, 2, DataLayout> out(72, 53);
  in.setRandom();

// Reduce on the innermost dimensions.
#if !EIGEN_HAS_CONSTEXPR
  array<int, 2> reduction_axis;
  reduction_axis[0] = 1;
  reduction_axis[1] = 2;
#else
  // This triggers the use of packets for RowMajor.
  Eigen::IndexList<Eigen::type2index<1>, Eigen::type2index<2>> reduction_axis;
#endif

  out = in.maximum(reduction_axis);

  for (int i = 0; i < 72; ++i) {
    for (int j = 0; j < 113; ++j) {
      float expected = -1e10f;
      for (int k = 0; k < 53; ++k) {
        for (int l = 0; l < 97; ++l) {
          expected = (std::max)(expected, in(i, k, l, j));
        }
      }
      VERIFY_IS_APPROX(out(i, j), expected);
    }
  }
}

void test_cxx11_tensor_reduction() {
  CALL_SUBTEST(test_trivial_reductions<ColMajor>());
  CALL_SUBTEST(test_trivial_reductions<RowMajor>());
  CALL_SUBTEST(test_simple_reductions<ColMajor>());
  CALL_SUBTEST(test_simple_reductions<RowMajor>());
  CALL_SUBTEST(test_reductions_in_expr<ColMajor>());
  CALL_SUBTEST(test_reductions_in_expr<RowMajor>());
  CALL_SUBTEST(test_full_reductions<ColMajor>());
  CALL_SUBTEST(test_full_reductions<RowMajor>());
  CALL_SUBTEST(test_user_defined_reductions<ColMajor>());
  CALL_SUBTEST(test_user_defined_reductions<RowMajor>());
  CALL_SUBTEST(test_tensor_maps<ColMajor>());
  CALL_SUBTEST(test_tensor_maps<RowMajor>());
  CALL_SUBTEST(test_static_dims<ColMajor>());
  CALL_SUBTEST(test_static_dims<RowMajor>());
  CALL_SUBTEST(test_innermost_last_dims<ColMajor>());
  CALL_SUBTEST(test_innermost_last_dims<RowMajor>());
  CALL_SUBTEST(test_innermost_first_dims<ColMajor>());
  CALL_SUBTEST(test_innermost_first_dims<RowMajor>());
  CALL_SUBTEST(test_reduce_middle_dims<ColMajor>());
  CALL_SUBTEST(test_reduce_middle_dims<RowMajor>());
}


/*
    Copyright 2013 Adobe
    Distributed under the Boost Software License, Version 1.0.
    (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
/**************************************************************************************************/

#include <adobe/algorithm.hpp>
#include <adobe/algorithm/rotate.hpp>
#include <adobe/algorithm/reduce.hpp>
#include <adobe/algorithm/other_of.hpp>
#include <adobe/iterator/value_iterator.hpp>

#include <boost/next_prior.hpp>

#include <utility>
#include <vector>
#include <iostream>

/**************************************************************************************************/

namespace {

/**************************************************************************************************/

template <typename I> // I models ForwardIterator
struct combine_ranges : std::binary_function<std::pair<I, I>, std::pair<I, I>, std::pair<I, I>> {
    typedef std::pair<I, I> second_argument_type;
    typedef std::pair<I, I> first_argument_type;
    typedef std::pair<I, I> result_type;

    result_type operator()(const first_argument_type& x, const second_argument_type& y) const {
        std::cout << "merging ( ";
        std::copy(x.first, x.second, std::ostream_iterator<ADOBE_VALUE_TYPE(I)>(std::cout, " "));
        std::cout << ") and ( ";
        std::copy(y.first, y.second, std::ostream_iterator<ADOBE_VALUE_TYPE(I)>(std::cout, " "));
        std::cout << ") yields ( ";

        result_type result(make_pair(
            adobe::other_of(adobe::rotate(x.first, x.second, y.first), x.second), y.second));

        std::copy(result.first, result.second,
                  std::ostream_iterator<ADOBE_VALUE_TYPE(I)>(std::cout, " "));
        std::cout << ")" << std::endl;

        return result;
    }
};

/**************************************************************************************************/

template <typename T>
struct myplus : std::binary_function<T, T, T> {
    typedef T second_argument_type;
    typedef T first_argument_type;
    typedef T result_type;

    result_type operator()(const first_argument_type& x, const second_argument_type& y) const {
        result_type result(x + y);

        std::cout << x << " + " << y << " = " << result << std::endl;

        return result;
    }
};

/**************************************************************************************************/

} // namespace

/**************************************************************************************************/

namespace adobe {

/**************************************************************************************************/

template <typename T>
struct identity_element<myplus<T>> {
    typedef T result_type;

    result_type operator()() const { return T(0); }
};

/**************************************************************************************************/

} // namespace adobe

/**************************************************************************************************/

namespace {

/**************************************************************************************************/

template <typename I, // I models ForwardIterator
          typename P> // P models UnaryPredicate
struct partition_trivial : std::unary_function<I, std::pair<I, I>> {
    P p;

    partition_trivial(const P& x) : p(x) {}

    std::pair<I, I> operator()(I i) const {
        if (p(*i))
            return std::make_pair(i, i);
        else
            return std::make_pair(i, boost::next(i));
    }
};

/**************************************************************************************************/

template <typename I, // I models ForwardIterator
          typename P>
// P models UnaryPredicate
I stable_partition_inplace_iterative(I f, I l, P p) {
    typedef partition_trivial<I, P> fun_t;
    typedef typename adobe::value_iterator<I, fun_t> val_iter;

    fun_t fun(p);

    std::pair<I, I> z(l, l);

    combine_ranges<I> op;

    val_iter f1(f, fun);

    val_iter l1(l, fun);

    std::pair<I, I> result(adobe::reduce_balanced(f1, l1, op, z));

    return result.first;
}

/**************************************************************************************************/

template <typename T>
struct is_odd : std::unary_function<T, bool> {
    typedef bool result_type;

    bool operator()(const T& x) const { return x & 1; }
};

/**************************************************************************************************/

void test1() {
    std::vector<int> set(20);

    adobe::iota(set.begin(), set.end(), 1);

    adobe::copy(set, std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;

    stable_partition_inplace_iterative(set.begin(), set.end(), is_odd<int>());

    adobe::copy(set, std::ostream_iterator<int>(std::cout, " "));

    std::cout << std::endl;
}

/**************************************************************************************************/

void test2() {
    std::vector<double> set(20);

    adobe::iota(set.begin(), set.end(), 1);

    adobe::copy(set, std::ostream_iterator<double>(std::cout, " "));
    std::cout << std::endl;

    // just to test std::plus
    std::cout << adobe::reduce_balanced(set.begin(), set.end(), std::plus<double>()) << std::endl;

    // just to test std::multiplies
    std::cout << adobe::reduce_balanced(set.begin(), set.end(), std::multiplies<double>())
              << std::endl;

    // now a more verbose version
    std::cout << adobe::reduce_balanced(set.begin(), set.end(), myplus<double>()) << std::endl;
}

/**************************************************************************************************/

} // namespace

/**************************************************************************************************/

int main() {
    test1();

    test2();
}


/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2003 RiskMap srl
 Copyright (C) 2012 StatPro Italia srl

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 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 license for more details.
*/

#include "utilities.hpp"
#include <ql/math/solvers1d/brent.hpp>
#include <ql/math/solvers1d/bisection.hpp>
#include <ql/math/solvers1d/falseposition.hpp>
#include <ql/math/solvers1d/ridder.hpp>
#include <ql/math/solvers1d/secant.hpp>
#include <ql/math/solvers1d/newton.hpp>
#include <ql/math/solvers1d/newtonsafe.hpp>
#include <ql/math/solvers1d/finitedifferencenewtonsafe.hpp>

/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2003 RiskMap srl
 Copyright (C) 2012 StatPro Italia srl

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 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 license for more details.
*/

#ifndef quantlib_test_solvers_hpp
#define quantlib_test_solvers_hpp

#include <boost/test/unit_test.hpp>

/* remember to document new and/or updated tests in the Doxygen
   comment block of the corresponding class */

class Solver1DTest {
  public:
    static void testBrent();
    static void testBisection();
    static void testFalsePosition();
    static void testNewton();
    static void testNewtonSafe();
    static void testFiniteDifferenceNewtonSafe();
    static void testRidder();
    static void testSecant();
    static boost::unit_test_framework::test_suite* suite();
};

using namespace QuantLib;
using namespace boost::unit_test_framework;

namespace {

    class F1 {
      public:
        Real operator()(Real x) const { return x*x-1.0; }
        Real derivative(Real x) const { return 2.0*x; }
    };

    class F2 {
      public:
        Real operator()(Real x) const { return 1.0-x*x; }
        Real derivative(Real x) const { return -2.0*x; }
    };

    class F3 {
      public:
        Real operator()(Real x) const { return std::atan(x-1); }
        Real derivative(Real x) const { return 1.0 / (1.0+(x-1.0)*(x-1.0)); }
    };

    template <class S, class F>
    void test_not_bracketed(const S& solver, const std::string& name,
                            const F& f, Real guess) {
        Real accuracy[] = { 1.0e-4, 1.0e-6, 1.0e-8 };
        Real expected = 1.0;
        for (Size i=0; i<LENGTH(accuracy); i++) {
            Real root = solver.solve(f,accuracy[i],guess,0.1);
            if (std::fabs(root-expected) > accuracy[i]) {
                BOOST_FAIL(name << " solver (not bracketed):\n"
                           << "    expected:   " << expected << "\n"
                           << "    calculated: " << root << "\n"
                           << "    accuracy:   " << accuracy[i]);
            }
        }
    }

    template <class S, class F>
    void test_bracketed(const S& solver, const std::string& name,
                        const F& f, Real guess) {
        Real accuracy[] = { 1.0e-4, 1.0e-6, 1.0e-8 };
        Real expected = 1.0;
        for (Size i=0; i<LENGTH(accuracy); i++) {
            // guess on the left side of the root, increasing function
            Real root = solver.solve(f,accuracy[i],guess,0.0,2.0);
            if (std::fabs(root-expected) > accuracy[i]) {
                BOOST_FAIL(name << " solver (bracketed):\n"
                           << "    expected:   " << expected << "\n"
                           << "    calculated: " << root << "\n"
                           << "    accuracy:   " << accuracy[i]);
            }
        }
    }

    class Probe {
      public:
        Probe(Real& result, Real offset)
        : result_(result), previous_(result), offset_(offset) {}
        Real operator()(Real x) const {
            result_ = x;
            return previous_ + offset_ - x*x;
        }
        Real derivative(Real x) const { return 2.0*x; }
      private:
        Real& result_;
        Real previous_;
        Real offset_;
    };

    template <class S>
    void test_last_call_with_root(const S& solver, const std::string& name,
                                  bool bracketed,  Real accuracy) {

        Real mins[] = { 3.0, 2.25, 1.5, 1.0 };
        Real maxs[] = { 7.0, 5.75, 4.5, 3.0 };
        Real steps[] = { 0.2, 0.2, 0.1, 0.1 };
        Real offsets[] = { 25.0, 11.0, 5.0, 1.0 };
        Real guesses[] = { 4.5, 4.5, 2.5, 2.5 };
        //Real expected[] = { 5.0, 4.0, 3.0, 2.0 };

        Real argument = 0.0;
        Real result;

        for (Size i=0; i<4; ++i) {
            if (bracketed) {
                result = solver.solve(Probe(argument, offsets[i]), accuracy,
                                      guesses[i], mins[i], maxs[i]);
            } else {
                result = solver.solve(Probe(argument, offsets[i]), accuracy,
                                      guesses[i], steps[i]);
            }

            Real error = std::fabs(result-argument);
            // no floating-point comparison: the solver should have
            // called the function with the very same value it's
            // returning
            if (result != argument) {
                BOOST_FAIL(name << " solver ("
                           << (bracketed ? "" : "not ")
                           << "bracketed):\n"
                           << "    index:   " << i << "\n"
                           << "    expected:   " << result << "\n"
                           << "    calculated: " << argument << "\n"
                           << "    error: " << error);
            }
        }
    }

    template <class S>
    void test_solver(const S& solver, const std::string& name, Real accuracy) {
        // guess on the left side of the root, increasing function
        test_not_bracketed(solver, name, F1(), 0.5);
        test_bracketed(solver, name, F1(), 0.5);
        // guess on the right side of the root, increasing function
        test_not_bracketed(solver, name, F1(), 1.5);
        test_bracketed(solver, name, F1(), 1.5);
        // guess on the left side of the root, decreasing function
        test_not_bracketed(solver, name, F2(), 0.5);
        test_bracketed(solver, name, F2(), 0.5);
        // guess on the right side of the root, decreasing function
        test_not_bracketed(solver, name, F2(), 1.5);
        test_bracketed(solver, name, F2(), 1.5);
        // situation where bisection is used in the finite difference
        // newton solver as the first step and where the initial
        // guess is equal to the next estimate (which causes an infinite
        // derivative if we do not handle this case with special care)
        test_not_bracketed(solver, name, F3(), 1.00001);
        // check that the last function call is made with the root value
        if(accuracy != Null<Real>()) {
            test_last_call_with_root(solver, name, false, accuracy);
            test_last_call_with_root(solver, name, true, accuracy);
        }
    }

}

void Solver1DTest::testBrent() {
    BOOST_TEST_MESSAGE("Testing Brent solver...");
    test_solver(Brent(), "Brent", 1.0e-6);
}

void Solver1DTest::testBisection() {
    BOOST_TEST_MESSAGE("Testing bisection solver...");
    test_solver(Bisection(), "Bisection", 1.0e-6);
}

void Solver1DTest::testFalsePosition() {
    BOOST_TEST_MESSAGE("Testing false-position solver...");
    test_solver(FalsePosition(), "FalsePosition", 1.0e-6);
}

void Solver1DTest::testNewton() {
    BOOST_TEST_MESSAGE("Testing Newton solver...");
    test_solver(Newton(), "Newton", 1.0e-12);
}

void Solver1DTest::testNewtonSafe() {
    BOOST_TEST_MESSAGE("Testing Newton-safe solver...");
    test_solver(NewtonSafe(), "NewtonSafe", 1.0e-9);
}

void Solver1DTest::testFiniteDifferenceNewtonSafe() {
    BOOST_TEST_MESSAGE("Testing finite-difference Newton-safe solver...");
    test_solver(FiniteDifferenceNewtonSafe(), "FiniteDifferenceNewtonSafe", Null<Real>());
}

void Solver1DTest::testRidder() {
    BOOST_TEST_MESSAGE("Testing Ridder solver...");
    test_solver(Ridder(), "Ridder", 1.0e-6);
}

void Solver1DTest::testSecant() {
    BOOST_TEST_MESSAGE("Testing secant solver...");
    test_solver(Secant(), "Secant", 1.0e-6);
}

test_suite* Solver1DTest::suite() {
    test_suite* suite = BOOST_TEST_SUITE("1-D solver tests");
    suite->add(QUANTLIB_TEST_CASE(&Solver1DTest::testBrent));
    suite->add(QUANTLIB_TEST_CASE(&Solver1DTest::testBisection));
    suite->add(QUANTLIB_TEST_CASE(&Solver1DTest::testFalsePosition));
    suite->add(QUANTLIB_TEST_CASE(&Solver1DTest::testNewton));
    suite->add(QUANTLIB_TEST_CASE(&Solver1DTest::testNewtonSafe));
    suite->add(QUANTLIB_TEST_CASE(&Solver1DTest::testFiniteDifferenceNewtonSafe));
    suite->add(QUANTLIB_TEST_CASE(&Solver1DTest::testRidder));
    suite->add(QUANTLIB_TEST_CASE(&Solver1DTest::testSecant));
    return suite;
}

#endif