<commit_before>/*
 * Copyright(c) Records For Living, Inc. 2004-2011.  All rights reserved
 */
/*
 * Portions of code from http://freecode-freecode.blogspot.com/2008/02/base64c.html (not copyrighted).
 */
/*
 * Portions of code from http://www.adp-gmbh.ch/cpp/common/base64.html
 *
 *  Copyright (C) 2004-2008 Ren Nyffenegger
 *
   base64.cpp and base64.h

   Copyright (C) 2004-2008 Ren Nyffenegger

   This source code is provided 'as-is', without any express or implied
   warranty. In no event will the author be held liable for any damages
   arising from the use of this software.

   Permission is granted to anyone to use this software for any purpose,
   including commercial applications, and to alter it and redistribute it
   freely, subject to the following restrictions:

   1. The origin of this source code must not be misrepresented; you must not
      claim that you wrote the original source code. If you use this source code
      in a product, an acknowledgment in the product documentation would be
      appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and must not be
      misrepresented as being the original source code.

   3. This notice may not be removed or altered from any source distribution.

   Ren Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/

#include	"../StroikaPreComp.h"

#include	"../Containers/Common.h"
#include	"../Debug/Assertions.h"
#include	"../Execution/Exceptions.h"
#include	"../Memory/SmallStackBuffer.h"
#include	"../Streams/BadFormatException.h"

#include	"Base64.h"

using	namespace	Stroika::Foundation;
using	namespace	Stroika::Foundation::Containers;
using	namespace	Stroika::Foundation::Cryptography;
using	namespace	Stroika::Foundation::Memory;

/*
 ********************************************************************************
 *************************** Encoding::DecodeBase64 *****************************
 ********************************************************************************
 */
namespace	{
	const char	BASE64_CHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

#if		0
	/**
	 * encode three bytes using base64 (RFC 3548)
	 *
	 * @param triple three bytes that should be encoded
	 * @param result buffer of four characters where the result is stored
	 */
	void base64_encode_triple_ (unsigned char triple[3], char result[4])
	{
		int tripleValue = triple[0];
		tripleValue *= 256;
		tripleValue += triple[1];
		tripleValue *= 256;
		tripleValue += triple[2];
		for (int i = 0; i < 4; i++) {
			result[3-i] = BASE64_CHARS[tripleValue%64];
			tripleValue /= 64;
		}
	}

	/**
	 * encode an array of bytes using Base64 (RFC 3548)
	 *
	 * @param source the source buffer
	 * @param sourcelen the length of the source buffer
	 * @param target the target buffer
	 * @param targetlen the length of the target buffer
	 * @return 1 on success, 0 otherwise
	 */
	int	base64_encode_ (unsigned char *source, size_t sourcelen, char *target, size_t targetlen)
	{
		/* check if the result will fit in the target buffer */
		if ((sourcelen+2)/3*4 > targetlen-1) {
			return 0;
		}

		/* encode all full triples */
		while (sourcelen >= 3) {
			base64_encode_triple_ (source, target);
			sourcelen -= 3;
			source += 3;
			target += 4;
		}

		/* encode the last one or two characters */
		if (sourcelen > 0) {
			unsigned char temp[3];
			memset(temp, 0, sizeof(temp));
			memcpy(temp, source, sourcelen);
			base64_encode_triple_ (temp, target);
			target[3] = '=';
			if (sourcelen == 1) {
				target[2] = '=';
			}
			target += 4;
		}

		/* terminate the string */
		target[0] = 0;

		return 1;
	}
#endif

	/**
	 * determine the value of a base64 encoding character
	 *
	 * @param base64char the character of which the value is searched
	 * @return the value in case of success (0-63), -1 on failure
	 */
	int base64_char_value_ (char base64char)
	{
		if (base64char >= 'A' && base64char <= 'Z') {
			return base64char-'A';
		}
		if (base64char >= 'a' && base64char <= 'z') {
			return base64char-'a'+26;
		}
		if (base64char >= '0' && base64char <= '9') {
			return base64char-'0'+2*26;
		}
		if (base64char == '+') {
			return 2*26+10;
		}
		if (base64char == '/') {
			return 2*26+11;
		}
		return -1;	// cuz we SKIP prefixing / trialing invalid characters - use to find end of series
	}

	/**
	 * decode a 4 char base64 encoded byte triple
	 *
	 * @param quadruple the 4 characters that should be decoded
	 * @param result the decoded data
	 * @return lenth of the result (1, 2 or 3), 0 on failure
	 */
	int base64_decode_triple_ (const char quadruple[4], unsigned char* result)
	{
		int triple_value, bytes_to_decode = 3, only_equals_yet = 1;
		int char_value[4];

		for (int i = 0; i < 4; i++) {
			char_value[i] = base64_char_value_ (quadruple[i]);
		}

		/* check if the characters are valid */
		for (int i = 3; i >= 0; i--) {
			if (char_value[i]<0) {
				if (only_equals_yet && quadruple[i]=='=') {
					/* we will ignore this character anyway, make it something
					* that does not break our calculations */
					char_value[i]=0;
					bytes_to_decode--;
					continue;
				}
				return 0;
			}
			/* after we got a real character, no other '=' are allowed anymore */
			only_equals_yet = 0;
		}

		/* if we got "====" as input, bytes_to_decode is -1 */
		if (bytes_to_decode < 0) {
			bytes_to_decode = 0;
		}

		/* make one big value out of the partial values */
		triple_value = char_value[0];
		triple_value *= 64;
		triple_value += char_value[1];
		triple_value *= 64;
		triple_value += char_value[2];
		triple_value *= 64;
		triple_value += char_value[3];

		/* break the big value into bytes */
		for (int i = bytes_to_decode; i < 3; i++) {
			triple_value /= 256;
		}
		for (int i = bytes_to_decode - 1; i >= 0; i--) {
			result[i] = triple_value%256;
			triple_value /= 256;
		}
		return bytes_to_decode;
	}

	/**
	 * decode base64 encoded data
	 *
	 * @param source the encoded data (zero terminated)
	 * @param target pointer to the target buffer
	 * @param targetlen length of the target buffer
	 * @return length of converted data on success, -1 otherwise
	 */
	size_t base64_decode_X_ (const char *source, size_t sourceLen, unsigned char *target, size_t targetlen)
	{
		size_t converted = 0;

		/* concatinate '===' to the source to handle unpadded base64 data */
		Memory::SmallStackBuffer<char>	src (sourceLen + 5);
		memcpy (src, source, sourceLen);
		memcpy (src + sourceLen, "===", 3);

		const char*	tmpptr = src;

		/* convert as long as we get a full result */
		size_t tmplen = 3;
		while (tmplen == 3) {
			char quadruple[4];
			/* get 4 characters to convert */
			for (int i = 0; i < 4; i++) {
				/* skip invalid characters - we won't reach the end */
				while (*tmpptr != '=' and base64_char_value_ (*tmpptr) < 0) {
					tmpptr++;
				}
				quadruple[i] = *(tmpptr++);
			}

			/* convert the characters */
			unsigned char tmpresult[3];
			tmplen = base64_decode_triple_ (quadruple, (unsigned char*)tmpresult);

			/* check if the fit in the result buffer */
			if (targetlen < tmplen) {
				Assert (false);	// cuz only called from place that assures buffer big enuf
				return -1;
			}

			/* put the partial result in the result buffer */
			memcpy (target, tmpresult, tmplen);
			target += tmplen;
			targetlen -= tmplen;
			converted += tmplen;
		}

		return converted;
	}
}

vector<Byte>	Cryptography::DecodeBase64 (const string& s)
{
	size_t dataSize1 = s.length ();
	SmallStackBuffer<Byte>	buf1 (dataSize1);	// MUCH more than big enuf
	size_t r = base64_decode_X_ (s.c_str (), s.length (), buf1.begin (), dataSize1);
	Assert (r <= dataSize1);
	return vector<Byte> (buf1.begin (), buf1.begin () + r);
}

/*
 ********************************************************************************
 *********************** Cryptography::EncodeBase64 *****************************
 ********************************************************************************
 */
namespace	{
	std::string base64_encode_ (unsigned char const* bytes_to_encode, size_t in_len, LineBreak lb)
	{
		string ret;
		ret.reserve ((in_len / 4) + 3);
		int i = 0;
		unsigned char char_array_3[3];
		unsigned char char_array_4[4];

		int rowLen = 0;
		while (in_len--) {
			char_array_3[i++] = *(bytes_to_encode++);
			if (i == 3) {
				char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
				char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
				char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
				char_array_4[3] = char_array_3[2] & 0x3f;

				for (i = 0; (i <4) ; i++) {
					ret += BASE64_CHARS[char_array_4[i]];
				}

				rowLen += 4;
				i = 0;
				if ((rowLen %76) == 0) {
					switch (lb) {
						case	eLF_LB: ret += "\n";; break;
						case	eCRLF_LB: ret += "\r\n"; break;
					}
					rowLen=0;
				}
			}
		}

		if (i != 0) {
			for (int j = i; j < 3; j++) {
				char_array_3[j] = '\0';
			}

			char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
			char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
			char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
			char_array_4[3] = char_array_3[2] & 0x3f;

			for (int j = 0; (j < i + 1); j++) {
				ret += BASE64_CHARS[char_array_4[j]];
			}

			while ((i++ < 3)) {
				ret += '=';
			}
		}

		return ret;
	}
}

string	Cryptography::EncodeBase64 (const Byte* start, const Byte* end, LineBreak lb)
{
	return base64_encode_ (start, end-start, lb);
}

string	Cryptography::EncodeBase64 (const vector<Byte>& b, LineBreak lb)
{
	return base64_encode_ (Containers::Start (b), b.size (), lb);
}

<commit_msg>experiment with new base64 decoder (public domain)<commit_after>/*
 * Copyright(c) Records For Living, Inc. 2004-2011.  All rights reserved
 */
/*
 * Portions of code from http://freecode-freecode.blogspot.com/2008/02/base64c.html (not copyrighted).
 */
/*
 * Portions of code from http://www.adp-gmbh.ch/cpp/common/base64.html
 *
 *  Copyright (C) 2004-2008 Ren Nyffenegger
 *
   base64.cpp and base64.h

   Copyright (C) 2004-2008 Ren Nyffenegger

   This source code is provided 'as-is', without any express or implied
   warranty. In no event will the author be held liable for any damages
   arising from the use of this software.

   Permission is granted to anyone to use this software for any purpose,
   including commercial applications, and to alter it and redistribute it
   freely, subject to the following restrictions:

   1. The origin of this source code must not be misrepresented; you must not
      claim that you wrote the original source code. If you use this source code
      in a product, an acknowledgment in the product documentation would be
      appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and must not be
      misrepresented as being the original source code.

   3. This notice may not be removed or altered from any source distribution.

   Ren Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/

#include	"../StroikaPreComp.h"

#include	"../Containers/Common.h"
#include	"../Debug/Assertions.h"
#include	"../Execution/Exceptions.h"
#include	"../Memory/SmallStackBuffer.h"
#include	"../Streams/BadFormatException.h"

#include	"Base64.h"

using	namespace	Stroika::Foundation;
using	namespace	Stroika::Foundation::Containers;
using	namespace	Stroika::Foundation::Cryptography;
using	namespace	Stroika::Foundation::Memory;

/*
 ********************************************************************************
 *************************** Encoding::DecodeBase64 *****************************
 ********************************************************************************
 */
namespace	{
	const char	BASE64_CHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
}
#if		0
namespace	{
	const char	BASE64_CHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

#if		0
	/**
	 * encode three bytes using base64 (RFC 3548)
	 *
	 * @param triple three bytes that should be encoded
	 * @param result buffer of four characters where the result is stored
	 */
	void base64_encode_triple_ (unsigned char triple[3], char result[4])
	{
		int tripleValue = triple[0];
		tripleValue *= 256;
		tripleValue += triple[1];
		tripleValue *= 256;
		tripleValue += triple[2];
		for (int i = 0; i < 4; i++) {
			result[3-i] = BASE64_CHARS[tripleValue%64];
			tripleValue /= 64;
		}
	}

	/**
	 * encode an array of bytes using Base64 (RFC 3548)
	 *
	 * @param source the source buffer
	 * @param sourcelen the length of the source buffer
	 * @param target the target buffer
	 * @param targetlen the length of the target buffer
	 * @return 1 on success, 0 otherwise
	 */
	int	base64_encode_ (unsigned char *source, size_t sourcelen, char *target, size_t targetlen)
	{
		/* check if the result will fit in the target buffer */
		if ((sourcelen+2)/3*4 > targetlen-1) {
			return 0;
		}

		/* encode all full triples */
		while (sourcelen >= 3) {
			base64_encode_triple_ (source, target);
			sourcelen -= 3;
			source += 3;
			target += 4;
		}

		/* encode the last one or two characters */
		if (sourcelen > 0) {
			unsigned char temp[3];
			memset(temp, 0, sizeof(temp));
			memcpy(temp, source, sourcelen);
			base64_encode_triple_ (temp, target);
			target[3] = '=';
			if (sourcelen == 1) {
				target[2] = '=';
			}
			target += 4;
		}

		/* terminate the string */
		target[0] = 0;

		return 1;
	}
#endif

	/**
	 * determine the value of a base64 encoding character
	 *
	 * @param base64char the character of which the value is searched
	 * @return the value in case of success (0-63), -1 on failure
	 */
	int base64_char_value_ (char base64char)
	{
		if (base64char >= 'A' && base64char <= 'Z') {
			return base64char-'A';
		}
		if (base64char >= 'a' && base64char <= 'z') {
			return base64char-'a'+26;
		}
		if (base64char >= '0' && base64char <= '9') {
			return base64char-'0'+2*26;
		}
		if (base64char == '+') {
			return 2*26+10;
		}
		if (base64char == '/') {
			return 2*26+11;
		}
		return -1;	// cuz we SKIP prefixing / trialing invalid characters - use to find end of series
	}

	/**
	 * decode a 4 char base64 encoded byte triple
	 *
	 * @param quadruple the 4 characters that should be decoded
	 * @param result the decoded data
	 * @return lenth of the result (1, 2 or 3), 0 on failure
	 */
	int base64_decode_triple_ (const char quadruple[4], unsigned char* result)
	{
		int triple_value, bytes_to_decode = 3, only_equals_yet = 1;
		int char_value[4];

		for (int i = 0; i < 4; i++) {
			char_value[i] = base64_char_value_ (quadruple[i]);
		}

		/* check if the characters are valid */
		for (int i = 3; i >= 0; i--) {
			if (char_value[i]<0) {
				if (only_equals_yet && quadruple[i]=='=') {
					/* we will ignore this character anyway, make it something
					* that does not break our calculations */
					char_value[i]=0;
					bytes_to_decode--;
					continue;
				}
				return 0;
			}
			/* after we got a real character, no other '=' are allowed anymore */
			only_equals_yet = 0;
		}

		/* if we got "====" as input, bytes_to_decode is -1 */
		if (bytes_to_decode < 0) {
			bytes_to_decode = 0;
		}

		/* make one big value out of the partial values */
		triple_value = char_value[0];
		triple_value *= 64;
		triple_value += char_value[1];
		triple_value *= 64;
		triple_value += char_value[2];
		triple_value *= 64;
		triple_value += char_value[3];

		/* break the big value into bytes */
		for (int i = bytes_to_decode; i < 3; i++) {
			triple_value /= 256;
		}
		for (int i = bytes_to_decode - 1; i >= 0; i--) {
			result[i] = triple_value%256;
			triple_value /= 256;
		}
		return bytes_to_decode;
	}

	/**
	 * decode base64 encoded data
	 *
	 * @param source the encoded data (zero terminated)
	 * @param target pointer to the target buffer
	 * @param targetlen length of the target buffer
	 * @return length of converted data on success, -1 otherwise
	 */
	size_t base64_decode_X_ (const char *source, size_t sourceLen, unsigned char *target, size_t targetlen)
	{
		size_t converted = 0;

		/* concatinate '===' to the source to handle unpadded base64 data */
		Memory::SmallStackBuffer<char>	src (sourceLen + 5);
		memcpy (src, source, sourceLen);
		memcpy (src + sourceLen, "===", 3);

		const char*	tmpptr = src;

		/* convert as long as we get a full result */
		size_t tmplen = 3;
		while (tmplen == 3) {
			char quadruple[4];
			/* get 4 characters to convert */
			for (int i = 0; i < 4; i++) {
				/* skip invalid characters - we won't reach the end */
				while (*tmpptr != '=' and base64_char_value_ (*tmpptr) < 0) {
					tmpptr++;
				}
				quadruple[i] = *(tmpptr++);
			}

			/* convert the characters */
			unsigned char tmpresult[3];
			tmplen = base64_decode_triple_ (quadruple, (unsigned char*)tmpresult);

			/* check if the fit in the result buffer */
			if (targetlen < tmplen) {
				Assert (false);	// cuz only called from place that assures buffer big enuf
				return -1;
			}

			/* put the partial result in the result buffer */
			memcpy (target, tmpresult, tmplen);
			target += tmplen;
			targetlen -= tmplen;
			converted += tmplen;
		}

		return converted;
	}
}
#endif

#if 1
//PUBLIC DOMAIN IMPL OF DECODE libb64
namespace	{
	enum base64_decodestep {
		step_a, step_b, step_c, step_d
	};
	struct base64_decodestate {
		base64_decodestep step;
		char plainchar;
	};
	void base64_init_decodestate(base64_decodestate* state_in)
		{
			state_in->step = step_a;
			state_in->plainchar = 0;
		}
	int base64_decode_value(char value_in)
		{
			static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
			static const char decoding_size = sizeof(decoding);
			value_in -= 43;
			if (value_in < 0 || value_in > decoding_size) return -1;
			return decoding[(int)value_in];
		}
	int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in)
		{
			const char* codechar = code_in;
			char* plainchar = plaintext_out;
			char fragment;

			*plainchar = state_in->plainchar;

			switch (state_in->step)
			{
				while (1)
				{
			case step_a:
					do {
						if (codechar == code_in+length_in)
						{
							state_in->step = step_a;
							state_in->plainchar = *plainchar;
							return plainchar - plaintext_out;
						}
						fragment = (char)base64_decode_value(*codechar++);
					} while (fragment < 0);
					*plainchar    = (fragment & 0x03f) << 2;
			case step_b:
					do {
						if (codechar == code_in+length_in)
						{
							state_in->step = step_b;
							state_in->plainchar = *plainchar;
							return plainchar - plaintext_out;
						}
						fragment = (char)base64_decode_value(*codechar++);
					} while (fragment < 0);
					*plainchar++ |= (fragment & 0x030) >> 4;
					*plainchar    = (fragment & 0x00f) << 4;
			case step_c:
					do {
						if (codechar == code_in+length_in)
						{
							state_in->step = step_c;
							state_in->plainchar = *plainchar;
							return plainchar - plaintext_out;
						}
						fragment = (char)base64_decode_value(*codechar++);
					} while (fragment < 0);
					*plainchar++ |= (fragment & 0x03c) >> 2;
					*plainchar    = (fragment & 0x003) << 6;
			case step_d:
					do {
						if (codechar == code_in+length_in)
						{
							state_in->step = step_d;
							state_in->plainchar = *plainchar;
							return plainchar - plaintext_out;
						}
						fragment = (char)base64_decode_value(*codechar++);
					} while (fragment < 0);
					*plainchar++   |= (fragment & 0x03f);
				}
			}
			/* control should not reach here */
			return plainchar - plaintext_out;
		}
}
#endif

vector<Byte>	Cryptography::DecodeBase64 (const string& s)
{
	size_t dataSize1 = s.length ();
	SmallStackBuffer<Byte>	buf1 (dataSize1);	// MUCH more than big enuf
#if 1
	base64_decodestate _state;
	base64_init_decodestate(&_state);
	int r = base64_decode_block(s.c_str (), s.length (), (char*)buf1.begin (), &_state);
#else
	size_t r = base64_decode_X_ (s.c_str (), s.length (), buf1.begin (), dataSize1);
#endif
	Assert (r <= dataSize1);
	return vector<Byte> (buf1.begin (), buf1.begin () + r);
}

/*
 ********************************************************************************
 *********************** Cryptography::EncodeBase64 *****************************
 ********************************************************************************
 */
namespace	{
	std::string base64_encode_ (unsigned char const* bytes_to_encode, size_t in_len, LineBreak lb)
	{
		string ret;
		ret.reserve ((in_len / 4) + 3);
		int i = 0;
		unsigned char char_array_3[3];
		unsigned char char_array_4[4];

		int rowLen = 0;
		while (in_len--) {
			char_array_3[i++] = *(bytes_to_encode++);
			if (i == 3) {
				char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
				char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
				char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
				char_array_4[3] = char_array_3[2] & 0x3f;

				for (i = 0; (i <4) ; i++) {
					ret += BASE64_CHARS[char_array_4[i]];
				}

				rowLen += 4;
				i = 0;
				if ((rowLen %76) == 0) {
					switch (lb) {
						case	eLF_LB: ret += "\n";; break;
						case	eCRLF_LB: ret += "\r\n"; break;
					}
					rowLen=0;
				}
			}
		}

		if (i != 0) {
			for (int j = i; j < 3; j++) {
				char_array_3[j] = '\0';
			}

			char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
			char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
			char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
			char_array_4[3] = char_array_3[2] & 0x3f;

			for (int j = 0; (j < i + 1); j++) {
				ret += BASE64_CHARS[char_array_4[j]];
			}

			while ((i++ < 3)) {
				ret += '=';
			}
		}

		return ret;
	}
}

string	Cryptography::EncodeBase64 (const Byte* start, const Byte* end, LineBreak lb)
{
	return base64_encode_ (start, end-start, lb);
}

string	Cryptography::EncodeBase64 (const vector<Byte>& b, LineBreak lb)
{
	return base64_encode_ (Containers::Start (b), b.size (), lb);
}

<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2017.  All rights reserved
 */
#include "../StroikaPreComp.h"

#include <random>

#include "../../Foundation/Execution/TimeOutException.h"
#include "../../Foundation/IO/Network/Socket.h"
#include "../../Foundation/IO/Network/SocketAddress.h"
#include "../../Foundation/Traversal/DiscreteRange.h"

#include "Ping.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::Containers;
using namespace Stroika::Foundation::Debug;
using namespace Stroika::Foundation::Execution;
using namespace Stroika::Foundation::Memory;
using namespace Stroika::Foundation::IO;
using namespace Stroika::Foundation::IO::Network;
using namespace Stroika::Foundation::Traversal;

using namespace Stroika::Frameworks;
using namespace Stroika::Frameworks::NetworkMontior;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

/*
 ********************************************************************************
 ***************************** NetworkMontior::Ping *****************************
 ********************************************************************************
 */
namespace {
    using Memory::Byte;

    // The IP header
    struct IPHeader {
        alignas (1) Byte h_len : 4;     // Length of the header in dwords
        alignas (1) Byte version : 4;   // Version of IP
        alignas (1) Byte tos;           // Type of service
        alignas (1) uint16_t total_len; // Length of the packet in dwords
        alignas (1) uint16_t ident;     // unique identifier
        alignas (1) uint16_t flags;     // Flags
        alignas (1) Byte ttl;           // Time to live
        alignas (1) Byte proto;         // Protocol number (TCP, UDP etc)
        alignas (1) uint16_t checksum;  // IP checksum
        alignas (1) uint32_t source_ip;
        alignas (1) uint32_t dest_ip;
    };
    static_assert (sizeof (IPHeader) == 20);

    // ICMP header
    struct ICMPHeader {
        alignas (1) Byte type; // ICMP packet type
        alignas (1) Byte code; // Type sub code
        alignas (1) uint16_t checksum;
        alignas (1) uint16_t id;
        alignas (1) uint16_t seq;
        alignas (1) uint32_t timestamp; // not part of ICMP, but we need it
    };
    static_assert (sizeof (ICMPHeader) == 12);

// ICMP packet types
#define ICMP_ECHO_REPLY 0
#define ICMP_DEST_UNREACH 3
#define ICMP_TTL_EXPIRE 11
#define ICMP_ECHO_REQUEST 8

    uint16_t ip_checksum (uint16_t* buffer, int size)
    {
        unsigned long cksum = 0;

        // Sum all the words together, adding the final byte if size is odd
        while (size > 1) {
            cksum += *buffer++;
            size -= sizeof (uint16_t);
        }
        if (size) {
            cksum += *(Byte*)buffer;
        }

        // Do a little shuffling
        cksum = (cksum >> 16) + (cksum & 0xffff);
        cksum += (cksum >> 16);

        // Return the bitwise complement of the resulting mishmash
        return (uint16_t) (~cksum);
    }
}

namespace {
// Minimum ICMP packet size, in bytes
#define ICMP_MIN 8
    constexpr size_t       DEFAULT_PACKET_SIZE = 32;
    constexpr unsigned int DEFAULT_TTL         = 30;
    constexpr size_t       MAX_PING_DATA_SIZE  = 1024;
    constexpr size_t       MAX_PING_PACKET_SIZE{MAX_PING_DATA_SIZE + sizeof (IPHeader)};
#define ICMP_ECHO_REQUEST 8
}

Duration NetworkMontior::Ping (const InternetAddress& addr, const PingOptions& options)
{
    // file:///C:/Sandbox/Stroika/DevRoot/Winsock%20Programmer%E2%80%99s%20FAQ_%20Ping_%20Raw%20Sockets%20Method.html
    int packet_size = DEFAULT_PACKET_SIZE;
    int ttl         = DEFAULT_TTL;
    packet_size     = max (sizeof (ICMPHeader), min (MAX_PING_DATA_SIZE, (unsigned int)packet_size));

    ICMPHeader pingRequest = []() {
        static std::mt19937                                             rng{std::random_device () ()};
        static std::uniform_int_distribution<std::mt19937::result_type> dist6 (0, numeric_limits<uint16_t>::max ());

        static uint16_t seq_no = dist6 (rng);

        ICMPHeader tmp{};
        tmp.type      = ICMP_ECHO_REQUEST;
        tmp.id        = dist6 (rng);
        tmp.seq       = seq_no++;
        tmp.timestamp = static_cast<uint32_t> (Time::GetTickCount () * 1000);
        return tmp;
    }();
    SmallStackBuffer<Byte> sendPacket (packet_size);
    memcpy (sendPacket.begin (), &pingRequest, sizeof (pingRequest));
    {
        const unsigned long int deadmeat   = 0xDEADBEEF;
        char*                   datapart   = (char*)sendPacket.begin () + sizeof (ICMPHeader);
        int                     bytes_left = packet_size - sizeof (ICMPHeader);
        while (bytes_left > 0) {
            memcpy (datapart, &deadmeat, min (int(sizeof (deadmeat)), bytes_left));
            bytes_left -= sizeof (deadmeat);
            datapart += sizeof (deadmeat);
        }
    }
    reinterpret_cast<ICMPHeader*> (sendPacket.begin ())->checksum = ip_checksum ((uint16_t*)sendPacket.begin (), packet_size);

    Socket s{Socket::ProtocolFamily::INET, Socket::SocketKind::RAW, IPPROTO_ICMP};
    s.setsockopt (IPPROTO_IP, IP_TTL, ttl);

    s.SendTo (sendPacket.begin (), sendPacket.end (), SocketAddress{addr, 0});

    while (true) {
        SocketAddress fromAddress;

        SmallStackBuffer<Byte> recv_buf (MAX_PING_PACKET_SIZE);
        size_t                 n     = s.ReceiveFrom (begin (recv_buf), end (recv_buf), 0, &fromAddress);
        IPHeader*              reply = reinterpret_cast<IPHeader*> (recv_buf.begin ());

        {
            // Skip ahead to the ICMP header within the IP packet
            unsigned short header_len = reply->h_len * 4;
            ICMPHeader*    icmphdr    = (ICMPHeader*)((char*)reply + header_len);

            // Make sure the reply is sane
            if (n < header_len + ICMP_MIN) {
                Execution::Throw (Execution::StringException (L"too few bytes from")); // draft @todo fix - from inet_ntoa (from->sin_addr)
            }
            else if (icmphdr->type != ICMP_ECHO_REPLY) {
                if (icmphdr->type != ICMP_TTL_EXPIRE) {
                    if (icmphdr->type == ICMP_DEST_UNREACH) {
                        Execution::Throw (Execution::StringException (L"Destination unreachable")); // draft @todo fix
                    }
                    else {
                        Execution::Throw (Execution::StringException (L"Unknown ICMP packet type")); // draft @todo fix - int (icmphdr->type)
                    }
                }
                // If "TTL expired", fall through.  Next test will fail if we
                // try it, so we need a way past it.
            }
            else if (icmphdr->id != pingRequest.id) {
                // Must be a reply for another pinger running locally, so just
                // ignore it.
                //              return -2;
            }

            // Figure out how far the packet travelled
            int nHops = int(256 - reply->ttl);
            if (nHops == 192) {
                // TTL came back 64, so ping was probably to a host on the
                // LAN -- call it a single hop.
                nHops = 1;
            }
            else if (nHops == 128) {
                // Probably localhost
                nHops = 0;
            }

            if (icmphdr->type == ICMP_TTL_EXPIRE) {
                Execution::Throw (Execution::StringException (L"TTL expired")); // draft @todo fix
            }
            return Duration ((Time::GetTickCount () * 1000 - icmphdr->timestamp) / 1000);
        }
    }
    Execution::Throw (Execution::TimeOutException (L"No response"));
}
<commit_msg>small fixes for new ping code to port to unix<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2017.  All rights reserved
 */
#include "../StroikaPreComp.h"

#include <random>

#include "../../Foundation/Execution/TimeOutException.h"
#include "../../Foundation/IO/Network/Socket.h"
#include "../../Foundation/IO/Network/SocketAddress.h"
#include "../../Foundation/Traversal/DiscreteRange.h"

#include "Ping.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::Containers;
using namespace Stroika::Foundation::Debug;
using namespace Stroika::Foundation::Execution;
using namespace Stroika::Foundation::Memory;
using namespace Stroika::Foundation::IO;
using namespace Stroika::Foundation::IO::Network;
using namespace Stroika::Foundation::Traversal;

using namespace Stroika::Frameworks;
using namespace Stroika::Frameworks::NetworkMontior;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

/*
 ********************************************************************************
 ***************************** NetworkMontior::Ping *****************************
 ********************************************************************************
 */
namespace {
    using Memory::Byte;

// The IP header
#if qPlatform_Linux
    using IPHeader = iphdr;
#else
    struct IPHeader {
        alignas (1) Byte h_len : 4;     // Length of the header in dwords
        alignas (1) Byte version : 4;   // Version of IP
        alignas (1) Byte tos;           // Type of service
        alignas (2) uint16_t total_len; // Length of the packet in dwords
        alignas (2) uint16_t ident;     // unique identifier
        alignas (2) uint16_t flags;     // Flags
        alignas (1) Byte ttl;           // Time to live
        alignas (1) Byte proto;         // Protocol number (TCP, UDP etc)
        alignas (2) uint16_t checksum;  // IP checksum
        alignas (4) uint32_t source_ip;
        alignas (4) uint32_t dest_ip;
    };
#endif
    static_assert (sizeof (IPHeader) == 20);

    // ICMP header
    struct ICMPHeader {
        alignas (1) Byte type; // ICMP packet type
        alignas (1) Byte code; // Type sub code
        alignas (1) uint16_t checksum;
        alignas (1) uint16_t id;
        alignas (1) uint16_t seq;
        alignas (1) uint32_t timestamp; // not part of ICMP, but we need it
    };
    static_assert (sizeof (ICMPHeader) == 12);

// ICMP packet types
#define ICMP_ECHO_REPLY 0
#define ICMP_DEST_UNREACH 3
#define ICMP_TTL_EXPIRE 11
#define ICMP_ECHO_REQUEST 8

    uint16_t ip_checksum (uint16_t* buffer, int size)
    {
        unsigned long cksum = 0;

        // Sum all the words together, adding the final byte if size is odd
        while (size > 1) {
            cksum += *buffer++;
            size -= sizeof (uint16_t);
        }
        if (size) {
            cksum += *(Byte*)buffer;
        }

        // Do a little shuffling
        cksum = (cksum >> 16) + (cksum & 0xffff);
        cksum += (cksum >> 16);

        // Return the bitwise complement of the resulting mishmash
        return (uint16_t) (~cksum);
    }
}

namespace {
    // Minimum ICMP packet size, in bytes
    constexpr size_t       ICMP_MIN{8};
    constexpr size_t       DEFAULT_PACKET_SIZE = 32;
    constexpr unsigned int DEFAULT_TTL         = 30;
    constexpr size_t       MAX_PING_DATA_SIZE  = 1024;
    constexpr size_t       MAX_PING_PACKET_SIZE{MAX_PING_DATA_SIZE + sizeof (IPHeader)};
#define ICMP_ECHO_REQUEST 8
}

Duration NetworkMontior::Ping (const InternetAddress& addr, const PingOptions& options)
{
    // file:///C:/Sandbox/Stroika/DevRoot/Winsock%20Programmer%E2%80%99s%20FAQ_%20Ping_%20Raw%20Sockets%20Method.html
    size_t packet_size = DEFAULT_PACKET_SIZE;
    int    ttl         = DEFAULT_TTL;
    packet_size        = max (sizeof (ICMPHeader), min (MAX_PING_DATA_SIZE, packet_size));

    ICMPHeader pingRequest = []() {
        static std::mt19937                                             rng{std::random_device () ()};
        static std::uniform_int_distribution<std::mt19937::result_type> dist6 (0, numeric_limits<uint16_t>::max ());

        static uint16_t seq_no = dist6 (rng);

        ICMPHeader tmp{};
        tmp.type      = ICMP_ECHO_REQUEST;
        tmp.id        = dist6 (rng);
        tmp.seq       = seq_no++;
        tmp.timestamp = static_cast<uint32_t> (Time::GetTickCount () * 1000);
        return tmp;
    }();
    SmallStackBuffer<Byte> sendPacket (packet_size);
    memcpy (sendPacket.begin (), &pingRequest, sizeof (pingRequest));
    {
        const unsigned long int deadmeat   = 0xDEADBEEF;
        char*                   datapart   = (char*)sendPacket.begin () + sizeof (ICMPHeader);
        int                     bytes_left = packet_size - sizeof (ICMPHeader);
        while (bytes_left > 0) {
            memcpy (datapart, &deadmeat, min (int(sizeof (deadmeat)), bytes_left));
            bytes_left -= sizeof (deadmeat);
            datapart += sizeof (deadmeat);
        }
    }
    reinterpret_cast<ICMPHeader*> (sendPacket.begin ())->checksum = ip_checksum ((uint16_t*)sendPacket.begin (), packet_size);

    Socket s{Socket::ProtocolFamily::INET, Socket::SocketKind::RAW, IPPROTO_ICMP};
    s.setsockopt (IPPROTO_IP, IP_TTL, ttl);

    s.SendTo (sendPacket.begin (), sendPacket.end (), SocketAddress{addr, 0});

    while (true) {
        SocketAddress fromAddress;

        SmallStackBuffer<Byte> recv_buf (MAX_PING_PACKET_SIZE);
        size_t                 n     = s.ReceiveFrom (begin (recv_buf), end (recv_buf), 0, &fromAddress);
        IPHeader*              reply = reinterpret_cast<IPHeader*> (recv_buf.begin ());

        {
            // Skip ahead to the ICMP header within the IP packet
            unsigned short header_len = reply->h_len * 4;
            ICMPHeader*    icmphdr    = (ICMPHeader*)((char*)reply + header_len);

            // Make sure the reply is sane
            if (n < header_len + ICMP_MIN) {
                Execution::Throw (Execution::StringException (L"too few bytes from")); // draft @todo fix - from inet_ntoa (from->sin_addr)
            }
            else if (icmphdr->type != ICMP_ECHO_REPLY) {
                if (icmphdr->type != ICMP_TTL_EXPIRE) {
                    if (icmphdr->type == ICMP_DEST_UNREACH) {
                        Execution::Throw (Execution::StringException (L"Destination unreachable")); // draft @todo fix
                    }
                    else {
                        Execution::Throw (Execution::StringException (L"Unknown ICMP packet type")); // draft @todo fix - int (icmphdr->type)
                    }
                }
                // If "TTL expired", fall through.  Next test will fail if we
                // try it, so we need a way past it.
            }
            else if (icmphdr->id != pingRequest.id) {
                // Must be a reply for another pinger running locally, so just
                // ignore it.
                //              return -2;
            }

            // Figure out how far the packet travelled
            int nHops = int(256 - reply->ttl);
            if (nHops == 192) {
                // TTL came back 64, so ping was probably to a host on the
                // LAN -- call it a single hop.
                nHops = 1;
            }
            else if (nHops == 128) {
                // Probably localhost
                nHops = 0;
            }

            if (icmphdr->type == ICMP_TTL_EXPIRE) {
                Execution::Throw (Execution::StringException (L"TTL expired")); // draft @todo fix
            }
            return Duration ((Time::GetTickCount () * 1000 - icmphdr->timestamp) / 1000);
        }
    }
    Execution::Throw (Execution::TimeOutException (L"No response"));
}
<|endoftext|>


<commit_before>/*
 * Author: Dino Wernli
 */

#include "scene/geometry/triangle.h"

#include "renderer/intersection_data.h"
#include "util/ray.h"

Triangle::Triangle(const Point3& c1, const Point3& c2, const Point3& c3)
    : corners_({c1, c2, c3}) {
}

Triangle::~Triangle() {
}

bool Triangle::Intersect(const Ray& ray, IntersectionData* data) {
  // TODO(dinow): Actually test for intersection.s
  return false;
}

<commit_msg>Add initializer of triangle for compatibility.<commit_after>/*
 * Author: Dino Wernli
 */

#include "scene/geometry/triangle.h"

#include "renderer/intersection_data.h"
#include "util/ray.h"

// TODO(dinow): Use array initializer as soon as all compilers can handler it.
Triangle::Triangle(const Point3& c1, const Point3& c2, const Point3& c3) {
    //: corners_({c1, c2, c3}) {
  corners_[0] = c1;
  corners_[1] = c2;
  corners_[2] = c3;
}

Triangle::~Triangle() {
}

bool Triangle::Intersect(const Ray& ray, IntersectionData* data) {
  // TODO(dinow): Actually test for intersection.s
  return false;
}

<|endoftext|>


<commit_before>// Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved.
// Copyright (c) 2021 by ApexAI Inc. All rights reserved.
//
// 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.
//
// SPDX-License-Identifier: Apache-2.0

#include "iceoryx_utils/cxx/deadline_timer.hpp"
#include "iceoryx_utils/internal/posix_wrapper/mutex.hpp"
#include "test.hpp"
#include "testutils/watch_dog.hpp"

#include <atomic>
#include <thread>

using namespace ::testing;
using namespace iox::units::duration_literals;

class Mutex_test : public Test
{
  public:
    void SetUp() override
    {
        internal::CaptureStderr();
        m_deadlockWatchdog.watchAndActOnFailure([] { std::terminate(); });
    }

    void TearDown() override
    {
        std::string output = internal::GetCapturedStderr();
        if (Test::HasFailure())
        {
            std::cout << output << std::endl;
        }
    }

    iox::posix::mutex sutNonRecursive{false};
    iox::posix::mutex sutRecursive{true};
    iox::units::Duration m_watchdogTimeout = 5_s;
    Watchdog m_deadlockWatchdog{m_watchdogTimeout};
};

TEST_F(Mutex_test, TryLockWithNoLock)
{
    EXPECT_THAT(sutNonRecursive.try_lock(), Eq(true));
    EXPECT_THAT(sutNonRecursive.unlock(), Eq(true));
}

#ifndef _WIN32
TEST_F(Mutex_test, TryLockWithNonRecursiveLock)
{
    EXPECT_THAT(sutNonRecursive.lock(), Eq(true));
    EXPECT_THAT(sutNonRecursive.try_lock(), Eq(false));
    EXPECT_THAT(sutNonRecursive.unlock(), Eq(true));
}
#endif

TEST_F(Mutex_test, LockAndUnlock)
{
    EXPECT_THAT(sutNonRecursive.lock(), Eq(true));
    EXPECT_THAT(sutNonRecursive.unlock(), Eq(true));
}

TEST_F(Mutex_test, LockAndUnlockRepeatedly)
{
    EXPECT_THAT(sutNonRecursive.lock(), Eq(true));
    EXPECT_THAT(sutNonRecursive.unlock(), Eq(true));
    EXPECT_THAT(sutNonRecursive.lock(), Eq(true));
    EXPECT_THAT(sutNonRecursive.unlock(), Eq(true));
}

#if !defined(_WIN32) || !defined(QNX) || !defined(QNX__) || !defined(__QNX__)
// in qnx you can destroy a locked mutex, without error if the thread holding the lock is destructing it.
TEST_F(Mutex_test, DestructorFailsOnLockedMutex)
{
    std::string output = internal::GetCapturedStderr();
    std::set_terminate([]() { std::cout << "", std::abort(); });

    EXPECT_DEATH(
        {
            iox::posix::mutex mtx{false};
            mtx.lock();
        },
        ".*");

    internal::CaptureStderr();
}
#endif

TEST_F(Mutex_test, LockedMutexBlocks)
{
    std::atomic_bool isLockFinished{false};
    sutNonRecursive.lock();

    std::thread lockThread([&] {
        sutNonRecursive.lock();
        isLockFinished.store(true);
        sutNonRecursive.unlock();
    });

    std::this_thread::sleep_for(std::chrono::milliseconds(500));
    EXPECT_THAT(isLockFinished.load(), Eq(false));

    sutNonRecursive.unlock();
    lockThread.join();

    EXPECT_THAT(isLockFinished.load(), Eq(true));
}

TEST_F(Mutex_test, TryLockWithRecursiveMutexWhenMutexLocked)
{
    std::atomic_bool isTryLockSuccessful{true};
    sutRecursive.lock();

    std::thread lockThread([&] { isTryLockSuccessful = sutRecursive.try_lock(); });

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    EXPECT_THAT(isTryLockSuccessful.load(), Eq(false));

    sutRecursive.unlock();
    lockThread.join();
}
<commit_msg>iox-#495 tidy up mutex test names<commit_after>// Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved.
// Copyright (c) 2021 by ApexAI Inc. All rights reserved.
//
// 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.
//
// SPDX-License-Identifier: Apache-2.0

#include "iceoryx_utils/cxx/deadline_timer.hpp"
#include "iceoryx_utils/internal/posix_wrapper/mutex.hpp"
#include "test.hpp"
#include "testutils/watch_dog.hpp"

#include <atomic>
#include <thread>

using namespace ::testing;
using namespace iox::units::duration_literals;

class Mutex_test : public Test
{
  public:
    void SetUp() override
    {
        internal::CaptureStderr();
        m_deadlockWatchdog.watchAndActOnFailure([] { std::terminate(); });
    }

    void TearDown() override
    {
        std::string output = internal::GetCapturedStderr();
        if (Test::HasFailure())
        {
            std::cout << output << std::endl;
        }
    }

    iox::posix::mutex sutNonRecursive{false};
    iox::posix::mutex sutRecursive{true};
    iox::units::Duration m_watchdogTimeout = 5_s;
    Watchdog m_deadlockWatchdog{m_watchdogTimeout};
};

TEST_F(Mutex_test, TryLockAndUnlockWithNonRecursiveMutexReturnTrue)
{
    EXPECT_THAT(sutNonRecursive.try_lock(), Eq(true));
    EXPECT_THAT(sutNonRecursive.unlock(), Eq(true));
}

#ifndef _WIN32
TEST_F(Mutex_test, TryLockWithNonRecursiveMutexReturnsFalseAfterLock)
{
    EXPECT_THAT(sutNonRecursive.lock(), Eq(true));
    EXPECT_THAT(sutNonRecursive.try_lock(), Eq(false));
    EXPECT_THAT(sutNonRecursive.unlock(), Eq(true));
}
#endif

TEST_F(Mutex_test, LockAndUnlockWithNonRecursiveMutexReturnsTrue)
{
    EXPECT_THAT(sutNonRecursive.lock(), Eq(true));
    EXPECT_THAT(sutNonRecursive.unlock(), Eq(true));
}

TEST_F(Mutex_test, RepeatedLockAndUnlockWithNonRecursiveMutexReturnsTrue)
{
    EXPECT_THAT(sutNonRecursive.lock(), Eq(true));
    EXPECT_THAT(sutNonRecursive.unlock(), Eq(true));
    EXPECT_THAT(sutNonRecursive.lock(), Eq(true));
    EXPECT_THAT(sutNonRecursive.unlock(), Eq(true));
}

#if !defined(_WIN32) || !defined(QNX) || !defined(QNX__) || !defined(__QNX__) || !defined(__APPLE__)
// in qnx you can destroy a locked mutex, without error if the thread holding the lock is destructing it.
TEST_F(Mutex_test, CallingDestructorOnLockedMutexLeadsToTermination)
{
    std::string output = internal::GetCapturedStderr();
    std::set_terminate([]() { std::cout << "", std::abort(); });

    EXPECT_DEATH(
        {
            iox::posix::mutex mtx{false};
            mtx.lock();
        },
        ".*");

    internal::CaptureStderr();
}
#endif

TEST_F(Mutex_test, LockedMutexBlocksCallingThreadExecution)
{
    std::atomic_bool isLockFinished{false};
    sutNonRecursive.lock();

    std::thread lockThread([&] {
        sutNonRecursive.lock();
        isLockFinished.store(true);
        sutNonRecursive.unlock();
    });

    std::this_thread::sleep_for(std::chrono::milliseconds(500));
    EXPECT_THAT(isLockFinished.load(), Eq(false));

    sutNonRecursive.unlock();
    lockThread.join();

    EXPECT_THAT(isLockFinished.load(), Eq(true));
}

TEST_F(Mutex_test, TryLockWithRecursiveMutexReturnsFalseWhenMutexLockedInOtherThread)
{
    std::atomic_bool isTryLockSuccessful{true};
    sutRecursive.lock();

    std::thread lockThread([&] { isTryLockSuccessful = sutRecursive.try_lock(); });

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    EXPECT_THAT(isTryLockSuccessful.load(), Eq(false));

    sutRecursive.unlock();
    lockThread.join();
}
<|endoftext|>


<commit_before>//
// Created by Manuel Polzhofer on 19.05.15.
//

#include "SimpleCompilerPipeline.h"

#include <TweeParser.h>
#include <fstream>
#include <iostream>
#include <memory>
#include <map>
#include <TweeFile.h>
#include "TweeCompiler.h"
#include "AssemblyParser.h"
#include <sstream>

using namespace std;

//Just a Simple Compiler Pipeline
void SimpleCompilerPipeline::compile(string filename, string zCodeFileName) {

    log("Simple Compiler Pipeline started");

    ifstream inputFile(filename);

    Twee::TweeParser parser(&inputFile);

    std::unique_ptr<TweeFile> tweeFile;
    try {
        tweeFile.reset(parser.parse());
    } catch (Twee::ParseException e) {
        log("Parse error");
        throw e;
    }

    log("Parsed twee file");

    stringstream buffer;

    TweeCompiler compiler;
    compiler.compile(*tweeFile, buffer);

    ofstream testFile("test.zas");
    testFile << buffer.str();

    //create header
    ZCodeHeader header = ZCodeHeader();

    //create memory sections
    vector<bitset<8>> dynamicMemory = generateDynamicMemory(header, 0x3f);
    vector<bitset<8>> staticMemory = generateStaticMemory(header, (int) (0x3f + dynamicMemory.size()));
    vector<bitset<8>> highMemory = generateHighMemory(header,
                                                      (int) (0x3f + staticMemory.size() + dynamicMemory.size()),
                                                      buffer);

    //init header
    header.setRoutinesOffset(88);
    header.setStaticStringsOffset(99);
    header.setFileLength(3, 52);
    header.baseOfStatMem = (uint16_t) (0x3f + dynamicMemory.size());
    header.baseOfHighMem = (uint16_t) (0x3f + dynamicMemory.size() + staticMemory.size());
    header.initValOfPC = header.baseOfHighMem;
    vector<bitset<8>> headerMemory = header.getHeaderBits();

    //concat memory sections
    vector<bitset<8>> zCode = vector<bitset<8>>();
    Utils::append(zCode, headerMemory);
    Utils::append(zCode, dynamicMemory);
    Utils::append(zCode, staticMemory);
    Utils::append(zCode, highMemory);

    RoutineGenerator::resolveCallInstructions(zCode);

    //calculate fileSize
    size_t fileSize = Utils::calculateNextPackageAddress(zCode.size());
    zCode = addFileSizeToHeader(zCode, fileSize);

    //generate empty space for padding
    size_t empty = fileSize - zCode.size();
    Utils::fillWithBytes(zCode, 0, (empty > 0) ? empty : 0);

    BinaryFileWriter binaryFileWriter;
    binaryFileWriter.write(zCodeFileName, zCode);
    log("ZCode File '" + zCodeFileName + "' generated");
}

std::vector<std::bitset<8>> SimpleCompilerPipeline::generateDynamicMemory(ZCodeHeader &header, size_t offset) {
    vector<bitset<8>> akk = vector<bitset<8>>();
    //abbervation strings
    Utils::fillWithBytes(akk, 0, 2);
    //abbervation table
    header.locOfAbbrTable = (uint16_t) (offset + akk.size());
    Utils::fillWithBytes(akk, 0, 0xc0);
    //property defaults
    Utils::fillWithBytes(akk, 0, 0x3e);
    //objects
    header.locOfObjTable = (uint16_t) (offset + akk.size());
    Utils::fillWithBytes(akk, 0, 0x5a3);
    //globalVariables
    header.locOfGlobVarTable = (uint16_t) (offset + akk.size());
    vector<bitset<8>> vars = printGlobalTable((int) (offset + akk.size()));
    akk.insert(akk.end(), vars.begin(), vars.end());
    return akk;
}

std::vector<std::bitset<8>> SimpleCompilerPipeline::generateStaticMemory(ZCodeHeader &header, size_t offset) {
    vector<bitset<8>> akk = vector<bitset<8>>();
    //grammar table
    Utils::fillWithBytes(akk, 0, 0x55f);
    //actions table
    Utils::fillWithBytes(akk, 0, 0xac);
    //preactions table
    Utils::fillWithBytes(akk, 0, 0xae);
    //adjectives table
    Utils::fillWithBytes(akk, 0, 0x4c);
    //dictionary
    Utils::fillWithBytes(akk, 0, 0x7bd);
    return akk;
}

std::vector<std::bitset<8>> SimpleCompilerPipeline::generateHighMemory(ZCodeHeader &header, size_t offset, std::istream& instructionsInput) {
    vector<bitset<8>> highMemoryZcode = vector<bitset<8>>();

    // this part creates call to first routine
    RoutineGenerator callToMainroutineGenerator = RoutineGenerator(offset);
    vector<bitset<8>> routine = callToMainroutineGenerator.getRoutine();
    Utils::append(highMemoryZcode, routine);

    AssemblyParser assemblyParser;

    ifstream input("eat_apple.zap", ifstream::in);

    assemblyParser.readAssembly(input, highMemoryZcode, offset);

    return highMemoryZcode;
}

std::vector<std::bitset<8>> SimpleCompilerPipeline::addFileSizeToHeader(std::vector<std::bitset<8>> zCode,
                                                                        size_t fileSize) {
    //change fileSize in header
    bitset<16> shortVal(fileSize / 8);
    bitset<8> firstHalf, secondHalf;

    for (size_t i = 0; i < 8; i++) {
        secondHalf.set(i, shortVal[i]);
    }

    for (size_t i = 8; i < 16; i++) {
        firstHalf.set(i - 8, shortVal[i]);
    }
    zCode[ZCodeHeader::HEADER_FILE_SIZE_POSITION] = firstHalf;
    zCode[ZCodeHeader::HEADER_FILE_SIZE_POSITION + 1] = secondHalf;
    return zCode;
}

void SimpleCompilerPipeline::printHex(std::vector<std::bitset<8>> bitsetList) {
    cout << endl << endl;
    for (unsigned int i = 0; i < bitsetList.size(); i++) {
        bitset<8> set(bitsetList.at(i));
        cout << hex << set.to_ulong() << endl;
    }
    cout << endl;
}

std::vector<std::bitset<8>> SimpleCompilerPipeline::printGlobalTable(int offset) {
    vector<bitset<8>> akk = vector<bitset<8>>();
    for (int i = 0; i < (0xff - 0x10); i++) {
        int adr = offset + (0xff - 0x10) * 2 + i * 100;
        bitset<8> one = bitset<8>((unsigned long long int) adr / 255);
        bitset<8> two = bitset<8>((unsigned long long int) adr % 256);
        akk.push_back(one);
        akk.push_back(two);
    }
    for (int i = 0; i < (0xff - 0x10); i++) {
        akk.push_back(bitset<8>(4));
        akk.push_back(bitset<8>(0));
        Utils::fillWithBytes(akk, 0, 96);
        akk.push_back(bitset<8>(0x80));
        akk.push_back(bitset<8>(0));
    }

    return akk;
}

void SimpleCompilerPipeline::log(string message) {
    cout << "Compiler: " << message << " . . ." << "\n";
}
<commit_msg>remove testinput<commit_after>//
// Created by Manuel Polzhofer on 19.05.15.
//

#include "SimpleCompilerPipeline.h"

#include <TweeParser.h>
#include <fstream>
#include <iostream>
#include <memory>
#include <map>
#include <TweeFile.h>
#include "TweeCompiler.h"
#include "AssemblyParser.h"
#include <sstream>

using namespace std;

//Just a Simple Compiler Pipeline
void SimpleCompilerPipeline::compile(string filename, string zCodeFileName) {

    log("Simple Compiler Pipeline started");

    ifstream inputFile(filename);

    Twee::TweeParser parser(&inputFile);

    std::unique_ptr<TweeFile> tweeFile;
    try {
        tweeFile.reset(parser.parse());
    } catch (Twee::ParseException e) {
        log("Parse error");
        throw e;
    }

    log("Parsed twee file");

    stringstream buffer;

    TweeCompiler compiler;
    compiler.compile(*tweeFile, buffer);

    ofstream testFile("test.zas");
    testFile << buffer.str();

    //create header
    ZCodeHeader header = ZCodeHeader();

    //create memory sections
    vector<bitset<8>> dynamicMemory = generateDynamicMemory(header, 0x3f);
    vector<bitset<8>> staticMemory = generateStaticMemory(header, (int) (0x3f + dynamicMemory.size()));
    vector<bitset<8>> highMemory = generateHighMemory(header,
                                                      (int) (0x3f + staticMemory.size() + dynamicMemory.size()),
                                                      buffer);

    //init header
    header.setRoutinesOffset(88);
    header.setStaticStringsOffset(99);
    header.setFileLength(3, 52);
    header.baseOfStatMem = (uint16_t) (0x3f + dynamicMemory.size());
    header.baseOfHighMem = (uint16_t) (0x3f + dynamicMemory.size() + staticMemory.size());
    header.initValOfPC = header.baseOfHighMem;
    vector<bitset<8>> headerMemory = header.getHeaderBits();

    //concat memory sections
    vector<bitset<8>> zCode = vector<bitset<8>>();
    Utils::append(zCode, headerMemory);
    Utils::append(zCode, dynamicMemory);
    Utils::append(zCode, staticMemory);
    Utils::append(zCode, highMemory);

    RoutineGenerator::resolveCallInstructions(zCode);

    //calculate fileSize
    size_t fileSize = Utils::calculateNextPackageAddress(zCode.size());
    zCode = addFileSizeToHeader(zCode, fileSize);

    //generate empty space for padding
    size_t empty = fileSize - zCode.size();
    Utils::fillWithBytes(zCode, 0, (empty > 0) ? empty : 0);

    BinaryFileWriter binaryFileWriter;
    binaryFileWriter.write(zCodeFileName, zCode);
    log("ZCode File '" + zCodeFileName + "' generated");
}

std::vector<std::bitset<8>> SimpleCompilerPipeline::generateDynamicMemory(ZCodeHeader &header, size_t offset) {
    vector<bitset<8>> akk = vector<bitset<8>>();
    //abbervation strings
    Utils::fillWithBytes(akk, 0, 2);
    //abbervation table
    header.locOfAbbrTable = (uint16_t) (offset + akk.size());
    Utils::fillWithBytes(akk, 0, 0xc0);
    //property defaults
    Utils::fillWithBytes(akk, 0, 0x3e);
    //objects
    header.locOfObjTable = (uint16_t) (offset + akk.size());
    Utils::fillWithBytes(akk, 0, 0x5a3);
    //globalVariables
    header.locOfGlobVarTable = (uint16_t) (offset + akk.size());
    vector<bitset<8>> vars = printGlobalTable((int) (offset + akk.size()));
    akk.insert(akk.end(), vars.begin(), vars.end());
    return akk;
}

std::vector<std::bitset<8>> SimpleCompilerPipeline::generateStaticMemory(ZCodeHeader &header, size_t offset) {
    vector<bitset<8>> akk = vector<bitset<8>>();
    //grammar table
    Utils::fillWithBytes(akk, 0, 0x55f);
    //actions table
    Utils::fillWithBytes(akk, 0, 0xac);
    //preactions table
    Utils::fillWithBytes(akk, 0, 0xae);
    //adjectives table
    Utils::fillWithBytes(akk, 0, 0x4c);
    //dictionary
    Utils::fillWithBytes(akk, 0, 0x7bd);
    return akk;
}

std::vector<std::bitset<8>> SimpleCompilerPipeline::generateHighMemory(ZCodeHeader &header, size_t offset, std::istream& instructionsInput) {
    vector<bitset<8>> highMemoryZcode = vector<bitset<8>>();

    // this part creates call to first routine
    RoutineGenerator callToMainroutineGenerator = RoutineGenerator(offset);
    vector<bitset<8>> routine = callToMainroutineGenerator.getRoutine();
    Utils::append(highMemoryZcode, routine);

    AssemblyParser assemblyParser;

    assemblyParser.readAssembly(instructionsInput, highMemoryZcode, offset);

    return highMemoryZcode;
}

std::vector<std::bitset<8>> SimpleCompilerPipeline::addFileSizeToHeader(std::vector<std::bitset<8>> zCode,
                                                                        size_t fileSize) {
    //change fileSize in header
    bitset<16> shortVal(fileSize / 8);
    bitset<8> firstHalf, secondHalf;

    for (size_t i = 0; i < 8; i++) {
        secondHalf.set(i, shortVal[i]);
    }

    for (size_t i = 8; i < 16; i++) {
        firstHalf.set(i - 8, shortVal[i]);
    }
    zCode[ZCodeHeader::HEADER_FILE_SIZE_POSITION] = firstHalf;
    zCode[ZCodeHeader::HEADER_FILE_SIZE_POSITION + 1] = secondHalf;
    return zCode;
}

void SimpleCompilerPipeline::printHex(std::vector<std::bitset<8>> bitsetList) {
    cout << endl << endl;
    for (unsigned int i = 0; i < bitsetList.size(); i++) {
        bitset<8> set(bitsetList.at(i));
        cout << hex << set.to_ulong() << endl;
    }
    cout << endl;
}

std::vector<std::bitset<8>> SimpleCompilerPipeline::printGlobalTable(int offset) {
    vector<bitset<8>> akk = vector<bitset<8>>();
    for (int i = 0; i < (0xff - 0x10); i++) {
        int adr = offset + (0xff - 0x10) * 2 + i * 100;
        bitset<8> one = bitset<8>((unsigned long long int) adr / 255);
        bitset<8> two = bitset<8>((unsigned long long int) adr % 256);
        akk.push_back(one);
        akk.push_back(two);
    }
    for (int i = 0; i < (0xff - 0x10); i++) {
        akk.push_back(bitset<8>(4));
        akk.push_back(bitset<8>(0));
        Utils::fillWithBytes(akk, 0, 96);
        akk.push_back(bitset<8>(0x80));
        akk.push_back(bitset<8>(0));
    }

    return akk;
}

void SimpleCompilerPipeline::log(string message) {
    cout << "Compiler: " << message << " . . ." << "\n";
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014-2017 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

/*!
 * \file stochastic_gradient_descent.hpp
 * \brief Stochastic Gradient Descent (SGD) Implementation for neural networks
 *
 * This implementations supports fully-connected layers, convolutional layers,
 * RBM layers, CRBM layers, transform layers and pooling layers.
 */

#pragma once

#include "cpp_utils/static_if.hpp"
#include "cpp_utils/tuple_utils.hpp"

#include "dll/util/checks.hpp"         // For NaN checks
#include "dll/util/timers.hpp"         // For auto_timer

namespace dll {

template<template<typename, typename, size_t> class Context, typename DBN, size_t... I>
auto build_context(DBN& dbn, std::index_sequence<I...> /*seq*/){
    return std::make_tuple
        (
            (std::make_pair(
                std::ref(dbn.template layer_get<I>()),  // Reference to the layer
                std::make_shared<Context<DBN, typename DBN::template layer_type<I>, I>>(dbn.template layer_get<I>()))
            )...
        );
}

template<template<typename, typename, size_t> class Context, typename DBN>
auto build_context(DBN& dbn){
    return build_context<Context>(dbn, std::make_index_sequence<DBN::layers>());
}

template <typename DBN>
struct sgd_trainer {
    using dbn_t     = DBN;
    using weight    = typename dbn_t::weight;
    using this_type = sgd_trainer<dbn_t>;

    static constexpr const auto layers     = dbn_t::layers;
    static constexpr const auto batch_size = dbn_t::batch_size;

    bool ae_training = false;

    dbn_t& dbn;

    /*!
     * \brief Indicates if the model is being trained as an auto-encoder (true) or not (false)
     */
    void set_autoencoder(bool ae){
        this->ae_training = ae;;
    }

    // Transform layers need to inherit dimensions from back

    template<typename L1, typename L2, cpp_enable_if(decay_layer_traits<typename L2::first_type>::is_transform_layer())>
    static void inherit_from_front(L1& l1, L2& l2){
        auto& ctx1 = *l1.second;
        auto& ctx2 = *l2.second;

        if (ctx2.errors.size() == 0) {
            ctx2.output = ctx1.output;
            ctx2.errors = ctx1.output;
            ctx2.input  = ctx1.output;
        }
    }

    template<typename L1, typename L2, cpp_disable_if(decay_layer_traits<typename L2::first_type>::is_transform_layer())>
    static void inherit_from_front(L1& /*l1*/, L2& /*l2*/){ }

    explicit sgd_trainer(dbn_t& dbn) : dbn(dbn) {
    }

    void init_training(std::size_t) {}

    template <typename D, typename It>
    void copy_inputs(D& dest, It first, It last) {
        std::size_t i = 0;

        while (first != last) {
            dest(i++) = *first++;
        }
    }

    template <typename D, typename It, cpp_enable_if(etl::decay_traits<D>::dimensions() == 2)>
    void copy_labels(D& dest, It first, It last) {
        //TODO How does that work in auto encoder mode ?

        std::size_t i = 0;

        while (first != last) {
            for (std::size_t l = 0; l < etl::dim<1>(dest); ++l) {
                dest(i, l) = (*first)[l];
            }
            ++i;
            ++first;
        }
    }

    template <typename D, typename It, cpp_enable_if(etl::decay_traits<D>::dimensions() == 4)>
    void copy_labels(D& dest, It first, It last) {
        //TODO How does that work in auto encoder mode ?

        std::size_t i = 0;

        while (first != last) {
            dest(i++) = *first++;
        }
    }

    // TODO: There are way too many copies going in this function

    template <typename Inputs, typename Labels, typename InputTransformer>
    std::pair<double, double> train_batch(std::size_t /*epoch*/, const Inputs& inputs, const Labels& labels, InputTransformer input_transformer) {
        dll::auto_timer timer("sgd::train_batch");

        // Build the contexts

        auto full_context = build_context<sgd_context>(dbn);

        // Inherit dimensions from front to end (for transform layers)

        cpp::for_each_pair(full_context, [](auto& layer_ctx_1, auto& layer_ctx_2) {
            constexpr bool l2_transform = decay_layer_traits<decltype(layer_ctx_2.first)>::is_transform_layer();

            if (l2_transform) {
                this_type::inherit_from_front(layer_ctx_1, layer_ctx_2);
            }
        });

        // Ensure that the data batch and the label batch are of the same size
        cpp_assert(etl::dim<0>(inputs) == etl::dim<0>(labels), "Invalid sizes");

        const auto n = etl::dim<0>(inputs);

        auto& first_layer = std::get<0>(full_context).first;
        auto& first_ctx   = *std::get<0>(full_context).second;

        auto& last_layer = std::get<layers - 1>(full_context).first;
        auto& last_ctx   = *std::get<layers - 1>(full_context).second;

        const bool full_batch = etl::dim<0>(inputs) == etl::dim<0>(first_ctx.input);

        //Copy inputs into suitable data structure

        auto tilde_inputs = force_temporary(inputs);
        for(size_t i = 0; i < etl::dim<0>(tilde_inputs); ++i){
            input_transformer(tilde_inputs(i));
        }

        //Feedforward pass

        {
            dll::auto_timer timer("sgd::forward");

            if(cpp_unlikely(!full_batch)){
                first_ctx.input  = 0;
                first_ctx.output = 0;

                for (size_t i = 0; i < etl::dim<0>(inputs); ++i) {
                    first_ctx.input(i) = tilde_inputs(i);
                }
            } else {
                first_ctx.input = tilde_inputs;
            }

            first_layer.batch_activate_hidden(first_ctx.output, first_ctx.input);

            cpp::for_each_pair(full_context, [](auto& layer_ctx_1, auto& layer_ctx_2) {
                auto& layer_2 = layer_ctx_2.first;

                auto& ctx1 = *layer_ctx_1.second;
                auto& ctx2 = *layer_ctx_2.second;

                ctx2.input = ctx1.output;
                layer_2.batch_activate_hidden(ctx2.output, ctx2.input);
            });
        }

        //Compute the errors of the last layer

        if (cpp_unlikely(!full_batch)) {
            first_ctx.input = 0;
            last_ctx.errors = 0;

            for (size_t i = 0; i < etl::dim<0>(inputs); ++i) {
                last_ctx.errors(i) = labels(i) - last_ctx.output(i);
            }
        } else {
            last_ctx.errors = labels - last_ctx.output;
        }

        // Backpropagate the error

        {
            dll::auto_timer timer("sgd::backward");

            cpp::for_each_pair(full_context, [](auto& layer_ctx_1, auto& layer_ctx_2) {
                auto& r2 = layer_ctx_2.first;

                auto& ctx1 = *layer_ctx_1.second;
                auto& ctx2 = *layer_ctx_2.second;

                r2.adapt_errors(ctx2);
                r2.backward_batch(ctx1.errors, ctx2);
            });

            first_layer.adapt_errors(first_ctx);
        }

        // Compute and apply the gradients

        {
            dll::auto_timer timer("sgd::grad");

            cpp::for_each(full_context, [this, n](auto& layer_ctx) {
                // Compute the gradients
                layer_ctx.first.compute_gradients(*layer_ctx.second);

                // Apply the gradients
                this->apply_gradients(layer_ctx.first, *layer_ctx.second, n);
            });
        }

        // Compute error and loss

        double error = 0.0;
        double loss = 0.0;

        {
            dll::auto_timer timer("sgd::error");

            auto& out = last_ctx.output;

            if (cpp_unlikely(!full_batch)) {
                error = amean(labels - slice(out, 0, etl::dim<0>(inputs)));

                if (ae_training) {
                    // Reconstruction Cross-Entropy Loss
                    loss = -sum((labels >> log(slice(out, 0, etl::dim<0>(inputs)))) + ((1.0 - labels) >> log(1 - slice(out, 0, etl::dim<0>(inputs))))) / double(n);
                } else {
                    // Cross-Entropy Loss
                    loss = -sum(log(slice(out, 0, etl::dim<0>(inputs))) >> labels) / double(n);
                }
            } else {
                error = amean(labels - out);

                if (ae_training) {
                    // Reconstruction Cross-Entropy Loss
                    loss = -sum((labels >> log(out)) + ((1.0 - labels) >> log(1 - out))) / double(n);
                } else {
                    // Cross-Entropy Loss
                    loss = -sum(log(out) >> labels) / double(n);
                }
            }
        }

        return std::make_pair(error, loss);
    }

    template <typename L, typename C, cpp_enable_if(decay_layer_traits<L>::is_neural_layer())>
    void apply_gradients(L& layer, C& context, std::size_t n) {
        dll::auto_timer timer("sgd::apply_grad");

        //Update the gradients
        this->update_grad(layer.w, context.w_grad, w_decay(dbn_traits<dbn_t>::decay()), 0.0);
        this->update_grad(layer.b, context.b_grad, b_decay(dbn_traits<dbn_t>::decay()), 0.0);

        //Update with momentum and learning rate
        if (dbn_traits<dbn_t>::has_momentum()) {
            auto momentum = dbn.momentum;
            auto eps      = dbn.learning_rate;

            // Note(perf): Some performance could be gained by doing the pair of
            // operations on w in a loop to improve data locality

            context.w_inc = momentum * context.w_inc + (eps / n) * context.w_grad;
            layer.w += context.w_inc;

            context.b_inc = momentum * context.b_inc + (eps / n) * context.b_grad;
            layer.b += context.b_inc;
        } else {
            auto eps = dbn.learning_rate;

            layer.w += (eps / n) * context.w_grad;
            layer.b += (eps / n) * context.b_grad;
        }

        nan_check_deep(layer.w);
        nan_check_deep(layer.b);
    }

    template <typename L, typename C, cpp_disable_if(decay_layer_traits<L>::is_neural_layer())>
    void apply_gradients(L& /*layer*/, C& /*context*/, std::size_t /*n*/) {
        //Pooling and transform layers have no weights, therefore no
        //gradients
    }

    template <typename V, typename G>
    void update_grad(const V& value, G& grad, decay_type decay, double penalty) {
        if (decay == decay_type::L1) {
            grad = grad - dbn.l1_weight_cost * abs(value) - penalty;
        } else if (decay == decay_type::L2) {
            grad = grad - dbn.l2_weight_cost * value - penalty;
        } else if (decay == decay_type::L1L2) {
            grad = grad - dbn.l1_weight_cost * abs(value) - dbn.l2_weight_cost * value - penalty;
        } else {
            if(penalty != 0.0){
                grad = grad - penalty;
            }
        }
    }

    static std::string name() {
        return "Stochastic Gradient Descent";
    }
};

} //end of dll namespace
<commit_msg>Fixes for the context<commit_after>//=======================================================================
// Copyright (c) 2014-2017 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

/*!
 * \file stochastic_gradient_descent.hpp
 * \brief Stochastic Gradient Descent (SGD) Implementation for neural networks
 *
 * This implementations supports fully-connected layers, convolutional layers,
 * RBM layers, CRBM layers, transform layers and pooling layers.
 */

#pragma once

#include "cpp_utils/static_if.hpp"
#include "cpp_utils/tuple_utils.hpp"

#include "dll/util/checks.hpp"         // For NaN checks
#include "dll/util/timers.hpp"         // For auto_timer

namespace dll {

template<template<typename, typename, size_t> class Context, typename DBN, size_t... I>
auto build_context(DBN& dbn, std::index_sequence<I...> /*seq*/){
    return std::make_tuple
        (
            (std::make_pair(
                std::ref(dbn.template layer_get<I>()),  // Reference to the layer
                std::make_shared<Context<DBN, typename DBN::template layer_type<I>, I>>(dbn.template layer_get<I>()))
            )...
        );
}

template<template<typename, typename, size_t> class Context, typename DBN>
auto build_context(DBN& dbn){
    return build_context<Context>(dbn, std::make_index_sequence<DBN::layers>());
}

template <typename DBN>
struct sgd_trainer {
    using dbn_t     = DBN;
    using weight    = typename dbn_t::weight;
    using this_type = sgd_trainer<dbn_t>;

    static constexpr const auto layers     = dbn_t::layers;
    static constexpr const auto batch_size = dbn_t::batch_size;

    bool ae_training = false;

    dbn_t& dbn;
    decltype(build_context<sgd_context>(dbn)) full_context;

    /*!
     * \brief Indicates if the model is being trained as an auto-encoder (true) or not (false)
     */
    void set_autoencoder(bool ae){
        this->ae_training = ae;;
    }

    // Transform layers need to inherit dimensions from back

    template<typename L1, typename L2, cpp_enable_if(decay_layer_traits<typename L2::first_type>::is_transform_layer())>
    static void inherit_from_front(L1& l1, L2& l2){
        auto& ctx1 = *l1.second;
        auto& ctx2 = *l2.second;

        if (ctx2.errors.size() == 0) {
            ctx2.output = ctx1.output;
            ctx2.errors = ctx1.output;
            ctx2.input  = ctx1.output;
        }
    }

    template<typename L1, typename L2, cpp_disable_if(decay_layer_traits<typename L2::first_type>::is_transform_layer())>
    static void inherit_from_front(L1& /*l1*/, L2& /*l2*/){ }

    explicit sgd_trainer(dbn_t& dbn) : dbn(dbn), full_context(build_context<sgd_context>(dbn)) {
        // Inherit dimensions from front to end (for transform layers)

        cpp::for_each_pair(full_context, [](auto& layer_ctx_1, auto& layer_ctx_2) {
            constexpr bool l2_transform = decay_layer_traits<decltype(layer_ctx_2.first)>::is_transform_layer();

            if (l2_transform) {
                this_type::inherit_from_front(layer_ctx_1, layer_ctx_2);
            }
        });
    }

    void init_training(std::size_t) {}

    template <typename D, typename It>
    void copy_inputs(D& dest, It first, It last) {
        std::size_t i = 0;

        while (first != last) {
            dest(i++) = *first++;
        }
    }

    template <typename D, typename It, cpp_enable_if(etl::decay_traits<D>::dimensions() == 2)>
    void copy_labels(D& dest, It first, It last) {
        //TODO How does that work in auto encoder mode ?

        std::size_t i = 0;

        while (first != last) {
            for (std::size_t l = 0; l < etl::dim<1>(dest); ++l) {
                dest(i, l) = (*first)[l];
            }
            ++i;
            ++first;
        }
    }

    template <typename D, typename It, cpp_enable_if(etl::decay_traits<D>::dimensions() == 4)>
    void copy_labels(D& dest, It first, It last) {
        //TODO How does that work in auto encoder mode ?

        std::size_t i = 0;

        while (first != last) {
            dest(i++) = *first++;
        }
    }

    // TODO: There are way too many copies going in this function

    template <typename Inputs, typename Labels, typename InputTransformer>
    std::pair<double, double> train_batch(std::size_t /*epoch*/, const Inputs& inputs, const Labels& labels, InputTransformer input_transformer) {
        dll::auto_timer timer("sgd::train_batch");

        // Ensure that the data batch and the label batch are of the same size
        cpp_assert(etl::dim<0>(inputs) == etl::dim<0>(labels), "Invalid sizes");

        const auto n = etl::dim<0>(inputs);

        auto& first_layer = std::get<0>(full_context).first;
        auto& first_ctx   = *std::get<0>(full_context).second;

        auto& last_layer = std::get<layers - 1>(full_context).first;
        auto& last_ctx   = *std::get<layers - 1>(full_context).second;

        const bool full_batch = etl::dim<0>(inputs) == etl::dim<0>(first_ctx.input);

        //Copy inputs into suitable data structure

        auto tilde_inputs = force_temporary(inputs);
        for(size_t i = 0; i < etl::dim<0>(tilde_inputs); ++i){
            input_transformer(tilde_inputs(i));
        }

        //Feedforward pass

        {
            dll::auto_timer timer("sgd::forward");

            if(cpp_unlikely(!full_batch)){
                first_ctx.input  = 0;
                first_ctx.output = 0;

                for (size_t i = 0; i < etl::dim<0>(inputs); ++i) {
                    first_ctx.input(i) = tilde_inputs(i);
                }
            } else {
                first_ctx.input = tilde_inputs;
            }

            first_layer.batch_activate_hidden(first_ctx.output, first_ctx.input);

            cpp::for_each_pair(full_context, [](auto& layer_ctx_1, auto& layer_ctx_2) {
                auto& layer_2 = layer_ctx_2.first;

                auto& ctx1 = *layer_ctx_1.second;
                auto& ctx2 = *layer_ctx_2.second;

                ctx2.input = ctx1.output;
                layer_2.batch_activate_hidden(ctx2.output, ctx2.input);
            });
        }

        //Compute the errors of the last layer

        if (cpp_unlikely(!full_batch)) {
            first_ctx.input = 0;
            last_ctx.errors = 0;

            for (size_t i = 0; i < etl::dim<0>(inputs); ++i) {
                last_ctx.errors(i) = labels(i) - last_ctx.output(i);
            }
        } else {
            last_ctx.errors = labels - last_ctx.output;
        }

        // Backpropagate the error

        {
            dll::auto_timer timer("sgd::backward");

            cpp::for_each_rpair(full_context, [](auto& layer_ctx_1, auto& layer_ctx_2) {
                auto& r2 = layer_ctx_2.first;

                auto& ctx1 = *layer_ctx_1.second;
                auto& ctx2 = *layer_ctx_2.second;

                r2.adapt_errors(ctx2);
                r2.backward_batch(ctx1.errors, ctx2);
            });

            first_layer.adapt_errors(first_ctx);
        }

        // Compute and apply the gradients

        {
            dll::auto_timer timer("sgd::grad");

            cpp::for_each(full_context, [this, n](auto& layer_ctx) {
                // Compute the gradients
                layer_ctx.first.compute_gradients(*layer_ctx.second);

                // Apply the gradients
                this->apply_gradients(layer_ctx.first, *layer_ctx.second, n);
            });
        }

        // Compute error and loss

        double error = 0.0;
        double loss = 0.0;

        {
            dll::auto_timer timer("sgd::error");

            auto& out = last_ctx.output;

            if (cpp_unlikely(!full_batch)) {
                error = amean(labels - slice(out, 0, etl::dim<0>(inputs)));

                if (ae_training) {
                    // Reconstruction Cross-Entropy Loss
                    loss = -sum((labels >> log(slice(out, 0, etl::dim<0>(inputs)))) + ((1.0 - labels) >> log(1 - slice(out, 0, etl::dim<0>(inputs))))) / double(n);
                } else {
                    // Cross-Entropy Loss
                    loss = -sum(log(slice(out, 0, etl::dim<0>(inputs))) >> labels) / double(n);
                }
            } else {
                error = amean(labels - out);

                if (ae_training) {
                    // Reconstruction Cross-Entropy Loss
                    loss = -sum((labels >> log(out)) + ((1.0 - labels) >> log(1 - out))) / double(n);
                } else {
                    // Cross-Entropy Loss
                    loss = -sum(log(out) >> labels) / double(n);
                }
            }
        }

        return std::make_pair(error, loss);
    }

    template <typename L, typename C, cpp_enable_if(decay_layer_traits<L>::is_neural_layer())>
    void apply_gradients(L& layer, C& context, std::size_t n) {
        dll::auto_timer timer("sgd::apply_grad");

        //Update the gradients
        this->update_grad(layer.w, context.w_grad, w_decay(dbn_traits<dbn_t>::decay()), 0.0);
        this->update_grad(layer.b, context.b_grad, b_decay(dbn_traits<dbn_t>::decay()), 0.0);

        //Update with momentum and learning rate
        if (dbn_traits<dbn_t>::has_momentum()) {
            auto momentum = dbn.momentum;
            auto eps      = dbn.learning_rate;

            // Note(perf): Some performance could be gained by doing the pair of
            // operations on w in a loop to improve data locality

            context.w_inc = momentum * context.w_inc + (eps / n) * context.w_grad;
            layer.w += context.w_inc;

            context.b_inc = momentum * context.b_inc + (eps / n) * context.b_grad;
            layer.b += context.b_inc;
        } else {
            auto eps = dbn.learning_rate;

            layer.w += (eps / n) * context.w_grad;
            layer.b += (eps / n) * context.b_grad;
        }

        nan_check_deep(layer.w);
        nan_check_deep(layer.b);
    }

    template <typename L, typename C, cpp_disable_if(decay_layer_traits<L>::is_neural_layer())>
    void apply_gradients(L& /*layer*/, C& /*context*/, std::size_t /*n*/) {
        //Pooling and transform layers have no weights, therefore no
        //gradients
    }

    template <typename V, typename G>
    void update_grad(const V& value, G& grad, decay_type decay, double penalty) {
        if (decay == decay_type::L1) {
            grad = grad - dbn.l1_weight_cost * abs(value) - penalty;
        } else if (decay == decay_type::L2) {
            grad = grad - dbn.l2_weight_cost * value - penalty;
        } else if (decay == decay_type::L1L2) {
            grad = grad - dbn.l1_weight_cost * abs(value) - dbn.l2_weight_cost * value - penalty;
        } else {
            if(penalty != 0.0){
                grad = grad - penalty;
            }
        }
    }

    static std::string name() {
        return "Stochastic Gradient Descent";
    }
};

} //end of dll namespace
<|endoftext|>


<commit_before>#include "Waves.h"

namespace TMM {

	ArrayXd TukeyFunc(ArrayXd xs, double w0, double a) {
		ArrayXd res(xs.size());
		for (int i = 0; i < xs.size(); i++) {
			double x = xs(i);

			if (x < -0.5 * w0 * (2.0 - a)) {
				res(i) = 0.0;
			}
			else if (x > 0.5 * w0 * (2.0 - a)) {
				res(i) = 0.0;
			} else if (x > 0.5 * a * w0) {
				res(i) = 0.5 * std::cos(PI / (w0 * (1.0 - a)) * (x - 0.5 * a * w0)) + 0.5;
			}
			else if (x < -0.5 * a * w0) {
				res(i) = 0.5 * std::cos(PI / (w0 * (1.0 - a)) * (x + 0.5 * (2.0 - a) * w0) + PI) + 0.5;
			}
			else {
				res(i) = 1.0;
			}
		}
		return res;
	}

	void Wave::SolvePlaneWave() {
		maxXThis = (0.5 * w0 / std::cos(thLayer0));

		// Plane waves
		phis = ArrayXd(1);
		kxs = ArrayXd(1);
		kzs = ArrayXd(1);
		expansionCoefsKx = ArrayXcd(1);
		fieldProfileXs = ArrayXd(0);
		fieldProfile = ArrayXd(0);
		phis(0) = 0.0;
		kxs(0) = k * std::sin(thLayer0);
		kzs(0) = k * std::cos(thLayer0);
		expansionCoefsKx(0) = E0;
	}

	void Wave::SolveFFTWave() {
		if (nPointsInteg < 2) {
			std::cerr << "nPointsInteg too small" << std::endl;
			throw std::runtime_error("nPointsInteg too small");
		}

		double maxXByPhi = (nPointsInteg / 2) * PI / (std::sin(maxPhi) * k);
		// Calc maxX
		if (dynamicMaxX) {
			maxXThis = dynamicMaxXCoef * (0.5 * w0 / std::cos(thLayer0)) + dynamicMaxXAddition;
		}
		else {
			maxXThis = maxX;
		}
		maxXThis = std::max(maxXThis, maxXByPhi);

		// Init xs
		ArrayXd xs(nPointsInteg);
		xs = ArrayXd::LinSpaced(nPointsInteg, -maxXThis, maxXThis);
		double dx = xs(1) - xs(0);

		// Field profile
		fieldProfileXs = xs;
		switch (waveType)
		{
		case TMM::GAUSSIANWAVE:
			fieldProfile = E0 * (-(xs * xs) / sqr(w0 / 1.2533141369277430)).exp();
			break;
		case TMM::TUKEYWAVE:
			fieldProfile = E0 * TukeyFunc(xs, w0 / (0.25 * a + 0.75), a);
			break;
		default:
			std::cerr << "Unknown FFT wave type." << std::endl;
			throw std::invalid_argument("Unknown FFT wave type.");
			break;
		}

		// FFT spectrum
		ArrayXd shiftedFieldProfile = IFFTShift(fieldProfile);
		ArrayXcd fieldProfileSpectrum(fieldProfile.size());
		fft.fwd(fieldProfileSpectrum.matrix(), shiftedFieldProfile.matrix());
		fieldProfileSpectrum = FFTShift(fieldProfileSpectrum);
		fieldProfileSpectrum *= dx / (2.0 * PI);
		ArrayXd kxPs = 2.0 * PI * FFTFreq(fieldProfile.size(), dx);
		phis = (kxPs / k).asin();

		// Check for backward-propagating waves
		if (phis.maxCoeff() + thLayer0 >= PI / 2.0) {
			std::cerr << "Phi larger than 90 deg." << std::endl;
			throw std::runtime_error("Phi larger than 90 deg.");
		}

		// Save result
		ArrayXd kzPs = phis.cos() * k;
		kxs = kxPs * std::cos(thLayer0) + kzPs * std::sin(thLayer0);
		kzs = -kxPs * std::sin(thLayer0) + kzPs * std::cos(thLayer0);
		expansionCoefsKx = fieldProfileSpectrum / std::cos(thLayer0);
	}

	void Wave::SolveSpdcWave() {
		// Checks
		if (std::isnan(deltaKxSpdc)) {
			std::cerr << "SPDC wave can only be used through SecondOrderNLTMM" << std::endl;
			throw std::runtime_error("SPDC wave can only be used through SecondOrderNLTMM");
		}
		maxXThis = (0.5 * w0 / std::cos(thLayer0));

		// Init arrays
		phis = ArrayXd(1);
		fieldProfileXs = ArrayXd(0);
		fieldProfile = ArrayXd(0);

		double kxMid = k * std::sin(thLayer0);
		kxs = ArrayXd::LinSpaced(nPointsInteg, kxMid - deltaKxSpdc, kxMid + deltaKxSpdc);
		kzs = (k * k - kxs.pow(2)).sqrt();
		expansionCoefsKx = ArrayXcd::Ones(nPointsInteg);
		phis(0) = 0.0; // Currently not calculated (not needed)
	}

	Wave::Wave() : phis(0), kxs(0), kzs(0), expansionCoefsKx(0), fieldProfileXs(0), fieldProfile(0) {
		waveType = PLANEWAVE;
		pwr = 1.0;
		overrideE0 = false;
		E0OverrideValue = 1.0;
		w0 = 100e-6;
		materialLayer0 = NULL;
		materialLayerThis = NULL;
		Ly = 1e-3;
		a = 0.7;
		nPointsInteg = 100;
		maxX = 1e-3;
		dynamicMaxX = true;
		dynamicMaxXCoef = 2.0;
		dynamicMaxXAddition = 0.0;
		maxXThis = 0.0;
		solved = false;
		maxPhi = 0.17;
		deltaKxSpdc = constNAN;

		wl = 0.0;
		beta = 0.0;
		k0 = 0.0;
		E0 = 0.0;
		k = 0.0;
		nLayer0 = 0.0;
		thLayer0 = 0.0;
	}

	void Wave::SetWaveType(WaveType waveType_) {
		waveType = waveType_;
	}

	void Wave::SetPwr(double pwr_) {
		pwr = pwr_;
	}

	void Wave::SetOverrideE0(bool overrideE0_) {
		overrideE0 = overrideE0_;
	}

	void Wave::SetE0(double E0_) {
		E0OverrideValue = E0_;
	}

	void Wave::SetW0(double w0_) {
		w0 = w0_;
	}

	void Wave::SetLy(double Ly_) {
		Ly = Ly_;
	}

	void Wave::SetA(double a_) {
		a = a_;
	}

	void Wave::SetNPointsInteg(int nPointsInteg_) {
		nPointsInteg = nPointsInteg_;
	}

	void Wave::SetMaxX(double maxX_) {
		maxX = maxX_;
	}

	void Wave::EnableDynamicMaxX(bool dynamicMaxX_) {
		dynamicMaxX = dynamicMaxX_;
	}

	void Wave::SetDynamicMaxXCoef(double dynamicMaxXCoef_) {
		dynamicMaxXCoef = dynamicMaxXCoef_;
	}

	void Wave::SetDynamicMaxXAddition(double dynamicMaxXAddition_) {
		dynamicMaxXAddition = dynamicMaxXAddition_;
	}

	void Wave::SetMaxPhi(double maxPhi_) {
		maxPhi = maxPhi_;
	}

	void Wave::SetParam(TMMParam param, double value) {
		switch (param)
		{
		case PARAM_WAVE_W0:
			SetW0(value);
			break;
		default:
			throw std::invalid_argument("Param not in list.");
			break;
		}
	}

	void Wave::Solve(double wl_, double beta_, Material *materialLayer0_, Material *materialLayerThis_, double deltaKxSpdc_) {
		wl = wl_;
		beta = beta_;
		materialLayer0 = materialLayer0_;
		materialLayerThis = materialLayerThis_;
		deltaKxSpdc = deltaKxSpdc_;
		maxXThis = maxX;

		// Calc E0
		if (!overrideE0) {
			double I0 = pwr / (Ly * w0);
			E0 = std::sqrt(2.0 * constMu0 * constC * I0);
		}
		else {
			E0 = E0OverrideValue;
		}

		// Precalc variables
		k0 = 2.0 * PI / wl;
		nLayer0 = real(materialLayer0->GetN(wl));
		k = k0 * nLayer0;
		thLayer0 = std::asin(beta / nLayer0);
		beamArea = w0 * Ly / std::cos(thLayer0);

		// Solve
		if (waveType == PLANEWAVE) {
			SolvePlaneWave();
		}
		else if (waveType == GAUSSIANWAVE || waveType == TUKEYWAVE) {
			SolveFFTWave();
		}
		else if (waveType == SPDCWAVE) {
			SolveSpdcWave();
		}
		else {
			std::cerr << "Unknown wave type." << std::endl;
			throw std::invalid_argument("Unknown wave type.");
		}
		solved = true;
	}

	// Getters

	WaveType Wave::GetWaveType() {
		return waveType;
	}

	double Wave::GetPwr() {
		return pwr;
	}

	bool Wave::GetOverrideE0() {
		return overrideE0;
	}

	double Wave::GetE0() {
		return E0OverrideValue;
	}

	double Wave::GetW0() {
		return w0;
	}

	double Wave::GetLy() {
		return Ly;
	}

	double Wave::GetA() {
		return a;
	}

	int Wave::GetNPointsInteg() {
		return nPointsInteg;
	}

	double Wave::GetMaxX() {
		return maxX;
	}

	bool Wave::IsDynamicMaxXEnabled() {
		return dynamicMaxX;
	}

	double Wave::GetDynamicMaxXCoef() {
		return dynamicMaxXCoef;
	}

	double Wave::GetDynamicMaxXAddition() {
		return dynamicMaxXAddition;
	}

	double Wave::GetMaxPhi() {
		return maxPhi;
	}

	double Wave::GetDouble(TMMParam param) {
		switch (param)
		{
		case PARAM_WAVE_W0:
			return GetW0();
			break;
		default:
			throw std::invalid_argument("Param not in list.");
			break;
		}
	}

	pairdd Wave::GetXRange() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return pairdd(-maxXThis, maxXThis);
	}

	ArrayXd Wave::GetBetas() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return kxs / k0;
	}

	ArrayXd Wave::GetPhis() { return phis; }

	ArrayXd Wave::GetKxs() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return kxs;
	}

	ArrayXd Wave::GetKzs() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return kzs;
	}

	ArrayXd Wave::GetFieldProfileXs() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return fieldProfileXs;
	}

	ArrayXd Wave::GetFieldProfile() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return fieldProfile;
	}

	ArrayXcd Wave::GetExpansionCoefsKx() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return expansionCoefsKx;
	}

	double Wave::GetBeamArea() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return beamArea;
	}

	bool Wave::IsCoherent() {
		if (waveType == SPDCWAVE) {
			return false;
		}
		return true;
	}

}<commit_msg>FFT fix test by pointers<commit_after>#include "Waves.h"

namespace TMM {

	ArrayXd TukeyFunc(ArrayXd xs, double w0, double a) {
		ArrayXd res(xs.size());
		for (int i = 0; i < xs.size(); i++) {
			double x = xs(i);

			if (x < -0.5 * w0 * (2.0 - a)) {
				res(i) = 0.0;
			}
			else if (x > 0.5 * w0 * (2.0 - a)) {
				res(i) = 0.0;
			} else if (x > 0.5 * a * w0) {
				res(i) = 0.5 * std::cos(PI / (w0 * (1.0 - a)) * (x - 0.5 * a * w0)) + 0.5;
			}
			else if (x < -0.5 * a * w0) {
				res(i) = 0.5 * std::cos(PI / (w0 * (1.0 - a)) * (x + 0.5 * (2.0 - a) * w0) + PI) + 0.5;
			}
			else {
				res(i) = 1.0;
			}
		}
		return res;
	}

	void Wave::SolvePlaneWave() {
		maxXThis = (0.5 * w0 / std::cos(thLayer0));

		// Plane waves
		phis = ArrayXd(1);
		kxs = ArrayXd(1);
		kzs = ArrayXd(1);
		expansionCoefsKx = ArrayXcd(1);
		fieldProfileXs = ArrayXd(0);
		fieldProfile = ArrayXd(0);
		phis(0) = 0.0;
		kxs(0) = k * std::sin(thLayer0);
		kzs(0) = k * std::cos(thLayer0);
		expansionCoefsKx(0) = E0;
	}

	void Wave::SolveFFTWave() {
		if (nPointsInteg < 2) {
			std::cerr << "nPointsInteg too small" << std::endl;
			throw std::runtime_error("nPointsInteg too small");
		}

		double maxXByPhi = (nPointsInteg / 2) * PI / (std::sin(maxPhi) * k);
		// Calc maxX
		if (dynamicMaxX) {
			maxXThis = dynamicMaxXCoef * (0.5 * w0 / std::cos(thLayer0)) + dynamicMaxXAddition;
		}
		else {
			maxXThis = maxX;
		}
		maxXThis = std::max(maxXThis, maxXByPhi);

		// Init xs
		ArrayXd xs(nPointsInteg);
		xs = ArrayXd::LinSpaced(nPointsInteg, -maxXThis, maxXThis);
		double dx = xs(1) - xs(0);

		// Field profile
		fieldProfileXs = xs;
		switch (waveType)
		{
		case TMM::GAUSSIANWAVE:
			fieldProfile = E0 * (-(xs * xs) / sqr(w0 / 1.2533141369277430)).exp();
			break;
		case TMM::TUKEYWAVE:
			fieldProfile = E0 * TukeyFunc(xs, w0 / (0.25 * a + 0.75), a);
			break;
		default:
			std::cerr << "Unknown FFT wave type." << std::endl;
			throw std::invalid_argument("Unknown FFT wave type.");
			break;
		}

		// FFT spectrum
		ArrayXd shiftedFieldProfile = IFFTShift(fieldProfile);
		ArrayXcd fieldProfileSpectrum(fieldProfile.size());
		fft.fwd(&fieldProfileSpectrum(0), &shiftedFieldProfile(0), fieldProfile.size());
		fieldProfileSpectrum = FFTShift(fieldProfileSpectrum);
		fieldProfileSpectrum *= dx / (2.0 * PI);
		ArrayXd kxPs = 2.0 * PI * FFTFreq(fieldProfile.size(), dx);
		phis = (kxPs / k).asin();

		// Check for backward-propagating waves
		if (phis.maxCoeff() + thLayer0 >= PI / 2.0) {
			std::cerr << "Phi larger than 90 deg." << std::endl;
			throw std::runtime_error("Phi larger than 90 deg.");
		}

		// Save result
		ArrayXd kzPs = phis.cos() * k;
		kxs = kxPs * std::cos(thLayer0) + kzPs * std::sin(thLayer0);
		kzs = -kxPs * std::sin(thLayer0) + kzPs * std::cos(thLayer0);
		expansionCoefsKx = fieldProfileSpectrum / std::cos(thLayer0);
	}

	void Wave::SolveSpdcWave() {
		// Checks
		if (std::isnan(deltaKxSpdc)) {
			std::cerr << "SPDC wave can only be used through SecondOrderNLTMM" << std::endl;
			throw std::runtime_error("SPDC wave can only be used through SecondOrderNLTMM");
		}
		maxXThis = (0.5 * w0 / std::cos(thLayer0));

		// Init arrays
		phis = ArrayXd(1);
		fieldProfileXs = ArrayXd(0);
		fieldProfile = ArrayXd(0);

		double kxMid = k * std::sin(thLayer0);
		kxs = ArrayXd::LinSpaced(nPointsInteg, kxMid - deltaKxSpdc, kxMid + deltaKxSpdc);
		kzs = (k * k - kxs.pow(2)).sqrt();
		expansionCoefsKx = ArrayXcd::Ones(nPointsInteg);
		phis(0) = 0.0; // Currently not calculated (not needed)
	}

	Wave::Wave() : phis(0), kxs(0), kzs(0), expansionCoefsKx(0), fieldProfileXs(0), fieldProfile(0) {
		waveType = PLANEWAVE;
		pwr = 1.0;
		overrideE0 = false;
		E0OverrideValue = 1.0;
		w0 = 100e-6;
		materialLayer0 = NULL;
		materialLayerThis = NULL;
		Ly = 1e-3;
		a = 0.7;
		nPointsInteg = 100;
		maxX = 1e-3;
		dynamicMaxX = true;
		dynamicMaxXCoef = 2.0;
		dynamicMaxXAddition = 0.0;
		maxXThis = 0.0;
		solved = false;
		maxPhi = 0.17;
		deltaKxSpdc = constNAN;

		wl = 0.0;
		beta = 0.0;
		k0 = 0.0;
		E0 = 0.0;
		k = 0.0;
		nLayer0 = 0.0;
		thLayer0 = 0.0;
	}

	void Wave::SetWaveType(WaveType waveType_) {
		waveType = waveType_;
	}

	void Wave::SetPwr(double pwr_) {
		pwr = pwr_;
	}

	void Wave::SetOverrideE0(bool overrideE0_) {
		overrideE0 = overrideE0_;
	}

	void Wave::SetE0(double E0_) {
		E0OverrideValue = E0_;
	}

	void Wave::SetW0(double w0_) {
		w0 = w0_;
	}

	void Wave::SetLy(double Ly_) {
		Ly = Ly_;
	}

	void Wave::SetA(double a_) {
		a = a_;
	}

	void Wave::SetNPointsInteg(int nPointsInteg_) {
		nPointsInteg = nPointsInteg_;
	}

	void Wave::SetMaxX(double maxX_) {
		maxX = maxX_;
	}

	void Wave::EnableDynamicMaxX(bool dynamicMaxX_) {
		dynamicMaxX = dynamicMaxX_;
	}

	void Wave::SetDynamicMaxXCoef(double dynamicMaxXCoef_) {
		dynamicMaxXCoef = dynamicMaxXCoef_;
	}

	void Wave::SetDynamicMaxXAddition(double dynamicMaxXAddition_) {
		dynamicMaxXAddition = dynamicMaxXAddition_;
	}

	void Wave::SetMaxPhi(double maxPhi_) {
		maxPhi = maxPhi_;
	}

	void Wave::SetParam(TMMParam param, double value) {
		switch (param)
		{
		case PARAM_WAVE_W0:
			SetW0(value);
			break;
		default:
			throw std::invalid_argument("Param not in list.");
			break;
		}
	}

	void Wave::Solve(double wl_, double beta_, Material *materialLayer0_, Material *materialLayerThis_, double deltaKxSpdc_) {
		wl = wl_;
		beta = beta_;
		materialLayer0 = materialLayer0_;
		materialLayerThis = materialLayerThis_;
		deltaKxSpdc = deltaKxSpdc_;
		maxXThis = maxX;

		// Calc E0
		if (!overrideE0) {
			double I0 = pwr / (Ly * w0);
			E0 = std::sqrt(2.0 * constMu0 * constC * I0);
		}
		else {
			E0 = E0OverrideValue;
		}

		// Precalc variables
		k0 = 2.0 * PI / wl;
		nLayer0 = real(materialLayer0->GetN(wl));
		k = k0 * nLayer0;
		thLayer0 = std::asin(beta / nLayer0);
		beamArea = w0 * Ly / std::cos(thLayer0);

		// Solve
		if (waveType == PLANEWAVE) {
			SolvePlaneWave();
		}
		else if (waveType == GAUSSIANWAVE || waveType == TUKEYWAVE) {
			SolveFFTWave();
		}
		else if (waveType == SPDCWAVE) {
			SolveSpdcWave();
		}
		else {
			std::cerr << "Unknown wave type." << std::endl;
			throw std::invalid_argument("Unknown wave type.");
		}
		solved = true;
	}

	// Getters

	WaveType Wave::GetWaveType() {
		return waveType;
	}

	double Wave::GetPwr() {
		return pwr;
	}

	bool Wave::GetOverrideE0() {
		return overrideE0;
	}

	double Wave::GetE0() {
		return E0OverrideValue;
	}

	double Wave::GetW0() {
		return w0;
	}

	double Wave::GetLy() {
		return Ly;
	}

	double Wave::GetA() {
		return a;
	}

	int Wave::GetNPointsInteg() {
		return nPointsInteg;
	}

	double Wave::GetMaxX() {
		return maxX;
	}

	bool Wave::IsDynamicMaxXEnabled() {
		return dynamicMaxX;
	}

	double Wave::GetDynamicMaxXCoef() {
		return dynamicMaxXCoef;
	}

	double Wave::GetDynamicMaxXAddition() {
		return dynamicMaxXAddition;
	}

	double Wave::GetMaxPhi() {
		return maxPhi;
	}

	double Wave::GetDouble(TMMParam param) {
		switch (param)
		{
		case PARAM_WAVE_W0:
			return GetW0();
			break;
		default:
			throw std::invalid_argument("Param not in list.");
			break;
		}
	}

	pairdd Wave::GetXRange() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return pairdd(-maxXThis, maxXThis);
	}

	ArrayXd Wave::GetBetas() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return kxs / k0;
	}

	ArrayXd Wave::GetPhis() { return phis; }

	ArrayXd Wave::GetKxs() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return kxs;
	}

	ArrayXd Wave::GetKzs() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return kzs;
	}

	ArrayXd Wave::GetFieldProfileXs() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return fieldProfileXs;
	}

	ArrayXd Wave::GetFieldProfile() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return fieldProfile;
	}

	ArrayXcd Wave::GetExpansionCoefsKx() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return expansionCoefsKx;
	}

	double Wave::GetBeamArea() {
		if (!solved) {
			std::cerr << "Wave must be solved first." << std::endl;
			throw std::runtime_error("Wave must be solved first.");
		}
		return beamArea;
	}

	bool Wave::IsCoherent() {
		if (waveType == SPDCWAVE) {
			return false;
		}
		return true;
	}

}<|endoftext|>