#include "PmergeMe.hpp"

PmergeMe::PmergeMe()
{
}

PmergeMe::PmergeMe(const PmergeMe &other)
{
	this->_vec = other._vec;
	this->_deq = other._deq;
}

PmergeMe &PmergeMe::operator=(const PmergeMe &other)
{
	this->_vec = other._vec;
	this->_deq = other._deq;
	return (*this);
}

PmergeMe::~PmergeMe()
{
}

void PmergeMe::printDeque() const
{
	for (std::deque<int>::const_iterator it = _deq.begin(); it != _deq.end(); ++it)
		std::cout << *it << " ";
	std::cout << std::endl;
}

void PmergeMe::printVector() const
{
	for (std::vector<int>::const_iterator it = _vec.begin(); it != _vec.end(); ++it)
		std::cout << *it << " ";
	std::cout << std::endl;
}

void PmergeMe::parseInput(int ac, char **av)
{
	std::string token;
	int n;

	for (int i = 1; i < ac; i++) {
		std::istringstream ss(av[i]);
		while (ss >> token)
		{
			std::istringstream iss(token);
			if (!(iss >> n) || !iss.eof() || n < 0)
				throw std::runtime_error("Error");
			if (std::find(_vec.begin(), _vec.end(), n) != _vec.end())
				throw std::runtime_error("Error");
			_vec.push_back(n);
			_deq.push_back(n);
		}
	}
	if (_vec.empty())
		throw std::runtime_error("Error");
}

void PmergeMe::run(int ac, char **av)
{
	std::cout << std::fixed << std::setprecision(5);
	parseInput(ac, av);
	std::cout << "Before: ";
	printVector();
	sortVector();
	sortDeque();
	std::cout << "After:  ";
	printVector();
	std::cout << "Time to process a range of " << _vec.size() << " elements with std::vector : " << _vecTime << " us" << std::endl;
  	std::cout << "Time to process a range of " << _deq.size() << " elements with std::deque  : " << _deqTime << " us" << std::endl;
}

void PmergeMe::sortVector()
{
	clock_t start = clock();
	_vec = fordJohnsonVec(_vec);
	clock_t end = clock();
	_vecTime = (double)(end - start) * 1e6 / CLOCKS_PER_SEC;
}

std::vector<std::pair<int, int> > makePairs(std::vector<int> v)
{
    std::vector<std::pair<int, int> > pairs;
	for (size_t i = 0; i + 1 < v.size(); i += 2)
	{
        int a = v[i];
        int b = v[i + 1];
        if (a >= b)
            pairs.push_back(std::make_pair(a, b));
        else
            pairs.push_back(std::make_pair(b, a));
    }
    return pairs;
}

std::vector<std::pair<int, int> > reorderPairs(std::vector<std::pair<int, int> > pairs, std::vector<int> winners)
{
    std::vector<std::pair<int, int> > reordered;
	for (size_t i = 0; i < winners.size(); i++)
	{
		for (size_t j = 0; j < pairs.size(); j++)
		{
			if (pairs[j].first == winners[i])
			{
				reordered.push_back(pairs[j]);
				break;
			}
		}
	}
    return reordered;
}

std::vector<size_t> generateInsertionOrder(std::vector<int> pend)
{
    std::vector<size_t> order;
    std::vector<size_t> jacob;
	jacob.push_back(0);
	jacob.push_back(1);
	while (jacob.back() <= pend.size())
		jacob.push_back(jacob.back() + 2 * jacob[jacob.size() - 2]);
	for (size_t k = 3; k < jacob.size(); k++)
	{
		size_t high = jacob[k] - 2;
		if (high >= pend.size()) 
			high = pend.size() - 1; 
		size_t low = jacob[k - 1] - 1;
		for (size_t idx = high + 1; idx > low; idx--)
			order.push_back(idx - 1);
    }
	return order;
}

std::vector<int> PmergeMe::fordJohnsonVec(std::vector<int> v)
{
	if (v.size() <= 1)
		return v;
    
	std::vector<std::pair<int, int> > pairs = makePairs(v);
	int straggler = 0;
    bool hasStraggler = false;
    if (v.size() % 2 != 0)
    {
        straggler = v.back();
        hasStraggler = true;
    }

	std::vector<int> winners;
	for (size_t i = 0; i < pairs.size(); i++)
		winners.push_back(pairs[i].first);
	winners = fordJohnsonVec(winners);
	pairs = reorderPairs(pairs, winners);

	std::vector<int> mainChain;
	mainChain.push_back(pairs[0].second);
	for (size_t i = 0; i < pairs.size(); i++)
	{
		mainChain.push_back(pairs[i].first);
	}

	std::vector<int> pend;
	for (size_t i = 1; i < pairs.size(); i++)
    {
		pend.push_back(pairs[i].second);
	}

    std::vector<size_t> order = generateInsertionOrder(pend);
	for (size_t i = 0; i < order.size(); i++)
	{
		int value = pend[order[i]];
		int aiValue = pairs[order[i] + 1].first;
		std::vector<int>::iterator aiPos = std::find(mainChain.begin(), mainChain.end(), aiValue);
		std::vector<int>::iterator insertPos = std::upper_bound(mainChain.begin(), aiPos, value);
		mainChain.insert(insertPos, value);
	}

	if (hasStraggler)
	{
		std::vector<int>::iterator insertPos = std::upper_bound(mainChain.begin(), mainChain.end(), straggler);
		mainChain.insert(insertPos, straggler);
	}
	return mainChain;
}

void PmergeMe::sortDeque()
{
	clock_t start = clock();
	_deq = fordJohnsonDeque(_deq);
	clock_t end = clock();
	_deqTime = (double)(end - start) * 1e6 / CLOCKS_PER_SEC;
}

std::deque<std::pair<int, int> > makePairs(std::deque<int> d)
{
    std::deque<std::pair<int, int> > pairs;
	for (size_t i = 0; i + 1 < d.size(); i += 2)
	{
        int a = d[i];
        int b = d[i + 1];
        if (a >= b)
            pairs.push_back(std::make_pair(a, b));
        else
            pairs.push_back(std::make_pair(b, a));
    }
    return pairs;
}

std::deque<std::pair<int, int> > reorderPairs(std::deque<std::pair<int, int> > pairs, std::deque<int> winners)
{
    std::deque<std::pair<int, int> > reordered;
	for (size_t i = 0; i < winners.size(); i++)
	{
		for (size_t j = 0; j < pairs.size(); j++)
		{
			if (pairs[j].first == winners[i])
			{
				reordered.push_back(pairs[j]);
				break;
			}
		}
	}
    return reordered;
}

std::deque<size_t> generateInsertionOrder(std::deque<int> pend)
{
    std::deque<size_t> order;
    std::deque<size_t> jacob;
	jacob.push_back(0);
	jacob.push_back(1);
	while (jacob.back() <= pend.size())
		jacob.push_back(jacob.back() + 2 * jacob[jacob.size() - 2]);
	for (size_t k = 3; k < jacob.size(); k++)
	{
		size_t high = jacob[k] - 2;
		if (high >= pend.size()) 
			high = pend.size() - 1; 
		size_t low = jacob[k - 1] - 1;
		for (size_t idx = high + 1; idx > low; idx--)
			order.push_back(idx - 1);
    }
	return order;
}

std::deque<int> PmergeMe::fordJohnsonDeque(std::deque<int> d)
{
	if (d.size() <= 1)
		return d;
    
	std::deque<std::pair<int, int> > pairs = makePairs(d);
	int straggler = 0;
    bool hasStraggler = false;
    if (d.size() % 2 != 0)
    {
        straggler = d.back();
        hasStraggler = true;
    }

	std::deque<int> winners;
	for (size_t i = 0; i < pairs.size(); i++)
		winners.push_back(pairs[i].first);
	winners = fordJohnsonDeque(winners);
	pairs = reorderPairs(pairs, winners);

	std::deque<int> mainChain;
	mainChain.push_back(pairs[0].second);
	for (size_t i = 0; i < pairs.size(); i++)
	{
		mainChain.push_back(pairs[i].first);
	}

	std::deque<int> pend;
	for (size_t i = 1; i < pairs.size(); i++)
    {
		pend.push_back(pairs[i].second);
	}

    std::deque<size_t> order = generateInsertionOrder(pend);
	for (size_t i = 0; i < order.size(); i++)
	{
		int value = pend[order[i]];
		int aiValue = pairs[order[i] + 1].first;
		std::deque<int>::iterator aiPos = std::find(mainChain.begin(), mainChain.end(), aiValue);
		std::deque<int>::iterator insertPos = std::upper_bound(mainChain.begin(), aiPos, value);
		mainChain.insert(insertPos, value);
	}

	if (hasStraggler)
	{
		std::deque<int>::iterator insertPos = std::upper_bound(mainChain.begin(), mainChain.end(), straggler);
		mainChain.insert(insertPos, straggler);
	}
	return mainChain;
}