diff --git a/tasks/perepelkin_i_convex_hull_graham_scan/stl/include/ops_stl.hpp b/tasks/perepelkin_i_convex_hull_graham_scan/stl/include/ops_stl.hpp new file mode 100644 index 000000000..12dddf507 --- /dev/null +++ b/tasks/perepelkin_i_convex_hull_graham_scan/stl/include/ops_stl.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +#include "perepelkin_i_convex_hull_graham_scan/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace perepelkin_i_convex_hull_graham_scan { + +class PerepelkinIConvexHullGrahamScanSTL : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSTL; + } + explicit PerepelkinIConvexHullGrahamScanSTL(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + static size_t FindPivotParallel(const std::vector> &pts); + static void ParallelSort(std::vector> &data, const std::pair &pivot); + static void DataPartitioning(size_t total_size, const int &threads, std::vector &start); + + static void HullConstruction(std::vector> &hull, + const std::vector> &pts, + const std::pair &pivot); + static bool AngleCmp(const std::pair &a, const std::pair &b, + const std::pair &pivot); + static double Orientation(const std::pair &p, const std::pair &q, + const std::pair &r); +}; + +} // namespace perepelkin_i_convex_hull_graham_scan diff --git a/tasks/perepelkin_i_convex_hull_graham_scan/stl/src/ops_stl.cpp b/tasks/perepelkin_i_convex_hull_graham_scan/stl/src/ops_stl.cpp new file mode 100644 index 000000000..1c8e39fad --- /dev/null +++ b/tasks/perepelkin_i_convex_hull_graham_scan/stl/src/ops_stl.cpp @@ -0,0 +1,212 @@ +#include "perepelkin_i_convex_hull_graham_scan/stl/include/ops_stl.hpp" + +#include +#include +#include +#include +#include +#include + +#include "perepelkin_i_convex_hull_graham_scan/common/include/common.hpp" +#include "util/include/util.hpp" + +namespace perepelkin_i_convex_hull_graham_scan { + +PerepelkinIConvexHullGrahamScanSTL::PerepelkinIConvexHullGrahamScanSTL(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = std::vector>(); +} + +bool PerepelkinIConvexHullGrahamScanSTL::ValidationImpl() { + return GetOutput().empty(); +} + +bool PerepelkinIConvexHullGrahamScanSTL::PreProcessingImpl() { + return true; +} + +bool PerepelkinIConvexHullGrahamScanSTL::RunImpl() { + const auto &data = GetInput(); + + if (data.size() < 2) { + GetOutput() = data; + return true; + } + + std::vector> pts = data; + + // Find pivot + size_t pivot_idx = FindPivotParallel(pts); + + std::pair pivot = pts[pivot_idx]; + pts.erase(pts.begin() + static_cast(pivot_idx)); + + // Parallel sorting + ParallelSort(pts, pivot); + + // Sequential hull construction + std::vector> hull; + HullConstruction(hull, pts, pivot); + + GetOutput() = std::move(hull); + return true; +} + +size_t PerepelkinIConvexHullGrahamScanSTL::FindPivotParallel(const std::vector> &pts) { + const int threads = std::min(ppc::util::GetNumThreads(), static_cast(pts.size())); + + // Partitioning + std::vector start(threads + 1); + DataPartitioning(pts.size(), threads, start); + + // Parallel search + std::vector local_idx(threads, 0); + { + std::vector workers(threads); + + for (int tid = 0; tid < threads; tid++) { + workers.emplace_back([&, tid]() { + size_t begin = start[tid]; + size_t end = start[tid + 1]; + + size_t local = begin; + + for (size_t i = begin + 1; i < end; i++) { + if (pts[i].second < pts[local].second || + (pts[i].second == pts[local].second && pts[i].first < pts[local].first)) { + local = i; + } + } + + local_idx[tid] = local; + }); + } + } + + // Sequential reduction + size_t pivot_idx = 0; + + for (int tid = 0; tid < threads; tid++) { + size_t i = local_idx[tid]; + + if (pts[i].second < pts[pivot_idx].second || + (pts[i].second == pts[pivot_idx].second && pts[i].first < pts[pivot_idx].first)) { + pivot_idx = i; + } + } + + return pivot_idx; +} + +void PerepelkinIConvexHullGrahamScanSTL::ParallelSort(std::vector> &data, + const std::pair &pivot) { + const int threads = std::min(ppc::util::GetNumThreads(), static_cast(data.size())); + + // Partitioning + std::vector start(threads + 1); + DataPartitioning(data.size(), threads, start); + + // Parallel local sorting + { + std::vector workers(threads); + + for (int tid = 0; tid < threads; tid++) { + workers.emplace_back([&, tid]() { + std::sort(data.begin() + start[tid], data.begin() + start[tid + 1], + [&](const auto &a, const auto &b) { return AngleCmp(a, b, pivot); }); + }); + } + } + + // Merge sorted segments + for (int size = 1; size < threads; size *= 2) { + std::vector workers(threads); + + for (int i = 0; i < threads; i += 2 * size) { + if (i + size >= threads) { + continue; + } + + workers.emplace_back([&, i, size]() { + int left = start[i]; + int mid = start[i + size]; + int right = start[std::min(i + (2 * size), threads)]; + + std::inplace_merge(data.begin() + left, data.begin() + mid, data.begin() + right, + [&](const auto &a, const auto &b) { return AngleCmp(a, b, pivot); }); + }); + } + } +} + +void PerepelkinIConvexHullGrahamScanSTL::DataPartitioning(size_t total_size, const int &threads, + std::vector &start) { + size_t base = total_size / threads; + size_t rem = total_size % threads; + + size_t offset = 0; + + for (int i = 0; i < threads; i++) { + start[i] = static_cast(offset); + + size_t extra = std::cmp_less(i, rem) ? 1 : 0; + offset += base + extra; + } + + start[threads] = static_cast(total_size); +} + +void PerepelkinIConvexHullGrahamScanSTL::HullConstruction(std::vector> &hull, + const std::vector> &pts, + const std::pair &pivot) { + hull.reserve(pts.size() + 1); + + hull.push_back(pivot); + hull.push_back(pts[0]); + + for (size_t i = 1; i < pts.size(); i++) { + while (hull.size() >= 2 && Orientation(hull[hull.size() - 2], hull[hull.size() - 1], pts[i]) <= 0) { + hull.pop_back(); + } + + hull.push_back(pts[i]); + } +} + +double PerepelkinIConvexHullGrahamScanSTL::Orientation(const std::pair &p, + const std::pair &q, + const std::pair &r) { + double val = ((q.first - p.first) * (r.second - p.second)) - ((q.second - p.second) * (r.first - p.first)); + + if (std::abs(val) < 1e-9) { + return 0.0; + } + + return val; +} + +bool PerepelkinIConvexHullGrahamScanSTL::AngleCmp(const std::pair &a, + const std::pair &b, + const std::pair &pivot) { + double dx1 = a.first - pivot.first; + double dy1 = a.second - pivot.second; + double dx2 = b.first - pivot.first; + double dy2 = b.second - pivot.second; + + double cross = (dx1 * dy2) - (dy1 * dx2); + + if (std::abs(cross) < 1e-9) { + double dist1 = (dx1 * dx1) + (dy1 * dy1); + double dist2 = (dx2 * dx2) + (dy2 * dy2); + return dist1 < dist2; + } + + return cross > 0; +} + +bool PerepelkinIConvexHullGrahamScanSTL::PostProcessingImpl() { + return true; +} + +} // namespace perepelkin_i_convex_hull_graham_scan diff --git a/tasks/perepelkin_i_convex_hull_graham_scan/tests/functional/main.cpp b/tasks/perepelkin_i_convex_hull_graham_scan/tests/functional/main.cpp index 8d96bf950..82ffcae9c 100644 --- a/tasks/perepelkin_i_convex_hull_graham_scan/tests/functional/main.cpp +++ b/tasks/perepelkin_i_convex_hull_graham_scan/tests/functional/main.cpp @@ -9,6 +9,7 @@ #include "perepelkin_i_convex_hull_graham_scan/common/include/common.hpp" #include "perepelkin_i_convex_hull_graham_scan/omp/include/ops_omp.hpp" #include "perepelkin_i_convex_hull_graham_scan/seq/include/ops_seq.hpp" +#include "perepelkin_i_convex_hull_graham_scan/stl/include/ops_stl.hpp" #include "perepelkin_i_convex_hull_graham_scan/tbb/include/ops_tbb.hpp" #include "util/include/func_test_util.hpp" #include "util/include/util.hpp" @@ -87,6 +88,8 @@ const auto kTestTasksList = std::tuple_cat(ppc::util::AddFuncTask( kTestParam, PPC_SETTINGS_perepelkin_i_convex_hull_graham_scan), ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_perepelkin_i_convex_hull_graham_scan), + ppc::util::AddFuncTask( kTestParam, PPC_SETTINGS_perepelkin_i_convex_hull_graham_scan)); const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); diff --git a/tasks/perepelkin_i_convex_hull_graham_scan/tests/performance/main.cpp b/tasks/perepelkin_i_convex_hull_graham_scan/tests/performance/main.cpp index bda78bac9..469e65ccf 100644 --- a/tasks/perepelkin_i_convex_hull_graham_scan/tests/performance/main.cpp +++ b/tasks/perepelkin_i_convex_hull_graham_scan/tests/performance/main.cpp @@ -12,6 +12,7 @@ #include "perepelkin_i_convex_hull_graham_scan/common/include/common.hpp" #include "perepelkin_i_convex_hull_graham_scan/omp/include/ops_omp.hpp" #include "perepelkin_i_convex_hull_graham_scan/seq/include/ops_seq.hpp" +#include "perepelkin_i_convex_hull_graham_scan/stl/include/ops_stl.hpp" #include "perepelkin_i_convex_hull_graham_scan/tbb/include/ops_tbb.hpp" #include "task/include/task.hpp" #include "util/include/perf_test_util.hpp" @@ -78,6 +79,8 @@ const auto kAllPerfTasks = std::tuple_cat(ppc::util::MakeAllPerfTasks( PPC_SETTINGS_perepelkin_i_convex_hull_graham_scan), ppc::util::MakeAllPerfTasks( + PPC_SETTINGS_perepelkin_i_convex_hull_graham_scan), + ppc::util::MakeAllPerfTasks( PPC_SETTINGS_perepelkin_i_convex_hull_graham_scan)); const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks);