From ca822bdcb2288d13cf8f1904e559ecf6acb5a94b Mon Sep 17 00:00:00 2001 From: runfme Date: Fri, 16 Nov 2018 19:21:36 +0500 Subject: [PATCH 1/3] Initial commit --- CMakeLists.txt | 26 +++ cmake/googletest-download.cmake | 20 +++ cmake/googletest.cmake | 32 ++++ src/BinaryHeap/BinaryHeap.hpp | 269 ++++++++++++++++++++++++++++++++ src/BinaryHeap/CMakeLists.txt | 16 ++ src/BinaryHeap/Vector.hpp | 75 +++++++++ test/CMakeLists.txt | 20 +++ test/binary_heap_test.cpp | 151 ++++++++++++++++++ test/main.cpp | 11 ++ test/vector_test.cpp | 71 +++++++++ 10 files changed, 691 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 cmake/googletest-download.cmake create mode 100644 cmake/googletest.cmake create mode 100644 src/BinaryHeap/BinaryHeap.hpp create mode 100644 src/BinaryHeap/CMakeLists.txt create mode 100644 src/BinaryHeap/Vector.hpp create mode 100644 test/CMakeLists.txt create mode 100644 test/binary_heap_test.cpp create mode 100644 test/main.cpp create mode 100644 test/vector_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..41d22f1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +project(heaps CXX) +set(CMAKE_CXX_STANDARD 17) + +include(GNUInstallDirs) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}) + +# we use this to get code coverage +if(CMAKE_CXX_COMPILER_ID MATCHES GNU) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") +endif() + +add_subdirectory(src/BinaryHeap) + +include(cmake/googletest.cmake) +fetch_googletest( + ${PROJECT_SOURCE_DIR}/cmake + ${PROJECT_BINARY_DIR}/googletest +) + +enable_testing() +add_subdirectory(test) + diff --git a/cmake/googletest-download.cmake b/cmake/googletest-download.cmake new file mode 100644 index 0000000..f580295 --- /dev/null +++ b/cmake/googletest-download.cmake @@ -0,0 +1,20 @@ +# code copied from https://crascit.com/2015/07/25/cmake-gtest/ +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +project(googletest-download NONE) + +include(ExternalProject) + +ExternalProject_Add( + googletest + SOURCE_DIR "@GOOGLETEST_DOWNLOAD_ROOT@/googletest-src" + BINARY_DIR "@GOOGLETEST_DOWNLOAD_ROOT@/googletest-build" + GIT_REPOSITORY + https://github.com/google/googletest.git + GIT_TAG + release-1.8.0 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/cmake/googletest.cmake b/cmake/googletest.cmake new file mode 100644 index 0000000..de0c3bb --- /dev/null +++ b/cmake/googletest.cmake @@ -0,0 +1,32 @@ +# the following code to fetch googletest +# is inspired by and adapted after https://crascit.com/2015/07/25/cmake-gtest/ +# download and unpack googletest at configure time + +macro(fetch_googletest _download_module_path _download_root) + set(GOOGLETEST_DOWNLOAD_ROOT ${_download_root}) + configure_file( + ${_download_module_path}/googletest-download.cmake + ${_download_root}/CMakeLists.txt + @ONLY + ) + unset(GOOGLETEST_DOWNLOAD_ROOT) + + execute_process( + COMMAND + "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" . + WORKING_DIRECTORY + ${_download_root} + ) + execute_process( + COMMAND + "${CMAKE_COMMAND}" --build . + WORKING_DIRECTORY + ${_download_root} + ) + + # adds the targers: gtest, gtest_main, gmock, gmock_main + add_subdirectory( + ${_download_root}/googletest-src + ${_download_root}/googletest-build + ) +endmacro() diff --git a/src/BinaryHeap/BinaryHeap.hpp b/src/BinaryHeap/BinaryHeap.hpp new file mode 100644 index 0000000..078eaf1 --- /dev/null +++ b/src/BinaryHeap/BinaryHeap.hpp @@ -0,0 +1,269 @@ +// +// Created by not sashka on 19.10.18. +// + +#ifndef BINARY_HEAP_BINARYHEAP_H +#define BINARY_HEAP_BINARYHEAP_H + +#include +#include +#include +#include +#include +#include "Vector.hpp" + +template +class BinaryHeap { + class Pointer{ + friend class BinaryHeap; + public: + Pointer(unsigned long index, BinaryHeap* parent_heap); + + T value(); + + bool is_valid(); + + BinaryHeap* get_parent_heap(); + + private: + BinaryHeap* parent_heap; + unsigned long index; + bool valid; + }; + public: + + bool empty(); + + size_t size(); + + std::shared_ptr insert(T value); + + T min(); + + T extract_min(); + + T extract(std::shared_ptr pointer); + + void change(std::shared_ptr pointer, T new_value); + + BinaryHeap(); + + template + BinaryHeap(Iterator begin, Iterator end); + + void optimize(size_t insert_count, size_t extract_count) { + + } + + private: + struct Element { + T value; + std::shared_ptr::Pointer> pointer; + }; + + unsigned long tree_degree = 2; + Vector storage; + + void swap_elements(unsigned long first_index, unsigned long second_index); + + unsigned long parent(unsigned long index); + + unsigned long nth_child(unsigned long index, unsigned long n); + + void sift_up(unsigned long index); + + void sift_down(unsigned long index); + + std::shared_ptr push_back_element(T value); +}; + +template +BinaryHeap::BinaryHeap() = default; + +template +template +BinaryHeap::BinaryHeap(Iterator begin, Iterator end) { + for (Iterator iter = begin; iter != end ; ++iter) { + push_back_element(*iter); + } + + //call sift_down for all non leaves + for (unsigned long i = size()/2; i > 0; --i) { + sift_down(i-1); + } +} + +template +size_t BinaryHeap::size() { + return storage.size(); +} + +template +bool BinaryHeap::empty() { + return storage.empty(); +} + +template +std::shared_ptr::Pointer> BinaryHeap::insert(T value) { + auto pointer = push_back_element(value); + sift_up(pointer->index); + return pointer; +} + +template +T BinaryHeap::min() { + if (empty()) { + throw std::runtime_error ("No minimal element. Heap is empty."); + } + return storage[0].value; +} + +template +T BinaryHeap::extract(std::shared_ptr pointer) { + if (!pointer->is_valid()) { + throw std::runtime_error ("Invalidated pointer to an element"); + } + if (pointer->get_parent_heap() != this) { + throw std::runtime_error ("Pointer from another heap"); + } + + unsigned long extract_index = pointer->index; + T value = pointer->value(); + + //try to swap extracted with last element and sift it down + if (extract_index == size() - 1) { + storage.pop_back(); + } else { + swap_elements(extract_index, size()-1); + storage.pop_back(); + sift_down(extract_index); + } + + pointer->valid = false; + return value; +} + +template +T BinaryHeap::extract_min() { + if (empty()) { + throw std::runtime_error ("No minimal element. Heap is empty."); + } + return extract(storage[0].pointer); +} + +template +void BinaryHeap::change(std::shared_ptr pointer, T new_value) { + if (!pointer->is_valid()) { + throw std::runtime_error ("Invalidated pointer to an element"); + } + if (pointer->get_parent_heap() != this) { + throw std::runtime_error ("Pointer from another heap"); + } + + T previous_value = pointer->value(); + storage[pointer->index].value = new_value; + + if (new_value > previous_value) { + sift_down(pointer->index); + } else { + sift_up(pointer->index); + } +} + +template +void BinaryHeap::swap_elements(unsigned long first_index, unsigned long second_index) { + if (first_index >= size() || second_index>= size()) { + throw std::out_of_range ("At least one of indexes is out of range"); + } + std::swap(storage[first_index].value, storage[second_index].value); + std::swap(storage[first_index].pointer->index, storage[second_index].pointer->index); + std::swap(storage[first_index].pointer, storage[second_index].pointer); +} + +template +std::shared_ptr::Pointer> BinaryHeap::push_back_element(T value) { + unsigned long new_index = storage.size(); + std::shared_ptr pointer(new Pointer(new_index, this)); + storage.push_back({value, pointer}); + + return pointer; +} + +template +void BinaryHeap::sift_down(unsigned long index) { + if (index >= size()) { + throw std::out_of_range ("Index is out of range"); + } + unsigned long min_index = index; + + for (unsigned long i = 0; i < tree_degree; ++i) { + unsigned long child_index = nth_child(index, i); + if (child_index < size() && storage[child_index].value < storage[min_index].value) { + min_index = child_index; + } + } + + if (min_index != index) { + swap_elements(min_index, index); + sift_down(min_index); + } +} + +template +void BinaryHeap::sift_up(unsigned long index) { + if (index >= size()) { + throw std::out_of_range ("Index is out of range"); + } + unsigned long current = index; + + while (current != 0) { + unsigned long current_parent = parent(current); + if (storage[current].value < storage[current_parent].value) { + swap_elements(current, current_parent); + } + current = current_parent; + } +} + +template +unsigned long BinaryHeap::nth_child(unsigned long index, unsigned long n) { + if (index >= size()) { + throw std::out_of_range ("Index is out of range"); + } + return index * tree_degree + n + 1; +} + +template +unsigned long BinaryHeap::parent(unsigned long index) { + if (index == 0) { + throw std::runtime_error ("Root node has no parent"); + } + if (index >= size()) { + throw std::out_of_range ("Index is out of range"); + } + return (index - 1) / tree_degree; +} + +template +bool BinaryHeap::Pointer::is_valid() { + return valid; +} + +template +T BinaryHeap::Pointer::value() { + return parent_heap->storage[index].value; +} + +template +BinaryHeap::Pointer::Pointer(unsigned long index, BinaryHeap* const parent_heap) { + this->index = index; + this->parent_heap = parent_heap; + this->valid = true; +} + +template +BinaryHeap* BinaryHeap::Pointer::get_parent_heap() { + return parent_heap; +} + +#endif //BINARY_HEAP_BINARYHEAP_H diff --git a/src/BinaryHeap/CMakeLists.txt b/src/BinaryHeap/CMakeLists.txt new file mode 100644 index 0000000..34d4f6f --- /dev/null +++ b/src/BinaryHeap/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(binaryHeap) + +target_sources( + binaryHeap + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/BinaryHeap.hpp + ${CMAKE_CURRENT_LIST_DIR}/Vector.hpp +) + +target_include_directories( + binaryHeap + PUBLIC + ${CMAKE_CURRENT_LIST_DIR} +) + +set_target_properties(binaryHeap PROPERTIES LINKER_LANGUAGE CXX) \ No newline at end of file diff --git a/src/BinaryHeap/Vector.hpp b/src/BinaryHeap/Vector.hpp new file mode 100644 index 0000000..867b950 --- /dev/null +++ b/src/BinaryHeap/Vector.hpp @@ -0,0 +1,75 @@ +// +// Created by runfme on 13.11.18. +// +#ifndef VECTOR_H +#define VECTOR_H + +#include +template +class Vector { + public: + Vector() { + storage_size = 1; + size_ = 0; + storage = new T[storage_size]; + } + + void push_back(T value) { + storage[size_] = value; + ++size_; + adjust_storage(); + } + + void pop_back() { + --size_; + adjust_storage(); + } + + unsigned long size() { + return size_; + } + + bool empty() { + return size_ == 0; + } + + T& operator[](unsigned long index) { + if (index >= size_) { + throw std::out_of_range ("Index is out of range"); + } + return storage[index]; + } + + unsigned long get_storage_size() { + return storage_size; + } + + ~Vector() { + delete[] storage; + } + + private: + T* storage; + unsigned long storage_size; + unsigned long size_; + + void set_new_storage(unsigned long new_size) { + T* new_storage = new T[storage_size * 2]; + for (int i = 0; i < size_; ++i) { + new_storage[i] = storage[i]; + } + delete[] storage; + storage = new_storage; + storage_size = new_size; + } + + void adjust_storage() { + if (size_ >= storage_size) { + set_new_storage(storage_size * 2); + } else if (size_ != 0 && size_ * 4 <= storage_size) { + set_new_storage(storage_size / 2); + } + } +}; + +#endif \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..ee9b79d --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.9) + +add_executable( + unit_tests + binary_heap_test.cpp + vector_test.cpp + main.cpp) + +target_link_libraries( + unit_tests + gtest_main + binaryHeap +) + +add_test( + NAME + unit + COMMAND + ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}/unit_tests +) diff --git a/test/binary_heap_test.cpp b/test/binary_heap_test.cpp new file mode 100644 index 0000000..0c9a352 --- /dev/null +++ b/test/binary_heap_test.cpp @@ -0,0 +1,151 @@ +// +// Created by runfme on 16.11.18. +// + + +#define BOOST_TEST_MAIN +#define BOOST_TEST_MODULE testBinaryHeap + +#include "../src/BinaryHeap/BinaryHeap.hpp" +#include + +TEST(BinaryHeapBasics, Constructor) { + BinaryHeap heap = BinaryHeap(); + EXPECT_EQ(heap.empty(), true); +} + +TEST(BinaryHeapBasics, Insert) { + BinaryHeap heap = BinaryHeap(); + auto pointer = heap.insert(1); + EXPECT_EQ(pointer->value(), 1); +} + +TEST(BinaryHeapBasics, Empty) { + BinaryHeap heap = BinaryHeap(); + EXPECT_EQ(heap.empty(), true); + + heap.insert(1); + EXPECT_EQ(heap.empty(), false); +} + +TEST(BinaryHeapBasics, GetMin) { + BinaryHeap heap = BinaryHeap(); + heap.insert(2); + heap.insert(3); + heap.insert(1); + heap.insert(4); + EXPECT_EQ(heap.min(), 1); +} + +TEST(BinaryHeapBasics, ExtractMin) { + BinaryHeap heap = BinaryHeap(); + heap.insert(2); + heap.insert(3); + EXPECT_EQ(heap.extract_min(), 2); + heap.insert(1); + EXPECT_EQ(heap.extract_min(), 1); +} + +TEST(BinaryHeapBasics, Size) { + BinaryHeap heap = BinaryHeap(); + EXPECT_EQ(heap.size(), 0); + heap.insert(1); + EXPECT_EQ(heap.size(), 1); + heap.insert(2); + heap.insert(6); + heap.insert(1); + EXPECT_EQ(heap.size(), 4); + heap.extract_min(); + heap.extract_min(); + EXPECT_EQ(heap.size(), 2); +} + +TEST(BinaryHeapAdvanced, Delete) { + BinaryHeap heap = BinaryHeap(); + auto pointer_to_1 = heap.insert(1); + EXPECT_EQ(heap.extract(pointer_to_1), 1); + EXPECT_EQ(pointer_to_1->is_valid(), false); + + heap.insert(3); + auto pointer_to_2 = heap.insert(2); + heap.insert(1); + EXPECT_EQ(heap.extract(pointer_to_2), 2); + EXPECT_EQ(pointer_to_2->is_valid(), false); +} + +TEST(BinaryHeapAdvanced, Change) { + BinaryHeap heap = BinaryHeap(); + heap.insert(1); + heap.insert(3); + auto pointer_to_2 = heap.insert(2); + heap.insert(1); + + heap.change(pointer_to_2, 0); + EXPECT_EQ(pointer_to_2->value(), 0); + EXPECT_EQ(heap.min(), 0); +} + +TEST(BinaryHeapAdvanced, IteratorConstructor) { + int a[] = {4,2,5,1,6,3}; + BinaryHeap heap = BinaryHeap(a, a+6); + EXPECT_EQ(heap.extract_min(), 1); + EXPECT_EQ(heap.extract_min(), 2); + EXPECT_EQ(heap.extract_min(), 3); + EXPECT_EQ(heap.extract_min(), 4); + EXPECT_EQ(heap.extract_min(), 5); + EXPECT_EQ(heap.extract_min(), 6); +} + +TEST(BinaryHeapAdvanced, EmptyIteratorConstructor) { + int a[] = {4}; + BinaryHeap heap = BinaryHeap(a, a+0); + EXPECT_EQ(heap.empty(), true); +} + +TEST(BinaryHeapExceptions, RequestsToEmptyHeap) { + BinaryHeap heap = BinaryHeap(); + + EXPECT_THROW(heap.min(), std::runtime_error); + EXPECT_THROW(heap.extract_min(), std::runtime_error); +} + +TEST(BinaryHeapExceptions, RequestsToInvalidatedPointer) { + BinaryHeap heap = BinaryHeap(); + auto pointer_to_1 = heap.insert(1); + heap.extract(pointer_to_1); + + EXPECT_THROW(heap.extract(pointer_to_1), std::runtime_error); + EXPECT_THROW(heap.change(pointer_to_1, 10), std::runtime_error); +} + +TEST(BinaryHeapExceptions, RequestsToPointerFromAnotherHeap) { + BinaryHeap heap1 = BinaryHeap(); + BinaryHeap heap2 = BinaryHeap(); + auto pointer_to_1 = heap1.insert(1); + + EXPECT_THROW(heap2.change(pointer_to_1, 10), std::runtime_error); +} + + +TEST(BinaryHeapExceptions, Stress) { + BinaryHeap heap = BinaryHeap(); + int size = 0; + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 5000; ++j) { + auto a = heap.insert(j); + size++; + EXPECT_EQ(heap.size(), size); + heap.change(a, 2500); + EXPECT_EQ(a->value(), 2500); + heap.change(a, j); + EXPECT_EQ(a->value(), j); + heap.change(a, rand()); + } + for (int k = 0; k < 2000; ++k) { + heap.extract_min(); + + size--; + EXPECT_EQ(heap.size(), size); + } + } +} diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..5b08882 --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,11 @@ +// +// Created by sashka on 19.10.18. +// + +#include "gtest/gtest.h" + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/vector_test.cpp b/test/vector_test.cpp new file mode 100644 index 0000000..d2a0e45 --- /dev/null +++ b/test/vector_test.cpp @@ -0,0 +1,71 @@ +// +// Created by runfme on 16.11.18. +// + +#define BOOST_TEST_MAIN +#define BOOST_TEST_MODULE testVector + +#include "../src/BinaryHeap/Vector.hpp" +#include + +TEST(VectorBasics, Constuctor) { + Vector array; + EXPECT_EQ(array.empty(), true); +} + +TEST(VectorBasics, PushBack) { + Vector array; + array.push_back(1); + EXPECT_EQ(array[0], 1); + array.push_back(2); + EXPECT_EQ(array[1], 2); +} + +TEST(VectorBasics, ElementAcess) { + Vector array; + array.push_back(1); + EXPECT_EQ(array[0], 1); + array[0] = 2; + EXPECT_EQ(array[0], 2); +} + +TEST(VectorBasics, Size) { + Vector array; + EXPECT_EQ(array.size(), 0); + array.push_back(1); + EXPECT_EQ(array.size(), 1); + array.push_back(2); + array.push_back(3); + array.push_back(4); + EXPECT_EQ(array.size(), 4); +} + +TEST(VectorBasics, PopBack) { + Vector array; + array.push_back(1); + array.push_back(2); + array.push_back(3); + array.push_back(4); + array.pop_back(); + EXPECT_EQ(array.size(), 3); +} + +TEST(VectorBasics, StorageResize) { + Vector array; + EXPECT_EQ(array.get_storage_size(), 1); + array.push_back(1); + EXPECT_EQ(array.get_storage_size(), 2); + array.push_back(2); + array.push_back(3); + array.push_back(4); + EXPECT_EQ(array.get_storage_size(), 8); + array.pop_back(); + array.pop_back(); + EXPECT_EQ(array.get_storage_size(), 4); +} + +TEST(VectorBasics, OutOfRangeAccess) { + Vector array; + array.push_back(1); + EXPECT_THROW(array[10], std::out_of_range); +} \ No newline at end of file From 3c17bdf462cb4c50a08f13773a6c46e65c883bc7 Mon Sep 17 00:00:00 2001 From: RunFMe Date: Sat, 17 Nov 2018 22:42:21 +0500 Subject: [PATCH 2/3] Add linear search for optimal k in optimize --- src/BinaryHeap/BinaryHeap.hpp | 34 ++++++++++++++++++++++++++++++++-- test/binary_heap_test.cpp | 28 ++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/BinaryHeap/BinaryHeap.hpp b/src/BinaryHeap/BinaryHeap.hpp index 078eaf1..2effe41 100644 --- a/src/BinaryHeap/BinaryHeap.hpp +++ b/src/BinaryHeap/BinaryHeap.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "Vector.hpp" template @@ -36,6 +37,8 @@ class BinaryHeap { size_t size(); + size_t get_tree_degree(); + std::shared_ptr insert(T value); T min(); @@ -52,7 +55,29 @@ class BinaryHeap { BinaryHeap(Iterator begin, Iterator end); void optimize(size_t insert_count, size_t extract_count) { - + if (size() != 0) { + throw std::runtime_error ("Can Not optimize non empty heap"); + } + if (extract_count > insert_count) { + throw std::invalid_argument ("Extract count can not be grater than insert count"); + } + if (extract_count == 0) { + throw std::invalid_argument ("Extract count can not be 0"); + } + + double alpha = 1.0d * insert_count / extract_count; + double prev_time_consumption = -1; + double time_consumption = -1; + unsigned long k = 1; + + while (prev_time_consumption == -1 || time_consumption <= prev_time_consumption) { + ++k; + prev_time_consumption = time_consumption; + time_consumption = (1.0d * k + alpha) / std::log(k); + } + + + tree_degree = k - 1; } private: @@ -61,7 +86,7 @@ class BinaryHeap { std::shared_ptr::Pointer> pointer; }; - unsigned long tree_degree = 2; + size_t tree_degree = 2; Vector storage; void swap_elements(unsigned long first_index, unsigned long second_index); @@ -103,6 +128,11 @@ bool BinaryHeap::empty() { return storage.empty(); } +template +size_t BinaryHeap::get_tree_degree() { + return tree_degree; +} + template std::shared_ptr::Pointer> BinaryHeap::insert(T value) { auto pointer = push_back_element(value); diff --git a/test/binary_heap_test.cpp b/test/binary_heap_test.cpp index 0c9a352..7eb94be 100644 --- a/test/binary_heap_test.cpp +++ b/test/binary_heap_test.cpp @@ -102,6 +102,24 @@ TEST(BinaryHeapAdvanced, EmptyIteratorConstructor) { EXPECT_EQ(heap.empty(), true); } +TEST(BinaryHeapAdvanced, Optimize) { + BinaryHeap heap = BinaryHeap(); + + heap.optimize(1, 1); + EXPECT_EQ(heap.get_tree_degree(), 4); + + heap.insert(2); + heap.insert(3); + heap.insert(3); + heap.insert(63); + heap.insert(-1); + heap.insert(0); + heap.insert(7); + EXPECT_EQ(heap.extract_min(), -1); + heap.insert(1); + EXPECT_EQ(heap.extract_min(), 0); +} + TEST(BinaryHeapExceptions, RequestsToEmptyHeap) { BinaryHeap heap = BinaryHeap(); @@ -149,3 +167,13 @@ TEST(BinaryHeapExceptions, Stress) { } } } + +TEST(BinaryHeapExceptions, OptimizeExceptions) { + BinaryHeap heap = BinaryHeap(); + heap.insert(1); + + EXPECT_THROW(heap.optimize(1, 1), std::runtime_error); + + heap.extract_min(); + EXPECT_THROW(heap.optimize(10, 100), std::invalid_argument); +} \ No newline at end of file From 4d90b71fb9879a288c58f2f2fccabef6f064f7e0 Mon Sep 17 00:00:00 2001 From: RunFMe Date: Sun, 3 Feb 2019 15:34:53 +0500 Subject: [PATCH 3/3] Cleanup code --- src/BinaryHeap/BinaryHeap.hpp | 106 +++++++++++++++++----------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/src/BinaryHeap/BinaryHeap.hpp b/src/BinaryHeap/BinaryHeap.hpp index 2effe41..b50c24f 100644 --- a/src/BinaryHeap/BinaryHeap.hpp +++ b/src/BinaryHeap/BinaryHeap.hpp @@ -13,26 +13,11 @@ #include #include "Vector.hpp" -template +template class BinaryHeap { - class Pointer{ - friend class BinaryHeap; - public: - Pointer(unsigned long index, BinaryHeap* parent_heap); - - T value(); - - bool is_valid(); - - BinaryHeap* get_parent_heap(); - - private: - BinaryHeap* parent_heap; - unsigned long index; - bool valid; - }; + private: + class Pointer; public: - bool empty(); size_t size(); @@ -51,21 +36,21 @@ class BinaryHeap { BinaryHeap(); - template + template BinaryHeap(Iterator begin, Iterator end); void optimize(size_t insert_count, size_t extract_count) { if (size() != 0) { - throw std::runtime_error ("Can Not optimize non empty heap"); + throw std::runtime_error("Can Not optimize non empty heap"); } if (extract_count > insert_count) { - throw std::invalid_argument ("Extract count can not be grater than insert count"); + throw std::invalid_argument("Extract count can not be grater than insert count"); } if (extract_count == 0) { - throw std::invalid_argument ("Extract count can not be 0"); + throw std::invalid_argument("Extract count can not be 0"); } - double alpha = 1.0d * insert_count / extract_count; + double alpha = 1.0 * insert_count / extract_count; double prev_time_consumption = -1; double time_consumption = -1; unsigned long k = 1; @@ -73,14 +58,31 @@ class BinaryHeap { while (prev_time_consumption == -1 || time_consumption <= prev_time_consumption) { ++k; prev_time_consumption = time_consumption; - time_consumption = (1.0d * k + alpha) / std::log(k); + time_consumption = (1.0 * k + alpha) / std::log(k); } - tree_degree = k - 1; } private: + class Pointer { + public: + friend class BinaryHeap; + + Pointer(unsigned long index, BinaryHeap *parent_heap); + + T value(); + + bool is_valid(); + + BinaryHeap *get_parent_heap(); + + private: + BinaryHeap *parent_heap; + unsigned long array_index; + bool valid; + }; + struct Element { T value; std::shared_ptr::Pointer> pointer; @@ -108,13 +110,13 @@ BinaryHeap::BinaryHeap() = default; template template BinaryHeap::BinaryHeap(Iterator begin, Iterator end) { - for (Iterator iter = begin; iter != end ; ++iter) { + for (Iterator iter = begin; iter != end; ++iter) { push_back_element(*iter); } //call sift_down for all non leaves - for (unsigned long i = size()/2; i > 0; --i) { - sift_down(i-1); + for (unsigned long i = size() / 2; i > 0; --i) { + sift_down(i - 1); } } @@ -136,14 +138,14 @@ size_t BinaryHeap::get_tree_degree() { template std::shared_ptr::Pointer> BinaryHeap::insert(T value) { auto pointer = push_back_element(value); - sift_up(pointer->index); + sift_up(pointer->array_index); return pointer; } template T BinaryHeap::min() { if (empty()) { - throw std::runtime_error ("No minimal element. Heap is empty."); + throw std::runtime_error("No minimal element. Heap is empty."); } return storage[0].value; } @@ -151,20 +153,20 @@ T BinaryHeap::min() { template T BinaryHeap::extract(std::shared_ptr pointer) { if (!pointer->is_valid()) { - throw std::runtime_error ("Invalidated pointer to an element"); + throw std::runtime_error("Invalidated pointer to an element"); } if (pointer->get_parent_heap() != this) { - throw std::runtime_error ("Pointer from another heap"); + throw std::runtime_error("Pointer from another heap"); } - unsigned long extract_index = pointer->index; + unsigned long extract_index = pointer->array_index; T value = pointer->value(); //try to swap extracted with last element and sift it down if (extract_index == size() - 1) { storage.pop_back(); } else { - swap_elements(extract_index, size()-1); + swap_elements(extract_index, size() - 1); storage.pop_back(); sift_down(extract_index); } @@ -176,7 +178,7 @@ T BinaryHeap::extract(std::shared_ptr pointer) { template T BinaryHeap::extract_min() { if (empty()) { - throw std::runtime_error ("No minimal element. Heap is empty."); + throw std::runtime_error("No minimal element. Heap is empty."); } return extract(storage[0].pointer); } @@ -184,29 +186,29 @@ T BinaryHeap::extract_min() { template void BinaryHeap::change(std::shared_ptr pointer, T new_value) { if (!pointer->is_valid()) { - throw std::runtime_error ("Invalidated pointer to an element"); + throw std::runtime_error("Invalidated pointer to an element"); } if (pointer->get_parent_heap() != this) { - throw std::runtime_error ("Pointer from another heap"); + throw std::runtime_error("Pointer from another heap"); } T previous_value = pointer->value(); - storage[pointer->index].value = new_value; + storage[pointer->array_index].value = new_value; if (new_value > previous_value) { - sift_down(pointer->index); + sift_down(pointer->array_index); } else { - sift_up(pointer->index); + sift_up(pointer->array_index); } } template void BinaryHeap::swap_elements(unsigned long first_index, unsigned long second_index) { - if (first_index >= size() || second_index>= size()) { - throw std::out_of_range ("At least one of indexes is out of range"); + if (first_index >= size() || second_index >= size()) { + throw std::out_of_range("At least one of indexes is out of range"); } std::swap(storage[first_index].value, storage[second_index].value); - std::swap(storage[first_index].pointer->index, storage[second_index].pointer->index); + std::swap(storage[first_index].pointer->array_index, storage[second_index].pointer->array_index); std::swap(storage[first_index].pointer, storage[second_index].pointer); } @@ -222,7 +224,7 @@ std::shared_ptr::Pointer> BinaryHeap::push_back_elemen template void BinaryHeap::sift_down(unsigned long index) { if (index >= size()) { - throw std::out_of_range ("Index is out of range"); + throw std::out_of_range("Index is out of range"); } unsigned long min_index = index; @@ -242,7 +244,7 @@ void BinaryHeap::sift_down(unsigned long index) { template void BinaryHeap::sift_up(unsigned long index) { if (index >= size()) { - throw std::out_of_range ("Index is out of range"); + throw std::out_of_range("Index is out of range"); } unsigned long current = index; @@ -258,7 +260,7 @@ void BinaryHeap::sift_up(unsigned long index) { template unsigned long BinaryHeap::nth_child(unsigned long index, unsigned long n) { if (index >= size()) { - throw std::out_of_range ("Index is out of range"); + throw std::out_of_range("Index is out of range"); } return index * tree_degree + n + 1; } @@ -266,10 +268,10 @@ unsigned long BinaryHeap::nth_child(unsigned long index, unsigned long n) { template unsigned long BinaryHeap::parent(unsigned long index) { if (index == 0) { - throw std::runtime_error ("Root node has no parent"); + throw std::runtime_error("Root node has no parent"); } if (index >= size()) { - throw std::out_of_range ("Index is out of range"); + throw std::out_of_range("Index is out of range"); } return (index - 1) / tree_degree; } @@ -281,18 +283,18 @@ bool BinaryHeap::Pointer::is_valid() { template T BinaryHeap::Pointer::value() { - return parent_heap->storage[index].value; + return parent_heap->storage[array_index].value; } template -BinaryHeap::Pointer::Pointer(unsigned long index, BinaryHeap* const parent_heap) { - this->index = index; +BinaryHeap::Pointer::Pointer(unsigned long index, BinaryHeap *const parent_heap) { + this->array_index = index; this->parent_heap = parent_heap; this->valid = true; } template -BinaryHeap* BinaryHeap::Pointer::get_parent_heap() { +BinaryHeap *BinaryHeap::Pointer::get_parent_heap() { return parent_heap; }