├── .gitignore ├── LICENSE ├── tests ├── CMakeLists.txt ├── warnings.h ├── test_types.h ├── optional_test.cc ├── index_range_test.cc ├── set_test.cc └── vector_test.cc ├── src ├── CMakeLists.txt └── index_range.cc ├── include ├── compatibility.h ├── export_def.h ├── index_range.h ├── optional.h ├── set.h └── vector.h ├── CMakeLists.txt ├── .github └── workflows │ └── cmake.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeLists.txt.user 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Testing 6 | Makefile 7 | cmake_install.cmake 8 | install_manifest.txt 9 | compile_commands.json 10 | CTestTestfile.cmake 11 | _deps 12 | build/ 13 | build*/ 14 | /.vs 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Ioannis Kaliakatsos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(EXENAME unit_tests) 2 | 3 | # Enable test detection 4 | enable_testing() 5 | include(GoogleTest) 6 | 7 | # Properties->C/C++->General->Additional Include Directories 8 | include_directories ("${PROJECT_SOURCE_DIR}/include") 9 | 10 | # Collect test sources into the variable TEST_SOURCES 11 | file (GLOB TEST_SOURCES 12 | "*.cpp" 13 | "*.cc") 14 | 15 | # Set Properties->General->Configuration Type to Application 16 | add_executable (${EXENAME} ${TEST_SOURCES}) 17 | 18 | # Properties->Linker->Input->Additional Dependencies 19 | target_link_libraries(${EXENAME} PUBLIC fcpp gtest_main) 20 | 21 | # Creates a folder "executables" and adds target project under it 22 | set_property(TARGET ${EXENAME} PROPERTY FOLDER "executables") 23 | 24 | # The official site of Google Test suggests the gtest_discover_tests 25 | # command, but this does not work for Xcode on macOS, therefore we chose 26 | # the legacy gtest_add_tests command 27 | gtest_add_tests(TARGET ${EXENAME} TEST_PREFIX fp:) 28 | 29 | # Adds logic to copy executable to destination directory 30 | install (TARGETS ${EXENAME} 31 | RUNTIME DESTINATION ${PROJECT_BINARY_DIR}/bin) 32 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set (LIBNAME fcpp) 2 | 3 | # Properties->C/C++->General->Additional Include Directories 4 | include_directories ("../include") 5 | 6 | # Collect files without having to explicitly list each header and source file 7 | file (GLOB HEADERS 8 | "../include/*.h" 9 | "../include/*.hpp") 10 | 11 | file (GLOB SOURCES 12 | "*.cpp" 13 | "*.cc") 14 | 15 | # Create named folders for the sources within the project 16 | source_group("include" FILES ${HEADERS}) 17 | source_group("src" FILES ${SOURCES}) 18 | 19 | # Set Properties->General->Configuration Type to Dynamic Library (.dll/.so/.dylib) 20 | add_library(${LIBNAME} SHARED ${HEADERS} ${SOURCES}) 21 | 22 | if(CMAKE_HOST_UNIX AND NOT CMAKE_HOST_APPLE) 23 | target_link_libraries(${LIBNAME} PUBLIC tbb) 24 | endif() 25 | 26 | # creates preprocessor definition used for library exports 27 | add_compile_definitions("FUNCTIONAL_CPP_EXPORTS") 28 | 29 | set_target_properties(${LIBNAME} PROPERTIES 30 | OUTPUT_NAME ${LIBNAME} 31 | FOLDER "lib") 32 | 33 | # Adds logic to install the library to the destination directory 34 | install (TARGETS ${LIBNAME} 35 | RUNTIME DESTINATION ${PROJECT_BINARY_DIR}/bin) 36 | -------------------------------------------------------------------------------- /include/compatibility.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2023 Ioannis Kaliakatsos 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #if __cplusplus >= 201703L 26 | #define CPP17_AVAILABLE 27 | #endif 28 | 29 | #if defined(CPP17_AVAILABLE) && !defined(__clang__) 30 | #define PARALLEL_ALGORITHM_AVAILABLE 31 | #endif 32 | -------------------------------------------------------------------------------- /tests/warnings.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2022 Ioannis Kaliakatsos 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #ifdef _MSC_VER 26 | #pragma warning(disable: 4834) // discarding return value of function with 'nodiscard' attribute 27 | #pragma warning(disable: 4100) // unreferenced formal parameter 28 | #pragma warning(disable: 4018) // signed/unsigned mismatch 29 | #pragma warning(disable: 4389) // signed/unsigned mismatch 30 | #endif 31 | -------------------------------------------------------------------------------- /include/export_def.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2023 Ioannis Kaliakatsos 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) 26 | #ifdef FUNCTIONAL_CPP_EXPORTS 27 | #define FunctionalCppExport __declspec( dllexport ) 28 | #else 29 | #define FunctionalCppExport __declspec( dllimport ) 30 | #endif 31 | #else 32 | #define FunctionalCppExport __attribute__ ((__visibility__("default"))) 33 | #endif 34 | 35 | #include "compatibility.h" 36 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(functional_cpp VERSION 1.0) 3 | 4 | # GoogleTest requires at least C++11 5 | if(NOT "${CMAKE_CXX_STANDARD}") 6 | set(CMAKE_CXX_STANDARD 11) 7 | endif() 8 | message("-- C++ standard version: ${CMAKE_CXX_STANDARD}") 9 | 10 | # For Windows: Prevent overriding the parent project's compiler/linker settings 11 | if(CMAKE_HOST_WIN32) 12 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 13 | endif() 14 | 15 | # Caching googletest source and libs 16 | include(FetchContent) 17 | FetchContent_Declare( 18 | googletest 19 | URL https://github.com/google/googletest/archive/8d51ffdfab10b3fba636ae69bc03da4b54f8c235.zip 20 | ) 21 | FetchContent_MakeAvailable(googletest) 22 | 23 | # Set compiler settings based on the current platform 24 | if(CMAKE_HOST_UNIX) 25 | add_definitions(-w -fpermissive -Wshadow -Wunused -Woverloaded-virtual -Wnon-virtual-dtor -Werror) 26 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -w -Wshadow -Wunused -Woverloaded-virtual -Wnon-virtual-dtor -Werror") 27 | else() 28 | set(CMAKE_CONFIGURATION_TYPES "Debug;Release") 29 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Ox /Ob2 /Oi /Ot /Oy /GT /GL /W4 /Zc:__cplusplus") 30 | 31 | set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /OPT:REF /OPT:ICF /INCREMENTAL:NO /LTCG" ) 32 | set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} /INCREMENTAL:NO /LTCG" ) 33 | 34 | set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /INCREMENTAL:NO /LTCG" ) 35 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /INCREMENTAL:NO /LTCG" ) 36 | 37 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 38 | endif() 39 | 40 | 41 | # Turn on the ability to create folders to organize projects 42 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 43 | 44 | # Include the testing subdirectory 45 | add_subdirectory(src) 46 | add_subdirectory(tests) 47 | -------------------------------------------------------------------------------- /src/index_range.cc: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2023 Ioannis Kaliakatsos 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "index_range.h" 24 | 25 | index_range index_range::invalid = start_count(-1, -1); 26 | 27 | index_range::index_range(int start, int count) 28 | : start(-1), end(-1), count(-1) 29 | { 30 | is_valid = start >= 0 && count > 0; 31 | if (is_valid) { 32 | this->start = start; 33 | this->count = count; 34 | end = start + count - 1; 35 | } 36 | } 37 | 38 | index_range index_range::start_count(int start, int count) 39 | { 40 | return index_range(start, count); 41 | } 42 | 43 | index_range index_range::start_end(int start, int end) 44 | { 45 | return index_range(start, end - start + 1); 46 | } 47 | 48 | bool index_range::operator ==(const index_range& rhs) const 49 | { 50 | return start == rhs.start && count == rhs.count; 51 | } 52 | 53 | bool index_range::operator !=(const index_range& rhs) const 54 | { 55 | return !(*this == rhs); 56 | } 57 | -------------------------------------------------------------------------------- /tests/test_types.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2022 Ioannis Kaliakatsos 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | #include 25 | 26 | struct child 27 | { 28 | child() 29 | : age(0) 30 | { 31 | } 32 | 33 | child(int age) 34 | : age(age) 35 | { 36 | } 37 | 38 | int age; 39 | 40 | bool operator< (const child& other) const { 41 | return age < other.age; 42 | } 43 | }; 44 | 45 | struct child_comparator { 46 | bool operator() (const child& a, const child& b) const { 47 | return a < b; 48 | } 49 | }; 50 | 51 | struct person 52 | { 53 | person() 54 | : age(0), name("") 55 | { 56 | } 57 | 58 | person(int age, std::string name) 59 | : age(age), name(std::move(name)) 60 | { 61 | } 62 | 63 | int age; 64 | std::string name; 65 | 66 | std::size_t hash() const { 67 | std::size_t h1 = std::hash{}(name); 68 | std::size_t h2 = std::hash{}(age); 69 | return h1 ^ (h2 << 1); 70 | } 71 | 72 | bool operator == (const person& other) const { 73 | return age == other.age && name == other.name; 74 | } 75 | 76 | bool operator< (const person& other) const { 77 | return hash() < other.hash(); 78 | } 79 | }; 80 | 81 | struct person_comparator { 82 | bool operator() (const person& a, const person& b) const { 83 | return a < b; 84 | } 85 | }; 86 | -------------------------------------------------------------------------------- /include/index_range.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2023 Ioannis Kaliakatsos 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | #include "export_def.h" 25 | 26 | // A struct used for container safe access based on index 27 | struct FunctionalCppExport index_range 28 | { 29 | // Used for returning values of invalid operations 30 | static index_range invalid; 31 | 32 | // Create with start index and element count (end index is calculated) 33 | static index_range start_count(int start, int count); 34 | 35 | // Create with start and end index (count is calculated) 36 | static index_range start_end(int start, int end); 37 | 38 | // The start index of the index range 39 | // example: 40 | // [0] [1] [2] [3] [4] [5] [6] 41 | // 5 3 9 1 8 3 2 42 | // ^ ^ 43 | // | | 44 | // start end 45 | // 46 | // start = 2 47 | // end = 4 48 | // count = 3 49 | int start; 50 | 51 | // The end index of the index range 52 | // example: 53 | // [0] [1] [2] [3] [4] [5] [6] 54 | // 5 3 9 1 8 3 2 55 | // ^ ^ 56 | // | | 57 | // start end 58 | // 59 | // start = 2 60 | // end = 4 61 | // count = 3 62 | int end; 63 | 64 | // The total count of the elements in the index range 65 | // example: 66 | // [0] [1] [2] [3] [4] [5] [6] 67 | // 5 3 9 1 8 3 2 68 | // ^ ^ 69 | // | | 70 | // start end 71 | // 72 | // start = 2 73 | // end = 4 74 | // count = 3 75 | int count; 76 | 77 | // Returns true if it's safe to use its contents 78 | bool is_valid; 79 | 80 | bool operator ==(const index_range& rhs) const; 81 | bool operator !=(const index_range& rhs) const; 82 | 83 | private: 84 | index_range(int start, int count); 85 | }; 86 | -------------------------------------------------------------------------------- /include/optional.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Created by iamOgunyinka on 08 Sep 2021 (@iamOgunyinka, https://github.com/iamOgunyinka) 4 | // Copyright (c) 2023 Ioannis Kaliakatsos 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | #pragma once 25 | #include "compatibility.h" 26 | 27 | namespace fcpp { 28 | #ifdef CPP17_AVAILABLE 29 | #include 30 | template 31 | using optional_t = std::optional; 32 | #else 33 | #include 34 | #include 35 | 36 | // A replacement for std::optional when C++17 is not available 37 | template 38 | class optional 39 | { 40 | public: 41 | optional() 42 | : _value{nullptr} 43 | { 44 | } 45 | 46 | ~optional() 47 | { 48 | reset(); 49 | } 50 | 51 | optional& operator=(optional const& other) 52 | { 53 | _value = nullptr; 54 | if (other.has_value()) { 55 | _value = new T{other.value()}; 56 | } 57 | return *this; 58 | } 59 | 60 | optional(const optional& other) 61 | { 62 | _value = nullptr; 63 | if (other.has_value()) { 64 | _value = new T{other.value()}; 65 | } 66 | } 67 | 68 | optional(T const& val) 69 | : _value(new T{val}) 70 | { 71 | } 72 | 73 | bool has_value() const 74 | { 75 | return _value != nullptr; 76 | } 77 | 78 | T* operator->() const 79 | { 80 | assert(has_value()); 81 | return _value; 82 | } 83 | 84 | T& operator*() const 85 | { 86 | assert(has_value()); 87 | return *_value; 88 | } 89 | 90 | const T& value() const 91 | { 92 | assert(has_value()); 93 | return *_value; 94 | } 95 | 96 | optional& operator=(T const& value) 97 | { 98 | reset(); 99 | _value = new T(value); 100 | return *this; 101 | } 102 | 103 | private: 104 | void reset() 105 | { 106 | if (_value) { 107 | delete _value; 108 | _value = nullptr; 109 | } 110 | } 111 | 112 | T* _value; 113 | }; 114 | 115 | template 116 | using optional_t = optional; 117 | 118 | #endif 119 | } 120 | -------------------------------------------------------------------------------- /tests/optional_test.cc: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2023 Ioannis Kaliakatsos 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | #include "optional.h" 25 | 26 | using namespace fcpp; 27 | 28 | TEST(OptionalTest, InvalidTest) 29 | { 30 | const optional_t index; 31 | EXPECT_FALSE(index.has_value()); 32 | } 33 | 34 | TEST(OptionalTest, ValidTest) 35 | { 36 | const optional_t index(5); 37 | EXPECT_TRUE(index.has_value()); 38 | EXPECT_EQ(5, index.value()); 39 | } 40 | 41 | TEST(OptionalTest, AssignmentFromValueTest) 42 | { 43 | optional_t index(5); 44 | index = 3; 45 | EXPECT_TRUE(index.has_value()); 46 | EXPECT_EQ(3, index.value()); 47 | } 48 | 49 | TEST(OptionalTest, AssignmentFromOptionalTest) 50 | { 51 | optional_t v1(5); 52 | const optional_t v2(2); 53 | v1 = v2; 54 | EXPECT_TRUE(v1.has_value()); 55 | EXPECT_EQ(2, v1.value()); 56 | EXPECT_TRUE(v2.has_value()); 57 | EXPECT_EQ(2, v2.value()); 58 | } 59 | 60 | TEST(OptionalTest, AssignmentFromNullOptionalTest) 61 | { 62 | optional_t v1(5); 63 | const optional_t v2; 64 | v1 = v2; 65 | EXPECT_FALSE(v1.has_value()); 66 | EXPECT_FALSE(v2.has_value()); 67 | } 68 | 69 | TEST(OptionalTest, AssignmentToNullFromValueTest) 70 | { 71 | optional_t index; 72 | index = 3; 73 | EXPECT_TRUE(index.has_value()); 74 | EXPECT_EQ(3, index.value()); 75 | } 76 | 77 | TEST(OptionalTest, AssignmentToNullFromOptionalTest) 78 | { 79 | optional_t v1; 80 | const optional_t v2(2); 81 | v1 = v2; 82 | EXPECT_TRUE(v1.has_value()); 83 | EXPECT_EQ(2, v1.value()); 84 | EXPECT_TRUE(v2.has_value()); 85 | EXPECT_EQ(2, v2.value()); 86 | } 87 | 88 | TEST(OptionalTest, AssignmentToNullFromNullOptionalTest) 89 | { 90 | optional_t v1; 91 | const optional_t v2; 92 | v1 = v2; 93 | EXPECT_FALSE(v1.has_value()); 94 | EXPECT_FALSE(v2.has_value()); 95 | } 96 | 97 | TEST(OptionalTest, CopyConstructorTest) 98 | { 99 | const optional_t v2(5); 100 | const optional_t v1(v2); 101 | EXPECT_TRUE(v1.has_value()); 102 | EXPECT_EQ(5, v2.value()); 103 | } 104 | 105 | TEST(OptionalTest, CopyConstructorNullTest) 106 | { 107 | const optional_t v2; 108 | const optional_t v1(v2); 109 | EXPECT_FALSE(v1.has_value()); 110 | } 111 | -------------------------------------------------------------------------------- /tests/index_range_test.cc: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2023 Ioannis Kaliakatsos 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | #include "index_range.h" 25 | 26 | TEST(RangeTest, InvalidTest) 27 | { 28 | auto range = index_range::start_count(0, 0); 29 | EXPECT_FALSE(range.is_valid); 30 | EXPECT_EQ(-1, range.start); 31 | EXPECT_EQ(-1, range.end); 32 | EXPECT_EQ(-1, range.count); 33 | 34 | range = index_range::start_count(0, -1); 35 | EXPECT_FALSE(range.is_valid); 36 | EXPECT_EQ(-1, range.start); 37 | EXPECT_EQ(-1, range.end); 38 | EXPECT_EQ(-1, range.count); 39 | 40 | range = index_range::start_count(0, -5); 41 | EXPECT_FALSE(range.is_valid); 42 | EXPECT_EQ(-1, range.start); 43 | EXPECT_EQ(-1, range.end); 44 | EXPECT_EQ(-1, range.count); 45 | 46 | range = index_range::start_count(-1, 10); 47 | EXPECT_FALSE(range.is_valid); 48 | EXPECT_EQ(-1, range.start); 49 | EXPECT_EQ(-1, range.end); 50 | EXPECT_EQ(-1, range.count); 51 | 52 | range = index_range::start_count(-3, 10); 53 | EXPECT_FALSE(range.is_valid); 54 | EXPECT_EQ(-1, range.start); 55 | EXPECT_EQ(-1, range.end); 56 | EXPECT_EQ(-1, range.count); 57 | } 58 | 59 | TEST(RangeTest, ValidFromStartAndCountTest) 60 | { 61 | auto range = index_range::start_count(0, 1); 62 | EXPECT_TRUE(range.is_valid); 63 | EXPECT_EQ(0, range.start); 64 | EXPECT_EQ(0, range.end); 65 | EXPECT_EQ(1, range.count); 66 | 67 | range = index_range::start_count(13, 3); 68 | EXPECT_TRUE(range.is_valid); 69 | EXPECT_EQ(13, range.start); 70 | EXPECT_EQ(15, range.end); 71 | EXPECT_EQ(3, range.count); 72 | } 73 | 74 | TEST(RangeTest, ValidFromStartAndEndTest) 75 | { 76 | auto range = index_range::start_end(0, 1); 77 | EXPECT_TRUE(range.is_valid); 78 | EXPECT_EQ(0, range.start); 79 | EXPECT_EQ(1, range.end); 80 | EXPECT_EQ(2, range.count); 81 | 82 | range = index_range::start_end(13, 15); 83 | EXPECT_TRUE(range.is_valid); 84 | EXPECT_EQ(13, range.start); 85 | EXPECT_EQ(15, range.end); 86 | EXPECT_EQ(3, range.count); 87 | 88 | range = index_range::start_end(13, 13); 89 | EXPECT_TRUE(range.is_valid); 90 | EXPECT_EQ(13, range.start); 91 | EXPECT_EQ(13, range.end); 92 | EXPECT_EQ(1, range.count); 93 | } 94 | 95 | TEST(RangeTest, InvalidFromStartAndEndTest) 96 | { 97 | const auto range = index_range::start_end(10, 9); 98 | EXPECT_FALSE(range.is_valid); 99 | EXPECT_EQ(-1, range.start); 100 | EXPECT_EQ(-1, range.end); 101 | EXPECT_EQ(-1, range.count); 102 | } 103 | 104 | TEST(RangeTest, EqualityTest) 105 | { 106 | EXPECT_FALSE(index_range::invalid == index_range::start_end(9, 10)); 107 | EXPECT_TRUE(index_range::invalid == index_range::start_end(10, 9)); 108 | EXPECT_TRUE(index_range::invalid == index_range::invalid); 109 | EXPECT_FALSE(index_range::invalid != index_range::invalid); 110 | 111 | EXPECT_TRUE(index_range::start_end(9, 10) == index_range::start_end(9, 10)); 112 | EXPECT_FALSE(index_range::start_end(9, 10) != index_range::start_end(9, 10)); 113 | 114 | EXPECT_FALSE(index_range::start_end(9, 10) == index_range::start_end(8, 10)); 115 | EXPECT_TRUE(index_range::start_end(9, 10) != index_range::start_end(8, 10)); 116 | } 117 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | # Heavily inspired (i.e. copy/paste) from https://gist.github.com/NickNaso/0d478f1481686d5bcc868cac06620a60 2 | name: CMake Build Matrix 3 | 4 | # Controls when the action will run. Triggers the workflow on push 5 | on: 6 | push: 7 | pull_request: 8 | release: 9 | # tags: 10 | # - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 11 | 12 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 13 | jobs: 14 | # This workflow contains a single job called "build" 15 | build: 16 | # The type of runner that the job will run on 17 | name: ${{ matrix.config.name }} 18 | runs-on: ${{ matrix.config.os }} 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | config: 23 | - { 24 | name: "Windows Latest MSVC (C++11)", 25 | os: windows-latest, 26 | build_type: "Debug", 27 | cc: "cl", 28 | cxx: "cl", 29 | environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat", 30 | generators: "Visual Studio 17 2022" 31 | } 32 | - { 33 | name: "Windows Latest MSVC (C++17)", 34 | os: windows-latest, 35 | build_type: "Debug", 36 | cc: "cl", 37 | cxx: "cl", 38 | environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat", 39 | generators: "Visual Studio 17 2022" 40 | } 41 | - { 42 | name: "Ubuntu Latest GCC (C++11)", 43 | os: ubuntu-latest, 44 | build_type: "Debug", 45 | cc: "gcc", 46 | cxx: "g++", 47 | generators: "Ninja" 48 | } 49 | - { 50 | name: "Ubuntu Latest GCC (C++17)", 51 | os: ubuntu-latest, 52 | build_type: "Debug", 53 | cc: "gcc", 54 | cxx: "g++", 55 | generators: "Ninja" 56 | } 57 | - { 58 | name: "macOS Latest Clang (C++11)", 59 | os: macos-latest, 60 | build_type: "Debug", 61 | cc: "clang", 62 | cxx: "clang++", 63 | generators: "Xcode" 64 | } 65 | - { 66 | name: "macOS Latest Clang (C++17)", 67 | os: macos-latest, 68 | build_type: "Debug", 69 | cc: "clang", 70 | cxx: "clang++", 71 | generators: "Xcode" 72 | } 73 | 74 | steps: 75 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 76 | - uses: actions/checkout@v3 77 | 78 | - name: Print env 79 | run: | 80 | echo github.event.action: ${{ github.event.action }} 81 | echo github.event_name: ${{ github.event_name }} 82 | 83 | - name: Install dependencies on windows 84 | if: startsWith(matrix.config.os, 'windows') 85 | run: | 86 | choco install ninja cmake 87 | ninja --version 88 | cmake --version 89 | 90 | - name: Install dependencies on ubuntu 91 | if: startsWith(matrix.config.name, 'Ubuntu Latest GCC') 92 | run: | 93 | sudo apt-get update 94 | sudo apt-get install ninja-build cmake libtbb-dev 95 | ninja --version 96 | cmake --version 97 | gcc --version 98 | 99 | - name: Install dependencies on macos 100 | if: startsWith(matrix.config.os, 'macos') 101 | run: | 102 | brew install cmake ninja 103 | ninja --version 104 | cmake --version 105 | 106 | - name: Configure C++11 107 | shell: bash 108 | if: contains(matrix.config.name, 'C++11') 109 | run: | 110 | mkdir build 111 | cmake \ 112 | -S . \ 113 | -B build \ 114 | -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ 115 | -G "${{ matrix.config.generators }}" 116 | 117 | - name: Configure C++17 118 | shell: bash 119 | if: contains(matrix.config.name, 'C++17') 120 | run: | 121 | mkdir build 122 | cmake \ 123 | -S . \ 124 | -B build \ 125 | -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ 126 | -DCMAKE_CXX_STANDARD=17 \ 127 | -G "${{ matrix.config.generators }}" 128 | 129 | - name: Build 130 | shell: bash 131 | run: cmake --build build --config ${{ matrix.config.build_type }} 132 | 133 | - name: Run tests on Windows 134 | if: startsWith(matrix.config.os, 'windows') 135 | shell: bash 136 | run: | 137 | status=$? 138 | cmd="build/bin/${{ matrix.config.build_type }}/unit_tests" 139 | $cmd 140 | status=$? 141 | [ $status -eq 0 ] && exit 0 || exit 1 142 | 143 | - name: Run tests on macos 144 | if: startsWith(matrix.config.os, 'macos') 145 | shell: bash 146 | run: | 147 | status=$? 148 | cmd="build/tests/${{ matrix.config.build_type }}/unit_tests" 149 | $cmd 150 | status=$? 151 | [ $status -eq 0 ] && exit 0 || exit 1 152 | 153 | - name: Run tests on Ubuntu 154 | if: startsWith(matrix.config.name, 'Ubuntu Latest GCC') 155 | shell: bash 156 | run: | 157 | status=$? 158 | cmd="./build/tests/unit_tests" 159 | $cmd 160 | status=$? 161 | [ $status -eq 0 ] && exit 0 || exit 1 162 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CMake Build Matrix](https://github.com/jkalias/functional_cpp/actions/workflows/cmake.yml/badge.svg)](https://github.com/jkalias/functional_cpp/actions/workflows/cmake.yml) 2 | [![GitHub license](https://img.shields.io/github/license/jkalias/functional_cpp)](https://github.com/jkalias/functional_cpp/blob/main/LICENSE) 3 | # Say hello to functional C++ 4 | A wrapper for C++ std::vector and std::set geared towards functional programming and fluent APIs. This project is heavily influenced and inspired by C# and Swift. 5 | 6 | The primary focus of this library is 7 | * readability at the call site ("make it work, make it right, make it fast") 8 | * surfacing existing algorithms from the standard library, and lowering the barrier for their extended usage 9 | * elimination of vector index operations 10 | * encapsulation of the iterator madness 11 | * removal of manual for-loops 12 | 13 | ## Compilation (Cmake) 14 | ### Dependencies 15 | * CMake >= 3.14 16 | 17 | ### Minimum C++ version 18 | * C++11 19 | 20 | An out-of-source build strategy is used. All following examples assume an output build folder named ```build```. If no additional argument is passed to CMake, C++11 is used. Otherwise, you can pass ```-DCMAKE_CXX_STANDARD=17``` and it will use C++17 for example. 21 | ### macOS (Xcode) 22 | ```console 23 | cd functional_cpp 24 | cmake -S . -B build -G Xcode 25 | ``` 26 | Then open the generated ```functional_cpp.xcodeproj``` in the ```build``` folder. 27 | 28 | ### macOS (Makefiles/clang) 29 | ```console 30 | cd functional_cpp 31 | cmake -S . -B build 32 | cmake --build build 33 | build/tests/unit_tests 34 | ``` 35 | 36 | ### macOS (Makefiles/g++) 37 | Assuming you have installed Homebrew, you can then use the gcc and g++ compilers by doing the following (this example uses version gcc 11) 38 | ```console 39 | cd functional_cpp 40 | cmake \ 41 | -S . \ 42 | -B build \ 43 | -DCMAKE_C_COMPILER=/opt/homebrew/Cellar/gcc/11.2.0/bin/gcc-11 \ 44 | -DCMAKE_CXX_COMPILER=/opt/homebrew/Cellar/gcc/11.2.0/bin/g++-11 45 | cmake --build build 46 | build/tests/unit_tests 47 | ``` 48 | 49 | ### Linux (Makefiles) 50 | ```console 51 | cd functional_cpp 52 | cmake -S . -B build 53 | cmake --build build 54 | build/tests/unit_tests 55 | ``` 56 | 57 | ### Windows (Visual Studio) 58 | ```console 59 | cd functional_cpp 60 | cmake -S . -B build 61 | ``` 62 | Then open the generated ```functional_cpp.sln``` in the ```build``` folder. 63 | 64 | ## Functional vector usage (fcpp::vector) 65 | ### extract unique (distinct) elements in a set 66 | ```c++ 67 | #include "vector.h" // instead of 68 | 69 | const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 70 | 71 | // contains only 1, 2, 3, 4, 5, 7, 8 72 | const fcpp::set unique_numbers = numbers.distinct(); 73 | ``` 74 | 75 | ### zip, map, filter, sort, reduce 76 | ```c++ 77 | #include "vector.h" // instead of 78 | 79 | struct person { 80 | person(int age, std::string name) 81 | : age(age), name(std::move(name)) 82 | {} 83 | int age; 84 | std::string name; 85 | 86 | std::size_t hash() const { 87 | // a clever implementation of hash 88 | // ... 89 | } 90 | 91 | bool operator< (const person& other) const { 92 | return hash() < other.hash(); 93 | } 94 | }; 95 | 96 | // ... 97 | 98 | // the employees' ages 99 | const fcpp::vector ages({32, 45, 37, 23}); 100 | 101 | // the employees' names 102 | const fcpp::vector names({"Jake", "Anna", "Kate", "Bob"}); 103 | 104 | const auto employees_below_40 = ages 105 | // zip two vectors for simultaneous processing 106 | .zip(names) 107 | 108 | // apply the functional map algorithm (transform from one type to another) 109 | .map([](const std::pair& pair) { 110 | return person(pair.first, pair.second); 111 | }) 112 | 113 | // filter the elements using a local function (lambda) 114 | .filter([](const person& p) { 115 | return p.age < 40; 116 | }) 117 | 118 | // sort according to custom predicate 119 | .sort([](const person& person1, const person& person2) { 120 | return person1.age < person2.age; 121 | }); 122 | 123 | /* 124 | prints the following: 125 | Bob is 23 years old. 126 | Jake is 32 years old. 127 | Kate is 37 years old. 128 | */ 129 | employees_below_40.for_each([](const person& p) { 130 | std::cout << p.name << " is " << p.age << " years old." << std::endl; 131 | }); 132 | 133 | // total_age = 92 134 | const auto total_age = employees_below_40.reduce(0, [](const int& partial_sum, const person& p){ 135 | return partial_sum + p.age; 136 | }); 137 | ``` 138 | ### index search 139 | ```c++ 140 | #include "vector.h" // instead of 141 | 142 | const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 143 | 144 | const auto first_index_of_one = numbers.find_first_index(1); 145 | // returns 0 146 | first_index_of_one.value(); 147 | 148 | const auto last_index_of_one = numbers.find_last_index(1); 149 | // returns 8 150 | last_index_of_one.value(); 151 | 152 | // all_indices_of_one -> { 0, 6, 8 } 153 | const auto all_indices_of_one = numbers.find_all_indices(1); 154 | 155 | const auto index_of_nine = numbers.find_first_index(9); 156 | // returns false 157 | index_of_nine.has_value(); 158 | ``` 159 | 160 | ### remove, insert 161 | ```c++ 162 | #include "vector.h" // instead of 163 | #include "index_range.h" 164 | 165 | fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 166 | 167 | // numbers -> fcpp::vector({1, 4, 2, 5, 3, 1, 7, 1}); 168 | numbers.remove_at(4); 169 | 170 | // numbers -> fcpp::vector({4, 2, 5, 3, 1, 7, 1}); 171 | numbers.remove_front(); 172 | 173 | // numbers -> fcpp::vector({4, 2, 5, 3, 1, 7}); 174 | numbers.remove_back(); 175 | 176 | // numbers -> fcpp::vector({4, 2, 7}); 177 | numbers.remove_range(index_range::start_count(2, 3)); 178 | 179 | // numbers -> fcpp::vector({4, 8, 2, 7}); 180 | numbers.insert_at(1, 8); 181 | 182 | // numbers -> fcpp::vector({-10, 4, 8, 2, 7}); 183 | numbers.insert_front(-10); 184 | 185 | // numbers -> fcpp::vector({-10, 4, 8, 2, 7, 9}); 186 | numbers.insert_back(9); 187 | 188 | // numbers -> fcpp::vector({-10, 4, 8, 3, -2, 5, 2, 7, 9}); 189 | numbers.insert_at(3, std::vector({3, -2, 5})); 190 | 191 | // numbers -> fcpp::vector({4, -6, 7, -10, 4, 8, 3, -2, 5, 2, 7, 9}); 192 | numbers.insert_front(fcpp::vector({4, -6, 7})); 193 | 194 | // numbers -> fcpp::vector({4, -6, 7, -10, 4, 8, 3, -2, 5, 2, 7, 9, 7, 3}); 195 | numbers.insert_back(std::initializer_list({7, 3})); 196 | ``` 197 | 198 | ### size, capacity, reserve, resize 199 | ```c++ 200 | #include "vector.h" // instead of 201 | 202 | // numbers.capacity() = 9 203 | // numbers.size() = 9 204 | fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 205 | 206 | // numbers -> fcpp::vector({1, 4, 2, 5, 8}); 207 | // numbers.capacity() = 9 208 | // numbers.size() = 5 209 | numbers.resize(5); 210 | 211 | // numbers -> fcpp::vector({1, 4, 2, 5, 8, 0, 0}); 212 | // numbers.capacity() = 9 213 | // numbers.size() = 7 214 | numbers.resize(7); 215 | 216 | // empty_numbers.capacity() = 0 217 | // empty_numbers.size() = 0 218 | fcpp::vector empty_numbers; 219 | 220 | // empty_numbers.capacity() = 5 221 | // empty_numbers.size() = 0 222 | empty_numbers.reserve(5); 223 | ``` 224 | 225 | ### all_of, any_of, none_of 226 | ```c++ 227 | #include "vector.h" // instead of 228 | 229 | fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 230 | 231 | // returns true 232 | numbers.all_of([](const int& number) { 233 | return number < 10; 234 | }); 235 | 236 | // returns false 237 | numbers.all_of([](const int& number) { 238 | return number > 2; 239 | }); 240 | 241 | // returns true 242 | numbers.any_of([](const int& number) { 243 | return number < 5; 244 | }); 245 | 246 | // returns false 247 | numbers.any_of([](const int& number) { 248 | return number > 9; 249 | }); 250 | 251 | // returns true 252 | numbers.none_of([](const int& number) { 253 | return number < -2; 254 | }); 255 | 256 | // returns false 257 | numbers.none_of([](const int& number) { 258 | return number > 7; 259 | }); 260 | ``` 261 | 262 | ### Parallel algorithms 263 | Since C++17 several STL algorithms can be executed in parallel. 264 | 265 | clang on macOS does not yet fully support the parallel execution model, however on Windows and Linux, an `fcpp::vector` supports the following parallel algorithms 266 | ```c++ 267 | for_each_parallel 268 | map_parallel 269 | filter_parallel 270 | sort_parallel 271 | all_of_parallel 272 | any_of_parallel 273 | none_of_parallel 274 | ``` 275 | 276 | ## Functional set usage (fcpp::set) 277 | ### difference, union, intersection (works with fcpp::set and std::set) 278 | ```c++ 279 | #include "set.h" // instead of 280 | 281 | // struct person as defined previously 282 | struct person_comparator { 283 | bool operator() (const person& a, const person& b) const { 284 | return a < b; 285 | } 286 | }; 287 | 288 | // ... 289 | 290 | // a set containing all colleagues 291 | const fcpp::set colleagues({ 292 | person(51, "George"), 293 | person(15, "Jake"), 294 | person(18, "Jannet"), 295 | person(41, "Jackie"), 296 | person(25, "Kate") 297 | }); 298 | 299 | // a set containing all friends 300 | const fcpp::set friends({ 301 | person(51, "George"), 302 | person(41, "Jackie"), 303 | person(42, "Crystal"), 304 | }); 305 | 306 | // find which colleagues are not friends 307 | // contains person(15, "Jake"), person(18, "Jannet") and person(25, "Kate") 308 | const auto colleagues_but_not_friends = colleagues.difference_with(friends); 309 | 310 | // find which friends are colleagues 311 | // same as colleagues.intersect_with(friends) 312 | // contains person(51, "George"), person(41, "Jackie") 313 | const auto good_colleagues = friends.intersection_with(colleagues); 314 | 315 | // a set of close family members 316 | const fcpp::set family({ 317 | person(51, "Paul"), 318 | person(81, "Barbara"), 319 | }); 320 | 321 | // all of our friends and family for the next party invitation 322 | // contains person(51, "George"), person(41, "Jackie"), person(42, "Crystal"), person(51, "Paul"), person(81, "Barbara") 323 | const auto friends_and_family = friends.union_with(family); 324 | 325 | // all set keys in a vetor 326 | const fcpp::vector = friends_and_family.keys(); 327 | ``` 328 | 329 | ### zip, map, filter, reduce 330 | ```c++ 331 | #include "set.h" // instead of 332 | 333 | // the employees' ages 334 | const fcpp::set ages({ 25, 45, 30, 63 }); 335 | 336 | // the employees' names 337 | const fcpp::set names({ "Jake", "Bob", "Michael", "Philipp" }); 338 | 339 | const auto employees_below_40 = ages 340 | // zip two sets for simultaneous processing 341 | .zip(names) 342 | 343 | // apply the functional map algorithm (transform from one type to another) 344 | .map([](const std::pair& pair) { 345 | return person(pair.first, pair.second); 346 | }) 347 | 348 | // filter the elements using a local function (lambda) 349 | .filter([](const person& p) { 350 | return p.age < 40; 351 | }); 352 | 353 | /* 354 | prints the following: 355 | Jake is 30 years old. 356 | Bob is 25 years old. 357 | */ 358 | employees_below_40.for_each([](const person& p) { 359 | std::cout << p.name << " is " << p.age << " years old." << std::endl; 360 | }); 361 | 362 | // total_age = 55 363 | const auto total_age = employees_below_40.reduce(0, [](const int& partial_sum, const person& p){ 364 | return partial_sum + p.age; 365 | }); 366 | ``` 367 | 368 | ### all_of, any_of, none_of 369 | ```c++ 370 | #include "set.h" // instead of 371 | 372 | fcpp::set numbers({1, 4, 2, 5, 8, 3, 7}); 373 | 374 | // returns true 375 | numbers.all_of([](const int& number) { 376 | return number < 10; 377 | }); 378 | 379 | // returns false 380 | numbers.all_of([](const int& number) { 381 | return number > 2; 382 | }); 383 | 384 | // returns true 385 | numbers.any_of([](const int& number) { 386 | return number < 5; 387 | }); 388 | 389 | // returns false 390 | numbers.any_of([](const int& number) { 391 | return number > 9; 392 | }); 393 | 394 | // returns true 395 | numbers.none_of([](const int& number) { 396 | return number < -2; 397 | }); 398 | 399 | // returns false 400 | numbers.none_of([](const int& number) { 401 | return number > 7; 402 | }); 403 | ``` 404 | 405 | ### remove, insert, contains, size, clear 406 | ```c++ 407 | #include "set.h" // instead of 408 | 409 | fcpp::set numbers({1, 2, 3, 4, 5, 7, 8}); 410 | 411 | // numbers -> fcpp::set numbers({1, 2, 3, 5, 7, 8}); 412 | numbers.remove(4); 413 | 414 | // numbers -> fcpp::set numbers({1, 2, 3, 5, 7, 8, 10}); 415 | numbers.insert(10); 416 | 417 | // returns true 418 | numbers.contains(10); 419 | 420 | // returns false 421 | numbers.contains(25); 422 | 423 | // returns 7 424 | numbers.size(); 425 | 426 | // removes all keys 427 | numbers.clear(); 428 | ``` 429 | -------------------------------------------------------------------------------- /tests/set_test.cc: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2023 Ioannis Kaliakatsos 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | #include "warnings.h" 25 | #include "set.h" 26 | #include "vector.h" 27 | #include "test_types.h" 28 | 29 | using namespace fcpp; 30 | 31 | template 32 | void debug(set& set) 33 | { 34 | set.for_each([](const T& element){ 35 | std::cout << element << std::endl; 36 | }); 37 | } 38 | 39 | void test_contents(const set& set) 40 | { 41 | EXPECT_EQ(3, set.size()); 42 | EXPECT_EQ(1, set[0]); 43 | EXPECT_EQ(3, set[1]); 44 | EXPECT_EQ(5, set[2]); 45 | } 46 | 47 | TEST(SetTest, EmptyConstructor) 48 | { 49 | const set set_under_test; 50 | EXPECT_EQ(0, set_under_test.size()); 51 | } 52 | 53 | TEST(SetTest, StdSetConstructor) 54 | { 55 | const set set_under_test(std::set({1, 5, 3, 3})); 56 | test_contents(set_under_test); 57 | } 58 | 59 | TEST(SetTest, StdVectorConstructor) 60 | { 61 | const set set_under_test(std::vector({1, 5, 3, 3})); 62 | test_contents(set_under_test); 63 | } 64 | 65 | TEST(SetTest, FunctionalVectorConstructor) 66 | { 67 | const set set_under_test(vector({1, 5, 3, 3})); 68 | test_contents(set_under_test); 69 | } 70 | 71 | TEST(SetTest, StdInitializerListConstructor) 72 | { 73 | const set set_under_test(std::initializer_list({1, 5, 3, 3})); 74 | test_contents(set_under_test); 75 | } 76 | 77 | TEST(SetTest, Subscripting) 78 | { 79 | const set set_under_test(std::set({1, 5, 3, 3})); 80 | test_contents(set_under_test); 81 | } 82 | 83 | TEST(SetTest, ConstSubscripting) 84 | { 85 | const set set_under_test(std::set({1, 5, 3, 3})); 86 | test_contents(set_under_test); 87 | } 88 | 89 | TEST(SetTest, Difference) 90 | { 91 | const set set1(std::set({1, 2, 3, 5, 7, 8, 10})); 92 | const set set2(std::set({2, 5, 7, 10, 15, 17})); 93 | const auto& diff = set1.difference_with(set2); 94 | EXPECT_EQ(set({1, 3, 8}), diff); 95 | } 96 | 97 | TEST(SetTest, DifferenceStdSet) 98 | { 99 | const set set1(std::set({1, 2, 3, 5, 7, 8, 10})); 100 | const std::set set2({2, 5, 7, 10, 15, 17}); 101 | const auto& diff = set1.difference_with(set2); 102 | EXPECT_EQ(set({1, 3, 8}), diff); 103 | } 104 | 105 | TEST(SetTest, DifferenceFunctionalSet) 106 | { 107 | const set set1({1, 2, 3, 5, 7, 8, 10}); 108 | const set set2({2, 5, 7, 10, 15, 17}); 109 | const auto& diff = set1.difference_with(set2); 110 | EXPECT_EQ(set({1, 3, 8}), diff); 111 | } 112 | 113 | TEST(SetTest, DifferenceFunctionalSetCustomType) 114 | { 115 | const set set1({ 116 | person(51, "George"), 117 | person(81, "Jackie"), 118 | person(15, "Jake"), 119 | person(18, "Jannet"), 120 | person(25, "Kate") 121 | }); 122 | 123 | const set set2({ 124 | person(51, "George"), 125 | person(81, "Jackie"), 126 | }); 127 | 128 | const auto& diff = set1.difference_with(set2); 129 | 130 | const set expected({ 131 | person(15, "Jake"), 132 | person(18, "Jannet"), 133 | person(25, "Kate") 134 | }); 135 | 136 | EXPECT_EQ(expected, diff); 137 | } 138 | 139 | TEST(SetTest, Union) 140 | { 141 | const set set1(std::set({1, 2, 3, 5, 7, 8, 10})); 142 | const set set2(std::set({2, 5, 7, 10, 15, 17})); 143 | const auto& combined = set1.union_with(set2); 144 | EXPECT_EQ(set({1, 2, 3, 5, 7, 8, 10, 15, 17}), combined); 145 | } 146 | 147 | TEST(SetTest, UnionStdSet) 148 | { 149 | const set set1(std::set({1, 2, 3, 5, 7, 8, 10})); 150 | const std::set set2({2, 5, 7, 10, 15, 17}); 151 | const auto& combined = set1.union_with(set2); 152 | EXPECT_EQ(set({1, 2, 3, 5, 7, 8, 10, 15, 17}), combined); 153 | } 154 | 155 | TEST(SetTest, UnionFunctionalSet) 156 | { 157 | const set set1({1, 2, 3, 5, 7, 8, 10}); 158 | const set set2({2, 5, 7, 10, 15, 17}); 159 | const auto& combined = set1.union_with(set2); 160 | EXPECT_EQ(set({1, 2, 3, 5, 7, 8, 10, 15, 17}), combined); 161 | } 162 | 163 | TEST(SetTest, UnionFunctionalSetCustomType) 164 | { 165 | const set set1({ 166 | person(15, "Jake"), 167 | person(18, "Jannet"), 168 | person(25, "Kate") 169 | }); 170 | 171 | const set set2({ 172 | person(51, "George"), 173 | person(81, "Jackie"), 174 | }); 175 | 176 | const auto& combined = set1.union_with(set2); 177 | 178 | const set expected({ 179 | person(15, "Jake"), 180 | person(18, "Jannet"), 181 | person(25, "Kate"), 182 | person(51, "George"), 183 | person(81, "Jackie"), 184 | }); 185 | 186 | EXPECT_EQ(expected, combined); 187 | } 188 | 189 | TEST(SetTest, Intersection) 190 | { 191 | const set set1(std::set({1, 2, 3, 5, 7, 8, 10})); 192 | const set set2(std::set({2, 5, 7, 10, 15, 17})); 193 | const auto& intersection = set1.intersect_with(set2); 194 | EXPECT_EQ(set({2, 5, 7, 10}), intersection); 195 | } 196 | 197 | TEST(SetTest, IntersectionStdSet) 198 | { 199 | const set set1(std::set({1, 2, 3, 5, 7, 8, 10})); 200 | const std::set set2({2, 5, 7, 10, 15, 17}); 201 | const auto& intersection = set1.intersect_with(set2); 202 | EXPECT_EQ(set({2, 5, 7, 10}), intersection); 203 | } 204 | 205 | TEST(SetTest, IntersectionFunctionalSet) 206 | { 207 | const set set1({1, 2, 3, 5, 7, 8, 10}); 208 | const set set2({2, 5, 7, 10, 15, 17}); 209 | const auto& intersection = set1.intersect_with(set2); 210 | EXPECT_EQ(set({2, 5, 7, 10}), intersection); 211 | } 212 | 213 | TEST(SetTest, IntersectionFunctionalSetCustomType) 214 | { 215 | const set set1({ 216 | person(15, "Jake"), 217 | person(18, "Jannet"), 218 | person(25, "Kate"), 219 | person(51, "George"), 220 | person(81, "Jackie"), 221 | }); 222 | 223 | const set set2({ 224 | person(39, "Robert"), 225 | person(18, "Jannet"), 226 | person(25, "Kate"), 227 | person(52, "Anna"), 228 | person(63, "Simon"), 229 | }); 230 | 231 | const auto& intersection = set1.intersect_with(set2); 232 | 233 | const set expected({ 234 | person(18, "Jannet"), 235 | person(25, "Kate"), 236 | }); 237 | 238 | EXPECT_EQ(expected, intersection); 239 | } 240 | 241 | TEST(SetTest, Min) 242 | { 243 | const set numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 244 | const auto minimum = numbers.min(); 245 | EXPECT_TRUE(minimum.has_value()); 246 | EXPECT_EQ(1, minimum.value()); 247 | } 248 | 249 | TEST(SetTest, MinCustomType) 250 | { 251 | const set persons({ 252 | person(15, "Jake"), 253 | person(18, "Jannet"), 254 | person(25, "Kate"), 255 | person(62, "Bob") 256 | }); 257 | const auto minimum = persons.min(); 258 | #if defined(__clang__) 259 | EXPECT_EQ(person(18, "Jannet"), minimum.value()); 260 | #else 261 | EXPECT_EQ(person(62, "Bob"), minimum.value()); 262 | #endif 263 | } 264 | 265 | TEST(SetTest, MinEmptySet) 266 | { 267 | const set numbers; 268 | const auto minimum = numbers.min(); 269 | EXPECT_FALSE(minimum.has_value()); 270 | } 271 | 272 | TEST(SetTest, Max) 273 | { 274 | const set numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 275 | const auto maximum = numbers.max(); 276 | EXPECT_TRUE(maximum.has_value()); 277 | EXPECT_EQ(8, maximum.value()); 278 | } 279 | 280 | TEST(SetTest, MaxCustomType) 281 | { 282 | const set persons({ 283 | person(15, "Jake"), 284 | person(18, "Jannet"), 285 | person(25, "Kate"), 286 | person(62, "Bob") 287 | }); 288 | const auto maximum = persons.max(); 289 | std::cout << maximum.value().name << std::endl; 290 | #if __linux__ // NOLINT(clang-diagnostic-undef) 291 | EXPECT_EQ(person(18, "Jannet"), maximum.value()); 292 | #else 293 | EXPECT_EQ(person(25, "Kate"), maximum.value()); 294 | #endif 295 | } 296 | 297 | TEST(SetTest, MaxEmptySet) 298 | { 299 | const set numbers; 300 | const auto maximum = numbers.max(); 301 | EXPECT_FALSE(maximum.has_value()); 302 | } 303 | 304 | TEST(SetTest, Map) 305 | { 306 | const set numbers({4, 1, 3}); 307 | const auto mapped_set = numbers.map([](const int& age){ 308 | return child(age); 309 | }); 310 | EXPECT_EQ(3, mapped_set.size()); 311 | EXPECT_EQ(1, mapped_set[0].age); 312 | EXPECT_EQ(3, mapped_set[1].age); 313 | EXPECT_EQ(4, mapped_set[2].age); 314 | } 315 | 316 | TEST(SetTest, AllOf) 317 | { 318 | const set numbers({1, 4, 2, 5, 8, 3}); 319 | EXPECT_TRUE(numbers.all_of([](const int &number) { return number < 10; })); 320 | EXPECT_FALSE(numbers.all_of([](const int &number) { return number > 2; })); 321 | } 322 | 323 | TEST(SetTest, AnyOf) 324 | { 325 | const set numbers({1, 4, 2, 5, 8, 3}); 326 | EXPECT_TRUE(numbers.any_of([](const int &number) { return number < 5; })); 327 | EXPECT_FALSE(numbers.any_of([](const int &number) { return number > 10; })); 328 | } 329 | 330 | TEST(SetTest, NoneOf) 331 | { 332 | const set numbers({1, 4, 2, 5, 8, 3}); 333 | EXPECT_TRUE(numbers.none_of([](const int &number) { return number > 10; })); 334 | EXPECT_FALSE(numbers.none_of([](const int &number) { return number < 6; })); 335 | } 336 | 337 | TEST(SetTest, Reduce) 338 | { 339 | const set tokens({"the", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "brown", "dog"}); 340 | const auto sentence = tokens.reduce(std::string(""), [](const std::string& partial, const std::string& token){ 341 | return partial.length() != 0 342 | ? partial + " " + token 343 | : token; 344 | }); 345 | EXPECT_EQ("brown dog fox jumps lazy over quick the", sentence); 346 | } 347 | 348 | TEST(SetTest, Filter) 349 | { 350 | set numbers({1, 3, -5, 2, -1, 9, -4}); 351 | numbers.filter([](const int& element){ 352 | return element >= 1.5; 353 | }); 354 | EXPECT_EQ(set({2, 3, 9}), numbers); 355 | } 356 | 357 | TEST(SetTest, Filtered) 358 | { 359 | const set numbers({1, 3, -5, 2, -1, 9, -4}); 360 | auto filtered_numbers = numbers.filtered([](const int& element){ 361 | return element >= 1.5; 362 | }); 363 | EXPECT_EQ(set({2, 3, 9}), filtered_numbers); 364 | EXPECT_EQ(set({ 1, 3, -5, 2, -1, 9, -4 }), numbers); 365 | } 366 | 367 | TEST(SetTest, ZipWithFunctionalSet) 368 | { 369 | const set ages({25, 45, 30, 63}); 370 | const set persons({"Jake", "Bob", "Michael", "Philipp"}); 371 | const auto zipped = ages.zip(persons); 372 | const auto expected = set>({ 373 | std::pair(25, "Bob"), 374 | std::pair(30, "Jake"), 375 | std::pair(45, "Michael"), 376 | std::pair(63, "Philipp"), 377 | }); 378 | EXPECT_EQ(expected, zipped); 379 | } 380 | 381 | TEST(SetTest, ZipWithFunctionalSetDifferentSizes) 382 | { 383 | const set ages({25, 45, 30, 63}); 384 | const set persons({"Jake"}); 385 | EXPECT_DEATH(ages.zip(persons), ""); 386 | } 387 | 388 | TEST(SetTest, ZipWithStdSet) 389 | { 390 | const set ages({25, 45, 30, 63}); 391 | const std::set persons({"Jake", "Bob", "Michael", "Philipp"}); 392 | const auto zipped = ages.zip(persons); 393 | const auto expected = set>({ 394 | std::pair(25, "Bob"), 395 | std::pair(30, "Jake"), 396 | std::pair(45, "Michael"), 397 | std::pair(63, "Philipp"), 398 | }); 399 | EXPECT_EQ(expected, zipped); 400 | } 401 | 402 | TEST(SetTest, ZipWithStdSetDifferentSizes) 403 | { 404 | const set ages({25, 45, 30, 63}); 405 | const std::set persons({"Jake"}); 406 | EXPECT_DEATH(ages.zip(persons), ""); 407 | } 408 | 409 | TEST(SetTest, ZipWithFunctionalVector) 410 | { 411 | const set ages({25, 45, 30, 63}); 412 | const vector persons({"Jake", "Bob", "Michael", "Philipp"}); 413 | const auto zipped = ages.zip(persons); 414 | const auto expected = set>({ 415 | std::pair(25, "Bob"), 416 | std::pair(30, "Jake"), 417 | std::pair(45, "Michael"), 418 | std::pair(63, "Philipp"), 419 | }); 420 | EXPECT_EQ(expected, zipped); 421 | } 422 | 423 | TEST(SetTest, ZipWithFunctionalVectorDifferentSizes) 424 | { 425 | const set ages({25, 45, 30, 63}); 426 | const vector persons({"Jake"}); 427 | EXPECT_DEATH(ages.zip(persons), ""); 428 | } 429 | 430 | TEST(SetTest, ZipWithStdVector) 431 | { 432 | const set ages({25, 45, 30, 63}); 433 | const std::vector persons({"Jake", "Bob", "Michael", "Philipp"}); 434 | const auto zipped = ages.zip(persons); 435 | const auto expected = set>({ 436 | std::pair(25, "Bob"), 437 | std::pair(30, "Jake"), 438 | std::pair(45, "Michael"), 439 | std::pair(63, "Philipp"), 440 | }); 441 | EXPECT_EQ(expected, zipped); 442 | } 443 | 444 | TEST(SetTest, ZipWithStdVectorDifferentSizes) 445 | { 446 | const set ages({25, 45, 30, 63}); 447 | const std::vector persons({"Jake"}); 448 | EXPECT_DEATH(ages.zip(persons), ""); 449 | } 450 | 451 | TEST(SetTest, Keys) 452 | { 453 | const set numbers({25, 45, 30, 63}); 454 | const auto keys = numbers.keys(); 455 | EXPECT_EQ(vector({25, 30, 45, 63}), keys); 456 | } 457 | 458 | TEST(SetTest, RemoveExistingElement) 459 | { 460 | set numbers({1, 4, 2}); 461 | numbers.remove(4); 462 | EXPECT_EQ(set({1, 2}), numbers); 463 | } 464 | 465 | TEST(SetTest, RemoveNonExistentElement) 466 | { 467 | set numbers({1, 4, 2}); 468 | numbers.remove(18); 469 | EXPECT_EQ(set({1, 2, 4}), numbers); 470 | } 471 | 472 | TEST(SetTest, RemovingExistingElement) 473 | { 474 | const set numbers({1, 4, 2}); 475 | const auto less_numbers = numbers.removing(4); 476 | EXPECT_EQ(set({1, 2}), less_numbers); 477 | EXPECT_EQ(set({1, 2, 4}), numbers); 478 | } 479 | 480 | TEST(SetTest, InsertNewElement) 481 | { 482 | set numbers({1, 4, 2}); 483 | numbers.insert(18); 484 | EXPECT_EQ(set({1, 2, 4, 18}), numbers); 485 | } 486 | 487 | TEST(SetTest, InsertingNewElement) 488 | { 489 | const set numbers({1, 4, 2}); 490 | const auto augmented_numbers = numbers.inserting(18); 491 | EXPECT_EQ(set({1, 2, 4, 18}), augmented_numbers); 492 | EXPECT_EQ(set({1, 2, 4}), numbers); 493 | } 494 | 495 | TEST(SetTest, InsertExistingElement) 496 | { 497 | set numbers({1, 4, 2}); 498 | numbers.insert(2); 499 | EXPECT_EQ(set({1, 2, 4}), numbers); 500 | } 501 | 502 | TEST(SetTest, InsertingExistingElement) 503 | { 504 | const set numbers({1, 4, 2}); 505 | const auto augmented_numbers = numbers.inserting(2); 506 | EXPECT_EQ(set({1, 2, 4}), augmented_numbers); 507 | EXPECT_EQ(set({1, 2, 4}), numbers); 508 | } 509 | 510 | TEST(SetTest, Clear) 511 | { 512 | set numbers({1, 4, 2}); 513 | numbers.clear(); 514 | EXPECT_EQ(0, numbers.size()); 515 | } 516 | 517 | TEST(SetTest, Clearing) 518 | { 519 | const set numbers({1, 4, 2}); 520 | const auto cleared_numbers = numbers.clearing(); 521 | EXPECT_EQ(0, cleared_numbers.size()); 522 | EXPECT_EQ(3, numbers.size()); 523 | } 524 | 525 | TEST(SetTest, IsEmpty) 526 | { 527 | const set numbers({1, 4, 2}); 528 | EXPECT_FALSE(numbers.is_empty()); 529 | EXPECT_TRUE(set().is_empty()); 530 | } 531 | 532 | TEST(SetTest, Contains) 533 | { 534 | const set numbers({1, 4, 2}); 535 | EXPECT_TRUE(numbers.contains(1)); 536 | EXPECT_FALSE(numbers.contains(15)); 537 | } 538 | 539 | TEST(SetTest, EqualityOperator) 540 | { 541 | const set set1(std::set({1, 2, 3})); 542 | const set set2(std::set({1, 2, 3, 2, 3})); 543 | EXPECT_TRUE(set1 == set2); 544 | EXPECT_FALSE(set1 != set2); 545 | } 546 | 547 | TEST(SetTest, InequalityOperator) 548 | { 549 | const set set1(std::set({1, 2, 3})); 550 | const set set2(std::set({1, 2, 3, 4})); 551 | EXPECT_FALSE(set1 == set2); 552 | EXPECT_TRUE(set1 != set2); 553 | } 554 | 555 | TEST(SetTest, EqualityOperatorCustomType) 556 | { 557 | const set set1({ 558 | person(15, "Jake"), 559 | person(18, "Jannet"), 560 | person(25, "Kate") 561 | }); 562 | 563 | const set set2({ 564 | person(15, "Jake"), 565 | person(18, "Jannet"), 566 | person(25, "Kate") 567 | }); 568 | 569 | EXPECT_TRUE(set1 == set2); 570 | EXPECT_FALSE(set1 != set2); 571 | } 572 | -------------------------------------------------------------------------------- /include/set.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2023 Ioannis Kaliakatsos 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | #include 25 | #include 26 | #include "optional.h" 27 | 28 | namespace fcpp { 29 | template 30 | class vector; 31 | 32 | // A lightweight wrapper around std::set, enabling fluent and functional 33 | // programming on the set itself, rather than using the more procedural style 34 | // of the standard library algorithms. 35 | // 36 | // Member functions can be mutating (eg. my_set.insert()) or 37 | // non-mutating (eg. my_vector.inserting()) enforcing thread safety if needed 38 | template > 39 | class set 40 | { 41 | public: 42 | set() 43 | : m_set() 44 | { 45 | } 46 | 47 | explicit set(std::set set) 48 | : m_set(std::move(set)) 49 | { 50 | } 51 | 52 | explicit set(const std::vector& vector) 53 | : m_set(vector.begin(), vector.end()) 54 | { 55 | } 56 | 57 | explicit set(const vector& vector) 58 | : m_set(vector.begin(), vector.end()) 59 | { 60 | } 61 | 62 | explicit set(const std::initializer_list& list) 63 | : m_set(list.begin(), list.end()) 64 | { 65 | } 66 | 67 | // Returns the set of elements which belong to the current set but not in the other set. 68 | // In Venn diagram notation, if A is the current set and B is the other set, then 69 | // the difference is the operation A – B = {x : x ∈ A and x ∉ B} 70 | // 71 | // example: 72 | // const fcpp::set set1(std::set({1, 2, 3, 5, 7, 8, 10})); 73 | // const fcpp::set set2(std::set({2, 5, 7, 10, 15, 17})); 74 | // const auto& diff = set1.difference(set2); 75 | // 76 | // outcome: 77 | // diff -> fcpp::set({1, 3, 8}) 78 | [[nodiscard]] set difference_with(const set& other) const 79 | { 80 | std::set diff; 81 | std::set_difference(begin(), 82 | end(), 83 | other.begin(), 84 | other.end(), 85 | std::inserter(diff, diff.begin())); 86 | return set(diff); 87 | } 88 | 89 | [[nodiscard]] set difference_with(const std::set& other) const 90 | { 91 | return difference_with(set(other)); 92 | } 93 | 94 | // Returns the set of elements which belong either to the current or the other set. 95 | // In Venn diagram notation, if A is the current set and B is the other set, then 96 | // the union is the operation A ∪ B = {x : x ∈ A or x ∈ B} 97 | // 98 | // example: 99 | // const fcpp::set set1(std::set({1, 2, 3, 5, 7, 8, 10})); 100 | // const fcpp::set set2(std::set({2, 5, 7, 10, 15, 17})); 101 | // const auto& combined = set1.set_union(set2); 102 | // 103 | // outcome: 104 | // combined -> fcpp::set({1, 2, 3, 5, 7, 8, 10, 15, 17}) 105 | [[nodiscard]] set union_with(const set& other) const 106 | { 107 | std::set combined; 108 | std::set_union(begin(), 109 | end(), 110 | other.begin(), 111 | other.end(), 112 | std::inserter(combined, combined.begin())); 113 | return set(combined); 114 | } 115 | 116 | [[nodiscard]] set union_with(const std::set& other) const 117 | { 118 | return union_with(set(other)); 119 | } 120 | 121 | // Returns the set of elements which belong to both the current and the other set. 122 | // In Venn diagram notation, if A is the current set and B is the other set, then 123 | // the intersection is the operation A ∩ B = {x : x ∈ A and x ∈ B} 124 | // 125 | // example: 126 | // const fcpp::set set1(std::set({1, 2, 3, 5, 7, 8, 10})); 127 | // const fcpp::set set2(std::set({2, 5, 7, 10, 15, 17})); 128 | // const auto& combined = set1.set_union(set2); 129 | // 130 | // outcome: 131 | // combined -> fcpp::set({2, 5, 7, 10}) 132 | [[nodiscard]] set intersect_with(const set& other) const 133 | { 134 | std::set intersection; 135 | std::set_intersection(begin(), 136 | end(), 137 | other.begin(), 138 | other.end(), 139 | std::inserter(intersection, intersection.begin())); 140 | return set(intersection); 141 | } 142 | 143 | [[nodiscard]] set intersect_with(const std::set& other) const 144 | { 145 | return intersect_with(set(other)); 146 | } 147 | 148 | // Returns the minimum key in the set, if it's not empty. 149 | // 150 | // example: 151 | // const fcpp::set numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 152 | // auto minimum = numbers.min(); 153 | // 154 | // // an empty's set minimum value 155 | // fcpp::set().min().has_value() // false 156 | // 157 | // outcome: 158 | // minimum.has_value() -> true 159 | // minimum.value() -> 1 160 | [[nodiscard]] fcpp::optional_t min() const 161 | { 162 | const auto& it = std::min_element(begin(), end()); 163 | if (it != end()) { 164 | return *it; 165 | } 166 | return fcpp::optional_t(); 167 | } 168 | 169 | // Returns the maximum key in the set, if it's not empty. 170 | // 171 | // example: 172 | // const fcpp::set numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 173 | // auto maximum = numbers.max(); 174 | // 175 | // // an empty's set maximum value 176 | // fcpp::set().max().has_value() // false 177 | // 178 | // outcome: 179 | // maximum.has_value() -> true 180 | // maximum.value() -> 8 181 | [[nodiscard]] fcpp::optional_t max() const 182 | { 183 | const auto& it = std::max_element(begin(), end()); 184 | if (it != end()) { 185 | return *it; 186 | } 187 | return fcpp::optional_t(); 188 | } 189 | 190 | // Performs the functional `map` algorithm, in which every element of the resulting set is the 191 | // output of applying the transform function on every element of this instance. 192 | // 193 | // example: 194 | // const fcpp::vector input_set({ 1, 3, -5 }); 195 | // const auto output_set = input_set.map([](const int& element) { 196 | // return std::to_string(element); 197 | // }); 198 | // 199 | // outcome: 200 | // output_set -> fcpp::set({ "-5", "1", "3" }) 201 | // 202 | // is equivalent to: 203 | // const fcpp::set input_set({ 1, 3, -5 }); 204 | // fcpp::set output_set; 205 | // for (auto const& key: input_set) { 206 | // output_set.insert(std::to_string(key)); 207 | // } 208 | #ifdef CPP17_AVAILABLE 209 | template , typename Transform, typename = std::enable_if_t< 210 | std::is_invocable_r_v>> 211 | #else 212 | template , typename Transform> 213 | #endif 214 | set map(Transform&& transform) const 215 | { 216 | std::set transformed_set; 217 | for (const auto& key : m_set) { 218 | transformed_set.insert(transform(key)); 219 | } 220 | return set(transformed_set); 221 | } 222 | 223 | // Returns true if all keys match the predicate (return true) 224 | // 225 | // example: 226 | // const fcpp::set numbers({1, 4, 2, 5, 8, 3}); 227 | // 228 | // // returns true 229 | // numbers.all_of([](const int &number) { 230 | // return number < 10; 231 | // }); 232 | // 233 | // // returns false 234 | // numbers.all_of([](const int &number) { 235 | // return number > 2; 236 | // }); 237 | #ifdef CPP17_AVAILABLE 238 | template >> 239 | #else 240 | template 241 | #endif 242 | bool all_of(Callable&& unary_predicate) const 243 | { 244 | return std::all_of(begin(), 245 | end(), 246 | std::forward(unary_predicate)); 247 | } 248 | 249 | // Returns true if at least one key match the predicate (returns true) 250 | // 251 | // example: 252 | // const fcpp::set numbers({1, 4, 2, 5, 8, 3}); 253 | // 254 | // // returns true 255 | // numbers.any_of([](const int &number) { 256 | // return number < 5; 257 | // }); 258 | // 259 | // // returns false 260 | // numbers.any_of([](const int &number) { 261 | // return number > 10; 262 | // }); 263 | #ifdef CPP17_AVAILABLE 264 | template >> 265 | #else 266 | template 267 | #endif 268 | bool any_of(Callable&& unary_predicate) const 269 | { 270 | return std::any_of(begin(), 271 | end(), 272 | std::forward(unary_predicate)); 273 | } 274 | 275 | // Returns true if none of the keys match the predicate (all return false) 276 | // 277 | // example: 278 | // const fcpp::set numbers({1, 4, 2, 5, 8, 3}); 279 | // 280 | // // returns true 281 | // numbers.none_of([](const int &number) { 282 | // return number > 10; 283 | // }); 284 | // 285 | // // returns false 286 | // numbers.none_of([](const int &number) { 287 | // return number < 6; 288 | // }); 289 | #ifdef CPP17_AVAILABLE 290 | template >> 291 | #else 292 | template 293 | #endif 294 | bool none_of(Callable&& unary_predicate) const 295 | { 296 | return std::none_of(begin(), 297 | end(), 298 | std::forward(unary_predicate)); 299 | } 300 | 301 | // Performs the functional `reduce` (fold/accumulate) algorithm, by returning the result of 302 | // accumulating all the values in the vector to an initial value. (non-mutating) 303 | // 304 | // example: 305 | // const fcpp::set tokens({ "the", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "brown", "dog" }); 306 | // const auto sentence = tokens.reduce("", [](const std::string& partial, const std::string& token) { 307 | // return partial.length() != 0 308 | // ? partial + " " + token 309 | // : token; 310 | // }); 311 | // 312 | // outcome: (a set does not allow multiple entries, and its elements are internally managed, order can vary) 313 | // 314 | // sentence -> std::string("brown dog fox jumps lazy over quick the"); 315 | #ifdef CPP17_AVAILABLE 316 | template >> 317 | #else 318 | template 319 | #endif 320 | U reduce(const U& initial, Reduce&& reduction) const 321 | { 322 | auto result = initial; 323 | for (const auto& x : m_set) { 324 | result = reduction(result, x); 325 | } 326 | return result; 327 | } 328 | 329 | // Performs the functional `filter` algorithm, in which all keys of this instance 330 | // which match the given predicate are kept (mutating) 331 | // 332 | // example: 333 | // fcpp::set numbers({ 1, 3, -5, 2, -1, 9, -4 }); 334 | // numbers.filter([](const int& element) { 335 | // return element >= 1.5; 336 | // }); 337 | // 338 | // outcome: 339 | // numbers -> fcpp::set({ 2, 3, 9 }); 340 | // 341 | // is equivalent to: 342 | // fcpp::set numbers({ 1, 3, -5, 2, -1, 9, -4 }); 343 | // for (auto i = 0; i < numbers.size(); ++i) { 344 | // if (numbers[i] >= 1.5) { 345 | // continue; 346 | // } 347 | // numbers.remove(i); 348 | // i--; 349 | // } 350 | #ifdef CPP17_AVAILABLE 351 | template >> 352 | #else 353 | template 354 | #endif 355 | set& filter(Filter&& predicate_to_keep) 356 | { 357 | std::set copy; 358 | auto it = begin(); 359 | for (; it != end(); ++it) { 360 | if (predicate_to_keep(*it)) { 361 | copy.insert(*it); 362 | } 363 | } 364 | m_set = std::move(copy); 365 | return *this; 366 | } 367 | 368 | // Performs the functional `filter` algorithm in a copy of this instance, in which all keys 369 | // of the copy which match the given predicate are kept (non-mutating) 370 | // 371 | // example: 372 | // const fcpp::set numbers({ 1, 3, -5, 2, -1, 9, -4 }); 373 | // auto filtered_numbers = numbers.filtered([](const int& element) { 374 | // return element >= 1.5; 375 | // }); 376 | // 377 | // outcome: 378 | // filtered_numbers -> fcpp::set({ 2, 3, 9 }); 379 | // numbers -> fcpp::set({ 1, 3, -5, 2, -1, 9, -4 }); 380 | #ifdef CPP17_AVAILABLE 381 | template >> 382 | #else 383 | template 384 | #endif 385 | set filtered(Filter&& predicate_to_keep) const 386 | { 387 | std::set copy; 388 | auto it = begin(); 389 | for (; it != end(); ++it) { 390 | if (predicate_to_keep(*it)) { 391 | copy.insert(*it); 392 | } 393 | } 394 | return set(copy); 395 | } 396 | 397 | #ifdef CPP17_AVAILABLE 398 | template 399 | using deref_type = typename std::iterator_traits::value_type; 400 | 401 | template 402 | struct is_valid_iterator 403 | { 404 | static bool const value = std::is_constructible_v>; 405 | }; 406 | #endif 407 | 408 | // Performs the functional `zip` algorithm, in which every key of the resulting set is a 409 | // tuple of this instance's key (first) and the second set's key (second). 410 | // The sizes of the two sets must be equal. 411 | // 412 | // example: 413 | // const fcpp::set ages({ 25, 45, 30, 63 }); 414 | // const fcpp::set persons({ "Jake", "Bob", "Michael", "Philipp" }); 415 | // const auto zipped = ages.zip(persons); 416 | // 417 | // outcome: 418 | // zipped -> fcpp::set>({ 419 | // std::pair(25, "Bob"), 420 | // std::pair(30, "Jake"), 421 | // std::pair(45, "Michael"), 422 | // std::pair(63, "Philipp"), 423 | // }) 424 | template 425 | [[nodiscard]] set> zip(const set& set) const 426 | { 427 | #ifdef CPP17_AVAILABLE 428 | return zip_impl(set.begin(), set.end()); 429 | #else 430 | return zip_impl(set.begin(), set.end()); 431 | #endif 432 | } 433 | 434 | // Performs the functional `zip` algorithm. 435 | // The number of keys must match the set's size. 436 | // For more details, see the zip function which accepts a fcpp::set as input. 437 | template 438 | [[nodiscard]] set> zip(const std::set& set) const 439 | { 440 | return zip(fcpp::set(set)); 441 | } 442 | 443 | // Performs the functional `zip` algorithm by using the unique values of the vector. 444 | // The number of uniques vector values must match the set's size. 445 | // For more details, see the zip function which accepts a fcpp::set as input. 446 | template 447 | [[nodiscard]] set> zip(const vector& vector) const 448 | { 449 | const auto distinct_values = vector.distinct(); 450 | return zip(distinct_values); 451 | } 452 | 453 | // Performs the functional `zip` algorithm by using the unique values of the vector. 454 | // The number of uniques vector values must match the set's size. 455 | // For more details, see the zip function which accepts a fcpp::set as input. 456 | template 457 | [[nodiscard]] set> zip(const std::vector& vector) const 458 | { 459 | return zip(fcpp::vector(vector)); 460 | } 461 | 462 | // Executes the given operation for each key of the set. 463 | // The operation must not change the set's contents during execution. 464 | #ifdef CPP17_AVAILABLE 465 | template >> 466 | #else 467 | template 468 | #endif 469 | const set& for_each(Callable&& operation) const 470 | { 471 | std::for_each(begin(), 472 | end(), 473 | std::forward(operation)); 474 | return *this; 475 | } 476 | 477 | vector keys() const 478 | { 479 | vector vec; 480 | vec.reserve(size()); 481 | for_each([&vec](const TKey& key){ 482 | vec.insert_back(key); 483 | }); 484 | return std::move(vec); 485 | } 486 | 487 | // Removes an element from the set, if it exists, potentially changing the set's contents (mutating) 488 | // 489 | // example: 490 | // fcpp::set numbers({1, 4, 2}); 491 | // numbers.remove(4); 492 | // 493 | // outcome: 494 | // numbers -> fcpp::set({1, 2}) 495 | set& remove(const TKey& element) 496 | { 497 | m_set.erase(element); 498 | return *this; 499 | } 500 | 501 | // Returns a copy by removing an element from the set, if it exists (non-mutating) 502 | // 503 | // example: 504 | // const fcpp::set numbers({1, 4, 2}); 505 | // auto less_numbers = numbers.removing(4); 506 | // 507 | // outcome: 508 | // less_numbers -> fcpp::set({1, 2}) 509 | // numbers -> fcpp::set({1, 2, 4}) 510 | [[nodiscard]] set removing(const TKey& element) const 511 | { 512 | auto copy(m_set); 513 | copy.erase(element); 514 | return set(copy); 515 | } 516 | 517 | // Inserts an element in the set, if it does not already exist, potentially changing the set's contents (mutating) 518 | // 519 | // example: 520 | // fcpp::set numbers({1, 4, 2}); 521 | // numbers.insert(18); 522 | // 523 | // outcome: 524 | // numbers -> fcpp::set({1, 2, 4, 18}) 525 | set& insert(const TKey& element) 526 | { 527 | m_set.insert(element); 528 | return *this; 529 | } 530 | 531 | // Returns a copy by inserting an element in the set, if it does not already exist (non-mutating) 532 | // 533 | // example: 534 | // const fcpp::set numbers({1, 4, 2}); 535 | // auto augmented_numbers = numbers.inserting(18); 536 | // 537 | // outcome: 538 | // augmented_numbers -> fcpp::set({1, 2, 4, 18}) 539 | // numbers -> fcpp::set({1, 2, 4}) 540 | [[nodiscard]] set inserting(const TKey& element) const 541 | { 542 | auto copy(m_set); 543 | copy.insert(element); 544 | return set(copy); 545 | } 546 | 547 | // Removes all keys from the set (mutating) 548 | // 549 | // example: 550 | // fcpp::set numbers({1, 4, 2}); 551 | // numbers.clear(); 552 | // 553 | // outcome: 554 | // numbers -> fcpp::set({}) 555 | set& clear() 556 | { 557 | m_set.clear(); 558 | return *this; 559 | } 560 | 561 | // Returns a new set by clearing all keys from the current set (non-mutating) 562 | // 563 | // example: 564 | // const fcpp::set numbers({1, 4, 2}); 565 | // auto cleared_numbers = numbers.clearing(); 566 | // 567 | // outcome: 568 | // cleared_numbers -> fcpp::set({}) 569 | // numbers -> fcpp::set numbers({1, 4, 2}) 570 | [[nodiscard]] set clearing() const 571 | { 572 | return set(); 573 | } 574 | 575 | // Returns true if the set is empty 576 | // 577 | // example: 578 | // const fcpp::set numbers({1, 4, 2}); 579 | // 580 | // // returns false 581 | // numbers.is_empty(); 582 | // 583 | // // returns true 584 | // fcpp::set().is_empty(); 585 | [[nodiscard]] bool is_empty() const 586 | { 587 | return m_set.empty(); 588 | } 589 | 590 | // Returns true if the key is present in the set, otherwise false 591 | // 592 | // example: 593 | // const fcpp::set numbers({1, 4, 2}); 594 | // numbers.contains(1); // true 595 | // numbers.contains(15); // false 596 | [[nodiscard]] bool contains(const TKey& key) const 597 | { 598 | return m_set.count(key) != 0; 599 | } 600 | 601 | // Returns the size of the vector (how many elements it contains, it may be different from its capacity) 602 | [[nodiscard]] size_t size() const 603 | { 604 | return m_set.size(); 605 | } 606 | 607 | // Returns the begin iterator, useful for other standard library algorithms 608 | [[nodiscard]] typename std::set::iterator begin() 609 | { 610 | return m_set.begin(); 611 | } 612 | 613 | // Returns the const begin iterator, useful for other standard library algorithms 614 | [[nodiscard]] typename std::set::const_iterator begin() const 615 | { 616 | return m_set.begin(); 617 | } 618 | 619 | // Returns the end iterator, useful for other standard library algorithms 620 | [[nodiscard]] typename std::set::iterator end() 621 | { 622 | return m_set.end(); 623 | } 624 | 625 | // Returns the const end iterator, useful for other standard library algorithms 626 | [[nodiscard]] typename std::set::const_iterator end() const 627 | { 628 | return m_set.end(); 629 | } 630 | 631 | // Returns the given key in the current set, allowing subscripting. 632 | // Bounds checking (assert) is enabled for debug builds. 633 | // Performance is O(n), so be careful for performance critical code sections. 634 | TKey operator[](size_t index) 635 | { 636 | assert_smaller_size(index); 637 | #ifdef CPP17_AVAILABLE 638 | auto it = std::advance(begin(), index); 639 | return *it; 640 | #else 641 | auto count = 0; 642 | auto it = begin(); 643 | while (count++ < index) { 644 | ++it; 645 | } 646 | return *it; 647 | #endif 648 | } 649 | 650 | // Returns the given key in the current constant set, allowing subscripting. 651 | // Bounds checking (assert) is enabled for debug builds. 652 | // Performance is O(n), so be careful for performance critical code sections. 653 | TKey operator[](size_t index) const 654 | { 655 | assert_smaller_size(index); 656 | #ifdef CPP17_AVAILABLE 657 | auto it = begin(); 658 | std::advance(it, index); 659 | return *it; 660 | #else 661 | auto count = 0; 662 | auto it = begin(); 663 | while (count++ < index) { 664 | ++it; 665 | } 666 | return *it; 667 | #endif 668 | } 669 | 670 | // Returns true if both instances have equal sizes and the corresponding elements (keys) are equal 671 | bool operator ==(const set& rhs) const 672 | { 673 | #ifdef CPP17_AVAILABLE 674 | return std::equal(begin(), 675 | end(), 676 | rhs.begin(), 677 | rhs.end()); 678 | #else 679 | if (size() != rhs.size()) { 680 | return false; 681 | } 682 | 683 | auto it1 = begin(); 684 | auto it2 = rhs.begin(); 685 | while (it1 != end() && it2 != rhs.end()) { 686 | if (!(*it1 == *it2)) { 687 | return false; 688 | } 689 | ++it1; 690 | ++it2; 691 | } 692 | 693 | return true; 694 | #endif 695 | } 696 | 697 | // Returns false if either the sizes are not equal or at least one corresponding element (key) is not equal 698 | bool operator !=(const set& rhs) const 699 | { 700 | return !((*this) == rhs); 701 | } 702 | 703 | private: 704 | std::set m_set; 705 | 706 | void assert_smaller_size(const size_t index) const 707 | { 708 | assert(index < size()); 709 | } 710 | 711 | #ifdef CPP17_AVAILABLE 712 | template ::value>> 713 | [[nodiscard]] auto zip_impl(const Iterator& set_begin, const Iterator& set_end) const -> 714 | set>> 715 | { 716 | using UKey = deref_type; 717 | #else 718 | template 719 | [[nodiscard]] set> zip_impl(const typename std::set::const_iterator& set_begin, 720 | const typename std::set::const_iterator& set_end) const 721 | { 722 | #endif 723 | const auto vec_size = std::distance(set_begin, set_end); 724 | assert(size() == vec_size); 725 | std::set> combined_set; 726 | auto it1 = begin(); 727 | auto it2 = set_begin; 728 | for (; it1 != end() && it2 != set_end; ++it1, ++it2) { 729 | combined_set.insert({*it1, *it2}); 730 | } 731 | return set>(combined_set); 732 | } 733 | }; 734 | } 735 | -------------------------------------------------------------------------------- /tests/vector_test.cc: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2023 Ioannis Kaliakatsos 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | #include "vector.h" 25 | #include "set.h" 26 | #include "index_range.h" 27 | #include "test_types.h" 28 | #include "warnings.h" 29 | 30 | #pragma warning( push ) 31 | #pragma warning( disable : 4245) 32 | 33 | using namespace fcpp; 34 | 35 | template 36 | void debug(const vector& vec) 37 | { 38 | vec.for_each([](const T& element){ 39 | std::cout << element << std::endl; 40 | }); 41 | } 42 | 43 | TEST(VectorTest, InsertBack) 44 | { 45 | vector vector_under_test; 46 | EXPECT_EQ(0, vector_under_test.size()); 47 | 48 | vector_under_test.insert_back(5); 49 | EXPECT_EQ(1, vector_under_test.size()); 50 | EXPECT_EQ(5, vector_under_test[0]); 51 | 52 | vector_under_test.insert_back(-1); 53 | EXPECT_EQ(2, vector_under_test.size()); 54 | EXPECT_EQ(5, vector_under_test[0]); 55 | EXPECT_EQ(-1, vector_under_test[1]); 56 | } 57 | 58 | TEST(VectorTest, InsertFront) 59 | { 60 | vector vector_under_test; 61 | EXPECT_EQ(0, vector_under_test.size()); 62 | 63 | vector_under_test.insert_front(5); 64 | EXPECT_EQ(1, vector_under_test.size()); 65 | EXPECT_EQ(5, vector_under_test[0]); 66 | 67 | vector_under_test.insert_front(-1); 68 | EXPECT_EQ(2, vector_under_test.size()); 69 | EXPECT_EQ(-1, vector_under_test[0]); 70 | EXPECT_EQ(5, vector_under_test[1]); 71 | } 72 | 73 | TEST(VectorTest, InsertingBack) 74 | { 75 | const vector vector_under_test({3, 6, 2, 8}); 76 | const auto vector_new_instance = vector_under_test.inserting_back(5); 77 | EXPECT_EQ(4, vector_under_test.size()); 78 | EXPECT_EQ(3, vector_under_test[0]); 79 | EXPECT_EQ(8, vector_under_test[3]); 80 | EXPECT_EQ(vector({ 3, 6, 2, 8, 5 }), vector_new_instance); 81 | } 82 | 83 | TEST(VectorTest, InsertingFront) 84 | { 85 | const vector vector_under_test({3, 6, 2, 8}); 86 | const auto vector_new_instance = vector_under_test.inserting_front(5); 87 | EXPECT_EQ(4, vector_under_test.size()); 88 | EXPECT_EQ(3, vector_under_test[0]); 89 | EXPECT_EQ(8, vector_under_test[3]); 90 | EXPECT_EQ(vector({ 5, 3, 6, 2, 8}), vector_new_instance); 91 | } 92 | 93 | TEST(VectorTest, InsertBackFromFunctionalVector) 94 | { 95 | vector vector_under_test({4, 5, 6}); 96 | vector_under_test.insert_back(vector({1, 2, 3})); 97 | EXPECT_EQ(vector({ 4, 5, 6, 1, 2, 3 }), vector_under_test); 98 | } 99 | 100 | TEST(VectorTest, InsertBackFromStdVector) 101 | { 102 | vector vector_under_test({4, 5, 6}); 103 | vector_under_test.insert_back(std::vector{1, 2, 3}); 104 | EXPECT_EQ(vector({ 4, 5, 6, 1, 2, 3 }), vector_under_test); 105 | } 106 | 107 | TEST(VectorTest, InsertBackFromInitializerList) 108 | { 109 | vector vector_under_test({4, 5, 6}); 110 | vector_under_test.insert_back(std::initializer_list{1, 2, 3}); 111 | EXPECT_EQ(vector({ 4, 5, 6, 1, 2, 3 }), vector_under_test); 112 | } 113 | 114 | TEST(VectorTest, InsertFrontFromFunctionalVector) 115 | { 116 | vector vector_under_test({4, 5, 6}); 117 | vector_under_test.insert_front(vector({1, 2, 3})); 118 | EXPECT_EQ(vector({ 1, 2, 3, 4, 5, 6 }), vector_under_test); 119 | } 120 | 121 | TEST(VectorTest, InsertFrontFromStdVector) 122 | { 123 | vector vector_under_test({4, 5, 6}); 124 | vector_under_test.insert_front(std::vector{1, 2, 3}); 125 | EXPECT_EQ(vector({ 1, 2, 3, 4, 5, 6 }), vector_under_test); 126 | } 127 | 128 | TEST(VectorTest, InsertFrontFromInitializerList) 129 | { 130 | vector vector_under_test({4, 5, 6}); 131 | vector_under_test.insert_front(std::initializer_list{1, 2, 3}); 132 | EXPECT_EQ(vector({ 1, 2, 3, 4, 5, 6 }), vector_under_test); 133 | } 134 | 135 | TEST(VectorTest, InsertingBackFromFunctionalVector) 136 | { 137 | const vector vector_under_test({4, 5, 6}); 138 | const auto vector_new_instance = vector_under_test.inserting_back(vector({1, 2, 3})); 139 | EXPECT_EQ(vector({ 4, 5, 6 }), vector_under_test); 140 | EXPECT_EQ(vector({ 4, 5, 6, 1, 2, 3 }), vector_new_instance); 141 | } 142 | 143 | TEST(VectorTest, InsertingBackFromStdVector) 144 | { 145 | const vector vector_under_test({4, 5, 6}); 146 | const auto vector_new_instance = vector_under_test.inserting_back(std::vector{1, 2, 3}); 147 | EXPECT_EQ(vector({ 4, 5, 6 }), vector_under_test); 148 | EXPECT_EQ(vector({ 4, 5, 6, 1, 2, 3 }), vector_new_instance); 149 | } 150 | 151 | TEST(VectorTest, InsertingBackFromInitializerList) 152 | { 153 | const vector vector_under_test({4, 5, 6}); 154 | const auto vector_new_instance = vector_under_test.inserting_back(std::initializer_list{1, 2, 3}); 155 | EXPECT_EQ(vector({ 4, 5, 6 }), vector_under_test); 156 | EXPECT_EQ(vector({ 4, 5, 6, 1, 2, 3 }), vector_new_instance); 157 | } 158 | 159 | TEST(VectorTest, InsertingFrontFromFunctionalVector) 160 | { 161 | const vector vector_under_test({4, 5, 6}); 162 | const auto vector_new_instance = vector_under_test.inserting_front(vector({1, 2, 3})); 163 | EXPECT_EQ(vector({ 4, 5, 6 }), vector_under_test); 164 | EXPECT_EQ(vector({ 1, 2, 3, 4, 5, 6 }), vector_new_instance); 165 | } 166 | 167 | TEST(VectorTest, InsertingFrontFromStdVector) 168 | { 169 | const vector vector_under_test({4, 5, 6}); 170 | const auto vector_new_instance = vector_under_test.inserting_front(std::vector{1, 2, 3}); 171 | EXPECT_EQ(vector({ 4, 5, 6 }), vector_under_test); 172 | EXPECT_EQ(vector({ 1, 2, 3, 4, 5, 6 }), vector_new_instance); 173 | } 174 | 175 | TEST(VectorTest, InsertingFrontFromInitializerList) 176 | { 177 | const vector vector_under_test({4, 5, 6}); 178 | const auto vector_new_instance = vector_under_test.inserting_front(std::initializer_list{1, 2, 3}); 179 | EXPECT_EQ(vector({ 4, 5, 6 }), vector_under_test); 180 | EXPECT_EQ(vector({ 1, 2, 3, 4, 5, 6 }), vector_new_instance); 181 | } 182 | 183 | TEST(VectorTest, Map) 184 | { 185 | const vector vector_under_test({1, 3, 4}); 186 | const auto mapped_vector = vector_under_test.map([](const int& age){ 187 | return child(age); 188 | }); 189 | EXPECT_EQ(3, mapped_vector.size()); 190 | EXPECT_EQ(1, mapped_vector[0].age); 191 | EXPECT_EQ(3, mapped_vector[1].age); 192 | EXPECT_EQ(4, mapped_vector[2].age); 193 | } 194 | 195 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 196 | TEST(VectorTest, MapParallel) 197 | { 198 | const vector vector_under_test({1, 3, 4}); 199 | const auto mapped_vector = vector_under_test.map_parallel([](const int& age){ 200 | return child(age); 201 | }); 202 | EXPECT_EQ(3, mapped_vector.size()); 203 | EXPECT_EQ(1, mapped_vector[0].age); 204 | EXPECT_EQ(3, mapped_vector[1].age); 205 | EXPECT_EQ(4, mapped_vector[2].age); 206 | } 207 | #endif 208 | 209 | TEST(VectorTest, Filter) 210 | { 211 | vector vector_under_test({child(1), child(3), child(4)}); 212 | vector_under_test.filter([](const child& child){ 213 | return child.age < 2; 214 | }); 215 | EXPECT_EQ(1, vector_under_test.size()); 216 | EXPECT_EQ(1, vector_under_test[0].age); 217 | vector_under_test.filter([](const child& child){ 218 | return child.age > 7; 219 | }); 220 | EXPECT_EQ(0, vector_under_test.size()); 221 | } 222 | 223 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 224 | TEST(VectorTest, FilterParallel) 225 | { 226 | vector vector_under_test({child(1), child(3), child(4)}); 227 | vector_under_test.filter_parallel([](const child& child){ 228 | return child.age < 2; 229 | }); 230 | EXPECT_EQ(1, vector_under_test.size()); 231 | EXPECT_EQ(1, vector_under_test[0].age); 232 | vector_under_test.filter_parallel([](const child& child){ 233 | return child.age > 7; 234 | }); 235 | EXPECT_EQ(0, vector_under_test.size()); 236 | } 237 | #endif 238 | 239 | TEST(VectorTest, Filtered) 240 | { 241 | const vector vector_under_test({child(1), child(3), child(4)}); 242 | const auto filtered_vector = vector_under_test.filtered([](const child& child){ 243 | return child.age < 2; 244 | }); 245 | EXPECT_EQ(3, vector_under_test.size()); 246 | EXPECT_EQ(1, filtered_vector.size()); 247 | EXPECT_EQ(1, filtered_vector[0].age); 248 | } 249 | 250 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 251 | TEST(VectorTest, FilteredParallel) 252 | { 253 | const vector vector_under_test({child(1), child(3), child(4)}); 254 | const auto filtered_vector = vector_under_test.filtered_parallel([](const child& child){ 255 | return child.age < 2; 256 | }); 257 | EXPECT_EQ(3, vector_under_test.size()); 258 | EXPECT_EQ(1, filtered_vector.size()); 259 | EXPECT_EQ(1, filtered_vector[0].age); 260 | } 261 | #endif 262 | 263 | TEST(VectorTest, Reduce) 264 | { 265 | const vector vector_under_test({child(1), child(3), child(4)}); 266 | const auto age_sum = vector_under_test.reduce(0, [](const int& partial_sum, const child& child){ 267 | return partial_sum + child.age; 268 | }); 269 | EXPECT_EQ(8, age_sum); 270 | } 271 | 272 | TEST(VectorTest, ReduceString) 273 | { 274 | const vector tokens({"the", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "brown", "dog"}); 275 | const auto sentence = tokens.reduce(std::string(""), [](const std::string& partial, const std::string& token){ 276 | return partial.length() != 0 277 | ? partial + " " + token 278 | : token; 279 | }); 280 | EXPECT_EQ("the quick brown fox jumps over the lazy brown dog", sentence); 281 | } 282 | 283 | TEST(VectorTest, Reverse) 284 | { 285 | vector vector_under_test({child(6), child(2), child(9)}); 286 | vector_under_test.reverse(); 287 | EXPECT_EQ(3, vector_under_test.size()); 288 | EXPECT_EQ(9, vector_under_test[0].age); 289 | EXPECT_EQ(2, vector_under_test[1].age); 290 | EXPECT_EQ(6, vector_under_test[2].age); 291 | } 292 | 293 | TEST(VectorTest, Reversed) 294 | { 295 | const vector vector_under_test({child(6), child(2), child(9)}); 296 | const auto reversed_vector = vector_under_test.reversed(); 297 | EXPECT_EQ(3, reversed_vector.size()); 298 | EXPECT_EQ(9, reversed_vector[0].age); 299 | EXPECT_EQ(2, reversed_vector[1].age); 300 | EXPECT_EQ(6, reversed_vector[2].age); 301 | 302 | EXPECT_EQ(3, vector_under_test.size()); 303 | EXPECT_EQ(6, vector_under_test[0].age); 304 | EXPECT_EQ(2, vector_under_test[1].age); 305 | EXPECT_EQ(9, vector_under_test[2].age); 306 | } 307 | 308 | TEST(VectorTest, ZipWithStdVectorUnequalSizesThrows) 309 | { 310 | const vector ages_vector({32, 25, 53, 62}); 311 | EXPECT_DEATH({ const auto zipped_vector = ages_vector.zip(std::vector({"Jake", "Mary"})); }, ""); 312 | } 313 | 314 | TEST(VectorTest, ZipWithFunctionalVectorUnequalSizesThrows) 315 | { 316 | const vector ages_vector({32, 25, 53, 62}); 317 | const auto names_vector = vector({"Jake", "Mary"}); 318 | EXPECT_DEATH({ const auto zipped_vector = ages_vector.zip(names_vector); }, ""); 319 | } 320 | 321 | TEST(VectorTest, ZipWithFunctionalVector) 322 | { 323 | const vector ages_vector({32, 25, 53}); 324 | const vector names_vector({"Jake", "Mary", "John"}); 325 | const auto zipped_vector = ages_vector.zip(names_vector); 326 | EXPECT_EQ(3, zipped_vector.size()); 327 | 328 | EXPECT_EQ(32, zipped_vector[0].first); 329 | EXPECT_EQ("Jake", zipped_vector[0].second); 330 | 331 | EXPECT_EQ(25, zipped_vector[1].first); 332 | EXPECT_EQ("Mary", zipped_vector[1].second); 333 | 334 | EXPECT_EQ(53, zipped_vector[2].first); 335 | EXPECT_EQ("John", zipped_vector[2].second); 336 | } 337 | 338 | TEST(VectorTest, ZipWithStdVector) 339 | { 340 | const vector ages_vector({32, 25, 53}); 341 | const auto zipped_vector = ages_vector.zip(std::vector{"Jake", "Mary", "John"}); 342 | EXPECT_EQ(3, zipped_vector.size()); 343 | 344 | EXPECT_EQ(32, zipped_vector[0].first); 345 | EXPECT_EQ("Jake", zipped_vector[0].second); 346 | 347 | EXPECT_EQ(25, zipped_vector[1].first); 348 | EXPECT_EQ("Mary", zipped_vector[1].second); 349 | 350 | EXPECT_EQ(53, zipped_vector[2].first); 351 | EXPECT_EQ("John", zipped_vector[2].second); 352 | } 353 | 354 | TEST(VectorTest, ZipWithInitializerList) 355 | { 356 | const vector ages_vector({32, 25, 53}); 357 | const auto zipped_vector = ages_vector.zip(std::initializer_list{"Jake", "Mary", "John"}); 358 | EXPECT_EQ(3, zipped_vector.size()); 359 | 360 | EXPECT_EQ(32, zipped_vector[0].first); 361 | EXPECT_EQ("Jake", zipped_vector[0].second); 362 | 363 | EXPECT_EQ(25, zipped_vector[1].first); 364 | EXPECT_EQ("Mary", zipped_vector[1].second); 365 | 366 | EXPECT_EQ(53, zipped_vector[2].first); 367 | EXPECT_EQ("John", zipped_vector[2].second); 368 | } 369 | 370 | TEST(VectorTest, Sort) 371 | { 372 | vector vector_under_test({ 373 | person(45, "Jake"), person(34, "Bob"), person(52, "Manfred"), person(8, "Alice") 374 | }); 375 | vector_under_test.sort([](const person& person1, const person& person2){ 376 | return person1.name < person2.name; 377 | }); 378 | EXPECT_EQ(4, vector_under_test.size()); 379 | 380 | EXPECT_EQ("Alice", vector_under_test[0].name); 381 | EXPECT_EQ(8, vector_under_test[0].age); 382 | 383 | EXPECT_EQ("Bob", vector_under_test[1].name); 384 | EXPECT_EQ(34, vector_under_test[1].age); 385 | 386 | EXPECT_EQ("Jake", vector_under_test[2].name); 387 | EXPECT_EQ(45, vector_under_test[2].age); 388 | 389 | EXPECT_EQ("Manfred", vector_under_test[3].name); 390 | EXPECT_EQ(52, vector_under_test[3].age); 391 | } 392 | 393 | TEST(VectorTest, Sorted) 394 | { 395 | const vector vector_under_test({ 396 | person(45, "Jake"), person(34, "Bob"), person(52, "Manfred"), person(8, "Alice") 397 | }); 398 | const auto sorted_vector = vector_under_test.sorted([](const person& person1, const person& person2){ 399 | return person1.name < person2.name; 400 | }); 401 | 402 | EXPECT_EQ(4, vector_under_test.size()); 403 | EXPECT_EQ("Jake", vector_under_test[0].name); 404 | EXPECT_EQ(45, vector_under_test[0].age); 405 | EXPECT_EQ("Bob", vector_under_test[1].name); 406 | EXPECT_EQ(34, vector_under_test[1].age); 407 | EXPECT_EQ("Manfred", vector_under_test[2].name); 408 | EXPECT_EQ(52, vector_under_test[2].age); 409 | EXPECT_EQ("Alice", vector_under_test[3].name); 410 | EXPECT_EQ(8, vector_under_test[3].age); 411 | 412 | EXPECT_EQ(4, sorted_vector.size()); 413 | EXPECT_EQ("Alice", sorted_vector[0].name); 414 | EXPECT_EQ(8, sorted_vector[0].age); 415 | EXPECT_EQ("Bob", sorted_vector[1].name); 416 | EXPECT_EQ(34, sorted_vector[1].age); 417 | EXPECT_EQ("Jake", sorted_vector[2].name); 418 | EXPECT_EQ(45, sorted_vector[2].age); 419 | EXPECT_EQ("Manfred", sorted_vector[3].name); 420 | EXPECT_EQ(52, sorted_vector[3].age); 421 | } 422 | 423 | TEST(VectorTest, SortAscending) 424 | { 425 | vector vector_under_test({3, 1, 9, -4}); 426 | vector_under_test.sort_ascending(); 427 | EXPECT_EQ(4, vector_under_test.size()); 428 | EXPECT_EQ(-4, vector_under_test[0]); 429 | EXPECT_EQ(1, vector_under_test[1]); 430 | EXPECT_EQ(3, vector_under_test[2]); 431 | EXPECT_EQ(9, vector_under_test[3]); 432 | } 433 | 434 | TEST(VectorTest, SortedAscending) 435 | { 436 | const vector vector_under_test({3, 1, 9, -4}); 437 | const auto sorted_vector = vector_under_test.sorted_ascending(); 438 | EXPECT_EQ(4, vector_under_test.size()); 439 | EXPECT_EQ(3, vector_under_test[0]); 440 | EXPECT_EQ(1, vector_under_test[1]); 441 | EXPECT_EQ(9, vector_under_test[2]); 442 | EXPECT_EQ(-4, vector_under_test[3]); 443 | 444 | EXPECT_EQ(4, sorted_vector.size()); 445 | EXPECT_EQ(-4, sorted_vector[0]); 446 | EXPECT_EQ(1, sorted_vector[1]); 447 | EXPECT_EQ(3, sorted_vector[2]); 448 | EXPECT_EQ(9, sorted_vector[3]); 449 | } 450 | 451 | TEST(VectorTest, SortDescending) 452 | { 453 | vector vector_under_test({3, 1, 9, -4}); 454 | vector_under_test.sort_descending(); 455 | EXPECT_EQ(4, vector_under_test.size()); 456 | EXPECT_EQ(9, vector_under_test[0]); 457 | EXPECT_EQ(3, vector_under_test[1]); 458 | EXPECT_EQ(1, vector_under_test[2]); 459 | EXPECT_EQ(-4, vector_under_test[3]); 460 | } 461 | 462 | TEST(VectorTest, SortedDescending) 463 | { 464 | const vector vector_under_test({3, 1, 9, -4}); 465 | const auto sorted_vector = vector_under_test.sorted_descending(); 466 | EXPECT_EQ(4, vector_under_test.size()); 467 | EXPECT_EQ(3, vector_under_test[0]); 468 | EXPECT_EQ(1, vector_under_test[1]); 469 | EXPECT_EQ(9, vector_under_test[2]); 470 | EXPECT_EQ(-4, vector_under_test[3]); 471 | 472 | EXPECT_EQ(4, sorted_vector.size()); 473 | EXPECT_EQ(9, sorted_vector[0]); 474 | EXPECT_EQ(3, sorted_vector[1]); 475 | EXPECT_EQ(1, sorted_vector[2]); 476 | EXPECT_EQ(-4, sorted_vector[3]); 477 | } 478 | 479 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 480 | TEST(VectorTest, SortParallel) 481 | { 482 | vector vector_under_test({ 483 | person(45, "Jake"), person(34, "Bob"), person(52, "Manfred"), person(8, "Alice") 484 | }); 485 | vector_under_test.sort_parallel([](const person& person1, const person& person2){ 486 | return person1.name < person2.name; 487 | }); 488 | EXPECT_EQ(4, vector_under_test.size()); 489 | 490 | EXPECT_EQ("Alice", vector_under_test[0].name); 491 | EXPECT_EQ(8, vector_under_test[0].age); 492 | 493 | EXPECT_EQ("Bob", vector_under_test[1].name); 494 | EXPECT_EQ(34, vector_under_test[1].age); 495 | 496 | EXPECT_EQ("Jake", vector_under_test[2].name); 497 | EXPECT_EQ(45, vector_under_test[2].age); 498 | 499 | EXPECT_EQ("Manfred", vector_under_test[3].name); 500 | EXPECT_EQ(52, vector_under_test[3].age); 501 | } 502 | 503 | TEST(VectorTest, SortedParallel) 504 | { 505 | const vector vector_under_test({ 506 | person(45, "Jake"), person(34, "Bob"), person(52, "Manfred"), person(8, "Alice") 507 | }); 508 | const auto sorted_vector = vector_under_test.sorted_parallel([](const person& person1, const person& person2){ 509 | return person1.name < person2.name; 510 | }); 511 | 512 | EXPECT_EQ(4, vector_under_test.size()); 513 | EXPECT_EQ("Jake", vector_under_test[0].name); 514 | EXPECT_EQ(45, vector_under_test[0].age); 515 | EXPECT_EQ("Bob", vector_under_test[1].name); 516 | EXPECT_EQ(34, vector_under_test[1].age); 517 | EXPECT_EQ("Manfred", vector_under_test[2].name); 518 | EXPECT_EQ(52, vector_under_test[2].age); 519 | EXPECT_EQ("Alice", vector_under_test[3].name); 520 | EXPECT_EQ(8, vector_under_test[3].age); 521 | 522 | EXPECT_EQ(4, sorted_vector.size()); 523 | EXPECT_EQ("Alice", sorted_vector[0].name); 524 | EXPECT_EQ(8, sorted_vector[0].age); 525 | EXPECT_EQ("Bob", sorted_vector[1].name); 526 | EXPECT_EQ(34, sorted_vector[1].age); 527 | EXPECT_EQ("Jake", sorted_vector[2].name); 528 | EXPECT_EQ(45, sorted_vector[2].age); 529 | EXPECT_EQ("Manfred", sorted_vector[3].name); 530 | EXPECT_EQ(52, sorted_vector[3].age); 531 | } 532 | 533 | TEST(VectorTest, SortAscendingParallel) 534 | { 535 | vector vector_under_test({3, 1, 9, -4}); 536 | vector_under_test.sort_ascending_parallel(); 537 | EXPECT_EQ(4, vector_under_test.size()); 538 | EXPECT_EQ(-4, vector_under_test[0]); 539 | EXPECT_EQ(1, vector_under_test[1]); 540 | EXPECT_EQ(3, vector_under_test[2]); 541 | EXPECT_EQ(9, vector_under_test[3]); 542 | } 543 | 544 | TEST(VectorTest, SortedAscendingParallel) 545 | { 546 | const vector vector_under_test({3, 1, 9, -4}); 547 | const auto sorted_vector = vector_under_test.sorted_ascending_parallel(); 548 | EXPECT_EQ(4, vector_under_test.size()); 549 | EXPECT_EQ(3, vector_under_test[0]); 550 | EXPECT_EQ(1, vector_under_test[1]); 551 | EXPECT_EQ(9, vector_under_test[2]); 552 | EXPECT_EQ(-4, vector_under_test[3]); 553 | 554 | EXPECT_EQ(4, sorted_vector.size()); 555 | EXPECT_EQ(-4, sorted_vector[0]); 556 | EXPECT_EQ(1, sorted_vector[1]); 557 | EXPECT_EQ(3, sorted_vector[2]); 558 | EXPECT_EQ(9, sorted_vector[3]); 559 | } 560 | 561 | TEST(VectorTest, SortDescendingParallel) 562 | { 563 | vector vector_under_test({3, 1, 9, -4}); 564 | vector_under_test.sort_descending_parallel(); 565 | EXPECT_EQ(4, vector_under_test.size()); 566 | EXPECT_EQ(9, vector_under_test[0]); 567 | EXPECT_EQ(3, vector_under_test[1]); 568 | EXPECT_EQ(1, vector_under_test[2]); 569 | EXPECT_EQ(-4, vector_under_test[3]); 570 | } 571 | 572 | TEST(VectorTest, SortedDescendingParallel) 573 | { 574 | const vector vector_under_test({3, 1, 9, -4}); 575 | const auto sorted_vector = vector_under_test.sorted_descending_parallel(); 576 | EXPECT_EQ(4, vector_under_test.size()); 577 | EXPECT_EQ(3, vector_under_test[0]); 578 | EXPECT_EQ(1, vector_under_test[1]); 579 | EXPECT_EQ(9, vector_under_test[2]); 580 | EXPECT_EQ(-4, vector_under_test[3]); 581 | 582 | EXPECT_EQ(4, sorted_vector.size()); 583 | EXPECT_EQ(9, sorted_vector[0]); 584 | EXPECT_EQ(3, sorted_vector[1]); 585 | EXPECT_EQ(1, sorted_vector[2]); 586 | EXPECT_EQ(-4, sorted_vector[3]); 587 | } 588 | #endif 589 | 590 | TEST(VectorTest, SubscriptOperatorNegativeDeath) 591 | { 592 | const vector vector_under_test({3, 1, 9, -4}); 593 | EXPECT_DEATH(vector_under_test[-1], ""); 594 | } 595 | 596 | TEST(VectorTest, SubscriptOperatorIndexEqualToSizeDeath) 597 | { 598 | const vector vector_under_test({3, 1, 9, -4}); 599 | EXPECT_DEATH(vector_under_test[4], ""); 600 | } 601 | 602 | TEST(VectorTest, SubscriptOperatorIndexLargerThanSizeDeath) 603 | { 604 | const vector vector_under_test({3, 1, 9, -4}); 605 | EXPECT_DEATH(vector_under_test[5], ""); 606 | } 607 | 608 | TEST(VectorTest, SubscriptOperatorAssignNegativeDeath) 609 | { 610 | vector vector_under_test({3, 1, 9, -4}); 611 | EXPECT_DEATH(vector_under_test[-1] = 5, ""); 612 | } 613 | 614 | TEST(VectorTest, SubscriptOperatorAssignIndexEqualSizeDeath) 615 | { 616 | vector vector_under_test({3, 1, 9, -4}); 617 | EXPECT_DEATH(vector_under_test[4] = 5, ""); 618 | } 619 | 620 | TEST(VectorTest, SubscriptOperatorAssignIndexLargerThanSizeDeath) 621 | { 622 | vector vector_under_test({3, 1, 9, -4}); 623 | EXPECT_DEATH(vector_under_test[5] = -3, ""); 624 | } 625 | 626 | TEST(VectorTest, SubscriptOperatorAssign) 627 | { 628 | vector vector_under_test({3, 1, 9, -4}); 629 | vector_under_test[2] = 7; 630 | EXPECT_EQ(4, vector_under_test.size()); 631 | EXPECT_EQ(3, vector_under_test[0]); 632 | EXPECT_EQ(1, vector_under_test[1]); 633 | EXPECT_EQ(7, vector_under_test[2]); 634 | EXPECT_EQ(-4, vector_under_test[3]); 635 | } 636 | 637 | TEST(VectorTest, FindFirstIndexEmptyVector) 638 | { 639 | const vector vector_under_test; 640 | EXPECT_FALSE(vector_under_test.find_first_index(-3).has_value()); 641 | } 642 | 643 | TEST(VectorTest, FindFirstIndexFilledVectorWithoutMatch) 644 | { 645 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 646 | EXPECT_FALSE(vector_under_test.find_first_index(9).has_value()); 647 | } 648 | 649 | TEST(VectorTest, FindFirstIndexFilledVector) 650 | { 651 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 652 | EXPECT_EQ(0, vector_under_test.find_first_index(1).value()); 653 | EXPECT_EQ(7, vector_under_test.find_first_index(7).value()); 654 | EXPECT_EQ(3, vector_under_test.find_first_index(5).value()); 655 | } 656 | 657 | TEST(VectorTest, FindLastIndexEmptyVector) 658 | { 659 | const vector vector_under_test; 660 | EXPECT_FALSE(vector_under_test.find_last_index(-3).has_value()); 661 | } 662 | 663 | TEST(VectorTest, FindLastIndexFilledVectorWithoutMatch) 664 | { 665 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 666 | EXPECT_FALSE(vector_under_test.find_last_index(9).has_value()); 667 | } 668 | 669 | TEST(VectorTest, FindLastIndexFilledVector) 670 | { 671 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 672 | EXPECT_EQ(8, vector_under_test.find_last_index(1).value()); 673 | EXPECT_EQ(7, vector_under_test.find_last_index(7).value()); 674 | EXPECT_EQ(3, vector_under_test.find_last_index(5).value()); 675 | } 676 | 677 | TEST(VectorTest, FindAllIndicesEmptyVector) 678 | { 679 | const vector vector_under_test; 680 | EXPECT_EQ(0, vector_under_test.find_all_indices(-3).size()); 681 | } 682 | 683 | TEST(VectorTest, FindAllIndicesFilledVectorWithoutMatch) 684 | { 685 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 686 | EXPECT_EQ(0, vector_under_test.find_all_indices(9).size()); 687 | } 688 | 689 | TEST(VectorTest, FindAllIndicesFilledVector) 690 | { 691 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 9, 1}); 692 | const auto one_indices = vector_under_test.find_all_indices(1); 693 | EXPECT_EQ(std::vector({ 0, 6, 8 }), one_indices); 694 | const auto seven_indices = vector_under_test.find_all_indices(9); 695 | EXPECT_EQ(std::vector({ 7 }), seven_indices); 696 | } 697 | 698 | TEST(VectorTest, RemoveAtEmptyVector) 699 | { 700 | vector vector_under_test; 701 | EXPECT_DEATH(vector_under_test.remove_at(-1), ""); 702 | EXPECT_DEATH(vector_under_test.remove_at(0), ""); 703 | EXPECT_DEATH(vector_under_test.remove_at(1), ""); 704 | } 705 | 706 | TEST(VectorTest, RemoveAtFilledVectorAboveSize) 707 | { 708 | auto vector_under_test = vector({1, 4, 2, 5, 8, 3, 1, 7, 1}); 709 | EXPECT_DEATH(vector_under_test.remove_at(15), ""); 710 | } 711 | 712 | TEST(VectorTest, RemoveAtFilledVector) 713 | { 714 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 715 | vector_under_test.remove_at(1); 716 | EXPECT_EQ(vector({ 1, 2, 5, 8, 3, 1, 7, 1 }), vector_under_test); 717 | vector_under_test.remove_at(1); 718 | EXPECT_EQ(vector({ 1, 5, 8, 3, 1, 7, 1 }), vector_under_test); 719 | vector_under_test.remove_at(4); 720 | EXPECT_EQ(vector({ 1, 5, 8, 3, 7, 1 }), vector_under_test); 721 | } 722 | 723 | TEST(VectorTest, RemovingAtEmptyVector) 724 | { 725 | const vector vector_under_test; 726 | EXPECT_DEATH(vector_under_test.removing_at(-1), ""); 727 | EXPECT_DEATH(vector_under_test.removing_at(0), ""); 728 | EXPECT_DEATH(vector_under_test.removing_at(1), ""); 729 | } 730 | 731 | TEST(VectorTest, RemovingAtFilledVectorAboveSize) 732 | { 733 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 734 | EXPECT_DEATH(vector_under_test.removing_at(15), ""); 735 | } 736 | 737 | TEST(VectorTest, RemovingAtFilledVector) 738 | { 739 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 740 | const auto shorter_vector = vector_under_test.removing_at(1); 741 | EXPECT_EQ(vector({ 1, 2, 5, 8, 3, 1, 7, 1 }), shorter_vector); 742 | EXPECT_EQ(vector({ 1, 4, 2, 5, 8, 3, 1, 7, 1 }), vector_under_test); 743 | } 744 | 745 | TEST(VectorTest, RemoveBack) 746 | { 747 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 748 | vector_under_test.remove_back(); 749 | EXPECT_EQ(vector({ 1, 4, 2, 5, 8, 3, 1, 7 }), vector_under_test); 750 | vector_under_test.remove_back(); 751 | EXPECT_EQ(vector({ 1, 4, 2, 5, 8, 3, 1 }), vector_under_test); 752 | vector_under_test.remove_back(); 753 | EXPECT_EQ(vector({ 1, 4, 2, 5, 8, 3 }), vector_under_test); 754 | } 755 | 756 | TEST(VectorTest, RemovingBack) 757 | { 758 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 759 | const auto vector_without_last_element = vector_under_test.removing_back(); 760 | EXPECT_EQ(vector({ 1, 4, 2, 5, 8, 3, 1, 7 }), vector_without_last_element); 761 | EXPECT_EQ(vector({ 1, 4, 2, 5, 8, 3, 1, 7, 1 }), vector_under_test); 762 | } 763 | 764 | TEST(VectorTest, RemoveFront) 765 | { 766 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 767 | vector_under_test.remove_front(); 768 | EXPECT_EQ(vector({ 4, 2, 5, 8, 3, 1, 7, 1 }), vector_under_test); 769 | vector_under_test.remove_front(); 770 | EXPECT_EQ(vector({ 2, 5, 8, 3, 1, 7, 1 }), vector_under_test); 771 | vector_under_test.remove_front(); 772 | EXPECT_EQ(vector({ 5, 8, 3, 1, 7, 1 }), vector_under_test); 773 | } 774 | 775 | TEST(VectorTest, RemovingFront) 776 | { 777 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 778 | const auto vector_without_first_element = vector_under_test.removing_front(); 779 | EXPECT_EQ(vector({ 4, 2, 5, 8, 3, 1, 7, 1 }), vector_without_first_element); 780 | EXPECT_EQ(vector({ 1, 4, 2, 5, 8, 3, 1, 7, 1 }), vector_under_test); 781 | } 782 | 783 | TEST(VectorTest, InsertAtEmptyVector) 784 | { 785 | vector vector_under_test; 786 | EXPECT_DEATH(vector_under_test.insert_at(15, -1), ""); 787 | vector_under_test.insert_at(0, -1); 788 | EXPECT_EQ(1, vector_under_test.size()); 789 | EXPECT_EQ(-1, vector_under_test[0]); 790 | } 791 | 792 | TEST(VectorTest, InsertAtFilledVector) 793 | { 794 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 795 | EXPECT_DEATH(vector_under_test.insert_at(15, -1), ""); 796 | vector_under_test.insert_at(3, 18); 797 | EXPECT_EQ(vector({ 1, 4, 2, 18, 5, 8, 3, 1, 7, 1 }), vector_under_test); 798 | } 799 | 800 | TEST(VectorTest, InsertingAtEmptyVector) 801 | { 802 | const vector vector_under_test; 803 | EXPECT_DEATH(vector_under_test.inserting_at(15, -1), ""); 804 | const auto augmented_vector = vector_under_test.inserting_at(0, -1); 805 | EXPECT_EQ(1, augmented_vector.size()); 806 | EXPECT_EQ(-1, augmented_vector[0]); 807 | EXPECT_EQ(0, vector_under_test.size()); 808 | } 809 | 810 | TEST(VectorTest, InsertingAtFilledVector) 811 | { 812 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 813 | EXPECT_DEATH(vector_under_test.inserting_at(15, -1), ""); 814 | const auto augmented_vector = vector_under_test.inserting_at(3, 18); 815 | EXPECT_EQ(vector({ 1, 4, 2, 18, 5, 8, 3, 1, 7, 1 }), augmented_vector); 816 | EXPECT_EQ(vector({ 1, 4, 2, 5, 8, 3, 1, 7, 1 }), vector_under_test); 817 | } 818 | 819 | TEST(VectorTest, InsertRangeEmptyFunctionalVector) 820 | { 821 | vector vector_under_test; 822 | const vector vector_to_insert({4, 7, 3, -5}); 823 | vector_under_test.insert_at(0, vector_to_insert); 824 | EXPECT_EQ(vector({ 4, 7, 3, -5 }), vector_under_test); 825 | } 826 | 827 | TEST(VectorTest, InsertRangeExistingWrongInsertionIndexFunctionalVector) 828 | { 829 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 830 | const vector vector_to_insert({9, -5, 6}); 831 | EXPECT_DEATH(vector_under_test.insert_at(-1, vector_to_insert), ""); 832 | EXPECT_DEATH(vector_under_test.insert_at(10, vector_to_insert), ""); 833 | } 834 | 835 | TEST(VectorTest, InsertRangeExistingFunctionalVector) 836 | { 837 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 838 | const vector vector_to_insert({9, -5, 6}); 839 | vector_under_test.insert_at(3, vector_to_insert); 840 | EXPECT_EQ(vector({ 1, 4, 2, 9, -5, 6, 5, 8, 3, 1, 7, 1 }), vector_under_test); 841 | } 842 | 843 | TEST(VectorTest, InsertRangeEmptyStdVector) 844 | { 845 | vector vector_under_test; 846 | const std::vector vector_to_insert{4, 7, 3, -5}; 847 | vector_under_test.insert_at(0, vector_to_insert); 848 | EXPECT_EQ(vector({ 4, 7, 3, -5 }), vector_under_test); 849 | } 850 | 851 | TEST(VectorTest, InsertRangeExistingWrongInsertionIndexStdVector) 852 | { 853 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 854 | const std::vector vector_to_insert({9, -5, 6}); 855 | EXPECT_DEATH(vector_under_test.insert_at(-1, vector_to_insert), ""); 856 | EXPECT_DEATH(vector_under_test.insert_at(10, vector_to_insert), ""); 857 | } 858 | 859 | TEST(VectorTest, InsertRangeExistingStdVector) 860 | { 861 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 862 | const std::vector vector_to_insert({9, -5, 6}); 863 | vector_under_test.insert_at(3, vector_to_insert); 864 | EXPECT_EQ(vector({ 1, 4, 2, 9, -5, 6, 5, 8, 3, 1, 7, 1 }), vector_under_test); 865 | } 866 | 867 | TEST(VectorTest, InsertRangeEmptyInitializerList) 868 | { 869 | vector vector_under_test; 870 | vector_under_test.insert_at(0, {4, 7, 3, -5}); 871 | EXPECT_EQ(vector({ 4, 7, 3, -5 }), vector_under_test); 872 | } 873 | 874 | TEST(VectorTest, InsertRangeExistingWrongInsertionIndexInitializerList) 875 | { 876 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 877 | EXPECT_DEATH(vector_under_test.insert_at(-1, { 9, -5, 6 }), ""); 878 | EXPECT_DEATH(vector_under_test.insert_at(10, { 9, -5, 6 }), ""); 879 | } 880 | 881 | TEST(VectorTest, InsertRangeExistingInitializerList) 882 | { 883 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 884 | vector_under_test.insert_at(3, {9, -5, 6}); 885 | EXPECT_EQ(vector({ 1, 4, 2, 9, -5, 6, 5, 8, 3, 1, 7, 1 }), vector_under_test); 886 | } 887 | 888 | TEST(VectorTest, InsertingRangeEmptyFunctionalVector) 889 | { 890 | const vector vector_under_test; 891 | const vector vector_to_insert({4, 7, 3, -5}); 892 | const auto result_vector = vector_under_test.inserting_at(0, vector_to_insert); 893 | EXPECT_EQ(vector({ 4, 7, 3, -5 }), result_vector); 894 | } 895 | 896 | TEST(VectorTest, InsertingRangeExistingWrongInsertionIndexFunctionalVector) 897 | { 898 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 899 | const vector vector_to_insert({9, -5, 6}); 900 | EXPECT_DEATH(vector_under_test.inserting_at(-1, vector_to_insert), ""); 901 | EXPECT_DEATH(vector_under_test.inserting_at(10, vector_to_insert), ""); 902 | } 903 | 904 | TEST(VectorTest, InsertingRangeExistingFunctionalVector) 905 | { 906 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 907 | const vector vector_to_insert({9, -5, 6}); 908 | const auto result_vector = vector_under_test.inserting_at(3, vector_to_insert); 909 | EXPECT_EQ(vector({ 1, 4, 2, 9, -5, 6, 5, 8, 3, 1, 7, 1 }), result_vector); 910 | } 911 | 912 | TEST(VectorTest, InsertingRangeEmptyStdVector) 913 | { 914 | const vector vector_under_test; 915 | const std::vector vector_to_insert({4, 7, 3, -5}); 916 | const auto result_vector = vector_under_test.inserting_at(0, vector_to_insert); 917 | EXPECT_EQ(vector({ 4, 7, 3, -5 }), result_vector); 918 | } 919 | 920 | TEST(VectorTest, InsertingRangeExistingWrongInsertionIndexStdVector) 921 | { 922 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 923 | const std::vector vector_to_insert({9, -5, 6}); 924 | EXPECT_DEATH(vector_under_test.inserting_at(-1, vector_to_insert), ""); 925 | EXPECT_DEATH(vector_under_test.inserting_at(10, vector_to_insert), ""); 926 | } 927 | 928 | TEST(VectorTest, InsertingRangeExistingStdVector) 929 | { 930 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 931 | const std::vector vector_to_insert({9, -5, 6}); 932 | const auto result_vector = vector_under_test.inserting_at(3, vector_to_insert); 933 | EXPECT_EQ(vector({ 1, 4, 2, 9, -5, 6, 5, 8, 3, 1, 7, 1 }), result_vector); 934 | } 935 | 936 | TEST(VectorTest, InsertingRangeEmptyInitializerList) 937 | { 938 | const vector vector_under_test; 939 | const auto result_vector = vector_under_test.inserting_at(0, {4, 7, 3, -5}); 940 | EXPECT_EQ(vector({ 4, 7, 3, -5 }), result_vector); 941 | } 942 | 943 | TEST(VectorTest, InsertingRangeExistingWrongInsertionIndexInitializerList) 944 | { 945 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 946 | EXPECT_DEATH(vector_under_test.inserting_at(-1, { 9, -5, 6 }), ""); 947 | EXPECT_DEATH(vector_under_test.inserting_at(10, { 9, -5, 6 }), ""); 948 | } 949 | 950 | TEST(VectorTest, InsertingRangeExistingInitializerList) 951 | { 952 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 953 | const auto result_vector = vector_under_test.inserting_at(3, {9, -5, 6}); 954 | EXPECT_EQ(vector({ 1, 4, 2, 9, -5, 6, 5, 8, 3, 1, 7, 1 }), result_vector); 955 | } 956 | 957 | TEST(VectorTest, RemoveIndexRangeWithInvalidRange) 958 | { 959 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 960 | vector_under_test.remove_range(index_range::invalid); 961 | EXPECT_EQ(vector({ 1, 4, 2, 5, 8, 3, 1, 7, 1 }), vector_under_test); 962 | } 963 | 964 | TEST(VectorTest, RemoveIndexRangeEmptyVector) 965 | { 966 | vector vector_under_test; 967 | vector_under_test.remove_range(index_range::start_count(1, 12)); 968 | EXPECT_EQ(vector(), vector_under_test); 969 | } 970 | 971 | TEST(VectorTest, RemoveIndexRangeStartCountSuccess) 972 | { 973 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 974 | vector_under_test.remove_range(index_range::start_count(2, 3)); 975 | EXPECT_EQ(vector({ 1, 4, 3, 1, 7, 1 }), vector_under_test); 976 | } 977 | 978 | TEST(VectorTest, RemoveRangeStartEndSuccess) 979 | { 980 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 981 | vector_under_test.remove_range(index_range::start_end(3, 6)); 982 | EXPECT_EQ(vector({ 1, 4, 2, 7, 1 }), vector_under_test); 983 | } 984 | 985 | TEST(VectorTest, RemovingIndexRangeWithInvalidRange) 986 | { 987 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 988 | const auto shorter_vector = vector_under_test.removing_range(index_range::invalid); 989 | EXPECT_EQ(vector({ 1, 4, 2, 5, 8, 3, 1, 7, 1 }), shorter_vector); 990 | } 991 | 992 | TEST(VectorTest, RemovingRangeEmptyVector) 993 | { 994 | const vector vector_under_test; 995 | const auto shorter_vector = vector_under_test.removing_range(index_range::start_count(1, 12)); 996 | EXPECT_EQ(vector(), shorter_vector); 997 | } 998 | 999 | TEST(VectorTest, RemovingIndexRangeStartCountSuccess) 1000 | { 1001 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1002 | const auto shorter_vector = vector_under_test.removing_range(index_range::start_count(2, 3)); 1003 | EXPECT_EQ(vector({ 1, 4, 3, 1, 7, 1 }), shorter_vector); 1004 | } 1005 | 1006 | TEST(VectorTest, RemovingIndexRangeStartEndSuccess) 1007 | { 1008 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1009 | const auto shorter_vector = vector_under_test.removing_range(index_range::start_end(3, 6)); 1010 | EXPECT_EQ(vector({ 1, 4, 2, 7, 1 }), shorter_vector); 1011 | } 1012 | 1013 | TEST(VectorTest, ReplaceRangeAtWithEmptySource) 1014 | { 1015 | vector vector_under_test({5, -3, 4, -9}); 1016 | vector_under_test.replace_range_at(3, std::vector()); 1017 | EXPECT_EQ(vector({ 5, -3 , 4, -9 }), vector_under_test); 1018 | } 1019 | 1020 | TEST(VectorTest, ReplaceRangeAtWrongIndex) 1021 | { 1022 | vector vector_under_test({5, -3, 4, -9}); 1023 | EXPECT_DEATH(vector_under_test.replace_range_at(-1, { 1, 2, 6, 4 }), ""); 1024 | EXPECT_DEATH(vector_under_test.replace_range_at(4, { 1, 2, 6, 4 }), ""); 1025 | EXPECT_DEATH(vector_under_test.replace_range_at(5, { 1, 2, 6, 4 }), ""); 1026 | } 1027 | 1028 | TEST(VectorTest, ReplaceRangeAtMoreElementsInSourceThanCapacity) 1029 | { 1030 | vector vector_under_test({5, -3, 4, -9}); 1031 | EXPECT_DEATH(vector_under_test.replace_range_at(2, { 1, 2, 6, 4, 8, 9, -10 }), ""); 1032 | } 1033 | 1034 | TEST(VectorTest, ReplaceRangeAtWithFunctionalVector) 1035 | { 1036 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1037 | vector_under_test.replace_range_at(4, vector({9, -10, 8})); 1038 | EXPECT_EQ(vector({ 1, 4, 2, 5, 9, -10, 8, 7, 1 }), vector_under_test); 1039 | } 1040 | 1041 | TEST(VectorTest, ReplaceRangeAtWithStdVector) 1042 | { 1043 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1044 | vector_under_test.replace_range_at(4, std::vector({9, -10, 8})); 1045 | EXPECT_EQ(vector({ 1, 4, 2, 5, 9, -10, 8, 7, 1 }), vector_under_test); 1046 | } 1047 | 1048 | TEST(VectorTest, ReplaceRangeAtWithInitializerList) 1049 | { 1050 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1051 | vector_under_test.replace_range_at(4, std::initializer_list({9, -10, 8})); 1052 | EXPECT_EQ(vector({ 1, 4, 2, 5, 9, -10, 8, 7, 1 }), vector_under_test); 1053 | } 1054 | 1055 | TEST(VectorTest, ReplacingRangeAtWithEmptySource) 1056 | { 1057 | const vector vector_under_test({5, -3, 4, -9}); 1058 | const auto replaced_vector = vector_under_test.replacing_range_at(3, std::vector()); 1059 | EXPECT_EQ(vector({ 5, -3 , 4, -9 }), replaced_vector); 1060 | } 1061 | 1062 | TEST(VectorTest, ReplacingRangeAtWrongIndex) 1063 | { 1064 | const vector vector_under_test({5, -3, 4, -9}); 1065 | EXPECT_DEATH(vector_under_test.replacing_range_at(-1, { 1, 2, 6, 4 }), ""); 1066 | EXPECT_DEATH(vector_under_test.replacing_range_at(4, { 1, 2, 6, 4 }), ""); 1067 | EXPECT_DEATH(vector_under_test.replacing_range_at(5, { 1, 2, 6, 4 }), ""); 1068 | } 1069 | 1070 | TEST(VectorTest, ReplacingRangeAtMoreElementsInSourceThanCapacity) 1071 | { 1072 | const vector vector_under_test({5, -3, 4, -9}); 1073 | EXPECT_DEATH(vector_under_test.replacing_range_at(2, { 1, 2, 6, 4, 8, 9, -10 }), ""); 1074 | } 1075 | 1076 | TEST(VectorTest, ReplacingRangeAtWithFunctionalVector) 1077 | { 1078 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1079 | const auto replaced_vector = vector_under_test.replacing_range_at(4, vector({9, -10, 8})); 1080 | EXPECT_EQ(vector({ 1, 4, 2, 5, 9, -10, 8, 7, 1 }), replaced_vector); 1081 | } 1082 | 1083 | TEST(VectorTest, ReplacingRangeAtWithStdVector) 1084 | { 1085 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1086 | const auto replaced_vector = vector_under_test.replacing_range_at(4, std::vector({9, -10, 8})); 1087 | EXPECT_EQ(vector({ 1, 4, 2, 5, 9, -10, 8, 7, 1 }), replaced_vector); 1088 | } 1089 | 1090 | TEST(VectorTest, ReplacingRangeAtWithInitializerList) 1091 | { 1092 | const vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1093 | const auto replaced_vector = vector_under_test.replacing_range_at(4, std::initializer_list({9, -10, 8})); 1094 | EXPECT_EQ(vector({ 1, 4, 2, 5, 9, -10, 8, 7, 1 }), replaced_vector); 1095 | } 1096 | 1097 | TEST(VectorTest, Fill) 1098 | { 1099 | vector vector_under_test({1, 3, -6, 4, -9}); 1100 | vector_under_test.fill(7); 1101 | EXPECT_EQ(vector({ 7, 7, 7, 7, 7 }), vector_under_test); 1102 | } 1103 | 1104 | TEST(VectorTest, RepeatingConstructor) 1105 | { 1106 | const vector vector_under_test(3, "John"); 1107 | EXPECT_EQ(vector({ "John", "John", "John" }), vector_under_test); 1108 | } 1109 | 1110 | TEST(VectorTest, EqualityOperatorEmptyVectors) 1111 | { 1112 | const vector vec1; 1113 | const vector vec2; 1114 | EXPECT_TRUE(vec1 == vec2); 1115 | EXPECT_FALSE(vec1 != vec2); 1116 | } 1117 | 1118 | TEST(VectorTest, EqualityOperatorUnequalSizes) 1119 | { 1120 | const vector vec1({1, 2, 3}); 1121 | const vector vec2({1, 2, 3, 4}); 1122 | EXPECT_TRUE(vec1 != vec2); 1123 | EXPECT_FALSE(vec1 == vec2); 1124 | } 1125 | 1126 | TEST(VectorTest, EqualityOperatorEqualSizesDifferentElements) 1127 | { 1128 | const vector vec1({1, 2, 3}); 1129 | const vector vec2({1, 2, 4}); 1130 | EXPECT_TRUE(vec1 != vec2); 1131 | EXPECT_FALSE(vec1 == vec2); 1132 | } 1133 | 1134 | TEST(VectorTest, EqualityOperatorEqualVectors) 1135 | { 1136 | const vector vec1({1, 2, 3}); 1137 | const vector vec2({1, 2, 3}); 1138 | EXPECT_TRUE(vec1 == vec2); 1139 | EXPECT_FALSE(vec1 != vec2); 1140 | } 1141 | 1142 | TEST(VectorTest, EqualityOperatorCustomTypeEqualVectors) 1143 | { 1144 | const vector vec1({ 1145 | person(15, "Jake"), 1146 | person(18, "Jannet"), 1147 | person(25, "Kate") 1148 | }); 1149 | 1150 | const vector vec2({ 1151 | person(15, "Jake"), 1152 | person(18, "Jannet"), 1153 | person(25, "Kate") 1154 | }); 1155 | 1156 | EXPECT_TRUE(vec1 == vec2); 1157 | EXPECT_FALSE(vec1 != vec2); 1158 | } 1159 | 1160 | TEST(VectorTest, EqualityOperatorCustomTypeUnequalSizes) 1161 | { 1162 | const vector vec1({ 1163 | person(15, "Jake"), 1164 | person(18, "Jannet"), 1165 | person(25, "Kate") 1166 | }); 1167 | 1168 | const vector vec2({ 1169 | person(15, "Jake"), 1170 | person(18, "Jannet"), 1171 | person(25, "Kate"), 1172 | person(50, "Barbara") 1173 | }); 1174 | 1175 | EXPECT_FALSE(vec1 == vec2); 1176 | EXPECT_TRUE(vec1 != vec2); 1177 | } 1178 | 1179 | TEST(VectorTest, EqualityOperatorCustomTypeUnequalVectors) 1180 | { 1181 | const vector vec1({ 1182 | person(15, "Jake"), 1183 | person(18, "Jannet"), 1184 | person(25, "Kate") 1185 | }); 1186 | 1187 | const vector vec2({ 1188 | person(15, "Jake"), 1189 | person(53, "Bob"), 1190 | person(35, "Suzan") 1191 | }); 1192 | 1193 | EXPECT_FALSE(vec1 == vec2); 1194 | EXPECT_TRUE(vec1 != vec2); 1195 | } 1196 | 1197 | TEST(VectorTest, ClearEmptyVector) 1198 | { 1199 | vector vector_under_test; 1200 | EXPECT_TRUE(vector_under_test.is_empty()); 1201 | vector_under_test.clear(); 1202 | EXPECT_EQ(0, vector_under_test.size()); 1203 | EXPECT_TRUE(vector_under_test.is_empty()); 1204 | } 1205 | 1206 | TEST(VectorTest, ClearFilledVector) 1207 | { 1208 | vector vector_under_test({5, -3, 4, -9}); 1209 | EXPECT_FALSE(vector_under_test.is_empty()); 1210 | vector_under_test.clear(); 1211 | EXPECT_EQ(0, vector_under_test.size()); 1212 | EXPECT_TRUE(vector_under_test.is_empty()); 1213 | } 1214 | 1215 | TEST(VectorTest, CapacityReserveClear) 1216 | { 1217 | vector vector_under_test; 1218 | EXPECT_EQ(0, vector_under_test.capacity()); 1219 | EXPECT_EQ(0, vector_under_test.size()); 1220 | 1221 | vector_under_test.reserve(5); 1222 | EXPECT_EQ(5, vector_under_test.capacity()); 1223 | EXPECT_EQ(0, vector_under_test.size()); 1224 | 1225 | vector_under_test.insert_back({1, 4, -5, 2}); 1226 | EXPECT_EQ(5, vector_under_test.capacity()); 1227 | EXPECT_EQ(4, vector_under_test.size()); 1228 | 1229 | vector_under_test.clear(); 1230 | EXPECT_EQ(5, vector_under_test.capacity()); 1231 | EXPECT_EQ(0, vector_under_test.size()); 1232 | } 1233 | 1234 | TEST(VectorTest, ResizeEmptyVector) 1235 | { 1236 | vector vector_under_test; 1237 | vector_under_test.resize(5); 1238 | EXPECT_EQ(vector({ 0, 0, 0, 0, 0 }), vector_under_test); 1239 | EXPECT_EQ(5, vector_under_test.capacity()); 1240 | EXPECT_EQ(5, vector_under_test.size()); 1241 | } 1242 | 1243 | TEST(VectorTest, ResizeSmallerThanCurrentSize) 1244 | { 1245 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1246 | EXPECT_EQ(9, vector_under_test.capacity()); 1247 | EXPECT_EQ(9, vector_under_test.size()); 1248 | 1249 | vector_under_test.resize(5); 1250 | EXPECT_EQ(vector({1, 4, 2, 5, 8}), vector_under_test); 1251 | EXPECT_EQ(9, vector_under_test.capacity()); 1252 | EXPECT_EQ(5, vector_under_test.size()); 1253 | } 1254 | 1255 | TEST(VectorTest, AllOfFalse) 1256 | { 1257 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1258 | EXPECT_FALSE(vector_under_test.all_of([](const int& number) { return number > 5; })); 1259 | } 1260 | 1261 | TEST(VectorTest, AllOfTrue) 1262 | { 1263 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1264 | vector_under_test.all_of([](const int& number){ 1265 | return number < 10; 1266 | }); 1267 | EXPECT_TRUE(vector_under_test.all_of([](const int& number) { return number < 10; })); 1268 | } 1269 | 1270 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 1271 | TEST(VectorTest, AllOfParallelFalse) 1272 | { 1273 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1274 | EXPECT_FALSE(vector_under_test.all_of_parallel([](const int& number) { return number > 5; })); 1275 | } 1276 | 1277 | TEST(VectorTest, AllOfParallelTrue) 1278 | { 1279 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1280 | vector_under_test.all_of([](const int& number){ 1281 | return number < 10; 1282 | }); 1283 | EXPECT_TRUE(vector_under_test.all_of_parallel([](const int& number) { return number < 10; })); 1284 | } 1285 | #endif 1286 | 1287 | TEST(VectorTest, AnyOfFalse) 1288 | { 1289 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1290 | EXPECT_FALSE(vector_under_test.any_of([](const int& number) { return number > 20; })); 1291 | } 1292 | 1293 | TEST(VectorTest, AnyOfTrue) 1294 | { 1295 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1296 | EXPECT_TRUE(vector_under_test.any_of([](const int& number) { return number >= 7; })); 1297 | } 1298 | 1299 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 1300 | TEST(VectorTest, AnyOfParallelFalse) 1301 | { 1302 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1303 | EXPECT_FALSE(vector_under_test.any_of_parallel([](const int& number) { return number > 20; })); 1304 | } 1305 | 1306 | TEST(VectorTest, AnyOfParallelTrue) 1307 | { 1308 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1309 | EXPECT_TRUE(vector_under_test.any_of_parallel([](const int& number) { return number >= 7; })); 1310 | } 1311 | #endif 1312 | 1313 | TEST(VectorTest, NoneOfFalse) 1314 | { 1315 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1316 | EXPECT_FALSE(vector_under_test.none_of([](const int& number) { return number > 7; })); 1317 | } 1318 | 1319 | TEST(VectorTest, NoneOfTrue) 1320 | { 1321 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1322 | EXPECT_TRUE(vector_under_test.none_of([](const int& number) { return number < -2; })); 1323 | } 1324 | 1325 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 1326 | TEST(VectorTest, NoneOfParallelFalse) 1327 | { 1328 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1329 | EXPECT_FALSE(vector_under_test.none_of_parallel([](const int& number) { return number > 7; })); 1330 | } 1331 | 1332 | TEST(VectorTest, NoneOfParallelTrue) 1333 | { 1334 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1335 | EXPECT_TRUE(vector_under_test.none_of_parallel([](const int& number) { return number < -2; })); 1336 | } 1337 | 1338 | TEST(VectorTest, ForEachParallel) 1339 | { 1340 | vector vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1341 | EXPECT_EQ(9, vector_under_test.size()); 1342 | std::atomic counter(0); 1343 | vector_under_test.for_each_parallel([&](const int& element){ ++counter; }); 1344 | EXPECT_EQ(9, counter); 1345 | } 1346 | #endif 1347 | 1348 | TEST(VectorTest, Distinct) 1349 | { 1350 | const vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1351 | const auto& unique_numbers = numbers.distinct(); 1352 | EXPECT_EQ(set({1, 2, 3, 4, 5, 7, 8}), unique_numbers); 1353 | } 1354 | 1355 | TEST(VectorTest, DistinctCustomType) 1356 | { 1357 | const vector persons({ 1358 | person(15, "Jake"), 1359 | person(15, "Jake"), 1360 | person(18, "Jannet"), 1361 | person(25, "Kate"), 1362 | person(25, "Kate"), 1363 | person(62, "Bob") 1364 | }); 1365 | 1366 | const auto& unique_persons = persons.distinct(); 1367 | 1368 | const set expected({ 1369 | person(15, "Jake"), 1370 | person(18, "Jannet"), 1371 | person(25, "Kate"), 1372 | person(62, "Bob") 1373 | }); 1374 | 1375 | EXPECT_EQ(expected, unique_persons); 1376 | } 1377 | 1378 | #pragma warning( pop ) 1379 | -------------------------------------------------------------------------------- /include/vector.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2023 Ioannis Kaliakatsos 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "index_range.h" 30 | #include "optional.h" 31 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 32 | #include 33 | #endif 34 | 35 | namespace fcpp { 36 | template 37 | class set; 38 | 39 | // A lightweight wrapper around std::vector, enabling fluent and functional 40 | // programming on the vector itself, rather than using the more procedural style 41 | // of the standard library algorithms. 42 | // 43 | // Member functions can be mutating (eg. my_vector.reverse()) or 44 | // non-mutating (eg. my_vector.reversed()) enforcing thread safety if needed 45 | template 46 | class vector 47 | { 48 | public: 49 | vector() 50 | : m_vector() 51 | { 52 | } 53 | 54 | explicit vector(const std::vector& vector) 55 | : m_vector(vector) 56 | { 57 | } 58 | 59 | explicit vector(std::vector&& vector) 60 | : m_vector(std::move(vector)) 61 | { 62 | } 63 | 64 | explicit vector(std::initializer_list list) 65 | : m_vector(std::move(list)) 66 | { 67 | } 68 | 69 | // Creates a new vector by repeating a given element. 70 | // 71 | // example: 72 | // const fcpp::vector filled_vector(3, "John"); 73 | // 74 | // outcome: 75 | // filled_vector -> fcpp::vector({ "John", "John", "John" }) 76 | explicit vector(size_t count, const T& element) 77 | : m_vector(count, element) 78 | { 79 | } 80 | 81 | // Performs the functional `map` algorithm, in which every element of the resulting vector is the 82 | // output of applying the transform function on every element of this instance. 83 | // 84 | // example: 85 | // const fcpp::vector input_vector({ 1, 3, -5 }); 86 | // const auto output_vector = input_vector.map([](const auto& element) { 87 | // return std::to_string(element); 88 | // }); 89 | // 90 | // outcome: 91 | // output_vector -> fcpp::vector({ "1", "3", "-5" }) 92 | // 93 | // is equivalent to: 94 | // const fcpp::vector input_vector({ 1, 3, -5 }); 95 | // fcpp::vector output_vector; 96 | // for (auto i = 0; i < input_vector.size(); ++i) { 97 | // output_vector.insert_back(std::to_string(input_vector[i])); 98 | // } 99 | #ifdef CPP17_AVAILABLE 100 | template >> 101 | #else 102 | template 103 | #endif 104 | vector map(Transform&& transform) const 105 | { 106 | std::vector transformed_vector; 107 | transformed_vector.reserve(m_vector.size()); 108 | std::transform(m_vector.begin(), 109 | m_vector.end(), 110 | std::back_inserter(transformed_vector), 111 | std::forward(transform)); 112 | return vector(transformed_vector); 113 | } 114 | 115 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 116 | // Performs the functional `map` algorithm in parallel. 117 | // See also the sequential version for more documentation. 118 | template >> 119 | vector map_parallel(Transform&& transform) const 120 | { 121 | std::vector transformed_vector; 122 | transformed_vector.resize(m_vector.size()); 123 | std::transform(std::execution::par, 124 | m_vector.cbegin(), 125 | m_vector.cend(), 126 | transformed_vector.begin(), 127 | std::forward(transform)); 128 | return vector(transformed_vector); 129 | } 130 | #endif 131 | 132 | // Returns true if all elements match the predicate (return true) 133 | // 134 | // example: 135 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 136 | // 137 | // // returns true 138 | // numbers.all_of([](const auto &number) { 139 | // return number < 10; 140 | // }); 141 | // 142 | // // returns false 143 | // numbers.all_of([](const auto &number) { 144 | // return number > 2; 145 | // }); 146 | #ifdef CPP17_AVAILABLE 147 | template >> 148 | #else 149 | template 150 | #endif 151 | bool all_of(Callable&& unary_predicate) const 152 | { 153 | return std::all_of(begin(), 154 | end(), 155 | std::forward(unary_predicate)); 156 | } 157 | 158 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 159 | // Performs the `all_of` algorithm in parallel. 160 | // See also the sequential version for more documentation. 161 | template >> 162 | bool all_of_parallel(Callable&& unary_predicate) const 163 | { 164 | return std::all_of(std::execution::par, 165 | begin(), 166 | end(), 167 | std::forward(unary_predicate)); 168 | } 169 | #endif 170 | 171 | // Returns true if at least one of the elements matches the predicate (returns true) 172 | // 173 | // example: 174 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 175 | // 176 | // // returns true 177 | // numbers.any_of([](const auto &number) { 178 | // return number < 5; 179 | // }); 180 | // 181 | // // returns false 182 | // numbers.any_of([](const auto &number) { 183 | // return number > 9; 184 | // }); 185 | #ifdef CPP17_AVAILABLE 186 | template >> 187 | #else 188 | template 189 | #endif 190 | bool any_of(Callable&& unary_predicate) const 191 | { 192 | return std::any_of(begin(), 193 | end(), 194 | std::forward(unary_predicate)); 195 | } 196 | 197 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 198 | // Performs the `any_of` algorithm in parallel. 199 | // See also the sequential version for more documentation. 200 | template >> 201 | bool any_of_parallel(Callable&& unary_predicate) const 202 | { 203 | return std::any_of(std::execution::par, 204 | begin(), 205 | end(), 206 | std::forward(unary_predicate)); 207 | } 208 | #endif 209 | 210 | // Returns true if no element matches the predicate (all return false) 211 | // 212 | // example: 213 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 214 | // 215 | // // returns true 216 | // numbers.none_of([](const auto &number) { 217 | // return number < -2; 218 | // }); 219 | // 220 | // // returns false 221 | // numbers.none_of([](const auto &number) { 222 | // return number > 7; 223 | // }); 224 | #ifdef CPP17_AVAILABLE 225 | template >> 226 | #else 227 | template 228 | #endif 229 | bool none_of(Callable&& unary_predicate) const 230 | { 231 | return std::none_of(begin(), 232 | end(), 233 | std::forward(unary_predicate)); 234 | } 235 | 236 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 237 | // Performs the `none_of` algorithm in parallel. 238 | // See also the sequential version for more documentation. 239 | template >> 240 | bool none_of_parallel(Callable&& unary_predicate) const 241 | { 242 | return std::none_of(std::execution::par, 243 | begin(), 244 | end(), 245 | std::forward(unary_predicate)); 246 | } 247 | #endif 248 | 249 | // Performs the functional `reduce` (fold/accumulate) algorithm, by returning the result of 250 | // accumulating all the values in the vector to an initial value. (non-mutating) 251 | // 252 | // example: 253 | // const fcpp::vector tokens({ "the", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "brown", "dog" }); 254 | // const auto sentence = tokens.reduce("", [](const std::string& partial, const std::string& token) { 255 | // return partial.length() != 0 256 | // ? partial + " " + token 257 | // : token; 258 | // }); 259 | // 260 | // outcome: 261 | // sentence -> std::string("the quick brown fox jumps over the lazy brown dog"); 262 | #ifdef CPP17_AVAILABLE 263 | template >> 264 | #else 265 | template 266 | #endif 267 | U reduce(const U& initial, Reduce&& reduction) const 268 | { 269 | auto result = initial; 270 | for (const auto& x : m_vector) { 271 | result = reduction(result, x); 272 | } 273 | return result; 274 | } 275 | 276 | // Performs the functional `filter` algorithm, in which all elements of this instance 277 | // which match the given predicate are kept (mutating) 278 | // 279 | // example: 280 | // fcpp::vector numbers({ 1, 3, -5, 2, -1, 9, -4 }); 281 | // numbers.filter([](const auto& element) { 282 | // return element >= 1.5; 283 | // }); 284 | // 285 | // outcome: 286 | // numbers -> fcpp::vector({ 3, 2, 9 }); 287 | // 288 | // is equivalent to: 289 | // fcpp::vector numbers({ 1, 3, -5, 2, -1, 9, -4 }); 290 | // for (auto i = 0; i < numbers.size(); ++i) { 291 | // if (numbers[i] >= 1.5) { 292 | // continue; 293 | // } 294 | // numbers.remove_at(i); 295 | // i--; 296 | // } 297 | #ifdef CPP17_AVAILABLE 298 | template >> 299 | #else 300 | template 301 | #endif 302 | vector& filter(Filter&& predicate_to_keep) 303 | { 304 | m_vector.erase(std::remove_if(m_vector.begin(), 305 | m_vector.end(), 306 | [predicate=std::forward(predicate_to_keep)](const T& element){ 307 | return !predicate(element); 308 | }), m_vector.end()); 309 | return *this; 310 | } 311 | 312 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 313 | // Performs the functional `filter` algorithm in parallel. 314 | // See also the sequential version for more documentation. 315 | template >> 316 | vector& filter_parallel(Filter&& predicate_to_keep) 317 | { 318 | m_vector.erase(std::remove_if(std::execution::par, 319 | m_vector.begin(), 320 | m_vector.end(), 321 | [predicate=std::forward(predicate_to_keep)](const T& element){ 322 | return !predicate(element); 323 | }), m_vector.end()); 324 | return *this; 325 | } 326 | #endif 327 | 328 | // Performs the functional `filter` algorithm in a copy of this instance, in which all elements of 329 | // the copy which match the given predicate are kept (non-mutating) 330 | // 331 | // example: 332 | // const fcpp::vector numbers({ 1, 3, -5, 2, -1, 9, -4 }); 333 | // const auto filtered_numbers = numbers.filtered([](const auto& element) { 334 | // return element >= 1.5; 335 | // }); 336 | // 337 | // outcome: 338 | // numbers -> fcpp::vector({ 1, 3, -5, 2, -1, 9, -4 }) 339 | // filtered_numbers -> fcpp::vector({ 3, 2, 9 }) 340 | // 341 | // is equivalent to: 342 | // const fcpp::vector numbers({ 1, 3, -5, 2, -1, 9, -4 }); 343 | // fcpp::vector filtered_numbers; 344 | // for (auto i = 0; i < numbers.size(); ++i) { 345 | // if (numbers[i] >= 1.5) { 346 | // filtered_numbers.insert_back(numbers[i]); 347 | // } 348 | // } 349 | #ifdef CPP17_AVAILABLE 350 | template >> 351 | #else 352 | template 353 | #endif 354 | vector filtered(Callable&& predicate_to_keep) const 355 | { 356 | std::vector filtered_vector; 357 | filtered_vector.reserve(m_vector.size()); 358 | std::copy_if(m_vector.begin(), 359 | m_vector.end(), 360 | std::back_inserter(filtered_vector), 361 | std::forward(predicate_to_keep)); 362 | return vector(filtered_vector); 363 | } 364 | 365 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 366 | // Performs the `filtered` algorithm in parallel. 367 | // See also the sequential version for more documentation. 368 | template >> 369 | vector filtered_parallel(Callable&& predicate_to_keep) const 370 | { 371 | #ifdef _MSC_VER 372 | // Visual Studio compiler is stricter than GCC in its use of iterators, so back_inserter wouldn't work here 373 | auto copy(*this); 374 | copy.filter_parallel(predicate_to_keep); 375 | return copy; 376 | #else 377 | std::vector filtered_vector; 378 | filtered_vector.reserve(m_vector.size()); 379 | std::copy_if(std::execution::par, 380 | m_vector.begin(), 381 | m_vector.end(), 382 | std::back_inserter(filtered_vector), 383 | std::forward(predicate_to_keep)); 384 | return vector(filtered_vector); 385 | #endif 386 | } 387 | #endif 388 | 389 | // Reverses the order of the elements in place (mutating) 390 | // 391 | // example: 392 | // fcpp::vector numbers_vector({ 1, 3, -5, 2, -1, 9, -4 }); 393 | // numbers_vector.reverse(); 394 | // 395 | // outcome: 396 | // numbers_vector -> fcpp::vector({ -4, 9, -1, 2, -5, 3, 1 }) 397 | vector& reverse() 398 | { 399 | std::reverse(m_vector.begin(), m_vector.end()); 400 | return *this; 401 | } 402 | 403 | // Returns a copy of this instance, whose elements are in reverse order (non-mutating) 404 | // 405 | // example: 406 | // const fcpp::vector input_vector({ 1, 3, -5, 2, -1, 9, -4 }); 407 | // const auto reversed_vector = input_vector.reversed(); 408 | // 409 | // outcome: 410 | // input_vector -> fcpp::vector({ 1, 3, -5, 2, -1, 9, -4 }); 411 | // reversed_vector -> fcpp::vector({ -4, 9, -1, 2, -5, 3, 1 }) 412 | [[nodiscard]] vector reversed() const 413 | { 414 | std::vector reversed_vector(m_vector.crbegin(), m_vector.crend()); 415 | return vector(std::move(reversed_vector)); 416 | } 417 | 418 | #ifdef CPP17_AVAILABLE 419 | template 420 | using deref_type = typename std::iterator_traits::value_type; 421 | 422 | template 423 | struct is_valid_iterator 424 | { 425 | static bool const value = std::is_constructible_v>; 426 | }; 427 | #endif 428 | 429 | // Performs the functional `zip` algorithm, in which every element of the resulting vector is a 430 | // tuple of this instance's element (first) and the second vector's element (second) at the same 431 | // index. The sizes of the two vectors must be equal. 432 | // 433 | // example: 434 | // const fcpp::vector ages_vector({32, 25, 53}); 435 | // const fcpp::vector names_vector({"Jake", "Mary", "John"}); 436 | // const auto zipped_vector = ages_vector.zip(names_vector); 437 | // 438 | // outcome: 439 | // zipped_vector -> fcpp::vector>({ 440 | // (32, "Jake"), 441 | // (25, "Mary"), 442 | // (53, "John"), 443 | // }) 444 | // 445 | // is equivalent to: 446 | // const fcpp::vector ages_vector({32, 25, 53}); 447 | // const fcpp::vector names_vector({"Jake", "Mary", "John"}); 448 | // fcpp::vector> zipped_vector; 449 | // for (auto i = 0; i < ages_vector.size(); ++i) { 450 | // fcpp::vector::pair tuple; 451 | // tuple.first = ages_vector[i]; 452 | // tuple.second = names_vector[i]; 453 | // zipped_vector.insert_back(tuple); 454 | // } 455 | template 456 | [[nodiscard]] vector> zip(const vector& vector) const 457 | { 458 | #ifdef CPP17_AVAILABLE 459 | return zip_impl(vector.begin(), vector.end()); 460 | #else 461 | return zip_impl(vector.begin(), vector.end()); 462 | #endif 463 | } 464 | 465 | // Performs the functional `zip` algorithm, in which every element of the resulting vector is a 466 | // tuple of this instance's element (first) and the second vector's element (second) at the same 467 | // index. The sizes of the two vectors must be equal. 468 | // 469 | // example: 470 | // const fcpp::vector ages_vector({32, 25, 53}); 471 | // const std::vector names_vector({"Jake", "Mary", "John"}); 472 | // const auto zipped_vector = ages_vector.zip(names_vector); 473 | // 474 | // outcome: 475 | // zipped_vector -> fcpp::vector>({ 476 | // (32, "Jake"), 477 | // (25, "Mary"), 478 | // (53, "John"), 479 | // }) 480 | // 481 | // is equivalent to: 482 | // const fcpp::vector ages_vector({32, 25, 53}); 483 | // const std::vector names_vector({"Jake", "Mary", "John"}); 484 | // fcpp::vector> zipped_vector; 485 | // for (auto i = 0; i < ages_vector.size(); ++i) { 486 | // fcpp::vector::pair tuple; 487 | // tuple.first = ages_vector[i]; 488 | // tuple.second = names_vector[i]; 489 | // zipped_vector.insert_back(tuple); 490 | // } 491 | template 492 | [[nodiscard]] vector> zip(const std::vector& vector) const 493 | { 494 | #ifdef CPP17_AVAILABLE 495 | return zip_impl(vector.cbegin(), vector.cend()); 496 | #else 497 | return zip_impl(vector.cbegin(), vector.cend()); 498 | #endif 499 | } 500 | 501 | // example: 502 | // const fcpp::vector ages_vector({32, 25, 53}); 503 | // const std::initializer_list names_vector({"Jake", "Mary", "John"}); 504 | // const auto zipped_vector = ages_vector.zip(names_vector); 505 | // 506 | // outcome: 507 | // zipped_vector -> fcpp::vector>({ 508 | // (32, "Jake"), 509 | // (25, "Mary"), 510 | // (53, "John"), 511 | // }) 512 | // 513 | // is equivalent to: 514 | // const fcpp::vector ages_vector({32, 25, 53}); 515 | // const std::initializer_list names_vector({"Jake", "Mary", "John"}); 516 | // fcpp::vector> zipped_vector; 517 | // for (auto i = 0; i < ages_vector.size(); ++i) { 518 | // fcpp::vector::pair tuple; 519 | // tuple.first = ages_vector[i]; 520 | // tuple.second = names_vector[i]; 521 | // zipped_vector.insert_back(tuple); 522 | // } 523 | template 524 | [[nodiscard]] vector> zip(const std::initializer_list& list) const 525 | { 526 | #ifdef CPP17_AVAILABLE 527 | return zip_impl(list.begin(), list.end()); 528 | #else 529 | return zip(std::vector(list)); 530 | #endif 531 | } 532 | 533 | // Sorts the vector in place (mutating). The comparison predicate takes two elements 534 | // `v1` and `v2` and returns true if the first element `v1` should appear before `v2`. 535 | // 536 | // example: 537 | // struct person 538 | // { 539 | // int age; 540 | // std::string name; 541 | // }; 542 | // ... 543 | // fcpp::vector persons_vector({ 544 | // person(45, "Jake"), person(34, "Bob"), person(52, "Manfred"), person(8, "Alice") 545 | // }); 546 | // persons_vector.sort([](const auto& person1, const auto& person2) { 547 | // return person1.name < person2.name; 548 | // }); 549 | // 550 | // outcome: 551 | // person_vector -> fcpp::vector({ 552 | // person(8, "Alice"), person(34, "Bob"), person(45, "Jake"), person(52, "Manfred") 553 | // }); 554 | #ifdef CPP17_AVAILABLE 555 | template >> 556 | #else 557 | template 558 | #endif 559 | vector& sort(Sortable&& comparison_predicate) 560 | { 561 | std::sort(m_vector.begin(), 562 | m_vector.end(), 563 | std::forward(comparison_predicate)); 564 | return *this; 565 | } 566 | 567 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 568 | // Performs the `sort` algorithm in parallel. 569 | // See also the sequential version for more documentation. 570 | template >> 571 | vector& sort_parallel(Sortable&& comparison_predicate) 572 | { 573 | std::sort(std::execution::par, 574 | m_vector.begin(), 575 | m_vector.end(), 576 | std::forward(comparison_predicate)); 577 | return *this; 578 | } 579 | #endif 580 | 581 | // Sorts the vector in place in ascending order, when its elements support comparison by std::less_equal [<=] (mutating). 582 | // 583 | // example: 584 | // fcpp::vector numbers({3, 1, 9, -4}); 585 | // numbers.sort_ascending(); 586 | // 587 | // outcome: 588 | // numbers -> fcpp::vector({-4, 1, 3, 9}); 589 | vector& sort_ascending() 590 | { 591 | return sort(std::less_equal()); 592 | } 593 | 594 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 595 | // Performs the `sort_ascending` algorithm in parallel. 596 | // See also the sequential version for more documentation. 597 | vector& sort_ascending_parallel() 598 | { 599 | return sort_parallel(std::less_equal()); 600 | } 601 | #endif 602 | 603 | // Sorts the vector in place in descending order, when its elements support comparison by std::greater_equal [>=] (mutating). 604 | // 605 | // example: 606 | // fcpp::vector numbers({3, 1, 9, -4}); 607 | // numbers.sort_ascending(); 608 | // 609 | // outcome: 610 | // numbers -> fcpp::vector({9, 3, 1, -4}); 611 | vector& sort_descending() 612 | { 613 | return sort(std::greater_equal()); 614 | } 615 | 616 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 617 | // Performs the `sort_ascending` algorithm in parallel. 618 | // See also the sequential version for more documentation. 619 | vector& sort_descending_parallel() 620 | { 621 | return sort_parallel(std::greater_equal()); 622 | } 623 | #endif 624 | 625 | // Returns its elements copied and sorted (non-mutating). The comparison predicate takes two elements 626 | // `v1` and `v2` and returns true if the first element `v1` should appear before `v2`. 627 | // 628 | // example: 629 | // struct person 630 | // { 631 | // int age; 632 | // std::string name; 633 | // }; 634 | // ... 635 | // const fcpp::vector persons_vector({ 636 | // person(45, "Jake"), person(34, "Bob"), person(52, "Manfred"), person(8, "Alice") 637 | // }); 638 | // auto sorted_persons_vector = persons_vector.sorted([](const auto& person1, const auto& person2) { 639 | // return person1.name < person2.name; 640 | // }); 641 | // 642 | // outcome: 643 | // sorted_persons_vector -> fcpp::vector({ 644 | // person(8, "Alice"), person(34, "Bob"), person(45, "Jake"), person(52, "Manfred") 645 | // }); 646 | #ifdef CPP17_AVAILABLE 647 | template >> 648 | #else 649 | template 650 | #endif 651 | vector sorted(Sortable&& comparison_predicate) const 652 | { 653 | auto sorted_vector(m_vector); 654 | std::sort(sorted_vector.begin(), 655 | sorted_vector.end(), 656 | std::forward(comparison_predicate)); 657 | return vector(sorted_vector); 658 | } 659 | 660 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 661 | // Performs the `sorted` algorithm in parallel. 662 | // See also the sequential version for more documentation. 663 | template >> 664 | vector sorted_parallel(Sortable&& comparison_predicate) const 665 | { 666 | auto sorted_vector(m_vector); 667 | std::sort(std::execution::par, 668 | sorted_vector.begin(), 669 | sorted_vector.end(), 670 | std::forward(comparison_predicate)); 671 | return fcpp::vector(sorted_vector); 672 | } 673 | #endif 674 | 675 | // Sorts its elements copied and sorted in ascending order, when its elements support comparison by std::less_equal [<=] (non-mutating). 676 | // 677 | // example: 678 | // const fcpp::vector numbers({3, 1, 9, -4}); 679 | // auto sorted_numbers = numbers.sorted_ascending(); 680 | // 681 | // outcome: 682 | // sorted_numbers -> fcpp::vector({-4, 1, 3, 9}); 683 | [[nodiscard]] vector sorted_ascending() const 684 | { 685 | return sorted(std::less_equal()); 686 | } 687 | 688 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 689 | // Performs the `sorted_ascending` algorithm in parallel. 690 | // See also the sequential version for more documentation. 691 | [[nodiscard]] vector sorted_ascending_parallel() const 692 | { 693 | return sorted_parallel(std::less_equal()); 694 | } 695 | #endif 696 | 697 | // Sorts its elements copied and sorted in descending order, when its elements support comparison by std::greater_equal [>=] (non-mutating). 698 | // 699 | // example: 700 | // const fcpp::vector numbers({3, 1, 9, -4}); 701 | // auto sorted_numbers = numbers.sorted_descending(); 702 | // 703 | // outcome: 704 | // sorted_numbers -> fcpp::vector({9, 3, 1, -4}); 705 | [[nodiscard]] vector sorted_descending() const 706 | { 707 | return sorted(std::greater_equal()); 708 | } 709 | 710 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 711 | // Performs the `sorted_descending` algorithm in parallel. 712 | // See also the sequential version for more documentation. 713 | [[nodiscard]] vector sorted_descending_parallel() const 714 | { 715 | return sorted_parallel(std::greater_equal()); 716 | } 717 | #endif 718 | 719 | // Executes the given operation for each element of the vector. The operation must not 720 | // change the vector's contents during execution. 721 | #ifdef CPP17_AVAILABLE 722 | template >> 723 | #else 724 | template 725 | #endif 726 | const vector& for_each(Callable&& operation) const 727 | { 728 | std::for_each(m_vector.cbegin(), 729 | m_vector.cend(), 730 | std::forward(operation)); 731 | return *this; 732 | } 733 | 734 | #ifdef PARALLEL_ALGORITHM_AVAILABLE 735 | // Executes the given operation for each element of the vector in parallel. The operation must not 736 | // change the vector's contents during execution. 737 | template >> 738 | const vector& for_each_parallel(Callable&& operation) const 739 | { 740 | std::for_each(std::execution::par, 741 | m_vector.cbegin(), 742 | m_vector.cend(), 743 | std::forward(operation)); 744 | return *this; 745 | } 746 | #endif 747 | 748 | // Returns the first index in which the given element is found in the vector. 749 | // In case of multiple occurrences, only the first index is returned 750 | // (see find_all_indices for multiple occurrences). 751 | // 752 | // example: 753 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 754 | // const auto index_of_one = numbers.find_first_index(1); 755 | // const auto index_of_nine = numbers.find_first_index(9); 756 | // 757 | // outcome: 758 | // index_of_one.value() -> 0 759 | // index_of_one.has_value() -> true 760 | // index_of_nine.has_value() -> false 761 | [[nodiscard]] fcpp::optional_t find_first_index(const T& element) const 762 | { 763 | auto const it = std::find(m_vector.cbegin(), 764 | m_vector.cend(), 765 | element); 766 | if (it != m_vector.cend()) { 767 | auto index = std::distance(m_vector.cbegin(), it); 768 | return index; 769 | } 770 | return fcpp::optional_t(); 771 | } 772 | 773 | // Returns the last index in which the given element is found in the vector. 774 | // In case of multiple occurrences, only the last index is returned 775 | // (see find_all_indices for multiple occurrences). 776 | // 777 | // example: 778 | // const fcpp::vector numbers({1, 4, 2, 5, -6, 3, 1, 7, 1}); 779 | // const auto index_of_one = numbers.find_last_index(1); 780 | // const auto index_of_nine = numbers.find_last_index(9); 781 | // 782 | // outcome: 783 | // index_of_one.value() -> 8 784 | // index_of_one.has_value() -> true 785 | // index_of_nine.has_value() -> false 786 | [[nodiscard]] fcpp::optional_t find_last_index(const T& element) const 787 | { 788 | auto const it = std::find(m_vector.crbegin(), 789 | m_vector.crend(), 790 | element); 791 | if (it != m_vector.crend()) { 792 | auto index = std::distance(it, m_vector.crend()) - 1; 793 | return index; 794 | } 795 | return fcpp::optional_t(); 796 | } 797 | 798 | // Returns all indices in which the given element is found in the vector. 799 | // 800 | // example: 801 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 9, 1}); 802 | // const auto indices_of_one = numbers.find_all_indices(1); 803 | // const auto indices_of_ten = numbers.find_all_indices(10); 804 | // 805 | // outcome: 806 | // indices_of_one -> { 0, 6, 8 } 807 | // indices_of_ten -> empty vector 808 | [[nodiscard]] std::vector find_all_indices(const T& element) const 809 | { 810 | std::vector indices; 811 | auto it = std::find(m_vector.cbegin(), 812 | m_vector.cend(), 813 | element); 814 | while (it != m_vector.cend()) { 815 | indices.push_back(std::distance(m_vector.cbegin(), it)); 816 | ++it; 817 | it = std::find(it, m_vector.cend(), element); 818 | } 819 | return indices; 820 | } 821 | 822 | // Removes the element at `index` (mutating) 823 | // 824 | // example: 825 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 826 | // numbers.remove_at(4); 827 | // 828 | // outcome: 829 | // numbers -> fcpp::vector({1, 4, 2, 5, 3, 1, 7, 1}); 830 | vector& remove_at(size_t index) 831 | { 832 | assert_smaller_size(index); 833 | m_vector.erase(begin() + index); 834 | return *this; 835 | } 836 | 837 | // Returns a copy of itself in which the element at `index` is removed (non-mutating) 838 | // 839 | // example: 840 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 841 | // auto shorter_vector = numbers.removing_at(4); 842 | // 843 | // outcome: 844 | // shorter_vector -> fcpp::vector({1, 4, 2, 5, 3, 1, 7, 1}); 845 | [[nodiscard]] vector removing_at(size_t index) const 846 | { 847 | assert_smaller_size(index); 848 | auto copy(m_vector); 849 | copy.erase(copy.begin() + index); 850 | return vector(copy); 851 | } 852 | 853 | // Removes the last element, if present (mutating) 854 | // 855 | // example: 856 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 857 | // numbers.remove_back(); 858 | // 859 | // outcome: 860 | // numbers -> fcpp::vector({1, 4, 2, 5, 8, 3, 1, 7}); 861 | vector& remove_back() 862 | { 863 | m_vector.pop_back(); 864 | return *this; 865 | } 866 | 867 | // Returns a copy of itself in which the last element is removed (non-mutating) 868 | // 869 | // example: 870 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 871 | // auto shorter_vector = numbers.removing_back(); 872 | // 873 | // outcome: 874 | // shorter_vector -> fcpp::vector({1, 4, 2, 5, 8, 3, 1, 7}); 875 | [[nodiscard]] vector removing_back() const 876 | { 877 | auto copy(m_vector); 878 | copy.pop_back(); 879 | return vector(copy); 880 | } 881 | 882 | // Removes the first element, if present (mutating) 883 | // 884 | // example: 885 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 886 | // numbers.remove_front(); 887 | // 888 | // outcome: 889 | // numbers -> fcpp::vector({4, 2, 5, 8, 3, 1, 7, 1}); 890 | vector& remove_front() 891 | { 892 | if (size() == 0) { 893 | return *this; 894 | } 895 | return remove_at(0); 896 | } 897 | 898 | // Returns a copy of itself in which the first element is removed (non-mutating) 899 | // 900 | // example: 901 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 902 | // auto shorter_numbers = numbers.removing_front(); 903 | // 904 | // outcome: 905 | // shorter_numbers -> fcpp::vector({4, 2, 5, 8, 3, 1, 7, 1}); 906 | [[nodiscard]] vector removing_front() const 907 | { 908 | if (size() == 0) { 909 | return *this; 910 | } 911 | return removing_at(0); 912 | } 913 | 914 | // Removes the elements whose index is contained in the given index range (mutating) 915 | // 916 | // example: 917 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 918 | // numbers.remove_range(index_range::start_count(2, 3)); 919 | // 920 | // outcome: 921 | // numbers -> fcpp::vector({ 1, 4, 2, 7, 1 }) 922 | vector& remove_range(index_range range) 923 | { 924 | if (!range.is_valid || size() < range.end + 1) { 925 | return *this; 926 | } 927 | m_vector.erase(begin() + range.start, 928 | begin() + range.start + range.count); 929 | return *this; 930 | } 931 | 932 | // Returns a copy by removing the elements whose index is contained in the given index range (non-mutating) 933 | // 934 | // example: 935 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 936 | // const auto shorter_vector = numbers.removing_range(index_range::start_count(2, 3)); 937 | // 938 | // outcome: 939 | // shorter_vector -> fcpp::vector({ 1, 4, 3, 1, 7, 1 }) 940 | [[nodiscard]] vector removing_range(index_range range) const 941 | { 942 | if (!range.is_valid || size() < range.end + 1) { 943 | return *this; 944 | } 945 | auto shorter_vector(m_vector); 946 | shorter_vector.erase(shorter_vector.begin() + range.start, 947 | shorter_vector.begin() + range.start + range.count); 948 | return vector(shorter_vector); 949 | } 950 | 951 | // Inserts an element at the given index, therefore changing the vector's contents (mutating) 952 | // 953 | // example: 954 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 955 | // numbers.insert_at(3, 18); 956 | // 957 | // outcome: 958 | // numbers -> fcpp::vector({1, 4, 2, 18, 5, 8, 3, 1, 7, 1}); 959 | vector& insert_at(size_t index, const T& element) 960 | { 961 | assert_smaller_or_equal_size(index); 962 | m_vector.insert(begin() + index, element); 963 | return *this; 964 | } 965 | 966 | // Returns a copy by inserting an element at the given index (non-mutating) 967 | // 968 | // example: 969 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 970 | // auto augmented_numbers = numbers.inserting_at(3, 18); 971 | // 972 | // outcome: 973 | // augmented_numbers -> fcpp::vector({1, 4, 2, 18, 5, 8, 3, 1, 7, 1}); 974 | [[nodiscard]] vector inserting_at(size_t index, const T& element) const 975 | { 976 | assert_smaller_or_equal_size(index); 977 | auto copy(m_vector); 978 | copy.insert(copy.begin() + index, element); 979 | return vector(copy); 980 | } 981 | 982 | // Inserts a range of elements starting at the given index, therefore changing the vector's contents (mutating) 983 | // 984 | // example: 985 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 986 | // const fcpp::vector vector_to_insert({9, -5, 6}); 987 | // numbers.insert_at(3, vector_to_insert); 988 | // 989 | // outcome: 990 | // numbers -> fcpp::vector({1, 4, 2, 9, -5, 6, 5, 8, 3, 1, 7, 1}); 991 | vector& insert_at(size_t index, const vector& vector) 992 | { 993 | return insert_at_impl(index, vector.begin(), vector.end()); 994 | } 995 | 996 | // Returns a copy by inserting a range of elements starting at the given index (non-mutating) 997 | // 998 | // example: 999 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1000 | // const fcpp::vector vector_to_insert({9, -5, 6}); 1001 | // auto augmented_numbers = numbers.inserting_at(3, vector_to_insert); 1002 | // 1003 | // outcome: 1004 | // augmented_numbers -> fcpp::vector({1, 4, 2, 9, -5, 6, 5, 8, 3, 1, 7, 1}); 1005 | [[nodiscard]] vector inserting_at(size_t index, const vector& vector) const 1006 | { 1007 | return inserting_at_impl(index, vector.begin(), vector.end()); 1008 | } 1009 | 1010 | // Inserts a range of elements starting at the given index, therefore changing the vector's contents (mutating) 1011 | // 1012 | // example: 1013 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1014 | // const std::vector vector_to_insert({9, -5, 6}); 1015 | // numbers.insert_at(3, vector_to_insert); 1016 | // 1017 | // outcome: 1018 | // numbers -> fcpp::vector({1, 4, 2, 9, -5, 6, 5, 8, 3, 1, 7, 1}); 1019 | vector& insert_at(size_t index, const std::vector& vector) 1020 | { 1021 | return insert_at_impl(index, vector.cbegin(), vector.cend()); 1022 | } 1023 | 1024 | // Returns a copy by inserting a range of elements starting at the given index (non-mutating) 1025 | // 1026 | // example: 1027 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1028 | // const std::vector vector_to_insert({9, -5, 6}); 1029 | // auto augmented_numbers = numbers.inserting_at(3, vector_to_insert); 1030 | // 1031 | // outcome: 1032 | // augmented_numbers -> fcpp::vector({1, 4, 2, 9, -5, 6, 5, 8, 3, 1, 7, 1}); 1033 | [[nodiscard]] vector inserting_at(size_t index, const std::vector& vector) const 1034 | { 1035 | return inserting_at_impl(index, vector.cbegin(), vector.cend()); 1036 | } 1037 | 1038 | // Inserts a range of elements starting at the given index, therefore changing the vector's contents (mutating) 1039 | // 1040 | // example: 1041 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1042 | // const std::initializer_list vector_to_insert({9, -5, 6}); 1043 | // numbers.insert_at(3, vector_to_insert); 1044 | // 1045 | // outcome: 1046 | // numbers -> fcpp::vector({1, 4, 2, 9, -5, 6, 5, 8, 3, 1, 7, 1}); 1047 | vector& insert_at(size_t index, std::initializer_list list) 1048 | { 1049 | return insert_at(index, std::vector(std::move(list))); 1050 | } 1051 | 1052 | // Returns a copy by inserting a range of elements starting at the given index (non-mutating) 1053 | // 1054 | // example: 1055 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1056 | // const std::initializer_list vector_to_insert({9, -5, 6}); 1057 | // auto augmented_numbers = numbers.inserting_at(3, vector_to_insert); 1058 | // 1059 | // outcome: 1060 | // augmented_numbers -> fcpp::vector({1, 4, 2, 9, -5, 6, 5, 8, 3, 1, 7, 1}); 1061 | [[nodiscard]] vector inserting_at(size_t index, std::initializer_list list) const 1062 | { 1063 | return inserting_at(index, std::vector(std::move(list))); 1064 | } 1065 | 1066 | // Inserts a value at the end of the vector in place (mutating) 1067 | // 1068 | // example: 1069 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1070 | // numbers.insert_back(18); 1071 | // 1072 | // outcome: 1073 | // numbers -> fcpp::vector({1, 4, 2, 5, 8, 3, 1, 7, 1, 18}); 1074 | vector& insert_back(T value) 1075 | { 1076 | m_vector.push_back(value); 1077 | return *this; 1078 | } 1079 | 1080 | // Inserts a value at the beginning of the vector in place (mutating) 1081 | // 1082 | // example: 1083 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1084 | // numbers.insert_front(18); 1085 | // 1086 | // outcome: 1087 | // numbers -> fcpp::vector({18, 1, 4, 2, 5, 8, 3, 1, 7, 1}); 1088 | vector& insert_front(T value) 1089 | { 1090 | return insert_at(0, value); 1091 | } 1092 | 1093 | // Makes a copy of the vector, inserts value at the end of the copy and returns the copy (non-mutating) 1094 | // 1095 | // example: 1096 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1097 | // auto augmented_numbers = numbers.inserting_back(18); 1098 | // 1099 | // outcome: 1100 | // augmented_numbers -> fcpp::vector({1, 4, 2, 5, 8, 3, 1, 7, 1, 18}); 1101 | [[nodiscard]] vector inserting_back(T value) const 1102 | { 1103 | auto augmented_vector(m_vector); 1104 | augmented_vector.push_back(value); 1105 | return vector(augmented_vector); 1106 | } 1107 | 1108 | // Makes a copy of the vector, inserts value at the beginning of the copy and returns the copy (non-mutating) 1109 | // 1110 | // example: 1111 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1112 | // auto augmented_numbers = numbers.inserting_front(18); 1113 | // 1114 | // outcome: 1115 | // augmented_numbers -> fcpp::vector({18, 1, 4, 2, 5, 8, 3, 1, 7, 1}); 1116 | [[nodiscard]] vector inserting_front(T value) const 1117 | { 1118 | return inserting_at(0, value); 1119 | } 1120 | 1121 | // Inserts a range of values at the end of the vector in place (mutating) 1122 | // 1123 | // example: 1124 | // fcpp::vector numbers({ 4, 5, 6 }); 1125 | // numbers.insert_back(fcpp::vector({1, 2, 3})); 1126 | // 1127 | // outcome: 1128 | // numbers -> fcpp::vector numbers({ 4, 5, 6, 1, 2, 3 }); 1129 | vector& insert_back(const vector& vector) 1130 | { 1131 | return insert_back_range_impl(vector.begin(), vector.end()); 1132 | } 1133 | 1134 | // Inserts a range of values at the beginning of the vector in place (mutating) 1135 | // 1136 | // example: 1137 | // fcpp::vector numbers({ 4, 5, 6 }); 1138 | // numbers.insert_front(fcpp::vector({1, 2, 3})); 1139 | // 1140 | // outcome: 1141 | // numbers -> fcpp::vector numbers({ 1, 2, 3, 4, 5, 6 }); 1142 | vector& insert_front(const vector& vector) 1143 | { 1144 | return insert_front_range_impl(vector.begin(), vector.end()); 1145 | } 1146 | 1147 | // Makes a copy of the vector, inserts a range of values at the end of the copy, and returns the copy (non-mutating) 1148 | // 1149 | // example: 1150 | // const fcpp::vector numbers({ 4, 5, 6 }); 1151 | // auto augmented_numbers = numbers.inserting_back(fcpp::vector({1, 2, 3})); 1152 | // 1153 | // outcome: 1154 | // augmented_numbers -> fcpp::vector numbers({ 4, 5, 6, 1, 2, 3 }); 1155 | [[nodiscard]] vector inserting_back(const vector& vector) const 1156 | { 1157 | return inserting_back_range_impl(vector.begin(), vector.end()); 1158 | } 1159 | 1160 | // Makes a copy of the vector, inserts a range of values at the beginning of the copy, and returns the copy (non-mutating) 1161 | // 1162 | // example: 1163 | // const fcpp::vector numbers({ 4, 5, 6 }); 1164 | // auto augmented_numbers = numbers.inserting_front(fcpp::vector({1, 2, 3})); 1165 | // 1166 | // outcome: 1167 | // augmented_numbers -> fcpp::vector numbers({ 1, 2, 3, 4, 5, 6 }); 1168 | [[nodiscard]] vector inserting_front(const vector& vector) const 1169 | { 1170 | return inserting_front_range_impl(vector.begin(), vector.end()); 1171 | } 1172 | 1173 | // Inserts a range of values at the end of the vector in place (mutating) 1174 | // 1175 | // example: 1176 | // fcpp::vector numbers({ 4, 5, 6 }); 1177 | // numbers.insert_back(std::vector({1, 2, 3})); 1178 | // 1179 | // outcome: 1180 | // numbers -> fcpp::vector numbers({ 4, 5, 6, 1, 2, 3 }); 1181 | vector& insert_back(const std::vector& vector) 1182 | { 1183 | return insert_back_range_impl(vector.cbegin(), vector.cend()); 1184 | } 1185 | 1186 | // Inserts a range of values at the beginning of the vector in place (mutating) 1187 | // 1188 | // example: 1189 | // fcpp::vector numbers({ 4, 5, 6 }); 1190 | // numbers.insert_front(std::vector({1, 2, 3})); 1191 | // 1192 | // outcome: 1193 | // numbers -> fcpp::vector numbers({ 1, 2, 3, 4, 5, 6 }); 1194 | vector& insert_front(const std::vector& vector) 1195 | { 1196 | return insert_front_range_impl(vector.cbegin(), vector.cend()); 1197 | } 1198 | 1199 | // Makes a copy of the vector, inserts a range of values at the end of the copy, and returns the copy (non-mutating) 1200 | // 1201 | // example: 1202 | // const fcpp::vector numbers({ 4, 5, 6 }); 1203 | // auto augmented_numbers = numbers.inserting_back(std::vector({1, 2, 3})); 1204 | // 1205 | // outcome: 1206 | // augmented_numbers -> fcpp::vector numbers({ 4, 5, 6, 1, 2, 3 }); 1207 | [[nodiscard]] vector inserting_back(const std::vector& vector) const 1208 | { 1209 | return inserting_back_range_impl(vector.cbegin(), vector.cend()); 1210 | } 1211 | 1212 | // Makes a copy of the vector, inserts a range of values at the beginning of the copy, and returns the copy (non-mutating) 1213 | // 1214 | // example: 1215 | // const fcpp::vector numbers({ 4, 5, 6 }); 1216 | // auto augmented_numbers = numbers.inserting_front(std::vector({1, 2, 3})); 1217 | // 1218 | // outcome: 1219 | // augmented_numbers -> fcpp::vector numbers({ 1, 2, 3, 4, 5, 6 }); 1220 | [[nodiscard]] vector inserting_front(const std::vector& vector) const 1221 | { 1222 | return inserting_front_range_impl(vector.cbegin(), vector.cend()); 1223 | } 1224 | 1225 | // Inserts a range of values at the end of the vector in place (mutating) 1226 | // 1227 | // example: 1228 | // fcpp::vector numbers({ 4, 5, 6 }); 1229 | // numbers.insert_back(std::initializer_list({1, 2, 3})); 1230 | // 1231 | // outcome: 1232 | // numbers -> fcpp::vector numbers({ 4, 5, 6, 1, 2, 3 }); 1233 | vector& insert_back(const std::initializer_list& list) 1234 | { 1235 | return insert_back(std::vector(list)); 1236 | } 1237 | 1238 | // Inserts a range of values at the beginning of the vector in place (mutating) 1239 | // 1240 | // example: 1241 | // fcpp::vector numbers({ 4, 5, 6 }); 1242 | // numbers.insert_front(std::initializer_list({1, 2, 3})); 1243 | // 1244 | // outcome: 1245 | // numbers -> fcpp::vector numbers({ 1, 2, 3, 4, 5, 6 }); 1246 | vector& insert_front(const std::initializer_list& list) 1247 | { 1248 | return insert_front(std::vector(list)); 1249 | } 1250 | 1251 | // Makes a copy of the vector, inserts a range of values at the end of the copy, and returns the copy (non-mutating) 1252 | // 1253 | // example: 1254 | // const fcpp::vector numbers({ 4, 5, 6 }); 1255 | // auto augmented_numbers = numbers.inserting_back(std::initializer_list({1, 2, 3})); 1256 | // 1257 | // outcome: 1258 | // augmented_numbers -> fcpp::vector numbers({ 4, 5, 6, 1, 2, 3 }); 1259 | [[nodiscard]] vector inserting_back(const std::initializer_list& list) const 1260 | { 1261 | return inserting_back(std::vector(list)); 1262 | } 1263 | 1264 | // Makes a copy of the vector, inserts a range of values at the beginning of the copy, and returns the copy (non-mutating) 1265 | // 1266 | // example: 1267 | // const fcpp::vector numbers({ 4, 5, 6 }); 1268 | // auto augmented_numbers = numbers.inserting_front(std::initializer_list({1, 2, 3})); 1269 | // 1270 | // outcome: 1271 | // augmented_numbers -> fcpp::vector numbers({ 1, 2, 3, 4, 5, 6 }); 1272 | [[nodiscard]] vector inserting_front(const std::initializer_list& list) const 1273 | { 1274 | return inserting_front(std::vector(list)); 1275 | } 1276 | 1277 | // Replaces the existing contents starting at `index` with the contents of the given vector (mutating) 1278 | // 1279 | // example: 1280 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1281 | // numbers.replace_range_at(4, fcpp::vector({9, -10, 8})); 1282 | // 1283 | // outcome: 1284 | // numbers -> fcpp::vector({ 1, 4, 2, 5, 9, -10, 8, 7, 1 }) 1285 | vector& replace_range_at(size_t index, const vector& vector) 1286 | { 1287 | return replace_range_at_imp(index, vector.begin(), vector.end()); 1288 | } 1289 | 1290 | // Replaces the existing contents starting at `index` with the contents of the given vector (mutating) 1291 | // 1292 | // example: 1293 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1294 | // numbers.replace_range_at(4, std::vector({9, -10, 8})); 1295 | // 1296 | // outcome: 1297 | // numbers -> fcpp::vector({ 1, 4, 2, 5, 9, -10, 8, 7, 1 }) 1298 | vector& replace_range_at(size_t index, const std::vector& vector) 1299 | { 1300 | return replace_range_at_imp(index, vector.cbegin(), vector.cend()); 1301 | } 1302 | 1303 | // Replaces the existing contents starting at `index` with the contents of the given vector (mutating) 1304 | // 1305 | // example: 1306 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1307 | // numbers.replace_range_at(4, std::initializer_list({9, -10, 8})); 1308 | // 1309 | // outcome: 1310 | // numbers -> fcpp::vector({ 1, 4, 2, 5, 9, -10, 8, 7, 1 }) 1311 | vector& replace_range_at(size_t index, const std::initializer_list& list) 1312 | { 1313 | return replace_range_at(index, std::vector(list)); 1314 | } 1315 | 1316 | // Returns a copy whose elements starting at `index` are replaced with the contents of the given vector (non-mutating) 1317 | // 1318 | // example: 1319 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1320 | // auto replaced_numbers = numbers.replacing_range_at(4, fcpp::vector({9, -10, 8})); 1321 | // 1322 | // outcome: 1323 | // replaced_numbers -> fcpp::vector({ 1, 4, 2, 5, 9, -10, 8, 7, 1 }) 1324 | [[nodiscard]] vector replacing_range_at(size_t index, const vector& vector) const 1325 | { 1326 | return replacing_range_at_imp(index, vector.begin(), vector.end()); 1327 | } 1328 | 1329 | // Returns a copy whose elements starting at `index` are replaced with the contents of the given vector (non-mutating) 1330 | // 1331 | // example: 1332 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1333 | // auto replaced_numbers = numbers.replacing_range_at(4, std::vector({9, -10, 8})); 1334 | // 1335 | // outcome: 1336 | // replaced_numbers -> fcpp::vector({ 1, 4, 2, 5, 9, -10, 8, 7, 1 }) 1337 | [[nodiscard]] vector replacing_range_at(size_t index, const std::vector& vector) const 1338 | { 1339 | return replacing_range_at_imp(index, vector.cbegin(), vector.cend()); 1340 | } 1341 | 1342 | // Returns a copy whose elements starting at `index` are replaced with the contents of the given vector (non-mutating) 1343 | // 1344 | // example: 1345 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1346 | // auto replaced_numbers = numbers.replacing_range_at(4, std::initializer_list({9, -10, 8})); 1347 | // 1348 | // outcome: 1349 | // replaced_numbers -> fcpp::vector({ 1, 4, 2, 5, 9, -10, 8, 7, 1 }) 1350 | [[nodiscard]] vector replacing_range_at(size_t index, const std::initializer_list& list) const 1351 | { 1352 | return replacing_range_at(index, std::vector(list)); 1353 | } 1354 | 1355 | // Replaces all existing elements with a constant element (mutating) 1356 | // 1357 | // example: 1358 | // fcpp::vector numbers({1, 3, -6, 4, -9}); 1359 | // numbers.fill(7); 1360 | // 1361 | // outcome: 1362 | // numbers -> fcpp::vector({ 7, 7, 7, 7, 7 }) 1363 | vector& fill(const T& element) 1364 | { 1365 | std::fill(m_vector.begin(), 1366 | m_vector.end(), 1367 | element); 1368 | return *this; 1369 | } 1370 | 1371 | // Returns the size of the vector (how many elements it contains, it may be different from its capacity) 1372 | [[nodiscard]] size_t size() const 1373 | { 1374 | return m_vector.size(); 1375 | } 1376 | 1377 | // Clears the vector by removing all elements (mutating) 1378 | vector& clear() 1379 | { 1380 | m_vector.clear(); 1381 | return *this; 1382 | } 1383 | 1384 | // Returns true if the vector has no elements 1385 | [[nodiscard]] bool is_empty() const 1386 | { 1387 | return m_vector.empty(); 1388 | } 1389 | 1390 | // Returns the underlying capacity of the vector, which can be larger from its size 1391 | [[nodiscard]] size_t capacity() const 1392 | { 1393 | return m_vector.capacity(); 1394 | } 1395 | 1396 | // Reserves the necessary memory for `count` elements, so that subsequent changes in the 1397 | // vector's size due to addition/removal of elements is more performant 1398 | vector& reserve(size_t count) 1399 | { 1400 | m_vector.reserve(count); 1401 | return *this; 1402 | } 1403 | 1404 | // Resizes the vector to have given number of elements 1405 | // If `count` is larger than the current `size`, then `count-size` default elements are inserted at the back 1406 | // If `count` is smaller than the current `size`, then the last `size - count` elements are removed 1407 | // 1408 | // example: 1409 | // // numbers.capacity() = 9 1410 | // // numbers.size() = 9 1411 | // fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1412 | 1413 | // // numbers.capacity() = 9 1414 | // // numbers.size() = 5 1415 | // // numbers -> fcpp::vector({1, 4, 2, 5, 8}); 1416 | // numbers.resize(5); 1417 | // 1418 | // // empty_numbers.capacity() = 0 1419 | // // empty_numbers.size() = 0 1420 | // fcpp::vector empty_numbers; 1421 | // 1422 | // // empty_numbers.capacity() = 5 1423 | // // empty_numbers.size() = 5 1424 | // // empty_numbers -> fcpp::vector({0, 0, 0, 0, 0}); 1425 | // empty_numbers.resize(5); 1426 | vector& resize(size_t count) 1427 | { 1428 | m_vector.resize(count); 1429 | return *this; 1430 | } 1431 | 1432 | // Returns the begin iterator, useful for other standard library algorithms 1433 | [[nodiscard]] typename std::vector::iterator begin() 1434 | { 1435 | return m_vector.begin(); 1436 | } 1437 | 1438 | // Returns the const begin iterator, useful for other standard library algorithms 1439 | [[nodiscard]] typename std::vector::const_iterator begin() const 1440 | { 1441 | return m_vector.begin(); 1442 | } 1443 | 1444 | // Returns the end iterator, useful for other standard library algorithms 1445 | [[nodiscard]] typename std::vector::iterator end() 1446 | { 1447 | return m_vector.end(); 1448 | } 1449 | 1450 | // Returns the const end iterator, useful for other standard library algorithms 1451 | [[nodiscard]] typename std::vector::const_iterator end() const 1452 | { 1453 | return m_vector.end(); 1454 | } 1455 | 1456 | // Returns a set, whose elements are the elements of the vector, removing any potential duplicates 1457 | // 1458 | // example: 1459 | // const fcpp::vector numbers({1, 4, 2, 5, 8, 3, 1, 7, 1}); 1460 | // const auto& unique_numbers = numbers.distinct(); 1461 | // 1462 | // outcome: 1463 | // unique_numbers -> fcpp::set({1, 2, 3, 4, 5, 7, 8}) 1464 | template > 1465 | set distinct() const 1466 | { 1467 | return set(*this); 1468 | } 1469 | 1470 | // Returns a reference to the element in the given index, allowing subscripting and value editing. 1471 | // Bounds checking (assert) is enabled for debug builds. 1472 | T& operator[](size_t index) 1473 | { 1474 | assert_smaller_size(index); 1475 | return m_vector[index]; 1476 | } 1477 | 1478 | // Returns a constant reference to the element in the given index, allowing subscripting. 1479 | // Bounds checking (assert) is enabled for debug builds. 1480 | const T& operator[](size_t index) const 1481 | { 1482 | assert_smaller_size(index); 1483 | return m_vector[index]; 1484 | } 1485 | 1486 | // Returns true if both instances have equal sizes and the corresponding elements (same index) are equal 1487 | bool operator ==(const vector& rhs) const 1488 | { 1489 | #ifdef CPP17_AVAILABLE 1490 | return std::equal(begin(), 1491 | end(), 1492 | rhs.begin(), 1493 | rhs.end()); 1494 | #else 1495 | if (size() != rhs.size()) { 1496 | return false; 1497 | } 1498 | 1499 | for (auto i = 0; i < size(); ++i) { 1500 | if (!((*this)[i] == rhs[i])) { 1501 | return false; 1502 | } 1503 | } 1504 | 1505 | return true; 1506 | #endif 1507 | } 1508 | 1509 | // Returns false if either the sizes are not equal or at least one corresponding element (same index) is not equal 1510 | bool operator !=(const vector& rhs) const 1511 | { 1512 | return !((*this) == rhs); 1513 | } 1514 | 1515 | private: 1516 | std::vector m_vector; 1517 | // The iterator passed here may not necessarily be from std::vector as long as it's a valid iterable range 1518 | #ifdef CPP17_AVAILABLE 1519 | template ::value_type>>> 1521 | #else 1522 | template 1523 | #endif 1524 | vector& insert_back_range_impl(const Iterator& vec_begin, const Iterator& vec_end) 1525 | { 1526 | m_vector.insert(m_vector.end(), 1527 | vec_begin, 1528 | vec_end); 1529 | return *this; 1530 | } 1531 | 1532 | #ifdef CPP17_AVAILABLE 1533 | template ::value_type>>> 1535 | #else 1536 | template 1537 | #endif 1538 | vector& insert_front_range_impl(const Iterator& vec_begin, const Iterator& vec_end) 1539 | { 1540 | m_vector.insert(m_vector.begin(), 1541 | vec_begin, 1542 | vec_end); 1543 | return *this; 1544 | } 1545 | 1546 | #ifdef CPP17_AVAILABLE 1547 | template ::value_type>>> 1549 | #else 1550 | template 1551 | #endif 1552 | [[nodiscard]] vector inserting_back_range_impl(const Iterator& vec_begin, const Iterator& vec_end) const 1553 | { 1554 | auto augmented_vector(m_vector); 1555 | augmented_vector.reserve(augmented_vector.size() + std::distance(vec_begin, vec_end)); 1556 | augmented_vector.insert(augmented_vector.end(), 1557 | vec_begin, 1558 | vec_end); 1559 | return vector(augmented_vector); 1560 | } 1561 | 1562 | #ifdef CPP17_AVAILABLE 1563 | template ::value_type>>> 1565 | #else 1566 | template 1567 | #endif 1568 | [[nodiscard]] vector inserting_front_range_impl(const Iterator& vec_begin, const Iterator& vec_end) const 1569 | { 1570 | auto augmented_vector(m_vector); 1571 | augmented_vector.reserve(augmented_vector.size() + std::distance(vec_begin, vec_end)); 1572 | augmented_vector.insert(augmented_vector.begin(), 1573 | vec_begin, 1574 | vec_end); 1575 | return vector(augmented_vector); 1576 | } 1577 | 1578 | #ifdef CPP17_AVAILABLE 1579 | template ::value>> 1580 | [[nodiscard]] auto zip_impl(const Iterator& vec_begin, const Iterator& vec_end) const -> 1581 | vector>> 1582 | { 1583 | using U = deref_type; 1584 | #else 1585 | template 1586 | [[nodiscard]] vector> zip_impl(const typename std::vector::const_iterator& vec_begin, 1587 | const typename std::vector::const_iterator& vec_end) const 1588 | { 1589 | #endif 1590 | const auto vec_size = std::distance(vec_begin, vec_end); 1591 | assert(m_vector.size() == vec_size); 1592 | std::vector> combined_vector; 1593 | combined_vector.reserve(vec_size); 1594 | for (size_t i = 0; i < vec_size; ++i) { 1595 | combined_vector.push_back({m_vector[i], *(vec_begin + i)}); 1596 | } 1597 | return vector>(std::move(combined_vector)); 1598 | } 1599 | 1600 | #ifdef CPP17_AVAILABLE 1601 | // checks if the value of dereferencing the passed Iterators is convertible to type T 1602 | template ::value_type>>> 1604 | #else 1605 | template 1606 | #endif 1607 | vector& insert_at_impl(size_t index, 1608 | const Iterator& vec_begin, 1609 | const Iterator& vec_end) 1610 | { 1611 | if (vec_begin != vec_end) { 1612 | assert_smaller_or_equal_size(index); 1613 | m_vector.insert(begin() + index, 1614 | vec_begin, 1615 | vec_end); 1616 | } 1617 | return *this; 1618 | } 1619 | 1620 | #ifdef CPP17_AVAILABLE 1621 | template ::value_type>>> 1623 | #else 1624 | template 1625 | #endif 1626 | [[nodiscard]] vector inserting_at_impl(size_t index, 1627 | const Iterator& vec_begin, 1628 | const Iterator& vec_end) const 1629 | { 1630 | if (vec_begin == vec_end) { 1631 | return *this; 1632 | } 1633 | assert_smaller_or_equal_size(index); 1634 | auto augmented_vector(m_vector); 1635 | augmented_vector.insert(augmented_vector.begin() + index, 1636 | vec_begin, 1637 | vec_end); 1638 | return vector(std::move(augmented_vector)); 1639 | } 1640 | 1641 | #ifdef CPP17_AVAILABLE 1642 | template ::value_type>>> 1644 | #else 1645 | template 1646 | #endif 1647 | vector& replace_range_at_imp(size_t index, 1648 | const Iterator& vec_begin, 1649 | const Iterator& vec_end) 1650 | { 1651 | const auto vec_size = std::distance(vec_begin, vec_end); 1652 | assert(index + vec_size >= vec_size && index + vec_size <= size()); 1653 | std::copy(vec_begin, 1654 | vec_end, 1655 | m_vector.begin() + index); 1656 | return *this; 1657 | } 1658 | 1659 | #ifdef CPP17_AVAILABLE 1660 | template ::value_type>>> 1662 | #else 1663 | template 1664 | #endif 1665 | [[nodiscard]] vector replacing_range_at_imp(size_t index, 1666 | const Iterator& vec_begin, 1667 | const Iterator& vec_end) const 1668 | { 1669 | const auto vec_size = std::distance(vec_begin, vec_end); 1670 | assert(index + vec_size >= vec_size && index + vec_size <= size()); 1671 | auto replaced_vector(m_vector); 1672 | std::copy(vec_begin, 1673 | vec_end, 1674 | replaced_vector.begin() + index); 1675 | return vector(replaced_vector); 1676 | } 1677 | 1678 | void assert_smaller_size(size_t index) const 1679 | { 1680 | assert(index < size()); 1681 | } 1682 | 1683 | void assert_smaller_or_equal_size(size_t index) const 1684 | { 1685 | assert(index <= size()); 1686 | } 1687 | }; 1688 | } 1689 | --------------------------------------------------------------------------------