#include <gsl/gsl_test.h>
#include <gsl/gsl_ieee_utils.h>
#include <gsl/gsl_math.h>
#include "gsl_cblas.h"

#include "tests.h"

void
test_hemm (void) {
const double flteps = 1e-4, dbleps = 1e-6;
  {
   int order = 101;
   int side = 141;
   int uplo = 121;
   int M = 1;
   int N = 2;
   float alpha[2] = {0.0f, 0.1f};
   float beta[2] = {0.0f, 0.1f};
   float A[] = { -0.126f, 0.079f };
   int lda = 1;
   float B[] = { -0.954f, -0.059f, 0.296f, -0.988f };
   int ldb = 2;
   float C[] = { -0.859f, -0.731f, 0.737f, 0.593f };
   int ldc = 2;
   float C_expected[] = { 0.0723566f, -0.0738796f, -0.0717488f, 0.0699704f };
   cblas_chemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "chemm(case 1550) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "chemm(case 1550) imag");
     };
   };
  };

  {
   int order = 102;
   int side = 141;
   int uplo = 121;
   int M = 1;
   int N = 2;
   float alpha[2] = {0.0f, 0.1f};
   float beta[2] = {0.0f, 0.1f};
   float A[] = { 0.652f, 0.584f };
   int lda = 1;
   float B[] = { -0.983f, -0.734f, -0.422f, -0.825f };
   int ldb = 1;
   float C[] = { 0.387f, 0.341f, -0.734f, 0.632f };
   int ldc = 1;
   float C_expected[] = { 0.0137568f, -0.0253916f, -0.00941f, -0.100914f };
   cblas_chemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "chemm(case 1551) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "chemm(case 1551) imag");
     };
   };
  };

  {
   int order = 101;
   int side = 142;
   int uplo = 121;
   int M = 1;
   int N = 2;
   float alpha[2] = {0.0f, 1.0f};
   float beta[2] = {-1.0f, 0.0f};
   float A[] = { 0.78f, 0.885f, 0.507f, 0.765f, 0.911f, -0.461f, 0.707f, 0.508f };
   int lda = 2;
   float B[] = { -0.905f, 0.633f, 0.85f, -0.943f };
   int ldb = 2;
   float C[] = { 0.045f, -0.237f, 0.078f, -0.252f };
   int ldc = 2;
   float C_expected[] = { 0.589611f, -0.759345f, 0.960095f, -0.09013f };
   cblas_chemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "chemm(case 1552) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "chemm(case 1552) imag");
     };
   };
  };

  {
   int order = 102;
   int side = 142;
   int uplo = 121;
   int M = 1;
   int N = 2;
   float alpha[2] = {0.0f, 1.0f};
   float beta[2] = {-1.0f, 0.0f};
   float A[] = { 0.947f, 0.939f, -0.267f, -0.819f, -0.827f, -0.937f, 0.991f, 0.838f };
   int lda = 2;
   float B[] = { 0.871f, -0.988f, -0.232f, -0.434f };
   int ldb = 1;
   float C[] = { -0.261f, 0.927f, -0.351f, -0.203f };
   int ldc = 1;
   float C_expected[] = { 1.0551f, 0.496359f, 0.780145f, -1.67298f };
   cblas_chemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "chemm(case 1553) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "chemm(case 1553) imag");
     };
   };
  };

  {
   int order = 101;
   int side = 141;
   int uplo = 122;
   int M = 1;
   int N = 2;
   float alpha[2] = {-1.0f, 0.0f};
   float beta[2] = {0.0f, 0.0f};
   float A[] = { -0.593f, -0.9f };
   int lda = 1;
   float B[] = { -0.861f, 0.747f, -0.984f, 0.595f };
   int ldb = 2;
   float C[] = { -0.589f, -0.671f, -0.011f, -0.417f };
   int ldc = 2;
   float C_expected[] = { -0.510573f, 0.442971f, -0.583512f, 0.352835f };
   cblas_chemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "chemm(case 1554) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "chemm(case 1554) imag");
     };
   };
  };

  {
   int order = 102;
   int side = 141;
   int uplo = 122;
   int M = 1;
   int N = 2;
   float alpha[2] = {-1.0f, 0.0f};
   float beta[2] = {0.0f, 0.0f};
   float A[] = { -0.79f, 0.132f };
   int lda = 1;
   float B[] = { -0.243f, -0.12f, 0.633f, -0.556f };
   int ldb = 1;
   float C[] = { -0.658f, -0.74f, -0.47f, 0.481f };
   int ldc = 1;
   float C_expected[] = { -0.19197f, -0.0948f, 0.50007f, -0.43924f };
   cblas_chemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "chemm(case 1555) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "chemm(case 1555) imag");
     };
   };
  };

  {
   int order = 101;
   int side = 142;
   int uplo = 122;
   int M = 1;
   int N = 2;
   float alpha[2] = {-0.3f, 0.1f};
   float beta[2] = {0.0f, 1.0f};
   float A[] = { -0.114f, -0.515f, -0.513f, -0.527f, -0.995f, 0.986f, 0.229f, -0.076f };
   int lda = 2;
   float B[] = { 0.084f, 0.522f, 0.61f, 0.694f };
   int ldb = 2;
   float C[] = { 0.802f, 0.136f, -0.161f, -0.364f };
   int ldc = 2;
   float C_expected[] = { 0.269101f, 0.716492f, 0.237088f, 0.0290666f };
   cblas_chemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "chemm(case 1556) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "chemm(case 1556) imag");
     };
   };
  };

  {
   int order = 102;
   int side = 142;
   int uplo = 122;
   int M = 1;
   int N = 2;
   float alpha[2] = {-0.3f, 0.1f};
   float beta[2] = {0.0f, 1.0f};
   float A[] = { 0.798f, -0.324f, -0.693f, -0.893f, -0.223f, 0.749f, 0.102f, -0.357f };
   int lda = 2;
   float B[] = { -0.572f, -0.569f, -0.391f, -0.938f };
   int ldb = 1;
   float C[] = { 0.152f, -0.834f, -0.633f, -0.473f };
   int ldc = 1;
   float C_expected[] = { 1.08642f, -0.113853f, 0.234826f, -0.48289f };
   cblas_chemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], flteps, "chemm(case 1557) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], flteps, "chemm(case 1557) imag");
     };
   };
  };

  {
   int order = 101;
   int side = 141;
   int uplo = 121;
   int M = 1;
   int N = 2;
   double alpha[2] = {0, 0.1};
   double beta[2] = {0, 0.1};
   double A[] = { -0.359, 0.089 };
   int lda = 1;
   double B[] = { -0.451, -0.337, -0.901, -0.871 };
   int ldb = 2;
   double C[] = { 0.729, 0.631, 0.364, 0.246 };
   int ldc = 2;
   double C_expected[] = { -0.0751983, 0.0890909, -0.0558689, 0.0687459 };
   cblas_zhemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zhemm(case 1558) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zhemm(case 1558) imag");
     };
   };
  };

  {
   int order = 102;
   int side = 141;
   int uplo = 121;
   int M = 1;
   int N = 2;
   double alpha[2] = {0, 0.1};
   double beta[2] = {0, 0.1};
   double A[] = { 0.044, -0.496 };
   int lda = 1;
   double B[] = { -0.674, 0.281, 0.366, 0.888 };
   int ldb = 1;
   double C[] = { -0.9, 0.919, 0.857, -0.049 };
   int ldc = 1;
   double C_expected[] = { -0.0931364, -0.0929656, 0.0009928, 0.0873104 };
   cblas_zhemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zhemm(case 1559) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zhemm(case 1559) imag");
     };
   };
  };

  {
   int order = 101;
   int side = 142;
   int uplo = 121;
   int M = 1;
   int N = 2;
   double alpha[2] = {0, 0};
   double beta[2] = {0, 0.1};
   double A[] = { -0.314, 0.115, 0.114, 0.878, 0.961, -0.224, 0.973, 0.771 };
   int lda = 2;
   double B[] = { 0.5, -0.016, -0.5, 0.149 };
   int ldb = 2;
   double C[] = { -0.054, 0.064, 0.02, 0.245 };
   int ldc = 2;
   double C_expected[] = { -0.0064, -0.0054, -0.0245, 0.002 };
   cblas_zhemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zhemm(case 1560) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zhemm(case 1560) imag");
     };
   };
  };

  {
   int order = 102;
   int side = 142;
   int uplo = 121;
   int M = 1;
   int N = 2;
   double alpha[2] = {0, 0};
   double beta[2] = {0, 0.1};
   double A[] = { 0.186, 0.578, 0.797, -0.957, -0.539, -0.969, -0.21, 0.354 };
   int lda = 2;
   double B[] = { 0.641, -0.968, 0.15, -0.569 };
   int ldb = 1;
   double C[] = { -0.556, -0.9, 0.197, 0.31 };
   int ldc = 1;
   double C_expected[] = { 0.09, -0.0556, -0.031, 0.0197 };
   cblas_zhemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zhemm(case 1561) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zhemm(case 1561) imag");
     };
   };
  };

  {
   int order = 101;
   int side = 141;
   int uplo = 122;
   int M = 1;
   int N = 2;
   double alpha[2] = {1, 0};
   double beta[2] = {1, 0};
   double A[] = { 0.323, 0.641 };
   int lda = 1;
   double B[] = { -0.188, 0.091, -0.235, 0.523 };
   int ldb = 2;
   double C[] = { 0.919, 0.806, 0.823, -0.94 };
   int ldc = 2;
   double C_expected[] = { 0.858276, 0.835393, 0.747095, -0.771071 };
   cblas_zhemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zhemm(case 1562) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zhemm(case 1562) imag");
     };
   };
  };

  {
   int order = 102;
   int side = 141;
   int uplo = 122;
   int M = 1;
   int N = 2;
   double alpha[2] = {1, 0};
   double beta[2] = {1, 0};
   double A[] = { -0.688, 0.915 };
   int lda = 1;
   double B[] = { 0.914, -0.204, 0.205, -0.476 };
   int ldb = 1;
   double C[] = { 0.27, -0.628, -0.079, 0.507 };
   int ldc = 1;
   double C_expected[] = { -0.358832, -0.487648, -0.22004, 0.834488 };
   cblas_zhemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zhemm(case 1563) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zhemm(case 1563) imag");
     };
   };
  };

  {
   int order = 101;
   int side = 142;
   int uplo = 122;
   int M = 1;
   int N = 2;
   double alpha[2] = {0, 1};
   double beta[2] = {0, 0.1};
   double A[] = { 0.681, 0.574, -0.425, -0.64, 0.792, 0.661, -0.009, 0.005 };
   int lda = 2;
   double B[] = { -0.221, 0.554, -0.465, -0.95 };
   int ldb = 2;
   double C[] = { 0.331, -0.958, -0.826, -0.972 };
   int ldc = 2;
   double C_expected[] = { 0.778291, 0.142269, -0.496199, 0.112747 };
   cblas_zhemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zhemm(case 1564) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zhemm(case 1564) imag");
     };
   };
  };

  {
   int order = 102;
   int side = 142;
   int uplo = 122;
   int M = 1;
   int N = 2;
   double alpha[2] = {0, 1};
   double beta[2] = {0, 0.1};
   double A[] = { 0.959, 0.34, -0.23, 0.064, 0.516, -0.275, 0.714, 0.899 };
   int lda = 2;
   double B[] = { -0.502, -0.987, -0.134, 0.215 };
   int ldb = 1;
   double C[] = { 0.929, 0.181, -0.16, -0.921 };
   int ldc = 1;
   double C_expected[] = { 0.986459, -0.371458, -0.320548, -0.059384 };
   cblas_zhemm(order, side, uplo, M, N, alpha, A, lda, B, ldb, beta, C, ldc);
   {
     int i;
     for (i = 0; i < 2; i++) {
       gsl_test_rel(C[2*i], C_expected[2*i], dbleps, "zhemm(case 1565) real");
       gsl_test_rel(C[2*i+1], C_expected[2*i+1], dbleps, "zhemm(case 1565) imag");
     };
   };
  };

}


#ifndef BIO_GSL_H_
#define BIO_GSL_H_

#include "bio/defs.h"
#include "bio/raii.h"

#include <gsl/gsl_integration.h>

BIO_NS_START

void
gsl_init();

template <>
RAII<gsl_integration_workspace *>::~RAII();

#define BIO_GSL_EXP(x) ((x) < GSL_LOG_DBL_MIN ? 0.0 : gsl_sf_exp(x))

BIO_NS_END

#endif //BIO_GSL_H_


#pragma once

#include <gsl.h>

#include "TMP_Helper.h"
#include "HE_Assert.h"
#include "HE_Math.h"

#include <type_traits>

namespace HE
{
	struct MemoryBlock
	{
		void* ptr;
		size_t length;

		void* begin() const { return ptr; }
		void* end() const { return static_cast<char*>(ptr) + length; }
	};
	using Blk = MemoryBlock;

	// Max alignment
	constexpr size_t PlatformMaxAlignment = Math::Max(alignof(void*), alignof(size_t));
	static_assert(Math::IsPow2(PlatformMaxAlignment), "PlatformMaxAlignment is not a power of 2, as should be");

	namespace Private
	{
		template<class T>
		using try_allocate = std::enable_if_t<std::is_same<Blk, decltype(std::declval<T>().allocate(std::declval<size_t>()))>::value>;

		// Custom aligned allocation's alignment parameter generally has to be a power of 2 and bigger than the allocator's default alignment,
		// in order to respect the allocator's semantics
		template<class T>
		using try_aligned_allocate = std::enable_if_t<std::is_same<Blk, decltype(std::declval<T>().allocate(std::declval<size_t>(), std::declval<size_t>()))>::value>;

		template<class T>
		using try_deallocate = decltype(std::declval<T>().deallocate(std::declval<Blk>()));

		template<class T>
		using try_deallocateAll = decltype(std::declval<T>().deallocateAll());

		template<class T>
		using try_owns = std::enable_if_t<std::is_same<bool, decltype(std::declval<T>().owns(std::declval<Blk>()))>::value>;

		template<class T>
		using try_it = std::enable_if_t<std::is_same<T, decltype(T::it)>::value>;
	}

	// Allocator
	// Must have (for allocator of type T):
	// - MemoryBlock T::allocate(size_t)
	// - void T::deallocate(MemoryBlock) noexcept
	template<class T>
	using is_allocator = and_ < has_op<T, Private::try_allocate>, has_op<T, Private::try_deallocate> > ;
	template<class... T>
	constexpr bool IsAllocator()
	{
		return and_<is_allocator<T>...>::value;
	}

	// StatelessAllocator
	// - Allocator
	// - StateSize<T>::value == 0
	// - is_same<decltype(T::it), T>
	template<class T>
	using is_stateless_allocator = and_<is_allocator<T>, equal_<StateSize<T>, std::integral_constant<size_t, 0>>>;
	template<class... T>
	constexpr bool IsStatelessAllocator()
	{
		return and_<is_stateless_allocator<T>...>::value;
	}

	// OwningAllocator
	// Must have (for allocator of type T)
	// - HE::is_allocator<T>()
	// - bool T::owns(MemoryBlock)
	template<class T>
	using is_owning_allocator = and_<is_allocator<T>, has_op<T, Private::try_owns>>;
	template<class... T>
	constexpr bool IsOwningAllocator()
	{
		return and_<is_owning_allocator<T>...>::value;
	}

	// AlignedAllocator
	// Must have (for allocator of type T)
	// - HE::is_allocator<T>()
	// - bool T::allocate(size_t, size_t)
	template<class T>
	using is_aligned_allocator = and_<is_allocator<T>, has_op<T, Private::try_aligned_allocate>>;
	template<class... T>
	constexpr bool IsAlignedAllocator()
	{
		return and_<is_aligned_allocator<T>...>::value;
	}

	// Generic allocator functions
	template< class Type, class Allocator, class Enable = std::enable_if_t<is_allocator<Allocator>::value>>
	Blk allocate(Allocator&& a)
	{
		return a.allocate(sizeof(Type));
	}

	template<class Type, class Allocator, class Enable = std::enable_if_t<is_allocator<Allocator>::value>>
	Blk allocate(Allocator&& a, size_t count)
	{
		return a.allocate(count*sizeof(Type), alignof(Type));
	}

	class NullAllocator
	{
	public:
		static constexpr size_t alignment = 64 * 1024;
		static NullAllocator it;

		Blk allocate(size_t);
		Blk allocate(size_t, size_t);
		void deallocate(Blk) noexcept;
		void deallocateAll() noexcept;
		bool owns(Blk);
	};

	// Returns the beginning of the buffer is the buffer is big enough, even if it
	// was already allocated. This is because the allocator does no tracking. The client
	// is responsible for making sure memory doesn't get corrupted
	// Probably shouldn't be used as the main allocator of the Fallback allocator, but pairs
	// well with Segregator
	template<size_t N>
	class alignas(PlatformMaxAlignment) LightInlineAllocator
	{
	public:
		static constexpr size_t alignment = PlatformMaxAlignment;

		Blk allocate(size_t n)
		{
			if (n <= N)
			{
				return{ m_buffer, n };
			}
			else
			{
				return{ nullptr, 0 };
			}
		}

		Blk allocate(size_t n, size_t a)
		{
			EXPECTS(Math::IsPow2(alignment) && a >= alignment);
			auto const p = reinterpret_cast<char*>(Math::RoundUpToMultipleOf(reinterpret_cast<size_t>(&m_buffer[0]), a));
			Blk const b{ p, n };
			if (b.end() <= m_buffer + N)
			{
				return b;
			}
			else
			{
				return{ nullptr, 0 };
			}
		}

		bool owns(Blk b)
		{
			return b.begin() >= m_buffer && b.end() <= m_buffer + N;
		}

		void deallocate(Blk) noexcept {}

	private:
		char m_buffer[N];
	};

	class MallocAllocator
	{
	public:
		static constexpr size_t alignment = PlatformMaxAlignment;
		static MallocAllocator it;

		Blk allocate(size_t n);
		void deallocate(Blk) noexcept;
	};

	class AlignedMallocAllocator
	{
	public:
		static constexpr size_t alignment = PlatformMaxAlignment;
		static AlignedMallocAllocator it;

		Blk allocate(size_t n);
		Blk allocate(size_t n, size_t alignment);
		void deallocate(Blk) noexcept;
	};

	template< class Primary, class Fallback >
	class FallbackAllocator;

	namespace Private
	{
		template < class Primary, class Fallback, class Enable = void>
		struct FallbackAllocatorImpl {	};

		template< class Primary, class Fallback >
		using FallbackStatelessCond = std::enable_if_t<
			and_<
			equal_<StateSize<Primary>, std::integral_constant<size_t, 0>>,
			equal_<StateSize<Fallback>, std::integral_constant<size_t, 0>>
			>::value
		>;

		template < class Primary, class Fallback >
		struct FallbackAllocatorImpl<Primary, Fallback,
			FallbackStatelessCond<Primary, Fallback> >
		{
			static FallbackAllocator<Primary, Fallback> it;
		};
	}

	template< class Primary, class Fallback >
	class FallbackAllocator :
		private Primary,
		private Fallback,
		private Private::FallbackAllocatorImpl<Primary, Fallback>
	{
	protected:
		using P = Primary;
		using F = Fallback;

		static_assert(IsOwningAllocator<P>(), "Primary allocator does not meet the HE::OwningAllocator concept");
		static_assert(IsAllocator<F>(), "Fallback allocator does not meet the HE::Allocator concept");

	public:
		static constexpr size_t alignment = Math::Min(Primary::alignment, Fallback::alignment);

		Blk allocate(size_t n)
		{
			auto const blk = P::allocate(n);
			if (!blk.ptr) return F::allocate(n);
			return blk;
		}

		template<class Enable = std::enable_if_t<and_<is_aligned_allocator<Primary>, is_aligned_allocator<Fallback>>::value>>
		Blk allocate(size_t n, size_t alignment)
		{
			auto const blk = P::allocate(n, alignment);
			if (!blk.ptr) return F::allocate(n, alignment);
			return blk;
		}

		void deallocate(Blk b) noexcept
		{
			if (P::owns(b))
				P::deallocate(b);
			else
				F::deallocate(b);
		}

		template<class Enable = std::enable_if_t<and_<has_op<Primary, Private::try_deallocateAll>, has_op<Fallback, Private::try_deallocateAll>>::value>>
		void deallocateAll() noexcept
		{
			Primary::deallocateAll();
			Fallback::deallocateAll();
		}

		template<class Enable = std::enable_if_t<is_owning_allocator<Fallback>::value>>
		bool owns(Blk b)
		{
			return Primary::owns(b) || Fallback::owns(b);
		}
	};

	template< class Primary, class Fallback >
	FallbackAllocator<Primary, Fallback> Private::FallbackAllocatorImpl<Primary, Fallback, Private::FallbackStatelessCond<Primary, Fallback>>::it;

	namespace Allocator
	{
		constexpr size_t unbounded = static_cast<size_t>(-1);
	}

	// An allocator that allocates nodes on a Parent allocator and internally keeps blocks
	// of memory on deallocation instead of actually deallocating them, but only
	// if they have a certain size
	// Important: Actual deallocations to the parent allocator are not guaranteed to be ordered as
	// deallocated in the Freelist allocator, and therefore should not be used with allocators
	// that require ordered deallocations, such as StackAllocator

	// minSize: Minimum size of the allocation to be considered in range
	// maxSize: Maxsimum size of the allocation to be considered in range
	// batchCount: Number of allocations on a fresh "in range" allocation. Basically fills up the Freelist with batchCount nodes on allocation
	// maxNodes: Max number of nodes the freelist can keep
	template< class Parent,
		size_t MinSize,
		size_t MaxSize = MinSize,
		size_t MaxNodes = Allocator::unbounded,
		class Enable = std::enable_if_t<is_allocator<Parent>::value>
		>
	class FreelistAllocator
		: private Parent
	{
		static_assert(MaxSize >= MinSize, "FreelistAllocator's MaxSize should be higher or equal to MinSize");
		static_assert(MaxSize >= sizeof(void*), "FreelistAllocator's MaxSize and MinSize should be higher or equal than sizeof(void*)");

	public:
		static constexpr size_t alignment = Parent::alignment;

		Blk allocate(size_t n)
		{
			return allocateImpl(n);
		}

		template<class Enable = std::enable_if_t<is_aligned_allocator<Parent>::value>>
		Blk allocate(size_t n, size_t alignment)
		{
			return allocateImpl(n, alignment);
		}

		void deallocate(Blk b) noexcept
		{
			if ((MaxNodes == Allocator::unbounded || m_nNodesCount != MaxNodes) && inRange(b.length))
			{
				auto const next = m_pFreelistRoot;
				m_pFreelistRoot = static_cast<Node*>(b.ptr);
				m_pFreelistRoot->next = next;
				++m_nNodesCount;
			}
			else
			{
				Parent::deallocate(b);
			}
		}

		// Only O(1) if the Parent allocator supports deallocateAll
		// If the Freelist is unbounded and the Parent doesn't support deallocateAll,
		// the Freelist will do a best effort of deallocating all the nodes in its freelist
		// in O(n)
		void deallocateAll() noexcept
		{
			deallocateAllImpl();
		}

		static constexpr bool has_fast_deallocateAll() { return has_op<Parent, Private::try_deallocateAll>::value; }

		bool owns(Blk b)
		{
			return Parent::owns(b);
		}

	private:
		struct Node
		{
			Node* next;
		};
		Node* m_pFreelistRoot{ nullptr };
		size_t m_nNodesCount{ 0 };

		bool inRange(size_t n) const
		{
			if (MinSize == MaxSize) return n == MaxSize;

			return (MinSize == 0 || n >= MinSize) && n <= MaxSize;
		}

		template<class... Args>
		Blk allocateImpl(size_t n, Args... args)
		{
			if (!inRange(n)) return Parent::allocate(n, args...);

			if (!m_pFreelistRoot)
			{
				auto const b = Parent::allocate(MaxSize, args...);
				return{ b.ptr, n };
			}
			else
			{
				Blk const result{ static_cast<void*>(m_pFreelistRoot), n };
				m_pFreelistRoot = m_pFreelistRoot->next;
				--m_nNodesCount;
				return result;
			}
		}

		void deallocateAllImpl(std::enable_if_t<has_op<Parent, Private::try_deallocateAll>::value>* = nullptr) noexcept
		{
			Parent::deallocateAll();
			m_pFreelistRoot = nullptr;
		}

		// In this case, we can only guarantee a complete deallocation if the freelist is unbounded
		void deallocateAllImpl(std::enable_if_t<
			and_<
			not_<has_op<Parent, Private::try_deallocateAll>>,
			has_op<Parent, Private::try_deallocate>,
			equal_<std::integral_constant<size_t, MaxNodes>, std::integral_constant<size_t, Allocator::unbounded>>
			>::value>* = nullptr) noexcept
		{
			auto next = m_pFreelistRoot;
			while (next)
			{
				Blk const b{ static_cast<void*>(next), MaxSize };
				next = next->next;
				Parent::deallocate(b);
			}
			m_pFreelistRoot = nullptr;
		}
	};

	template<class Parent, class PrefixType, class SuffixType>
	class AffixAllocator;

	namespace Private
	{
		template < class Parent, class PrefixType, class SuffixType, class Enable = void>
		struct AffixParentImpl : protected Parent {	};

		template < class Parent, class PrefixType, class SuffixType>
		struct AffixParentImpl<Parent, PrefixType, SuffixType, std::enable_if_t<StateSize<Parent>::value == 0>>
			: protected Parent
		{
			static AffixAllocator<Parent, PrefixType, SuffixType> it;
		};

		template<class PrefixType>
		struct PrefixImpl
		{
			template<class Enable = std::enable_if_t<StateSize<PrefixType>::value != 0>>
			static PrefixType& Prefix(Blk& b)
			{
				return reinterpret_cast<PrefixType*>(b.ptr)[-1];
			}
		};

		template<>
		struct PrefixImpl<void>{};

		template<class PrefixType, class SuffixType>
		struct SuffixImpl
		{
			template<class Enable = std::enable_if_t<StateSize<SuffixType>::value != 0>>
			static SuffixType& Suffix(Blk& b)
			{

				auto const p = static_cast<char*>(b.ptr) + b.length;
				return *reinterpret_cast<SuffixType*>(p);
			}

		protected:
			static size_t totalAllocationSize(size_t s)
			{
				if (StateSize<SuffixType>::value == 0)
				{
					return s + StateSize<PrefixType>::value;
				}
				else
				{
					return Math::RoundUpToMultipleOf(s + StateSize<PrefixType>::value, alignof(SuffixType)) + StateSize<SuffixType>::value;
				}
			}
		};

		template<class PrefixType>
		struct SuffixImpl<PrefixType, void>
		{
		protected:
			static size_t totalAllocationSize(size_t s)
			{
				return s + StateSize<PrefixType>::value;
			}
		};
	}

	template<class Parent, class PrefixType, class SuffixType = void>
	class AffixAllocator : public Private::AffixParentImpl<Parent, PrefixType, SuffixType>,
		public Private::PrefixImpl<PrefixType>,
		public Private::SuffixImpl<PrefixType, SuffixType>
	{
	public:
		static constexpr size_t alignment = StateSize<PrefixType>::value ? alignof(PrefixType) : Parent::alignment;

		Blk allocate(size_t n)
		{
			auto const result = Parent::allocate(totalAllocationSize(n));
			if (!result.ptr) return result;

			return{ static_cast<char*>(result.ptr) + StateSize<PrefixType>::value, n };
		}

		bool owns(Blk b)
		{
			return Parent::owns(actualAllocation(b));
		}

		void deallocate(Blk b)
		{
			Parent::deallocate(actualAllocation(b));
		}

	private:
		// Takes a requested allocation, and returns the actual allocation that was request to the parent
		static Blk actualAllocation(Blk b)
		{
			if (!b.ptr) return{ nullptr, 0 };
			return{ static_cast<char*>(b.ptr) - StateSize<PrefixType>::value, totalAllocationSize(b.length) };
		}
	};

	template<class Parent, class PrefixType, class SuffixType>
	AffixAllocator<Parent, PrefixType, SuffixType> Private::AffixParentImpl<Parent, PrefixType, SuffixType, std::enable_if_t<StateSize<Parent>::value == 0>>::it;

	template < size_t Threshold, class SmallAllocator, class LargeAllocator >
	class SegregateAllocator;

	namespace Private
	{
		template < size_t Threshold, class SmallAllocator, class LargeAllocator, class Enable = void>
		struct SegregateAllocatorImpl {	};

		template< class SmallAllocator, class LargeAllocator >
		using SegregateAllocatorStatelessCond = std::enable_if_t<
			and_<
			equal_<StateSize<SmallAllocator>, std::integral_constant<size_t, 0>>,
			equal_<StateSize<LargeAllocator>, std::integral_constant<size_t, 0>>
			>::value
		>;

		template < size_t Threshold, class SmallAllocator, class LargeAllocator >
		struct SegregateAllocatorImpl<Threshold, SmallAllocator, LargeAllocator,
			SegregateAllocatorStatelessCond<SmallAllocator, LargeAllocator> >
		{
			static SegregateAllocator<Threshold, SmallAllocator, LargeAllocator> it;
		};
	}

	// Seggregator
	template<size_t Threshold,
		class SmallAllocator,
		class LargeAllocator >
	class SegregateAllocator
		: private SmallAllocator,
		private LargeAllocator
	{
	public:
		constexpr static size_t alignment = Math::Min(SmallAllocator::alignment, LargeAllocator::alignment);

		Blk allocate(size_t n)
		{
			return allocate_Impl(n);
		}

		Blk allocate(size_t n, size_t alignment_requirement)
		{
			EXPECTS(alignment_requirement >= alignment && Math::IsPow2(alignment_requirement));
			return allocate_Impl(n, alignment_requirement);
		}

		bool owns(Blk b)
		{
			return n <= Threshold ? SmallAllocator::owns(b) : LargeAllocator::owns(b);
		}

		void deallocate(Blk b)
		{
			return n <= Threshold ? SmallAllocator::deallocate(b) : LargeAllocator::deallocate(b);
		}

		void deallocateAll()
		{
			SmallAllocator::deallocateAll();
			LargeAllocator::deallocateAll();
		}

	private:
		template<class... Args>
		Blk allocate_Impl(size_t n, Args... args)
		{
			return n <= Threshold ? SmallAllocator::allocate(n, args...) : LargeAllocator::allocate(n, args...);
		}
	};

	template < size_t Threshold, class SmallAllocator, class LargeAllocator >
	SegregateAllocator<Threshold, SmallAllocator, LargeAllocator> Private::SegregateAllocatorImpl<Threshold, SmallAllocator, LargeAllocator, Private::SegregateAllocatorStatelessCond<SmallAllocator, LargeAllocator>>::it;
}


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

#pragma once

#ifndef polygon_9cb4a5c6_8903_42fd_9232_eb81bfcd30a5_h
#define polygon_9cb4a5c6_8903_42fd_9232_eb81bfcd30a5_h

#include <gslib/std.h>
#include <gslib/math.h>
#include <ariel/config.h>

__ariel_begin__

class polygon
{
public:
    typedef gs::vector<point3> vtstream;
    typedef gs::vector<int> idstream;
    typedef vtstream::iterator vtiter;
    typedef vtstream::const_iterator const_vtiter;
    typedef idstream::iterator iditer;
    typedef idstream::const_iterator const_iditer;

public:
    polygon();
    int get_vertex_count() const { return (int)_vtstream.size(); }
    int get_index_count() const { return (int)_idstream.size(); }
    int get_face_count() const { return _faces; }
    int query_face_count();
    void write_vtstream(int start, int cnt, const point3* src);
    void append_vtstream(int cnt, const point3* src) { write_vtstream(get_vertex_count(), cnt, src); }
    void append_vtstream(const point3& src) { _vtstream.push_back(src); }
    void write_idstream(int start, int cnt, const int* src);
    void append_idstream(int cnt, const int* src) { write_idstream(get_index_count(), cnt, src); }
    void append_idstream(int idx) { _idstream.push_back(idx); }
    point3& get_vertex(int i) { return _vtstream.at(i); }
    const point3& get_vertex(int i) const { return _vtstream.at(i); }
    int& get_index(int i) { return _idstream.at(i); }
    const int& get_index(int i) const { return _idstream.at(i); }

protected:
    vtstream    _vtstream;
    idstream    _idstream;
    int         _faces;
};

__ariel_end__

#endif


#ifndef OPENMC_CELL_H
#define OPENMC_CELL_H

#include <cstdint>
#include <functional> // for hash
#include <limits>
#include <memory> // for unique_ptr
#include <string>
#include <unordered_map>
#include <vector>

#include <gsl/gsl>
#include "hdf5.h"
#include "pugixml.hpp"
#include "dagmc.h"

#include "openmc/constants.h"
#include "openmc/neighbor_list.h"
#include "openmc/position.h"
#include "openmc/surface.h"

namespace openmc {

//==============================================================================
// Constants
//==============================================================================

enum class Fill {
  MATERIAL,
  UNIVERSE,
  LATTICE
};

// TODO: Convert to enum
constexpr int32_t OP_LEFT_PAREN   {std::numeric_limits<int32_t>::max()};
constexpr int32_t OP_RIGHT_PAREN  {std::numeric_limits<int32_t>::max() - 1};
constexpr int32_t OP_COMPLEMENT   {std::numeric_limits<int32_t>::max() - 2};
constexpr int32_t OP_INTERSECTION {std::numeric_limits<int32_t>::max() - 3};
constexpr int32_t OP_UNION        {std::numeric_limits<int32_t>::max() - 4};

//==============================================================================
// Global variables
//==============================================================================

class Cell;
class ParentCell;
class CellInstance;
class Universe;
class UniversePartitioner;

namespace model {
  extern std::unordered_map<int32_t, int32_t> cell_map;
  extern std::vector<std::unique_ptr<Cell>> cells;

  extern std::unordered_map<int32_t, int32_t> universe_map;
  extern std::vector<std::unique_ptr<Universe>> universes;
} // namespace model

//==============================================================================
//! A geometry primitive that fills all space and contains cells.
//==============================================================================

class Universe
{
public:
  int32_t id_;                  //!< Unique ID
  std::vector<int32_t> cells_;  //!< Cells within this universe

  //! \brief Write universe information to an HDF5 group.
  //! \param group_id An HDF5 group id.
  void to_hdf5(hid_t group_id) const;

  BoundingBox bounding_box() const;

  std::unique_ptr<UniversePartitioner> partitioner_;
};

//==============================================================================
//==============================================================================

class Cell {
public:
  //----------------------------------------------------------------------------
  // Constructors, destructors, factory functions

  explicit Cell(pugi::xml_node cell_node);
  Cell() {};
  virtual ~Cell() = default;

  //----------------------------------------------------------------------------
  // Methods

  //! \brief Determine if a cell contains the particle at a given location.
  //!
  //! The bounds of the cell are detemined by a logical expression involving
  //! surface half-spaces. At initialization, the expression was converted
  //! to RPN notation.
  //!
  //! The function is split into two cases, one for simple cells (those
  //! involving only the intersection of half-spaces) and one for complex cells.
  //! Simple cells can be evaluated with short circuit evaluation, i.e., as soon
  //! as we know that one half-space is not satisfied, we can exit. This
  //! provides a performance benefit for the common case. In
  //! contains_complex, we evaluate the RPN expression using a stack, similar to
  //! how a RPN calculator would work.
  //! \param r The 3D Cartesian coordinate to check.
  //! \param u A direction used to "break ties" the coordinates are very
  //!   close to a surface.
  //! \param on_surface The signed index of a surface that the coordinate is
  //!   known to be on.  This index takes precedence over surface sense
  //!   calculations.
  virtual bool
  contains(Position r, Direction u, int32_t on_surface) const = 0;

  //! Find the oncoming boundary of this cell.
  virtual std::pair<double, int32_t>
  distance(Position r, Direction u, int32_t on_surface, Particle* p) const = 0;

  //! Write all information needed to reconstruct the cell to an HDF5 group.
  //! \param group_id An HDF5 group id.
  virtual void to_hdf5(hid_t group_id) const = 0;

  //! Get the BoundingBox for this cell.
  virtual BoundingBox bounding_box() const = 0;

  //----------------------------------------------------------------------------
  // Accessors

  //! Get the temperature of a cell instance
  //! \param[in] instance Instance index. If -1 is given, the temperature for
  //!   the first instance is returned.
  //! \return Temperature in [K]
  double temperature(int32_t instance = -1) const;

  //! Set the temperature of a cell instance
  //! \param[in] T Temperature in [K]
  //! \param[in] instance Instance index. If -1 is given, the temperature for
  //!   all instances is set.
  //! \param[in] set_contained If this cell is not filled with a material,
  //!   collect all contained cells with material fills and set their
  //!   temperatures.
  void set_temperature(double T, int32_t instance = -1, bool set_contained = false);

  //! Get the name of a cell
  //! \return Cell name
  const std::string& name() const { return name_; };

  //! Set the temperature of a cell instance
  //! \param[in] name Cell name
  void set_name(const std::string& name) { name_ = name; };

  //! Get all cell instances contained by this cell
  //! \return Map with cell indexes as keys and instances as values
  std::unordered_map<int32_t, std::vector<int32_t>>
  get_contained_cells() const;

protected:
  void
  get_contained_cells_inner(std::unordered_map<int32_t, std::vector<int32_t>>& contained_cells,
                            std::vector<ParentCell>& parent_cells) const;

public:
  //----------------------------------------------------------------------------
  // Data members

  int32_t id_;                //!< Unique ID
  std::string name_;          //!< User-defined name
  Fill type_;                  //!< Material, universe, or lattice
  int32_t universe_;          //!< Universe # this cell is in
  int32_t fill_;              //!< Universe # filling this cell
  int32_t n_instances_{0};    //!< Number of instances of this cell

  //! \brief Index corresponding to this cell in distribcell arrays
  int distribcell_index_{C_NONE};

  //! \brief Material(s) within this cell.
  //!
  //! May be multiple materials for distribcell.
  std::vector<int32_t> material_;

  //! \brief Temperature(s) within this cell.
  //!
  //! The stored values are actually sqrt(k_Boltzmann * T) for each temperature
  //! T. The units are sqrt(eV).
  std::vector<double> sqrtkT_;

  //! Definition of spatial region as Boolean expression of half-spaces
  std::vector<std::int32_t> region_;
  //! Reverse Polish notation for region expression
  std::vector<std::int32_t> rpn_;
  bool simple_;  //!< Does the region contain only intersections?

  //! \brief Neighboring cells in the same universe.
  NeighborList neighbors_;

  Position translation_ {0, 0, 0}; //!< Translation vector for filled universe

  //! \brief Rotational tranfsormation of the filled universe.
  //
  //! The vector is empty if there is no rotation. Otherwise, the first 9 values
  //! give the rotation matrix in row-major order. When the user specifies
  //! rotation angles about the x-, y- and z- axes in degrees, these values are
  //! also present at the end of the vector, making it of length 12.
  std::vector<double> rotation_;

  std::vector<int32_t> offset_;  //!< Distribcell offset table
};

struct CellInstanceItem {
  int32_t index {-1};        //! Index into global cells array
  int     lattice_indx{-1};  //! Flat index value of the lattice cell
};

//==============================================================================

class CSGCell : public Cell
{
public:
  CSGCell();

  explicit CSGCell(pugi::xml_node cell_node);

  bool
  contains(Position r, Direction u, int32_t on_surface) const;

  std::pair<double, int32_t>
  distance(Position r, Direction u, int32_t on_surface, Particle* p) const;

  void to_hdf5(hid_t group_id) const;

  BoundingBox bounding_box() const;

protected:
  bool contains_simple(Position r, Direction u, int32_t on_surface) const;
  bool contains_complex(Position r, Direction u, int32_t on_surface) const;
  BoundingBox bounding_box_simple() const;
  static BoundingBox bounding_box_complex(std::vector<int32_t> rpn);

  //! Applies DeMorgan's laws to a section of the RPN
  //! \param start Starting point for token modification
  //! \param stop Stopping point for token modification
  static void apply_demorgan(std::vector<int32_t>::iterator start,
                             std::vector<int32_t>::iterator stop);

  //! Removes complement operators from the RPN
  //! \param rpn The rpn to remove complement operators from.
  static void remove_complement_ops(std::vector<int32_t>& rpn);

  //! Returns the beginning position of a parenthesis block (immediately before
  //! two surface tokens) in the RPN given a starting position at the end of
  //! that block (immediately after two surface tokens)
  //! \param start Starting position of the search
  //! \param rpn The rpn being searched
  static std::vector<int32_t>::iterator
  find_left_parenthesis(std::vector<int32_t>::iterator start,
                        const std::vector<int32_t>& rpn);

};

//==============================================================================

#ifdef DAGMC
class DAGCell : public Cell
{
public:
  DAGCell();

  bool contains(Position r, Direction u, int32_t on_surface) const;

  std::pair<double, int32_t>
  distance(Position r, Direction u, int32_t on_surface, Particle* p) const;

  BoundingBox bounding_box() const;

  void to_hdf5(hid_t group_id) const;

  moab::DagMC* dagmc_ptr_; //!< Pointer to DagMC instance
  int32_t dag_index_;      //!< DagMC index of cell
};
#endif

//==============================================================================
//! Speeds up geometry searches by grouping cells in a search tree.
//
//! Currently this object only works with universes that are divided up by a
//! bunch of z-planes.  It could be generalized to other planes, cylinders,
//! and spheres.
//==============================================================================

class UniversePartitioner
{
public:
  explicit UniversePartitioner(const Universe& univ);

  //! Return the list of cells that could contain the given coordinates.
  const std::vector<int32_t>& get_cells(Position r, Direction u) const;

private:
  //! A sorted vector of indices to surfaces that partition the universe
  std::vector<int32_t> surfs_;

  //! Vectors listing the indices of the cells that lie within each partition
  //
  //! There are n+1 partitions with n surfaces.  `partitions_.front()` gives the
  //! cells that lie on the negative side of `surfs_.front()`.
  //! `partitions_.back()` gives the cells that lie on the positive side of
  //! `surfs_.back()`.  Otherwise, `partitions_[i]` gives cells sandwiched
  //! between `surfs_[i-1]` and `surfs_[i]`.
  std::vector<std::vector<int32_t>> partitions_;
};

//==============================================================================
//! Define a containing (parent) cell
//==============================================================================

struct ParentCell {
  gsl::index cell_index;
  gsl::index lattice_index;
};

//==============================================================================
//! Define an instance of a particular cell
//==============================================================================

struct CellInstance {
  //! Check for equality
  bool operator==(const CellInstance& other) const
  { return index_cell == other.index_cell && instance == other.instance; }

  gsl::index index_cell;
  gsl::index instance;
};

struct CellInstanceHash {
  std::size_t operator()(const CellInstance& k) const
  {
    return 4096*k.index_cell + k.instance;
  }
};

//==============================================================================
// Non-member functions
//==============================================================================

void read_cells(pugi::xml_node node);

#ifdef DAGMC
int32_t next_cell(DAGCell* cur_cell, DAGSurface* surf_xed);
#endif

} // namespace openmc
#endif // OPENMC_CELL_H


/* randist/gsl-randist.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 James Theiler, Brian Gough
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#include <gsl/gsl_randist.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_test.h>

void error (const char * s);

int
main (int argc, char *argv[])
{
  size_t i,j;
  size_t n = 0;
  double mu = 0, nu = 0, nu1 = 0, nu2 = 0, sigma = 0, a = 0, b = 0, c = 0;
  double zeta = 0, sigmax = 0, sigmay = 0, rho = 0;
  double p = 0;
  double x = 0, y =0, z=0  ;
  unsigned int N = 0, t = 0, n1 = 0, n2 = 0 ;
  unsigned long int seed = 0 ;
  const char * name ;
  gsl_rng * r ;

  if (argc < 4)
    {
      printf (
"Usage: gsl-randist seed n DIST param1 param2 ...\n"
"Generates n samples from the distribution DIST with parameters param1,\n"
"param2, etc. Valid distributions are,\n\n");

      printf(
"  beta\n"
"  binomial\n"
"  bivariate-gaussian\n"
"  cauchy\n"
"  chisq\n"
"  dir-2d\n"
"  dir-3d\n"
"  dir-nd\n"
"  erlang\n"
"  exponential\n"
"  exppow\n"
"  fdist\n"
"  flat\n"
"  gamma\n"
"  gaussian-tail\n"
"  gaussian\n"
"  geometric\n"
"  gumbel1\n"
"  gumbel2\n"
"  hypergeometric\n"
"  laplace\n"
"  landau\n"
"  levy\n"
"  levy-skew\n"
"  logarithmic\n"
"  logistic\n"
"  lognormal\n"
"  negative-binomial\n"
"  pareto\n"
"  pascal\n"
"  poisson\n"
"  rayleigh-tail\n"
"  rayleigh\n"
"  tdist\n"
"  ugaussian-tail\n"
"  ugaussian\n"
"  weibull\n") ;
      exit (0);
    }

  argv++ ; seed = atol (argv[0]); argc-- ;
  argv++ ; n = atol (argv[0]); argc-- ;
  argv++ ; name = argv[0] ; argc-- ; argc-- ;

  gsl_rng_env_setup() ;

  if (gsl_rng_default_seed != 0) {
    fprintf(stderr,
            "overriding GSL_RNG_SEED with command line value, seed = %ld\n",
            seed) ;
  }

  gsl_rng_default_seed = seed ;

  r = gsl_rng_alloc(gsl_rng_default) ;

#define NAME(x) !strcmp(name,(x))
#define OUTPUT(x) for (i = 0; i < n; i++) { printf("%g\n", (x)) ; }
#define OUTPUT1(a,x) for(i = 0; i < n; i++) { a ; printf("%g\n", x) ; }
#define OUTPUT2(a,x,y) for(i = 0; i < n; i++) { a ; printf("%g %g\n", x, y) ; }
#define OUTPUT3(a,x,y,z) for(i = 0; i < n; i++) { a ; printf("%g %g %g\n", x, y, z) ; }
#define INT_OUTPUT(x) for (i = 0; i < n; i++) { printf("%d\n", (x)) ; }
#define ARGS(x,y) if (argc != x) error(y) ;
#define DBL_ARG(x) if (argc) { x=atof((++argv)[0]);argc--;} else {error( #x);};
#define INT_ARG(x) if (argc) { x=atoi((++argv)[0]);argc--;} else {error( #x);};

  if (NAME("bernoulli"))
    {
      ARGS(1, "p = probability of success");
      DBL_ARG(p)
      INT_OUTPUT(gsl_ran_bernoulli (r, p));
    }
  else if (NAME("beta"))
    {
      ARGS(2, "a,b = shape parameters");
      DBL_ARG(a)
      DBL_ARG(b)
      OUTPUT(gsl_ran_beta (r, a, b));
    }
  else if (NAME("binomial"))
    {
      ARGS(2, "p = probability, N = number of trials");
      DBL_ARG(p)
      INT_ARG(N)
      INT_OUTPUT(gsl_ran_binomial (r, p, N));
    }
  else if (NAME("cauchy"))
    {
      ARGS(1, "a = scale parameter");
      DBL_ARG(a)
      OUTPUT(gsl_ran_cauchy (r, a));
    }
  else if (NAME("chisq"))
    {
      ARGS(1, "nu = degrees of freedom");
      DBL_ARG(nu)
      OUTPUT(gsl_ran_chisq (r, nu));
    }
  else if (NAME("erlang"))
    {
      ARGS(2, "a = scale parameter, b = order");
      DBL_ARG(a)
      DBL_ARG(b)
      OUTPUT(gsl_ran_erlang (r, a, b));
    }
  else if (NAME("exponential"))
    {
      ARGS(1, "mu = mean value");
      DBL_ARG(mu) ;
      OUTPUT(gsl_ran_exponential (r, mu));
    }
  else if (NAME("exppow"))
    {
      ARGS(2, "a = scale parameter, b = power (1=exponential, 2=gaussian)");
      DBL_ARG(a) ;
      DBL_ARG(b) ;
      OUTPUT(gsl_ran_exppow (r, a, b));
    }
  else if (NAME("fdist"))
    {
      ARGS(2, "nu1, nu2 = degrees of freedom parameters");
      DBL_ARG(nu1) ;
      DBL_ARG(nu2) ;
      OUTPUT(gsl_ran_fdist (r, nu1, nu2));
    }
  else if (NAME("flat"))
    {
      ARGS(2, "a = lower limit, b = upper limit");
      DBL_ARG(a) ;
      DBL_ARG(b) ;
      OUTPUT(gsl_ran_flat (r, a, b));
    }
  else if (NAME("gamma"))
    {
      ARGS(2, "a = order, b = scale");
      DBL_ARG(a) ;
      DBL_ARG(b) ;
      OUTPUT(gsl_ran_gamma (r, a, b));
    }
  else if (NAME("gaussian"))
    {
      ARGS(1, "sigma = standard deviation");
      DBL_ARG(sigma) ;
      OUTPUT(gsl_ran_gaussian (r, sigma));
    }
  else if (NAME("gaussian-tail"))
    {
      ARGS(2, "a = lower limit, sigma = standard deviation");
      DBL_ARG(a) ;
      DBL_ARG(sigma) ;
      OUTPUT(gsl_ran_gaussian_tail (r, a, sigma));
    }
  else if (NAME("ugaussian"))
    {
      ARGS(0, "unit gaussian, no parameters required");
      OUTPUT(gsl_ran_ugaussian (r));
    }
  else if (NAME("ugaussian-tail"))
    {
      ARGS(1, "a = lower limit");
      DBL_ARG(a) ;
      OUTPUT(gsl_ran_ugaussian_tail (r, a));
    }
  else if (NAME("bivariate-gaussian"))
    {
      ARGS(3, "sigmax = x std.dev., sigmay = y std.dev., rho = correlation");
      DBL_ARG(sigmax) ;
      DBL_ARG(sigmay) ;
      DBL_ARG(rho) ;
      OUTPUT2(gsl_ran_bivariate_gaussian (r, sigmax, sigmay, rho, &x, &y),
              x, y);
    }
  else if (NAME("dir-2d"))
    {
      OUTPUT2(gsl_ran_dir_2d (r, &x, &y), x, y);
    }
  else if (NAME("dir-3d"))
    {
      OUTPUT3(gsl_ran_dir_3d (r, &x, &y, &z), x, y, z);
    }
  else if (NAME("dir-nd"))
    {
      double *xarr;
      ARGS(1, "n1 = number of dimensions of hypersphere");
      INT_ARG(n1) ;
      xarr = (double *)malloc(n1*sizeof(double));

      for(i = 0; i < n; i++) {
        gsl_ran_dir_nd (r, n1, xarr) ;
        for (j = 0; j < n1; j++) {
          if (j) putchar(' ');
          printf("%g", xarr[j]) ;
        }
        putchar('\n');
      } ;

      free(xarr);
    }
  else if (NAME("geometric"))
    {
      ARGS(1, "p = bernoulli trial probability of success");
      DBL_ARG(p) ;
      INT_OUTPUT(gsl_ran_geometric (r, p));
    }
  else if (NAME("gumbel1"))
    {
      ARGS(2, "a = order, b = scale parameter");
      DBL_ARG(a) ;
      DBL_ARG(b) ;
      OUTPUT(gsl_ran_gumbel1 (r, a, b));
    }
  else if (NAME("gumbel2"))
    {
      ARGS(2, "a = order, b = scale parameter");
      DBL_ARG(a) ;
      DBL_ARG(b) ;
      OUTPUT(gsl_ran_gumbel2 (r, a, b));
    }
  else if (NAME("hypergeometric"))
    {
      ARGS(3, "n1 = tagged population, n2 = untagged population, t = number of trials");
      INT_ARG(n1) ;
      INT_ARG(n2) ;
      INT_ARG(t) ;
      INT_OUTPUT(gsl_ran_hypergeometric (r, n1, n2, t));
    }
  else if (NAME("laplace"))
    {
      ARGS(1, "a = scale parameter");
      DBL_ARG(a) ;
      OUTPUT(gsl_ran_laplace (r, a));
    }
  else if (NAME("landau"))
    {
      ARGS(0, "no arguments required");
      OUTPUT(gsl_ran_landau (r));
    }
  else if (NAME("levy"))
    {
      ARGS(2, "c = scale, a = power (1=cauchy, 2=gaussian)");
      DBL_ARG(c) ;
      DBL_ARG(a) ;
      OUTPUT(gsl_ran_levy (r, c, a));
    }
  else if (NAME("levy-skew"))
    {
      ARGS(3, "c = scale, a = power (1=cauchy, 2=gaussian), b = skew");
      DBL_ARG(c) ;
      DBL_ARG(a) ;
      DBL_ARG(b) ;
      OUTPUT(gsl_ran_levy_skew (r, c, a, b));
    }
  else if (NAME("logarithmic"))
    {
      ARGS(1, "p = probability");
      DBL_ARG(p) ;
      INT_OUTPUT(gsl_ran_logarithmic (r, p));
    }
  else if (NAME("logistic"))
    {
      ARGS(1, "a = scale parameter");
      DBL_ARG(a) ;
      OUTPUT(gsl_ran_logistic (r, a));
    }
  else if (NAME("lognormal"))
    {
      ARGS(2, "zeta = location parameter, sigma = scale parameter");
      DBL_ARG(zeta) ;
      DBL_ARG(sigma) ;
      OUTPUT(gsl_ran_lognormal (r, zeta, sigma));
    }
  else if (NAME("negative-binomial"))
    {
      ARGS(2, "p = probability, a = order");
      DBL_ARG(p) ;
      DBL_ARG(a) ;
      INT_OUTPUT(gsl_ran_negative_binomial (r, p, a));
    }
  else if (NAME("pareto"))
    {
      ARGS(2, "a = power, b = scale parameter");
      DBL_ARG(a) ;
      DBL_ARG(b) ;
      OUTPUT(gsl_ran_pareto (r, a, b));
    }
  else if (NAME("pascal"))
    {
      ARGS(2, "p = probability, n = order (integer)");
      DBL_ARG(p) ;
      INT_ARG(N) ;
      INT_OUTPUT(gsl_ran_pascal (r, p, N));
    }
  else if (NAME("poisson"))
    {
      ARGS(1, "mu = scale parameter");
      DBL_ARG(mu) ;
      INT_OUTPUT(gsl_ran_poisson (r, mu));
    }
  else if (NAME("rayleigh"))
    {
      ARGS(1, "sigma = scale parameter");
      DBL_ARG(sigma) ;
      OUTPUT(gsl_ran_rayleigh (r, sigma));
    }
  else if (NAME("rayleigh-tail"))
    {
      ARGS(2, "a = lower limit, sigma = scale parameter");
      DBL_ARG(a) ;
      DBL_ARG(sigma) ;
      OUTPUT(gsl_ran_rayleigh_tail (r, a, sigma));
    }
  else if (NAME("tdist"))
    {
      ARGS(1, "nu = degrees of freedom");
      DBL_ARG(nu) ;
      OUTPUT(gsl_ran_tdist (r, nu));
    }
  else if (NAME("weibull"))
    {
      ARGS(2, "a = scale parameter, b = exponent");
      DBL_ARG(a) ;
      DBL_ARG(b) ;
      OUTPUT(gsl_ran_weibull (r, a, b));
    }
  else
    {
      fprintf(stderr,"Error: unrecognized distribution: %s\n", name) ;
    }

  return 0 ;
}

void
error (const char * s)
{
  fprintf(stderr, "Error: arguments should be %s\n",s) ;
  exit (EXIT_FAILURE) ;
}


/*
 * C version of Diffusive Nested Sampling (DNest4) by Brendon J. Brewer
 *
 * Yan-Rong Li, liyanrong@mail.ihep.ac.cn
 * Jun 30, 2016
 *
 */

#ifndef _MODEL3_H
#define _MODEL3_H

#include <stdbool.h>
#include <gsl/gsl_rng.h>

/* number of model parameters */
extern int num_params;

extern int which_particle_update; // which particule to be updated
extern int which_level_update;
extern double *limits;
extern int thistask, totaltask;

extern DNestFptrSet *fptrset_thismodel3;

/* functions */
void from_prior_thismodel3(void *model);
void print_particle_thismodel3(FILE *fp, const void *model);
double log_likelihoods_cal_thismodel3(const void *model);
double perturb_thismodel3(void *model);
void restart_action_model3(int iflag);

#endif


#ifndef I3SPRNGRANDOMSERVICE_H
#define I3SPRNGRANDOMSERVICE_H

#include "phys-services/I3RandomService.h"

#include <gsl/gsl_randist.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_test.h>

#include <string>

/**
 * copyright  (C) 2004
 * the icecube collaboration
 * $Id: I3SPRNGRandomService.h 127790 2015-01-14 04:03:06Z olivas $
 *
 * @brief SPRNG Implementation of the I3RandomService interface.
 * This implementation uses a combination of SPRNG and GSL to generate
 * statistically independent streams of pseudo-random number distributions.
 * See gsl-sprng.h for more details.
 *
 * NB : It's important that you use the same seed for different jobs.  Set
 * nstreams to the number of jobs and use a different streamnum for each job.
 * Otherwise you'll get correlations between the RNG streams.  I know this is
 * counterintuitive, but this is how SPRNG works.
 *
 * The code for this class is based on John Pretz's implementation of
 * I3GSLRandomService.
 *
 * @version $Revision: 127790 $
 * @date $Date: 2015-01-13 21:03:06 -0700 (Tue, 13 Jan 2015) $
 * @author juancarlos
 *
 * @todo Add ability to save state of rng after run is complete
 *  SPRNG has the functions:
 *
 *      int pack_sprng(char *bytes); // returns size of bytes
 *      void unpack_sprng(char bytes[MAX_PACKED_LENGTH]);
 *
 *  which can be used to save and retrieve the state of an rng
 */

class I3SPRNGRandomService : public I3RandomService{
 public:
  /**
   * default constructor
   */
  I3SPRNGRandomService();

  /**
   * constructors
   */
  I3SPRNGRandomService(int seed, int nstreams, int streamnum,
		       std::string instatefile=std::string(),
		       std::string outstatefile=std::string());

  /**
   * destructor
   */
  virtual ~I3SPRNGRandomService();

  /**
   * Binomial distribution
   */
  virtual int Binomial(int ntot, double prob);

  // As with John Pretz's GSL implementation, I have left this out for now.
  /* virtual double BreitWigner(double mean = 0, double gamma = 1)=0; */

  /**
   * Exponential distribution
   */
  virtual double Exp(double tau);

  /**
   * Uniform int distribution with range [0,imax)
   */
  virtual unsigned int Integer(unsigned int imax);

  /**
   * int Poisson distribution
   */
  virtual int Poisson(double mean);

  /**
   * double Poisson distribution
   */
  virtual double PoissonD(double mean);

  /**
   * double uniform distribution with range (0,x1)
   */
  virtual double Uniform(double x1 = 1);

  /**
   * double  uniform distribution with range (x1,x2)
   */
  virtual double Uniform(double x1, double x2);

  /**
   * double Gaussian distribution given mean and StdD
   */
  virtual double Gaus(double mean, double stddev);

  virtual I3FrameObjectPtr GetState() const;
  virtual void RestoreState(I3FrameObjectConstPtr state);

 private:

  // private copy constructors and assignment
  I3SPRNGRandomService(const I3SPRNGRandomService& );
  I3SPRNGRandomService operator=(const I3SPRNGRandomService& );

  gsl_rng* rng_;
  std::string instatefile_;
  std::string outstatefile_;

  int seed_;
  int streamnum_;
  int nstreams_;

  SET_LOGGER("I3SPRNGRandomService");

};

I3_POINTER_TYPEDEFS(I3SPRNGRandomService);

#endif // I3SPRNGRANDOMSERVICE_H


/* ieee-utils/fp-os2.c
 *
 * Copyright (C) 2001 Henry Sobotka <sobotka@axess.com>
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <float.h>
#include <gsl/gsl_ieee_utils.h>
#include <gsl/gsl_errno.h>

int
gsl_ieee_set_mode (int precision, int rounding, int exception_mask)
{
  unsigned mode = 0;

  switch (precision)
    {
    case GSL_IEEE_SINGLE_PRECISION:
      _control87(PC_24, MCW_PC);
      break ;
    case GSL_IEEE_DOUBLE_PRECISION:
      _control87(PC_53, MCW_PC);
      break ;
    case GSL_IEEE_EXTENDED_PRECISION:
      _control87(PC_64, MCW_PC);
      break ;
    }

  switch (rounding)
    {
    case GSL_IEEE_ROUND_TO_NEAREST:
      _control87(RC_NEAR, MCW_RC);
      break ;
    case GSL_IEEE_ROUND_DOWN:
      _control87(RC_DOWN, MCW_RC);
      break ;
    case GSL_IEEE_ROUND_UP:
      _control87(RC_UP, MCW_RC);
      break ;
    case GSL_IEEE_ROUND_TO_ZERO:
      _control87(RC_CHOP, MCW_RC);
      break ;
    default:
      _control87(RC_NEAR, MCW_RC);
    }

  /* Turn on all the exceptions apart from 'inexact' */

  mode = EM_INVALID | EM_DENORMAL | EM_ZERODIVIDE | EM_OVERFLOW | EM_UNDERFLOW;

  if (exception_mask & GSL_IEEE_MASK_INVALID)
    mode &= ~ EM_INVALID;

  if (exception_mask & GSL_IEEE_MASK_DENORMALIZED)
    mode &= ~ EM_DENORMAL;

  if (exception_mask & GSL_IEEE_MASK_DIVISION_BY_ZERO)
    mode &= ~ EM_ZERODIVIDE;

  if (exception_mask & GSL_IEEE_MASK_OVERFLOW)
    mode &= ~ EM_OVERFLOW;

  if (exception_mask & GSL_IEEE_MASK_UNDERFLOW)
    mode &= ~ EM_UNDERFLOW;

  if (exception_mask & GSL_IEEE_TRAP_INEXACT)
    {
      mode |= EM_INEXACT;
    }
  else
    {
      mode &= ~ EM_INEXACT;
    }

  _control87(mode, MCW_EM);

  return GSL_SUCCESS ;
}


/* gsl_histogram2d_calloc_range.c
 * Copyright (C) 2000  Simone Piccardi
 *
 * This library 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 library; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
/***************************************************************
 *
 * File gsl_histogram2d_calloc_range.c:
 * Routine to create a variable binning 2D histogram providing
 * the input range vectors. Need GSL library and header.
 * Do range check and allocate the histogram data.
 *
 * Author: S. Piccardi
 * Jan. 2000
 *
 ***************************************************************/
#include <config.h>
#include <stdlib.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_histogram2d.h>
/*
 * Routine that create a 2D histogram using the given
 * values for X and Y ranges
 */
gsl_histogram2d *
gsl_histogram2d_calloc_range (size_t nx, size_t ny,
                              double *xrange,
                              double *yrange)
{
  size_t i, j;
  gsl_histogram2d *h;

  /* check arguments */

  if (nx == 0)
    {
      GSL_ERROR_VAL ("histogram length nx must be positive integer",
                        GSL_EDOM, 0);
    }

  if (ny == 0)
    {
      GSL_ERROR_VAL ("histogram length ny must be positive integer",
                        GSL_EDOM, 0);
    }

  /* init ranges */

  for (i = 0; i < nx; i++)
    {
      if (xrange[i] >= xrange[i + 1])
        {
          GSL_ERROR_VAL ("histogram xrange not in increasing order",
                            GSL_EDOM, 0);
        }
    }

  for (j = 0; j < ny; j++)
    {
      if (yrange[j] >= yrange[j + 1])
        {
          GSL_ERROR_VAL ("histogram yrange not in increasing order"
                            ,GSL_EDOM, 0);
        }
    }

  /* Allocate histogram  */

  h = (gsl_histogram2d *) malloc (sizeof (gsl_histogram2d));

  if (h == 0)
    {
      GSL_ERROR_VAL ("failed to allocate space for histogram struct",
                        GSL_ENOMEM, 0);
    }

  h->xrange = (double *) malloc ((nx + 1) * sizeof (double));

  if (h->xrange == 0)
    {
      /* exception in constructor, avoid memory leak */
      free (h);

      GSL_ERROR_VAL ("failed to allocate space for histogram xrange",
                        GSL_ENOMEM, 0);
    }

  h->yrange = (double *) malloc ((ny + 1) * sizeof (double));

  if (h->yrange == 0)
    {
      /* exception in constructor, avoid memory leak */
      free (h);

      GSL_ERROR_VAL ("failed to allocate space for histogram yrange",
                        GSL_ENOMEM, 0);
    }

  h->bin = (double *) malloc (nx * ny * sizeof (double));

  if (h->bin == 0)
    {
      /* exception in constructor, avoid memory leak */
      free (h->xrange);
      free (h->yrange);
      free (h);

      GSL_ERROR_VAL ("failed to allocate space for histogram bins",
                        GSL_ENOMEM, 0);
    }

  /* init histogram */

  /* init ranges */

  for (i = 0; i <= nx; i++)
    {
      h->xrange[i] = xrange[i];
    }

  for (j = 0; j <= ny; j++)
    {
      h->yrange[j] = yrange[j];
    }

  /* clear contents */

  for (i = 0; i < nx; i++)
    {
      for (j = 0; j < ny; j++)
        {
          h->bin[i * ny + j] = 0;
        }
    }

  h->nx = nx;
  h->ny = ny;

  return h;
}


#ifndef DEINOS_CHESS_H
#define DEINOS_CHESS_H
#include <optional>
#include <array>
#include <vector>
#include <string>
#include <cassert>
#include <iostream>
#include <gsl/gsl_assert>
#include <cstddef>

namespace chess {
	enum class Almnt : uint8_t;
	enum class AlRes : uint8_t;
	enum class GameResult : uint8_t;
	enum class Side : uint8_t;
	struct Piece;
	class Square;
	struct MoveRecord;
	class Move;
	class Position;

	//Alignment (i.e. White or Black)
	enum class Almnt : uint8_t {White = 0, Black = 1};
	constexpr Almnt operator!(const Almnt& a) {return (a == Almnt::White ? Almnt::Black : Almnt::White);}
	inline uint8_t as_index(Almnt a) {return static_cast<uint8_t>(a);}

	//Alignment Result (for cases where "None" is an option)
	enum class AlRes : uint8_t {White = 0, Black = 1, None = 2}; //Utility to avoid constant Empty checks
	inline bool operator==(AlRes ar, Almnt a) {return static_cast<uint8_t>(ar) == static_cast<uint8_t>(a);}
	inline bool operator==(Almnt a, AlRes ar) {return ar == a;}
	inline bool operator!=(AlRes ar, Almnt a) {return !(ar == a);}
	inline bool operator!=(Almnt a, AlRes ar) {return !(ar == a);}

	//Game Result
	enum class GameResult : uint8_t {White = 0, Black = 1, Draw = 2};
	inline float evaluate(GameResult gr) {
		switch (gr) {
			case GameResult::White: return 1.0f;
			case GameResult::Black: return 0.0f;
			case GameResult::Draw: return 0.5f;
			default: return 0.5f;
		}
	}
	inline GameResult victory(Almnt a) {return static_cast<GameResult>(a);}
	inline AlRes victor(GameResult gr) {return static_cast<AlRes>(gr);}

	//Side of Board
	enum class Side : uint8_t {Kingside = 0, Queenside = 1};

	//The contents of a square (i.e. a chess piece or empty)
	struct Piece {
		enum class Type : uint8_t {Empty, Pawn, Knight, Bishop, Rook, Queen, King};

		constexpr Piece() = default;
		constexpr Piece(Almnt a, Type t) : data((static_cast<std::byte>(t) << 1) | static_cast<std::byte>(a)) {}

		inline AlRes almnt_res() const {return (type() == Type::Empty ? AlRes::None : static_cast<AlRes>(almnt()));}
		inline Almnt almnt() const {return static_cast<Almnt>(data & std::byte{0x01});}
		inline Type type() const {return static_cast<Type>(data >> 1);}

	private:
		constexpr Piece(uint8_t d) : data(std::byte{d}) {}
		inline uint8_t raw() const {return static_cast<uint8_t>(data);}
		std::byte data = std::byte{0};
		friend class Position;
	};

	bool operator==(Piece, Piece);
	bool operator!=(Piece, Piece);
	std::ostream& operator<<(std::ostream& os, Piece::Type t);
	std::ostream& operator<<(std::ostream& os, Piece p); //White uppercase, black lowercase

	//A bounds checked index into a chess position
	class Square{
	public:
		constexpr Square() = default;
		constexpr Square(const char* name){ //this must be a zstring TODO
			constexpr std::array<char, 8> files {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
			constexpr std::array<char, 8> ranks {'1', '2', '3', '4', '5', '6', '7', '8'};
			uint8_t file = 255;
			uint8_t rank = 255;
			for (int i = 0; i < 8; i++) {
				if (files[i] == name[0]) file = i;
				if (ranks[i] == name[1]) rank = i;
			}
			assert(file != 255);
			assert(rank != 255);
			data = (std::byte{rank} << 3) | std::byte{file};
		}

		std::optional<Square> translate(int files, int ranks) const; //TODO consider changing type to int8_t
		inline uint8_t file() const {return std::to_integer<uint8_t>(data & std::byte{0b00000111});}
		inline uint8_t rank() const {return std::to_integer<uint8_t>((data & std::byte{0b00111000}) >> 3);}
		explicit operator std::string () const;
	private:
		constexpr Square(uint8_t file, uint8_t rank) : data((std::byte{rank} << 3) | std::byte{file}) {
			assert(file < 8);
			assert(rank < 8);
		}
		constexpr Square(std::byte d) : data(d) {} //construct from raw data
		std::byte data = std::byte{0};
		friend class MoveRecord;
		friend bool operator==(Square, Square);
	};
	bool operator==(Square, Square);
	bool operator!=(Square, Square);

	constexpr std::array<Square, 64> all_squares {
		"a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1",
		"a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2",
		"a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3",
		"a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4",
		"a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5",
		"a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6",
		"a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7",
		"a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8",
		};

	//stores information necessary to reconstruct a move from a position
	struct MoveRecord {
	private:

		inline uint8_t promo_index() const {return static_cast<uint8_t>(data2) >> 6;}
	public:
		constexpr static std::array<Piece::Type, 4> promo_types
			{Piece::Type::Knight, Piece::Type::Bishop, Piece::Type::Rook, Piece::Type::Queen};

		static_assert(sizeof(Square) == 1);
		constexpr MoveRecord(Square t_initial, Square t_final)
			: data1(reinterpret_cast<std::byte&>(t_initial)), data2(reinterpret_cast<std::byte&>(t_final)) {}
		MoveRecord(Square t_initial, Square t_final, Piece::Type t_promo_type);

		inline Square initial() const {return Square(data1 & std::byte{0x3F});}
		inline Square final() const {return Square(data2 & std::byte{0x3F});}
		inline bool is_promo() const {return (data1 & std::byte{0b11000000}) != std::byte{0};}
		inline std::optional<Piece::Type> promo_type() const
		{
			return is_promo() ? std::make_optional<Piece::Type>(promo_types[promo_index()]) : std::nullopt;
		};
		std::string to_string() const;
	private:
		std::byte data1 = std::byte{0}; //initial square and promotion existence flag
		std::byte data2 = std::byte{0}; //final square and promotion type flag
	};
	bool operator==(const MoveRecord&, const MoveRecord&);
	inline bool operator!=(const MoveRecord& mr1, const MoveRecord& mr2) {return !(mr1 == mr2);}
	inline std::ostream& operator<<(std::ostream& os, const MoveRecord& mr) {os << mr.to_string(); return os;}

	//64 half-byte uints stored in 32B
	struct HalfByteBoard {
	private:
		inline uint8_t get_front(int i) const {return data[i] >> 4;}
		inline uint8_t get_back(int i) const {return data[i] & 0x0F;}
		inline void set_front(int i, uint8_t val) {data[i] = (val << 4) | (data[i] & 0x0F);}
		inline void set_back(int i, uint8_t val) {data[i] = val | (data[i] & 0xF0);}
	public:
		inline uint8_t get(int i) const
		{
			assert(i >= 0);
			assert(i < 64);
			if (i % 2 == 0) return get_front(i / 2);
			else return get_back(i / 2);
		}
		inline uint8_t get(int file, int rank) const {return get(rank * 8 + file);}
		inline void set(int i, uint8_t val)
		{
			assert(i >= 0);
			assert(i < 64);
			assert(val <= 0x0F);
			if (i % 2 == 0) set_front(i / 2, val);
			else set_back(i / 2, val);
		}
		inline void set(int file, int rank, uint8_t val) {set(rank * 8 + file, val);}
	private:
		std::array<uint8_t, 32> data = {0};
		friend bool operator==(const HalfByteBoard&, const HalfByteBoard&);
	};
	inline bool operator==(const HalfByteBoard& hbb1, const HalfByteBoard& hbb2) {return hbb1.data == hbb2.data;}
	inline bool operator!=(const HalfByteBoard& hbb1, const HalfByteBoard& hbb2) {return !(hbb1 == hbb2);}

	//Equivalent to a chess position recorded in Forsyth-Edwards Notaion
	class Position {
	public:
		constexpr Position() = default;
		Position(const std::string& fen); //construct from FEN representation
		Position(const Position&, const Move&); //generate new position by applying a move
		static Position std_start();

		//inline Piece& operator[] (Square s) {return m_board[s.file()][s.rank()];}
		//inline Piece operator[] (Square s) const {return m_board[s.file()][s.rank()];}
		inline Piece at(Square s) const {return Piece(m_board.get(s.file(), s.rank()));}
		//inline Piece at(Square s) const {return m_board[s.file()][s.rank()];}
		inline Piece at(int index) const {return Piece(m_board.get(index));}
		//inline Piece at(int index) const {return m_board[index % 8][index / 8];}
		inline void set(Square s, Piece p) {m_board.set(s.file(), s.rank(), p.raw());}
		//inline void set(Square s, Piece p) {m_board[s.file()][s.rank()] = p;}
		inline void set(int index, Piece p) {m_board.set(index, p.raw());}
		//inline void set(int index, Piece p) {m_board[index % 8][index / 8] = p;}
		inline bool can_castle(Almnt a, Side s) const {return m_castle[(int) a][(int) s];} //TODO use as_index
		inline bool& mut_castle(Almnt a, Side s){return m_castle[(int) a][(int) s];}
		inline Almnt to_move() const {return m_to_move;}
		inline std::optional<Square> en_passant_target() const {return m_en_passant_target;}
		inline int hm_clock() const {return m_hm_clock;}
		inline int fm_count() const {return m_fm_count;}

		std::string as_fen() const;
		explicit operator std::string() const;
		friend std::ostream& operator<<(std::ostream& os, const Position& pos);
		friend bool operator==(const Position&, const Position&);

	private:
		static_assert(sizeof(Piece) == 1);
		HalfByteBoard m_board;
		//std::array<std::array<Piece, 8>, 8> m_board = {Piece()};
		Almnt m_to_move = Almnt::White;
		std::array<std::array<bool, 2>, 2> m_castle = {{false}}; //"permitted to castle" flags indexed by enum values
		//std::optional<GameResult> m_result = std::nullopt;
		std::optional<Square> m_en_passant_target = std::nullopt;
		short m_hm_clock = 0;
		short m_fm_count = 1;
	};
	bool operator==(const Position&, const Position&);
	bool operator!=(const Position&, const Position&);
	std::ostream& operator<<(std::ostream& os, const Position& pos);

	//A wrapper for a position and move record, representing a single move
	class Move { //TODO: test code
	public:
		//constexpr Move() = default; //default constructor
		//Move(Square initial, Square final, Piece moved, Piece captured); //normal move
		//Move(const Position& pos, const std::string& name); //N.B this constructor does not support castling, promotion or en passant
		//void set_castling(); //castling 1.must be a king moving 2.must not capture 3.can only set once
		//void set_promotion(Piece::Type); //pawn promotion
		//void set_en_passant(); //en_passant
		constexpr Move(const Position& t_pos, const MoveRecord& t_record) : m_pos(t_pos), m_record(t_record) {
			if (moved().type() == Piece::Type::Empty) {std::cerr << *this << std::endl << t_pos << std::endl; throw std::exception();}
			if (captured().almnt_res() == moved().almnt()) {std::cerr << *this << " capt: " << captured() << " mr: " << m_record << std::endl << t_pos << std::endl; throw std::exception();}
			assert(moved().type() != Piece::Type::Empty);
			assert(captured().almnt_res() != moved().almnt());
		}
		//inline Square initial_square() const {return m_initial_square;}
		inline Square initial_sq() const {return m_record.initial();}
		//inline Square final_square() const {return m_final_square;}
		inline Square final_sq() const {return m_record.final();}
		//inline Piece moved_piece() const {return m_moved_piece;}
		inline Piece moved() const {return m_pos.at(initial_sq());}
		//inline Piece captured_piece() const {return m_captured_piece;}
		inline Piece captured() const {return m_pos.at(final_sq());} //returns empty piece if en passant
		//inline bool is_en_passant() const {return m_is_en_passant;}
		bool is_en_passant() const;
		//inline bool is_castling() const {return m_is_castling;}
		bool is_castling() const;
		//inline bool is_promotion() const {return m_promoted_to.type() != Piece::Type::Empty;}
		inline bool is_promotion() const {return m_record.is_promo();}
		//inline Piece promoted_to() const {return m_promoted_to;}
		inline std::optional<Piece::Type> promo_type() const {return m_record.promo_type();}
		inline const MoveRecord& record() const {return m_record;}
		inline Position apply() const {return Position(m_pos, *this);}

		explicit operator std::string() const;
		friend std::ostream& operator<<(std::ostream& os, const Move& mv);
		std::string to_xboard() const;

	private:
		//Square m_initial_square;
		//Square m_final_square;
		//Piece m_moved_piece;
		//Piece m_captured_piece;
		//Piece m_promoted_to;
		//bool m_is_castling = false;
		//bool m_is_en_passant = false;
		const Position& m_pos;
		const MoveRecord& m_record;
		friend bool operator==(const Move&, const Move&);
	};
	bool operator==(const Move&, const Move&);
	bool operator!=(const Move&, const Move&);
	std::ostream& operator<<(std::ostream& os, const Move& mv);
	//std::optional<Move> find_move(const std::string& t_name, const AnalysedPosition& apos);

	//std::optional<Move> move_from_fen(const std::string& fen, const Position& curr_pos, const std::vector<Move>& moves);
}
#endif


#ifndef BN
#define BN

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdbool.h>
#include <getopt.h>
#include <assert.h>

#include <gsl/gsl_randist.h>
#include <gsl/gsl_sf_gamma.h>
#include <gsl/gsl_multimin.h>
#include <gsl/gsl_matrix.h>

#include <omp.h>
#include <math.h>

#include "matrix.h"
#include "model.h"
#include "data.h"
#include "vector.h"
#include "a-queue.h"

bool verbose = false;
bool debug = false;

int** GENOTYPE; // GENOTYPE[i] stores the binary expansion of i.
int NUM_SAMPLES_READ; // total number of samples read in from dataset.

// set initial parameter values (seeimingly arbitrary, but makes it easier to tweak them since they're all here)
void initialize_MH_params(unsigned int* seed, int* number_samples, int* record_ith, int* burn_in, char* reg_scheme) {
	*seed = time(NULL); // random seed using time
	*number_samples = 100000;
	*record_ith = 1;
	*burn_in = 0;
	strcpy(reg_scheme, "bic");
}

/*
 * Check file inputs to bn. If no .poset file supplied (f flag), prints error message and exits.
 * Flags:
 *
 *
 *
 */
void check_file_inputs(int argc, char** argv, char* filestem, char* output, int* number_samples, unsigned int* seed, char* reg_scheme) {
	int c = 0;
	bool input_flag = false;
	bool output_flag = false;
	bool help_flag = false;
	static struct option long_options[] =
        {
          {"dataset",     		required_argument, 0, 'd'},
          {"output",  				required_argument, 0, 'o'},
          {"number_samples",	required_argument, 0, 'n'},
          {"seed", 						required_argument, 0, 's'},
          {"reg", 						required_argument, 0, 'r'},
          {"help", 						no_argument, 			 0, 'h'},
          {NULL, 0, NULL,0,}
        };
	while ((c = getopt_long(argc, argv, "d:o:n:s:r:hvt:e:", long_options, NULL)) != -1 ) {
		switch(c) {
			case 'd': // get input filename, satisfy boolean check for provided input.
				strcpy(filestem, optarg);
				input_flag = true;
				break;
			case 'o':
				strcpy(output, optarg);
				output_flag = true;
				break;
			case 'n':
				*number_samples = atoi(optarg);
				break;
			case 's':
				*seed = atoi(optarg);
				break;
			case 'r':
				strcpy(reg_scheme, optarg);
				break;
			case 'h':
				help_flag = true;
				break;
			case 'v':
				verbose = true;
				break;
		}
	}

	if (help_flag) {
		printf("Usage: ./cbn (-(option) (argument) )*\n");
		printf("where dataset input option (-d or --dataset) is required.\n");
		printf("Options include:\n");
		printf("-d | --dataset \tPath to input file with sample data. Please include file extension as well. ex: test1.txt\n");
		printf("-o | --output \tPath to output file to write results to. If not specified, then results not written to file.\n");
		printf("-n | --number_samples \tSet the number of iterations for Metropolis-Hastings to be run. Default value set to 500,000.\n");
		printf("-s | --seed \tSet the seed for random number generators in the program. Time used as default seed.\n");
		printf("-r | --reg \tSet the likelihood regularization scheme for the MCMC algorithm. Valid choices are bic, aic, and loglik.\n");
		printf("-h | --help \tPrint help information for program.\n");
		exit(1);
	}

	if (!input_flag) {
		fprintf(stderr, "Error: No input file specified. Please provide the filename (without filetype) of a file containing formatted sample data using the \"-d\" or \"--dataset\" options.\n");
		exit(1);
	}
	if (!output_flag) {
		strcpy(output, "");
	}
	if (strcmp(reg_scheme, "bic") &&  strcmp(reg_scheme, "aic") && strcmp(reg_scheme, "loglik")) {
		fprintf(stderr, "Error: Invalid regularization scheme. Please choose from bic (default), aic, or loglik.\n");
		exit(1);
	}
}

/*
 * Standard open_file helper. Throws error if error while opening file.
 */
FILE* open_file(char* filename, char* option) {
	FILE* input = fopen(filename, option);
	if (input == NULL) {
		fprintf(stderr, "Error: Could not read %s\n", filename);
		exit(1);
	}
	return input;
}

/*
 * Base 2 power function. Computes 2^n, for n >= 0
 */
static inline int pow2(int n) {
	assert(n >= 0);
	return 1 << n;
}

// Basic power function implementation. Only accepts integral power values.
double power(double base, int power) {
	double result = 1.0;
	for (int i = 0; i < power; i++) {
		result *= base;
	}
	return result;
}

/*
 * Generates a binary compression of pattern data, which is
 * used as a means of quickly determining whether two samples have
 * the same pattern. Presumably, this is faster (as opposed to
 * comparisons without such a compression scheme), since we compress
 * all samples once, and then compare the "hash" values.
 * Parallels index_of in TS code.
 */
int binary_compression(int* pat_i, int num_events) {
	int index = 0;
	for (int i = 0; i < num_events; i++) {
		if (pat_i[i] ==1)
			index += pow2(num_events - 1 - i);
	}
	return index;
}

// Used to generate binary expansions of genotypes, for easier access later on.
void precompute_binary(const int n) {
	int m = pow2(n);
	GENOTYPE = get_int_matrix(m, n);
	for (int i = 0; i < m; i++) {
		int count = 0;
		for (int j = 0; j < n; j++) {
			if ( ((1 << j) & i) > 0) {
				GENOTYPE[i][n - 1 - j] = 1;
				count++;
			} else {
				GENOTYPE[i][n - 1 - j] = 0;
			}
		}
	}
}

int count_edges(int** poset, int n) {
	int count = 0;
	for (int i=0; i< n; i++) {
		for (int j = 0; j< n; j++) {
			if (poset[i][j])
				count++;
		}
	}
	return count;
}

// TODO: reconsider the use of int* ptr vector here. We've changed the idea, and we're no longer looking
// at the probability of combined events. Instead, using the current poset to determine how children
// are valid or not in find_children_extended.

/*
 * Helper function for process_patterns function. Reads pattern data
 * from .txt file. Only 0/1 expected, but places negative values where
 * any deviations occur. Exits if any error is encountered.
 */
int** read_patterns(FILE *input, model* M, char* filename, int* num_samples, int* num_events) {
	size_t temp = 0;
	int n;
	char* line = NULL;
	int len = getline(&line, &temp, input);
	if (len != -1) {
		n = len / 2;
		free(line);
		line = NULL;
		rewind(input);
	} else {
		fprintf(stderr, "Error reading sample data.\n");
		exit(1);
	}

	M->n = n;
	*num_events = n;

	ptr_vector* v = new_ptr_vector(); // vector takes ints, hopefully won't get mutilated?
	while (getline(&line, &temp, input) != -1) {
		int line_index = 0;
		int* genotype = get_int_array(n);
		for (int i = 0; i< n; i++) { // fill in each event with genotype.
			if (line[line_index] == '0') genotype[i] = 0;
			if (line[line_index] == '1') genotype[i] = 1;
			line_index += 2;
		}
		push_back_ptr(v, genotype);
		free(line);
		line = NULL;
		temp = 0;
	}
	free(line);
	*num_samples = v->size;

	if (debug) {
		printf("Finished reading patterns.\nPatterns matrix: \n");
		print_int_matrix(v->elems, v->size, n);
	}

	return free_ptr_vector(v, false);
}

// counts uniq patterns among samples.
// It seems that pat_idx counts the number of unique samples
// up to that last unique sample.
// ----- variables -----
// index stores the "hashed" patterns
// count stores the number of samples with the same pattern
// Removed pat_idx necessity, since the same information is stored in index.

// Note: The binary compression places a limit on the number of loci that can be observed (32 for int, 64 for long int).
// For a completely scalable implementation, should re-work this scheme, use a hashset for speed.
void count_uniq_patterns(int* index, int* count, int num_samples, int num_events, int* N_u, int** pat) {

	//binary compression that "hashes" genotypes
	for (int i = 0; i< num_samples; i++)
		index[i] = binary_compression(pat[i], num_events);

	// count unique patterns. Runs in worst case O(n^2) time. can be optimized to O(nlgn) using sort -> iterate, or linear time using a hashtable/set.
	bool skips; // true if another sample has same pattern ("index" value)
	int num_unique = 0; // counts number of unique patterns
	for (int i = 0; i < num_samples; i++) {
		skips = false;
		for (int j = 0; j < i; j++) {
			if (index[i] == index[j]) { //samples have the same pattern
				count[j]++; //increment the count at first index
				skips = true;
				break;
			}
		}
		if (!skips) { // unique pattern --> NOT previously observed
			count[i]++;
			num_unique++;
		}
	}
	*N_u = num_unique;

	if (verbose) {
		printf("Number of samples processed = %d\n", num_samples);
		printf("Number of gene loci considered = %d\n", num_events);
		printf("Number of unique genotypes observed = %d\n", num_unique);
	}
	if (debug) {
		printf("num_samples = %d\n", num_samples);
		printf("count array: \n");
		print_int_array(count, num_samples);
		printf("index array: \n");
		print_int_array(index, num_samples);
	}
}

data* construct_data_set(int num_unique, int* count, int* index, int num_samples, int num_events, int** pat) {
	data* D = calloc(num_unique, sizeof(data));
	if (D == NULL) {
		fprintf(stderr, "Error: calloc failure - out of memory.\n");
		exit(1);
	}
	int d_index = 0;
	for (int i = 0; i < num_samples; i++) {
		if (count[i] > 0) {//if was not a repeat
			D[d_index].count = count[i];
			D[d_index].g = get_int_array(num_events);
			for (int j = 0; j < num_events; j++)
				D[d_index].g[j] = pat[i][j];
			d_index++;
		}
	}

	if (debug) {
		print_data_array(D, num_unique, num_events);
	}
	return D;
}

// N_u = number of unique genotypes in dataset
// n  = number of events
int process_patterns(char* filename, model* M, int* N_u, data** D) {

	FILE* input = open_file(filename, "r");
	int num_samples, num_events;
	int** pat = read_patterns(input, M, filename, &num_samples, &num_events);
	fclose(input);

	//Count unique patterns, construct data set.
	int* index = get_int_array(num_samples);
	int* count = get_int_array(num_samples);
	count_uniq_patterns(index, count, num_samples, num_events, N_u, pat);
	*D = construct_data_set(*N_u, count, index, num_samples, num_events, pat);

	NUM_SAMPLES_READ = num_samples;
	free_int_matrix(pat, num_samples);
	free_int_array(index);
	free_int_array(count);

	return 0;
}

// Suppes condition
// Confirm that the proposed modification (move) is supported by event probabilities in the data set, taking into account
// current theta types.
bool cumulative_probs_maintained(model* M, data* D, int* theta_types, int num_unique) {
	int n = M->n;
	for (int i = 0; i< n; i++) { // loop through each event
		int num_parents = M->num_pa[i];
		int* parents = M->pa[i];
		int type = theta_types[i];
		int count_parents = 0;
		int count_child = 0;
		int count_parents_child = 0;
		for (int j = 0; j< num_unique; j++) { // loop through all observed genotypes
			int* g = D[j].g;
			int found = 0;

			// get child present bool value
			bool child_present = false;
			if (g[i]) child_present = true;

			// get parent set satisfied bool value
			bool parents_present = true;
			switch (type) { // 0 or 1 parents, conjunctive
				case -1 ... 0:
					for (int k = 0; k < num_parents; k++) {
						if (!g[parents[k]]) parents_present = false;
					}
					break;
				case 1: // or
					for (int k = 0; k < num_parents; k++) {
						if (g[parents[k]]) found++;
					}
					if (!found) parents_present = false;
					break;
				case 2: // xor
					for (int k = 0; k < num_parents; k++) {
						if (g[parents[k]]) found++;
					}
					if (found != 1) parents_present = false;
					break;
			}

			if (child_present && parents_present) {
				count_parents_child += D[j].count;
			}

			if (child_present) {
				count_child += D[j].count;
			}

			if (parents_present) {
				count_parents += D[j].count;
			}
		}
		// check that the checks and additions are correct.
		if (num_parents > 0) {
			if (count_parents <= count_child || count_parents_child * NUM_SAMPLES_READ <= count_parents * count_child) {
				return false;
			}
		}
	}
	return true;
}

// Initializes the parent fields in the model so that they can be used to find children more efficiently.
void set_theta_types(model* M, int* theta_types, int* theta_types_p) {
	int num_events = M->n;
	int** poset = M->P;
	free_parents(M);
	M->num_pa = get_int_array(num_events); // create an array that stores the sizes of parents.
	M->pa = (int**) malloc(num_events * sizeof(int*)); // create an array to hold arrays of integers

	// for all events, create a vector of all other events that relate to them, according to the current poset.
	// Then, determine theta type if an event has > 1 parent events.
	for (int i = 0; i< num_events; i++) {
		vector* v = new_vector();
		for (int j = 0; j< num_events; j++) {
			if (poset[j][i]) {
				push_back(v, j);
			}
		}
		int num_parents = v->size;
		M->num_pa[i] = num_parents;
		M->pa[i] = free_vector(v, false);

		if (num_parents <= 1) { // if 1 parent at most, no need for special care.
			theta_types_p[i] = -1;
		} else {
			if (theta_types[i] < 0) { // event goes from 0/1 parents to 2+ parents
				theta_types_p[i] = rand() % 3; // randomly pick between 0, 1, and 2.
			} else { // carry over formula from previous iteration.
				theta_types_p[i] = theta_types[i];
			}
		}
	}
}

// imposes a total ordering on the events. Use Fisher-Yates shuffling algorithm to randomize ordering
void new_total_ordering(model* M, int* theta_types, data* D, int num_unique) {
	int num_events = M->n;
	// initialize new array to use.
	int rand_arr[num_events];
	for (int i = 0; i < num_events; i++) {
		rand_arr[i] = i;
	}

	// establish total ordering
	for (int i = num_events - 1; i >= 0; i--) {
		int randy = rand() % (i + 1);
		int temp = rand_arr[i];
		rand_arr[i] = rand_arr[randy];
		rand_arr[randy] = temp;
	}

	int** initial_poset = get_int_matrix(num_events, num_events);
	// mark in poset.
	for (int i = 0; i < num_events - 1; i++) {
		initial_poset[rand_arr[i]][rand_arr[i+1]] = 1;
	}

	// transitive closure & theta types
	int** T = get_int_matrix(num_events, num_events);
	transitive_closure(initial_poset, T, num_events);

	for (int i = 0; i < num_events; i++) {
		int num_parents = 0;
		for (int j = 0; j < num_events; j++) {
			if (T[j][i]) num_parents++;
		}
		if (num_parents == 0) continue; // don't bother checking if there are no parents, since we're only going to remove excess edges.
		int reduce = num_parents - (rand() % (num_parents + 1));
		for (int j = 0; j < reduce; j++) { // delete a random relation reduce number of times.
			int ith_event = rand() % num_parents;
			int counted = 0;
			for (int k = 0; k < num_events; k++) {
				if (counted == ith_event && T[k][i]) {
					T[k][i] = 0; // delete event
					num_parents--; // decrement number of parents.
					break;
				}
				if (T[k][i]) counted++;
			}
		}
	}

	free_int_matrix(initial_poset, num_events);
	free_int_matrix(M->P, num_events);
	M->P = T;

	int* theta_types_p = get_int_array(num_events);
	for (int i = 0; i < num_events; i++) {
		theta_types_p[i] = -1;
	}
	set_theta_types(M, theta_types_p, theta_types);

	model M_p;
	M_p.n = num_events;
	M_p.pa = M->pa;
	M_p.P = T;
	M_p.num_pa = get_int_array(num_events); // initializes each to 0.
	// do suppes checking for each events' parent set.
	for (int i = 0; i < num_events; i++) {
		// determine suppes prob condition for this event and its parent set.
		// copy the parent set of this event into a new poset.
		theta_types_p[i] = theta_types[i];
		M_p.num_pa[i] = M->num_pa[i];
		if (!cumulative_probs_maintained(&M_p, D, theta_types, num_unique)) {// reject entire parent set.
			for (int j = 0; j < num_events; j++) {
				M->P[j][i] = 0;
			}
			M->num_pa[i] = 0;
			theta_types[i] = -1;
		}
		theta_types_p[i] = -1; // push to conjunctive so that it doesn't go to 'or' and 'xor' cases.
		M_p.num_pa[i] = 0; // set back to 0.
	}
	free_int_array(M_p.num_pa);
	free_int_array(theta_types_p);
}

// returns the total number of events associated with a given genotype.
// ex. For n= 4, 0101 returns 2.
int total_events(const int genotype, const int n) {
	int* binary = GENOTYPE[genotype];
	int num_events = 0;
	for (int i=0; i< n; i++)
		num_events += binary[i];
	return num_events;
}

// Given two integers, returns the number of bitwise differences between the two.
// If hamming distance > 1, returns index of last (largest) bit.
int hamming_distance(unsigned int x, unsigned int y, const int num_events, int* diff_index) {
	unsigned int dist = 0;
	unsigned int diff = x ^ y;
	for (int i= 0; i < num_events; i++) {
		if ( (1 << i) & diff ) {
			dist++;
			if (diff_index != NULL)	*diff_index = num_events - 1 - i;
		}
	}
	return dist;
}

// Checks that the parent conditions of the ith event are satisfied in provided genotype g.
// Returns true if parent conditions (according to poset and theta_types) are fulfilled, false otherwise.
bool check_parent_set(model* M, int* theta_types, int* g, int i) {
	int num_parents = M->num_pa[i];
	int type = theta_types[i]; // relationship between parents.
	bool compatible = false;
	int found = 0;

	switch (type) { // will only go into one case for each, so this may be faster.
		case -1 ... 0: // 0/1 parents, or and case if > 1 parents. loop through all parents, confirm that all are present.
			for (int j = 0; j< num_parents; j++) {
				if (!g[M->pa[i][j]]) return false;
			}
			break;
		case 1: // or.
			for (int j = 0; j< num_parents; j++) {
				if (g[M->pa[i][j]]) {
					compatible = true;
					break;
				}
			}
			if (!compatible) return false;
			break;
		case 2: // xor. loop through all parents, confirm that only one present at a time.
			for (int j = 0; j< num_parents; j++) {
				if (g[M->pa[i][j]]) { // another event observed that isn't the current
					found++;
				}
			}
			if (found != 1) return false;
			break;
	}
	return true;
}

// Theta types gives the relationship between "parent events", if an event has more than one direct parent.
// If check_parent_set returns true for all events, then returns true.
bool compatible_with_poset(int genotype, model* M, int* theta_types) {
	int num_events = M->n;
	int* g = GENOTYPE[genotype];
	for (int i = 0; i< num_events; i++) { // for each event
		if (g[i]) { // if event is observed
			if (!check_parent_set(M, theta_types, g, i)) return false;
		}
	}
	return true;
}

#endif


// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//------------------------------------------------------------------------------
// Intrinsics.h
//------------------------------------------------------------------------------

#pragma once

#include <arcana/analysis/introspector.h>

#include <gsl/gsl>

#include <array>

namespace mage
{
    class Intrinsics
    {
    public:
        Intrinsics() = default;
        Intrinsics(gsl::span<const float, 4> intrinsicsCxCyFxFy, uint32_t imageWidth, uint32_t imageHeight);
        Intrinsics(float cx, float cy, float fx, float fy, uint32_t imageWidth, uint32_t imageHeight);

        // cx, cy, fx, fy
        gsl::span<const float, 4> GetCoefficients() const { return m_coefficients; }
        std::array<float, 4> GetNormalizedCoefficients() const;

        float GetCx() const { return m_coefficients[0]; } //principle point pixels
        float GetCy() const { return m_coefficients[1]; } //principle point pixels
        float GetFx() const { return m_coefficients[2]; } //focal length pixels
        float GetFy() const { return m_coefficients[3]; } //focal length pixels

        void SetCx(float cx) { m_coefficients[0] = cx; }  //principle point pixels
        void SetCy(float cy) { m_coefficients[1] = cy; }  //principle point pixels
        void SetFx(float fx) { m_coefficients[2] = fx; }  //focal length pixels
        void SetFy(float fy) { m_coefficients[3] = fy; }  //focal length pixels

        uint32_t GetCalibrationWidth() const { return m_widthPixels; }
        uint32_t GetCalibrationHeight() const { return m_heightPixels; }

    private:
        std::array<float, 4> m_coefficients{}; // cx, cy, fx, fy in pixels
        uint32_t m_widthPixels{};  //widht/height calibration was performed for
        uint32_t m_heightPixels{};
    };

    template<typename ArchiveT>
    void introspect_object(mira::introspector<ArchiveT>& intro, const Intrinsics& intrin)
    {
        intro(
            cereal::make_nvp("Coefficients", intrin.GetCoefficients())
        );
    }
}