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

/*
 Copyright (C) 2004, 2005, 2006, 2007 Ferdinando Ametrano
 Copyright (C) 2004, 2005, 2006, 2007, 2008 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 <ql/settings.hpp>
#include <ql/types.hpp>
#include <ql/utilities/dataparsers.hpp>
#include <ql/version.hpp>

#ifdef QL_ENABLE_PARALLEL_UNIT_TEST_RUNNER
#include "paralleltestrunner.hpp"
#else
#include <boost/test/unit_test.hpp>
#endif

#include <boost/timer.hpp>

/* Use BOOST_MSVC instead of _MSC_VER since some other vendors (Metrowerks,
   for example) also #define _MSC_VER
*/
#ifdef BOOST_MSVC
#include <qlext/auto_link.hpp>
#ifndef QL_ENABLE_PARALLEL_UNIT_TEST_RUNNER
#define BOOST_LIB_NAME boost_unit_test_framework
#include <boost/config/auto_link.hpp>
#undef BOOST_LIB_NAME
#endif

/* uncomment the following lines to unmask floating-point exceptions.
   See http://www.wilmott.com/messageview.cfm?catid=10&threadid=9481
*/
//#  include <float.h>
//   namespace { unsigned int u = _controlfp(_EM_INEXACT, _MCW_EM); }

#endif
#include "speedlevel.hpp"
#include "utilities.hpp"

#include "bonds.hpp"
#include "calibratedcurve.hpp"
#include "swaps.hpp"

#include <iomanip>
#include <iostream>

using namespace boost::unit_test_framework;

namespace {

boost::timer t;

void startTimer() { t.restart(); }
void stopTimer() {
    double seconds = t.elapsed();
    int hours = int(seconds / 3600);
    seconds -= hours * 3600;
    int minutes = int(seconds / 60);
    seconds -= minutes * 60;
    std::cout << " \nTests completed in ";
    if (hours > 0)
        std::cout << hours << " h ";
    if (hours > 0 || minutes > 0)
        std::cout << minutes << " m ";
    std::cout << std::fixed << std::setprecision(0) << seconds << " s\n" << std::endl;
}

void configure(QuantLib::Date evaluationDate) {
    /* if needed, a subset of the lines below can be
       uncommented and/or changed to run the test suite with a
       different configuration. In the future, we'll need a
       mechanism that doesn't force us to recompile (possibly a
       couple of command-line flags for the test suite?)
    */

    // QuantLib::Settings::instance().includeReferenceDateCashFlows() = true;
    // QuantLib::Settings::instance().includeTodaysCashFlows() = boost::none;

    QuantLib::Settings::instance().evaluationDate() = evaluationDate;
}

} // namespace

#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib {

Integer sessionId() { return 0; }

} // namespace QuantLib
#endif

QuantLib::Date evaluation_date(int argc, char** argv) {
    /*! Dead simple parser:
        - passing --date=YYYY-MM-DD causes the test suite to run on
          that date;
        - passing --date=today causes it to run on today's date;
        - passing nothing causes it to run on a known date for which
          there should be no date-dependent errors as far as we know.

        Dates that should eventually be checked include:
        - 2015-08-29 causes three tests to fail;
        - 2016-02-29 causes two tests to fail.
    */

    QuantLib::Date knownGoodDefault = QuantLib::Date(16, QuantLib::September, 2015);

    for (int i = 1; i < argc; ++i) {
        std::string arg = argv[i];
        if (arg == "--date=today")
            return QuantLib::Date::todaysDate();
        else if (arg.substr(0, 7) == "--date=")
            return QuantLib::DateParser::parseISO(arg.substr(7));
    }
    return knownGoodDefault;
}

SpeedLevel speed_level(int argc, char** argv) {
    /*! Again, dead simple parser:
        - passing --slow causes all tests to be run;
        - passing --fast causes most tests to be run, except the slowest;
        - passing --faster causes only the faster tests to be run;
        - passing nothing is the same as --slow
    */

    for (int i = 1; i < argc; ++i) {
        std::string arg = argv[i];
        if (arg == "--slow")
            return Slow;
        else if (arg == "--fast")
            return Fast;
        else if (arg == "--faster")
            return Faster;
    }
    return Slow;
}

test_suite* init_unit_test_suite(int, char* []) {

    int argc = boost::unit_test::framework::master_test_suite().argc;
    char** argv = boost::unit_test::framework::master_test_suite().argv;
    configure(evaluation_date(argc, argv));
    SpeedLevel speed = speed_level(argc, argv);

    const QuantLib::Settings& settings = QuantLib::Settings::instance();
    std::ostringstream header;
    header << " Testing "
#ifdef BOOST_MSVC
        QLEXT_LIB_NAME
#else
              "QuantLib " QL_VERSION
#endif
              "\n  QL_NEGATIVE_RATES "
#ifdef QL_NEGATIVE_RATES
              "       defined"
#else
              "     undefined"
#endif
              "\n  QL_EXTRA_SAFETY_CHECKS "
#ifdef QL_EXTRA_SAFETY_CHECKS
              "  defined"
#else
              "undefined"
#endif
              "\n  QL_USE_INDEXED_COUPON "
#ifdef QL_USE_INDEXED_COUPON
              "   defined"
#else
              " undefined"
#endif
              "\n"
           << "evaluation date is " << settings.evaluationDate() << ",\n"
           << (settings.includeReferenceDateEvents() ? "reference date events are included,\n"
                                                     : "reference date events are excluded,\n")
           << (settings.includeTodaysCashFlows() == boost::none
                   ? ""
                   : (*settings.includeTodaysCashFlows() ? "today's cashflows are included,\n"
                                                         : "today's cashflows are excluded,\n"))
           << (settings.enforcesTodaysHistoricFixings() ? "today's historic fixings are enforced."
                                                        : "today's historic fixings are not enforced.")
           << "\nRunning " << (speed == Faster ? "faster" : (speed == Fast ? "fast" : "all")) << " tests.";

    std::string rule = std::string(41, '=');

    BOOST_TEST_MESSAGE(rule);
    BOOST_TEST_MESSAGE(header.str());
    BOOST_TEST_MESSAGE(rule);
    test_suite* test = BOOST_TEST_SUITE("QuantLibExt test suite");

    test->add(QUANTLIB_TEST_CASE(startTimer));

    test->add(BondTest::suite());
    test->add(CalibratedCurveTest::suite());
    test->add(SwapTest::suite());

    test->add(QUANTLIB_TEST_CASE(stopTimer));

    return test;
}


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

#include "objects/numerics/integer.hpp"
#include "objects/collections/ast.hpp"
#include "globals/globals.hpp"

using obj::Object;

BOOST_AUTO_TEST_SUITE( ast_suite )

//  TODO: run this before globals::numerics::add
//  then, we know that the add function works!
BOOST_AUTO_TEST_CASE ( func_parents_correct ) {
  using obj::AST;
  using obj::mk_ast;
  using obj::Integer;

  Object* test_ast = mk_ast({num::add, new Integer(2), new Integer(3)});

  obj::MaybeEffect out = test_ast->eval(*context::empty);
  Integer* ans = dynamic_cast<Integer*>(std::get<Object*>(out));
  BOOST_TEST(ans != nullptr);

  BOOST_TEST(ans->val == 5);
}

//  TODO: run this before globals::numerics::add
//  then, we know that the add function works!
BOOST_AUTO_TEST_CASE ( mod_ast_calls_func ) {
  using obj::AST;
  using obj::mk_ast;
  using obj::Integer;

  Object* test_ast = mk_ast({num::add, new Integer(2), new Integer(3)});

  obj::MaybeEffect out = test_ast->eval(*context::empty);
  Integer* ans = dynamic_cast<Integer*>(std::get<Object*>(out));
  BOOST_TEST(ans != nullptr);

  BOOST_TEST(ans->val == 5);
}

// BOOST_AUTO_TEST_CASE( mod_ast_calls_macro ) {
// }

BOOST_AUTO_TEST_SUITE_END()


/*
 * This file is part of SpatialCL, a library for the spatial processing of
 * particles.
 *
 * Copyright (c) 2018 Aksel Alpay
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 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.
 */

#include <vector>
#include <iostream>
#include <string>

#include <boost/preprocessor/stringize.hpp>

#include <SpatialCL/tree.hpp>
#include <SpatialCL/query.hpp>

#include <QCL/qcl.hpp>
#include <QCL/qcl_array.hpp>

#include "../../common/environment.hpp"
#include "../../common/random_vectors.hpp"
#include "../../common/timer.hpp"

constexpr std::size_t particle_dimension = 3;
constexpr std::size_t num_runs = 10;

const std::size_t num_particles = 700000;
const std::size_t num_query_groups_xy = 128;
const std::size_t query_group_size_xy = 8;

constexpr std::size_t max_retrieved_particles = 8;

using tree_type = spatialcl::hilbert_bvh_sp3d_tree<particle_dimension>;
using type_system = tree_type::type_system;
using scalar = type_system::scalar;

// Define queries
using strict_dfs_range_engine =
  spatialcl::query::strict_dfs_range_query_engine<tree_type,
                                                  max_retrieved_particles>;

using relaxed_dfs_range_engine =
  spatialcl::query::relaxed_dfs_range_query_engine<tree_type,
                                                  max_retrieved_particles>;

template<std::size_t Group_size>
using grouped_dfs_range_engine =
  spatialcl::query::grouped_dfs_range_query_engine<tree_type,
                                                    max_retrieved_particles,
                                                    Group_size>;

using particle_type = spatialcl::configuration<type_system>::particle_type;
using vector_type = spatialcl::configuration<type_system>::vector_type;
constexpr std::size_t dimension = type_system::dimension;

template<class Query_engine>
void run_benchmark(const std::string& name,
                   const qcl::device_context_ptr& ctx,
                   const tree_type& tree,
                   const qcl::device_array<vector_type>& queries_min,
                   const qcl::device_array<vector_type>& queries_max,
                   qcl::device_array<particle_type>& result,
                   qcl::device_array<cl_uint>& num_results)
{
  Query_engine query_engine;

  typename Query_engine::handler_type query_handler {
    queries_min.get_buffer(),
    queries_max.get_buffer(),
    result.get_buffer(),
    num_results.get_buffer(),
    queries_min.size()
  };

  common::timer t;
  // Execute the query once before measuring to make sure
  // we do not take into account kernel compilation times.
  query_engine(tree, query_handler);

  cl_int err = ctx->get_command_queue().finish();
  qcl::check_cl_error(err, "Error while executing range query");

  t.start();
  for (std::size_t run = 0; run < num_runs; ++run)
  {
    query_engine(tree, query_handler);

    err = ctx->get_command_queue().finish();
    qcl::check_cl_error(err, "Error while executing range query");
  }
  double time = t.stop();

  std::size_t total_num_retrieved_particles = 0;
  std::vector<cl_uint> host_num_particles;
  num_results.read(host_num_particles);

  for(const cl_uint n: host_num_particles)
    total_num_retrieved_particles += n;

  std::cout << "Benchmark " << name << " completed in "
            << time << "s => " << num_runs * queries_min.size() / time << " Queries/s"
            << " (retrieved: " << total_num_retrieved_particles << " particles)" << std::endl;
}

int main()
{
  common::environment env;
  qcl::device_context_ptr ctx = env.get_device_context();

  // Setup tree

  std::vector<particle_type> particles;
  common::random_vectors<scalar, particle_dimension> rnd;
  rnd(num_particles, particles);

  tree_type gpu_tree{ctx, particles};

  // Create queries
  std::size_t total_num_queries =
    num_query_groups_xy * num_query_groups_xy * query_group_size_xy * query_group_size_xy;

  std::vector<vector_type> query_ranges_min;
  std::vector<vector_type> query_ranges_max;

  double query_stepsize = 1.0 / static_cast<double>(num_query_groups_xy * query_group_size_xy);
  scalar query_diameter =  static_cast<scalar>(3.0 * query_stepsize);

  for (std::size_t x = 0; x < num_query_groups_xy; ++x)
  {
    for (std::size_t y = 0; y < num_query_groups_xy; ++y)
    {
      for (std::size_t tile_x = 0; tile_x < query_group_size_xy; ++tile_x)
      {
        for (std::size_t tile_y = 0; tile_y < query_group_size_xy; ++tile_y)
        {
          std::size_t query_id_x = x * query_group_size_xy + tile_x;
          std::size_t query_id_y = y * query_group_size_xy + tile_y;

          scalar x_position = static_cast<scalar>(query_id_x * query_stepsize);
          scalar y_position = static_cast<scalar>(query_id_y * query_stepsize);

          vector_type query_min, query_max;

          query_min.s[0] = x_position;
          query_min.s[1] = y_position;
          query_min.s[2] = 0.0f;

          query_max.s[0] = x_position + query_diameter;
          query_max.s[1] = y_position + query_diameter;
          query_max.s[2] = query_diameter;

          query_ranges_min.push_back(query_min);
          query_ranges_max.push_back(query_max);

        }
      }
    }
  }

  qcl::device_array<vector_type> device_ranges_min{ctx, query_ranges_min};
  qcl::device_array<vector_type> device_ranges_max{ctx, query_ranges_max};
  qcl::device_array<particle_type> result_particles{ctx,
                                                  total_num_queries*max_retrieved_particles};
  qcl::device_array<cl_uint> result_num_retrieved_particles{ctx,
                                                            total_num_queries};

#define RUN_BENCHMARK(engine) \
  run_benchmark<engine>(BOOST_PP_STRINGIZE(engine), \
                        ctx, \
                        gpu_tree, \
                        device_ranges_min, \
                        device_ranges_max, \
                        result_particles, \
                        result_num_retrieved_particles)

  RUN_BENCHMARK(strict_dfs_range_engine);
  RUN_BENCHMARK(relaxed_dfs_range_engine);
  //RUN_BENCHMARK(grouped_dfs_range_engine<16>);
  RUN_BENCHMARK(grouped_dfs_range_engine<32>);
  RUN_BENCHMARK(grouped_dfs_range_engine<64>);
  RUN_BENCHMARK(grouped_dfs_range_engine<128>);
  RUN_BENCHMARK(grouped_dfs_range_engine<256>);
  RUN_BENCHMARK(grouped_dfs_range_engine<512>);

  return 0;
}


#include <cstdio>
#include <iostream>
#include <boost/graph/adjacency_list.hpp>

#include "algos.h"
#include "random_graph.h"
#include "bench_mem_trace.h"

#define do_not_optimize(v) asm volatile("" : : "r,m"(v) : "memory");

typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS> Graph;

const unsigned vertices[] = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};

template <unsigned num, unsigned div>
void fill_random_graph(void) {
	char name[128];

	for (const auto v : vertices) {
		Graph g = gen_random_connected_graph<Graph>(v, (double)num/div);
		auto o = gen_random_order(g);
		const unsigned n = boost::num_vertices(g) + boost::num_edges(g);

		sprintf(name, "fill_random_graph<%u,%u> v=%u n=%u", num, div, v, n);
		start_trace(name);
		fill(g, o);
		stop_trace();
	}
}

template <unsigned num, unsigned div>
void lex_m_random_graph(void) {
	char name[128];

	for (const auto v : vertices) {
		Graph g = gen_random_connected_graph<Graph>(v, (double)num/div);
		const unsigned n = boost::num_vertices(g) * boost::num_edges(g);

		sprintf(name, "lex_m_random_graph<%u,%u> v=%u n=%u", num, div, v, n);
		start_trace(name);
		do_not_optimize(lex_m(g));
		stop_trace();
	}
}

template <unsigned num, unsigned div>
void lex_p_random_graph(void) {
	char name[128];

	for (const auto v : vertices) {
		Graph g = gen_random_connected_graph<Graph>(v, (double)num/div);
		const unsigned n = boost::num_vertices(g) + boost::num_edges(g);

		if (num != div)
			fill(g, lex_m(g));

		sprintf(name, "lex_p_random_graph<%u,%u> v=%u n=%u", num, div, v, n);
		start_trace(name);
		do_not_optimize(lex_p(g));
		stop_trace();
	}
}

int main(void) {
	fill_random_graph <1, 10>(); // edge density  10%
	fill_random_graph <1,  4>(); // edge density  25%
	fill_random_graph <1,  2>(); // edge density  50%
	fill_random_graph <2,  3>(); // edge density  66%
	fill_random_graph <3,  4>(); // edge density  75%
	fill_random_graph <1,  1>(); // edge density 100% (complete graph)

	lex_m_random_graph<1, 10>(); // edge density  10%
	lex_m_random_graph<1,  4>(); // edge density  25%
	lex_m_random_graph<1,  2>(); // edge density  50%
	lex_m_random_graph<2,  3>(); // edge density  66%
	lex_m_random_graph<3,  4>(); // edge density  75%
	lex_m_random_graph<1,  1>(); // edge density 100% (complete graph)

	lex_p_random_graph<1, 10>(); // edge density  10%
	lex_p_random_graph<1,  4>(); // edge density  25%
	lex_p_random_graph<1,  2>(); // edge density  50%
	lex_p_random_graph<2,  3>(); // edge density  66%
	lex_p_random_graph<3,  4>(); // edge density  75%
	lex_p_random_graph<1,  1>(); // edge density 100% (complete graph)

	return 0;
}


//  (C) Copyright John Maddock 2000.
//  Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies. This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.

//  See http://www.boost.org for most recent version including documentation.

#include <climits>
#include <boost/limits.hpp>
#include <boost/static_assert.hpp>

template <class UnsignedInt>
class myclass
{
private:
   BOOST_STATIC_ASSERT(sizeof(UnsignedInt) * CHAR_BIT >= 16);
   BOOST_STATIC_ASSERT(std::numeric_limits<UnsignedInt>::is_specialized
                        && std::numeric_limits<UnsignedInt>::is_integer
                        && !std::numeric_limits<UnsignedInt>::is_signed);
public:
   /* details here */
};

myclass<unsigned>      m1; // this should be OK
//myclass<int>           m2; // this should fail
myclass<unsigned char> m3; // and so should this

int main()
{
   return 0;
}


/*******************************************************************************
 * Copyright (c) 2012, Dougal J. Sutherland (dsutherl@cs.cmu.edu).             *
 * 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 Carnegie Mellon University nor the                *
 *       names of the contributors may be used to endorse or promote products  *
 *       derived from this software without specific prior written permission. *
 *                                                                             *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" *
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE   *
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  *
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE   *
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR         *
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF        *
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS    *
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN     *
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)     *
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  *
 * POSSIBILITY OF SUCH DAMAGE.                                                 *
 ******************************************************************************/
#include "sdm/basics.hpp"
#include "sdm/kernels/gaussian.hpp"

#include <cmath>
#include <string>
#include <boost/format.hpp>
#include <boost/ptr_container/ptr_vector.hpp>

#include "sdm/utils.hpp"

namespace sdm {

std::string GaussianKernel::name() const {
    return (boost::format("Gaussian(%g)") % sigma).str();
}

double GaussianKernel::transformDivergence(double div) const {
    div /= sigma;
    return std::exp(-.5 * div * div);
}

GaussianKernel* GaussianKernel::do_clone() const {
    return new GaussianKernel(sigma);
}

////////////////////////////////////////////////////////////////////////////////

typedef std::vector<double>::const_iterator double_iter;

const boost::ptr_vector<Kernel>* GaussianKernelGroup::getTuningVector(
        const double* divs, size_t n) const
{
    double scale;
    if (scale_sigma) {
        // find median of the nonzero divergences, for scaling sigma
        std::vector<double> pos_divs;
        pos_divs.reserve(n * (n - 1)); // diag is usually 0
        for (size_t i = 0; i < n; i++) {
            for (size_t j = 0; j < n; j++) {
                double d = divs[i*n + j];
                if (d > 0)
                    pos_divs.push_back(d);
            }
        }
        scale = detail::median(pos_divs);
    } else {
        scale = 1.0;
    }

    boost::ptr_vector<Kernel> *kerns = new boost::ptr_vector<Kernel>;
    kerns->reserve(sigmas.size());
    for (double_iter i = sigmas.begin(); i != sigmas.end(); ++i) {
        kerns->push_back(new GaussianKernel(*i * scale));
    }
    return kerns;
}

GaussianKernelGroup* GaussianKernelGroup::do_clone() const {
    return new GaussianKernelGroup();
}

} // end namespace


//---------------------------------------------------------------------------//
// Copyright (c) 2013 Muhammad Junaid Muzammil <mjunaidmuzammil@gmail.com>
//
// 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
//
// See http://kylelutz.github.com/compute for more information.
//---------------------------------------------------------------------------//

#define BOOST_TEST_MODULE TestThreefry
#include <boost/test/unit_test.hpp>

#include <boost/compute/random/threefry_engine.hpp>
#include <boost/compute/container/vector.hpp>

#include <boost/compute/random/uniform_real_distribution.hpp>

#include "check_macros.hpp"
#include "context_setup.hpp"

BOOST_AUTO_TEST_CASE(generate_uint)
{
    using boost::compute::uint_;

    boost::compute::threefry_engine<> random_engine(queue);
    boost::compute::vector<uint_> random_values(19, context);

    random_engine.generate(random_values.begin(), random_values.end(), queue);
    queue.finish();
    CHECK_RANGE_EQUAL(
        uint_, 19, random_values,
        (uint_(0x6b200159),
         uint_(0x99ba4efe),
         uint_(0x508efb2c),
         uint_(0xc0de3f32),
         uint_(0x64a626ec),
         uint_(0xfc15e573),
         uint_(0xb8abc4d1),
         uint_(0x537eb86),
         uint_(0xac6dc2bb),
         uint_(0xa7adb3c3),
         uint_(0x5641e094),
         uint_(0xe4ab4fd),
         uint_(0xa53c1ce9),
         uint_(0xabcf1dba),
         uint_(0x2677a25a),
         uint_(0x76cf5efc),
         uint_(0x2d08247f),
         uint_(0x815480f1),
         uint_(0x2d1fa53a))
    );
}

BOOST_AUTO_TEST_CASE(generate_float)
{
    using boost::compute::float_;

    boost::compute::threefry_engine<> random_engine(queue);
    boost::compute::uniform_real_distribution<float_> random_distribution(0.f, 4.f);

    boost::compute::vector<float_> random_values(1024, context);
    random_distribution.generate(
        random_values.begin(), random_values.end(), random_engine, queue
    );

    std::vector<float_> random_values_host(1024);
    boost::compute::copy(
        random_values.begin(), random_values.end(),
        random_values_host.begin(),
        queue
    );
    queue.finish();

    double sum = 0.0;
    for(size_t i = 0; i < random_values_host.size(); i++)
    {
        BOOST_CHECK_LT(random_values_host[i], 4.0f);
        BOOST_CHECK_GE(random_values_host[i], 0.0f);
        sum += random_values_host[i];
    }
    double mean = sum / random_values_host.size();
    // For 1024 it can be 10% off
    BOOST_CHECK_CLOSE(mean, 2.0f, 10.0);
}

BOOST_AUTO_TEST_SUITE_END()


/*
 * This file is part of SpatialCL, a library for the spatial processing of
 * particles.
 *
 * Copyright (c) 2017, 2018 Aksel Alpay
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 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.
 */

#include <iostream>

#include <boost/preprocessor/stringize.hpp>

#include <SpatialCL/tree.hpp>
#include <SpatialCL/query.hpp>

#include <QCL/qcl.hpp>
#include <QCL/qcl_array.hpp>

#include "../../common/environment.hpp"
#include "../../common/random_vectors.hpp"
#include "../../common/verification_range.hpp"

constexpr std::size_t particle_dimension = 3;

const std::size_t num_particles = 32000;
const std::size_t num_queries = 2000;

constexpr std::size_t max_retrieved_particles = 8;

using tree_type = spatialcl::hilbert_bvh_sp3d_tree<particle_dimension>;
using type_system = tree_type::type_system;
using scalar = type_system::scalar;

constexpr scalar query_diameter = 0.05f;

// Define queries
using strict_dfs_range_engine =
  spatialcl::query::strict_dfs_range_query_engine<tree_type,
                                                  max_retrieved_particles>;

using relaxed_dfs_range_engine =
  spatialcl::query::relaxed_dfs_range_query_engine<tree_type,
                                                  max_retrieved_particles>;

template<std::size_t Group_size>
using grouped_dfs_range_engine =
  spatialcl::query::grouped_dfs_range_query_engine<tree_type,
                                                    max_retrieved_particles,
                                                    Group_size>;

using particle_type = spatialcl::configuration<type_system>::particle_type;
using vector_type = spatialcl::configuration<type_system>::vector_type;
constexpr std::size_t dimension = type_system::dimension;

template<class Query_engine>
std::size_t execute_range_query_test(const qcl::device_context_ptr& ctx,
                                     const tree_type& tree,
                                     const std::vector<vector_type>& host_queries_min,
                                     const std::vector<vector_type>& host_queries_max,
                                     const qcl::device_array<vector_type>& queries_min,
                                     const qcl::device_array<vector_type>& queries_max,
                                     const std::vector<particle_type>& particles,
                                     qcl::device_array<particle_type>& result,
                                     qcl::device_array<cl_uint>& num_results)
{
  Query_engine query_engine;

  typename Query_engine::handler_type query_handler {
    queries_min.get_buffer(),
    queries_max.get_buffer(),
    result.get_buffer(),
    num_results.get_buffer(),
    queries_min.size()
  };

  std::cout << "Executing query..." << std::endl;

  query_engine(tree, query_handler);

  cl_int err = ctx->get_command_queue().finish();
  qcl::check_cl_error(err, "Error while executing range query");

  // Retrieve results
  std::vector<particle_type> host_results;
  std::vector<cl_uint> host_num_results;
  result.read(host_results);
  num_results.read(host_num_results);

  std::cout << "Verifying results, please wait..." << std::endl;
  // Verify results
  common::verification::naive_cpu_range_verifier<type_system> verifier{
    host_queries_min,
    host_queries_max,
    max_retrieved_particles
  };

  return verifier(particles, host_results, host_num_results);
}

int main()
{
  // Setup particle tree
  common::environment env;
  qcl::device_context_ptr ctx = env.get_device_context();

  std::vector<particle_type> particles;
  common::random_vectors<scalar, particle_dimension> rnd;
  rnd(num_particles, particles);

  tree_type gpu_tree{ctx, particles};

  // Create random ranges for the queries
  std::vector<vector_type> query_points;
  rnd(num_queries, query_points);

  std::vector<vector_type> host_ranges_min = query_points;
  std::vector<vector_type> host_ranges_max = query_points;

  std::transform(host_ranges_min.begin(), host_ranges_min.end(),
                 host_ranges_min.begin(),
                 [&](vector_type current){
    for(std::size_t i = 0; i < dimension; ++i)
      current.s[i] -= query_diameter / 2;
    return current;
  });

  std::transform(host_ranges_max.begin(), host_ranges_max.end(),
                 host_ranges_max.begin(),
                 [&](vector_type current){
    for(std::size_t i = 0; i < dimension; ++i)
      current.s[i] += query_diameter / 2;
    return current;
  });

  // Create buffers on the compute device
  qcl::device_array<vector_type> ranges_min{ctx, host_ranges_min};
  qcl::device_array<vector_type> ranges_max{ctx, host_ranges_max};
  qcl::device_array<particle_type> result_particles{ctx,
                                    num_queries*max_retrieved_particles};
  qcl::device_array<cl_uint> result_num_retrieved_particles{ctx,
                                                            num_queries};

  std::size_t num_errors = 0;

#define RUN_TEST(test_name) \
  num_errors = \
      execute_range_query_test<test_name>(ctx,              \
                                          gpu_tree,         \
                                          host_ranges_min,  \
                                          host_ranges_max,  \
                                          ranges_min,       \
                                          ranges_max,       \
                                          particles,        \
                                          result_particles, \
                                          result_num_retrieved_particles); \
  std::cout << BOOST_PP_STRINGIZE(test_name) <<" completed queries with " \
            << num_errors << " errors." << std::endl

  RUN_TEST(strict_dfs_range_engine);
  RUN_TEST(relaxed_dfs_range_engine);
  //RUN_TEST(grouped_dfs_range_engine<16>);
  RUN_TEST(grouped_dfs_range_engine<32>);
  RUN_TEST(grouped_dfs_range_engine<64>);

  return 0;
}


//          Copyright Gavin Band 2008 - 2012.
// 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 <iostream>
#include <string>
#include <Eigen/Core>
#include "genfile/snp_data_utils.hpp"
#include "genfile/gen.hpp"
#include "genfile/GenLikeSNPDataSink.hpp"
#include "genfile/GenDosageFileSNPDataSink.hpp"
#include "genfile/vcf/get_set.hpp"
#include "genfile/vcf/get_set_eigen.hpp"

namespace genfile {
	GenDosageFileSNPDataSink::GenDosageFileSNPDataSink( std::string const& filename, Chromosome chromosome ):
		GenLikeSNPDataSink( filename, chromosome, get_compression_type_indicated_by_filename( filename ) )
	{}

	GenDosageFileSNPDataSink::GenDosageFileSNPDataSink( std::string const& filename, Chromosome chromosome, CompressionType compression_type ):
		GenLikeSNPDataSink( filename, chromosome, compression_type )
	{}

	namespace {
		struct GenotypeWriter: public vcf::GenotypeSetterBase {
			GenotypeWriter( Eigen::VectorXd& data ):
				m_data( data )
			{
				m_data.setConstant( -1 ) ;
			}
			~GenotypeWriter() throw() {}

			void set( std::size_t i, double AA, double AB, double BB ) {
				if( AA == 0 && AB == 0 && BB == 0 ) {
					m_data[i] = -1 ;
				} else {
					m_data[i] = ( ( 2 * BB ) + AB ) ;
				}
			}

			void write_to_stream( std::ostream& stream ) const {
				for( std::size_t i = 0; i < m_data.size(); ++i ) {
					if( m_data[i] == -1 ) {
						stream << " NA" ;
					} else {
						stream << " " << m_data[i] ;
					}
				}
			}

		private:
			Eigen::VectorXd& m_data ;
		} ;
	}

	void GenDosageFileSNPDataSink::set_sample_names_impl( std::size_t number_of_samples, SampleNameGetter getter ) {
		if( write_chromosome_column() ) {
			stream() << "chromosome " ;
		}
		stream() << "SNPID rsid position alleleA alleleB" ;
		for( std::size_t i = 0; i < number_of_samples; ++i ) {
			stream() << " " << getter(i) ;
		}
		stream() << "\n" ;
		m_data.resize( number_of_samples ) ;
		m_data.setZero() ;
	}

	void GenDosageFileSNPDataSink::write_variant_data_impl(
		VariantIdentifyingData const& id_data,
		VariantDataReader& data_reader,
		Info const& info
	) {
		write_variant( stream(), id_data ) ;
		GenotypeWriter writer( m_data ) ;
		data_reader.get( ":genotypes:", writer ) ;
		writer.write_to_stream( stream() ) ;
		stream() << "\n" ;
	}
}


#include <boost/heap/skew_heap.hpp>


// Copyright (c) 2014 The Bitcoin Core developers
// Copyright (c) 2014-2015 The Dash developers
// Copyright (c) 2015-2017 The PIVX developers
// Copyright (c) 2015-2017 The ALQO developers
// Copyright (c) 2017-2018 The nood developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "primitives/transaction.h"
#include "main.h"

#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(main_tests)

CAmount nMoneySupplyPoWEnd = 41769651 * COIN;

BOOST_AUTO_TEST_CASE(subsidy_limit_test)
{
    CAmount nSum = 0;
    for (int nHeight = 0; nHeight < 1; nHeight += 1) {
        /* premine in block 1 */
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 315000 * COIN);
        nSum += nSubsidy;
    }
    for (int nHeight = 1; nHeight < 3600; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 2 * COIN);
        nSum += nSubsidy;
    }
    for (int nHeight = 3600; nHeight < 15000; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 20 * COIN);
        nSum += nSubsidy;
    }
    for (int nHeight = 15000; nHeight < 262800; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 20 * COIN);
        nSum += nSubsidy;
    }
	for (int nHeight = 262800; nHeight < 525601; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 10 * COIN);
        nSum += nSubsidy;
    }
	for (int nHeight = 525601; nHeight < 788401; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 5 * COIN);
        nSum += nSubsidy;
    }
	for (int nHeight = 788401; nHeight < 1051201; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 2.5 * COIN);
        nSum += nSubsidy;
    }
	for (int nHeight = 1051201; nHeight < 1314001; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 1.25 * COIN);
        nSum += nSubsidy;
    }
	for (int nHeight = 1314001; nHeight < 1576800; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 0.625 * COIN);
        nSum += nSubsidy;
    }
    for (int nHeight = 1576800; nHeight < 1839600; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 0.3125 * COIN);
        nSum += nSubsidy;
    }

	BOOST_CHECK(nSum > 0 && nSum <= nMoneySupplyPoWEnd);
}

BOOST_AUTO_TEST_SUITE_END()