#pragma once #include "PmergeMe.hpp" #include #include #include #include #include #include #include #include #include #include template std::string getTypeName() { if (std::is_same_v>) return "std::vector "; if (std::is_same_v>) return "std::deque "; return typeid(T).name(); // fallback to mangled name } template PmergeMe::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(argc) - 1 || _data.empty()) throw std::invalid_argument("Invalid input detected"); _jacobstahl_numbers = Jacobstahl().getUpTo(_data.size()); } template PmergeMe::PmergeMe(const PmergeMe &other) : _data(other._data), _jacobstahl_numbers(other._jacobstahl_numbers) { } template PmergeMe::~PmergeMe() {} template PmergeMe &PmergeMe::operator=(const PmergeMe &other) { if (this != &other) { _data = other._data; _jacobstahl_numbers = other._jacobstahl_numbers; } return *this; } template void PmergeMe::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(time_end - time_start).count(); std::cout << "Time to process a range of " << _data.size() << " elements with " << getTypeName() << ": " << time_diff << " us" << std::endl; } template std::string PmergeMe::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 int PmergeMe::getComparisons() const { return _comparisons; } template bool PmergeMe::less(int a, int b) { _comparisons++; return a < b; } template bool PmergeMe::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 Container PmergeMe::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(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(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 void PmergeMe::insert(Container &main, Container &pend, int to_insert, int right, int group_size) { if (pend.empty() || to_insert < 0 || to_insert >= static_cast(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(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 std::ostream &operator<<(std::ostream &os, const PmergeMe &obj) { os << obj.getString(); return os; }