#include "stdafx.h"

#include "help.h"
#include <bitset>
#include <random>
#include <experimental/filesystem>

using namespace std;

#undef min

///Checks if path exists.
///@param[in] path file path
///@return TRUE if path exists
bool help::isPath(std::string path)
{
	std::experimental::filesystem::path p = path;
	return std::experimental::filesystem::exists(p);
}

///Checks if path belongs to folder.
///@param[in] path file path
///@return TRUE if path is folder
bool help::isFolder(std::string path)
{
	std::experimental::filesystem::path p = path;
	return std::experimental::filesystem::is_directory(p);
}

///Checks if path belongs to file.
///@param[in] path file path
///@return TRUE if path is file
bool help::isFile(std::string path)
{
	std::experimental::filesystem::path p = path;
	
	if (std::experimental::filesystem::is_directory(p))
		return false;

	return true;
}

///Strips file name from path.
///@param[in] path file path
///@return file path without file name
string help::stripFileNameFromPath(std::string path)
{
	return path.substr(0, path.find_last_of("\\/"));
}

///Trims white space form the left side of the string.
///@param[in] s string
///@param[in] white whitespace characters.
void help::trimLeft(string &s, string const &white)
{
	const size_t startpos = s.find_first_not_of(white);

	if (string::npos != startpos)
		s.erase(s.begin(), s.begin() + startpos);
}

///Trims white space form the right side of the string.
///@param[in] s string
///@param[in] white whitespace character
void help::trimRight(string &s, string const &delimiters)
{
	const size_t endpos = s.find_last_not_of(delimiters);

	if (string::npos != endpos)
		s.erase(s.begin() + endpos + 1, s.end());
}

///Trims white spaces from both sides of the string.
///@param[in] s string
///@param[in] white string containing whitespace characters
void help::trim(string &s, string const &white)
{
	trimLeft(s, white);
	trimRight(s, white);
}

///Splits string by selected delimiters.
///@param[in] s string
///@param[in] delimiters array containing delimiters
///@return split strings
vector<string> help::split(string const& s, char const *delimiters)
{
	vector<string> output;

	bitset<255> delims;
	while (*delimiters)
	{
		unsigned char code = *delimiters++;
		delims[code] = true;
	}

	string::const_iterator beg;
	bool in_token = false;
	for (string::const_iterator it = s.begin(), end = s.end(); it != end; ++it)
	{
		if (delims[*it])
		{
			if (in_token)
			{
				//output.emplace_back(beg, it);
				output.emplace_back(vector<string>::value_type(beg, it));
				in_token = false;
			}
		}
		else if (!in_token)
		{
			beg = it;
			in_token = true;
		}
	}

	if (in_token)
		output.emplace_back(vector<string>::value_type(beg, s.end()));

	return output;
}

///Checks if string contains BOM characters and if yes then removes them (please use encoding without BOM or UTF-8).
///@param[in] s string
void help::correctBomLine(string &s)
{
	if (s.compare(0, 3, "\xEF\xBB\xBF") == 0)	// Is the file marked as UTF-8?
	{
		s.erase(0, 3);							// Now get rid of the BOM.
	}
	else if (s.compare(0, 2, "\xFE\xFF") == 0)  // Is the file marked as UTF-16 BE?
	{
		s.erase(0, 2);							// Now get rid of the BOM.
	}
	else if (s.compare(0, 2, "\xFF\xFE") == 0)  // Is the file marked as UTF-16 LE
	{
		s.erase(0, 2);							// Now get rid of the BOM.
	}
	else if (s.compare(0, 4, "\x00\x00\xFE\xFF") == 0)  // Is the file marked as UTF-32 BE?
	{
		s.erase(0, 4);							// Now get rid of the BOM.
	}
	else if (s.compare(0, 4, "\xFF\xFE\x00\x00") == 0)  // Is the file marked as UTF-32 LE?
	{
		s.erase(0, 4);							// Now get rid of the BOM.
	}
}

///Generates random real number.
///@param[in] min minimal generated number
///@param[in] max maximal generated number
///@return random real number
double help::randomReal(int min, int max)
{
	std::random_device rd;
	std::mt19937 gen(rd());
	std::uniform_real_distribution<double> dis(min, max);

	return dis(gen);
}

///Generates random integer number.
///@param[in] min minimal generated number
///@param[in] max maximal generated number
///@return random int number
int help::randomInt(int min, int max)
{
	std::random_device rd;
	std::mt19937 gen(rd());
	std::uniform_int_distribution<int> dis(min, max);

	return dis(gen);
}

///Generates random time series.
///@param[in] len time series length
///@param[in] dims number of dimensions
///@param[in] min minimal value of the time series element
///@param[in] max maximal value of the time series element
///@return random time series
vtr2<double> help::randomSeries(int len, int dims, int min, int max)
{
	vtr2<double> ts(len);

	for (size_t i = 0; i < (size_t)len; i++)
	{
		vtr<double> point(dims);
		for (size_t j = 0; j < (size_t)dims; j++)
		{
			point[j] = help::randomReal(min, max);
			ts[i] = (point);
		}
	}

	return ts;
}

///Initializes 1d vector.
///@param[in] size length of the vector
///@return 1d vector of specified size
template<class T>
vtr<T> help::vtrInit(size_t size)
{
	return vtr<T>(size);
}

///Initializes 2d vector.
///@param[in] size1 length of the vector
///@param[in] size2 length of the sub vectors
///@return 2d vector of specified sizes
template<class T>
vtr2<T> help::vtrInit(size_t size1, size_t size2)
{
	vtr2<T> tmp(size1);
	
	for (size_t i = 0; i < size1; i++)
	{
		tmp[i] = vtr<T>(size2);
	}

	return tmp;
}

///Initialize 2d vector by custom value.
///@param[in] m 2d matrix to be initialized
///@param[in] size1 length of the vector
///@param[in] size2 length of the sub vectors
///@param[in] value initialization value
///@return 2d vector of specified sizes and value
template<class T>
void help::vtrInit(vtr2<T> &m, size_t size1, size_t size2, T value)
{
	m.reserve(size1);
	for (size_t i = 0; i < size1; i++)
	{
		m[i] = vtr<T>(size2);
		std::fill(m[i].begin(), m[i].end(), value);
	}
}
template void help::vtrInit<float>(vtr2<float> &m, size_t size1, size_t size2, float value);

///Initializes 3d vector.
///@param[in] size1 length of the vector
///@param[in] size2 length of the sub vectors
///@param[in] size3 length of the sub sub vectors
///@return 3d vector of specified sizes
template<class T>
vtr3<T> help::vtrInit(size_t size1, size_t size2, size_t size3)
{
	vtr3<T> tmp(size1);
	
	for (size_t i = 0; i < size1; i++)
	{
		tmp[i] = help::vtrInit<T>(size2, size3);
	}

	return tmp;
}
template vtr3<int> help::vtrInit<int>(size_t size1, size_t size2, size_t size3);
template vtr3<double> help::vtrInit<double>(size_t size1, size_t size2, size_t size3);

///Initializes two dimensions of 3d vector.
///@param[in] size1 length of the vector
///@param[in] size2 length of the sub vectors
///@return 3d vector with initialized first 2 dimensions
template<class T>
vtr3<T> help::vtrInitPartial(size_t size1, size_t size2)
{
	vtr3<T> tmp(size1);
	
	for (size_t i = 0; i < size1; i++)
	{
		tmp[i] = vtr2<T>(size2);
	}

	return tmp;
}
template vtr3<double> help::vtrInitPartial<double>(size_t size1, size_t size2);

///Separates time series dimensions into separate time series.
///@param[in] input input time series
///@return set of separated time series
vtr3<double> help::separateSequences(vtr2<double> const &input)
{
	vtr3<double> output;

	const size_t dims = input[0].size();

	for (size_t i = 0; i < dims; i++)
	{
		vtr2<double> sequence;
		sequence.reserve(input.size());
		for (size_t j = 0; j < input.size(); j++)
		{
			vector<double> el(1);
			el[0] = input[j][i];
			sequence.emplace_back(el);
		}
		output.emplace_back(sequence);
	}

	return output;
}

///Converts array pointer to vector.
///@param[in] series time series in array
///@param[in] len time series length
///@return time series
vtr2<double> help::convertArrd(double* const &series, unsigned len)
{
	vtr2<double> out(len);
	for (size_t i = 0; i < len; i++)
	{
		vtr<double> point(1);
		point[0] = series[i];

		out[i] = point;
	}

	return out;
}

///Converts 2d array pointer to vector.
///@param[in] series time series in array
///@param[in] len time series length
///@param[in] dims number of dimensions
///@return n dimensional time series
vtr2<double> help::convertArr2d(double* const &series, unsigned len, unsigned dims)
{
	vtr2<double> out(len);
	for (size_t i = 0; i < len; i++)
	{
		vtr<double> point(&series[0] + (i * dims), &series[0] + ((i + 1) * dims));
		out[i] = point;
	}

	return out;
}

///Sorts vector and secondary vector follows sorting of the first vector
///@param[in] lead sorted vector
///@param[in] follow vector which follows sorting of the first vector
///@param[in] reversed if true: sorting direction will be reversed 
void help::sortFollow(vtr<double> &lead, vtr<int> &follow, bool reversed)
{
	for (size_t i = 0; i < lead.size() - 1; i++)
	{
		for (size_t j = 0; j < follow.size() - 1; j++)
		{
			if (reversed ? lead[j + 1] > lead[j] : lead[j + 1] < lead[j])
			{
				double tmp = lead[j];
				lead[j] = lead[j + 1];
				lead[j + 1] = tmp;

				int fTmp = follow[j];
				follow[j] = follow[j + 1];
				follow[j + 1] = fTmp;
			}
		}
	}
}

///Alters 3d matrix from [i][j][k] to [k][i][j] for easier manipulation with other dimension in some situations (used in operation 2).
///@param[in] matrix 3d vector for dimension reordering
///@return altered 3d matrix   
template<typename T>
vtr3<T> help::alterStructure(vtr3<T> const &matrix) 
{
	vtr3<T> m = help::vtrInit<T>(matrix[0][0].size(), matrix.size(), matrix[0].size());
	for (size_t i = 0; i < matrix.size(); i++)
	{
		for (size_t j = 0; j < matrix[0].size(); j++)
		{
			for (size_t k = 0; k < matrix[0][0].size(); k++)
			{
				m[k][i][j] = matrix[i][j][k];
			}
		}
	}

	return m;
}
template vtr3<double> help::alterStructure(vtr3<double> const &matrix);
template vtr3<int> help::alterStructure(vtr3<int> const &matrix);