220 lines
5.8 KiB
C++
220 lines
5.8 KiB
C++
#pragma once
|
|
|
|
#include "PmergeMe.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstring>
|
|
#include <deque>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <chrono>
|
|
|
|
template <typename T>
|
|
std::string getTypeName()
|
|
{
|
|
if (std::is_same_v<T, std::vector<int>>)
|
|
return "std::vector<int> ";
|
|
if (std::is_same_v<T, std::deque<int>>)
|
|
return "std::deque<int> ";
|
|
return typeid(T).name(); // fallback to mangled name
|
|
}
|
|
|
|
template <typename Container>
|
|
PmergeMe<Container>::PmergeMe(const int argc, const char **argv) : _comparisons(0)
|
|
{
|
|
for (int i = 1; argv[i] != nullptr; ++i)
|
|
{
|
|
size_t pos;
|
|
try
|
|
{
|
|
int value = std::stoi(argv[i], &pos);
|
|
if (value < 0)
|
|
throw std::invalid_argument("All inputs must be non-negative integers");
|
|
if (pos != std::strlen(argv[i]))
|
|
throw std::invalid_argument("Invalid input: not a valid integer");
|
|
|
|
_data.push_back(value);
|
|
}
|
|
catch (const std::out_of_range &e)
|
|
{
|
|
throw std::out_of_range("Input value out of range");
|
|
}
|
|
catch (const std::invalid_argument &e)
|
|
{
|
|
throw std::invalid_argument("Invalid input: not a valid integer");
|
|
}
|
|
}
|
|
|
|
if (!areAllUnique())
|
|
throw std::invalid_argument("All inputs must be unique integers");
|
|
|
|
if (_data.size() != static_cast<std::size_t>(argc) - 1 || _data.empty())
|
|
throw std::invalid_argument("Invalid input detected");
|
|
|
|
_jacobstahl_numbers = Jacobstahl().getUpTo(_data.size());
|
|
}
|
|
|
|
template <typename Container>
|
|
PmergeMe<Container>::PmergeMe(const PmergeMe<Container> &other)
|
|
: _data(other._data), _jacobstahl_numbers(other._jacobstahl_numbers)
|
|
{
|
|
}
|
|
|
|
template <typename Container>
|
|
PmergeMe<Container>::~PmergeMe() {}
|
|
|
|
template <typename Container>
|
|
PmergeMe<Container> &PmergeMe<Container>::operator=(const PmergeMe<Container> &other)
|
|
{
|
|
if (this != &other)
|
|
{
|
|
_data = other._data;
|
|
_jacobstahl_numbers = other._jacobstahl_numbers;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <typename Container>
|
|
void PmergeMe<Container>::sort()
|
|
{
|
|
|
|
// high resolution timing
|
|
_comparisons = 0;
|
|
auto time_start = std::chrono::high_resolution_clock::now();
|
|
_data = sort(_data, 0);
|
|
// std::cout << "Total comparisons: " << _comparisons << std::endl;
|
|
// std::cout << (std::is_sorted(_data_vector.begin(), _data_vector.end()) ? "Sorted!!!" : "ERROR!!") << std::endl;
|
|
auto time_end = std::chrono::high_resolution_clock::now();
|
|
auto time_diff = std::chrono::duration_cast<std::chrono::microseconds>(time_end - time_start).count();
|
|
std::cout << "Time to process a range of " << _data.size() << " elements with " << getTypeName<Container>() << ": " << time_diff << " us" << std::endl;
|
|
}
|
|
|
|
template <typename Container>
|
|
std::string PmergeMe<Container>::getString() const
|
|
{
|
|
std::string result;
|
|
for (auto it = _data.begin(); it != _data.end(); ++it)
|
|
{
|
|
if (it != _data.begin())
|
|
result += ", ";
|
|
result += std::to_string(*it);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename Container>
|
|
int PmergeMe<Container>::getComparisons() const
|
|
{
|
|
return _comparisons;
|
|
}
|
|
|
|
template <typename Container>
|
|
bool PmergeMe<Container>::less(int a, int b)
|
|
{
|
|
_comparisons++;
|
|
return a < b;
|
|
}
|
|
template <typename Container>
|
|
bool PmergeMe<Container>::areAllUnique() const
|
|
{
|
|
Container temp = _data; // Create a copy
|
|
std::sort(temp.begin(), temp.end());
|
|
auto it = std::unique(temp.begin(), temp.end());
|
|
return it == temp.end();
|
|
}
|
|
|
|
template <typename Container>
|
|
Container PmergeMe<Container>::sort(Container &data, int level)
|
|
{
|
|
// #1 Create pairs and sort them
|
|
if (data.size() <= 1)
|
|
return data;
|
|
|
|
size_t group_size = 1 << level;
|
|
|
|
int odd = data.size() % (2 * group_size);
|
|
|
|
Container straggler = Container(data.end() - odd, data.end());
|
|
|
|
data.erase(data.end() - odd, data.end());
|
|
|
|
for (size_t i = 0; i < data.size(); i += (2 * group_size))
|
|
{
|
|
if (!less(data[i + group_size - 1], data[i + 2 * group_size - 1]))
|
|
std::swap_ranges(
|
|
data.begin() + i, data.begin() + i + group_size,
|
|
data.begin() + i + group_size);
|
|
}
|
|
|
|
if (2 * group_size < data.size())
|
|
data = sort(data, level + 1);
|
|
|
|
Container main;
|
|
Container pend;
|
|
|
|
// #3 Separate main and pend
|
|
main.insert(main.end(), data.begin(), data.begin() + group_size); // inserting b1
|
|
auto it = data.begin() + group_size; // this is a1
|
|
while (it != data.end())
|
|
{
|
|
main.insert(main.end(), it, it + group_size);
|
|
it += group_size;
|
|
if (it == data.end())
|
|
break;
|
|
pend.insert(pend.end(), it, it + group_size);
|
|
it += group_size;
|
|
}
|
|
pend.insert(pend.end(), straggler.begin(), straggler.end());
|
|
|
|
int i = 0;
|
|
while (!pend.empty())
|
|
{
|
|
int search_window = std::min((1 << (i + 2)) - 1, static_cast<int>(main.size() / group_size));
|
|
|
|
int jacob_index = Jacobstahl().get(i + 3) - Jacobstahl().get(i + 2) - 1;
|
|
int first_to_insert = std::min(jacob_index, static_cast<int>(pend.size() / group_size) - 1);
|
|
|
|
for (int to_insert = first_to_insert; to_insert >= 0; --to_insert)
|
|
insert(main, pend, to_insert, search_window, group_size);
|
|
|
|
i++;
|
|
}
|
|
return main;
|
|
}
|
|
|
|
template <typename Container>
|
|
void PmergeMe<Container>::insert(Container &main, Container &pend, int to_insert, int right, int group_size)
|
|
{
|
|
if (pend.empty() || to_insert < 0 || to_insert >= static_cast<int>(pend.size()))
|
|
return;
|
|
|
|
int value_to_insert = pend[(to_insert + 1) * group_size - 1];
|
|
auto insert_pos = main.begin() + right * group_size;
|
|
|
|
int left = 0;
|
|
while (left < right)
|
|
{
|
|
int mid = left + (right - left) / 2;
|
|
int last_idx = std::min((mid + 1) * group_size, static_cast<int>(main.size())) - 1;
|
|
if (less(main[last_idx], value_to_insert))
|
|
left = mid + 1;
|
|
else
|
|
right = mid;
|
|
}
|
|
insert_pos = main.begin() + (left * group_size);
|
|
|
|
main.insert(insert_pos, pend.begin() + to_insert * group_size, pend.begin() + (to_insert + 1) * group_size);
|
|
pend.erase(pend.begin() + to_insert * group_size, pend.begin() + (to_insert + 1) * group_size);
|
|
}
|
|
template <typename Container>
|
|
std::ostream &operator<<(std::ostream &os, const PmergeMe<Container> &obj)
|
|
{
|
|
os << obj.getString();
|
|
|
|
return os;
|
|
}
|