/**
 * Copyright 2013 JDSU
 * Author: Andy Lai (Andy.Lai@jdsu.com)
 */
#include <netinet/in.h>
#include <boost/regex.hpp>
#include "xtreme/common/pubsub_filter/ipv4address_asterisk.h"

namespace xtreme {
namespace filter {
boost::regex regex_ipv4__asterisk("((\\d+)|(\\*))\\.((\\d+)|(\\*))\\.((\\d+)|(\\*))\\.((\\d+)|(\\*))");  // NOLINT[readability/streams]

IPv4AddressAsterisk::IPv4AddressAsterisk(const std::string& value)
throw(InvalidFieldValueException) : IPv4AddressBase(kIPV4TYPE_WILDCARD_STAR) {
  mask_ = 0;
  value_set_ = true;
  ipv4_value_ = 0;
  boost::match_results<std::string::const_iterator> what;
  boost::match_flag_type flags = boost::match_default;
  if (boost::regex_match(value, what, regex_ipv4__asterisk, flags)) {
    for (int i = 0; i < 4; ++i) {
      std::string str_addrpart = std::string(what[i*3 + 1].first,
                                             what[i*3 + 1].second);
      if (!str_addrpart.empty()) {
        if (str_addrpart == "*") {
        } else {
          int addrpart = atoi(str_addrpart.c_str());
          uint32_t base = 0x000000ff;
          mask_ += (base << (8*(3-i)));
          if (addrpart >= 0 && addrpart <= 255)
            ipv4_value_ += ((static_cast<uint32_t>(addrpart) & 0x000000ff) << (8*(3-i)));
          else
            throw InvalidFieldValueException("IPv4 Address Part out of range: " + value);
        }
      } else {
        throw InvalidFieldValueException("Malformed IPv4 Address: " + value);
      }
    }
  } else {
    throw InvalidFieldValueException("Malformed IPv4 Address: " + value);
  }
  // convert value_ and mask_ to big_endian due to the value form parsed frame is big_endian
  ipv4_value_ = htonl(ipv4_value_);
  mask_ = htonl(mask_);
}
bool IPv4AddressAsterisk::operator ==(const uint32_t& b) const {
  return ipv4_value_ == (b & mask_);
}

bool IPv4AddressAsterisk::IsIPv4Address(const std::string& value, std::string &err) {
  boost::match_results<std::string::const_iterator> what;
  boost::match_flag_type flags = boost::match_default;
  if (boost::regex_match(value, what, regex_ipv4__asterisk, flags)) {
    for (int i = 0; i < 4; ++i) {
      std::string str_addrpart = std::string(what[i*3 + 1].first,
                                             what[i*3 + 1].second);
      if (!str_addrpart.empty()) {
        if (str_addrpart == "*") {
        } else {
          int addrpart = atoi(str_addrpart.c_str());
          if (addrpart < 0 || addrpart > 255) {
            err += "IPv4 Address Part out of range: " + value;
            return false;
          }
        }
      } else {
        err += "Malformed IPv4 Address: ";
        err += value;
        return false;
      }
    }
  } else {
    err += "Malformed IPv4 Address: ";
    err += value;
    return false;
  }
  return true;
}
}
}


/**************************************************************************\
|
|    Copyright (C) 2009 Marc Stevens
|
|    This program is free software: you can redistribute it and/or modify
|    it under the terms of the GNU General Public License as published by
|    the Free Software Foundation, either version 3 of the License, or
|    (at your option) any later version.
|
|    This program is distributed in the hope that it will be useful,
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
|    GNU General Public License for more details.
|
|    You should have received a copy of the GNU General Public License
|    along with this program.  If not, see <http://www.gnu.org/licenses/>.
|
\**************************************************************************/

#ifndef MAIN_HPP
#define MAIN_HPP

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

#include <boost/filesystem/operations.hpp>
#include <boost/thread.hpp>

#include <hashclash/saveload_gz.hpp>
#include <hashclash/sdr.hpp>
#include <hashclash/differentialpath.hpp>
#include <hashclash/booleanfunction.hpp>
#include <hashclash/timer.hpp>

using namespace hashclash;
using namespace std;

extern boost::mutex mut;
extern std::string workdir;
class path_container;
void dostep(path_container& container);
bool check_path_collfind(const differentialpath& diffpath, const uint32 mdiff[16]);

struct connect_bitdata {
	uint32 dQt;
	uint32 dQtp1;
	uint32 dFt;
	uint32 dFtp1;
	uint32 dFtp2;
	uint32 dFtp3;

	inline bool operator== (const connect_bitdata& r) const {
		return dQt==r.dQt && dQtp1==r.dQtp1 && dFt==r.dFt && dFtp1==r.dFtp1 && dFtp2==r.dFtp2 && dFtp3==r.dFtp3;
	}
	inline bool operator!= (const connect_bitdata& r) const {
		return !(*this == r);
	}
	inline bool operator< (const connect_bitdata& r) const {
		if (dQt < r.dQt) return true;
		if (dQt > r.dQt) return false;
		if (dQtp1 < r.dQtp1) return true;
		if (dQtp1 > r.dQtp1) return false;
		if (dFt < r.dFt) return true;
		if (dFt > r.dFt) return false;
		if (dFtp1 < r.dFtp1) return true;
		if (dFtp1 > r.dFtp1) return false;
		if (dFtp2 < r.dFtp2) return true;
		if (dFtp2 > r.dFtp2) return false;
		if (dFtp3 < r.dFtp3) return true;
		return false;
	}
};

struct md5_connect_thread {
	md5_connect_thread(): sw(true), countb(33,0), countbaborted(33,0), countbdepth(33,0)
	{}
	void md5_connect(const vector<differentialpath>& lowerpaths, const differentialpath& upperpath, path_container& container);
	timer sw/*(true)*/;
	vector<unsigned> countb/*(33, 0)*/;
	vector<unsigned> countbaborted/*(33, 0)*/;
	vector<unsigned> countbdepth/*(33, 0)*/;
	uint64 count, countall;
	vector<unsigned char> isgood;
	vector<int> lowerpathsmaxtunnel;

	unsigned md5_connect_bits(const vector<differentialpath>& lowers, unsigned index, const differentialpath& upper, path_container& container);
	connect_bitdata startbit0;
	vector<connect_bitdata> bitdataresults[33];
	vector<connect_bitdata> bitdatastart[32];
	vector<connect_bitdata> bitdataend[32];
	vector<byteconditions>  bitdatanewcond[32];
	differentialpath newpath2, tmppath;
	unsigned bindex[32];

	void connectbits2(const connect_bitdata& in, unsigned b, const differentialpath& lower, const differentialpath& upper);
	bf_outcome bfo0, bfo1, bfo2, bfo3;
	bf_conditions bfc0, bfc1, bfc2, bfc3;

	void connectbits(const connect_bitdata& in, vector<connect_bitdata>& out, unsigned b,
		const differentialpath& lower, const differentialpath& upper, vector<byteconditions>* newconds = 0);
	connect_bitdata result;
	//bf_outcome bfo0, bfo1, bfo2, bfo3;
	//bf_conditions bfc0, bfc1, bfc2, bfc3;
	bitcondition Qt, Qtp1;
	byteconditions newcond;
	bool lastdFp1, lastdFp2, lastdFp3;

	unsigned t;
	booleanfunction* Ft;
	booleanfunction* Ftp1;
	booleanfunction* Ftp2;
	booleanfunction* Ftp3;
	vector<uint32> dFt, dFtp1, dFtp2, dFtp3;
	uint32 dmt, dmtp1, dmtp2, dmtp3;
	uint32 dQtp1, dQtp2, dQtp3, dQtp4;
	differentialpath newpath;

	inline bool isinrange(const differentialpath& needle, const differentialpath& haystack, unsigned b)
	{
		for (unsigned j = t-2; j <= t; ++j)
		{
			const wordconditions& nt = needle[j];
			const wordconditions& ht = haystack[j];
			unsigned k = 0;
			while (k+7 <= b)
			{
				if (nt.bytes[k>>3] != ht.bytes[k>>3])
					return false;
				k += 8;
			}
			for (; k <= b; ++k)
				if (nt[k] != ht[k])
					return false;
		}
		return true;
	}
	inline unsigned binary_search_lower_paths(const vector<differentialpath>& lowerpaths,
									   unsigned i, unsigned b)
	{
		const differentialpath& needle = lowerpaths[i];
		unsigned l = i, u = lowerpaths.size()-1;
		while (l < u)
		{
			unsigned j = (l+u+1)>>1;
			if (isinrange(needle, lowerpaths[j], b)) {
				l = j;
			} else {
				u = j-1;
			}
		}
		return l;
	}
};

extern vector<uint32> lowdQt, lowdQtm1, lowdQtm2, lowdQtm3;

class path_container {
public:
	path_container()
		: modn(1), modi(0), inputfilelow(), inputfilehigh()
		, t(0), noverify(false), noenhancepath(false)
		, verified(0), verifiedbad(0), bestpathcond(1<<20)
		, bestmaxtunnel(0), showstats(false), bestmaxcomp(-1000), threads(1)
	{
		for (unsigned k = 0; k < 16; ++k)
			m_diff[k] = 0;
	}

	~path_container()
	{
		cout << "Best path: totcompl=" << bestmaxcomp << " tottunnel=" << bestmaxtunnel << ", totcond=" << bestpathcond << endl;
		if (!noverify)
			cerr << "Verified: " << verifiedbad << " bad out of " << verified << endl;
	}

	void push_back(const differentialpath& fullpath)
	{
		differentialpath pathback = fullpath;
		//pathback = fullpath;
		try {
		cleanup(pathback);
		} catch (std::exception& e) {
			cerr << "hashclash::cleanup(differentialpath&): unknown exception!:" << endl << e.what() << endl;
			show_path(pathback, m_diff);
		}catch (...) {
			cerr << "hashclash::cleanup(differentialpath&): unknown exception!:" << endl;
			show_path(pathback, m_diff);
		}

//		++verified;
		if (!noverify && !test_path_fast(pathback, m_diff))
		{
			mut.lock();
			++verified;
			++verifiedbad;
			mut.unlock();
			return;
		}

		unsigned tunnel = totaltunnelstrength(pathback);
		int tuncompl = tunnel;
		for (int k = pathback.tbegin(); k < pathback.tend() && k < 64; ++k)
			if (k >= Qcondstart)
				tuncompl -= int(pathback[k].hw());
		if (tuncompl < bestmaxcomp)
			return;
		if (tunnel < bestmaxtunnel)
			return;
		unsigned cond = 0;
		for (int k = pathback.tbegin(); k < pathback.tend(); ++k)
			cond += pathback[k].hw();
		if (!(tuncompl >= bestmaxcomp || tunnel >= bestmaxtunnel || cond <= bestpathcond))
			return;
		try {
			if (!noenhancepath) {
				enhancepath(pathback, m_diff);
				tunnel = totaltunnelstrength(pathback);
				tuncompl = tunnel;
				for (int k = pathback.tbegin(); k < pathback.tend() && k < 64; ++k)
					if (k >= Qcondstart)
						tuncompl -= int(pathback[k].hw());
				if (tuncompl < bestmaxcomp)
					return;
				if (tunnel < bestmaxtunnel)
					return;
				cond = 0;
				for (int k = pathback.tbegin(); k < pathback.tend(); ++k)
					cond += pathback[k].hw();
				if (!(tuncompl >= bestmaxcomp || tunnel >= bestmaxtunnel || cond <= bestpathcond))
					return;
			}
		} catch (std::exception&) {
			return;
		} catch (...) {
			return;
		}

		if (!check_path_collfind(pathback, m_diff))
			return;

		if (tuncompl == bestmaxcomp && tunnel == bestmaxtunnel && cond == bestpathcond) {
			mut.lock();
			++verified;
			bestpaths.push_back(pathback);
			if (hw(uint32(bestpaths.size()))==1) {
				save_gz(bestpaths, workdir + "/bestpaths", binary_archive);
				cout << "Best paths: " << bestpaths.size() << endl;
			}
			mut.unlock();
			return;
		}
		if (tuncompl < bestmaxcomp) return;
		if (tunnel < bestmaxtunnel) return;
		if (cond > bestpathcond) return;
		mut.lock();
		++verified;
		bestpaths.clear();
		bestpaths.push_back(pathback);

		bestmaxcomp = tuncompl;
		bestpathcond = cond;
		bestpath = pathback;
		bestmaxtunnel = tunnel;
		show_path(pathback, m_diff);
		double p = test_path(pathback, m_diff);
		cout << "Best path: totcompl=" << bestmaxcomp << " tottunnel=" << bestmaxtunnel << ", totcond=" << bestpathcond << ", p=" << p << endl;
		save_gz(pathback, workdir + "/bestpath_t" + boost::lexical_cast<string>(tunnel) + "_c" + boost::lexical_cast<string>(cond), binary_archive);
		save_gz(pathback, workdir + "/bestpath_new", binary_archive);
		save_gz(bestpaths, workdir + "/bestpaths_new", binary_archive);
		try { boost::filesystem::rename(workdir + "/bestpath.bin.gz", workdir + "/bestpath_old.bin.gz"); } catch (...) {}
		try { boost::filesystem::rename(workdir + "/bestpaths.bin.gz", workdir + "/bestpaths_old.bin.gz"); } catch (...) {}
		try { boost::filesystem::rename(workdir + "/bestpath_new.bin.gz", workdir + "/bestpath.bin.gz"); } catch (...) {}
		try { boost::filesystem::rename(workdir + "/bestpaths_new.bin.gz", workdir + "/bestpaths.bin.gz"); } catch (...) {}
		mut.unlock();
	}

	uint32 m_diff[16];

	unsigned t;
	int Qcondstart;

	bool showstats;
	bool noenhancepath;
	bool noverify;
	unsigned verified, verifiedbad;

	unsigned modn;
	unsigned modi;
	std::string inputfilelow, inputfilehigh;
	bool showinputpaths;

	differentialpath bestpath;
	vector<differentialpath> bestpaths;
	volatile unsigned bestpathcond;
	volatile unsigned bestmaxtunnel;
	volatile int bestmaxcomp;
	int threads;
};

struct diffpathlower_less
	: public std::binary_function<differentialpath, differentialpath, bool>
{
	bool operator()(const differentialpath& _Left, const differentialpath& _Right) const
	{
		if (_Left.tend() < _Right.tend() || _Left.tbegin() > _Right.tbegin())
			return true;
		if (_Left.tend() > _Right.tend() || _Left.tbegin() < _Right.tbegin())
			return false;
		if (_Left.path.size() < 3)
			throw std::runtime_error("Lower differential paths of insufficient size");
		unsigned t = _Left.tend() - 1;
		uint32 LdQt   = _Left[t].diff();
		uint32 RdQt   = _Right[t].diff();
		for (unsigned b = 0; b < 32; ++b)
		{
			if ((LdQt & (1<<b)) < (RdQt & (1<<b)))
				return true;
			if ((LdQt & (1<<b)) > (RdQt & (1<<b)))
				return false;
			if (_Left[t-2][b] < _Right[t-2][b])
				return true;
			if (_Left[t-2][b] > _Right[t-2][b])
				return false;
			if (_Left[t-1][b] < _Right[t-1][b])
				return true;
			if (_Left[t-1][b] > _Right[t-1][b])
				return false;
		}
		return false;
	}
};

struct diffpathupper_less
	: public std::binary_function<differentialpath, differentialpath, bool>
{
	bool operator()(const differentialpath& _Left, const differentialpath& _Right) const
	{
		if (_Left.tend() < _Right.tend() || _Left.tbegin() > _Right.tbegin())
			return true;
		if (_Left.tend() > _Right.tend() || _Left.tbegin() < _Right.tbegin())
			return false;
		if (_Left.path.size() < 3)
			throw std::runtime_error("Lower differential paths of insufficient size");
		unsigned t = _Left.tbegin() - 1;
		uint32 LdQtp1   = _Left[t+1].diff();
		uint32 RdQtp1   = _Right[t+1].diff();
		if (_Left[t+1].hw() > _Right[t+1].hw()) return true;
		if (_Left[t+1].hw() < _Right[t+1].hw()) return false;
		for (unsigned b = 0; b < 32; ++b)
		{
			if ((LdQtp1 & (1<<b)) < (RdQtp1 & (1<<b)))
				return true;
			if ((LdQtp1 & (1<<b)) > (RdQtp1 & (1<<b)))
				return false;
			if (_Left[t+2][b] < _Right[t+2][b])
				return true;
			if (_Left[t+2][b] > _Right[t+2][b])
				return false;
			if (_Left[t+3][b] < _Right[t+3][b])
				return true;
			if (_Left[t+3][b] > _Right[t+3][b])
				return false;
		}
		return false;
	}
};

#endif // MAIN_HPP


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

  @copyright 2016 NumScale SAS

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

#include <boost/simd/config.hpp>
#include <boost/simd/detail/brigand.hpp>
#include <boost/simd/detail/dispatch.hpp>
#include <boost/simd/detail/constant_traits.hpp>
#include <boost/simd/detail/dispatch/function/make_callable.hpp>
#include <boost/simd/detail/dispatch/hierarchy/functions.hpp>
#include <boost/simd/detail/dispatch/as.hpp>

/*

    @ingroup group-constant

    Generate the constant expx2c2.

    @return The Expx2c2 constant for the proper type
  */

namespace boost { namespace simd
{
  namespace tag
  {
    struct expx2c2_ : boost::dispatch::constant_value_<expx2c2_>
    {
      BOOST_DISPATCH_MAKE_CALLABLE(ext,expx2c2_,boost::dispatch::constant_value_<expx2c2_>);
      BOOST_SIMD_REGISTER_CONSTANT(0, 0x3d000000UL, 0x3f80000000000000ull);
    };
  }

  namespace ext
  {
    BOOST_DISPATCH_FUNCTION_DECLARATION(tag,expx2c2_);
  }

  namespace detail
  {
    BOOST_DISPATCH_CALLABLE_DEFINITION(tag::expx2c2_,expx2c2);
  }

  template<typename T> BOOST_FORCEINLINE auto Expx2c2()
  BOOST_NOEXCEPT_DECLTYPE(detail::expx2c2( boost::dispatch::as_<T>{}))
  {
    return detail::expx2c2( boost::dispatch::as_<T>{} );
  }
} }

#include <boost/simd/arch/common/scalar/constant/constant_value.hpp>
#include <boost/simd/arch/common/simd/constant/constant_value.hpp>

#endif


/*
Copyright 2020 Glen Joseph Fernandes
(glenjofe@gmail.com)

Distributed under the Boost Software License,
Version 1.0. (See accompanying file LICENSE_1_0.txt
or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#ifdef TEST_STD
#include <type_traits>
#else
#include <boost/type_traits/conjunction.hpp>
#endif
#include "check_integral_constant.hpp"

template<int V>
struct Int {
    static const int value = V;
};

TT_TEST_BEGIN(conjunction)

BOOST_CHECK_INTEGRAL_CONSTANT((::tt::conjunction<Int<2>, Int<4> >::value), 4);
BOOST_CHECK_INTEGRAL_CONSTANT((::tt::conjunction<Int<0>, Int<4> >::value), 0);

#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
BOOST_CHECK_INTEGRAL_CONSTANT((::tt::conjunction<Int<2>, Int<4>, Int<6>,
    Int<8>, Int<10> >::value), 10);
BOOST_CHECK_INTEGRAL_CONSTANT((::tt::conjunction<Int<2>, Int<0>, Int<4>,
    Int<6>, Int<8> >::value), 0);
BOOST_CHECK_INTEGRAL_CONSTANT(::tt::conjunction<Int<4> >::value, 4);
BOOST_CHECK_INTEGRAL_CONSTANT(::tt::conjunction<>::value, true);
#endif

TT_TEST_END


///////////////////////////////////////////////////////////////////////////////
// distribution::toolkit::map_pdf::product_pdf::is_pseudo_scalar_distribution.hpp //
//                                                                           //
//  (C) Copyright 2009 Erwann Rogard                                         //
//  Use, modification and distribution are subject to the                    //
//  Boost Software License, Version 1.0. (See accompanying file              //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_DISTRIBUTION_TOOLKIT_MAP_PDF_PRODUCT_PDF_IS_PSEUDO_SCALAR_DISTRIBUTION_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_DISTRIBUTION_TOOLKIT_MAP_PDF_PRODUCT_PDF_IS_PSEUDO_SCALAR_DISTRIBUTION_HPP_ER_2009
#include <boost/mpl/bool.hpp>
#include <boost/mpl/and.hpp>
#include <boost/statistics/detail/distribution_toolkit/meta/is_pseudo_scalar_distribution.hpp>
#include <boost/statistics/detail/distribution_toolkit/map_pdf/product_pdf/product_pdf.hpp>

namespace boost{
namespace statistics{
namespace detail{
namespace distribution{
namespace toolkit{

namespace meta{

    template<typename A,typename B>
    struct is_pseudo_scalar_distribution<
        product_pdf<A,B>
    > : mpl::and_<
        is_pseudo_scalar_distribution<A>,
        is_pseudo_scalar_distribution<B>
    > {};

}// meta
}// distribution
}// toolkit

}// detail
}// statistics
}// boost

#endif


// Copyright 2012 by BBN Technologies Corp.
// All Rights Reserved.

#include "Generic/common/leak_detection.h"

#include "Generic/values/NumberConverter.h"
#include "English/values/en_NumberConverter.h"

#include <boost/algorithm/string.hpp>

#include <string>

int EnglishNumberConverter::convertComplexTwoPartNumberWord(const std::wstring& str) {
	// Simple handling of two-part numbers
	std::vector<std::wstring> two_part_vector;
	boost::split(two_part_vector, str, boost::is_any_of(L" "));
	if (two_part_vector.size() == 2) {
		int part1 = convertNumberWord(two_part_vector.at(0));
		int part2 = convertNumberWord(two_part_vector.at(1));
		if ((two_part_vector.at(1) == L"hundred" || two_part_vector.at(1) == L"dozen") &&
			part1 != 0 && part2 != 0 && part1 < 10) {
			return part1*part2;
		} else if (two_part_vector.at(1) == L"thousand" && part1 != 0 && part2 != 0 && part1 < 100) {
			return part1*part2;
		} else if (two_part_vector.at(1) == L"million" && part1 != 0 && part2 != 0 && part1 < 1000) {
			return part1*part2;
		}
	}
	return INT_MIN; // signals invalid value
}

/**
 * Hard-coded table of mappings from English number words to integers.
 * Probably want to implement a table plus language-generic version of this method.
 **/
int EnglishNumberConverter::convertSpelledOutNumberWord(const std::wstring& str) {
	if (!str.compare(L"zero")) return 0;
	if (!str.compare(L"one")) return 1;
	if (!str.compare(L"two")) return 2;
	if (!str.compare(L"three")) return 3;
	if (!str.compare(L"four")) return 4;
	if (!str.compare(L"five")) return 5;
	if (!str.compare(L"six")) return 6;
	if (!str.compare(L"seven")) return 7;
	if (!str.compare(L"eight")) return 8;
	if (!str.compare(L"nine")) return 9;
	if (!str.compare(L"ten")) return 10;
	if (!str.compare(L"eleven")) return 11;
	if (!str.compare(L"twelve")) return 12;
	if (!str.compare(L"thirteen")) return 13;
	if (!str.compare(L"fourteen")) return 14;
	if (!str.compare(L"fifteen")) return 15;
	if (!str.compare(L"sixteen")) return 16;
	if (!str.compare(L"seventeen")) return 17;
	if (!str.compare(L"eighteen")) return 18;
	if (!str.compare(L"nineteen")) return 19;
	if (!str.compare(L"twenty")) return 20;
	if (!str.compare(L"thirty")) return 30;
	if (!str.compare(L"forty")) return 40;
	if (!str.compare(L"fifty")) return 50;
	if (!str.compare(L"sixty")) return 60;
	if (!str.compare(L"seventy")) return 70;
	if (!str.compare(L"eighty")) return 80;
	if (!str.compare(L"ninety")) return 90;
	if (!str.compare(L"hundred")) return 100;
	if (!str.compare(L"thousand")) return 1000;
	if (!str.compare(L"million")) return 1000000;
	if (!str.compare(L"billion")) return 1000000000;
	if (!str.compare(L"dozen")) return 12;
	return INT_MIN; // signals invalid value
}

bool EnglishNumberConverter::isSpelledOutNumberWord(const std::wstring& str) {
	return (str.find(L"two") == 0 ||
			str.find(L"three") == 0 ||
			str.find(L"four") == 0 ||
			str.find(L"five") == 0 ||
			str.find(L"six") == 0 ||
			str.find(L"seven") == 0 ||
			str.find(L"eight") == 0 ||
			str.find(L"nine") == 0 ||
			str.find(L"ten") == 0 ||
			str.find(L"eleven") == 0 ||
			str.find(L"twelve") == 0 ||
			str.find(L"thirteen") == 0 ||
			str.find(L"fourteen") == 0 ||
			str.find(L"fifteen") == 0 ||
			str.find(L"sixteen") == 0 ||
			str.find(L"seventeen") == 0 ||
			str.find(L"eighteen") == 0 ||
			str.find(L"nineteen") == 0 ||
			str.find(L"twenty") == 0 ||
			str.find(L"thirty") == 0 ||
			str.find(L"forty") == 0 ||
			str.find(L"fifty") == 0 ||
			str.find(L"sixty") == 0 ||
			str.find(L"seventy") == 0 ||
			str.find(L"eighty") == 0 ||
			str.find(L"ninety") == 0 ||
			str.find(L"hundred") == 0 ||
			str.find(L"thousand") == 0 ||
			str.find(L"million") == 0 ||
			str.find(L"billion") == 0);
}


// Copyright 2020 The Hds Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "mnemonic.h"

#include "core/ecc_native.h"
#include "pkcs5_pbkdf2.h"

#include <boost/algorithm/string.hpp>
#include <random>

using namespace std;

namespace hds
{
    namespace
    {
        const size_t bitsPerWord = 11;
        const uint8_t byteBits = 8;
        const std::string passphrasePrefix = "mnemonic";
        const size_t hmacIterations = 2048;
        const size_t sizeHash = 512 >> 3;

        uint8_t shiftBits(size_t bit)
        {
            return (1 << (byteBits - (bit % byteBits) - 1));
        }
    }

    std::vector<uint8_t> getEntropy()
    {
        std::vector<uint8_t> res;
        res.resize(16);
        ECC::GenRandom(res.data(), static_cast<uint32_t>(res.size()));

        return res;
    }

    WordList createMnemonic(const std::vector<uint8_t>& entropy, const Dictionary& dict)
    {
        // entropy should be 16 bytes or 128 bits for 12 words
        assert(entropy.size() == 128 >> 3);

        ECC::Hash::Value value;
        Blob blob(entropy.data(), static_cast<uint32_t>(entropy.size()));
        ECC::Hash::Processor() << blob >> value;
        std::vector<uint8_t> data(entropy);
        data.push_back(value.m_pData[0]);

        std::vector<std::string> words;
        size_t bit = 0;

        for (size_t word = 0; word < WORD_COUNT; word++)
        {
            size_t position = 0;
            for (size_t loop = 0; loop < bitsPerWord; loop++)
            {
                bit = (word * bitsPerWord + loop);
                position <<= 1;

                const auto byte = bit / byteBits;

                if ((data[byte] & shiftBits(bit)) > 0)
                    position++;
            }

            words.push_back(dict[position]);
        }

        return words;
    }

    std::vector<uint8_t> decodeMnemonic(const WordList& words)
    {
        const std::string sentence = boost::join(words, " ");
        std::vector<uint8_t> passphrase(sentence.begin(), sentence.end());
        std::vector<uint8_t> salt(passphrasePrefix.begin(), passphrasePrefix.end());
        std::vector<uint8_t> hash(sizeHash);

        const auto result = pkcs5_pbkdf2(
            passphrase.data(),
            passphrase.size(),
            salt.data(),
            salt.size(),
            hash.data(),
            hash.size(),
            hmacIterations);

        if (result != 0)
            throw MnemonicException("pbkdf2 returned bad result");

        return hash;
    }

    bool isAllowedWord(const string& word, const Dictionary& dict)
    {
        auto predicate = [] (const char* left, const char* right) {
            return strcmp(left, right) < 0;
        };
        assert(is_sorted(dict.begin(), dict.end(), predicate));
        return binary_search(dict.begin(), dict.end(), word.c_str(), predicate);
    }

    bool isValidMnemonic(const WordList& words, const Dictionary& dict)
    {
        return words.size() == WORD_COUNT && all_of(words.begin(), words.end(), [&dict](const string& w) {return isAllowedWord(w, dict); });
    }
}


#ifndef DQN_HPP_
#define DQN_HPP_

#include <memory>
#include <random>
#include <tuple>
#include <unordered_map>
#include <vector>
#include <ale_interface.hpp>
#include <caffe/caffe.hpp>
#include <boost/functional/hash.hpp>
#include <boost/optional.hpp>

namespace dqn {

constexpr auto kRawFrameHeight = 210;
constexpr auto kRawFrameWidth = 160;
constexpr auto kCroppedFrameSize = 84;
constexpr auto kCroppedFrameDataSize = kCroppedFrameSize * kCroppedFrameSize;
constexpr auto kInputFrameCount = 4;
constexpr auto kInputDataSize = kCroppedFrameDataSize * kInputFrameCount;
constexpr auto kMinibatchSize = 32;
constexpr auto kMinibatchDataSize = kInputDataSize * kMinibatchSize;
constexpr auto kOutputCount = 18;

using FrameData = std::array<uint8_t, kCroppedFrameDataSize>;
using FrameDataSp = std::shared_ptr<FrameData>;
using InputFrames = std::array<FrameDataSp, 4>;
using Transition = std::tuple<InputFrames, Action,
                              float, boost::optional<FrameDataSp>>;

using FramesLayerInputData = std::array<float, kMinibatchDataSize>;
using TargetLayerInputData = std::array<float, kMinibatchSize * kOutputCount>;
using FilterLayerInputData = std::array<float, kMinibatchSize * kOutputCount>;

using ActionValue = std::pair<Action, float>;
using SolverSp = std::shared_ptr<caffe::Solver<float>>;
using NetSp = boost::shared_ptr<caffe::Net<float>>;

/**
 * Deep Q-Network
 */
class DQN {
public:
  DQN(const ActionVect& legal_actions,
      const caffe::SolverParameter& solver_param,
      const int replay_memory_capacity,
      const double gamma,
      const int clone_frequency) :
        legal_actions_(legal_actions),
        solver_param_(solver_param),
        replay_memory_capacity_(replay_memory_capacity),
        gamma_(gamma),
        clone_frequency_(clone_frequency),
        random_engine(0) {}

  // Initialize DQN. Must be called before calling any other method.
  void Initialize();

  // Load a trained model from a file.
  void LoadTrainedModel(const std::string& model_file);

  // Restore solving from a solver file.
  void RestoreSolver(const std::string& solver_file);

  // Snapshot the current model
  void Snapshot() { solver_->Snapshot(); }

  // Select an action by epsilon-greedy.
  Action SelectAction(const InputFrames& input_frames, double epsilon);

  // Select a batch of actions by epsilon-greedy.
  ActionVect SelectActions(const std::vector<InputFrames>& frames_batch,
                           double epsilon);

  // Add a transition to replay memory
  void AddTransition(const Transition& transition);

  // Update DQN using one minibatch
  void Update();

  // Clear the replay memory
  void ClearReplayMemory() { replay_memory_.clear(); }

  // Get the current size of the replay memory
  int memory_size() const { return replay_memory_.size(); }

  // Return the current iteration of the solver
  int current_iteration() const { return solver_->iter(); }

protected:
  // Clone the Primary network and store the result in clone_net_
  void ClonePrimaryNet();

  // Given a set of input frames and a network, select an
  // action. Returns the action and the estimated Q-Value.
  ActionValue SelectActionGreedily(caffe::Net<float>& net,
                                   const InputFrames& last_frames);

  // Given a batch of input frames, return a batch of selected actions + values.
  std::vector<ActionValue> SelectActionGreedily(
      caffe::Net<float>& net,
      const std::vector<InputFrames>& last_frames);

  // Input data into the Frames/Target/Filter layers of the given
  // net. This must be done before forward is called.
  void InputDataIntoLayers(caffe::Net<float>& net,
                           const FramesLayerInputData& frames_data,
                           const TargetLayerInputData& target_data,
                           const FilterLayerInputData& filter_data);

protected:
  const ActionVect legal_actions_;
  const caffe::SolverParameter solver_param_;
  const int replay_memory_capacity_;
  const double gamma_;
  const int clone_frequency_; // How often (steps) the clone_net is updated
  std::deque<Transition> replay_memory_;
  SolverSp solver_;
  NetSp net_; // The primary network used for action selection.
  NetSp clone_net_; // Clone of primary net. Used to generate targets.
  TargetLayerInputData dummy_input_data_;
  std::mt19937 random_engine;
};

/**
 * Preprocess an ALE screen (downsampling & grayscaling)
 */
FrameDataSp PreprocessScreen(const ALEScreen& raw_screen);

}

#endif /* DQN_HPP_ */


// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of
//    conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list
//    of conditions and the following disclaimer in the documentation and/or
//    other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be
//    used to endorse or promote products derived from this software without
//    specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote
// developers

#include <boost/shared_ptr.hpp>
#include <boost/thread/lock_guard.hpp>
#include <boost/thread/mutex.hpp>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <unistd.h>

#include "common/varint.h"
#include "crypto.h"
#include "hash.h"
#include "warnings.h"

// Add some log capabilities
#include "misc_log_ex.h"

#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "crypto"

namespace crypto {

using std::abort;
using std::int32_t;
using std::int64_t;
using std::size_t;
using std::uint32_t;
using std::uint64_t;

extern "C" {
#include "crypto-ops.h"
#include "random.h"

#include "salrs/src/salrs_main.h"
}

const crypto::public_key null_pkey = crypto::public_key{};
const crypto::secret_key null_skey = crypto::secret_key{};
const crypto::derived_public_key null_dPkey = crypto::derived_public_key{};

static inline unsigned char *operator&(ec_point &point) {
  return &reinterpret_cast<unsigned char &>(point);
}

static inline const unsigned char *operator&(const ec_point &point) {
  return &reinterpret_cast<const unsigned char &>(point);
}

static inline unsigned char *operator&(ec_scalar &scalar) {
  return &reinterpret_cast<unsigned char &>(scalar);
}

static inline const unsigned char *operator&(const ec_scalar &scalar) {
  return &reinterpret_cast<const unsigned char &>(scalar);
}

void generate_random_bytes_thread_safe(size_t _N, uint8_t *bytes) {
  static boost::mutex random_lock;
  boost::lock_guard<boost::mutex> lock(random_lock);
  generate_random_bytes_not_thread_safe(_N, bytes);
}

static inline bool less32(const unsigned char *k0, const unsigned char *k1) {
  for (int n = 31; n >= 0; --n) {
    if (k0[n] < k1[n])
      return true;
    if (k0[n] > k1[n])
      return false;
  }
  return false;
}

void random32_unbiased(unsigned char *bytes) {
  // l = 2^252 + 27742317777372353535851937790883648493.
  // it fits 15 in 32 bytes
  static const unsigned char limit[32] = {
      0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82,
      0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0};
  do {
    generate_random_bytes_thread_safe(32, bytes);
  } while (!sc_isnonzero(bytes) &&
           !less32(bytes, limit)); // should be good about 15/16 of the time
  sc_reduce32(bytes);
}
/* generate a random 32-byte (256-bit) integer and copy it to res */
static inline void random_scalar(random_key &res) {
  random32_unbiased((uint8_t *)&res);
}

void hash_to_scalar(const void *data, size_t length, ec_scalar &res) {
  cn_fast_hash(data, length, reinterpret_cast<hash &>(res));
  sc_reduce32(&res);
}

/*
 * generate public and secret keys from a random 256-bit integer
 * TODO: allow specifying random value (for wallet recovery)
 *
 */
rand_key crypto_ops::generate_keys(public_key &pub, secret_key &sec,
                                   const rand_key &recovery_key, bool recover) {
  rand_key rng{};

  if (recover) {
    rng = recovery_key;
  } else {
    random_scalar(rng);
  }
  master_key_gen(&pub, &sec, (uint8_t *)&rng);

  // The random 32 byte number
  return rng;
}

bool crypto_ops::check_derived_public_key(const derived_public_key &key) {
  std::array<uint8_t, SIZE_DPK> deriverdPK{};

  memcpy(&deriverdPK, &key, deriverdPK.size());
  return derived_public_key_public_check(deriverdPK.data()) == 1;
}

bool crypto_ops::secret_key_to_public_key(const secret_key &sec,
                                          public_key &pub) {
  ge_p3 point;
  if (sc_check(&unwrap(sec)) != 0) {
    return false;
  }
  ge_scalarmult_base(&point, &unwrap(sec));
  ge_p3_tobytes(&pub, &point);
  return true;
}

bool crypto_ops::generate_key_derivation(const public_key &key1,
                                         const secret_key &key2,
                                         derived_public_key &derivation) {
#ifdef NDILITHIUM_CRYPTO
  ge_p3 point;
  ge_p2 point2;
  ge_p1p1 point3;
  assert(sc_check(&key2) == 0);
  if (ge_frombytes_vartime(&point, &key1) != 0) {
    return false;
  }
  ge_scalarmult(&point2, &unwrap(key2), &point);
  ge_mul8(&point3, &point2);
  ge_p1p1_to_p2(&point2, &point3);
  ge_tobytes(&derivation, &point2);
#else
  derived_public_key_gen((uint8_t *)&key1, (uint8_t *)&derivation);
#endif
  return true;
}

void crypto_ops::derivation_to_scalar(const derived_public_key &derivation,
                                      size_t output_index, ec_scalar &res) {
  struct {
    derived_public_key derivation;
    char output_index[(sizeof(size_t) * 8 + 6) / 7];
  } buf{};
  char *end = buf.output_index;
  buf.derivation = derivation;
  tools::write_varint(end, output_index);
  assert(end <= buf.output_index + sizeof buf.output_index);
  hash_to_scalar(&buf, end - reinterpret_cast<char *>(&buf), res);
}

bool crypto_ops::derive_public_key(const derived_public_key &derivation,
                                   size_t output_index, const public_key &base,
                                   public_key &derived_key) {
#ifdef NDILITHIUM_CRYPTO
  ec_scalar scalar{};
  ge_p3 point1;
  ge_p3 point2;
  ge_cached point3;
  ge_p1p1 point4;
  ge_p2 point5;
  if (ge_frombytes_vartime(&point1, &base) != 0) {
    return false;
  }
  derivation_to_scalar(derivation, output_index, scalar);
  ge_scalarmult_base(&point2, &scalar);
  ge_p3_to_cached(&point3, &point2);
  ge_add(&point4, &point1, &point3);
  ge_p1p1_to_p2(&point5, &point4);
  ge_tobytes(&derived_key, &point5);
#else
  derived_key = base;
#endif
  return true;
}

bool crypto_ops::derive_master_public_key(const public_key &mPK,
                                          derived_public_key &dPK) {
  derived_public_key_gen((uint8_t *)&mPK, (uint8_t *)&dPK);

  return derived_public_key_public_check((uint8_t *)&dPK) == 1;
}

bool crypto_ops::check_derived_key_ownersip(const derived_public_key &dPK,
                                            const secret_key &mSK,
                                            const public_key &mPK) {
  return derived_public_key_owner_check((uint8_t *)&dPK, (uint8_t *)&mSK,
                                        (uint8_t *)&mPK) == 1;
}

void crypto_ops::derive_secret_key(const derived_public_key &derivation,
                                   size_t output_index, const secret_key &base,
                                   secret_key &derived_key) {
#ifdef NDILITHIUM_CRYPTO
  ec_scalar scalar{};
  assert(sc_check(&base) == 0);
  derivation_to_scalar(derivation, output_index, scalar);
  sc_add(&unwrap(derived_key), &unwrap(base), &scalar);
#else
  derived_key = base;
#endif
}

// TODO: Might be removed since we need to do derivation with Master Public Key,
// that function will suffice.
bool crypto_ops::derive_subaddress_public_key(
    const public_key &out_key, const derived_public_key &derivation,
    std::size_t output_index, public_key &derived_key) {
#ifdef NDILITHIUM_CRYPTO
  ec_scalar scalar{};
  ge_p3 point1;
  ge_p3 point2;
  ge_cached point3;
  ge_p1p1 point4;
  ge_p2 point5;
  if (ge_frombytes_vartime(&point1, &out_key) != 0) {
    return false;
  }
  derivation_to_scalar(derivation, output_index, scalar);
  ge_scalarmult_base(&point2, &scalar);
  ge_p3_to_cached(&point3, &point2);
  ge_sub(&point4, &point1, &point3);
  ge_p1p1_to_p2(&point5, &point4);
  ge_tobytes(&derived_key, &point5);
#else
  derived_key = out_key;
#endif
  return true;
}

struct s_comm {
  hash h;
  ec_point key;
  ec_point comm;
};

struct s_comm_2 {
  hash msg;
  ec_point D;
  ec_point X;
  ec_point Y;
};

void crypto_ops::generate_signature(const hash &prefix_hash,
                                    const public_key &pub,
                                    const secret_key &sec, signature &sig) {
#ifdef NDILITHIUM_CRYPTO
  ge_p3 tmp3;
  ec_scalar k;
  s_comm buf;
#if !defined(NDEBUG)
  {
    ge_p3 t;
    public_key t2;
    assert(sc_check(&sec) == 0);
    ge_scalarmult_base(&t, &sec);
    ge_p3_tobytes(&t2, &t);
    assert(pub == t2);
  }
#endif
  buf.h = prefix_hash;
  buf.key = pub;
try_again:
  random_scalar(k);
  if (((const uint32_t *)(&k))[7] == 0) // we don't want tiny numbers here
    goto try_again;
  ge_scalarmult_base(&tmp3, (uint8_t *)k.buffer.data());
  ge_p3_tobytes(&buf.comm, &tmp3);
  hash_to_scalar(&buf, sizeof(s_comm), sig.c);
  if (!sc_isnonzero((const unsigned char *)sig.c.buffer.data()))
    goto try_again;
  sc_mulsub(&sig.r, &sig.c, &unwrap(sec), &k);
  if (!sc_isnonzero((const unsigned char *)sig.r.buffer.data()))
    goto try_again;
#else
  // unsigned long long signatureLength{};
  // auto result = crypto_sign_dilithium((uint8_t *)sig.buffer.data(),
  // &signatureLength, (unsigned char *)&prefix_hash, sizeof(prefix_hash), &sec);
  // assert(result == 0);
#endif
}

bool crypto_ops::check_signature(const hash &prefix_hash, const public_key &pub,
                                 const signature &sig) {
#ifdef NDILITHIUM_CRYPTO
  ge_p2 tmp2;
  ge_p3 tmp3;
  ec_scalar c;
  s_comm buf;
  assert(check_key(pub));
  buf.h = prefix_hash;
  buf.key = pub;
  if (ge_frombytes_vartime(&tmp3, &pub) != 0) {
    return false;
  }
  if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0 || !sc_isnonzero(&sig.c)) {
    return false;
  }
  ge_double_scalarmult_base_vartime(&tmp2, &sig.c, &tmp3, &sig.r);
  ge_tobytes(&buf.comm, &tmp2);
  static const ec_point infinity = {{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                     0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
  if (memcmp(&buf.comm, &infinity, 32) == 0)
    return false;
  hash_to_scalar(&buf, sizeof(s_comm), c);
  sc_sub(&c, &c, &sig.c);
  return sc_isnonzero(&c) == 0;
#else
  // unsigned long long messageLength{};
  // std::array<uint8_t, CRYPTO_BYTES + HASH_SIZE> message{};
  // auto result = crypto_sign_dilithium_open(message.data(), &messageLength,
  // (uint8_t *)sig.buffer.data(), sizeof(sig), (uint8_t *)pub.buffer.data());

  return true;
#endif
}

void crypto_ops::generate_tx_proof(const hash &prefix_hash, const public_key &R,
                                   const public_key &A,
                                   const boost::optional<public_key> &B,
                                   const public_key &D, const secret_key &r,
                                   signature &sig) {
#ifdef NDILITHIUM_CRYPTO
  // sanity check
  ge_p3 R_p3;
  ge_p3 A_p3;
  ge_p3 B_p3;
  ge_p3 D_p3;
  if (ge_frombytes_vartime(&R_p3, &R) != 0)
    throw std::runtime_error("tx pubkey is invalid");
  if (ge_frombytes_vartime(&A_p3, &A) != 0)
    throw std::runtime_error("recipient view pubkey is invalid");
  if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
    throw std::runtime_error("recipient spend pubkey is invalid");
  if (ge_frombytes_vartime(&D_p3, &D) != 0)
    throw std::runtime_error("key derivation is invalid");
#if !defined(NDEBUG)
  {
    assert(sc_check(&r) == 0);
    // check R == r*G or R == r*B
    public_key dbg_R;
    if (B) {
      ge_p2 dbg_R_p2;
      ge_scalarmult(&dbg_R_p2, &r, &B_p3);
      ge_tobytes(&dbg_R, &dbg_R_p2);
    } else {
      ge_p3 dbg_R_p3;
      ge_scalarmult_base(&dbg_R_p3, &r);
      ge_p3_tobytes(&dbg_R, &dbg_R_p3);
    }
    assert(R == dbg_R);
    // check D == r*A
    ge_p2 dbg_D_p2;
    ge_scalarmult(&dbg_D_p2, &r, &A_p3);
    public_key dbg_D;
    ge_tobytes(&dbg_D, &dbg_D_p2);
    assert(D == dbg_D);
  }
#endif

  // pick random k
  ec_scalar k;
  random_scalar(k);

  s_comm_2 buf;
  buf.msg = prefix_hash;
  buf.D = D;

  if (B) {
    // compute X = k*B
    ge_p2 X_p2;
    ge_scalarmult(&X_p2, &k, &B_p3);
    ge_tobytes(&buf.X, &X_p2);
  } else {
    // compute X = k*G
    ge_p3 X_p3;
    ge_scalarmult_base(&X_p3, &k);
    ge_p3_tobytes(&buf.X, &X_p3);
  }

  // compute Y = k*A
  ge_p2 Y_p2;
  ge_scalarmult(&Y_p2, &k, &A_p3);
  ge_tobytes(&buf.Y, &Y_p2);

  // sig.c = Hs(Msg || D || X || Y)
  hash_to_scalar(&buf, sizeof(buf), sig.c);

  // sig.r = k - sig.c*r
  sc_mulsub(&sig.r, &sig.c, &unwrap(r), &k);
#else

#endif
}

bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R,
                                const public_key &A,
                                const boost::optional<public_key> &B,
                                const public_key &D, const signature &sig) {
#ifdef NDILITHIUM_CRYPTO
  // sanity check
  ge_p3 R_p3;
  ge_p3 A_p3;
  ge_p3 B_p3;
  ge_p3 D_p3;
  if (ge_frombytes_vartime(&R_p3, &R) != 0)
    return false;
  if (ge_frombytes_vartime(&A_p3, &A) != 0)
    return false;
  if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
    return false;
  if (ge_frombytes_vartime(&D_p3, &D) != 0)
    return false;
  if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0)
    return false;

  // compute sig.c*R
  ge_p3 cR_p3;
  {
    ge_p2 cR_p2;
    ge_scalarmult(&cR_p2, &sig.c, &R_p3);
    public_key cR;
    ge_tobytes(&cR, &cR_p2);
    if (ge_frombytes_vartime(&cR_p3, &cR) != 0)
      return false;
  }

  ge_p1p1 X_p1p1;
  if (B) {
    // compute X = sig.c*R + sig.r*B
    ge_p2 rB_p2;
    ge_scalarmult(&rB_p2, &sig.r, &B_p3);
    public_key rB;
    ge_tobytes(&rB, &rB_p2);
    ge_p3 rB_p3;
    if (ge_frombytes_vartime(&rB_p3, &rB) != 0)
      return false;
    ge_cached rB_cached;
    ge_p3_to_cached(&rB_cached, &rB_p3);
    ge_add(&X_p1p1, &cR_p3, &rB_cached);
  } else {
    // compute X = sig.c*R + sig.r*G
    ge_p3 rG_p3;
    ge_scalarmult_base(&rG_p3, &sig.r);
    ge_cached rG_cached;
    ge_p3_to_cached(&rG_cached, &rG_p3);
    ge_add(&X_p1p1, &cR_p3, &rG_cached);
  }
  ge_p2 X_p2;
  ge_p1p1_to_p2(&X_p2, &X_p1p1);

  // compute sig.c*D
  ge_p2 cD_p2;
  ge_scalarmult(&cD_p2, &sig.c, &D_p3);

  // compute sig.r*A
  ge_p2 rA_p2;
  ge_scalarmult(&rA_p2, &sig.r, &A_p3);

  // compute Y = sig.c*D + sig.r*A
  public_key cD;
  public_key rA;
  ge_tobytes(&cD, &cD_p2);
  ge_tobytes(&rA, &rA_p2);
  ge_p3 cD_p3;
  ge_p3 rA_p3;
  if (ge_frombytes_vartime(&cD_p3, &cD) != 0)
    return false;
  if (ge_frombytes_vartime(&rA_p3, &rA) != 0)
    return false;
  ge_cached rA_cached;
  ge_p3_to_cached(&rA_cached, &rA_p3);
  ge_p1p1 Y_p1p1;
  ge_add(&Y_p1p1, &cD_p3, &rA_cached);
  ge_p2 Y_p2;
  ge_p1p1_to_p2(&Y_p2, &Y_p1p1);

  // compute c2 = Hs(Msg || D || X || Y)
  s_comm_2 buf;
  buf.msg = prefix_hash;
  buf.D = D;
  ge_tobytes(&buf.X, &X_p2);
  ge_tobytes(&buf.Y, &Y_p2);
  ec_scalar c2;
  hash_to_scalar(&buf, sizeof(s_comm_2), c2);

  // test if c2 == sig.c
  sc_sub(&c2, &c2, &sig.c);
  return sc_isnonzero(&c2) == 0;
#else
  return true;
#endif
}

static void hash_to_ec(const public_key &key, ge_p3 &res) {
  hash h{};
  ge_p2 point;
  ge_p1p1 point2;
  cn_fast_hash(std::addressof(key), sizeof(public_key), h);
  ge_fromfe_frombytes_vartime(&point,
                              reinterpret_cast<const unsigned char *>(&h));
  ge_mul8(&point2, &point);
  ge_p1p1_to_p3(&res, &point2);
}

void crypto_ops::generate_key_image(const public_key &pub,
                                    const secret_key &sec, key_image &image) {
#ifdef NDILITHIUM_CRYPTO
  ge_p3 point;
  ge_p2 point2;
  assert(sc_check(&sec) == 0);
  hash_to_ec(pub, point);
  ge_scalarmult(&point2, &unwrap(sec), &point);
  ge_tobytes(&image, &point2);
#else
  // The key image on these experimental change is just the same as the 'pub'
  // parameter. In future releases this will be fixed.
  // image.buffer = pub.buffer;
#endif
}

PUSH_WARNINGS
DISABLE_VS_WARNINGS(4200)
struct ec_point_pair {
  ec_point a, b;
};
struct rs_comm {
  hash h;
  struct ec_point_pair ab[];
};
POP_WARNINGS

static inline size_t rs_comm_size(size_t pubs_count) {
  return sizeof(rs_comm) + pubs_count * sizeof(ec_point_pair);
}

void crypto_ops::generate_ring_signature(
    const hash &prefix_hash, const std::size_t &hash_len,
    const derived_public_key *const *derived_pubs, std::size_t pubs_count,
    const derived_public_key &dpk, const public_key &mpk, const secret_key &msk,
    key_image &img, polyvecl &z, signature *sigs) {
  uint8_t(*Ring)[SIZE_DPK];
  memcpy(&Ring, derived_pubs, sizeof((uint8_t *)derived_pubs));

  std::array<char, 4712> signatures{};
  std::array<char, 1152> I{};

  auto result = sign_salrs((uint8_t *)&prefix_hash, hash_len, Ring, pubs_count,
                           (uint8_t *)&dpk, (uint8_t *)&mpk, (uint8_t *)&msk,
                           (uint8_t *)&I, static_cast<polyvecl *>(&z),
                           (uint8_t *)&signatures);

  LOG_PRINT_L1("sign result: " << result);

  std::copy(signatures.begin(), signatures.end(), sigs->buffer.begin());
  std::copy(I.begin(), I.end(), img.buffer.begin());
}

bool crypto_ops::check_ring_signature(const hash &prefix_hash,
                                      const key_image &image,
                                      const public_key *const *pubs,
                                      size_t pubs_count, const signature *sig) {

  uint8_t(*Ring)[SIZE_DPK];
  memcpy(&Ring, pubs, sizeof((uint8_t *)pubs));

  polyvecl z;
  std::array<char, 4712> signatures{};

  return verify_salrs((uint8_t *)&prefix_hash, sizeof(prefix_hash), Ring,
                      pubs_count, &z, (uint8_t *)&signatures) == 1;
}
} // namespace crypto


#pragma once
/******************************************************************************
*
*   Copyright (c) 2019 AT&T Intellectual Property.
*   Copyright (c) 2018-2019 Nokia.
*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
*
******************************************************************************/

// Standard Includes: ANSI C/C++, MSA, and Third-Party Libraries
#include <cmath>
#include <boost/integer/static_log2.hpp>

// Local Includes: Application specific classes, functions, and libraries
#include "asn/per/common.hpp"
#include "asn/per/binary_integer.hpp"

namespace asn {
namespace per {

/***************************************************************************************
* Encoding of a constrained whole number (X.691 10.5)
***************************************************************************************/

template<bound_t R, class E = void>
struct length_determinant;

template<bound_t R>
struct length_determinant<R, std::enable_if_t< R == 0 > > { static constexpr bound_t value = 0; };

template<bound_t R>
struct length_determinant<R, std::enable_if_t< R == 1 > > { static constexpr bound_t value = 1; };

template<bound_t R>
struct length_determinant<R, std::enable_if_t< (R  > 1) > > { static constexpr bound_t value = boost::static_log2<(R - 1)>::value + 1; };

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

template <class Range, class V, class Enable = void>
struct ConstrainedWholeNumber;

//Bit-field case
template <class Range, class V>
struct ConstrainedWholeNumber<Range, V, std::enable_if_t<(Range::upper_bound < (Range::lower_bound + 255))> >
{
	//non-negative-binary-integer X.691 10.5
	static void inline run(EncoderCtx& ctx, const V& val)
	{
		Tools::bit_accessor::put(static_cast<u8>(val - Range::lower_bound),
			length_determinant<(Range::upper_bound - Range::lower_bound + 1)>::value,
			ctx.refBuffer());
	}
	static V inline run(DecoderCtx& ctx)
	{
		return Range::lower_bound + Tools::bit_accessor::get(
			length_determinant<(Range::upper_bound - Range::lower_bound + 1)>::value,
			ctx.refBuffer());
	}
};

//One octet case
template <class Range, class V>
struct ConstrainedWholeNumber<Range, V, std::enable_if_t<(Range::upper_bound == (Range::lower_bound + 255))> >
{
	//non-negative-binary-integer X.691 10.5
	static void inline run(EncoderCtx& ctx, const V& val)
	{
		Tools::bit_accessor::padByte(ctx.refBuffer());
		ctx.refBuffer().putByte(static_cast<u8>(val - Range::lower_bound));
	}
	static V inline run(DecoderCtx& ctx)
	{
		V rval = 0;
		Tools::bit_accessor::padByte(ctx.refBuffer());
		u8 const* data = ctx.refBuffer().getBytes(1);
		if (data)
			rval = Range::lower_bound + data[0];
		return rval;
	}
};

//Two octets case
template <class Range, class V>
struct ConstrainedWholeNumber<Range, V, std::enable_if_t<(Range::upper_bound > (Range::lower_bound + 255)) && (Range::upper_bound <= (Range::lower_bound + 65535))> >
{
	//non-negative-binary-integer X.691 10.5
	static void inline run(EncoderCtx& ctx, const V& v)
	{
		u64 val = static_cast<u64>(v - Range::lower_bound);
		Tools::bit_accessor::padByte(ctx.refBuffer());
		ctx.refBuffer().putByte((u8)(val >> 8));
		ctx.refBuffer().putByte((u8)val);
	}
	static V inline run(DecoderCtx& ctx)
	{
		V rval = 0;
		Tools::bit_accessor::padByte(ctx.refBuffer());
		u8 const* data = ctx.refBuffer().getBytes(2);
		if (data) {
			rval = data[0];
			rval = rval << 8;
			rval |= data[1];
			rval += Range::lower_bound;
		}
		return rval;
	}
};

//Indefinite case
template <class Range, class V>
struct ConstrainedWholeNumber<Range, V, std::enable_if_t< (Range::upper_bound > Range::lower_bound + 65535) > >
{
	struct NormalizedValueRange
	{
		using boundary_type = typename Range::boundary_type;
		static constexpr bool extended = Range::extended;
		static constexpr boundary_type lower_bound = 0;
		static constexpr boundary_type upper_bound = Range::upper_bound - Range::lower_bound;
	};

	//non-negative-binary-integer X.691 10.5
	static void inline run(EncoderCtx& ctx, const V& val)
	{
		NonnegativeBinaryInteger<NormalizedValueRange>::run(val - Range::lower_bound, ctx);
	}
	static V inline run(DecoderCtx& ctx)
	{
		V rval = 0;
		NonnegativeBinaryInteger<NormalizedValueRange>::run(rval, ctx, false);
		return rval + Range::lower_bound;
	}
};

} //namespace per
} //namespace asn


#ifndef BOOST_PREPROCESSOR_ARITHMETIC_MUL_HPP
#define BOOST_PREPROCESSOR_ARITHMETIC_MUL_HPP

/* Copyright (C) 2001
 * Housemarque Oy
 * http://www.housemarque.com
 *
 * Permission to copy, use, modify, sell and distribute this software is
 * granted provided this copyright notice appears in all copies. This
 * software is provided "as is" without express or implied warranty, and
 * with no claim as to its suitability for any purpose.
 *
 * See http://www.boost.org for most recent version.
 */

#include <boost/preprocessor/arithmetic/add.hpp>

/**
Expands to the product of X and Y.

Both X and Y must expand to integer literals
in the range [0, BOOST_PP_LIMIT_MAG].

For example, BOOST_PP_MUL(4,3) expands to 12 (a
single token).

Uses

BOOST_PP_WHILE()

Test

arithmetic_test.cpp

*/
#define BOOST_PP_MUL(X,Y) BOOST_PP_MUL_D(0,X,Y)

/**
Can be used inside BOOST_PP_WHILE().
 */
#define BOOST_PP_MUL_D(D,X,Y) BOOST_PP_TUPLE_ELEM(3,0,BOOST_PP_WHILE##D(BOOST_PP_MUL_C,BOOST_PP_MUL_F,(0,X,Y)))
#if !defined(BOOST_NO_COMPILER_CONFIG) && defined(__MWERKS__)
#  define BOOST_PP_MUL_C(D,P) BOOST_PP_TUPLE_ELEM(3,2,P)
#  define BOOST_PP_MUL_F(D,P) (BOOST_PP_ADD_D(D,BOOST_PP_TUPLE_ELEM(3,0,P),BOOST_PP_TUPLE_ELEM(3,1,P)),BOOST_PP_TUPLE_ELEM(3,1,P),BOOST_PP_DEC(BOOST_PP_TUPLE_ELEM(3,2,P)))
#else
#  define BOOST_PP_MUL_C(D,P) BOOST_PP_TUPLE3_ELEM2 P
#  define BOOST_PP_MUL_F(D,P) (BOOST_PP_ADD_D(D,BOOST_PP_TUPLE3_ELEM0 P,BOOST_PP_TUPLE3_ELEM1 P),BOOST_PP_TUPLE3_ELEM1 P,BOOST_PP_DEC(BOOST_PP_TUPLE3_ELEM2 P))
#endif

/*
Obsolete. Use BOOST_PP_MUL().
 */
#define BOOST_PREPROCESSOR_MUL(X,Y) BOOST_PP_MUL(X,Y)
#endif


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

  @copyright 2016 NumScale SAS
  @copyright 2016 J.T. Lapreste

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

#include <boost/simd/function/scalar/fms.hpp>
#include <boost/simd/arch/common/generic/function/autodispatcher.hpp>
#include <boost/simd/arch/common/generic/function/fms.hpp>

#if defined(BOOST_HW_SIMD_X86)

#  if BOOST_HW_SIMD_X86 >= BOOST_HW_SIMD_X86_FMA3_VERSION
// #    include <boost/simd/arch/x86/fma3/simd/function/fms.hpp>
#  endif
#  if BOOST_HW_SIMD_X86 >= BOOST_HW_SIMD_X86_FMA4_VERSION
// #    include <boost/simd/arch/x86/fma4/simd/function/fms.hpp>
#  endif

#endif

#endif


// Boost.Geometry

// Copyright (c) 2020, Oracle and/or its affiliates.

// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Licensed under the Boost Software License version 1.0.
// http://www.boost.org/users/license.html

#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_HPP
#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_HPP

#include <boost/geometry/strategies/area/geographic.hpp>
#include <boost/geometry/strategies/convex_hull/geographic.hpp>
#include <boost/geometry/strategies/envelope/geographic.hpp>
#include <boost/geometry/strategies/expand/geographic.hpp>

namespace boost { namespace geometry
{

namespace strategies
{

template
<
    typename FormulaPolicy = strategy::andoyer,
    std::size_t SeriesOrder = strategy::default_order<FormulaPolicy>::value,
    typename Spheroid = srs::spheroid<double>,
    typename CalculationType = void
>
class geographic : strategies::detail::geographic_base<Spheroid>
{
    using base_t = strategies::detail::geographic_base<Spheroid>;

public:
    geographic()
        : base_t()
    {}

    explicit geographic(Spheroid const& spheroid)
        : base_t(spheroid)
    {}

    // area

    template <typename Geometry>
    auto area(Geometry const&) const
    {
        return strategy::area::geographic
            <
                FormulaPolicy, SeriesOrder, Spheroid, CalculationType
            >(base_t::m_spheroid);
    }

    // envelope

    template <typename Geometry, typename Box>
    static auto envelope(Geometry const&, Box const&,
                         typename util::enable_if_point_t<Geometry> * = nullptr)
    {
        return strategy::envelope::spherical_point();
    }

    template <typename Geometry, typename Box>
    static auto envelope(Geometry const&, Box const&,
                         typename util::enable_if_multi_point_t<Geometry> * = nullptr)
    {
        return strategy::envelope::spherical_multipoint();
    }

    template <typename Geometry, typename Box>
    static auto envelope(Geometry const&, Box const&,
                         typename util::enable_if_box_t<Geometry> * = nullptr)
    {
        return strategy::envelope::spherical_box();
    }

    template <typename Geometry, typename Box>
    auto envelope(Geometry const&, Box const&,
                  typename util::enable_if_segment_t<Geometry> * = nullptr) const
    {
        return strategy::envelope::geographic_segment
            <
                FormulaPolicy, Spheroid, CalculationType
            >(base_t::m_spheroid);
    }

    template <typename Geometry, typename Box>
    auto envelope(Geometry const&, Box const&,
                  typename util::enable_if_polysegmental_t<Geometry> * = nullptr) const
    {
        return strategy::envelope::geographic
            <
                FormulaPolicy, Spheroid, CalculationType
            >(base_t::m_spheroid);
    }

    // expand

    template <typename Box, typename Geometry>
    static auto expand(Box const&, Geometry const&,
                       typename util::enable_if_point_t<Geometry> * = nullptr)
    {
        return strategy::expand::spherical_point();
    }

    template <typename Box, typename Geometry>
    static auto expand(Box const&, Geometry const&,
                       typename util::enable_if_box_t<Geometry> * = nullptr)
    {
        return strategy::expand::spherical_box();
    }

    template <typename Box, typename Geometry>
    auto expand(Box const&, Geometry const&,
                typename util::enable_if_segment_t<Geometry> * = nullptr) const
    {
        return strategy::expand::geographic_segment
            <
                FormulaPolicy, Spheroid, CalculationType
            >(base_t::m_spheroid);
    }
};

} // namespace strategies

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_HPP


//
/// \file monte_carlo_test.cpp
/// \package how_much_data
//
/// \author Created by Joseph Dunn on 12/31/18.
/// \copyright © 2018, 2019 Joseph Dunn. All rights reserved.
//

#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
using std::ostream;
#include <iomanip>
using std::setw;
using std::setprecision;
using std::right;
using std::fixed;
#include <string>
using std::string;
using std::to_string;
#include <sstream>
using std::istringstream;
using std::ostringstream;
#include <fstream>
using std::ofstream;
#include <random>
using std::mt19937;
#include <vector>
using std::vector;
#include <array>
using std::array;
#include <algorithm>
using std::max;
using std::min;
using std::max_element;
using std::sort;
#include <numeric>
using std::accumulate;
#include <mutex>
using std::mutex;
using std::unique_lock;
#include <thread>
using std::thread;

#include <boost/timer/timer.hpp>
using boost::timer::auto_cpu_timer;
using boost::timer::cpu_timer;
using boost::timer::cpu_times;
using boost::timer::nanosecond_type;
#include <boost/filesystem.hpp>
using boost::filesystem::path;
#include <boost/math/special_functions/beta.hpp>
using boost::math::beta;
#include <boost/math/distributions/students_t.hpp>
using boost::math::students_t;

#include "adaptive_integration.h"
using namespace adaptive_integration;
Kronrod<double> k_big(10);
int noext = 0;
double epsabs_double = 0;
double epsrel_double = 64 * std::numeric_limits<double>::epsilon();
int limit = 2000;
int verbose_integration = 0;

/// Controller of beta function integrals.
static IntegrationController<double> ctl_beta(noext, k_big,
                                              epsabs_double, epsrel_double,
                                              limit, verbose_integration);

/// Controller of mad2 integrals in pareto.
static IntegrationController<double> ctl_mad2(noext, k_big,
                                              epsabs_double, epsrel_double,
                                              limit, verbose_integration);

#include "pareto_distribution.h"
template<>
IntegrationController<double>& pareto_distribution<double>::_ctl_beta{ctl_beta};
template<>
IntegrationController<double>& pareto_distribution<double>::_ctl_mad2{ctl_mad2};

#include "student_t_distribution.h"
#include "exponential_distribution.h"
#include "lognormal_distribution.h"
#include "normal_switch_mean.h"
#include "normal_switch_stddev.h"
#include "taleb_results.h"

/// return the relative error between two numbers
template<typename RealType>
RealType rel_err(RealType a,   ///< [in] the first number
                 RealType b    ///< [in] the second number
                 ) {
  return fabs(a-b)/std::max(a,b);
}

/// return quantiles of the ensemble of trials
template<typename RealType>
vector<RealType> quantile(const vector<RealType> &x,   ///< [in] the result by trial
                         const vector<RealType>& probs ///< [in] the desired probabilities
                         )
{
  RealType eps = 100 * std::numeric_limits<RealType>::epsilon();
  long np = probs.size();
  for (auto p : probs) {
    if (p < -eps || p > 1 + eps)
      throw std::range_error("quantile: 'probs' outside [0,1]");
  }
  vector<RealType> qs(np);
  long n = x.size();
  if (n > 0 && np > 0) {
    vector<RealType> x_sort = x;
    sort(x_sort.data(), x_sort.data()+n);
    for (int j=0; j<np; ++j) {
      RealType index = (n - 1) * probs.at(j);
      int lo = static_cast<int>(floor(index));
      int hi = static_cast<int>(ceil(index));
      RealType h = index - lo;
      qs.at(j) = (1-h) * x_sort.at(lo) + h * x_sort.at(hi);
    }
    return qs;
  } else {
    throw std::range_error("quantile: Both x and prob must be of length greater than 0");
  }
}

/// a mutex for writing to KappaResults
mutex kr_mutex;
/// a mutex for writing to cout
mutex cout_mutex;

/// the struct holding the resutls of a single alpha
struct KappaResult {
  double param;         ///< the parameter for the run
  double mad_rel_err;   ///< the relative error of the mad vs theory
  size_t m = 0;         ///< the number of trials run for mad
  size_t m_ci = 0;      ///< the number of trials for the conf. interval
  vector<int> ns;       ///< the durations calculated
  /// initialize the structure
  void initialize(double param_in,         ///< the alpha of the run
                  const vector<int>& ns_in ///< the durations of the run
                  ) {
    param = param_in;
    ns = ns_in;
    sum_dev.resize(ns_in.size(),0);
    sum_abs_dev.resize(ns_in.size(),0);
    conf_int.resize(ns_in.size());
    x_ci.resize(ns_in.size());
  }
  /// update the deviations w result from one thread
  void update_dev(size_t m,                 ///< the number of trials for mad
                  size_t m_ci,              ///< the number of trials for ci
                  const vector<double>& dev, ///< sum of deviation by duration
                  const vector<double>& abs_dev, ///<  sum of abs dev by duration
                  const vector<vector<double> >& x_in ///< the variate by trial
                  ) {
    unique_lock<mutex> lock(kr_mutex);
    this->m += m;
    this->m_ci += m_ci;
    for (size_t i=0; i<ns.size(); ++i){
      sum_dev.at(i) += dev.at(i);
      sum_abs_dev.at(i) += abs_dev.at(i);
      x_ci.at(i).insert(x_ci.at(i).end(), x_in.at(i).begin(), x_in.at(i).end());
    }
  }
  /// calculate the confidence interval for all trials
  void update_conf_int(double ci_level     ///< the confidence level to use
                       ) {
    unique_lock<mutex> lock(kr_mutex);
    vector<double> p{ci_level/2,1-ci_level/2};
    for (size_t j=0; j<ns.size(); ++j) {
      vector<double> tmp = quantile(x_ci.at(j),p);
      conf_int.at(j) = tmp.at(1)-tmp.at(0);
      x_ci.at(j).clear();
    }
  }
  vector<double> sum_dev;        ///< sum or raw deviation by duration
  vector<double> sum_abs_dev;    ///< sum of abs deviations by duration
  vector<vector<double> > x_ci;   ///< the results by trial and duration

  double ci_rel_err;              ///< the relative error of the ci vs theory
  vector<double> conf_int;        ///< the calculated ci by duration
  cpu_times elapsed_time;         ///< the elapsed time for the run
};

/// output the results from a single run
ostream& operator<< (ostream& os,          ///< [in,out] the output stream
                     const KappaResult& k  ///< [in] the sturct with results
                     ) {
  os << fixed << setw(7) << setprecision(2) << k.param
     << setw(13) << k.m
  << setw(12) << setprecision(2) << k.mad_rel_err*100 << "%";
  double mad0 = static_cast<double>(k.sum_abs_dev.at(0)/k.m);
  for (size_t i=1; i<k.ns.size(); ++i) {
    double madn = static_cast<double>(k.sum_abs_dev.at(i)/k.m);
    double kappa_mad = 2 - (log(k.ns.at(i))-log(k.ns.at(0)))
                           /(log(madn)-log(mad0));
    os << setw(13) << setprecision(3) << kappa_mad;
  }
  os << setw(10) << k.m_ci
     << setw(11) << setprecision(2) << k.ci_rel_err*100 << "%";
  double ci0 = k.conf_int.at(0);
  for (size_t i=1; i<k.ns.size(); ++i){
    double cin = k.conf_int.at(i);
    double kappa_ci = 2 -(log(k.ns.at(i))-log(k.ns.at(0)))/
                         (log(cin) - log(ci0));
    os << setw(13) << setprecision(3) << kappa_ci;
  }
  os << format(k.elapsed_time,1,"   %ws wall, %ts CPU (%p%)");
  os << endl;
  return os;
}

/// sturcture holding the results from all runs
struct KappaResults {
  /// constructor
  KappaResults(const vector<int>& ns,       ///< the durations calculated
               const vector<double> &params,///< the params for each run
               const string param_label,    ///< the label for the param
               size_t taleb_offset          ///< the column offset into the table
                                            ///< of taleg's results. 0 for none
               )
  : ns(ns), param_label(param_label), kr(params.size()),
    taleb_offset(taleb_offset){
    for (size_t i=0; i<params.size(); ++i) {
      kr.at(i).initialize(params.at(i),ns);
    }
  }
  /// the durations calculated
  vector<int> ns;
  /// a vector holding the results of each run
  /// the label for the parameter
  string param_label;
  vector<KappaResult> kr;
  /// the offset into Taleb's table of results.  =0 for none available.
  size_t taleb_offset;
};

/// output the results for all runs
ostream& operator<< (ostream& os, KappaResults& ks) {
  os << setw(7) << right << ks.param_label
  << setw(13) << right << "m_mad"
  << setw(13) << right << "mad_rel_err";
  for (size_t j=1; j<ks.ns.size(); ++j)
    os << setw(13) << right << "kappa"+to_string(ks.ns.at(j))+"_mad";
  os << setw(10) << right << "m_ci"
     << setw(12) << right << "ci_rel_err";
  for (size_t j=1; j<ks.ns.size(); ++j)
    os << setw(13) << right << "kappa"+to_string(ks.ns.at(j))+"_ci";
  os << endl << endl;
  for (auto& kr : ks.kr)
    os << kr;
  os << endl;
  if (ks.taleb_offset > 0) {
    os << setw(68) << right << "Error vs Taleb's Results" << endl << endl;
    for (size_t i=0; i<ks.kr.size(); ++i) {
      os << setw(7) << setprecision(2) << ks.kr.at(i).param << setw(26) << " ";
      double mad0 = static_cast<double>(ks.kr.at(i).sum_abs_dev.at(0)/ks.kr.at(i).m);
      for (size_t j=1; j<ks.ns.size(); ++j) {
        double madn = static_cast<double>(ks.kr.at(i).sum_abs_dev.at(j)/ks.kr.at(i).m);
        double kappa_mad = 2 - (log(ks.kr.at(i).ns.at(j))-log(ks.kr.at(i).ns.at(0)))
        /(log(madn)-log(mad0));
        double err = kappa_mad - taleb_results.at(i).at(j-1+ks.taleb_offset);
        os << setw(13) << setprecision(3) << err;
      }
      os << endl;
    }
    os << endl;
  }
  return os;
}

/// the per thread cacluaiton engine
template<typename Dist>
void calc_kappa(unsigned int thread_id,    ///< [in] the number of the thread
                                           ///< used as seed for urng
                size_t m,                  ///< [in] the maximum # of trials for mad
                size_t m_ci_limit,         ///< [in] the maximum # of trials for ci
                vector<int> ns,            ///< [in] the durations to save
                Dist dist,                 ///< [in] the distribution
                double ci_level,           ///< [in] the confidence level for kappa_ci
                KappaResult* kp,           ///< [out]the results
                bool verbose = false       ///< [in] flag for trace infomation
                ) {
  if (verbose) {
    unique_lock<mutex> lock(cout_mutex);
    cout << "Starting thread " << thread_id
         << ", with m = " << m
         << ", and m_ci_limit = " << m_ci_limit << endl;
  }
  mt19937 eng(thread_id);
  double mean = dist.mean();

  // Up to m_ci_limit we'll save the individual run results calculate
  // confidence intervals
  vector<vector<double> > x_ci(ns.size(), vector<double>(m_ci_limit));

  vector<double> dev(ns.size(),0.);
  vector<double> abs_dev(ns.size(),0.);

  for (size_t i=0; i<m; ++i) {
    int n_old = 0;
    double sum = 0;
    for (size_t j=0; j<ns.size(); j++) {
      int n = ns.at(j);
      for (int jj=n_old; jj<n; ++jj)
        sum += dist(eng);
      if ( i<m_ci_limit )
        x_ci.at(j).at(i) =sum;
      dev.at(j) += sum;
      abs_dev.at(j) += fabs(sum-n*mean);
      n_old = n;
    }
  } // i
  kp->update_dev(m, m_ci_limit, dev, abs_dev, x_ci);

  return;
}

/// calculate kappa for sums of iid variables at specified durations
template<typename Dist>
void calculate_kappa(size_t m,               ///< [in] the number of scenarios
                     vector<int> ns,         ///< [in] the durations to save
                     Dist dist,              ///< [in] the distribution
                     double ci_level,        ///< [in] the confidence level to use
                     KappaResult* kp,        ///< [out] a ptr to the results
                     bool verbose = false    ///< [in] a flag to generate trace
                     )
{
  cpu_timer timer;

  size_t m_ci_limit = min(static_cast<size_t>(2000000/ci_level),m);

  // Subdivide m between eight threads
  array<thread, 8> threads;
  size_t m_unassigned = m;
  size_t m_ci_unassigned = m_ci_limit;
  for (int i = 0; i<static_cast<int>(threads.size()); ++i) {
    int threads_to_go = static_cast<int>(threads.size())-i;
    size_t m_assigned = m_unassigned/threads_to_go;
    size_t m_ci_assigned = m_ci_unassigned/threads_to_go;
    threads.at(i)=thread(calc_kappa<Dist>, i+1, m_assigned, m_ci_assigned,
                         ns, dist, ci_level, kp, verbose);
    m_unassigned -= m_assigned;
    m_ci_unassigned -= m_ci_assigned;
  }
  for (size_t i=0; i<threads.size(); ++i)
    threads.at(i).join();
  kp->update_conf_int(ci_level);

  kp->mad_rel_err = rel_err(dist.mad(), static_cast<double>(kp->sum_abs_dev.at(0)/kp->m));
  kp->ci_rel_err = rel_err(dist.ci(ci_level), kp->conf_int.at(0));
  kp->elapsed_time = timer.elapsed();
}

/// show the usage.  called when the wrong # of arguments is used
void show_usage(path p) {
  cerr << "Usage: " << p.filename().string() << " m" << endl
  << "  where m is number of trials" << endl;
}

/// main program for convolution test with one input parameter the # of trials
int main(int argc, const char * argv[]) {
  path p(argv[0]);
  if ( argc != 2) {
    show_usage(p);
    return 1;
  }
  size_t m;
  istringstream iss1{argv[1]};
  if (!(iss1 >> m) || m < 1) {
    show_usage(p);
    return 1;
  }

  // Needed for the lognormal characteristic function
  Kronrod<double> k_big(10);
  int noext = 0;
  double epsabs_double = std::numeric_limits<double>::epsilon()/128;
  double epsrel_double = boost::math::tools::root_epsilon<double>();
  int limit = 1000;
  int verbose_integration = 0;
  IntegrationController<double> cf_ctl(noext, k_big,
                                       epsabs_double, epsrel_double,
                                       limit, verbose_integration);

  vector<double> alphas;

  for (double alpha=1.25; alpha<=4; alpha+=.25)
    alphas.push_back(alpha);

  vector<int> ns{1, 2, 30, 100};

  const double eps = std::numeric_limits<double>::epsilon();
  const double max_size_t = static_cast<double>(std::numeric_limits<size_t>::max())*(1-eps);

  double ci_level = .05;
  size_t m_ci_limit = min(static_cast<size_t>(2000000/ci_level), m);

  string out_dir{"../output"};
  ostringstream oss;
  oss << out_dir << "/monte_carlo_test_"  << m << ".out";
  cout << "Writing to file " << oss.str() << endl << endl;
  cout.flush();

  ofstream out{oss.str()};
  auto_cpu_timer t(out);
  out << "m =     " << m << endl
      << "ns =     ";
  for (auto n : ns)
    out << n << " ";
  out << endl<< endl;
  out.flush();
  bool verbose = true;
  {
    KappaResults ks_pareto(ns, alphas, "alpha", 1);
    for (size_t i=0; i<alphas.size(); ++i) {
      pareto_distribution<> pd(alphas.at(i));
      // m_alpha is the number of samples needed to reduce the
      // uncertainty in the average of m_alpha stable variates to
      // .001 of the uncertainty of a single sample
      double alpha_cap = log(2)/(log(pd.mad2())-log(pd.mad()));
      size_t m_alpha = static_cast<size_t>(min(max_size_t,pow(.001,alpha_cap/(1-alpha_cap))));
      m_alpha = min(max(m_ci_limit, m_alpha), m);
      if (verbose)
        cout << pd << endl;
      KappaResult kr;
      kr.initialize(alphas.at(i), ns);
      calculate_kappa(m_alpha, ns, pd, ci_level, &kr, verbose);
      ks_pareto.kr.at(i) = kr;
    }
    out << "Pareto Distribution" << endl << endl;
    out << ks_pareto;
  }

  {
    KappaResults ks_student(ns, alphas, "alpha", 4);
    for (size_t i=0; i<alphas.size(); ++i) {
      student_t_distribution<> td(alphas.at(i));
      // m_alpha is the number of samples needed to reduce the
      // uncertainty in the average of m_alpha stable variates to
      // .001 of the uncertainty of a single sample
      double alpha_cap = log(2)/(log(td.mad2())-log(td.mad()));
      size_t m_alpha = static_cast<size_t>(min(max_size_t,pow(.001,alpha_cap/(1-alpha_cap))));
      m_alpha = min(max(m_ci_limit, m_alpha), m);
      if (verbose)
        cout << td << endl;
      KappaResult kr;
      kr.initialize(alphas.at(i),ns);
      calculate_kappa(m_alpha, ns, td, ci_level, &kr, verbose);
      ks_student.kr.at(i) = kr;
    }
    out << "Student Distribution" << endl << endl;
    out << ks_student;
  }

  {
    vector<double> lambdas = {1.};
    KappaResults ks_exponential(ns, lambdas, "lambda" , 0);
    exponential_distribution<> ed(1);
    // m_alpha is the number of samples needed to reduce the
    // uncertainty in the average of m_alpha stable variates to
    // .001 of the uncertainty of a single sample
    double alpha_cap = log(2)/(log(ed.mad2())-log(ed.mad()));
    size_t m_alpha = static_cast<size_t>(min(max_size_t,pow(.001,alpha_cap/(1-alpha_cap))));
    m_alpha = min(max(m_ci_limit, m_alpha), m);
    if (verbose)
      cout << ed << endl;
    KappaResult kr;
    kr.initialize(1,ns);
    calculate_kappa(m_alpha, ns, ed, ci_level, &kr, verbose);
    ks_exponential.kr.at(0) = kr;
    out << ed << endl;
    out << ks_exponential << endl;
  }

  {
    vector<double> sigmas = {.1, .2, 1, 5, 10};
    KappaResults ks_lognormal(ns, sigmas, "sigma", 0);
    for (size_t i=0; i<sigmas.size(); ++i) {
      lognormal_distribution<> lnd(0,sigmas.at(i), cf_ctl);
      // m_alpha is the number of samples needed to reduce the
      // uncertainty in the average of m_alpha stable variates to
      // .001 of the uncertainty of a single sample
      double alpha_cap = log(2)/(log(lnd.mad2())-log(lnd.mad()));
      size_t m_alpha = static_cast<size_t>(min(max_size_t,pow(.001,alpha_cap/(1-alpha_cap))));
      m_alpha = min(max(m_ci_limit, m_alpha), m);
      if (verbose)
        cout << lnd << endl;
      KappaResult kr;
      kr.initialize(sigmas.at(i),ns);
      calculate_kappa(m_alpha, ns, lnd, ci_level, &kr, verbose);
      ks_lognormal.kr.at(i) = kr;
    }
    out << "Lognormal Distribution" << endl << endl;
    out << ks_lognormal;
  }

  {
    vector<double> ds = {0, 1, 2, 3, 4, 5};
    KappaResults ks_normal_switch_mean(ns, ds, "d" , 0);
    for(size_t j=0; j<ds.size(); ++j) {
      double d = ds.at(j);
      normal_switch_mean<> nsm(d);
      // m_alpha is the number of samples needed to reduce the
      // uncertainty in the average of m_alpha stable variates to
      // .001 of the uncertainty of a single sample
      double alpha_cap = log(2)/(log(nsm.mad2())-log(nsm.mad()));
      size_t m_alpha = static_cast<size_t>(min(max_size_t,pow(.001,alpha_cap/(1-alpha_cap))));
      m_alpha = min(max(m_ci_limit, m_alpha), m);
      if (verbose)
        cout << nsm << endl;
      KappaResult kr;
      kr.initialize(ds.at(j),ns);
      calculate_kappa(m_alpha, ns, nsm, ci_level, &kr, verbose);
      ks_normal_switch_mean.kr.at(j) = kr;
    }
    out << "Normal with Switching Mean But Common sigma" << endl << endl;
    out << ks_normal_switch_mean << endl;
  }

  {
    vector<double> as = {1, 2, 3, 4, 5};
    KappaResults ks_normal_switch_stddev(ns, as, "a" , 0);
    for(size_t j=0; j<as.size(); ++j) {
      double a = as.at(j);
      normal_switch_stddev<> nss(a, .1);
      // m_alpha is the number of samples needed to reduce the
      // uncertainty in the average of m_alpha stable variates to
      // .001 of the uncertainty of a single sample
      double alpha_cap = log(2)/(log(nss.mad2())-log(nss.mad()));
      size_t m_alpha = static_cast<size_t>(min(max_size_t,pow(.001,alpha_cap/(1-alpha_cap))));
      m_alpha = min(max(m_ci_limit, m_alpha), m);
      if (verbose)
        cout << nss << endl;
      KappaResult kr;
      kr.initialize(as.at(j),ns);
      calculate_kappa(m_alpha, ns, nss, ci_level, &kr, verbose);
      ks_normal_switch_stddev.kr.at(j) = kr;
    }
    out << "Normal with Switching Stddev w Mean = 0" << endl << endl;
    out << ks_normal_switch_stddev << endl;
  }

  return 0;
}


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

  @copyright 2012-2015 NumScale SAS
  @copyright 2015 J.T.Lapreste

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

#include <boost/simd/function/definition/safe_min.hpp>
#include <boost/simd/arch/common/generic/function/safe_min.hpp>

#endif


#define BOOST_TEST_MODULE "C++ Unit Tests for metaLBM"
#include <boost/test/included/unit_test.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
namespace tt = boost::test_tools;

#include <iostream>

#define NPROCS 1
#include "Lattice.h"
typedef double valueType;
typedef lbm::Lattice<valueType, lbm::LatticeType::D1Q3> L;
#include "Commons.h"
#include "MathVector.h"

using namespace lbm;

BOOST_AUTO_TEST_SUITE(TestCommons)

BOOST_AUTO_TEST_CASE(TestVTR) {
  constexpr CommonsType commonsType = CommonsType::Global;
  constexpr MathVector<int, 3> iP{{1}};

  Commons<valueType, commonsType> commons(amplitude);

  BOOST_TEST(commons.write(iP)[d::X] == (valueType) 1,
             tt::tolerance(1e-15));
}

BOOST_AUTO_TEST_SUITE_END()


#include <boost/accumulators/statistics/density.hpp>