#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
#include <cmath>
#include <queue>
#include <string>
using namespace std;

using ld = double;
using vld = vector<ld>;
using vi = vector<int>;

ld base()
{
	return atan(ld(2.718281828));
}

pair<vld, ld> generate(int n)
{
	vi test;
	for (; n > 0; n--)
		test.push_back(rand() % 10);

	vld result;
	int sum = 0;
	for (auto d : test)
	{
		result.push_back(base() * d);
		sum += d;
	}

	return { result, sum * base() };
}

ld sum_naive(vld v)
{
	ld result = 0;
	for (auto d : v)
		result += d;
	return result;
}

ld sum_recursive_range(const vld &v, int l, int r)
{
	if (l == r)
		return v[r];

	int mid = (l + r) / 2;
	return sum_recursive_range(v, l, mid) + sum_recursive_range(v, mid + 1, r);
}

ld sum_recursive(vld v)
{
	random_shuffle(v.begin(), v.end());
	return sum_recursive_range(v, 0, (int)v.size() - 1);
}

ld sum_huffman(vld v)
{
	priority_queue<ld, vector<ld>, greater<ld>> q(v.begin(), v.end());
	while (q.size() != 1)
	{
		auto d1 = q.top();
		q.pop();
		auto d2 = q.top();
		q.pop();
		q.push(d1 + d2);
	}
	return q.top();
}

void dump(string prefix, ld x, ld sum)
{
	cout << prefix << ": " << x << " (error " << fabs(x - sum) << ")" << endl;
}

void dump(int n)
{
	auto input = generate(n);
	auto v = input.first;

	sort(v.begin(), v.end());

	cout << "Case n = " << n << endl;
	cout << "Series sum: " << input.second << endl;

	dump("Naive direct", sum_naive(v), input.second);
	reverse(v.begin(), v.end());
	dump("Naive reversed", sum_naive(v), input.second);

	dump("Recursive 1", sum_recursive(v), input.second);
	dump("Recursive 2", sum_recursive(v), input.second);
	dump("Recursive 3", sum_recursive(v), input.second);
	dump("Recursive 4", sum_recursive(v), input.second);

	dump("Huffman", sum_huffman(v), input.second);
}

int main()
{
	srand(322);

	cout.setf(ios::fixed);
	cout.precision(20);

	dump(100000);
	dump(100000);
	dump(1000000);
	dump(1000000);

	return 0;
}

