Refactor PmergeMe class; implement template-based sorting and insertion methods, enhance timing output, and update VSCode settings

This commit is contained in:
whaffman 2025-09-04 16:18:25 +02:00
parent bfbbb4acdd
commit e696e2a994
5 changed files with 116 additions and 128 deletions

View File

@ -50,6 +50,7 @@
"typeinfo": "cpp", "typeinfo": "cpp",
"ctime": "cpp", "ctime": "cpp",
"iomanip": "cpp", "iomanip": "cpp",
"sstream": "cpp" "sstream": "cpp",
"*.tpp": "cpp"
} }
} }

View File

@ -14,10 +14,8 @@ public:
~PmergeMe(); ~PmergeMe();
PmergeMe &operator=(const PmergeMe &other); PmergeMe &operator=(const PmergeMe &other);
std::ostream &operator<<(std::ostream &os) const;
void sort(); void sort();
void sortVector(int level);
std::string getPrintableVector() const; std::string getPrintableVector() const;
std::string getPrintableDeque() const; std::string getPrintableDeque() const;
@ -29,7 +27,14 @@ private:
int _comparisons; int _comparisons;
bool areAllUnique() const; bool areAllUnique() const;
void insertVector(std::vector<int> &main, std::vector<int> &pend, int start_index, int right, int group_size);
template <typename Container>
void insert(Container &main, Container &pend, int start_index, int right, int group_size);
template <typename Container>
Container sort(Container &data, int level);
}; };
std::ostream &operator<<(std::ostream &os, const PmergeMe &obj); std::ostream &operator<<(std::ostream &os, const PmergeMe &obj);
#include "PmergeMe.tpp"

85
ex02/inc/PmergeMe.tpp Normal file
View File

@ -0,0 +1,85 @@
#include "PmergeMe.hpp"
template <typename Container>
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<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::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);
}

View File

@ -8,6 +8,7 @@
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <vector> #include <vector>
#include <chrono>
std::ostream &operator<<(std::ostream &os, const std::vector<int> &obj) std::ostream &operator<<(std::ostream &os, const std::vector<int> &obj)
{ {
@ -63,96 +64,28 @@ PmergeMe &PmergeMe::operator=(const PmergeMe &other)
return *this; return *this;
} }
std::ostream &PmergeMe::operator<<(std::ostream &os) const
{
os << "Vector: ";
for (std::vector<int>::const_iterator it = _data_vector.begin(); it != _data_vector.end(); ++it)
{
if (it != _data_vector.begin())
os << " ";
os << *it;
}
os << "\nDeque: ";
for (std::deque<int>::const_iterator it = _data_deque.begin(); it != _data_deque.end(); ++it)
{
if (it != _data_deque.begin())
os << " ";
os << *it;
}
return os;
}
void PmergeMe::sort() void PmergeMe::sort()
{ {
//high resolution timing
_comparisons = 0; _comparisons = 0;
sortVector(0); auto time_start = std::chrono::high_resolution_clock::now();
std::cout << "Total comparisons: " << _comparisons << std::endl; _data_vector = sort(_data_vector, 0);
std::cout << (std::is_sorted(_data_vector.begin(), _data_vector.end()) ? "Sorted!!!" : "ERROR!!") << std::endl; // std::cout << "Total comparisons: " << _comparisons << std::endl;
std::sort(_data_deque.begin(), _data_deque.end()); // 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_vector.size() << " elements with std::vector : " << time_diff << " us" << std::endl;
void PmergeMe::sortVector(int level = 0) _comparisons = 0;
{ auto time_start_deque = std::chrono::high_resolution_clock::now();
// #1 Create pairs and sort them _data_deque = sort(_data_deque, 0);
if (_data_vector.size() <= 1) // std::cout << "Total comparisons: " << _comparisons << std::endl;
return; // std::cout << (std::is_sorted(_data_deque.begin(), _data_deque.end()) ? "Sorted!!!" : "ERROR!!") << std::endl;
auto time_end_deque = std::chrono::high_resolution_clock::now();
auto time_diff_deque = std::chrono::duration_cast<std::chrono::microseconds>(time_end_deque - time_start_deque).count();
std::cout << "Time to process a range of " << _data_deque.size() << " elements with std::deque : " << time_diff_deque << " us" << std::endl;
size_t group_size = std::pow(2, level + 1);
int odd = _data_vector.size() % group_size;
std::vector<int> left = std::vector<int>(_data_vector.end() - odd, _data_vector.end());
_data_vector.erase(_data_vector.end() - odd, _data_vector.end());
for (size_t i = 0; i < _data_vector.size(); i += group_size)
{
if (!less(_data_vector[i + group_size / 2 - 1], _data_vector[i + group_size - 1]))
std::swap_ranges(
_data_vector.begin() + i, _data_vector.begin() + i + group_size / 2,
_data_vector.begin() + i + group_size / 2);
}
if (group_size < _data_vector.size())
sortVector(level + 1);
std::vector<int> main;
std::vector<int> pend;
// #3 Separate main and pend
group_size /= 2;
main.insert(main.end(), _data_vector.begin(), _data_vector.begin() + group_size); // inserting b1
auto it = _data_vector.begin() + group_size; // this is a1
while (it != _data_vector.end())
{
main.insert(main.end(), it, it + group_size);
it += group_size;
if (it == _data_vector.end())
break;
pend.insert(pend.end(), it, it + group_size);
it += group_size;
}
pend.insert(pend.end(), left.begin(), left.end());
int i = 0;
while (!pend.empty())
{
int sorted_in_main = std::pow(2, i + 2) - 1; // TODO ??
sorted_in_main = sorted_in_main > static_cast<int>(main.size() / group_size)
? static_cast<int>(main.size() / group_size)
: sorted_in_main;
int jacob_index = Jacobstahl().get(i + 3) - Jacobstahl().get(i + 2) - 1;
int start_index = std::min(jacob_index, static_cast<int>(pend.size() / group_size) - 1);
for (int j = start_index; j >= 0; --j)
{
insertVector(main, pend, j, sorted_in_main, group_size);
}
i++;
}
_data_vector = main;
} }
std::string PmergeMe::getPrintableVector() const std::string PmergeMe::getPrintableVector() const
@ -193,42 +126,6 @@ bool PmergeMe::areAllUnique() const
return it == temp.end(); return it == temp.end();
} }
void PmergeMe::insertVector(std::vector<int> &main, std::vector<int> &pend, int start_index, int right, int group_size)
{
if (pend.empty() || start_index < 0 || start_index >= static_cast<int>(pend.size()))
return;
// Use the last element in the group for binary search
int value_to_insert = pend[(start_index + 1) * group_size - 1];
std::cout
// << main << " | " << std::setw(3) << value_to_insert << " | " << pend << " | pend_index: " << start_index
<< " | main: " << main.size() / group_size << " | end: " << right << " | group_size: " << group_size
<< (!std::is_sorted(main.begin(), main.end()) && group_size == 1 ? "<<<" : "") << std::endl;
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() + start_index * group_size, pend.begin() + (start_index + 1) * group_size);
pend.erase(pend.begin() + start_index * group_size, pend.begin() + (start_index + 1) * group_size);
}
std::ostream &operator<<(std::ostream &os, const PmergeMe &obj) std::ostream &operator<<(std::ostream &os, const PmergeMe &obj)
{ {
os << "Vector: " << obj.getPrintableVector() << "\n"; os << "Vector: " << obj.getPrintableVector() << "\n";

View File

@ -8,9 +8,9 @@ int main(const int argc, const char **argv)
try try
{ {
PmergeMe sorter(argc, argv); PmergeMe sorter(argc, argv);
// std::cout << "Before sorting:\n" << sorter << std::endl; std::cout << "Before sorting:\n" << sorter << std::endl;
sorter.sort(); sorter.sort();
// std::cout << "After sorting:\n" << sorter << std::endl; std::cout << "After sorting:\n" << sorter << std::endl;
} }
catch (const std::invalid_argument &e) catch (const std::invalid_argument &e)
{ {