/*
	This file is part of solidity.
	solidity is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	solidity is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	You should have received a copy of the GNU General Public License
	along with solidity.  If not, see .
*/
#include 
#include 
#include 
#include 
using namespace std;
using namespace solidity::phaser;
vector> RandomPairSelection::materialise(size_t _poolSize) const
{
	if (_poolSize < 2)
		return {};
	size_t count = static_cast(round(_poolSize * m_selectionSize));
	vector> selection;
	for (size_t i = 0; i < count; ++i)
	{
		size_t index1 = SimulationRNG::uniformInt(0, _poolSize - 1);
		size_t index2;
		do
		{
			index2 = SimulationRNG::uniformInt(0, _poolSize - 1);
		} while (index1 == index2);
		selection.emplace_back(index1, index2);
	}
	return selection;
}
vector> PairsFromRandomSubset::materialise(size_t _poolSize) const
{
	vector selectedIndices = RandomSubset(m_selectionChance).materialise(_poolSize);
	if (selectedIndices.size() % 2 != 0)
	{
		if (selectedIndices.size() < _poolSize && SimulationRNG::bernoulliTrial(0.5))
		{
			do
			{
				size_t extraIndex = SimulationRNG::uniformInt(0, selectedIndices.size() - 1);
				if (find(selectedIndices.begin(), selectedIndices.end(), extraIndex) == selectedIndices.end())
					selectedIndices.push_back(extraIndex);
			} while (selectedIndices.size() % 2 != 0);
		}
		else
			selectedIndices.erase(selectedIndices.begin() + SimulationRNG::uniformInt(0, selectedIndices.size() - 1));
	}
	assert(selectedIndices.size() % 2 == 0);
	vector> selectedPairs;
	for (size_t i = selectedIndices.size() / 2; i > 0; --i)
	{
		size_t position1 = SimulationRNG::uniformInt(0, selectedIndices.size() - 1);
		size_t value1 = selectedIndices[position1];
		selectedIndices.erase(selectedIndices.begin() + position1);
		size_t position2 = SimulationRNG::uniformInt(0, selectedIndices.size() - 1);
		size_t value2 = selectedIndices[position2];
		selectedIndices.erase(selectedIndices.begin() + position2);
		selectedPairs.push_back({value1, value2});
	}
	assert(selectedIndices.size() == 0);
	return selectedPairs;
}
vector> PairMosaicSelection::materialise(size_t _poolSize) const
{
	if (_poolSize < 2)
		return {};
	size_t count = static_cast(round(_poolSize * m_selectionSize));
	vector> selection;
	for (size_t i = 0; i < count; ++i)
	{
		tuple pair = m_pattern[i % m_pattern.size()];
		selection.emplace_back(min(get<0>(pair), _poolSize - 1), min(get<1>(pair), _poolSize - 1));
	}
	return selection;
}