#ifdef _MSC_VER
#pragma warning(disable:4996)
#endif

#include <iostream>
#include <chrono>
#include <ctime>
#include <string>
#include <sstream>

#define STDOUT(_what) std::cout << _what << "\n"


uint32_t fibo(uint32_t n)
{
	return n < 2
		? n
		: fibo(n - 1) + fibo(n - 2);
}


std::string str(const std::chrono::system_clock::time_point& t)
{
	auto asTimeT = std::chrono::system_clock::to_time_t(t);
	auto ans = std::ctime(&asTimeT);
	for(auto ch = ans; *ch; ch++)
	{
		if(*ch == '\n')
		{
			*ch = 0;
		}
	}
	return ans;
}

std::string strThreadSafe(const std::chrono::system_clock::time_point& t)
{
	auto asTimeT = std::chrono::system_clock::to_time_t(t);
	struct tm asTm;
	localtime_r(&asTm, &asTimeT); // gmtime_r for UTC, _s on windows
	char ans[32];
	std::strftime(ans, sizeof(ans), "%c", &asTm);
	return ans;
}


template<typename T>
std::string strS(const T& d)
{
	std::ostringstream ans;
	ans.precision(3);
	auto casted = std::chrono::duration_cast<std::chrono::milliseconds>(d);
	ans << std::fixed << (double(casted.count()) / 1000) << "s";
	return ans.str();
}

template<typename T>
std::string strMS(const T& d)
{
	std::ostringstream ans;
	ans.precision(3);
	auto casted = std::chrono::duration_cast<std::chrono::microseconds>(d);
	ans << std::fixed << (double(casted.count()) / 1000) << "ms";
	return ans.str();
}

template<typename T>
std::string strUS(const T& d)
{
	std::ostringstream ans;
	ans.precision(3);
	auto casted = std::chrono::duration_cast<std::chrono::nanoseconds>(d);
	ans << std::fixed << (double(casted.count()) / 1000) << "us";
	return ans.str();
}

template<typename T>
std::string strNS(const T& d)
{
	std::ostringstream ans;
	auto casted = std::chrono::duration_cast<std::chrono::nanoseconds>(d);
	ans << std::fixed << casted.count() << "ns";
	return ans.str();
}


std::string strS(const std::chrono::steady_clock::time_point& t)
{
	return strS(t.time_since_epoch());
}

std::string strMS(const std::chrono::steady_clock::time_point& t)
{
	return strMS(t.time_since_epoch());
}

std::string strUS(const std::chrono::steady_clock::time_point& t)
{
	return strUS(t.time_since_epoch());
}

std::string strNS(const std::chrono::steady_clock::time_point& t)
{
	return strNS(t.time_since_epoch());
}

std::string str(const std::chrono::steady_clock::time_point& t)
{
	return strMS(t.time_since_epoch());
}


int main()
{
	auto start = std::chrono::system_clock::now();
	STDOUT("started at " << str(start));

	STDOUT("fibo(41) = " << fibo(41));

	auto end = std::chrono::system_clock::now();
	STDOUT("finished at " << str(end));

	auto elapsed = end - start;
	STDOUT("elapsed:    " << strS(end - start));
	STDOUT("elapsed:    " << strMS(end - start));
	STDOUT("elapsed:    " << strUS(end - start));
	STDOUT("elapsed:    " << strNS(end - start));
}