#include <vector>
#include <array>
#include <string>
#include <iostream>
#include <fstream>
#include <iomanip>

#include <BlueLink/Host/AFU.hpp>
#include <BlueLink/Host/WED.hpp>

#include <boost/range/algorithm.hpp>

#include <boost/random/mersenne_twister.hpp>

#include <BlueLink/Host/aligned_allocator.hpp>

using namespace std;

struct MemLoadWED
{
	const uint64_t*	pSrc=nullptr;
	uint64_t* 		pDst=nullptr;
	uint64_t		size=0;

	array<uint64_t,13>	pad;

	MemLoadWED(){ boost::fill(pad,0); }
};

int main(int argc,char **argv)
{
	//// Source constants
#ifdef SIM
	const unsigned logNHL 	 = 10;				// 1024 512b half-lines
	const unsigned timeout=1800;
#else
	const unsigned logNHL	= 16;				// 64k 512b half-lines
	const unsigned timeout	= 4;
#endif

	const unsigned logNBanks =  4;				// 16 banks

	//// Derived constants
		const unsigned Nl1024=1 << (logNHL+1);	// # 1024b (128B) cache lines
		const unsigned Nhl512=1 << (logNHL);	// # 512b (64B) half-lines
		const unsigned Nui64=1 << (logNHL+3);	// # 64b (8B) words
		const unsigned NB= 1 << (logNHL+6);		// # 8b (1B) bytes

		const unsigned logNui64 = logNHL+3;
		const unsigned logNL = logNHL+1;

		const unsigned NBanks = 1<<logNBanks;

		const unsigned NWordsPerBank = (1 << (logNHL+3-logNBanks));

	BOOST_STATIC_ASSERT(logNL >= 6);

	StackWED<MemLoadWED,128,128> wed;

	vector<uint64_t,aligned_allocator<uint64_t,128>> src(Nui64,0),dst(Nui64,0);

	cout << "Running with " << NB << " byte transfer (" << Nl1024 << " cache lines, " << Nhl512 << " half-lines, " << Nui64 << " 64b words)" << endl;

	// set up WED
	wed->pSrc = src.data();
	wed->pDst = dst.data();
	wed->size = NB;

	// fill source with random numbers
	boost::mt19937_64 rng;
	boost::generate(src, rng);

	ofstream os("output.expected.hex");

	for(uint64_t i : src)
		os << setw(16) << hex << i << endl;
	os.close();

	// start accelerator
	AFU afu("/dev/cxl0.0d");
	afu.start(wed);
	sleep(1);

	////// Do host-AFU copy

	cout << "Waiting for host->AFU copy to finish" << endl;

	uint64_t tsH2AStart = afu.mmio_read64(0x10);
	uint64_t tsH2ADone  = 0;

	unsigned i;

	for(i=0;i<timeout && (tsH2ADone = afu.mmio_read64(0x18)) == 0; ++i)
		sleep(1);

	if (i==timeout)
		cout << "TIMEOUT!" << endl;

	cout << "  Started at " << tsH2AStart << " and finished at " << tsH2ADone << " (duration " << tsH2ADone-tsH2AStart << ")" << endl;

	////// Check BRAM readback

	std::vector<std::pair<uint8_t,uint32_t>> addrs{
		make_pair(0,0),
		make_pair(0,NWordsPerBank-1),						// last element in bank 1
		make_pair(1,NWordsPerBank-1),
		make_pair(1,0),
		make_pair(1,NWordsPerBank-1),
		make_pair(NBanks-2,NWordsPerBank-1),
		make_pair(NBanks-2,NWordsPerBank-2),
		make_pair(NBanks-2,NWordsPerBank-3),
		make_pair(NBanks-1,NWordsPerBank-1),				// last element overall
		make_pair(0,1),
		make_pair(NBanks-1,1),
		make_pair(NBanks-1,0)								// last bank, elements 1 and 0
	};

	cout << "Checking BRAM readback: " << endl;
	for(const auto p : addrs)
	{

		afu.mmio_write64(0x00,(p.first << 16) | p.second);
		cout << " Bank " << hex << (unsigned)p.first << " offset " << hex << p.second << ' ';
#ifdef SIM
		sleep(1);
#endif

		uint64_t expect = src[(p.first<<(logNui64-logNBanks)) | p.second];
		uint64_t actual = afu.mmio_read64(0x00);

		if (actual == expect)
			cout << "OK (" << hex << setw(16) << expect << ")";
		else
			cout << "Mismatch - expecting " << hex << setw(16) << expect << " received " << hex << actual;
		cout << endl;
	}

	////// Do AFU->host copy

	cout << "Starting AFU->host copy" << endl;

	afu.mmio_write64(0x08,0);

	uint64_t tsA2HStart=afu.mmio_read64(0x20);
	uint64_t tsA2HDone =afu.mmio_read64(0x28);

	for(unsigned i=0;i<timeout && (tsA2HDone = afu.mmio_read64(0x28)) == 0; ++i)
	{
#ifdef SIM
		sleep(1);
#endif
	}

	if (i==timeout)
		cout << "TIMEOUT!" << endl;

	cout << "AFU->host transfer started at " << tsA2HStart << " and finished at " << tsA2HDone << " (duration " << tsA2HDone-tsA2HStart << ")" << endl;

	////// Check output

	cout << "Output: " << endl;

	os.open("output.actual.hex");

	for(uint64_t o : dst)
		os << setw(16) << hex << o << endl;
	os.close();

	for(unsigned i=0;i<Nui64;++i)
		if (src[i] != dst[i])
			cout << "Mismatch at byte offset " << std::hex << setw(8) << 8*i << ": expected " << setw(16) << std::hex << src[i] << " and received " << setw(16) << std::hex << dst[i] << endl;

	cout << "Output check done" << endl;

	afu.mmio_write64(0x10,0);

	return 0;
}


/*******************************************************************************
 *         Copyright 2003-2014 LASMEA UMR 6602 CNRS/U.B.P
 *         Copyright 2011-2014 LRI    UMR 8623 CNRS/Univ Paris Sud XI
 *
 *          Distributed under the Boost Software License, Version 1.0.
 *                 See accompanying file LICENSE.txt or copy at
 *                     http://www.boost.org/LICENSE_1_0.txt
 ******************************************************************************/
#ifndef NT2_CORE_FUNCTIONS_EXPR_GLOBALNORM_HPP_INCLUDED
#define NT2_CORE_FUNCTIONS_EXPR_GLOBALNORM_HPP_INCLUDED

#include <nt2/include/functions/globalnorm.hpp>
#include <nt2/core/container/dsl.hpp>
#include <nt2/sdk/meta/as_real.hpp>
#include <nt2/include/functions/abs.hpp>
#include <nt2/include/functions/globalmin.hpp>
#include <nt2/include/functions/globalmax.hpp>
#include <nt2/include/functions/globalasum1.hpp>
#include <nt2/include/functions/globalnorm2.hpp>
#include <nt2/include/functions/globalnormp.hpp>
#include <nt2/include/constants/one.hpp>
#include <nt2/include/constants/two.hpp>
#include <nt2/include/constants/inf.hpp>
#include <nt2/include/constants/minf.hpp>
#include <nt2/tags.hpp>
#include <boost/dispatch/functor/meta/make_functor.hpp>
#include <boost/dispatch/attributes.hpp>
#include <boost/assert.hpp>

namespace nt2 { namespace ext
{
  // Default globalnorm is globalnorm2
  BOOST_DISPATCH_IMPLEMENT  ( globalnorm_, tag::cpu_
                            , (A0)
                            , ((ast_<A0, nt2::container::domain>))
                            )
  {
    BOOST_DISPATCH_RETURNS( 1
                          , (A0 const &a0)
                          , nt2::globalnorm2(a0)
                          );
  };

  // Selects globalnorm from dynamic norm value
  BOOST_DISPATCH_IMPLEMENT  ( globalnorm_, tag::cpu_
                            , (A0)(A1)
                            , ((ast_<A0, nt2::container::domain>))
                              (scalar_<arithmetic_<A1> > )
                           )
  {
    typedef typename A0::value_type                   type_t;
    typedef typename meta::as_real<type_t>::type result_type;

    BOOST_FORCEINLINE result_type operator()(A0 const &a0, A1 a1) const
    {
      BOOST_ASSERT_MSG( (a1 > 0) || (a1 == Minf<A1>())
                      , "p must be strictly positive or infinite"
                      );

      if(a1 == Two<A1>())  return nt2::globalnorm2(a0);
      if(a1 == One<A1>())  return nt2::globalasum1(a0);
      if(a1 == Inf<A1>())  return nt2::globalmax(nt2::abs(a0));
      if(a1 == Minf<A1>()) return nt2::globalmin(nt2::abs(a0));

      return nt2::globalnormp(a0, a1);
    }
  };

  // Selects globalnorm from static norm value
  BOOST_DISPATCH_IMPLEMENT  ( globalnorm_, tag::cpu_
                            , (A0)(A1)
                            , ((ast_<A0, nt2::container::domain>))
                              (mpl_integral_< scalar_< fundamental_<A1> > >)
                            )
  {
    typedef typename A0::value_type                   type_t;
    typedef typename meta::as_real<type_t>::type result_type;

    BOOST_FORCEINLINE result_type operator()(A0 const &a0, A1 const& a1) const
    {
      BOOST_ASSERT_MSG( (A1::value > 0), "p must be strictly positive" );
      return eval(a0, a1);
    }

    template<int Value>
    BOOST_FORCEINLINE result_type eval( A0 const &a0
                                      , boost::mpl::int_<Value> const&
                                      ) const
    {
      return nt2::globalnormp(a0, Value);
    }

    BOOST_FORCEINLINE result_type eval( A0 const &a0
                                      , boost::mpl::int_<1> const&
                                      ) const
    {
      return nt2::globalasum1(a0);
    }

    BOOST_FORCEINLINE result_type eval( A0 const &a0
                                      , boost::mpl::int_<2> const&
                                      ) const
    {
      return nt2::globalnorm2(a0);
    }
  };

  // Selects globalnorm from static norm value tag
  BOOST_DISPATCH_IMPLEMENT  ( globalnorm_, tag::cpu_
                            , (A0)(A1)
                            , ((ast_<A0, nt2::container::domain>))
                              (target_<unspecified_<A1> >)
                            )
  {
    typedef typename A0::value_type                   type_t;
    typedef typename meta::as_real<type_t>::type result_type;

    BOOST_FORCEINLINE result_type operator()(A0 const &a0, A1 const&) const
    {
      return eval(a0, typename A1::type());
    }

    BOOST_FORCEINLINE result_type eval(A0 const &a0, tag::Inf const&) const
    {
      return nt2::globalmax(nt2::abs(a0));
    }

    BOOST_FORCEINLINE result_type eval(A0 const &a0, tag::Minf const&) const
    {
      return nt2::globalmin(nt2::abs(a0));
    }

    BOOST_FORCEINLINE result_type eval(A0 const &a0, tag::One const&) const
    {
      return nt2::globalasum1(a0);
    }

    BOOST_FORCEINLINE result_type eval(A0 const &a0, tag::Two const&) const
    {
      return nt2::globalnorm2(a0);
    }

    BOOST_FORCEINLINE result_type eval(A0 const &a0, tag::inf_ const&) const
    {
      return nt2::globalmax(nt2::abs(a0));
    }

    BOOST_FORCEINLINE result_type eval(A0 const &a0, tag::minf_ const&) const
    {
      return nt2::globalmin(nt2::abs(a0));
    }

    BOOST_FORCEINLINE result_type eval(A0 const &a0, tag::one_ const&) const
    {
      return nt2::globalasum1(a0);
    }

    BOOST_FORCEINLINE result_type eval(A0 const &a0, tag::two_ const&) const
    {
      return nt2::globalnorm2(a0);
    }

    BOOST_FORCEINLINE result_type eval(A0 const &a0, tag::fro_ const&) const
    {
      return nt2::globalnorm2(a0);
    }

    template<typename Tag>
    BOOST_FORCEINLINE result_type eval(A0 const &a0, Tag const&) const
    {
      // outside of Inf,  Minf,  One and Two we get back to the dynamic normp
      typename boost::dispatch::make_functor<Tag,A0>::type callee;
      type_t value = callee( nt2::meta::as_<type_t>() );

      BOOST_ASSERT_MSG( value > 0, "Norm value must be strictly positive" );
      return  globalnormp(a0, value);
    }
  };
} }

#endif


#ifndef _DOUGLAS_PEUCKER_H_
#define _DOUGLAS_PEUCKER_H_

#include <Eigen/Eigen>
#include <vector>

/**
 * This class is used to simpified a curve containing numerous points,applying the
 * Ramer-Douglas-Peucker Algorithm.
 *
 * Usage : Create a instance of this class, then call the simplifiy() function
 * Input : Points of a curve, and epsilon
 * Output: Points of the simplified curve
 */
class RDPCurveSimplifier
{
public:
  RDPCurveSimplifier()
  {
  }

  void simplify(std::vector<Eigen::Vector3d> &curve, float epsilon);

private:
  void getMostDistantPoint(std::vector<Eigen::Vector3d> segment, int &point_id, float &distance);

  void breakSegment(std::vector<Eigen::Vector3d> segment, int point_id,
                    std::vector<Eigen::Vector3d> &part1, std::vector<Eigen::Vector3d> &part2);

  bool allSegmentSimplified(std::vector<std::vector<Eigen::Vector3d>> segments);

  void getDistanceToLine(Eigen::Vector3d front, Eigen::Vector3d back, Eigen::Vector3d point,
                         float &distance);
};

void RDPCurveSimplifier::simplify(std::vector<Eigen::Vector3d> &curve, float epsilon)
{
  // create a vector containing the simplified segments
  std::vector<std::vector<Eigen::Vector3d>> segments;

  // At the begining, there is only one segment
  segments.push_back(curve);

  // begin to simplify the curve, applying Ramer-Douglas-Peucker Algorithm
  while(1)
  {
    // output the segment
    std::cout << "size:" << std::endl;
    for(int i= 0; i < segments.size(); i++)
    {
      std::cout << segments[i].size() << ",";
    }
    std::cout << std::endl;

    std::vector<std::vector<Eigen::Vector3d>> updated_segments;

    // simplify each segment
    for(int i= 0; i < segments.size(); i++)
    {
      // get one segment
      std::vector<Eigen::Vector3d> sgm= segments[i];
      if(sgm.size() == 2)
      {
        updated_segments.push_back(sgm);
        continue;
      }

      // get the most distant point's id and distance in this segment
      float dist= -1.0;
      int pt_id= -1;
      getMostDistantPoint(sgm, pt_id, dist);
      // if distance larger than epsilon, break the segemnt into two parts
      if(dist > epsilon)
      {
        std::vector<Eigen::Vector3d> part1, part2;
        breakSegment(sgm, pt_id, part1, part2);
        updated_segments.push_back(part1);
        updated_segments.push_back(part2);
      }
      // distance small enough, only hold front and back point
      else
      {
        std::vector<Eigen::Vector3d> front_back;
        front_back.push_back(sgm.front());
        front_back.push_back(sgm.back());
        updated_segments.push_back(front_back);
      }
    }

    // after one iteration, reset the segments
    segments= updated_segments;
    updated_segments.clear();

    // if all segments are simplified, stop the iteration, else the iteration continue
    if(allSegmentSimplified(segments))
      break;
  }

  // finally reset points in curve
  curve.clear();
  for(int i= 0; i < segments.size(); i++)
  {
    curve.insert(curve.end(), segments[i].begin(), segments[i].end());
  }
}

void RDPCurveSimplifier::getMostDistantPoint(std::vector<Eigen::Vector3d> segment, int &point_id,
                                             float &distance)
{
  Eigen::Vector3d front= segment.front();
  Eigen::Vector3d back= segment.back();

  point_id= -1;
  distance= -10.0;
  for(int i= 1; i < segment.size() - 1; i++)
  {
    float dist= 0;
    getDistanceToLine(front, back, segment[i], dist);
    if(dist > distance)
    {
      distance= dist;
      point_id= i;
    }
  }
}

void RDPCurveSimplifier::breakSegment(std::vector<Eigen::Vector3d> segment, int point_id,
                                      std::vector<Eigen::Vector3d> &part1,
                                      std::vector<Eigen::Vector3d> &part2)
{
  part1.insert(part1.begin(), segment.begin(), segment.begin() + point_id + 1);
  part2.insert(part2.begin(), segment.begin() + point_id, segment.end());
}

bool RDPCurveSimplifier::allSegmentSimplified(std::vector<std::vector<Eigen::Vector3d>> segments)
{
  for(int i= 0; i < segments.size(); i++)
  {
    if(segments[i].size() != 2)
    {
      return false;
    }
  }

  return true;
}

void RDPCurveSimplifier::getDistanceToLine(Eigen::Vector3d front, Eigen::Vector3d back,
                                           Eigen::Vector3d point, float &distance)
{
  Eigen::Vector4d line_dir(back(0) - front(0), back(1) - front(1), back(2) - front(2), 0);
  line_dir.normalize();
  Eigen::Vector4d p2p(point(0) - front(0), point(1) - front(1), point(2) - front(2), 0);

  distance= p2p.cross3(line_dir).squaredNorm();
  distance= sqrt(distance);
}

#endif


/*******************************************************************************
 *         Copyright 2003 & onward LASMEA UMR 6602 CNRS/Univ. Clermont II
 *         Copyright 2009 & onward LRI    UMR 8623 CNRS/Univ Paris Sud XI
 *
 *          Distributed under the Boost Software License, Version 1.0.
 *                 See accompanying file LICENSE.txt or copy at
 *                     http://www.boost.org/LICENSE_1_0.txt
 ******************************************************************************/
#ifndef NT2_SDK_MEMORY_META_ALIGN_ON_HPP_INCLUDED
#define NT2_SDK_MEMORY_META_ALIGN_ON_HPP_INCLUDED

#include <cstddef>
#include <boost/mpl/size_t.hpp>
#include <boost/mpl/integral_c.hpp>
#include <nt2/sdk/memory/parameters.hpp>
#include <nt2/sdk/error/static_assert.hpp>
#include <nt2/sdk/memory/meta/is_power_of_2.hpp>

namespace nt2 { namespace meta
{
  //////////////////////////////////////////////////////////////////////////////
  // Compute an aligned value of an integral constant on a power of 2 boundary.
  // Documentation: align_on_c.rst
  //////////////////////////////////////////////////////////////////////////////
  template<std::size_t V, std::size_t N = NT2_CONFIG_ALIGNMENT>
  struct align_on_c
       : boost::mpl::integral_c< std::size_t
                               , (V+N-1) & ~(N-1)
                               >
  {
    NT2_STATIC_ASSERT ( (meta::is_power_of_2_c<N>::value)
                      , INVALID_ALIGNMENT_VALUE
                      , "Alignment done on a non-power of two boundary."
                      );
  };

  //////////////////////////////////////////////////////////////////////////////
  // Compute an aligned value of an Integral Constant on a power of 2 boundary.
  // Documentation: align_on.rst
  //////////////////////////////////////////////////////////////////////////////
  template<class V, class N = boost::mpl::size_t<NT2_CONFIG_ALIGNMENT> >
  struct align_on
       : boost::mpl::integral_c< typename V::value_type
                               , align_on_c<V::value,N::value>::value
                               >
  {};
} }

#endif


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

  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/function/ldexp.hpp>
#include <boost/simd/function/std.hpp>
#include <boost/simd/function/fast.hpp>
#include <boost/simd/pack.hpp>
#include <simd_test.hpp>
#include <boost/simd/detail/dispatch/meta/as_integer.hpp>

namespace bs = boost::simd;
namespace bd = boost::dispatch;

template <typename T, std::size_t N, typename Env>
void test(Env& $)
{
  using iT = bd::as_integer_t<T>;
  using p_t = bs::pack<T, N>;
  using pi_t = bs::pack<iT, N>;

  T a1[N],  b[N], c[N], d[N];
  iT a2[N];
  for(std::size_t i = 0; i < N; ++i)
  {
    a1[i] = (i%2) ? T(i) : T(-1.*i);
    a2[i] = i%(sizeof(T)*8-1);
    b[i] = bs::ldexp(a1[i], a2[i]);
    c[i] = bs::std_(bs::ldexp)(a1[i], a2[i]);
    d[i] = bs::fast_(bs::ldexp)(a1[i], a2[i]);
  }

  p_t aa1(&a1[0], &a1[0]+N);
  pi_t aa2(&a2[0], &a2[0]+N);
  p_t bb(&b[0], &b[0]+N);
  p_t cc(&c[0], &c[0]+N);
  p_t dd(&d[0], &d[0]+N);

  STF_IEEE_EQUAL(bs::ldexp(aa1, aa2)            , bb);
  STF_IEEE_EQUAL(bs::std_(bs::ldexp)(aa1, aa2)  , cc);
  STF_IEEE_EQUAL(bs::fast_(bs::ldexp)(aa1, aa2) , dd);
}

STF_CASE_TPL("Check ldexp on pack" , STF_NUMERIC_TYPES)
{
  static const std::size_t N = bs::pack<T>::static_size;
  test<T, N>($);
  test<T, N/2>($);
  test<T, N*2>($);
}

template <typename T, std::size_t N, typename Env>
void tests(Env& $)
{
  using p_t = bs::pack<T, N>;

  T a1[N],  b[N], c[N];
  for(std::size_t i = 0; i < N; ++i)
  {
    a1[i] = (i%2) ? T(i) : T(-1.*i);
    b[i] = bs::ldexp(a1[i], 2);
    c[i] = bs::fast_(bs::ldexp)(a1[i], 2);
  }

  p_t aa1(&a1[0], &a1[0]+N);
  p_t bb(&b[0], &b[0]+N);
  p_t cc(&c[0], &c[0]+N);

  STF_IEEE_EQUAL(bs::ldexp(aa1, 2)            , bb);
  STF_IEEE_EQUAL(bs::fast_(bs::ldexp)(aa1, 2) , cc);
}

STF_CASE_TPL("Check ldexp on pack/scalar" , STF_NUMERIC_TYPES)
{
  static const std::size_t N = bs::pack<T>::static_size;
  tests<T, N>($);
  tests<T, N/2>($);
  tests<T, N*2>($);
}


#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/compute_average_spacing.h>
#include <CGAL/IO/read_points.h>

#include <vector>
#include <fstream>
#include <boost/tuple/tuple.hpp>

// Types
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::FT FT;
typedef Kernel::Point_3 Point;

// Data type := index, followed by the point, followed by three integers that
// define the Red Green Blue color of the point.
typedef boost::tuple<int, Point, int, int, int> IndexedPointWithColorTuple;

// Concurrency
typedef CGAL::Parallel_if_available_tag Concurrency_tag;

int main(int argc, char*argv[])
{
  const std::string fname = (argc>1)?argv[1]:CGAL::data_file_path("points_3/sphere_20k.xyz");

  // Reads a file in points.
  // As the point is the second element of the tuple (that is with index 1)
  // we use a property map that accesses the 1st element of the tuple.

  std::vector<IndexedPointWithColorTuple> points;
  if (!CGAL::IO::read_points(fname, std::back_inserter(points),
                             CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<1, IndexedPointWithColorTuple>())))
  {
    std::cerr << "Error: cannot read file " << fname << std::endl;
    return EXIT_FAILURE;
  }

  // Initialize index and RGB color fields in tuple.
  // As the index and RGB color are respectively the first and third-fifth elements
  // of the tuple we use a get function from the property map that accesses the 0
  // and 2-4th elements of the tuple.
  for(unsigned int i = 0; i < points.size(); i++)
  {
    points[i].get<0>() = i; // set index value of tuple to i

    points[i].get<2>() = 0; // set RGB color to black
    points[i].get<3>() = 0;
    points[i].get<4>() = 0;
  }

  // Computes average spacing.
  const unsigned int nb_neighbors = 6; // 1 ring
  FT average_spacing = CGAL::compute_average_spacing<Concurrency_tag>(
                         points, nb_neighbors,
                         CGAL::parameters::point_map(CGAL::Nth_of_tuple_property_map<1,IndexedPointWithColorTuple>()));

  std::cout << "Average spacing: " << average_spacing << std::endl;

  return EXIT_SUCCESS;
}


/*
 * Copyright Andrey Semashev 2020
 *
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or copy at
 * https://www.boost.org/LICENSE_1_0.txt)
 */
/*!
 * \file countr_zero.hpp
 *
 * This header defines \c countr_zero algorithm, which counts the number of consecutive
 * least significant zero bits in an integer.
 */

#ifndef BOOST_BIT_OPS_COUNTING_COUNTR_ZERO_HPP_INCLUDED_
#define BOOST_BIT_OPS_COUNTING_COUNTR_ZERO_HPP_INCLUDED_

#include <limits>
#include <boost/bit_ops/detail/config.hpp>
#include <boost/bit_ops/detail/int_sizes.hpp>
#include <boost/bit_ops/detail/type_traits/enable_if.hpp>
#include <boost/bit_ops/detail/type_traits/integral_constant.hpp>
#include <boost/bit_ops/detail/type_traits/is_integral.hpp>
#include <boost/bit_ops/detail/type_traits/is_unsigned.hpp>

#if defined(_MSC_VER)
#include <intrin.h>
#endif

namespace boost {
namespace bit_ops {

namespace detail {

#if defined(BOOST_BIT_OPS_DETAIL_HAS_BUILTIN_CTZ)

template< typename T, unsigned int N >
inline typename bit_ops::detail::enable_if< N <= BOOST_BIT_OPS_DETAIL_SIZEOF_INT, unsigned int >::type countr_zero_nz(T value, bit_ops::detail::integral_constant< unsigned int, N >) BOOST_NOEXCEPT
{
    return static_cast< unsigned int >(__builtin_ctz(value));
}

#if BOOST_BIT_OPS_DETAIL_SIZEOF_LONG > BOOST_BIT_OPS_DETAIL_SIZEOF_INT

template< typename T, unsigned int N >
inline typename bit_ops::detail::enable_if< N == BOOST_BIT_OPS_DETAIL_SIZEOF_LONG, unsigned int >::type countr_zero_nz(T value, bit_ops::detail::integral_constant< unsigned int, N >) BOOST_NOEXCEPT
{
    return static_cast< unsigned int >(__builtin_ctzl(value));
}

#endif // BOOST_BIT_OPS_DETAIL_SIZEOF_LONG > BOOST_BIT_OPS_DETAIL_SIZEOF_INT

#if BOOST_BIT_OPS_DETAIL_SIZEOF_LLONG > BOOST_BIT_OPS_DETAIL_SIZEOF_LONG

template< typename T, unsigned int N >
inline typename bit_ops::detail::enable_if< N == BOOST_BIT_OPS_DETAIL_SIZEOF_LLONG, unsigned int >::type countr_zero_nz(T value, bit_ops::detail::integral_constant< unsigned int, N >) BOOST_NOEXCEPT
{
    return static_cast< unsigned int >(__builtin_ctzll(value));
}

#endif // BOOST_BIT_OPS_DETAIL_SIZEOF_LLONG > BOOST_BIT_OPS_DETAIL_SIZEOF_LONG

#elif defined(BOOST_BIT_OPS_DETAIL_HAS_BIT_SCAN_FORWARD)

template< typename T, unsigned int N >
inline typename bit_ops::detail::enable_if< N <= BOOST_BIT_OPS_DETAIL_SIZEOF_LONG, unsigned int >::type countr_zero_nz(T value, bit_ops::detail::integral_constant< unsigned int, N >) BOOST_NOEXCEPT
{
    unsigned long pos;
    _BitScanForward(&pos, value);
    return static_cast< unsigned int >(pos);
}

#if BOOST_BIT_OPS_DETAIL_SIZEOF_LLONG > BOOST_BIT_OPS_DETAIL_SIZEOF_LONG

template< typename T, unsigned int N >
inline typename bit_ops::detail::enable_if< N == BOOST_BIT_OPS_DETAIL_SIZEOF_LLONG, unsigned int >::type countr_zero_nz(T value, bit_ops::detail::integral_constant< unsigned int, N >) BOOST_NOEXCEPT
{
#if defined(_M_AMD64) || defined(_M_ARM64)
    unsigned long pos;
    _BitScanForward64(&pos, value);
    return static_cast< unsigned int >(pos);
#else
    unsigned long pos;
    if (!_BitScanForward(&pos, static_cast< unsigned int >(value)))
        _BitScanForward(&pos, static_cast< unsigned int >(value >> 32u));
    return static_cast< unsigned int >(pos);
#endif
}

#endif // BOOST_BIT_OPS_DETAIL_SIZEOF_LLONG > BOOST_BIT_OPS_DETAIL_SIZEOF_LONG

#else

template< typename T, unsigned int N >
inline unsigned int countr_zero_nz(T value, bit_ops::detail::integral_constant< unsigned int, N >) BOOST_NOEXCEPT
{
    unsigned int count = 0u;
    unsigned int digits = std::numeric_limits< T >::digits / 2u;
    T mask = (static_cast< T >(1u) << digits) - 1u;
    for (; digits > 0u; digits >>= 1u, mask >>= digits)
    {
        if ((value & mask) == 0u)
        {
            count += digits;
            value >>= digits;
        }
    }

    return count;
}

#endif

#if defined(BOOST_BIT_OPS_DETAIL_HAS_TZCNT_U32)

#if defined(BOOST_BIT_OPS_DETAIL_HAS_TZCNT_U16)

template< typename T, unsigned int N >
inline typename bit_ops::detail::enable_if< N <= 2u, unsigned int >::type countr_zero(T value, bit_ops::detail::integral_constant< unsigned int, N >) BOOST_NOEXCEPT
{
    // Note: gcc provides the intrinsic with two double underscores
    return static_cast< unsigned int >(__tzcnt_u16(value) - (16u - std::numeric_limits< T >::digits));
}

template< typename T, unsigned int N >
inline typename bit_ops::detail::enable_if< N == 4u, unsigned int >::type countr_zero(T value, bit_ops::detail::integral_constant< unsigned int, N >) BOOST_NOEXCEPT
{
    return static_cast< unsigned int >(_tzcnt_u32(value));
}

#else // defined(BOOST_BIT_OPS_DETAIL_HAS_TZCNT_U16)

template< typename T, unsigned int N >
inline typename bit_ops::detail::enable_if< N <= 4u, unsigned int >::type countr_zero(T value, bit_ops::detail::integral_constant< unsigned int, N >) BOOST_NOEXCEPT
{
    return static_cast< unsigned int >(_tzcnt_u32(value) - (32u - std::numeric_limits< T >::digits));
}

#endif // defined(BOOST_BIT_OPS_DETAIL_HAS_TZCNT_U16)

template< typename T, unsigned int N >
inline typename bit_ops::detail::enable_if< N == 8u, unsigned int >::type countr_zero(T value, bit_ops::detail::integral_constant< unsigned int, N >) BOOST_NOEXCEPT
{
#if defined(BOOST_BIT_OPS_DETAIL_HAS_TZCNT_U64)
    return static_cast< unsigned int >(_tzcnt_u64(value));
#else
    unsigned int count = _tzcnt_u32(static_cast< unsigned int >(value));
    if (count == 32u)
        count += _tzcnt_u32(static_cast< unsigned int >(value >> 32u));
    return count;
#endif // defined(BOOST_BIT_OPS_DETAIL_HAS_TZCNT_U64)
}

#endif // defined(BOOST_BIT_OPS_DETAIL_HAS_TZCNT_U32)

} // namespace detail

/*!
 * \brief Returns the number of consecutive least significant zero bits in \a value
 *
 * \pre \a value must not be zero
 */
template< typename T >
inline typename bit_ops::detail::enable_if<
    bit_ops::detail::is_integral< T >::value && bit_ops::detail::is_unsigned< T >::value,
    unsigned int
>::type countr_zero_nz(T value) BOOST_NOEXCEPT
{
    return bit_ops::detail::countr_zero_nz(value, bit_ops::detail::integral_constant< unsigned int, sizeof(T) >());
}

//! Returns the number of consecutive least significant zero bits in \a value
template< typename T >
inline typename bit_ops::detail::enable_if<
    bit_ops::detail::is_integral< T >::value && bit_ops::detail::is_unsigned< T >::value,
    unsigned int
>::type countr_zero(T value) BOOST_NOEXCEPT
{
#if defined(BOOST_BIT_OPS_DETAIL_HAS_TZCNT_U32)
    return bit_ops::detail::countr_zero(value, bit_ops::detail::integral_constant< unsigned int, sizeof(T) >());
#else
    return value == 0u ? static_cast< unsigned int >(std::numeric_limits< T >::digits) : bit_ops::countr_zero_nz(value);
#endif
}

} // namespace bit_ops
} // namespace boost

#endif // BOOST_BIT_OPS_COUNTING_COUNTR_ZERO_HPP_INCLUDED_


#include <glm/models/links/power_odds.hpp>

#include <armadillo>

using namespace arma;

power_odds_link::power_odds_link(float lambda)
    : glm_link::glm_link( "power_odds" ),
      m_lambda( lambda )
{
}

vec
power_odds_link::init_beta(const mat &X, const vec &y) const
{

    return arma::vec( 0.0 );
}

vec
power_odds_link::mu(const arma::vec &eta) const
{
    if( m_lambda == 0.0 )
    {
        return 1.0 / ( 1 + exp( -eta ) );
    }
    else
    {
        return 1.0 / ( 1 + pow( 1 + m_lambda * eta, -1 / m_lambda ) );
    }
}

vec
power_odds_link::eta(const arma::vec &mu) const
{
    if( m_lambda == 0.0 )
    {
        return log( mu / ( 1 - mu ) );
    }
    else
    {
        return ( pow( mu / ( 1 - mu ), m_lambda ) - 1 ) / m_lambda;
    }
}

vec
power_odds_link::mu_eta(const arma::vec &mu) const
{
    if( m_lambda == 0.0 )
    {
        return 1.0 / ( mu % ( 1 - mu ) );
    }
    else
    {
        return pow( mu / (1 - mu), m_lambda ) / ( mu % ( 1 - mu ) );
    }
}


// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2018 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

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

#include <boost/bind.hpp>

#ifndef __clang__
#include <ext/numeric>
using __gnu_cxx::power;
#else
template <class Value>
Value power(Value value, int n)
{
    return pow(value, n);
}
#endif

#include <boost/math/special_functions/gamma.hpp>
#include <boost/math/special_functions/hypot.hpp>
#include <boost/math/constants/constants.hpp>

#include <boost/graph/fruchterman_reingold.hpp>

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

namespace graph_tool
{
// convert point types
template <class Val>
struct convert<vector<Val>, typename convex_topology<2>::point>
{
    vector<Val> operator()(const typename convex_topology<2>::point& p) const
    {
        vector<Val> v(2);
        for (size_t i = 0; i < 2; ++i)
            v[i] = p[i];
        return v;
    }
};

template <class Val>
struct convert<typename convex_topology<2>::point, vector<Val> >
{
    typename convex_topology<2>::point operator()(const vector<Val>& v) const
    {
        typename convex_topology<2>::point p;
        for (size_t i = 0; i < min(size_t(2), v.size()); ++i)
            p[i] = v[i];
        return p;
    }
};
} // graph_tool namespace

template<class T>
struct anneal_cooling
{
    typedef T result_type;

    anneal_cooling(T ti, T tf, std::size_t iterations)
        : _ti(ti), _tf(tf), _iter(0), _n_iter(iterations)
    {
        _beta = (log(_tf) - log(_ti)) / _n_iter;
    }

    T operator()()
    {
        T temp = _ti * exp(T(_iter) * _beta);
        ++_iter;
        if (_iter == _n_iter)
            temp = 0;
        return temp;
    }

private:
    T _ti, _tf;
    size_t _iter;
    size_t _n_iter;
    T _beta;
};

template <class Topology>
struct get_layout
{
    template <class WeightMap, class Value>
    struct attr_force
    {
        attr_force(WeightMap w, Value a): _w(w), _a(a) {}
        WeightMap _w;
        Value _a;

        template <class Graph, class Edge, class KVal, class DVal>
        Value operator()(Edge e, KVal k, DVal dist, const Graph&) const
        {
            return _a * get(_w, e) * power(dist, 2) / k;
        }
    };

    template <class Value>
    struct rep_force
    {
        rep_force(Value r): _r(r){}
        Value _r;

        template <class Graph, class Vertex, class KVal, class DVal>
        Value operator()(Vertex, Vertex, KVal k, DVal dist, const Graph&) const
        {
            return _r * power(k, 2) / dist;
        }
    };

    template <class Graph, class PosMap, class WeightMap>
    void operator()(Graph& g, PosMap pos, WeightMap weight,
                    pair<double, double> f, double scale, bool grid,
                    pair<double, double> temp, size_t n_iter) const
    {
        typedef typename property_traits<PosMap>::value_type::value_type pos_t;
        anneal_cooling<pos_t> cool(temp.first, temp.second, n_iter);
        attr_force<WeightMap, pos_t> af(weight, f.first);
        rep_force<pos_t> rf(f.second);
        Topology topology(scale);
        ConvertedPropertyMap<PosMap, typename Topology::point_type> cpos(pos);
        if (grid)
            fruchterman_reingold_force_directed_layout
                (g, cpos,
                 topology,
                 attractive_force(af).
                 repulsive_force(rf).
                 cooling(cool));
        else
            fruchterman_reingold_force_directed_layout
                (g, cpos,
                 topology,
                 attractive_force(af).
                 repulsive_force(rf).
                 cooling(cool).
                 force_pairs(all_force_pairs()));
    }
};

void fruchterman_reingold_layout(GraphInterface& g, boost::any pos,
                                 boost::any weight, double a, double r,
                                 bool square, double scale, bool grid,
                                 double ti, double tf, size_t max_iter)
{
    typedef UnityPropertyMap<int,GraphInterface::edge_t> weight_map_t;
    typedef boost::mpl::push_back<edge_scalar_properties, weight_map_t>::type
        edge_props_t;

    if(weight.empty())
        weight = weight_map_t();
    if (square)
        run_action<graph_tool::detail::never_directed>()
            (g,
             std::bind(get_layout<square_topology<> >(), std::placeholders::_1,
                       std::placeholders::_2, std::placeholders::_3, make_pair(a, r), scale,
                       grid, make_pair(ti, tf), max_iter),
             vertex_floating_vector_properties(), edge_props_t())
            (pos, weight);
    else
        run_action<graph_tool::detail::never_directed>()
            (g,
             std::bind(get_layout<circle_topology<> >(), std::placeholders::_1,
                       std::placeholders::_2, std::placeholders::_3, make_pair(a, r),
                       scale, grid, make_pair(ti, tf), max_iter),
             vertex_floating_vector_properties(), edge_props_t()) (pos, weight);
}

#include <boost/python.hpp>

void export_fruchterman_reingold()
{
    python::def("fruchterman_reingold_layout", &fruchterman_reingold_layout);
}


/*

Copyright (c) 2007-2016, Un Shyam, Arvid Norberg, Steven Siloti
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 the author 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.

*/

#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)

#include <cstdint>
#include <algorithm>
#include <random>

#include "libtorrent/aux_/disable_warnings_push.hpp"

#include <boost/multiprecision/integer.hpp>
#include <boost/multiprecision/cpp_int.hpp>

// for backwards compatibility with boost < 1.60 which was before export_bits
// and import_bits were introduced
#if BOOST_VERSION < 106000
#include "libtorrent/aux_/cppint_import_export.hpp"
#endif

#include "libtorrent/aux_/disable_warnings_pop.hpp"

#include "libtorrent/random.hpp"
#include "libtorrent/alloca.hpp"
#include "libtorrent/pe_crypto.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/assert.hpp"
#include "libtorrent/span.hpp"

namespace libtorrent
{
	namespace mp = boost::multiprecision;

	namespace {
		// TODO: it would be nice to get the literal working
		key_t const dh_prime
			("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563");
	}

	std::array<char, 96> export_key(key_t const& k)
	{
		std::array<char, 96> ret;
		std::uint8_t* begin = reinterpret_cast<std::uint8_t*>(ret.data());
		std::uint8_t* end = mp::export_bits(k, begin, 8);

		// TODO: it would be nice to be able to export to a fixed width field, so
		// we wouldn't have to shift it later
		if (end < begin + 96)
		{
			int const len = end - begin;
			std::memmove(begin + 96 - len, begin, len);
			std::memset(begin, 0, 96 - len);
		}
		return ret;
	}

	void rc4_init(const unsigned char* in, unsigned long len, rc4 *state);
	unsigned long rc4_encrypt(unsigned char *out, unsigned long outlen, rc4 *state);

	// Set the prime P and the generator, generate local public key
	dh_key_exchange::dh_key_exchange()
	{
		std::array<std::uint8_t, 96> random_key;
		for (auto& i : random_key) i = random(0xff);

		// create local key (random)
		mp::import_bits(m_dh_local_secret, random_key.begin(), random_key.end());

		// key = (2 ^ secret) % prime
		m_dh_local_key = mp::powm(key_t(2), m_dh_local_secret, dh_prime);
	}

	// compute shared secret given remote public key
	void dh_key_exchange::compute_secret(std::uint8_t const* remote_pubkey)
	{
		TORRENT_ASSERT(remote_pubkey);
		key_t key;
		mp::import_bits(key, remote_pubkey, remote_pubkey + 96);
		compute_secret(key);
	}

	void dh_key_exchange::compute_secret(key_t const& remote_pubkey)
	{
		// shared_secret = (remote_pubkey ^ local_secret) % prime
		m_dh_shared_secret = mp::powm(remote_pubkey, m_dh_local_secret, dh_prime);

		std::array<char, 96> buffer;
		mp::export_bits(m_dh_shared_secret, reinterpret_cast<std::uint8_t*>(buffer.data()), 8);

		static char const req3[4] = {'r', 'e', 'q', '3'};
		// calculate the xor mask for the obfuscated hash
		m_xor_mask = hasher(req3).update(buffer).final();
	}

	std::tuple<int, span<span<char const>>>
	encryption_handler::encrypt(
		span<span<char>> iovec)
	{
		TORRENT_ASSERT(!m_send_barriers.empty());
		TORRENT_ASSERT(m_send_barriers.front().enc_handler);

		int to_process = m_send_barriers.front().next;

		span<char>* bufs;
		size_t num_bufs;
		bool need_destruct = false;
		if (to_process != INT_MAX)
		{
			bufs = TORRENT_ALLOCA(span<char>, iovec.size());
			need_destruct = true;
			num_bufs = 0;
			for (int i = 0; to_process > 0 && i < iovec.size(); ++i)
			{
				++num_bufs;
				int const size = int(iovec[i].size());
				if (to_process < size)
				{
					new (&bufs[i]) span<char>(
						iovec[i].data(), to_process);
					to_process = 0;
				}
				else
				{
					new (&bufs[i]) span<char>(iovec[i]);
					to_process -= size;
				}
			}
		}
		else
		{
			bufs = iovec.data();
			num_bufs = iovec.size();
		}

		int next_barrier = 0;
		span<span<char const>> out_iovec;
		if (num_bufs != 0)
		{
			std::tie(next_barrier, out_iovec)
				= m_send_barriers.front().enc_handler->encrypt({bufs, size_t(num_bufs)});
		}

		if (m_send_barriers.front().next != INT_MAX)
		{
			// to_process holds the difference between the size of the buffers
			// and the bytes left to the next barrier
			// if it's zero then pop the barrier
			// otherwise update the number of bytes remaining to the next barrier
			if (to_process == 0)
			{
				if (m_send_barriers.size() == 1)
				{
					// transitioning back to plaintext
					next_barrier = INT_MAX;
				}
				m_send_barriers.pop_front();
			}
			else
			{
				m_send_barriers.front().next = to_process;
			}
		}

#if TORRENT_USE_ASSERTS
		if (next_barrier != INT_MAX && next_barrier != 0)
		{
			int payload = 0;
			for (int i = 0; i < num_bufs; ++i)
				payload += int(bufs[i].size());

			int overhead = 0;
			for (auto buf : out_iovec)
				overhead += int(buf.size());
			TORRENT_ASSERT(overhead + payload == next_barrier);
		}
#endif
		if (need_destruct)
		{
			for (int i = 0; i < num_bufs; ++i)
				bufs[i].~span<char>();
		}
		return std::make_tuple(next_barrier, out_iovec);
	}

	int encryption_handler::decrypt(crypto_receive_buffer& recv_buffer
		, std::size_t& bytes_transferred)
	{
		TORRENT_ASSERT(!is_recv_plaintext());
		int consume = 0;
		if (recv_buffer.crypto_packet_finished())
		{
			span<char> wr_buf = recv_buffer.mutable_buffer(bytes_transferred);
			int produce = 0;
			int packet_size = 0;
			std::tie(consume, produce, packet_size) = m_dec_handler->decrypt(wr_buf);
			TORRENT_ASSERT(packet_size || produce);
			TORRENT_ASSERT(packet_size >= 0);
			bytes_transferred = produce;
			if (packet_size)
				recv_buffer.crypto_cut(consume, packet_size);
		}
		else
			bytes_transferred = 0;
		return consume;
	}

	bool encryption_handler::switch_send_crypto(std::shared_ptr<crypto_plugin> crypto
		, int pending_encryption)
	{
		bool place_barrier = false;
		if (!m_send_barriers.empty())
		{
			std::list<barrier>::iterator end = m_send_barriers.end(); --end;
			for (std::list<barrier>::iterator b = m_send_barriers.begin();
				b != end; ++b)
				pending_encryption -= b->next;
			TORRENT_ASSERT(pending_encryption >= 0);
			m_send_barriers.back().next = pending_encryption;
		}
		else if (crypto)
			place_barrier = true;

		if (crypto)
			m_send_barriers.push_back(barrier(crypto, INT_MAX));

		return place_barrier;
	}

	void encryption_handler::switch_recv_crypto(std::shared_ptr<crypto_plugin> crypto
		, crypto_receive_buffer& recv_buffer)
	{
		m_dec_handler = crypto;
		int packet_size = 0;
		if (crypto)
		{
			int consume = 0;
			int produce = 0;
			std::vector<span<char>> wr_buf;
			std::tie(consume, produce, packet_size) = crypto->decrypt(wr_buf);
			TORRENT_ASSERT(wr_buf.empty());
			TORRENT_ASSERT(consume == 0);
			TORRENT_ASSERT(produce == 0);
		}
		recv_buffer.crypto_reset(packet_size);
	}

	rc4_handler::rc4_handler()
		: m_encrypt(false)
		, m_decrypt(false)
	{
		m_rc4_incoming.x = 0;
		m_rc4_incoming.y = 0;
		m_rc4_outgoing.x = 0;
		m_rc4_outgoing.y = 0;
	}

	void rc4_handler::set_incoming_key(span<char const> key)
	{
		m_decrypt = true;
		rc4_init(reinterpret_cast<unsigned char const*>(key.data())
			, key.size(), &m_rc4_incoming);
		// Discard first 1024 bytes
		char buf[1024];
		span<char> vec(buf, sizeof(buf));
		decrypt(vec);
	}

	void rc4_handler::set_outgoing_key(span<char const> key)
	{
		m_encrypt = true;
		rc4_init(reinterpret_cast<unsigned char const*>(key.data())
			, key.size(), &m_rc4_outgoing);
		// Discard first 1024 bytes
		char buf[1024];
		span<char> vec(buf, sizeof(buf));
		encrypt(vec);
	}

	std::tuple<int, span<span<char const>>>
	rc4_handler::encrypt(span<span<char>> bufs)
	{
		span<span<char const>> empty;
		if (!m_encrypt) return std::make_tuple(0, empty);
		if (bufs.size() == 0) return std::make_tuple(0, empty);

		int bytes_processed = 0;
		for (auto& buf : bufs)
		{
			unsigned char* const pos = reinterpret_cast<unsigned char*>(buf.data());
			int const len = int(buf.size());

			TORRENT_ASSERT(len >= 0);
			TORRENT_ASSERT(pos);

			bytes_processed += len;
			rc4_encrypt(pos, len, &m_rc4_outgoing);
		}
		return std::make_tuple(bytes_processed, empty);
	}

	std::tuple<int, int, int> rc4_handler::decrypt(span<span<char>> bufs)
	{
		if (!m_decrypt) std::make_tuple(0, 0, 0);

		int bytes_processed = 0;
		for (auto& buf : bufs)
		{
			unsigned char* const pos = reinterpret_cast<unsigned char*>(buf.data());
			int const len = int(buf.size());

			TORRENT_ASSERT(len >= 0);
			TORRENT_ASSERT(pos);

			bytes_processed += len;
			rc4_encrypt(pos, len, &m_rc4_incoming);
		}
		return std::make_tuple(0, bytes_processed, 0);
	}

// All this code is based on libTomCrypt (http://www.libtomcrypt.com/)
// this library is public domain and has been specially
// tailored for libtorrent by Arvid Norberg

void rc4_init(const unsigned char* in, unsigned long len, rc4 *state)
{
	size_t const key_size = sizeof(state->buf);
	unsigned char key[key_size], tmp, *s;
	int keylen, x, y, j;

	TORRENT_ASSERT(state != nullptr);
	TORRENT_ASSERT(len <= key_size);
	if (len > key_size) len = key_size;

	state->x = 0;
	while (len--) {
		state->buf[state->x++] = *in++;
	}

	/* extract the key */
	s = state->buf.data();
	std::memcpy(key, s, key_size);
	keylen = state->x;

	/* make RC4 perm and shuffle */
	for (x = 0; x < key_size; ++x) {
		s[x] = x;
	}

	for (j = x = y = 0; x < key_size; x++) {
		y = (y + state->buf[x] + key[j++]) & 255;
		if (j == keylen) {
			j = 0;
		}
		tmp = s[x]; s[x] = s[y]; s[y] = tmp;
	}
	state->x = 0;
	state->y = 0;
}

unsigned long rc4_encrypt(unsigned char *out, unsigned long outlen, rc4 *state)
{
	unsigned char x, y, *s, tmp;
	unsigned long n;

	TORRENT_ASSERT(out != nullptr);
	TORRENT_ASSERT(state != nullptr);

	n = outlen;
	x = state->x;
	y = state->y;
	s = state->buf.data();
	while (outlen--) {
		x = (x + 1) & 255;
		y = (y + s[x]) & 255;
		tmp = s[x]; s[x] = s[y]; s[y] = tmp;
		tmp = (s[x] + s[y]) & 255;
		*out++ ^= s[tmp];
	}
	state->x = x;
	state->y = y;
	return n;
}

} // namespace libtorrent

#endif // #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)


#include "itkImage.h"
#include "itkImageFileReader.h"
#include "vnl/vnl_matrix_ref.h"
#include "vnl/vnl_matrix.h"
#include "tkdCmdParser.h"
#include "flens.h"
#include <cmath>
#include <boost/math/special_functions/fpclassify.hpp>

// 26-12-2009
// added to Kajo's clustercoefficient: nan-check! (for binary unconnected graphs).

class ClusterCoefficient
{
public:
	typedef float PixelType;
	typedef itk::Image< PixelType, 2 > ImageType;
	typedef vnl_matrix_ref< PixelType > DataMatrixType;
	typedef float PrecisionType;
	typedef flens::GeMatrix< flens::FullStorage< PrecisionType, flens::RowMajor > > MatrixType;
	typedef vnl_vector< PrecisionType > VectorType;

	/**
	 * Stam's algorithm ...
	 */
	void RunStam( const std::string& filename )
	{
		typedef itk::ImageFileReader< ImageType > ReaderType;
		ReaderType::Pointer reader = ReaderType::New();
		reader->SetFileName( filename.c_str() );
		reader->Update();

		ImageType::Pointer image = reader->GetOutput();
		reader = 0;

		PixelType* buffer = image->GetPixelContainer()->GetBufferPointer();
		ImageType::RegionType region = image->GetLargestPossibleRegion();
		ImageType::SizeType size = region.GetSize();
		int rows = size[0];
		int cols = size[1];
		int nans = 0;

		DataMatrixType wIn( rows, cols, buffer );

		VectorType clusteringcoefficients( rows );

		PrecisionType sum = 0;
		for ( int i = 0; i < rows; ++i )
		{
			PrecisionType numerator = 0;
			PrecisionType denominator = 0;

			for ( int k = 0; k < rows; ++k )
			{
				if ( k == i )
				{
					continue;
				}

				for ( int l = 0; l < rows; ++l )
				{
					if ( l == k || l == i )
					{
						continue;
					}

					numerator += ( wIn( i, k ) * wIn( i, l ) * wIn( k, l ) );
					denominator += ( wIn( i, k ) * wIn( i, l ) );
				}
			}

			clusteringcoefficients( i ) = numerator / denominator;

			if ( !boost::math::isnan< PixelType >( clusteringcoefficients( i ) ) )
			{
				 sum += clusteringcoefficients( i );
			}
			else
			{
				std::cout << "*** WARNING ***: nan detected!" << std::endl;
				nans++;
			}
		}

		std::cout << ( sum / static_cast< PrecisionType > ( rows - nans ) ) << std::endl;
	}

	void RunWeighted( const std::string& filename, PixelType threshold )
	{
		typedef itk::ImageFileReader< ImageType > ReaderType;
		ReaderType::Pointer reader = ReaderType::New();
		reader->SetFileName( filename.c_str() );
		reader->Update();

		ImageType::Pointer image = reader->GetOutput();
		reader = 0;

		PixelType* buffer = image->GetPixelContainer()->GetBufferPointer();
		ImageType::RegionType region = image->GetLargestPossibleRegion();
		ImageType::SizeType size = region.GetSize();
		int rows = size[0];
		int cols = size[1];

		DataMatrixType wIn( rows, cols, buffer );

		MatrixType A( rows, cols );
		MatrixType S( rows, cols );
		MatrixType B( rows, cols );
		MatrixType W( rows, cols );

		for ( int i = 0; i < rows; ++i )
		{
			for ( int j = 0; j < cols; ++j )
			{
				S( i + 1, j + 1 ) = 0;

				if ( wIn( i, j ) > threshold )
				{
					A( i + 1, j + 1 ) = 1;
					B( i + 1, j + 1 ) = vcl_pow( wIn( i, j ), 1. / 3. );
					W( i + 1, j + 1 ) = wIn( i, j );
				} else
				{
					A( i + 1, j + 1 ) = 0;
					B( i + 1, j + 1 ) = 0;
					W( i + 1, j + 1 ) = 0;
				}
			}
		}

		for ( int i = 1; i <= rows; ++i )
		{
			for ( int j = 1; j <= cols; ++j )
			{
				S( i, j ) = B( i, j ) + B( j, i );
			}
		}

		MatrixType p1( rows, cols );
		MatrixType p( rows, cols );
		MatrixType a( rows, cols );
		VectorType C( rows );
		VectorType K( rows );

		flens::copy( S * S, p1 );
		flens::copy( p1 * S, p );
		flens::copy( A * A, a );

		K.fill( 0 );
		for ( int i = 0; i < rows; ++i )
		{
			for ( int j = 0; j < cols; ++j )
			{
				K( i ) += ( A( i + 1, j + 1 ) + A( j + 1, i + 1 ) );
			}

			PrecisionType cyc = p( i + 1, i + 1 ) / 2.;
			if ( cyc == 0 )
			{
				K( i) = vcl_numeric_limits< PrecisionType>::max();
			}

			C( i ) = cyc / ( K( i ) * ( K( i ) - 1 ) - 2 * a( i + 1, i + 1) );
		}

		PrecisionType sum = 0;
		for( int i = 0; i < rows; ++i )
		{
			sum += C( i );
		}

		sum /= static_cast< PrecisionType>( rows );
		std::cout << sum << std::endl;
	}

	void Run( const std::string& filename, PixelType threshold )
	{
		typedef itk::ImageFileReader< ImageType> ReaderType;
		ReaderType::Pointer reader = ReaderType::New();
		reader->SetFileName( filename.c_str() );
		reader->Update();

		ImageType::Pointer image = reader->GetOutput();
		reader = 0;

		PixelType* buffer = image->GetPixelContainer()->GetBufferPointer();
		ImageType::RegionType region = image->GetLargestPossibleRegion();
		ImageType::SizeType size = region.GetSize();
		int rows = size[ 0 ];
		int cols = size[ 1 ];

		DataMatrixType data( rows, cols, buffer );
		for( int i = 0; i < ( rows * cols ); ++i )
		{
			buffer[ i ] *= ( buffer[ i ] < 0 ? -1. : 1. );
		}

		PixelType sum = 0;
		//
						//    std::vector< PixelType > t;
						//    for( int i = 0; i < rows; ++i )
						//      {
						//      for( int j = i + 1; j < cols; ++j )
						//        {
						//        if ( data( i, j ) > 0 )
						//          {
						//          t.push_back( data( i, j ) );
						//          }
						//        }
						//      }
						//
						//    std::sort( t.begin(), t.end() );
						//    int index = static_cast< int >( 0.8 * static_cast< float >( t.size() ) );
						//    std::cout << t[ index ] << std::endl;
						//    return;

						for( int i = 0; i < rows; ++i )
						{
							// 1) determine k = number of edges to neighbors
							int k = 0;
							std::vector< int> neighbors;
							for( int j = 0; j < cols; ++j )
							{
								if ( i == j )
								{
									continue;
								}

								if ( data( i, j ) < threshold )
								{
									continue;
								}

								++k;
								neighbors.push_back( j );
							}

							// 2) determine connections among neighbors
							int connections = 0;
							std::vector< int>::const_iterator end = neighbors.end();
							for( std::vector< int>::const_iterator j = neighbors.begin(); j != end; ++j )
							{
								for( std::vector< int>::const_iterator m = j; m != end; ++m )
								{
									if ( *j == *m || i == *j || *m == i )
									{
										continue;
									}

									if ( data( *j, *m ) < threshold )
									{
										continue;
									}

									++connections;
								}
							}

							// k(k-1)*0.5 possible connections
							if ( k> 1 )
							{
								PixelType coefficient = static_cast< PixelType>( 2 * connections ) / static_cast< PixelType>( k * ( k - 1 ) );
								sum += coefficient;
								//        std::cout << i << "\t" << coefficient << std::endl;
							}
						}

						PixelType average = sum / static_cast< PixelType>( rows );

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

int main( int argc, char ** argv )
{
	tkd::CmdParser p( "clustercoefficient", "Calculate voxel-wise cluster coefficients" );

	std::string inputFileName;
	float threshold;
	bool weightedGraph = false;
	bool stam = false;

	p.AddArgument( inputFileName, "input" ) ->AddAlias( "i" ) ->SetInput( "filename" ) ->SetDescription( "Input 2D image: adjacency matrix" ) ->SetRequired(
			true );

	p.AddArgument( threshold, "threshold" ) ->AddAlias( "t" ) ->SetInput( "float" ) ->SetDescription( "Threshold" ) ->SetRequired( true );

	p.AddArgument( weightedGraph, "weighted" ) ->AddAlias( "w" ) ->SetDescription( "Calculated weighted cluster coefficients" );

	p.AddArgument( stam, "stam" ) ->SetDescription(
			"Calculated weighted cluster coefficients similar to Stam et al. in Brain 2009 (no threshold options!)" );

	if ( !p.Parse( argc, argv ) )
	{
		p.PrintUsage( std::cout );
		return -1;
	}

	ClusterCoefficient cc;

	if ( stam )
	{
		cc.RunStam( inputFileName );
		return 0;
	}

	if ( weightedGraph )
	{
		cc.RunWeighted( inputFileName, threshold );
	} else
	{
		cc.Run( inputFileName, threshold );
	}

	return 0;
}


#ifndef STAN_MATH_PRIM_SCAL_PROB_EXP_MOD_NORMAL_LPDF_HPP
#define STAN_MATH_PRIM_SCAL_PROB_EXP_MOD_NORMAL_LPDF_HPP

#include <stan/math/prim/scal/meta/is_constant_struct.hpp>
#include <stan/math/prim/scal/meta/partials_return_type.hpp>
#include <stan/math/prim/scal/meta/operands_and_partials.hpp>
#include <stan/math/prim/scal/err/check_consistent_sizes.hpp>
#include <stan/math/prim/scal/err/check_finite.hpp>
#include <stan/math/prim/scal/err/check_not_nan.hpp>
#include <stan/math/prim/scal/err/check_positive_finite.hpp>
#include <stan/math/prim/scal/fun/size_zero.hpp>
#include <stan/math/prim/scal/fun/erfc.hpp>
#include <stan/math/prim/scal/fun/constants.hpp>
#include <stan/math/prim/scal/meta/include_summand.hpp>
#include <stan/math/prim/scal/meta/scalar_seq_view.hpp>
#include <stan/math/prim/scal/fun/value_of.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include <cmath>

namespace stan {
namespace math {

template <bool propto, typename T_y, typename T_loc, typename T_scale,
          typename T_inv_scale>
typename return_type<T_y, T_loc, T_scale, T_inv_scale>::type
exp_mod_normal_lpdf(const T_y& y, const T_loc& mu, const T_scale& sigma,
                    const T_inv_scale& lambda) {
  static const char* function = "exp_mod_normal_lpdf";
  typedef
      typename stan::partials_return_type<T_y, T_loc, T_scale,
                                          T_inv_scale>::type T_partials_return;

  using std::log;

  if (size_zero(y, mu, sigma, lambda))
    return 0.0;

  T_partials_return logp(0.0);

  check_not_nan(function, "Random variable", y);
  check_finite(function, "Location parameter", mu);
  check_positive_finite(function, "Inv_scale parameter", lambda);
  check_positive_finite(function, "Scale parameter", sigma);
  check_consistent_sizes(function, "Random variable", y, "Location parameter",
                         mu, "Scale parameter", sigma, "Inv_scale paramter",
                         lambda);

  if (!include_summand<propto, T_y, T_loc, T_scale, T_inv_scale>::value)
    return 0.0;

  using std::exp;
  using std::log;
  using std::sqrt;

  operands_and_partials<T_y, T_loc, T_scale, T_inv_scale> ops_partials(
      y, mu, sigma, lambda);

  scalar_seq_view<T_y> y_vec(y);
  scalar_seq_view<T_loc> mu_vec(mu);
  scalar_seq_view<T_scale> sigma_vec(sigma);
  scalar_seq_view<T_inv_scale> lambda_vec(lambda);
  size_t N = max_size(y, mu, sigma, lambda);

  for (size_t n = 0; n < N; n++) {
    const T_partials_return y_dbl = value_of(y_vec[n]);
    const T_partials_return mu_dbl = value_of(mu_vec[n]);
    const T_partials_return sigma_dbl = value_of(sigma_vec[n]);
    const T_partials_return lambda_dbl = value_of(lambda_vec[n]);

    const T_partials_return pi_dbl = boost::math::constants::pi<double>();

    if (include_summand<propto>::value)
      logp -= log(2.0);
    if (include_summand<propto, T_inv_scale>::value)
      logp += log(lambda_dbl);
    if (include_summand<propto, T_y, T_loc, T_scale, T_inv_scale>::value)
      logp += lambda_dbl
                  * (mu_dbl + 0.5 * lambda_dbl * sigma_dbl * sigma_dbl - y_dbl)
              + log(erfc((mu_dbl + lambda_dbl * sigma_dbl * sigma_dbl - y_dbl)
                         / (sqrt(2.0) * sigma_dbl)));

    const T_partials_return deriv_logerfc
        = -2.0 / sqrt(pi_dbl)
          * exp(-(mu_dbl + lambda_dbl * sigma_dbl * sigma_dbl - y_dbl)
                / (std::sqrt(2.0) * sigma_dbl)
                * (mu_dbl + lambda_dbl * sigma_dbl * sigma_dbl - y_dbl)
                / (sigma_dbl * std::sqrt(2.0)))
          / erfc((mu_dbl + lambda_dbl * sigma_dbl * sigma_dbl - y_dbl)
                 / (sigma_dbl * std::sqrt(2.0)));

    if (!is_constant_struct<T_y>::value)
      ops_partials.edge1_.partials_[n]
          += -lambda_dbl + deriv_logerfc * -1.0 / (sigma_dbl * std::sqrt(2.0));
    if (!is_constant_struct<T_loc>::value)
      ops_partials.edge2_.partials_[n]
          += lambda_dbl + deriv_logerfc / (sigma_dbl * std::sqrt(2.0));
    if (!is_constant_struct<T_scale>::value)
      ops_partials.edge3_.partials_[n]
          += sigma_dbl * lambda_dbl * lambda_dbl
             + deriv_logerfc
                   * (-mu_dbl / (sigma_dbl * sigma_dbl * std::sqrt(2.0))
                      + lambda_dbl / std::sqrt(2.0)
                      + y_dbl / (sigma_dbl * sigma_dbl * std::sqrt(2.0)));
    if (!is_constant_struct<T_inv_scale>::value)
      ops_partials.edge4_.partials_[n]
          += 1 / lambda_dbl + lambda_dbl * sigma_dbl * sigma_dbl + mu_dbl
             - y_dbl + deriv_logerfc * sigma_dbl / std::sqrt(2.0);
  }
  return ops_partials.build(logp);
}

template <typename T_y, typename T_loc, typename T_scale, typename T_inv_scale>
inline typename return_type<T_y, T_loc, T_scale, T_inv_scale>::type
exp_mod_normal_lpdf(const T_y& y, const T_loc& mu, const T_scale& sigma,
                    const T_inv_scale& lambda) {
  return exp_mod_normal_lpdf<false>(y, mu, sigma, lambda);
}

}  // namespace math
}  // namespace stan
#endif


#include <benchmark/benchmark.h>

#include <tnt/core/core.hpp>
#include <tnt/math/math.hpp>

#include <opencv2/core.hpp>
#include <benchmark/opencv_utils.hpp>

#include <Eigen/Dense>

template <typename DataType>
static void add_TNT(benchmark::State& state, int size)
{
    while (state.KeepRunning()) {
        state.PauseTiming(); // We don't count tensor creation
        tnt::Shape shape{size, size};

        tnt::Tensor<DataType> left(shape, 1.f);
        tnt::Tensor<DataType> right(shape, 2.f);

        state.ResumeTiming();
        tnt::add(left, right);
    }
}

template <typename DataType>
static void add_OCV(benchmark::State& state, int size)
{
    while (state.KeepRunning()) {
        state.PauseTiming();

        cv::Mat left  = tnt::create_cv_mat<DataType>(size, size, cv::Scalar(1));
        cv::Mat right = tnt::create_cv_mat<DataType>(size, size, cv::Scalar(2));

        cv::Mat dst;

        state.ResumeTiming();
        cv::add(left, right, dst);
    }
}

template <typename DataType>
static void add_EIG(benchmark::State& state, int size)
{
    using MatType = Eigen::Matrix<DataType, Eigen::Dynamic, Eigen::Dynamic>;

    while (state.KeepRunning()) {
        state.PauseTiming();

        MatType left = MatType::Constant(size, size, 1);
        MatType right = MatType::Constant(size, size, 2);

        state.ResumeTiming();
        MatType dst = left + right;
    }
}

/*template <typename DataType>
static void add_CUS(benchmark::State& state)
{
    while (state.KeepRunning()) {
        state.PauseTiming();

        cv::Mat left = create_cv_mat<DataType>(state.range(0), state.range(1), cv::Scalar(1));
        cv::Mat right = create_cv_mat<DataType>(state.range(0), state.range(1), cv::Scalar(2));
        cv::Mat dst(state.range(0), state.range(1), CV_32FC1);

        const DataType* left_p = left.ptr<DataType>();
        const DataType* right_p = right.ptr<DataType>();
        DataType* dst_p = dst.ptr<DataType>();

        int width = state.range(0);
        int height = state.range(1);
        int total = width * height;

        state.ResumeTiming();
        int x = 0;
        for ( ; total--; x++)
            dst_p[x] = left_p[x] + right_p[x];
    }
}*/

template <typename T>
class RegisterAddBenchmark
{
public:
    RegisterAddBenchmark(const std::string& type)
    {
        std::vector<int> sizes{4, 16, 64, 512, 2048};
        for (int size : sizes) {
            std::string suffix = type + ">[" + std::to_string(size) + "x" + std::to_string(size) + "]";
            benchmark::RegisterBenchmark(("Add:TNT<" + suffix).c_str(), add_TNT<T>, size);
            benchmark::RegisterBenchmark(("Add:OCV<" + suffix).c_str(), add_OCV<T>, size);
            benchmark::RegisterBenchmark(("Add:EIG<" + suffix).c_str(), add_EIG<T>, size);
        }
    }
};

static RegisterAddBenchmark<int> add_benchmark_int("int");
static RegisterAddBenchmark<float> add_benchmark_float("float");
static RegisterAddBenchmark<double> add_benchmark_double("double");


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

    @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_SINHC_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_SINHC_HPP_INCLUDED

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

  /*!

    @ingroup group-hyperbolic
    This function object returns the hyperbolic cardinal sine: \f$\sinh(x)/x\f$.

    @par Header <boost/simd/function/sinhc.hpp>

    @see cosh, sinh

    @par Example:

      @snippet sinhc.cpp sinhc

    @par Possible output:

      @snippet sinhc.txt sinhc

  **/
  IEEEValue sinhc(IEEEValue const& x);
} }
#endif

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

#endif


/*
    This file is part of Mitsuba, a physically based rendering system.

    Copyright (c) 2007-2014 by Wenzel Jakob and others.

    Mitsuba is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License Version 3
    as published by the Free Software Foundation.

    Mitsuba is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <mitsuba/render/testcase.h>
#include <mitsuba/core/quad.h>
#include <mitsuba/core/fstream.h>
#include <mitsuba/core/plugin.h>
#include <boost/bind.hpp>
#include "../bsdfs/rtrans.h"

MTS_NAMESPACE_BEGIN

void transmittanceIntegrand(const BSDF *bsdf, const Vector &wi, size_t nPts, const Float *in, Float *out) {
	Intersection its;

	for (size_t i=0; i<nPts; ++i) {
		BSDFSamplingRecord bRec(its, wi, Vector(), EImportance);
		bRec.typeMask = BSDF::ETransmission;
		out[i] = bsdf->sample(bRec, Point2(in[2*i], in[2*i+1]))[0];
	}
}

void diffTransmittanceIntegrand(Float *data, size_t resolution, size_t nPts, const Float *in, Float *out) {
	for (size_t i=0; i<nPts; ++i)
		out[i] = 2 * in[i] * evalCubicInterp1D(in[i], data, resolution, 0, 1);
}

class TestRoughTransmittance : public TestCase {
public:
	MTS_BEGIN_TESTCASE()
	MTS_DECLARE_TEST(test01_smoothTransmittance)
	MTS_DECLARE_TEST(test02_roughTransmittance)
	MTS_DECLARE_TEST(test03_roughTransmittanceFixedEta)
	MTS_DECLARE_TEST(test04_roughTransmittanceFixedEtaFixedAlpha)
	MTS_END_TESTCASE()

	Float computeDiffuseTransmittance(const char *name, Float eta, Float alpha, size_t resolution = 100) {
		Properties bsdfProps("roughdielectric");
		if (eta < 1) {
			bsdfProps.setFloat("intIOR", 1.0f);
			bsdfProps.setFloat("extIOR", 1.0f / eta);
		} else {
			bsdfProps.setFloat("extIOR", 1.0f);
			bsdfProps.setFloat("intIOR", eta);
		}

		bsdfProps.setFloat("alpha", alpha);
		bsdfProps.setString("distribution", name);
		ref<BSDF> bsdf = static_cast<BSDF *>(
				PluginManager::getInstance()->createObject(bsdfProps));

		Float *transmittances = new Float[resolution];
		Float stepSize = 1.0f / (resolution-1);
		Float error;

		NDIntegrator intTransmittance(1, 2, 50000, 0, 1e-6f);
		NDIntegrator intDiffTransmittance(1, 1, 50000, 0, 1e-6f);

		for (size_t i=0; i<resolution; ++i) {
			Float cosTheta = stepSize * i;
			Vector wi(math::safe_sqrt(1-cosTheta*cosTheta), 0, cosTheta);

			Float min[2] = {0, 0}, max[2] = {1, 1};
			intTransmittance.integrateVectorized(
				boost::bind(&transmittanceIntegrand, bsdf, wi, _1, _2, _3),
				min, max, &transmittances[i], &error, NULL);
		}

		Float Fdr;
		Float min[1] = { 0 }, max[1] = { 1 };
		intDiffTransmittance.integrateVectorized(
			boost::bind(&diffTransmittanceIntegrand, transmittances, resolution, _1, _2, _3),
			min, max, &Fdr, &error, NULL);

		delete[] transmittances;
		return Fdr;
	}

	Float computeTransmittance(const char *name, Float eta, Float alpha, Float cosTheta) {
		Properties bsdfProps("roughdielectric");
		if (cosTheta < 0) {
			cosTheta = -cosTheta;
			eta = 1.0f / eta;
		}
		if (eta < 1) {
			bsdfProps.setFloat("intIOR", 1.0f);
			bsdfProps.setFloat("extIOR", 1.0f / eta);
		} else {
			bsdfProps.setFloat("extIOR", 1.0f);
			bsdfProps.setFloat("intIOR", eta);
		}
		bsdfProps.setFloat("alpha", alpha);
		bsdfProps.setString("distribution", name);

		ref<BSDF> bsdf = static_cast<BSDF *>(
				PluginManager::getInstance()->createObject(bsdfProps));

		NDIntegrator intTransmittance(1, 2, 50000, 0, 1e-6f);

		Vector wi(math::safe_sqrt(1-cosTheta*cosTheta), 0, cosTheta);
		Float transmittance, error;

		Float min[2] = {0, 0}, max[2] = {1, 1};
		intTransmittance.integrateVectorized(
			boost::bind(&transmittanceIntegrand, bsdf, wi, _1, _2, _3),
			min, max, &transmittance, &error, NULL);

		return transmittance;
	}

	void test01_smoothTransmittance() {
		/* Smooth diffuse transmittance - compare polynomial approximations to ground truth */
		for (int i=0; i<=10; ++i) {
			Float eta = 1 + i/10.0f;

			Float f1 = fresnelDiffuseReflectance(eta, false);
			Float f2 = fresnelDiffuseReflectance(eta, true);
			Float f3 = fresnelDiffuseReflectance(1/eta, false);
			Float f4 = fresnelDiffuseReflectance(1/eta, true);

			assertEqualsEpsilon(std::abs(f1-f2), (Float) 0, 1e-3f);
			assertEqualsEpsilon(std::abs(f3-f4), (Float) 0, 1e-3f);
		}
	}

	void test02_roughTransmittance() {
		RoughTransmittance rtr(MicrofacetDistribution::EBeckmann);
		ref<Random> random = new Random();

		for (int i=0; i<50; ++i) {
			Float alpha = std::pow(random->nextFloat(), (Float) 4.0f)*4;
			Float eta = 1 + std::pow(random->nextFloat(), (Float) 4.0f)*3;
			if (alpha < 1e-5)
				alpha = 1e-5f;
			if (eta < 1+1e-5)
				eta = 1+1e-5f;
			//eta = 1/eta;

			Float refD = computeDiffuseTransmittance("beckmann", eta, alpha);
			Float datD = rtr.evalDiffuse(alpha, eta);

			cout << "Testing " << i << "/50" << endl;
			if (std::abs(refD-datD) > 1e-3f) {
				cout << endl;
				cout << "eta = " << eta << endl;
				cout << "alpha = " << alpha << endl;
				cout << "diff=" << datD-refD << " (datD=" << datD << ", ref=" << refD << ")" << endl;
			}
		}

		Float avgErr = 0.0f;
		for (int i=0; i<1000; ++i) {
			Float cosTheta = random->nextFloat();
			Float alpha = std::pow(random->nextFloat(), (Float) 4.0f)*4;
			Float eta = 1 + std::pow(random->nextFloat(), (Float) 4.0f)*3;
			if (cosTheta < 1e-5)
				cosTheta = 1e-5f;
			if (alpha < 1e-5)
				alpha = 1e-5f;
			if (eta < 1+1e-5)
				eta = 1+1e-5f;
			//eta = 1/eta;

			Float ref = computeTransmittance("beckmann", eta, alpha, cosTheta);
			Float dat = rtr.eval(cosTheta, alpha, eta);

			if (i % 20 == 0)
				cout << "Testing " << i << "/1000" << endl;
			if (std::abs(ref-dat) > 1e-3f) {
				cout << endl;
				cout << "eta = " << eta << endl;
				cout << "alpha = " << alpha << endl;
				cout << "cosTheta = " << cosTheta << endl;
				cout << "diff=" << dat-ref << " (dat=" << dat << ", ref=" << ref << ")" << endl;
			}

			avgErr += ref-dat;
		}
		avgErr /= 1000;
		cout << "Avg error = " << avgErr << endl;
	}

	void test03_roughTransmittanceFixedEta() {
		RoughTransmittance rtr(MicrofacetDistribution::EBeckmann);

		Float eta = 1.5f;
		rtr.setEta(eta);

		ref<Random> random = new Random();

		for (int i=0; i<50; ++i) {
			Float alpha = std::pow(random->nextFloat(), (Float) 4.0f)*4;
			if (alpha < 1e-5)
				alpha = 1e-5f;

			Float refD = computeDiffuseTransmittance("beckmann", eta, alpha);
			Float datD = rtr.evalDiffuse(alpha, eta);

			cout << "Testing " << i << "/50" << endl;
			if (std::abs(refD-datD) > 1e-3f) {
				cout << endl;
				cout << "alpha = " << alpha << endl;
				cout << "diff=" << datD-refD << " (datD=" << datD << ", ref=" << refD << ")" << endl;
			}
		}

		Float avgErr = 0.0f;
		for (int i=0; i<1000; ++i) {
			Float cosTheta = random->nextFloat();
			Float alpha = std::pow(random->nextFloat(), (Float) 4.0f)*4;
			if (cosTheta < 1e-5)
				cosTheta = 1e-5f;
			if (alpha < 1e-5)
				alpha = 1e-5f;

			Float ref = computeTransmittance("beckmann", eta, alpha, cosTheta);
			Float dat = rtr.eval(cosTheta, alpha, eta);

			if (i % 20 == 0)
				cout << "Testing " << i << "/1000" << endl;
			if (std::abs(ref-dat) > 1e-3f) {
				cout << endl;
				cout << "eta = " << eta << endl;
				cout << "alpha = " << alpha << endl;
				cout << "cosTheta = " << cosTheta << endl;
				cout << "diff=" << dat-ref << " (dat=" << dat << ", ref=" << ref << ")" << endl;
			}

			avgErr += ref-dat;
		}
		avgErr /= 1000;
		cout << "Avg error = " << avgErr << endl;
	}

	void test04_roughTransmittanceFixedEtaFixedAlpha() {
		ref<Timer> timer = new Timer();
		RoughTransmittance rtr(MicrofacetDistribution::EBeckmann);
		Float eta = 1.5f;
		Float alpha = 0.2f;
		rtr.setEta(eta);
		rtr.setAlpha(alpha);
		cout << "Loading and projecting took " << timer->getMilliseconds() << " ms" << endl;

		ref<Random> random = new Random();

		Float refD = computeDiffuseTransmittance("beckmann", eta, alpha);
		Float datD = rtr.evalDiffuse(alpha, eta);

		if (std::abs(refD-datD) > 1e-3f) {
			cout << endl;
			cout << "alpha = " << alpha << endl;
			cout << "diff=" << datD-refD << " (datD=" << datD << ", ref=" << refD << ")" << endl;
		}

		Float avgErr = 0.0f;
		for (int i=0; i<1000; ++i) {
			Float cosTheta = random->nextFloat();
			if (cosTheta < 1e-5)
				cosTheta = 1e-5f;

			Float ref = computeTransmittance("beckmann", eta, alpha, cosTheta);
			Float dat = rtr.eval(cosTheta, alpha, eta);

			if (i % 20 == 0)
				cout << "Testing " << i << "/1000" << endl;
			if (std::abs(ref-dat) > 1e-3f) {
				cout << endl;
				cout << "eta = " << eta << endl;
				cout << "alpha = " << alpha << endl;
				cout << "cosTheta = " << cosTheta << endl;
				cout << "diff=" << dat-ref << " (dat=" << dat << ", ref=" << ref << ")" << endl;
			}

			avgErr += ref-dat;
		}
		avgErr /= 1000;
		cout << "Avg error = " << avgErr << endl;
	}
};

MTS_EXPORT_TESTCASE(TestRoughTransmittance, "Testcase for rough transmittance computations")
MTS_NAMESPACE_END


// preprocesses the edge list to ensure that it's rehashed and weighted using Jaccard coefficient
// the rehashed weighted edgelist and the inverse maps are stored in the same directory

#include <iostream>
#include <fstream>
#include <unordered_map>
#include <unordered_set>
#include <boost/graph/adjacency_list.hpp>

using namespace std;
using namespace boost;

typedef adjacency_list< listS,vecS, undirectedS > Graph;
typedef graph_traits < Graph> :: vertex_descriptor vertex_descriptor;
typedef graph_traits < Graph> :: edge_descriptor edge_descriptor;
typedef Graph :: vertex_iterator vertex_iterator;
typedef Graph :: edge_iterator edge_iterator;
typedef std::pair <int ,int> Edge;
typedef graph_traits < Graph> :: adjacency_iterator my_adjacency_iterator;

void print_set(std::unordered_set<int> uset)
{
	cout << endl;
	for (auto item: uset)
		cout << item << " ";
	cout << endl;
}

std::unordered_set<int> union_(std::unordered_set<int> uset1, std::unordered_set<int> uset2)
{
	std::unordered_set<int> new_uset;
	if (uset1.size() > uset2.size())
	{
		new_uset = uset1;
		for (auto thing: uset2)
			new_uset.insert(thing);
	}

	else
	{
		new_uset = uset2;
		for (auto thing: uset1)
			new_uset.insert(thing);
	}

	return new_uset;
}

std::unordered_set<int> intersection(std::unordered_set<int> uset1, std::unordered_set<int> uset2)
{
	std::unordered_set<int> new_uset;

	if (uset1.size() > uset2.size())
	{
		for (auto node: uset2)
		{
			if (uset1.find(node) != uset1.end())
				new_uset.insert(node);
		}
	}

	else
	{
		for (auto node: uset1)
		{
			if (uset2.find(node) != uset2.end())
				new_uset.insert(node);
		}
	}

	return new_uset;
}

void preprocesses(string input_filename)
{
	// rehashes the edge list at input_filename
	Graph G;
	my_adjacency_iterator start, end;
	edge_iterator e_start, e_end;
	edge_descriptor e;
	std::unordered_map<int,int> ordered_labels;
	int u, v;
	int count = 0;
	float w;

	ifstream fin(input_filename);

	while (fin >> u >> v)
	{
		add_edge(u, v, G);

		if (ordered_labels.find(u) == ordered_labels.end())
		{
			ordered_labels[u] = count;
			count += 1;
		}

		if (ordered_labels.find(v) == ordered_labels.end())
		{
			ordered_labels[v] = count;
			count += 1;
		}
	}
	fin.close();

	string out_filename1 = "rehashed_weighted_" + input_filename;
	string out_filename2 = "rehashed_" + input_filename;

	ofstream fout1;
	fout1.open(out_filename1);
	ofstream fout2;
	fout2.open(out_filename2);

	for (tie(e_start, e_end) = edges(G); e_start != e_end; ++ e_start)
	{
		std::unordered_set<int> u_neighbors, v_neighbors;
		e = *e_start;
		u = source(e, G);
		v = target(e, G);

		for (tie(start, end) = adjacent_vertices(u, G); start != end; ++ start)
		{
			int neighbor = *start;
			u_neighbors.insert(neighbor);
		}

		for (tie(start, end) = adjacent_vertices(v, G); start != end; ++ start)
		{
			int neighbor = *start;
			v_neighbors.insert(neighbor);
		}

		float num = intersection(u_neighbors, v_neighbors).size();
		float denom = union_(u_neighbors, v_neighbors).size() - 2;

		if (denom == 0)
			w = 0;
		else
			w = num / denom;

		fout1 << ordered_labels[u] << " " << ordered_labels[v] << " " << w << endl;
		fout2 << ordered_labels[u] << " " << ordered_labels[v] << endl;
	}
	fout1.close();
	fout2.close();

	cout << "\nWeighted edgelist is at " << out_filename1 << endl;
	cout << "\nRehashed unweighted edgelist is at " << out_filename2 << endl;

	out_filename2 = input_filename + "_inv_maps";
	fout2.open(out_filename2);

	for (auto item: ordered_labels)
		fout2 << item.first << " " << item.second << endl;

	fout2.close();
	cout << "\nThe inverse mapping is at " << out_filename2 << endl;
}

int main(int argc, char const *argv[])
{
	if (argc < 2)
	{
		cout << "\nEnter filename of the unweighted edge list to preprocesses\n";
		return 0;
	}

	preprocesses(argv[1]);
	return 0;
}


/*    This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
 *    See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
 *    Author(s):       Vincent Rouvreau
 *
 *    Copyright (C) 2015 Inria
 *
 *    Modification(s):
 *      - YYYY/MM Author: Description of the modification
 */

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE "alpha_complex_3d"
#include <boost/test/unit_test.hpp>
#include <boost/mpl/list.hpp>

#include <cmath>  // float comparison
#include <limits>
#include <string>
#include <vector>
#include <random>
#include <cstddef>  // for std::size_t

#include <gudhi/Alpha_complex_3d.h>
#include <gudhi/graph_simplicial_complex.h>
#include <gudhi/Simplex_tree.h>
#include <gudhi/Unitary_tests_utils.h>
// to construct Alpha_complex from a OFF file of points
#include <gudhi/Points_3D_off_io.h>

#include <CGAL/Random.h>
#include <CGAL/point_generators_3.h>

using Fast_weighted_alpha_complex_3d =
    Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::FAST, true, false>;
using Safe_weighted_alpha_complex_3d =
    Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::SAFE, true, false>;
using Exact_weighted_alpha_complex_3d =
    Gudhi::alpha_complex::Alpha_complex_3d<Gudhi::alpha_complex::complexity::EXACT, true, false>;

typedef boost::mpl::list<Fast_weighted_alpha_complex_3d, Safe_weighted_alpha_complex_3d,
                         Exact_weighted_alpha_complex_3d>
    weighted_variants_type_list;

#ifdef GUDHI_DEBUG
BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_weighted_throw, Weighted_alpha_complex_3d, weighted_variants_type_list) {
  using Point_3 = typename Weighted_alpha_complex_3d::Point_3;
  std::vector<Point_3> w_points;
  w_points.push_back(Point_3(0.0, 0.0, 0.0));
  w_points.push_back(Point_3(0.0, 0.0, 0.2));
  w_points.push_back(Point_3(0.2, 0.0, 0.2));
  // w_points.push_back(Point_3(0.6, 0.6, 0.0));
  // w_points.push_back(Point_3(0.8, 0.8, 0.2));
  // w_points.push_back(Point_3(0.2, 0.8, 0.6));

  // weights size is different from w_points size to make weighted Alpha_complex_3d throw in debug mode
  std::vector<double> weights = {0.01, 0.005, 0.006, 0.01, 0.009, 0.001};

  std::cout << "Check exception throw in debug mode" << std::endl;
  BOOST_CHECK_THROW(Weighted_alpha_complex_3d wac(w_points, weights), std::invalid_argument);
}
#endif

BOOST_AUTO_TEST_CASE_TEMPLATE(Alpha_complex_weighted, Weighted_alpha_complex_3d, weighted_variants_type_list) {
  std::cout << "Weighted alpha complex 3d from points and weights" << std::endl;
  using Point_3 = typename Weighted_alpha_complex_3d::Point_3;
  std::vector<Point_3> w_points;
  w_points.push_back(Point_3(0.0, 0.0, 0.0));
  w_points.push_back(Point_3(0.0, 0.0, 0.2));
  w_points.push_back(Point_3(0.2, 0.0, 0.2));
  w_points.push_back(Point_3(0.6, 0.6, 0.0));
  w_points.push_back(Point_3(0.8, 0.8, 0.2));
  w_points.push_back(Point_3(0.2, 0.8, 0.6));

  // weights size is different from w_points size to make weighted Alpha_complex_3d throw in debug mode
  std::vector<double> weights = {0.01, 0.005, 0.006, 0.01, 0.009, 0.001};

  Weighted_alpha_complex_3d alpha_complex_p_a_w(w_points, weights);
  Gudhi::Simplex_tree<> stree;
  alpha_complex_p_a_w.create_complex(stree);

  std::cout << "Weighted alpha complex 3d from weighted points" << std::endl;
  using Weighted_point_3 = typename Weighted_alpha_complex_3d::Weighted_point_3;

  std::vector<Weighted_point_3> weighted_points;

  for (std::size_t i = 0; i < w_points.size(); i++) {
    weighted_points.push_back(Weighted_point_3(w_points[i], weights[i]));
  }
  Weighted_alpha_complex_3d alpha_complex_w_p(weighted_points);

  Gudhi::Simplex_tree<> stree_bis;
  alpha_complex_w_p.create_complex(stree_bis);

  // ---------------------
  // Compare both versions
  // ---------------------
  std::cout << "Weighted alpha complex 3d is of dimension " << stree_bis.dimension() << " - versus "
            << stree.dimension() << std::endl;
  BOOST_CHECK(stree_bis.dimension() == stree.dimension());
  std::cout << "Weighted alpha complex 3d num_simplices " << stree_bis.num_simplices() << " - versus "
            << stree.num_simplices() << std::endl;
  BOOST_CHECK(stree_bis.num_simplices() == stree.num_simplices());
  std::cout << "Weighted alpha complex 3d num_vertices " << stree_bis.num_vertices() << " - versus "
            << stree.num_vertices() << std::endl;
  BOOST_CHECK(stree_bis.num_vertices() == stree.num_vertices());

  auto sh = stree.filtration_simplex_range().begin();
  while (sh != stree.filtration_simplex_range().end()) {
    std::vector<int> simplex;
    std::vector<int> exact_simplex;
#ifdef DEBUG_TRACES
    std::cout << " ( ";
#endif
    for (auto vertex : stree.simplex_vertex_range(*sh)) {
      simplex.push_back(vertex);
#ifdef DEBUG_TRACES
      std::cout << vertex << " ";
#endif
    }
#ifdef DEBUG_TRACES
    std::cout << ") -> "
              << "[" << stree.filtration(*sh) << "] ";
    std::cout << std::endl;
#endif

    // Find it in the exact structure
    auto sh_exact = stree_bis.find(simplex);
    BOOST_CHECK(sh_exact != stree_bis.null_simplex());

    // Exact and non-exact version is not exactly the same due to float comparison
    GUDHI_TEST_FLOAT_EQUALITY_CHECK(stree_bis.filtration(sh_exact), stree.filtration(*sh));

    ++sh;
  }
}


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#include <iostream>
#include <cmath>
#include <complex>

#include <boost/numeric/mtl/mtl.hpp>

template <typename T>
inline void check(const T& result, double exp)
{
    MTL_THROW_IF(std::abs(result - exp) > 0.0001, mtl::runtime_error("dot product wrong"));
}

template <typename VectorU, typename VectorV>
void test(VectorU& u, VectorV& v, const char* name)
{
    //using mtl::dot;
    typedef typename mtl::Collection<VectorU>::size_type  size_type;
    for (size_type i= 0; i < size(v); i++)
	u[i]= i+1, v[i]= i+1;

    mtl::io::tout << name << "\n dot(u, v) = " << dot(u, v) << "\n"; mtl::io::tout.flush();
    check(dot(u, v), 285.0);

    mtl::io::tout << " dot<2>(u, v) = " << mtl::dot<2>(u, v) << "\n"; mtl::io::tout.flush();
    check(mtl::dot<2>(u, v), 285.0);

    mtl::io::tout << " dot<6>(u, v) = " << mtl::dot<6>(u, v) << "\n"; mtl::io::tout.flush();
    check(mtl::dot<6>(u, v), 285.0);
}

int main(int ,char**)
{
    using mtl::vec::parameters;
    const int size= 9;

    mtl::dense_vector<float>   u(size), v(size), w(size);
    mtl::dense_vector<double>  x(size), y(size), z(size);
    mtl::dense_vector<std::complex<double> >  xc(size), yc(size), zc(size);

    mtl::io::tout << "Testing vector operations\n";

    test(u, v, "test float");
    test(x, y, "test double");
    test(u, x, "test float, double mixed");
    test(xc, yc, "test complex<double>");
    test(x, yc, "test complex<double>, double mixed");

    mtl::dense_vector<float, parameters<mtl::row_major> >   ur(size), vr(size), wr(size);
    test(ur, vr, "test float in row vector");

    // test(ur, v, wr, "test float in mixed vector (shouldn't work)");

    return 0;
}