while (p < e)
	{
		if (*p == '\n')
		{
			if (p == in)
			{
				out->Append("\r\n", 2);
				p++;  // jump over NL
				p_beg = p;
			}
			else if (*(p - 1) != '\r')
			{
				len = p - p_beg;
				if (len > 0)
					out->Append(p_beg, len);

				out->Append("\r\n", 2);
				p++;  // jump over NL
				p_beg = p;
			}
			else // found CR LF; keep going
				p++;
		}
		else
			p++;
	}

//  Windows Mbox Viewer is a free tool to view, search and print mbox mail archives..
// Source code and executable can be downloaded from

#include "stdafx.h"
#include "TextUtilsEx.h"
#include "SimpleString.h"
#include <algorithm>

#if 1
void TextUtilsEx::ReplaceNL2CRNL(const char *in, int inLength, SimpleString *out)
{
	// Assume out is done by caller
	out->ClearAndResize(inLength * 2);
	register char *p = (char*)in;
	register char *e = p + inLength;
	char *p_beg = p;
	int len;

	p_beg = p;

len = p - p_beg;
	if (len > 0)
	{
		out->Append(p_beg, len);
		out->Append("\r\n", 2);
	}
}
#else
void ReplaceNL2CRNL(const char *in, int inLength, SimpleString *out)
{
	// Assume out is done by caller
	out->ClearAndResize(inLength * 2);
	register char *p = (char*)in;
	register char *e = p + inLength;
	char *p_beg = p;
	int len;

	p_beg = p;
	while (p < e)
	{
		while ((p < e) && (*p != '\n')) p++;
		if (p < e)  // found NL
		{
			if (p == in)
			{
				out->Append("\r\n", 2);
				p++;  // jump over NL
				p_beg = p;
			}
			else if (*(p - 1) != '\r')
			{
				len = p - p_beg;
				if (len > 0)
					out->Append(p_beg, len);

				out->Append("\r\n", 2);
				p++;  // jump over NL
				p_beg = p;
			}
			else
				p++;
		}
		else // p >= e didn't found NL done with looping
		{
			// we can be here if input is not terminated by NL
			len = p - p_beg;
			if (len > 0)
			{
				out->Append(p_beg, len);
				out->Append("\r\n", 2);
			}
		}
	}
}
#endif

void TextUtilsEx::EncodeAsHtml(const char *in, int inLength, SimpleString *out)
{
	// Assume out is done by caller
	out->ClearAndResize(inLength * 2);
	register char *p = (char*)in;
	register char *e = p + inLength;
	char *p_beg;
	int len;
	char c;

	p_beg = p;
	while (p < e)
	{
		c = *p;
		// TODO: implement as table and evalute performance
		if ((c == '>') || (c == '<') || (c == '\"') || (c == '&') || (c == '\''))
		{
			len = p - p_beg;
			if (len > 0) out->Append(p_beg, len);
			switch (c) {
			case '&':  out->Append("&"); break;
			case '\"': out->Append("""); break;
			case '\'': out->Append("'"); break;
			case '<':  out->Append("<"); break;
			case '>':  out->Append(">"); break;
			default:   break; // we should never be here
			}
			p++;
			p_beg = p;
		}
		else if (*p == '\r')
		{
			len = p - p_beg;
			if (len > 0)  out->Append(p_beg, len);

			p++;  // jump over '\r'
			if (p < e)
			{
				if (*p == '\n') {
					out->Append("
\r\n", 6);
					p++;
				}
				else
					out->Append("
\r", 5);

			}
			else
				out->Append("
\r", 5);

			p_beg = p;
		}
		else if (*p == '\n')
		{
			len = p - p_beg;
			if (len > 0)  out->Append(p_beg, len);
			out->Append("
\n", 5);
			p++;
			p_beg = p;
		}
		else
			p++;
	}
	len = p - p_beg;
	if (len > 0)
	{
		out->Append(p_beg, len);
	}
}

BOOL TextUtilsEx::Wide2Ansi(CStringW &strW, CString &strA, DWORD &error)
{
	UINT outCodePage = CP_ACP;
	BOOL ret = WStr2CodePage((wchar_t*)((LPCWSTR)strW), strW.GetLength(), outCodePage, &strA, error);
	return ret;
}

BOOL TextUtilsEx::Ansi2Wide(CString &strA, CStringW &strW, DWORD &error)
{
	UINT strCodePage = CP_ACP;
	BOOL ret = CodePage2WStr(&strA, strCodePage, &strW, error);
	return ret;
}

UINT TextUtilsEx::Str2PageCode(const  char* PageCodeStr)
{
	UINT CodePage = 0;

	CodePage = TextUtilsEx::charset2Id(PageCodeStr);

	return CodePage;
}

// TODO: Find time to reduce number of conversion functions, duplicates

DWORD TextUtilsEx::Str2Ansi(CString &str, UINT strCodePage)
{
	int len = str.GetLength() * 4 + 2;
	LPWSTR buff = (LPWSTR)malloc(len);  // or  we could call MultiByteToWideChar first to get the required length
	int len1 = MultiByteToWideChar(strCodePage, 0, str, str.GetLength(), buff, len);
	if (len1 == 0) {
		free(buff);
		// error - implement error log file
		const DWORD error = ::GetLastError();
		return error;
	}
	char * buff1 = (char *)malloc(len1 + 2); // or could  call WideCharToMultiByte first to get the required length
	int len2 = WideCharToMultiByte(CP_ACP, 0, buff, len1, buff1, len1 + 1, NULL, NULL);
	if (len2 == 0) {
		free(buff);
		free(buff1);
		// error - implement error log file
		const DWORD error = ::GetLastError();
		/*ERROR_INSUFFICIENT_BUFFER.A supplied buffer size was not large enough, or it was incorrectly set to NULL.
		ERROR_INVALID_FLAGS.The values supplied for flags were not valid.
		ERROR_INVALID_PARAMETER.Any of the parameter values was invalid.
		ERROR_NO_UNICODE_TRANSLATION.Invalid Unicode was found in a string.*/
		return error;
	}
	buff1[len2] = 0;
	str = buff1;
	free(buff);
	free(buff1);

	return 0;
}

BOOL TextUtilsEx::Str2CodePage(const char *str, int strlen, UINT inCodePage, UINT outCodePage, SimpleString *result, SimpleString *workBuff)
{
	int buffLen = strlen * 4 + 2;
	workBuff->ClearAndResize(buffLen);
	LPWSTR buff = (LPWSTR)workBuff->Data();  // or  we could call MultiByteToWideChar first to get the required length
	int wlen = MultiByteToWideChar(inCodePage, 0, str, strlen, buff, buffLen);
	if (wlen == 0) {
		result->Clear();
		// error - implement error log file
		const DWORD error = ::GetLastError();
		return FALSE;
	}

	int outLen = wlen * 4 + 2;
	result->ClearAndResize(outLen); // or could  call WideCharToMultiByte first to get the required length
	if (outCodePage == 0)
		outCodePage = CP_UTF8;
	int utf8Len = WideCharToMultiByte(outCodePage, 0, buff, wlen, result->Data(), outLen, NULL, NULL);
	if (utf8Len == 0) {
		result->Clear();
		// error - implement error log file
		const DWORD error = ::GetLastError();
		/*ERROR_INSUFFICIENT_BUFFER.A supplied buffer size was not large enough, or it was incorrectly set to NULL.
		ERROR_INVALID_FLAGS.The values supplied for flags were not valid.
		ERROR_INVALID_PARAMETER.Any of the parameter values was invalid.
		ERROR_NO_UNICODE_TRANSLATION.Invalid Unicode was found in a string.*/
		return FALSE;
	}
	result->SetCount(utf8Len);
	return TRUE;
}

BOOL TextUtilsEx::Str2CodePage(SimpleString *str, UINT inCodePage, UINT outCodePage, SimpleString *result, SimpleString *workBuff)
{
	int buffLen = str->Count() * 4 + 2;
	workBuff->ClearAndResize(buffLen);
	LPWSTR buff = (LPWSTR)workBuff->Data();  // or  we could call MultiByteToWideChar first to get the required length
	int wlen = MultiByteToWideChar(inCodePage, 0, str->Data(), str->Count(), buff, buffLen);
	if (wlen == 0) {
		result->Clear();
		// error - implement error log file
		const DWORD error = ::GetLastError();
		return FALSE;
	}

	int outLen = wlen * 4 + 2;
	result->ClearAndResize(outLen); // or could  call WideCharToMultiByte first to get the required length
	if (outCodePage == 0)
		outCodePage = CP_UTF8;
	int utf8Len = WideCharToMultiByte(outCodePage, 0, buff, wlen, result->Data(), outLen, NULL, NULL);
	if (utf8Len == 0) {
		result->Clear();
		// error - implement error log file
		const DWORD error = ::GetLastError();
		/*ERROR_INSUFFICIENT_BUFFER.A supplied buffer size was not large enough, or it was incorrectly set to NULL.
		ERROR_INVALID_FLAGS.The values supplied for flags were not valid.
		ERROR_INVALID_PARAMETER.Any of the parameter values was invalid.
		ERROR_NO_UNICODE_TRANSLATION.Invalid Unicode was found in a string.*/
		return FALSE;
	}
	result->SetCount(utf8Len);
	return TRUE;
}

BOOL TextUtilsEx::WStr2CodePage(wchar_t *wbuff, int wlen, UINT outCodePage, CString *result, DWORD &error)
{
	SimpleString outbuf(4 * wlen + 2);

	BOOL ret = WStr2CodePage(wbuff, wlen, outCodePage, &outbuf, error);

	*result = outbuf.Data();
	return ret;
}

BOOL TextUtilsEx::WStr2CodePage(wchar_t *wbuff, int wlen, UINT outCodePage, SimpleString *result, DWORD &error)
{
	error = 0;
	int outLen = wlen * 4 + 2;
	result->ClearAndResize(outLen); // or could  call WideCharToMultiByte first to get the required length
	if (outCodePage == 0)
		outCodePage = CP_UTF8;

	int utf8Len = WideCharToMultiByte(outCodePage, 0, wbuff, wlen, result->Data(), outLen, NULL, NULL);
	if (utf8Len == 0)
	{
		result->Clear();
		// error - implement error log file
		error = ::GetLastError();
		/*ERROR_INSUFFICIENT_BUFFER.A supplied buffer size was not large enough, or it was incorrectly set to NULL.
		ERROR_INVALID_FLAGS.The values supplied for flags were not valid.
		ERROR_INVALID_PARAMETER.Any of the parameter values was invalid.
		ERROR_NO_UNICODE_TRANSLATION.Invalid Unicode was found in a string.*/
		return FALSE;
	}
	result->SetCount(utf8Len);
	return TRUE;
}

BOOL TextUtilsEx::CodePage2WStr(SimpleString *str, UINT strCodePage, SimpleString *wstr, DWORD &error)
{
	error = 0;
	// SimpleString doesn't support UTF16 characters so be carfull
	int wbuffLen = str->Count() * 4 + 2;
	wstr->ClearAndResize(wbuffLen);

	int wlen = MultiByteToWideChar(strCodePage, 0, str->Data(), str->Count(), (LPWSTR)((void*)wstr->Data()), wbuffLen);
	if (wlen == 0) {
		wstr->Clear();
		// error - implement error log file
		error = ::GetLastError();
		return FALSE;
	}
	wstr->SetCount(wlen * 2);
	*wstr->Data(wlen * 2 + 1) = 0;
	*wstr->Data(wlen * 2 + 2) = 0;
	wchar_t *wbuf = (LPWSTR)((void*)wstr->Data());
	return TRUE;
}

BOOL TextUtilsEx::CodePage2WStr(CString *str, UINT strCodePage, SimpleString *wstr, DWORD &error)
{
	error = 0;
	int wbuffLen = str->GetLength() * 4 + 2;
	wstr->ClearAndResize(wbuffLen);

	int wlen = MultiByteToWideChar(strCodePage, 0, str->operator LPCSTR(), str->GetLength(), (LPWSTR)((void*)wstr->Data()), wbuffLen);
	if (wlen == 0) {
		wstr->Clear();
		// error - implement error log file
		error = ::GetLastError();
		return FALSE;
	}
	wstr->SetCount(wlen * 2);
	*wstr->Data(wlen * 2 + 1) = 0;
	*wstr->Data(wlen * 2 + 2) = 0;
	wchar_t *wbuf = (LPWSTR)((void*)wstr->Data());
	return TRUE;
}

BOOL TextUtilsEx::CodePage2WStr(CString *str, UINT strCodePage, CStringW *wstr, DWORD &error)
{
	error = 0;
	int wbuffLen = str->GetLength() * 4 + 2;
	SimpleString ss_wstr(wbuffLen);

	int wlen = MultiByteToWideChar(strCodePage, 0, str->operator LPCSTR(), str->GetLength(), (LPWSTR)((void*)ss_wstr.Data()), wbuffLen);
	if (wlen == 0)
	{
		wstr->Empty();
		// error - implement error log file
		error = ::GetLastError();
		return FALSE;
	}

	ss_wstr.SetCount(wlen * 2);
	ss_wstr.SetAt(wlen * 2 + 1, 0);
	ss_wstr.SetAt(wlen * 2 + 2, 0);

	wchar_t *wbuf = (LPWSTR)((void*)ss_wstr.Data());
	*wstr = wbuf;

	return TRUE;
}

BOOL TextUtilsEx::Str2UTF8(SimpleString *str, UINT strCodePage, SimpleString *result, SimpleString *workBuff)
{
	BOOL ret = Str2CodePage(str, strCodePage, CP_UTF8, result, workBuff);
	return ret;
}

BOOL TextUtilsEx::Str2CurrentCodepage(SimpleString *str, UINT strCodePage, SimpleString *result, SimpleString *workBuff)
{
	UINT currentCodePage = GetACP();
	BOOL ret = Str2CodePage(str, strCodePage, currentCodePage, result, workBuff);
	return ret;
}

#include "MimeCode.h"

CString TextUtilsEx::DecodeString(CString &subj, CString &charset, UINT &charsetId, UINT toCharacterId)
{
	CFieldCodeText tfc;
	tfc.SetInput(subj.GetBuffer(), subj.GetLength(), false);
	int outputLen = tfc.GetOutputLength();
	if (outputLen > 0)
	{
		int maxOutputLen = 2 * outputLen + 2;
		unsigned char *outBuf = (unsigned char*)malloc(maxOutputLen);
		int decodeLen = tfc.GetOutput(outBuf, maxOutputLen);
		outBuf[decodeLen] = 0;
		CString str(outBuf);
		free(outBuf);
		charset = tfc.GetCharset();
		UINT CodePage = TextUtilsEx::Str2PageCode(tfc.GetCharset());
		charsetId = CodePage;
		// No remapping for now. Header of message windows should show text properly
		return str;

		if ((CodePage > 0) && (toCharacterId > 0)) {
			TextUtilsEx::Str2Ansi(str, CodePage);
			return str;
		}
		else
			return subj;
	}
	else
		return subj;
}

int  TextUtilsEx::hextob(char ch)
{
	if (ch >= '0' && ch <= '9') return ch - '0';
	if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
	if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
	return -1;
}

int TextUtilsEx::DecodeMimeChunkedString(CString &inString, CString &charset, UINT &charsetId,  BOOL hasCharset, CString &outString)
{
	const char *p_beg = (LPCSTR)inString;
	const char *p = p_beg;
	int length = inString.GetLength();
	const char *e = p + length;

	charsetId = 0;
	if (hasCharset)
	{
		// find charset
		while ((p < e) && (*p != '\''))
		{
			p++;
		}
		if (p >= e)
		{
			//  TODO: can't decode; return untoched string for now
			return 0;
		}

		charset.Append(p_beg, p - p_beg);
		charsetId = TextUtilsEx::Str2PageCode(charset);

		p++;

		// find language
		while ((p < e) && (*p != '\''))
		{
			p++;
		}

		if (p >= e)
		{
			//  TODO: can't decode; return untoched string for now
			return 0;
		}

		p++;

		// decode

		SimpleString out(length * 2);

		int c;
		while (p < e)
		{
			c = *p;
			if (c == '%')
			{
				p++;
				c = *p++;
				int first = hextob(c);
				if (first < 0)
					return 0;
				c = *p++;
				int second = hextob(c);
				if (second < 0)
					return 0;
				unsigned char uc = first * 16 + second;
				out.Append(uc);
			}
			else
			{
				out.Append(c);
				p++;
			}
		}

		outString.Empty();
		outString.Append(out.Data(), out.Count());
	}

	return 1;
}

CP2NM cp2name[] = {
	{ 37 , "IBM037" , "IBM EBCDIC (US-Canada)" },
	{ 437 , "IBM437" , "OEM United States" },
	{ 500 , "IBM500" , "IBM EBCDIC (International)" },
	{ 708 , "ASMO-708" , "Arabic (ASMO 708)" },
	{ 720 , "DOS-720" , "Arabic (DOS)" },
	{ 737 , "ibm737" , "Greek (DOS)" },
	{ 775 , "ibm775" , "Baltic (DOS)" },
	{ 850 , "ibm850" , "Western European (DOS)" },
	{ 852 , "ibm852" , "Central European (DOS)" },
	{ 855 , "IBM855" , "OEM Cyrillic" },
	{ 857 , "ibm857" , "Turkish (DOS)" },
	{ 858 , "IBM00858" , "OEM Multilingual Latin I" },
	{ 860 , "IBM860" , "Portuguese (DOS)" },
	{ 861 , "ibm861" , "Icelandic (DOS)" },
	{ 862 , "DOS-862" , "Hebrew (DOS)" },
	{ 863 , "IBM863" , "French Canadian (DOS)" },
	{ 864 , "IBM864" , "Arabic (864)" },
	{ 865 , "IBM865" , "Nordic (DOS)" },
	{ 866 , "cp866" , "Cyrillic (DOS)" },
	{ 869 , "ibm869" , "Greek, Modern (DOS)" },
	{ 870 , "IBM870" , "IBM EBCDIC (Multilingual Latin-2)" },
	{ 874 , "windows-874" , "Thai (Windows)" },
	{ 875 , "cp875" , "IBM EBCDIC (Greek Modern)" },
	{ 932 , "shift_jis" , "Japanese (Shift-JIS)" },
	{ 936 , "gb2312" , "Chinese Simplified (GB2312)" },
	{ 949 , "ks_c_5601-1987" , "Korean" },
	{ 950 , "big5" , "Chinese Traditional (Big5)" },
	{ 1026 , "IBM1026" , "IBM EBCDIC (Turkish Latin-5)" },
	{ 1047 , "IBM01047" , "IBM Latin-1" },
	{ 1140 , "IBM01140" , "IBM EBCDIC (US-Canada-Euro)" },
	{ 1141 , "IBM01141" , "IBM EBCDIC (Germany-Euro)" },
	{ 1142 , "IBM01142" , "IBM EBCDIC (Denmark-Norway-Euro)" },
	{ 1143 , "IBM01143" , "IBM EBCDIC (Finland-Sweden-Euro)" },
	{ 1144 , "IBM01144" , "IBM EBCDIC (Italy-Euro)" },
	{ 1145 , "IBM01145" , "IBM EBCDIC (Spain-Euro)" },
	{ 1146 , "IBM01146" , "IBM EBCDIC (UK-Euro)" },
	{ 1147 , "IBM01147" , "IBM EBCDIC (France-Euro)" },
	{ 1148 , "IBM01148" , "IBM EBCDIC (International-Euro)" },
	{ 1149 , "IBM01149" , "IBM EBCDIC (Icelandic-Euro)" },
	{ 1200 , "utf-16" , "Unicode" },
	{ 1201 , "unicodeFFFE" , "Unicode (Big-Endian)" },
	{ 1250 , "windows-1250" , "Central European (Windows)" },
	{ 1251 , "windows-1251" , "Cyrillic (Windows)" },
	{ 1252 , "Windows-1252" , "Western European (Windows)" },
	{ 1253 , "windows-1253" , "Greek (Windows)" },
	{ 1254 , "windows-1254" , "Turkish (Windows)" },
	{ 1255 , "windows-1255" , "Hebrew (Windows)" },
	{ 1256 , "windows-1256" , "Arabic (Windows)" },
	{ 1257 , "windows-1257" , "Baltic (Windows)" },
	{ 1258 , "windows-1258" , "Vietnamese (Windows)" },
	{ 1361 , "Johab" , "Korean (Johab)" },
	{ 10000 , "macintosh" , "Western European (Mac)" },
	{ 10001 , "x-mac-japanese" , "Japanese (Mac)" },
	{ 10002 , "x-mac-chinesetrad" , "Chinese Traditional (Mac)" },
	{ 10003 , "x-mac-korean" , "Korean (Mac)" },
	{ 10004 , "x-mac-arabic" , "Arabic (Mac)" },
	{ 10005 , "x-mac-hebrew" , "Hebrew (Mac)" },
	{ 10006 , "x-mac-greek" , "Greek (Mac)" },
	{ 10007 , "x-mac-cyrillic" , "Cyrillic (Mac)" },
	{ 10008 , "x-mac-chinesesimp" , "Chinese Simplified (Mac)" },
	{ 10010 , "x-mac-romanian" , "Romanian (Mac)" },
	{ 10017 , "x-mac-ukrainian" , "Ukrainian (Mac)" },
	{ 10021 , "x-mac-thai" , "Thai (Mac)" },
	{ 10029 , "x-mac-ce" , "Central European (Mac)" },
	{ 10079 , "x-mac-icelandic" , "Icelandic (Mac)" },
	{ 10081 , "x-mac-turkish" , "Turkish (Mac)" },
	{ 10082 , "x-mac-croatian" , "Croatian (Mac)" },
	{ 12000 , "utf-32" , "Unicode (UTF-32)" },
	{ 12001 , "utf-32BE" , "Unicode (UTF-32 Big-Endian)" },
	{ 20000 , "x-Chinese-CNS" , "Chinese Traditional (CNS)" },
	{ 20001 , "x-cp20001" , "TCA Taiwan" },
	{ 20002 , "x-Chinese-Eten" , "Chinese Traditional (Eten)" },
	{ 20003 , "x-cp20003" , "IBM5550 Taiwan" },
	{ 20004 , "x-cp20004" , "TeleText Taiwan" },
	{ 20005 , "x-cp20005" , "Wang Taiwan" },
	{ 20105 , "x-IA5" , "Western European (IA5)" },
	{ 20106 , "x-IA5-German" , "German (IA5)" },
	{ 20107 , "x-IA5-Swedish" , "Swedish (IA5)" },
	{ 20108 , "x-IA5-Norwegian" , "Norwegian (IA5)" },
	{ 20127 , "us-ascii" , "US-ASCII" },
	{ 20261 , "x-cp20261" , "T.61" },
	{ 20269 , "x-cp20269" , "ISO-6937" },
	{ 20273 , "IBM273" , "IBM EBCDIC (Germany)" },
	{ 20277 , "IBM277" , "IBM EBCDIC (Denmark-Norway)" },
	{ 20278 , "IBM278" , "IBM EBCDIC (Finland-Sweden)" },
	{ 20280 , "IBM280" , "IBM EBCDIC (Italy)" },
	{ 20284 , "IBM284" , "IBM EBCDIC (Spain)" },
	{ 20285 , "IBM285" , "IBM EBCDIC (UK)" },
	{ 20290 , "IBM290" , "IBM EBCDIC (Japanese katakana)" },
	{ 20297 , "IBM297" , "IBM EBCDIC (France)" },
	{ 20420 , "IBM420" , "IBM EBCDIC (Arabic)" },
	{ 20423 , "IBM423" , "IBM EBCDIC (Greek)" },
	{ 20424 , "IBM424" , "IBM EBCDIC (Hebrew)" },
	{ 20833 , "x-EBCDIC-KoreanExtended" , "IBM EBCDIC (Korean Extended)" },
	{ 20838 , "IBM-Thai" , "IBM EBCDIC (Thai)" },
	{ 20866 , "koi8-r" , "Cyrillic (KOI8-R)" },
	{ 20871 , "IBM871" , "IBM EBCDIC (Icelandic)" },
	{ 20880 , "IBM880" , "IBM EBCDIC (Cyrillic Russian)" },
	{ 20905 , "IBM905" , "IBM EBCDIC (Turkish)" },
	{ 20924 , "IBM00924" , "IBM Latin-1" },
	{ 20932 , "EUC-JP" , "Japanese (JIS 0208-1990 and 0212-1990)" },
	{ 20936 , "x-cp20936" , "Chinese Simplified (GB2312-80)" },
	{ 20949 , "x-cp20949" , "Korean Wansung" },
	{ 21025 , "cp1025" , "IBM EBCDIC (Cyrillic Serbian-Bulgarian)" },
	{ 21866 , "koi8-u" , "Cyrillic (KOI8-U)" },
	{ 28591 , "iso-8859-1" , "Western European (ISO)" },
	{ 28592 , "iso-8859-2" , "Central European (ISO)" },
	{ 28593 , "iso-8859-3" , "Latin 3 (ISO)" },
	{ 28594 , "iso-8859-4" , "Baltic (ISO)" },
	{ 28595 , "iso-8859-5" , "Cyrillic (ISO)" },
	{ 28596 , "iso-8859-6" , "Arabic (ISO)" },
	{ 28597 , "iso-8859-7" , "Greek (ISO)" },
	{ 28598 , "iso-8859-8" , "Hebrew (ISO-Visual)" },
	{ 28599 , "iso-8859-9" , "Turkish (ISO)" },
	{ 28603 , "iso-8859-13" , "Estonian (ISO)" },
	{ 28605 , "iso-8859-15" , "Latin 9 (ISO)" },
	{ 29001 , "x-Europa" , "Europa" },
	{ 38598 , "iso-8859-8-i" , "Hebrew (ISO-Logical)" },
	{ 50220 , "iso-2022-jp" , "Japanese (JIS)" },
	{ 50221 , "csISO2022JP" , "Japanese (JIS-Allow 1 byte Kana)" },
	{ 50222 , "iso-2022-jp" , "Japanese (JIS-Allow 1 byte Kana - SO/SI)" },
	{ 50225 , "iso-2022-kr" , "Korean (ISO)" },
	{ 50227 , "x-cp50227" , "Chinese Simplified (ISO-2022)" },
	{ 51932 , "euc-jp" , "Japanese (EUC)" },
	{ 51936 , "EUC-CN" , "Chinese Simplified (EUC)" },
	{ 51949 , "euc-kr" , "Korean (EUC)" },
	{ 52936 , "hz-gb-2312" , "Chinese Simplified (HZ)" },
	{ 54936 , "GB18030" , "Chinese Simplified (GB18030)" },
	{ 57002 , "x-iscii-de" , "ISCII Devanagari" },
	{ 57003 , "x-iscii-be" , "ISCII Bengali" },
	{ 57004 , "x-iscii-ta" , "ISCII Tamil" },
	{ 57005 , "x-iscii-te" , "ISCII Telugu" },
	{ 57006 , "x-iscii-as" , "ISCII Assamese" },
	{ 57007 , "x-iscii-or" , "ISCII Oriya" },
	{ 57008 , "x-iscii-ka" , "ISCII Kannada" },
	{ 57009 , "x-iscii-ma" , "ISCII Malayalam" },
	{ 57010 , "x-iscii-gu" , "ISCII Gujarati" },
	{ 57011 , "x-iscii-pa" , "ISCII Punjabi" },
	{ 65000 , "utf-7" , "Unicode (UTF-7)" },
	{ 65001 , "utf-8" , "Unicode (UTF-8)" },
};

typedef struct {
	char *m_charset;
	char *m_alt_charset;
} Name2AlternateName;

Name2AlternateName name2altname[] = {
	{ "gbk" , "GB18030" },    // { 54936 , "GB18030" , "Chinese Simplified (GB18030)" },
	{ "cp1252" , "Windows-1252" },    // { 1252 , "Windows-1252" , "Western European (Windows)" },
	{ "ascii" , "us-ascii" },    // { 20127 , "us-ascii" , "US-ASCII" },
	{ "cp819" , "iso-8859-1" },    // { 28591 , "iso-8859-1" , "Western European (ISO)" },
	{ "latin1" , "iso-8859-1" },    // { 28591 , "iso-8859-1" , "Western European (ISO)" },
	{ "latin2" , "iso-8859-2" },    // { 28592 , "iso-8859-2" , "Central European (ISO)" },
	{ "big-5" , "big5" },    // { 950 , "big5" , "Chinese Traditional (Big5)" },
	{ "SJIS" , "shift_jis" },    // { 932 , "shift_jis" , "Japanese (Shift-JIS)" },
};

#include <unordered_map>

typedef unordered_map<std::string, unsigned int> myMap;
static myMap *cids = 0;

void TextUtilsEx::delete_charset2Id()
{
	delete cids;
	cids = 0;
}

UINT TextUtilsEx::charset2Id(const char *char_set)
{
	UINT id = 0;
	CP2NM *item;
	myMap::iterator it;

	if (!cids) {
		cids = new myMap;

		int cp2name_size = sizeof(cp2name) / sizeof(CP2NM);
		for (int i = 0; i < cp2name_size; i++)
		{
			item = &cp2name[i];
			std::string charset = item->m_charset;
			std::transform(charset.begin(), charset.end(), charset.begin(), ::tolower);
			if (cids->find(charset) == cids->end()) {  // not found, invalid iterator returned
				cids->insert(myMap::value_type(charset, item->m_charsetId));
			}
		}
	}
#if 0
	for (it = cids->begin(); it != cids->end(); it++) {
		TRACE("%d %s\n", it->second, it->first);
	}
#endif
	std::string charset = char_set;
	std::transform(charset.begin(), charset.end(), charset.begin(), ::tolower);
	if ((it = cids->find(charset)) != cids->end()) {
		id = it->second;
	}
	else
	{
		int i;
		int nsize = sizeof(name2altname) / sizeof(Name2AlternateName);
		for (i = 0; i < nsize; i++)
		{
			if (strcmp(charset.c_str(), name2altname[i].m_charset) == 0)
			{
				std::string alt_charset = name2altname[i].m_alt_charset;
				std::transform(alt_charset.begin(), alt_charset.end(), alt_charset.begin(), ::tolower);
				if ((it = cids->find(alt_charset)) != cids->end())
					id = it->second;
			}
		}
	}
	return id;
}

typedef unordered_map<unsigned int, std::string> myIdMap;
static myIdMap *ids = 0;

void TextUtilsEx::delete_id2charset()
{
	delete ids;
	ids = 0;
}

BOOL TextUtilsEx::id2charset(UINT id, std::string &charset)
{
	CP2NM *item;
	myIdMap::iterator it;

	if (!ids) {
		ids = new myIdMap;

		int cp2name_size = sizeof(cp2name) / sizeof(CP2NM);
		for (int i = 0; i < cp2name_size; i++)
		{
			item = &cp2name[i];
			if (ids->find(item->m_charsetId) == ids->end()) {  // not found, invalid iterator returned
				ids->insert(myIdMap::value_type(item->m_charsetId, item->m_charset));
			}
		}
	}
#if 0
	for (it = ids->begin(); it != ids->end(); it++) {
		TRACE("%d %s\n", it->first, it->second);
	}
#endif
	if ((it = ids->find(id)) != ids->end()) {
		charset = it->second;
		return TRUE;
	}
	return FALSE;
}

int TextUtilsEx::showCodePageTable(CString &path)
{
	CString codePageIdsFile = "WindowsCodePageIds.htm";
	CString fullPath = path + "\\" + codePageIdsFile;

	CFile fp;
	if (!fp.Open(fullPath, CFile::modeWrite | CFile::modeCreate)) {
		CString txt = _T("Could not create \"") + fullPath;
		txt += _T("\" file.\nMake sure file is not open on other applications.");
		HWND h = NULL; // we don't have any window yet
		int answer = ::MessageBox(h, txt, _T("Error"), MB_APPLMODAL | MB_ICONERROR | MB_OK);
		return -1;
	}

	CString htmlHdr;

	htmlHdr += "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=US-ASCII\">";
	htmlHdr += "

WINDOWS CODE PAGE IDENTIFIERS TABLE

";
	htmlHdr += "For additional information check the Microsoft document Code Page Identifiers";
	htmlHdr += "

\r\n";

	fp.Write((LPCSTR)htmlHdr, htmlHdr.GetLength());

	CP2NM *item;

	CString txt;
	txt.Format(" %22s  %26s  %42s  \n",
		" ", " ", " ");
	fp.Write((LPCSTR)txt, txt.GetLength());

	txt.Empty();
	txt.Format("|%22s |%26s |%42s |\n",
		"Code Page Identifiers", "Name", "Additional Information");
	fp.Write((LPCSTR)txt, txt.GetLength());

	int cp2name_size = sizeof(cp2name) / sizeof(CP2NM);
	for (int i = 0; i < cp2name_size; i++)
	{
		item = &cp2name[i];
		txt.Empty();
		txt.Format("|%22d |%26s |%42s |\n",
			item->m_charsetId, item->m_charset, item->m_info);
		fp.Write((LPCSTR)txt, txt.GetLength());
	}

	CString htmlEnd = "\r\n
";
	fp.Write((LPCSTR)htmlEnd, htmlEnd.GetLength());

	fp.Close();

	ShellExecute(NULL, _T("open"), fullPath, NULL, NULL, SW_SHOWNORMAL);

	int deb = 1;
	return 1;
}

BOOL TextUtilsEx::isNumeric(CString &str) {
	int i = 0;
	if (str[i] == '-')
		i++;
	if (i == str.GetLength())
		return FALSE;
	for (; i < str.GetLength(); i++) {
		if (!_istdigit(str[i])) {
			return FALSE;
		}
	}
	return TRUE;
}

BOOL TextUtilsEx::Str2Wide(CString &res, UINT CodePage, CStringW &m_strW)
{
	int len = res.GetLength() * 4 + 2;
	LPWSTR buff = (LPWSTR)malloc(len);  // or  we could call MultiByteToWideChar first to get the required length
	int len1 = MultiByteToWideChar(CodePage, 0, res, res.GetLength(), buff, len);
	if (len1 == 0) {
		free(buff);
		// error - implement error log file
		const DWORD error = ::GetLastError();
		return FALSE;
	}
	buff[len1] = 0;
	m_strW = buff;
	free(buff);
	return TRUE;
}

char *TextUtilsEx::strchar(char *beg, char *end, char c)
{
	while ((beg < end) && (*beg != c)) { beg++; };
	if (beg < end)
		return beg;
	else
		return 0;
}

char *TextUtilsEx::findOneOf(char *beg, char *end, char *charList)
{
	char oneOf;
	char *p_charList;
	char c;
	while (beg < end)
	{
		p_charList = charList;
		c = *beg;
		while (oneOf = *p_charList)
		{
			if (c == oneOf)
				return beg;
			else
				p_charList++;
		}
		beg++;
	};
	return 0;
}

char *TextUtilsEx::strnstrUpper2Lower(char *any, char *end, const char *lower, int lowerlength)
{
	// TODO: not very efficient; optimize
	char *p;
	for (p = any; p <= (end - lowerlength); p++)
	{
		if (strncmpUpper2Lower(p, end, lower, lowerlength) == 0)
			return p;
	}
	return 0;
}

int TextUtilsEx::strncmpUpper2Lower(char *any, char *end, const char *lower, int lowerlength) {
	// any can be multi line
	// lower is fixed length
	while ((any < end) && (lowerlength > 0) && (*lower++ == tolower(*any++))) { lowerlength--; }
	return lowerlength;
}

int TextUtilsEx::strncmpUpper2Lower(char *any, int anyLength, const char *lower, int lowerlength) {
	// any can be multi line
	// lower is fixed length
	if (anyLength >= lowerlength) {
		while ((lowerlength > 0) && (*lower++ == tolower(*any++))) { lowerlength--; }
	}
	return lowerlength;
}

int TextUtilsEx::strncmpExact(char *any, char *end, const char *lower, int lowerlength) {
	while ((lowerlength > 0) && (any < end) && (*lower++ == *any++)) { lowerlength--; }
	return lowerlength;
}

int TextUtilsEx::findNoCase(const char *input, int count, void const* Str, int  Size)
{
	int i;
	register char *p = (char*)input;
	register int delta_count = count - Size;
	for (i = 0; i < delta_count; i++, p++)
	{
		if (strncmpUpper2Lower(p, (count - i), (char*)Str, Size) == 0)
			return i;
	}
	return -1;
}

char *TextUtilsEx::findNoCaseP(const char *input, int count, void const* Str, int  Size)
{
	int i;
	register char *p = (char*)input;
	register int delta_count = count - Size;
	for (i = 0; i < delta_count; i++, p++)
	{
		if (strncmpUpper2Lower(p, (count - i), (char*)Str, Size) == 0)
			return p;
	}
	return 0;
}

int TextUtilsEx::DecodeURL(char *URL, int urlLen)
{
	int retLen = urlLen;
#if 0
#endif
	int outLen = 0;
	int escapedCharLen = 0;
	char *p_out = URL;
	char *p_beg = URL;
	char *p = URL;
	int i = 0;
	for (i = 0; i < urlLen; i++)
	{
		escapedCharLen = 0;
		p = &URL[i];
		if (*p == '&')
		{
			char *val = p + 1;
			if (strncmp(val, "amp;", 4) == 0) {
				*p = '&';
				escapedCharLen = 4;
			}
			else if (strncmp(val, "lt;", 3) == 0) {
				*p = '<';
				escapedCharLen = 3;
			}
			else if (strncmp(val, "gt;", 3) == 0) {
				*p = '>';
				escapedCharLen = 3;
			}
			else if (strncmp(val, "quote;", 6) == 0) {
				*p = '"';
				escapedCharLen = 6;
			}
			else if (strncmp(val, "apos;", 5) == 0) {
				*p = '\'';
				escapedCharLen = 5;
			}

			if (escapedCharLen > 0)
			{
				int blockLen = p - p_beg + 1;
				memcpy(p_out, p_beg, blockLen);
				p_out += blockLen;
				outLen += blockLen;
				p += escapedCharLen + 1;
				p_beg = p;
			}
		}
	}

	if (p != p_beg)
	{
		int blockLen = p - p_beg + 1;
		memcpy(p_out, p_beg, blockLen);
		outLen += blockLen;
		p_out += blockLen;

	}

	retLen = p_out - URL;
	if (outLen != retLen)
		int deb = 1;

	return retLen;
}

BOOL TextUtilsEx::isWhiteLine(CString &str)
{
	const char* p = (LPCSTR)str;
	const char* e = p + str.GetLength();
	return isWhiteLine(p, e);
}

BOOL TextUtilsEx::isWhiteLine(const char* p, const char* e)
{
	while ((p < e) && ((*p == '\r') || (*p == '\n') || (*p == ' ') || (*p == '\t')))  // eat white
		p++;
	if (p == e)
		return TRUE;
	else
		return FALSE;
}

// Very inefficient
void TextUtilsEx::SplitString(const CString &strIn, const CString &delim, CStringArray &a)
{
	int position = 0;
	CString strToken;

	a.RemoveAll();
	strToken = strIn.Tokenize(delim, position);
	while (!strToken.IsEmpty()) {
		a.Add(strToken);
		strToken = strIn.Tokenize(delim, position);
	}
}

// Simple tokenizer, heavy
// Will support: a bc d, xy z, "k l, n,'"'","a b", "a,b"

int TextUtilsEx::Tokenize(CString str, CStringArray &a, char del)
{
	enum { inHunt, notInsideDoubleQuotes, insideDoubleQuotes};

	int state = inHunt;
	CString token;

	int strLength = str.GetLength();
	for (int i = 0; i < strLength; i++)
	{
		char c = str.GetAt(i);
		if (state == inHunt)
		{
			if ((c == ' ') || (c == '\t'))
				continue;
			else if (c == '\'')
			{
				int bytesLeft = strLength - i;
				if ((bytesLeft >= 3) && (str.GetAt(i + 1) == '\"') && (str.GetAt(i + 2) == '\''))
				{
					token.AppendChar('"');
					i += 2;
				}
				else
					token.AppendChar(c);
				state = notInsideDoubleQuotes;
				continue;
			}
			else if (c == del)
				state = notInsideDoubleQuotes;
			else if (c == '"')
				state = insideDoubleQuotes;
			else if ((c != del) && (c != '"'))
			{
				state = notInsideDoubleQuotes;
				token.AppendChar(c);
			}
			continue;
		}
		if (state == notInsideDoubleQuotes)
		{
			if (c == '\'')
			{
				int bytesLeft = strLength - i;
				if ((bytesLeft >= 3) && (str.GetAt(i + 1) == '\"') && (str.GetAt(i + 2) == '\''))
				{
					token.AppendChar('"');
					i += 2;
				}
				else
					token.AppendChar(c);
				continue;
			}
			else if (c != del)
			{
				token.AppendChar(c);
			}
			else
			{
				token.Trim(" \t");
				a.Add(token);
				token.Empty();
				state = inHunt;
			}
			continue;
		}
		else  // state == insideDoubleQuotes
		{
			if (c == '\'')
			{
				int bytesLeft = strLength - i;
				if ((bytesLeft >= 3) && (str.GetAt(i + 1) == '\"') && (str.GetAt(i + 2) == '\''))
				{
					token.AppendChar('"');
					i += 2;
				}
				else
					token.AppendChar(c);
				continue;
			}
			else if (c != '"')
			{
				token.AppendChar(c);
			}
			else
			{
				token.Trim(" \t");
				a.Add(token);
				token.Empty();
				state = inHunt;
			}
		}
	}

	token.Trim(" \t");
	if (!token.IsEmpty())
		a.Add(token);
	return 1;
}

void TextUtilsEx::TraceStringArray(CStringArray &a)
{
	CString el;
	int first = 0;
	TRACE(_T("StringArray:\n"));
	for (int i = 0; i < a.GetSize(); i++)
	{
		el = a.ElementAt(i);
		TRACE(_T("\t|%s|\n"), el);
	}
}


/*

*/

#include "adjusttimesdialog.h"
#include "widgets/timeedit.h"

#include <QLabel>
#include <QGroupBox>
#include <QGridLayout>

#include <KLocalizedString>

using namespace SubtitleComposer;

AdjustTimesDialog::AdjustTimesDialog(QWidget *parent) :
	ActionDialog(i18n("Adjust"), parent)
{
	QGroupBox *settingsGroupBox = createGroupBox(i18nc("@title:group", "New Times"));

	m_firstLineTimeEdit = new TimeEdit(settingsGroupBox);

	QLabel *firstLineLabel = new QLabel(settingsGroupBox);
	firstLineLabel->setText(i18n("First spoken line:"));
	firstLineLabel->setBuddy(m_firstLineTimeEdit);

	m_lastLineTimeEdit = new TimeEdit(settingsGroupBox);

	QLabel *lastLineLabel = new QLabel(settingsGroupBox);
	lastLineLabel->setText(i18n("Last spoken line:"));
	lastLineLabel->setBuddy(m_lastLineTimeEdit);

	QGridLayout *settingsLayout = createLayout(settingsGroupBox);
	settingsLayout->addWidget(firstLineLabel, 0, 0, Qt::AlignRight | Qt::AlignVCenter);
	settingsLayout->addWidget(m_firstLineTimeEdit, 0, 1);
	settingsLayout->addWidget(lastLineLabel, 1, 0, Qt::AlignRight | Qt::AlignVCenter);
	settingsLayout->addWidget(m_lastLineTimeEdit, 1, 1);
}

Time
AdjustTimesDialog::firstLineTime() const
{
	return Time(m_firstLineTimeEdit->value());
}

void
AdjustTimesDialog::setFirstLineTime(const Time &time)
{
	m_firstLineTimeEdit->setValue(time.toMillis());
}

{
	return Time(m_lastLineTimeEdit->value());
}

void
AdjustTimesDialog::setLastLineTime(const Time &time)
{
	m_lastLineTimeEdit->setValue(time.toMillis());
}


{
	return Time(m_firstLineTimeEdit->value());
}

/*

*/

#include "adjusttimesdialog.h"
#include "widgets/timeedit.h"

#include <QLabel>
#include <QGroupBox>
#include <QGridLayout>

#include <KLocalizedString>

using namespace SubtitleComposer;

AdjustTimesDialog::AdjustTimesDialog(QWidget *parent) :
	ActionDialog(i18n("Adjust"), parent)
{
	QGroupBox *settingsGroupBox = createGroupBox(i18nc("@title:group", "New Times"));

	m_firstLineTimeEdit = new TimeEdit(settingsGroupBox);

	QLabel *firstLineLabel = new QLabel(settingsGroupBox);
	firstLineLabel->setText(i18n("First spoken line:"));
	firstLineLabel->setBuddy(m_firstLineTimeEdit);

	m_lastLineTimeEdit = new TimeEdit(settingsGroupBox);

	QLabel *lastLineLabel = new QLabel(settingsGroupBox);
	lastLineLabel->setText(i18n("Last spoken line:"));
	lastLineLabel->setBuddy(m_lastLineTimeEdit);

	QGridLayout *settingsLayout = createLayout(settingsGroupBox);
	settingsLayout->addWidget(firstLineLabel, 0, 0, Qt::AlignRight | Qt::AlignVCenter);
	settingsLayout->addWidget(m_firstLineTimeEdit, 0, 1);
	settingsLayout->addWidget(lastLineLabel, 1, 0, Qt::AlignRight | Qt::AlignVCenter);
	settingsLayout->addWidget(m_lastLineTimeEdit, 1, 1);
}

Time
AdjustTimesDialog::firstLineTime() const

void
AdjustTimesDialog::setFirstLineTime(const Time &time)
{
	m_firstLineTimeEdit->setValue(time.toMillis());
}

Time
AdjustTimesDialog::lastLineTime() const
{
	return Time(m_lastLineTimeEdit->value());
}

void
AdjustTimesDialog::setLastLineTime(const Time &time)
{
	m_lastLineTimeEdit->setValue(time.toMillis());
}


/*

*/

#include "adjusttimesdialog.h"
#include "widgets/timeedit.h"

#include <QLabel>
#include <QGroupBox>
#include <QGridLayout>

#include <KLocalizedString>

using namespace SubtitleComposer;

AdjustTimesDialog::AdjustTimesDialog(QWidget *parent) :
	ActionDialog(i18n("Adjust"), parent)
{
	QGroupBox *settingsGroupBox = createGroupBox(i18nc("@title:group", "New Times"));

	m_firstLineTimeEdit = new TimeEdit(settingsGroupBox);

	QLabel *firstLineLabel = new QLabel(settingsGroupBox);
	firstLineLabel->setText(i18n("First spoken line:"));
	firstLineLabel->setBuddy(m_firstLineTimeEdit);

	m_lastLineTimeEdit = new TimeEdit(settingsGroupBox);

	QLabel *lastLineLabel = new QLabel(settingsGroupBox);
	lastLineLabel->setText(i18n("Last spoken line:"));
	lastLineLabel->setBuddy(m_lastLineTimeEdit);

	QGridLayout *settingsLayout = createLayout(settingsGroupBox);
	settingsLayout->addWidget(firstLineLabel, 0, 0, Qt::AlignRight | Qt::AlignVCenter);
	settingsLayout->addWidget(m_firstLineTimeEdit, 0, 1);
	settingsLayout->addWidget(lastLineLabel, 1, 0, Qt::AlignRight | Qt::AlignVCenter);
	settingsLayout->addWidget(m_lastLineTimeEdit, 1, 1);
}

Time
AdjustTimesDialog::firstLineTime() const
{
	return Time(m_firstLineTimeEdit->value());
}

void
AdjustTimesDialog::setFirstLineTime(const Time &time)
{
	m_firstLineTimeEdit->setValue

}

Time
AdjustTimesDialog::lastLineTime() const
{
	return Time(m_lastLineTimeEdit->value());
}

void
AdjustTimesDialog::setLastLineTime(const Time &time)
{
	m_lastLineTimeEdit->setValue(time.toMillis());
}


/*=============================================================================

=============================================================================*/
#ifndef SPROUT_LIBS_ALGORITHM_TEST_SET_UNION_CPP
#define SPROUT_LIBS_ALGORITHM_TEST_SET_UNION_CPP

#include <sprout/algorithm/set_union.hpp>
#include <sprout/array.hpp>
#include <sprout/sub_array.hpp>
#include <sprout/container.hpp>
#include <testspr/tools.hpp>

namespace testspr {

{
		using namespace sprout;
		{
			SPROUT_STATIC_CONSTEXPR auto arr1 = array<int, 5>{{5, 10, 15, 20, 25}};
			SPROUT_STATIC_CONSTEXPR auto arr1_2 = array<int, 5>{{10, 20, 30, 40, 50}};
			SPROUT_STATIC_CONSTEXPR auto arr2 = array<int, 10>{{}};
			SPROUT_STATIC_CONSTEXPR auto arr3 = array<int, 4>{{}};

			// union
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr2
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 10>{{5, 10, 15, 20, 25, 30, 40, 50, 0, 0}}
					));
			}
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::fit::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr2
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 8>{{5, 10, 15, 20, 25, 30, 40, 50}}
					));
			}
			// union
			// overrun from output range
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr3
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 4>{{5, 10, 15, 20}}
					));
			}
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::fit::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr3
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 4>{{5, 10, 15, 20}}
					));
			}
			// union
			// to sub range
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					sprout::sub(arr2, 2, 8)
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 6>{{5, 10, 15, 20, 25, 30}}
					));
				TESTSPR_BOTH_ASSERT(testspr::equal(
					sprout::get_internal(merged),
					array<int, 10>{{0, 0, 5, 10, 15, 20, 25, 30, 0, 0}}
					));
			}
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::fit::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					sprout::sub(arr2, 2, 8)
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 6>{{5, 10, 15, 20, 25, 30}}
					));
				TESTSPR_BOTH_ASSERT(testspr::equal(
					sprout::get_internal(merged),
					array<int, 10>{{0, 0, 5, 10, 15, 20, 25, 30, 0, 0}}
					));
			}
		}
		{
			SPROUT_STATIC_CONSTEXPR auto arr1 = array<int, 5>{{5, 10, 15, 20, 25}};
			SPROUT_STATIC_CONSTEXPR auto arr1_2 = array<int, 5>{{10, 20, 30, 40, 50}};
			SPROUT_STATIC_CONSTEXPR auto arr2 = array<int, 10>{{}};
			SPROUT_STATIC_CONSTEXPR auto arr3 = array<int, 4>{{}};

			// union
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr2,
					testspr::less<int>()
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 10>{{5, 10, 15, 20, 25, 30, 40, 50, 0, 0}}
					));
			}
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::fit::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr2,
					testspr::less<int>()
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 8>{{5, 10, 15, 20, 25, 30, 40, 50}}
					));
			}
			// union
			// overrun from output range
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr3,
					testspr::less<int>()
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 4>{{5, 10, 15, 20}}
					));
			}
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::fit::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr3,
					testspr::less<int>()
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 4>{{5, 10, 15, 20}}
					));
			}
			// union
			// to sub range
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					sprout::sub(arr2, 2, 8),
					testspr::less<int>()
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 6>{{5, 10, 15, 20, 25, 30}}
					));
				TESTSPR_BOTH_ASSERT(testspr::equal(
					sprout::get_internal(merged),
					array<int, 10>{{0, 0, 5, 10, 15, 20, 25, 30, 0, 0}}
					));
			}
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::fit::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					sprout::sub(arr2, 2, 8),
					testspr::less<int>()
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 6>{{5, 10, 15, 20, 25, 30}}
					));
				TESTSPR_BOTH_ASSERT(testspr::equal(
					sprout::get_internal(merged),
					array<int, 10>{{0, 0, 5, 10, 15, 20, 25, 30, 0, 0}}
					));
			}
		}
	}
}	// namespace testspr

#ifndef TESTSPR_CPP_INCLUDE
#	define TESTSPR_TEST_FUNCTION testspr::algorithm_set_union_test
#	include <testspr/include_main.hpp>
#endif

#endif	// #ifndef SPROUT_LIBS_ALGORITHM_TEST_SET_UNION_CPP


/*=============================================================================

=============================================================================*/
#ifndef SPROUT_LIBS_ALGORITHM_TEST_SET_UNION_CPP
#define SPROUT_LIBS_ALGORITHM_TEST_SET_UNION_CPP

#include <sprout/algorithm/set_union.hpp>
#include <sprout/array.hpp>
#include <sprout/sub_array.hpp>
#include <sprout/container.hpp>
#include <testspr/tools.hpp>

namespace testspr {
	static void algorithm_set_union_test() {
		using namespace spr

		{
			SPROUT_STATIC_CONSTEXPR auto arr1 = array<int, 5>{{5, 10, 15, 20, 25}};
			SPROUT_STATIC_CONSTEXPR auto arr1_2 = array<int, 5>{{10, 20, 30, 40, 50}};
			SPROUT_STATIC_CONSTEXPR auto arr2 = array<int, 10>{{}};
			SPROUT_STATIC_CONSTEXPR auto arr3 = array<int, 4>{{}};

			// union
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr2
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 10>{{5, 10, 15, 20, 25, 30, 40, 50, 0, 0}}
					));
			}
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::fit::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr2
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 8>{{5, 10, 15, 20, 25, 30, 40, 50}}
					));
			}
			// union
			// overrun from output range
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr3
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 4>{{5, 10, 15, 20}}
					));
			}
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::fit::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr3
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 4>{{5, 10, 15, 20}}
					));
			}
			// union
			// to sub range
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					sprout::sub(arr2, 2, 8)
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 6>{{5, 10, 15, 20, 25, 30}}
					));
				TESTSPR_BOTH_ASSERT(testspr::equal(
					sprout::get_internal(merged),
					array<int, 10>{{0, 0, 5, 10, 15, 20, 25, 30, 0, 0}}
					));
			}
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::fit::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					sprout::sub(arr2, 2, 8)
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 6>{{5, 10, 15, 20, 25, 30}}
					));
				TESTSPR_BOTH_ASSERT(testspr::equal(
					sprout::get_internal(merged),
					array<int, 10>{{0, 0, 5, 10, 15, 20, 25, 30, 0, 0}}
					));
			}
		}
		{
			SPROUT_STATIC_CONSTEXPR auto arr1 = array<int, 5>{{5, 10, 15, 20, 25}};
			SPROUT_STATIC_CONSTEXPR auto arr1_2 = array<int, 5>{{10, 20, 30, 40, 50}};
			SPROUT_STATIC_CONSTEXPR auto arr2 = array<int, 10>{{}};
			SPROUT_STATIC_CONSTEXPR auto arr3 = array<int, 4>{{}};

			// union
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr2,
					testspr::less<int>()
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 10>{{5, 10, 15, 20, 25, 30, 40, 50, 0, 0}}
					));
			}
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::fit::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr2,
					testspr::less<int>()
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 8>{{5, 10, 15, 20, 25, 30, 40, 50}}
					));
			}
			// union
			// overrun from output range
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr3,
					testspr::less<int>()
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 4>{{5, 10, 15, 20}}
					));
			}
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::fit::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					arr3,
					testspr::less<int>()
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 4>{{5, 10, 15, 20}}
					));
			}
			// union
			// to sub range
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					sprout::sub(arr2, 2, 8),
					testspr::less<int>()
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 6>{{5, 10, 15, 20, 25, 30}}
					));
				TESTSPR_BOTH_ASSERT(testspr::equal(
					sprout::get_internal(merged),
					array<int, 10>{{0, 0, 5, 10, 15, 20, 25, 30, 0, 0}}
					));
			}
			{
				SPROUT_STATIC_CONSTEXPR auto merged = sprout::fit::set_union(
					sprout::begin(arr1),
					sprout::end(arr1),
					sprout::begin(arr1_2),
					sprout::end(arr1_2),
					sprout::sub(arr2, 2, 8),
					testspr::less<int>()
					);
				TESTSPR_BOTH_ASSERT(testspr::equal(
					merged,
					array<int, 6>{{5, 10, 15, 20, 25, 30}}
					));
				TESTSPR_BOTH_ASSERT(testspr::equal(
					sprout::get_internal(merged),
					array<int, 10>{{0, 0, 5, 10, 15, 20, 25, 30, 0, 0}}
					));
			}
		}
	}
}	// namespace testspr

#ifndef TESTSPR_CPP_INCLUDE
#	define TESTSPR_TEST_FUNCTION testspr::algorithm_set_union_test
#	include <testspr/include_main.hpp>
#endif

#endif	// #ifndef SPROUT_LIBS_ALGORITHM_TEST_SET_UNION_CPP


void partition3(std::vector<T> *arr, int32_t low, int32_t high, int32_t *i,
                int32_t *j)

/**
 * @file
 * @brief Implementation Details
 * @details Quick sort 3 works on Dutch National Flag Algorithm
 * The major difference between simple quicksort and quick sort 3 comes in the
 * function partition3 In quick_sort_partition3 we divide the vector/array into
 * 3 parts. quick sort 3 works faster in some cases as compared to simple
 * quicksort.
 * @author immortal-j
 * @author [Krishna Vedala](https://github/kvedala)
 */
#include <algorithm>
#include <cassert>
#include <ctime>
#include <iostream>
#include <vector>

namespace {
/**
 * Operator to print the array.
 * @param out std::ostream object to write to
 * @param arr array to write
 */
template <typename T>
std::ostream &operator<<(std::ostream &out, const std::vector<T> &arr) {
    for (size_t i = 0; i < arr.size(); ++i) {
        out << arr[i];
        if (i < arr.size() - 1) {
            out << ", ";
        }
    }
    return out;
}

}  // namespace

/**
 * @namespace sorting
 * @brief Sorting Algorithms
 */
namespace sorting {
namespace {  // using un-named namespace here to prevent partition function
             // being visible to end-users
/** This function partitions `arr[]` in three parts
 * 1. \f$arr[l\ldots i]\f$ contains all elements smaller than pivot
 * 2. \f$arr[(i+1)\ldots (j-1)]\f$ contains all occurrences of pivot
 * 3. \f$arr[j\ldots r]\f$ contains all elements greater than pivot
 * @tparam T type of data in the vector array
 * @param [in,out] arr vector array being partitioned
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 * @param [out] i updated lower limit of partition
 * @param [out] j updated upper limit of partition
 */
template <typename T>

{
    // To handle 2 elements
    if (high - low <= 1) {
        if ((*arr)[high] < (*arr)[low]) {
            std::swap((*arr)[high], (*arr)[low]);
        }
        *i = low;
        *j = high;
        return;
    }

    int32_t mid = low;
    T pivot = (*arr)[high];
    while (mid <= high) {
        if ((*arr)[mid] < pivot) {
            std::swap((*arr)[low++], (*arr)[mid++]);
        } else if ((*arr)[mid] == pivot) {
            mid++;
        } else if ((*arr)[mid] > pivot) {
            std::swap((*arr)[mid], (*arr)[high--]);
        }
    }

    // update i and j
    *i = low - 1;
    *j = mid;  // or high-1
}
}  // namespace

/** 3-way partition based quick sort. This function accepts array pointer and
 * modified the input array.
 * @tparam T type of data in the vector array
 * @param [in,out] arr vector array to sort
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 */
template <typename T>
void quicksort(std::vector<T> *arr, int32_t low, int32_t high) {
    if (low >= high) {  // 1 or 0 elements
        return;
    }

    int32_t i = 0, j = 0;

    // i and j are passed as reference
    partition3(arr, low, high, &i, &j);

    // Recur two halves
    quicksort(arr, low, i);
    quicksort(arr, j, high);
}

/** 3-way partition based quick sort. This function accepts array by value and
 * creates a copy of it. The array copy gets sorted and returned by the
 * function.
 * @tparam T type of data in the vector array
 * @param [in] arr vector array to sort
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 * @returns sorted array vector
 */
template <typename T>
std::vector<T> quicksort(std::vector<T> arr, int32_t low, int32_t high) {
    if (low >= high) {  // 1 or 0 elements
        return arr;
    }

    int32_t i = 0, j = 0;

    // i and j are passed as reference
    partition3(&arr, low, high, &i, &j);

    // Recur two halves
    quicksort(&arr, low, i);
    quicksort(&arr, j, high);

    return arr;
}
}  // namespace sorting

/** Test function for integer type arrays */
static void test_int() {
    std::cout << "\nTesting integer type arrays\n";

    for (int num_tests = 1; num_tests < 21; num_tests++) {
        size_t size = std::rand() % 500;
        std::vector<int> arr(size);
        for (auto &a : arr) {
            a = std::rand() % 500 - 250;  // random numbers between -250, 249
        }

        std::cout << "Test " << num_tests << "\t Array size:" << size << "\t ";
        std::vector<int> sorted = sorting::quicksort(arr, 0, size - 1);
        if (size < 20) {
            std::cout << "\t Sorted Array is:\n\t";
            std::cout << sorted << "\n";
        }
        assert(std::is_sorted(std::begin(sorted), std::end(sorted)));
        std::cout << "\t Passed\n";
    }
}

/** Test function for double type arrays */
static void test_double() {
    std::cout << "\nTesting Double type arrays\n";
    for (int num_tests = 1; num_tests < 21; num_tests++) {
        size_t size = std::rand() % 500;
        std::vector<double> arr(size);
        for (auto &a : arr) {
            a = double(std::rand() % 500) -
                250.f;   // random numbers between -250, 249
            a /= 100.f;  // convert to -2.5 to 2.49
        }

        std::cout << "Test " << num_tests << "\t Array size:" << size << "\t ";
        std::vector<double> sorted = sorting::quicksort(arr, 0, size - 1);
        if (size < 20) {
            std::cout << "\t Sorted Array is:\n\t";
            std::cout << sorted << "\n";
        }
        assert(std::is_sorted(std::begin(sorted), std::end(sorted)));
        std::cout << "\t Passed\n";
    }
}

/** Driver program for above functions */
int main() {
    std::srand(std::time(nullptr));
    test_int();
    test_double();
    return 0;
}


{
    std::srand(std::time(nullptr));
    test_int();
    test_double();
    return 0;
}

/**
 * @file
 * @brief Implementation Details
 * @details Quick sort 3 works on Dutch National Flag Algorithm
 * The major difference between simple quicksort and quick sort 3 comes in the
 * function partition3 In quick_sort_partition3 we divide the vector/array into
 * 3 parts. quick sort 3 works faster in some cases as compared to simple
 * quicksort.
 * @author immortal-j
 * @author [Krishna Vedala](https://github/kvedala)
 */
#include <algorithm>
#include <cassert>
#include <ctime>
#include <iostream>
#include <vector>

namespace {
/**
 * Operator to print the array.
 * @param out std::ostream object to write to
 * @param arr array to write
 */
template <typename T>
std::ostream &operator<<(std::ostream &out, const std::vector<T> &arr) {
    for (size_t i = 0; i < arr.size(); ++i) {
        out << arr[i];
        if (i < arr.size() - 1) {
            out << ", ";
        }
    }
    return out;
}

}  // namespace

/**
 * @namespace sorting
 * @brief Sorting Algorithms
 */
namespace sorting {
namespace {  // using un-named namespace here to prevent partition function
             // being visible to end-users
/** This function partitions `arr[]` in three parts
 * 1. \f$arr[l\ldots i]\f$ contains all elements smaller than pivot
 * 2. \f$arr[(i+1)\ldots (j-1)]\f$ contains all occurrences of pivot
 * 3. \f$arr[j\ldots r]\f$ contains all elements greater than pivot
 * @tparam T type of data in the vector array
 * @param [in,out] arr vector array being partitioned
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 * @param [out] i updated lower limit of partition
 * @param [out] j updated upper limit of partition
 */
template <typename T>
void partition3(std::vector<T> *arr, int32_t low, int32_t high, int32_t *i,
                int32_t *j) {
    // To handle 2 elements
    if (high - low <= 1) {
        if ((*arr)[high] < (*arr)[low]) {
            std::swap((*arr)[high], (*arr)[low]);
        }
        *i = low;
        *j = high;
        return;
    }

    int32_t mid = low;
    T pivot = (*arr)[high];
    while (mid <= high) {
        if ((*arr)[mid] < pivot) {
            std::swap((*arr)[low++], (*arr)[mid++]);
        } else if ((*arr)[mid] == pivot) {
            mid++;
        } else if ((*arr)[mid] > pivot) {
            std::swap((*arr)[mid], (*arr)[high--]);
        }
    }

    // update i and j
    *i = low - 1;
    *j = mid;  // or high-1
}
}  // namespace

/** 3-way partition based quick sort. This function accepts array pointer and
 * modified the input array.
 * @tparam T type of data in the vector array
 * @param [in,out] arr vector array to sort
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 */
template <typename T>
void quicksort(std::vector<T> *arr, int32_t low, int32_t high) {
    if (low >= high) {  // 1 or 0 elements
        return;
    }

    int32_t i = 0, j = 0;

    // i and j are passed as reference
    partition3(arr, low, high, &i, &j);

    // Recur two halves
    quicksort(arr, low, i);
    quicksort(arr, j, high);
}

/** 3-way partition based quick sort. This function accepts array by value and
 * creates a copy of it. The array copy gets sorted and returned by the
 * function.
 * @tparam T type of data in the vector array
 * @param [in] arr vector array to sort
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 * @returns sorted array vector
 */
template <typename T>
std::vector<T> quicksort(std::vector<T> arr, int32_t low, int32_t high) {
    if (low >= high) {  // 1 or 0 elements
        return arr;
    }

    int32_t i = 0, j = 0;

    // i and j are passed as reference
    partition3(&arr, low, high, &i, &j);

    // Recur two halves
    quicksort(&arr, low, i);
    quicksort(&arr, j, high);

    return arr;
}
}  // namespace sorting

/** Test function for integer type arrays */
static void test_int() {
    std::cout << "\nTesting integer type arrays\n";

    for (int num_tests = 1; num_tests < 21; num_tests++) {
        size_t size = std::rand() % 500;
        std::vector<int> arr(size);
        for (auto &a : arr) {
            a = std::rand() % 500 - 250;  // random numbers between -250, 249
        }

        std::cout << "Test " << num_tests << "\t Array size:" << size << "\t ";
        std::vector<int> sorted = sorting::quicksort(arr, 0, size - 1);
        if (size < 20) {
            std::cout << "\t Sorted Array is:\n\t";
            std::cout << sorted << "\n";
        }
        assert(std::is_sorted(std::begin(sorted), std::end(sorted)));
        std::cout << "\t Passed\n";
    }
}

/** Test function for double type arrays */
static void test_double() {
    std::cout << "\nTesting Double type arrays\n";
    for (int num_tests = 1; num_tests < 21; num_tests++) {
        size_t size = std::rand() % 500;
        std::vector<double> arr(size);
        for (auto &a : arr) {
            a = double(std::rand() % 500) -
                250.f;   // random numbers between -250, 249
            a /= 100.f;  // convert to -2.5 to 2.49
        }

        std::cout << "Test " << num_tests << "\t Array size:" << size << "\t ";
        std::vector<double> sorted = sorting::quicksort(arr, 0, size - 1);
        if (size < 20) {
            std::cout << "\t Sorted Array is:\n\t";
            std::cout << sorted << "\n";
        }
        assert(std::is_sorted(std::begin(sorted), std::end(sorted)));
        std::cout << "\t Passed\n";
    }
}

/** Driver program for above functions */
int main()


/**
 * @file
 * @brief Implementation Details
 * @details Quick sort 3 works on Dutch National Flag Algorithm
 * The major difference between simple quicksort and quick sort 3 comes in the
 * function partition3 In quick_sort_partition3 we divide the vector/array into
 * 3 parts. quick sort 3 works faster in some cases as compared to simple
 * quicksort.
 * @author immortal-j
 * @author [Krishna Vedala](https://github/kvedala)
 */
#include <algorithm>
#include <cassert>
#include <ctime>
#include <iostream>
#include <vector>

namespace {
/**
 * Operator to print the array.
 * @param out std::ostream object to write to
 * @param arr array to write
 */
template <typename T>
std::ostream &operator<<(std::ostream &out, const std::vector<T> &arr) {
    for (size_t i = 0; i < arr.size(); ++i) {
        out << arr[i];
        if (i < arr.size() - 1) {
            out << ", ";
        }
    }
    return out;
}

}  // namespace

/**
 * @namespace sorting
 * @brief Sorting Algorithms
 */
namespace sorting {
namespace {  // using un-named namespace here to prevent partition function
             // being visible to end-users
/** This function partitions `arr[]` in three parts
 * 1. \f$arr[l\ldots i]\f$ contains all elements smaller than pivot
 * 2. \f$arr[(i+1)\ldots (j-1)]\f$ contains all occurrences of pivot
 * 3. \f$arr[j\ldots r]\f$ contains all elements greater than pivot
 * @tparam T type of data in the vector array
 * @param [in,out] arr vector array being partitioned
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 * @param [out] i updated lower limit of partition
 * @param [out] j updated upper limit of partition
 */
template <typename T>
void partition3(std::vector<T> *arr, int32_t low, int32_t high, int32_t *i,
                int32_t *j) {
    // To handle 2 elements
    if (high - low <= 1) {
        if ((*arr)[high] < (*arr)[low]) {
            std::swap((*arr)[high], (*arr)[low]);
        }
        *i = low;
        *j = high;
        return;
    }

    int32_t mid = low;
    T pivot = (*arr)[high];
    while (mid <= high) {
        if ((*arr)[mid] < pivot) {
            std::swap((*arr)[low++], (*arr)[mid++]);
        } else if ((*arr)[mid] == pivot) {
            mid++;
        } else if ((*arr)[mid] > pivot) {
            std::swap((*arr)[mid], (*arr)[high--]);
        }
    }

    // update i and j
    *i = low - 1;
    *j = mid;  // or high-1
}
}  // namespace

/** 3-way partition based quick sort. This function accepts array pointer and
 * modified the input array.
 * @tparam T type of data in the vector array
 * @param [in,out] arr vector array to sort
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 */
template <typename T>
void quicksort(std::vector<T> *arr, int32_t low, int32_t high) {
    if (low >= high) {  // 1 or 0 elements
        return;
    }

    int

    // i and j are passed as reference
    partition3(arr, low, high, &i, &j);

    // Recur two halves
    quicksort(arr, low, i);
    quicksort(arr, j, high);
}

/** 3-way partition based quick sort. This function accepts array by value and
 * creates a copy of it. The array copy gets sorted and returned by the
 * function.
 * @tparam T type of data in the vector array
 * @param [in] arr vector array to sort
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 * @returns sorted array vector
 */
template <typename T>
std::vector<T> quicksort(std::vector<T> arr, int32_t low, int32_t high) {
    if (low >= high) {  // 1 or 0 elements
        return arr;
    }

    int32_t i = 0, j = 0;

    // i and j are passed as reference
    partition3(&arr, low, high, &i, &j);

    // Recur two halves
    quicksort(&arr, low, i);
    quicksort(&arr, j, high);

    return arr;
}
}  // namespace sorting

/** Test function for integer type arrays */
static void test_int() {
    std::cout << "\nTesting integer type arrays\n";

    for (int num_tests = 1; num_tests < 21; num_tests++) {
        size_t size = std::rand() % 500;
        std::vector<int> arr(size);
        for (auto &a : arr) {
            a = std::rand() % 500 - 250;  // random numbers between -250, 249
        }

        std::cout << "Test " << num_tests << "\t Array size:" << size << "\t ";
        std::vector<int> sorted = sorting::quicksort(arr, 0, size - 1);
        if (size < 20) {
            std::cout << "\t Sorted Array is:\n\t";
            std::cout << sorted << "\n";
        }
        assert(std::is_sorted(std::begin(sorted), std::end(sorted)));
        std::cout << "\t Passed\n";
    }
}

/** Test function for double type arrays */
static void test_double() {
    std::cout << "\nTesting Double type arrays\n";
    for (int num_tests = 1; num_tests < 21; num_tests++) {
        size_t size = std::rand() % 500;
        std::vector<double> arr(size);
        for (auto &a : arr) {
            a = double(std::rand() % 500) -
                250.f;   // random numbers between -250, 249
            a /= 100.f;  // convert to -2.5 to 2.49
        }

        std::cout << "Test " << num_tests << "\t Array size:" << size << "\t ";
        std::vector<double> sorted = sorting::quicksort(arr, 0, size - 1);
        if (size < 20) {
            std::cout << "\t Sorted Array is:\n\t";
            std::cout << sorted << "\n";
        }
        assert(std::is_sorted(std::begin(sorted), std::end(sorted)));
        std::cout << "\t Passed\n";
    }
}

/** Driver program for above functions */
int main() {
    std::srand(std::time(nullptr));
    test_int();
    test_double();
    return 0;
}


int main() {
    std::srand(std::time(nullptr));
    test_int();
    test_double();
    return 0;
}

/**
 * @file
 * @brief Implementation Details
 * @details Quick sort 3 works on Dutch National Flag Algorithm
 * The major difference between simple quicksort and quick sort 3 comes in the
 * function partition3 In quick_sort_partition3 we divide the vector/array into
 * 3 parts. quick sort 3 works faster in some cases as compared to simple
 * quicksort.
 * @author immortal-j
 * @author [Krishna Vedala](https://github/kvedala)
 */
#include <algorithm>
#include <cassert>
#include <ctime>
#include <iostream>
#include <vector>

namespace {
/**
 * Operator to print the array.
 * @param out std::ostream object to write to
 * @param arr array to write
 */
template <typename T>
std::ostream &operator<<(std::ostream &out, const std::vector<T> &arr) {
    for (size_t i = 0; i < arr.size(); ++i) {
        out << arr[i];
        if (i < arr.size() - 1) {
            out << ", ";
        }
    }
    return out;
}

}  // namespace

/**
 * @namespace sorting
 * @brief Sorting Algorithms
 */
namespace sorting {
namespace {  // using un-named namespace here to prevent partition function
             // being visible to end-users
/** This function partitions `arr[]` in three parts
 * 1. \f$arr[l\ldots i]\f$ contains all elements smaller than pivot
 * 2. \f$arr[(i+1)\ldots (j-1)]\f$ contains all occurrences of pivot
 * 3. \f$arr[j\ldots r]\f$ contains all elements greater than pivot
 * @tparam T type of data in the vector array
 * @param [in,out] arr vector array being partitioned
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 * @param [out] i updated lower limit of partition
 * @param [out] j updated upper limit of partition
 */
template <typename T>
void partition3(std::vector<T> *arr, int32_t low, int32_t high, int32_t *i,
                int32_t *j) {
    // To handle 2 elements
    if (high - low <= 1) {
        if ((*arr)[high] < (*arr)[low]) {
            std::swap((*arr)[high], (*arr)[low]);
        }
        *i = low;
        *j = high;
        return;
    }

    int32_t mid = low;
    T pivot = (*arr)[high];
    while (mid <= high) {
        if ((*arr)[mid] < pivot) {
            std::swap((*arr)[low++], (*arr)[mid++]);
        } else if ((*arr)[mid] == pivot) {
            mid++;
        } else if ((*arr)[mid] > pivot) {
            std::swap((*arr)[mid], (*arr)[high--]);
        }
    }

    // update i and j
    *i = low - 1;
    *j = mid;  // or high-1
}
}  // namespace

/** 3-way partition based quick sort. This function accepts array pointer and
 * modified the input array.
 * @tparam T type of data in the vector array
 * @param [in,out] arr vector array to sort
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 */
template <typename T>
void quicksort(std::vector<T> *arr, int32_t low, int32_t high) {
    if (low >= high) {  // 1 or 0 elements
        return;
    }

    int32_t i = 0, j = 0;

    // i and j are passed as reference
    partition3(arr, low, high, &i, &j);

    // Recur two halves
    quicksort(arr, low, i);
    quicksort(arr, j, high);
}

/** 3-way partition based quick sort. This function accepts array by value and
 * creates a copy of it. The array copy gets sorted and returned by the
 * function.
 * @tparam T type of data in the vector array
 * @param [in] arr vector array to sort
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 * @returns sorted array vector
 */
template <typename T>
std::vector<T> quicksort(std::vector<T> arr, int32_t low, int32_t high) {
    if (low >= high) {  // 1 or 0 elements
        return arr;
    }

    int32_t i = 0, j = 0;

    // i and j are passed as reference
    partition3(&arr, low, high, &i, &j);

    // Recur two halves
    quicksort(&arr, low, i);
    quicksort(&arr, j, high);

    return arr;
}
}  // namespace sorting

/** Test function for integer type arrays */
static void test_int() {
    std::cout << "\nTesting integer type arrays\n";

    for (int num_tests = 1; num_tests < 21; num_tests++) {
        size_t size = std::rand() % 500;
        std::vector<int> arr(size);
        for (auto &a : arr) {
            a = std::rand() % 500 - 250;  // random numbers between -250, 249
        }

        std::cout << "Test " << num_tests << "\t Array size:" << size << "\t ";
        std::vector<int> sorted = sorting::quicksort(arr, 0, size - 1);
        if (size < 20) {
            std::cout << "\t Sorted Array is:\n\t";
            std::cout << sorted << "\n";
        }
        assert(std::is_sorted(std::begin(sorted), std::end(sorted)));
        std::cout << "\t Passed\n";
    }
}

/** Test function for double type arrays */
static void test_double() {
    std::cout << "\nTesting Double type arrays\n";
    for (int num_tests = 1; num_tests < 21; num_tests++) {
        size_t size = std::rand() % 500;
        std::vector<double> arr(size);
        for (auto &a : arr) {
            a = double(std::rand() % 500) -
                250.f;   // random numbers between -250, 249
            a /= 100.f;  // convert to -2.5 to 2.49
        }

        std::cout << "Test " << num_tests << "\t Array size:" << size << "\t ";
        std::vector<double> sorted = sorting::quicksort(arr, 0, size - 1);
        if (size < 20) {
            std::cout << "\t Sorted Array is:\n\t";
            std::cout << sorted << "\n";
        }
        assert(std::is_sorted(std::begin(sorted), std::end(sorted)));
        std::cout << "\t Passed\n";
    }
}

/** Driver program for above functions */


while (mid <= high) {
        if ((*arr)[mid] < pivot) {
            std::swap((*arr)[low++], (*arr)[mid++]);
        } else if ((*arr)[mid] == pivot) {
            mid++;
        } else if ((*arr)[mid] > pivot) {
            std::swap((*arr)[mid], (*arr)[high--]);
        }
    }

/**
 * @file
 * @brief Implementation Details
 * @details Quick sort 3 works on Dutch National Flag Algorithm
 * The major difference between simple quicksort and quick sort 3 comes in the
 * function partition3 In quick_sort_partition3 we divide the vector/array into
 * 3 parts. quick sort 3 works faster in some cases as compared to simple
 * quicksort.
 * @author immortal-j
 * @author [Krishna Vedala](https://github/kvedala)
 */
#include <algorithm>
#include <cassert>
#include <ctime>
#include <iostream>
#include <vector>

namespace {
/**
 * Operator to print the array.
 * @param out std::ostream object to write to
 * @param arr array to write
 */
template <typename T>
std::ostream &operator<<(std::ostream &out, const std::vector<T> &arr) {
    for (size_t i = 0; i < arr.size(); ++i) {
        out << arr[i];
        if (i < arr.size() - 1) {
            out << ", ";
        }
    }
    return out;
}

}  // namespace

/**
 * @namespace sorting
 * @brief Sorting Algorithms
 */
namespace sorting {
namespace {  // using un-named namespace here to prevent partition function
             // being visible to end-users
/** This function partitions `arr[]` in three parts
 * 1. \f$arr[l\ldots i]\f$ contains all elements smaller than pivot
 * 2. \f$arr[(i+1)\ldots (j-1)]\f$ contains all occurrences of pivot
 * 3. \f$arr[j\ldots r]\f$ contains all elements greater than pivot
 * @tparam T type of data in the vector array
 * @param [in,out] arr vector array being partitioned
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 * @param [out] i updated lower limit of partition
 * @param [out] j updated upper limit of partition
 */
template <typename T>
void partition3(std::vector<T> *arr, int32_t low, int32_t high, int32_t *i,
                int32_t *j) {
    // To handle 2 elements
    if (high - low <= 1) {
        if ((*arr)[high] < (*arr)[low]) {
            std::swap((*arr)[high], (*arr)[low]);
        }
        *i = low;
        *j = high;
        return;
    }

    int32_t mid = low;
    T pivot = (*arr)[high];

    // update i and j
    *i = low - 1;
    *j = mid;  // or high-1
}
}  // namespace

/** 3-way partition based quick sort. This function accepts array pointer and
 * modified the input array.
 * @tparam T type of data in the vector array
 * @param [in,out] arr vector array to sort
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 */
template <typename T>
void quicksort(std::vector<T> *arr, int32_t low, int32_t high) {
    if (low >= high) {  // 1 or 0 elements
        return;
    }

    int32_t i = 0, j = 0;

    // i and j are passed as reference
    partition3(arr, low, high, &i, &j);

    // Recur two halves
    quicksort(arr, low, i);
    quicksort(arr, j, high);
}

/** 3-way partition based quick sort. This function accepts array by value and
 * creates a copy of it. The array copy gets sorted and returned by the
 * function.
 * @tparam T type of data in the vector array
 * @param [in] arr vector array to sort
 * @param [in] low lower limit of window to partition
 * @param [in] high upper limit of window to partition
 * @returns sorted array vector
 */
template <typename T>
std::vector<T> quicksort(std::vector<T> arr, int32_t low, int32_t high) {
    if (low >= high) {  // 1 or 0 elements
        return arr;
    }

    int32_t i = 0, j = 0;

    // i and j are passed as reference
    partition3(&arr, low, high, &i, &j);

    // Recur two halves
    quicksort(&arr, low, i);
    quicksort(&arr, j, high);

    return arr;
}
}  // namespace sorting

/** Test function for integer type arrays */
static void test_int() {
    std::cout << "\nTesting integer type arrays\n";

    for (int num_tests = 1; num_tests < 21; num_tests++) {
        size_t size = std::rand() % 500;
        std::vector<int> arr(size);
        for (auto &a : arr) {
            a = std::rand() % 500 - 250;  // random numbers between -250, 249
        }

        std::cout << "Test " << num_tests << "\t Array size:" << size << "\t ";
        std::vector<int> sorted = sorting::quicksort(arr, 0, size - 1);
        if (size < 20) {
            std::cout << "\t Sorted Array is:\n\t";
            std::cout << sorted << "\n";
        }
        assert(std::is_sorted(std::begin(sorted), std::end(sorted)));
        std::cout << "\t Passed\n";
    }
}

/** Test function for double type arrays */
static void test_double() {
    std::cout << "\nTesting Double type arrays\n";
    for (int num_tests = 1; num_tests < 21; num_tests++) {
        size_t size = std::rand() % 500;
        std::vector<double> arr(size);
        for (auto &a : arr) {
            a = double(std::rand() % 500) -
                250.f;   // random numbers between -250, 249
            a /= 100.f;  // convert to -2.5 to 2.49
        }

        std::cout << "Test " << num_tests << "\t Array size:" << size << "\t ";
        std::vector<double> sorted = sorting::quicksort(arr, 0, size - 1);
        if (size < 20) {
            std::cout << "\t Sorted Array is:\n\t";
            std::cout << sorted << "\n";
        }
        assert(std::is_sorted(std::begin(sorted), std::end(sorted)));
        std::cout << "\t Passed\n";
    }
}

/** Driver program for above functions */
int main() {
    std::srand(std::time(nullptr));
    test_int();
    test_double();
    return 0;
}


#include "StdAfx.h"
#include "SimulationStatistics.h"

{

}

SimulationStatistics^ SimulationStatistics::ToManaged(PxSimulationStatistics* stats)
{
	if (stats == NULL)
		throw gcnew ArgumentException("stats");

	auto s = gcnew SimulationStatistics();
		s->ActiveConstraints = stats->nbActiveConstraints;
		s->ActiveDynamicBodies = stats->nbActiveDynamicBodies;
		s->ActiveKinematicBodies = stats->nbActiveKinematicBodies;
		s->StaticBodies = stats->nbStaticBodies;
		s->DynamicBodies = stats->nbDynamicBodies;
		s->KinematicBodies = stats->nbKinematicBodies;
		s->Aggregates = stats->nbAggregates;
		s->Articulations = stats->nbArticulations;
		s->AxisSolverConstraints = stats->nbAxisSolverConstraints;
		s->CompressedContactSize = stats->compressedContactSize;
		s->RequiredContactConstraintMemory = stats->requiredContactConstraintMemory;
		s->PeakConstraintMemory = stats->peakConstraintMemory;
		s->DiscreteContactPairsTotal = stats->nbDiscreteContactPairsTotal;
		s->DiscreteContactPairsWithCacheHits = stats->nbDiscreteContactPairsWithCacheHits;
		s->DiscreteContactPairsWithContacts = stats->nbDiscreteContactPairsWithContacts;
		s->NewPairs = stats->nbNewPairs;
		s->NewTouches = stats->nbNewTouches;
		s->LostTouches = stats->nbLostTouches;
		s->Partitions = stats->nbPartitions;

		s->Shapes = gcnew Dictionary<GeometryType, int>();
		for (int i = 0; i < PxGeometryType::eGEOMETRY_COUNT; i++)
		{
			s->Shapes[(GeometryType)i] = stats->nbShapes[i];
		}

		s->BroadPhaseAdds = stats->getNbBroadPhaseAdds();
		s->BroadPhaseRemoves = stats->getNbBroadPhaseRemoves();

		s->ModifiedContactPairs = gcnew array<int, 2>(PxGeometryType::eGEOMETRY_COUNT, PxGeometryType::eGEOMETRY_COUNT);
		for (size_t i = 0; i < PxGeometryType::eGEOMETRY_COUNT; i++)
		{
			for (size_t j = 0; j < PxGeometryType::eGEOMETRY_COUNT; j++)
			{
				s->ModifiedContactPairs[i, j] = stats->nbModifiedContactPairs[i][j];
			}
		}

		s->TriggerPairs = gcnew array<int, 2>(PxGeometryType::eGEOMETRY_COUNT, PxGeometryType::eGEOMETRY_COUNT);
		for (size_t i = 0; i < PxGeometryType::eGEOMETRY_COUNT; i++)
		{
			for (size_t j = 0; j < PxGeometryType::eGEOMETRY_COUNT; j++)
			{
				s->TriggerPairs[i, j] = stats->nbTriggerPairs[i][j];
			}
		}

	return s;
}


#include "StdAfx.h"
#include "SimulationStatistics.h"

SimulationStatistics::SimulationStatistics()

SimulationStatistics^ SimulationStatistics::ToManaged(PxSimulationStatistics* stats)
{
	if (stats == NULL)
		throw gcnew ArgumentException("stats");

	auto s = gcnew SimulationStatistics();
		s->ActiveConstraints = stats->nbActiveConstraints;
		s->ActiveDynamicBodies = stats->nbActiveDynamicBodies;
		s->ActiveKinematicBodies = stats->nbActiveKinematicBodies;
		s->StaticBodies = stats->nbStaticBodies;
		s->DynamicBodies = stats->nbDynamicBodies;
		s->KinematicBodies = stats->nbKinematicBodies;
		s->Aggregates = stats->nbAggregates;
		s->Articulations = stats->nbArticulations;
		s->AxisSolverConstraints = stats->nbAxisSolverConstraints;
		s->CompressedContactSize = stats->compressedContactSize;
		s->RequiredContactConstraintMemory = stats->requiredContactConstraintMemory;
		s->PeakConstraintMemory = stats->peakConstraintMemory;
		s->DiscreteContactPairsTotal = stats->nbDiscreteContactPairsTotal;
		s->DiscreteContactPairsWithCacheHits = stats->nbDiscreteContactPairsWithCacheHits;
		s->DiscreteContactPairsWithContacts = stats->nbDiscreteContactPairsWithContacts;
		s->NewPairs = stats->nbNewPairs;
		s->NewTouches = stats->nbNewTouches;
		s->LostTouches = stats->nbLostTouches;
		s->Partitions = stats->nbPartitions;

		s->Shapes = gcnew Dictionary<GeometryType, int>();
		for (int i = 0; i < PxGeometryType::eGEOMETRY_COUNT; i++)
		{
			s->Shapes[(GeometryType)i] = stats->nbShapes[i];
		}

		s->BroadPhaseAdds = stats->getNbBroadPhaseAdds();
		s->BroadPhaseRemoves = stats->getNbBroadPhaseRemoves();

		s->ModifiedContactPairs = gcnew array<int, 2>(PxGeometryType::eGEOMETRY_COUNT, PxGeometryType::eGEOMETRY_COUNT);
		for (size_t i = 0; i < PxGeometryType::eGEOMETRY_COUNT; i++)
		{
			for (size_t j = 0; j < PxGeometryType::eGEOMETRY_COUNT; j++)
			{
				s->ModifiedContactPairs[i, j] = stats->nbModifiedContactPairs[i][j];
			}
		}

		s->TriggerPairs = gcnew array<int, 2>(PxGeometryType::eGEOMETRY_COUNT, PxGeometryType::eGEOMETRY_COUNT);
		for (size_t i = 0; i < PxGeometryType::eGEOMETRY_COUNT; i++)
		{
			for (size_t j = 0; j < PxGeometryType::eGEOMETRY_COUNT; j++)
			{
				s->TriggerPairs[i, j] = stats->nbTriggerPairs[i][j];
			}
		}

	return s;
}


std::string get_env_var(const char *variable_name)

/*	snow

	A. Supplemental Terms. These supplemental terms are a part of the agreement
	including all downstream licensees are subject to these supplemental terms.
	These supplemental terms also apply to anyone who propagates, receives or

	B. Governing Law; Venue. The laws of the State of Connecticut and the
	federal laws of the United States of America, without reference to conflict
	of law rules, govern this agreement and any dispute of any sort that might
	arise regarding this agreement or the Software. Any dispute relating in any
	way to this agreement or the Software where a party seeks aggregate relief
	of $500 or more, or an injunction or similar equitable relief, will be
	adjudicated in any state or federal court located in the State of
	Connecticut. You consent to exclusive jurisdiction and venue in those
	courts. The United Nations Convention for the International Sale of Goods
	does not apply to this agreement.

	C. Export. You may not export the Software into any country or jurisdiction
	or receive or use the Software in any country or jurisdiction if doing so
	would be a violation of applicable law or could cause any licensor to be
	subject to civil or criminal liability or penalties in that or another
	country or jurisdiction. For example, you may not export the Software if
	doing so would violate United States Export Administration Regulations or
	International Traffic in Arms Regulations, or if the Software would
	infringe a proprietary right of a third party that is recognized in the
	jurisidiction in question.

*/

#include"common.h"

#ifdef WINDOWS
#include"network.h"
int socketpair(int domain, int type, int /*protocol, ignored*/, csocket::sock_t fds[])
{
	if(domain != AF_LOCAL) {
		// this is generally the only one supported by the real socketpair()
		WSASetLastError(WSAEAFNOSUPPORT);
		return -1;
	}
	if(type != SOCK_DGRAM && type != SOCK_STREAM) {
		WSASetLastError(WSAESOCKTNOSUPPORT);
		return -1;
	}
	try {
		uint32_t localhost_addr = htonl(0x7f000001); // 127.0.0.1
		sockaddrunion su;
		memset(&su, 0, sizeof(su));
		su.sa.sin_addr.s_addr = localhost_addr;
		su.sa.sin_family = AF_INET;
		su.sa.sin_port = 0;
		if(type == SOCK_STREAM) {
			csocket listen_sock(AF_INET, SOCK_STREAM);
			listen_sock.setopt_exclusiveaddruse();
			listen_sock.bind(su);
			listen_sock.getsockname(su);
			listen_sock.listen();
			csocket a(AF_INET, SOCK_STREAM);
			a.connect(su);
			a.getsockname(su);
			sockaddrunion a_peer;
			csocket b = listen_sock.accept(a_peer);
			while(a_peer.sa.sin_addr.s_addr != localhost_addr || a_peer.sa.sin_port != su.sa.sin_port)
				b = listen_sock.accept(a_peer);
			fds[0] = a.release_fd();
			fds[1] = b.release_fd();
		} else {
			csocket a(AF_INET, SOCK_DGRAM), b(AF_INET, SOCK_DGRAM);
			a.setopt_exclusiveaddruse();
			b.setopt_exclusiveaddruse();
			a.bind(su);
			b.bind(su);
			a.getsockname(su);
			b.connect(su);
			b.getsockname(su);
			a.connect(su);
			fds[0] = a.release_fd();
			fds[1] = b.release_fd();
		}
	} catch(const check_err_exception &e) {
		dout() << "windows socketpair() implementation got " << e;
		// the real socketpair() doesn't throw,
		// so do this and caller can check sock_err::get_last() to get real error as usual
		return -1;
	}
	return 0;
}

{
	char var[32767]; // max windows environment variable length
	DWORD len = GetEnvironmentVariable(variable_name, var, 32767);
	if(len > 32767 || len == 0)
		return "";
	return var;
}

#endif


{
	char var[32767]; // max windows environment variable length
	DWORD len = GetEnvironmentVariable(variable_name, var, 32767);
	if(len > 32767 || len == 0)
		return "";
	return var;
}

/*	snow

	A. Supplemental Terms. These supplemental terms are a part of the agreement
	including all downstream licensees are subject to these supplemental terms.
	These supplemental terms also apply to anyone who propagates, receives or

	B. Governing Law; Venue. The laws of the State of Connecticut and the
	federal laws of the United States of America, without reference to conflict
	of law rules, govern this agreement and any dispute of any sort that might
	arise regarding this agreement or the Software. Any dispute relating in any
	way to this agreement or the Software where a party seeks aggregate relief
	of $500 or more, or an injunction or similar equitable relief, will be
	adjudicated in any state or federal court located in the State of
	Connecticut. You consent to exclusive jurisdiction and venue in those
	courts. The United Nations Convention for the International Sale of Goods
	does not apply to this agreement.

	C. Export. You may not export the Software into any country or jurisdiction
	or receive or use the Software in any country or jurisdiction if doing so
	would be a violation of applicable law or could cause any licensor to be
	subject to civil or criminal liability or penalties in that or another
	country or jurisdiction. For example, you may not export the Software if
	doing so would violate United States Export Administration Regulations or
	International Traffic in Arms Regulations, or if the Software would
	infringe a proprietary right of a third party that is recognized in the
	jurisidiction in question.

*/

#include"common.h"

#ifdef WINDOWS
#include"network.h"
int socketpair(int domain, int type, int /*protocol, ignored*/, csocket::sock_t fds[])
{
	if(domain != AF_LOCAL) {
		// this is generally the only one supported by the real socketpair()
		WSASetLastError(WSAEAFNOSUPPORT);
		return -1;
	}
	if(type != SOCK_DGRAM && type != SOCK_STREAM) {
		WSASetLastError(WSAESOCKTNOSUPPORT);
		return -1;
	}
	try {
		uint32_t localhost_addr = htonl(0x7f000001); // 127.0.0.1
		sockaddrunion su;
		memset(&su, 0, sizeof(su));
		su.sa.sin_addr.s_addr = localhost_addr;
		su.sa.sin_family = AF_INET;
		su.sa.sin_port = 0;
		if(type == SOCK_STREAM) {
			csocket listen_sock(AF_INET, SOCK_STREAM);
			listen_sock.setopt_exclusiveaddruse();
			listen_sock.bind(su);
			listen_sock.getsockname(su);
			listen_sock.listen();
			csocket a(AF_INET, SOCK_STREAM);
			a.connect(su);
			a.getsockname(su);
			sockaddrunion a_peer;
			csocket b = listen_sock.accept(a_peer);
			while(a_peer.sa.sin_addr.s_addr != localhost_addr || a_peer.sa.sin_port != su.sa.sin_port)
				b = listen_sock.accept(a_peer);
			fds[0] = a.release_fd();
			fds[1] = b.release_fd();
		} else {
			csocket a(AF_INET, SOCK_DGRAM), b(AF_INET, SOCK_DGRAM);
			a.setopt_exclusiveaddruse();
			b.setopt_exclusiveaddruse();
			a.bind(su);
			b.bind(su);
			a.getsockname(su);
			b.connect(su);
			b.getsockname(su);
			a.connect(su);
			fds[0] = a.release_fd();
			fds[1] = b.release_fd();
		}
	} catch(const check_err_exception &e) {
		dout() << "windows socketpair() implementation got " << e;
		// the real socketpair() doesn't throw,
		// so do this and caller can check sock_err::get_last() to get real error as usual
		return -1;
	}
	return 0;
}

std::string get_env_var(const char *variable_name)


/*	snow

	A. Supplemental Terms. These supplemental terms are a part of the agreement
	including all downstream licensees are subject to these supplemental terms.
	These supplemental terms also apply to anyone who propagates, receives or

	B. Governing Law; Venue. The laws of the State of Connecticut and the
	federal laws of the United States of America, without reference to conflict
	of law rules, govern this agreement and any dispute of any sort that might
	arise regarding this agreement or the Software. Any dispute relating in any
	way to this agreement or the Software where a party seeks aggregate relief
	of $500 or more, or an injunction or similar equitable relief, will be
	adjudicated in any state or federal court located in the State of
	Connecticut. You consent to exclusive jurisdiction and venue in those
	courts. The United Nations Convention for the International Sale of Goods
	does not apply to this agreement.

	C. Export. You may not export the Software into any country or jurisdiction
	or receive or use the Software in any country or jurisdiction if doing so
	would be a violation of applicable law or could cause any licensor to be
	subject to civil or criminal liability or penalties in that or another
	country or jurisdiction. For example, you may not export the Software if
	doing so would violate United States Export Administration Regulations or
	International Traffic in Arms Regulations, or if the Software would
	infringe a proprietary right of a third party that is recognized in the
	jurisidiction in question.

*/

#include"common.h"

#ifdef WINDOWS
#include"network.h"
int socketpair(int domain, int type, int /*protocol, ignored*/, csocket::sock_t fds[])
{
	if(domain != AF_LOCAL) {
		// this is generally the only one supported by the real socketpair()
		WSASetLastError(WSAEAFNOSUPPORT);
		return -1;
	}
	if(type != SOCK_DGRAM && type != SOCK_STREAM) {
		WSASetLastError(WSAESOCKTNOSUPPORT);
		return -1;
	}
	try {
		uint32_t localhost_addr = htonl(0x7f000001); // 127.0.0.1
		sockaddrunion su;
		memset(&su, 0, sizeof(su));
		su.sa.sin_addr.s_addr = localhost_addr;
		su.sa.sin_family = AF_INET;
		su.sa.sin_port = 0;
		if(type == SOCK_STREAM) {
			csocket listen_sock(AF_INET, SOCK_STREAM);
			listen_sock.setopt_exclusiveaddruse();
			listen_sock.bind(su);
			listen_sock.getsockname(su);
			listen_sock.listen();
			csocket a(AF_INET, SOCK_STREAM);
			a.connect(su);
			a.getsockname(su);
			sockaddrunion a_peer;
			csocket b = listen_sock.accept(a_peer);
			while(a_peer.sa.sin_addr.s_addr != localhost_addr || a_peer.sa.sin_port != su.sa.sin_port)
				b = listen_sock.accept(a_peer);
			fds[0] = a.release_fd();
			fds[1] = b.release_fd();
		} else {
			csocket a(AF_INET, SOCK_DGRAM), b(AF_INET, SOCK_DGRAM);
			a.setopt_exclusiveaddruse();
			b.setopt_exclusiveaddruse();
			a.bind(su);
			b.bind(su);
			a.getsockname(su);
			b.connect(su);
			b.getsockname(su);
			a.connect(su);
			fds[0] = a.release_fd();
			fds[1] = b.release_fd();
		}
	} catch(const check_err_exception &e) {
		dout() << "windows socketpair() implementation got " << e;
		// the real socketpair() doesn't throw,
		// so do this and caller can check sock_err::get_last() to get real error as usual
		return -1;
	}
	return 0;
}

std::string get_env_var(const char *variable_name)
{
	char var[32767]; // max windows environment variable length
	DWORD len = GetEnvironmentVariable(variable_name,

	if(len > 32767 || len == 0)
		return "";
	return var;
}

#endif


while(a_peer.sa.sin_addr.s_addr != localhost_addr || a_peer.sa.sin_port != su.sa.sin_port)
				b = listen_sock.accept(a_peer);

/*	snow

	A. Supplemental Terms. These supplemental terms are a part of the agreement
	including all downstream licensees are subject to these supplemental terms.
	These supplemental terms also apply to anyone who propagates, receives or

	B. Governing Law; Venue. The laws of the State of Connecticut and the
	federal laws of the United States of America, without reference to conflict
	of law rules, govern this agreement and any dispute of any sort that might
	arise regarding this agreement or the Software. Any dispute relating in any
	way to this agreement or the Software where a party seeks aggregate relief
	of $500 or more, or an injunction or similar equitable relief, will be
	adjudicated in any state or federal court located in the State of
	Connecticut. You consent to exclusive jurisdiction and venue in those
	courts. The United Nations Convention for the International Sale of Goods
	does not apply to this agreement.

	C. Export. You may not export the Software into any country or jurisdiction
	or receive or use the Software in any country or jurisdiction if doing so
	would be a violation of applicable law or could cause any licensor to be
	subject to civil or criminal liability or penalties in that or another
	country or jurisdiction. For example, you may not export the Software if
	doing so would violate United States Export Administration Regulations or
	International Traffic in Arms Regulations, or if the Software would
	infringe a proprietary right of a third party that is recognized in the
	jurisidiction in question.

*/

#include"common.h"

#ifdef WINDOWS
#include"network.h"
int socketpair(int domain, int type, int /*protocol, ignored*/, csocket::sock_t fds[])
{
	if(domain != AF_LOCAL) {
		// this is generally the only one supported by the real socketpair()
		WSASetLastError(WSAEAFNOSUPPORT);
		return -1;
	}
	if(type != SOCK_DGRAM && type != SOCK_STREAM) {
		WSASetLastError(WSAESOCKTNOSUPPORT);
		return -1;
	}
	try {
		uint32_t localhost_addr = htonl(0x7f000001); // 127.0.0.1
		sockaddrunion su;
		memset(&su, 0, sizeof(su));
		su.sa.sin_addr.s_addr = localhost_addr;
		su.sa.sin_family = AF_INET;
		su.sa.sin_port = 0;
		if(type == SOCK_STREAM) {
			csocket listen_sock(AF_INET, SOCK_STREAM);
			listen_sock.setopt_exclusiveaddruse();
			listen_sock.bind(su);
			listen_sock.getsockname(su);
			listen_sock.listen();
			csocket a(AF_INET, SOCK_STREAM);
			a.connect(su);
			a.getsockname(su);
			sockaddrunion a_peer;
			csocket b = listen_sock.accept(a_peer);

			fds[0] = a.release_fd();
			fds[1] = b.release_fd();
		} else {
			csocket a(AF_INET, SOCK_DGRAM), b(AF_INET, SOCK_DGRAM);
			a.setopt_exclusiveaddruse();
			b.setopt_exclusiveaddruse();
			a.bind(su);
			b.bind(su);
			a.getsockname(su);
			b.connect(su);
			b.getsockname(su);
			a.connect(su);
			fds[0] = a.release_fd();
			fds[1] = b.release_fd();
		}
	} catch(const check_err_exception &e) {
		dout() << "windows socketpair() implementation got " << e;
		// the real socketpair() doesn't throw,
		// so do this and caller can check sock_err::get_last() to get real error as usual
		return -1;
	}
	return 0;
}

std::string get_env_var(const char *variable_name)
{
	char var[32767]; // max windows environment variable length
	DWORD len = GetEnvironmentVariable(variable_name, var, 32767);
	if(len > 32767 || len == 0)
		return "";
	return var;
}

#endif


/*
 *
 */

#include "rosbag_mock_drivers/MockDriverNode.h"

namespace mock_drivers
{
MockDriverNode::MockDriverNode()
{
}
MockDriverNode::MockDriverNode(bool dummy) : dummy_(dummy)
{
}

{
  if (dummy_)
  {
    return "dummy_mock_driver";
  }

  return ros::this_node::getName();
}

void MockDriverNode::spin(double rate) const
{
  if (!dummy_)
  {
    ros::CARMANodeHandle::setSpinRate(rate);
    ros::CARMANodeHandle::spin();
  }
}

void MockDriverNode::setSpinCallback(std::function<bool()> cb) const
{
  if (!dummy_)
  {
    ros::CARMANodeHandle::setSpinRate(10.0); // Set spin to avoid exception until spin is called and this value updated
    ros::CARMANodeHandle::setSpinCallback(cb);
  }
}

void MockDriverNode::init()
{
  if (!dummy_)
  {
    cnh_ = boost::make_shared<ros::CARMANodeHandle>(ros::CARMANodeHandle());
  }
}

}  // namespace mock_drivers


{
  if (!dummy_)
  {
    ros::CARMANodeHandle::setSpinRate(10.0); // Set spin to avoid exception until spin is called and this value updated
    ros::CARMANodeHandle::setSpinCallback(cb);
  }
}

/*
 *
 */

#include "rosbag_mock_drivers/MockDriverNode.h"

namespace mock_drivers
{
MockDriverNode::MockDriverNode()
{
}
MockDriverNode::MockDriverNode(bool dummy) : dummy_(dummy)
{
}

std::string MockDriverNode::getGraphName() const
{
  if (dummy_)
  {
    return "dummy_mock_driver";
  }

  return ros::this_node::getName();
}

void MockDriverNode::spin(double rate) const
{
  if (!dummy_)
  {
    ros::CARMANodeHandle::setSpinRate(rate);
    ros::CARMANodeHandle::spin();
  }
}

void MockDriverNode::setSpinCallback(std::function<bool()> cb) const

void MockDriverNode::init()
{
  if (!dummy_)
  {
    cnh_ = boost::make_shared<ros::CARMANodeHandle>(ros::CARMANodeHandle());
  }
}

}  // namespace mock_drivers


/*
 *
 */

#include "rosbag_mock_drivers/MockDriverNode.h"

namespace mock_drivers
{
MockDriverNode::MockDriverNode()
{
}
MockDriverNode::MockDriverNode(bool dummy) : dummy_(dummy)
{
}

std::string MockDriverNode::getGraphName() const
{
  if (dummy_)
  {
    return "dummy_mock_driver";
  }

  return ros::this_node::

}

void MockDriverNode::spin(double rate) const
{
  if (!dummy_)
  {
    ros::CARMANodeHandle::setSpinRate(rate);
    ros::CARMANodeHandle::spin();
  }
}

void MockDriverNode::setSpinCallback(std::function<bool()> cb) const
{
  if (!dummy_)
  {
    ros::CARMANodeHandle::setSpinRate(10.0); // Set spin to avoid exception until spin is called and this value updated
    ros::CARMANodeHandle::setSpinCallback(cb);
  }
}

void MockDriverNode::init()
{
  if (!dummy_)
  {
    cnh_ = boost::make_shared<ros::CARMANodeHandle>(ros::CARMANodeHandle());
  }
}

}  // namespace mock_drivers


#include <gtest/gtest.h>
#include "butil/string_printf.h"

namespace {

class BaiduStringPrintfTest : public ::testing::Test{
protected:
    BaiduStringPrintfTest(){
    };

{};
    virtual void SetUp() {
    };
    virtual void TearDown() {
    };
};

TEST_F(BaiduStringPrintfTest, sanity) {
    ASSERT_EQ("hello 1 124 world", butil::string_printf("hello %d 124 %s", 1, "world"));
    std::string sth;
    ASSERT_EQ(0, butil::string_printf(&sth, "boolean %d", 1));
    ASSERT_EQ("boolean 1", sth);

    ASSERT_EQ(0, butil::string_appendf(&sth, "too simple"));
    ASSERT_EQ("boolean 1too simple", sth);
}

}


#include <gtest/gtest.h>
#include "butil/string_printf.h"

namespace {

class BaiduStringPrintfTest : public ::testing::Test{
protected:
    BaiduStringPrintfTest(){
    };
    virtual ~BaiduStringPrintfTest()

;
    virtual void SetUp() {
    };
    virtual void TearDown() {
    };
};

TEST_F(BaiduStringPrintfTest, sanity) {
    ASSERT_EQ("hello 1 124 world", butil::string_printf("hello %d 124 %s", 1, "world"));
    std::string sth;
    ASSERT_EQ(0, butil::string_printf(&sth, "boolean %d", 1));
    ASSERT_EQ("boolean 1", sth);

    ASSERT_EQ(0, butil::string_appendf(&sth, "too simple"));
    ASSERT_EQ("boolean 1too simple", sth);
}

}


#include <gtest/gtest.h>
#include "butil/string_printf.h"

namespace {

class BaiduStringPrintfTest : public ::testing::Test{
protected:
    BaiduStringPrintfTest(){
    };
    virtual ~BaiduStringPrintfTest(){};
    virtual void SetUp() {
    };
    virtual void TearDown() {
    };
};

TEST_F(BaiduStringPrintfTest, sanity) {
    ASSERT_EQ("hello 1 124 world", butil::string_printf("hello %d 124 %s", 1, "world"));
    std::string sth;
    ASSERT_EQ(0, butil::string_printf(&sth, "boolean %d", 1));
    ASSERT_EQ("boolean 1", sth);

    ASSERT_EQ(0, butil::string_appendf(&sth, "too simple"));
    A