diff --git a/docs/src/algorithm.rst b/docs/src/algorithm.rst new file mode 100644 index 0000000..9b11857 --- /dev/null +++ b/docs/src/algorithm.rst @@ -0,0 +1,5 @@ +algorithm +============= + +.. doxygenfile:: algorithm.hpp + diff --git a/docs/src/index.rst b/docs/src/index.rst index 6aa449b..af5e99a 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -40,6 +40,7 @@ AARE :caption: C++ API :maxdepth: 1 + algorithm NDArray NDView Frame diff --git a/include/aare/algorithm.hpp b/include/aare/algorithm.hpp index 88b8a4b..fc7d51f 100644 --- a/include/aare/algorithm.hpp +++ b/include/aare/algorithm.hpp @@ -7,13 +7,20 @@ namespace aare { /** - * @brief Find the index of the last element smaller than val - * assume a sorted array + * @brief Index of the last element that is smaller than val. + * Requires a sorted array. Uses >= for ordering. If all elements + * are smaller it returns the last element and if all elements are + * larger it returns the first element. + * @param first iterator to the first element + * @param last iterator to the last element + * @param val value to compare + * @return index of the last element that is smaller than val + * */ template size_t last_smaller(const T* first, const T* last, T val) { for (auto iter = first+1; iter != last; ++iter) { - if (*iter > val) { + if (*iter >= val) { return std::distance(first, iter-1); } } @@ -25,6 +32,21 @@ size_t last_smaller(const NDArray& arr, T val) { return last_smaller(arr.begin(), arr.end(), val); } +template +size_t last_smaller(const std::vector& vec, T val) { + return last_smaller(vec.data(), vec.data()+vec.size(), val); +} + +/** + * @brief Index of the first element that is larger than val. + * Requires a sorted array. Uses > for ordering. If all elements + * are larger it returns the first element and if all elements are + * smaller it returns the last element. + * @param first iterator to the first element + * @param last iterator to the last element + * @param val value to compare + * @return index of the first element that is larger than val + */ template size_t first_larger(const T* first, const T* last, T val) { for (auto iter = first; iter != last; ++iter) { @@ -45,6 +67,14 @@ size_t first_larger(const std::vector& vec, T val) { return first_larger(vec.data(), vec.data()+vec.size(), val); } +/** + * @brief Index of the nearest element to val. + * Requires a sorted array. If there is no difference it takes the first element. + * @param first iterator to the first element + * @param last iterator to the last element + * @param val value to compare + * @return index of the nearest element + */ template size_t nearest_index(const T* first, const T* last, T val) { auto iter = std::min_element(first, last, diff --git a/src/algorithm.test.cpp b/src/algorithm.test.cpp index 5929131..e2ae8fa 100644 --- a/src/algorithm.test.cpp +++ b/src/algorithm.test.cpp @@ -49,6 +49,16 @@ TEST_CASE("nearest index works with std::array", "[algorithm]"){ REQUIRE(aare::nearest_index(arr, -10.0) == 0); } +TEST_CASE("nearest index when there is no different uses the first element", "[algorithm]"){ + std::vector vec = {5, 5, 5, 5, 5}; + REQUIRE(aare::nearest_index(vec, 5) == 0); +} + +TEST_CASE("nearest index when there is no different uses the first element also when all smaller", "[algorithm]"){ + std::vector vec = {5, 5, 5, 5, 5}; + REQUIRE(aare::nearest_index(vec, 10) == 0); +} + TEST_CASE("last smaller", "[algorithm]"){ aare::NDArray arr({5}); @@ -68,10 +78,59 @@ TEST_CASE("returns last bin strictly smaller", "[algorithm]"){ arr[i] = i; } // arr 0, 1, 2, 3, 4 - REQUIRE(aare::last_smaller(arr, 2.0) == 2); + REQUIRE(aare::last_smaller(arr, 2.0) == 1); } +TEST_CASE("last_smaller with all elements smaller returns last element", "[algorithm]"){ + aare::NDArray arr({5}); + for (size_t i = 0; i < arr.size(); i++) { + arr[i] = i; + } + // arr 0, 1, 2, 3, 4 + REQUIRE(aare::last_smaller(arr, 50.) == 4); +} + +TEST_CASE("last_smaller with all elements bigger returns first element", "[algorithm]"){ + aare::NDArray arr({5}); + for (size_t i = 0; i < arr.size(); i++) { + arr[i] = i; + } + // arr 0, 1, 2, 3, 4 + REQUIRE(aare::last_smaller(arr, -50.) == 0); +} + +TEST_CASE("last smaller with all elements equal returns the first element", "[algorithm]"){ + std::vector vec = {5,5,5,5,5,5,5}; + REQUIRE(aare::last_smaller(vec, 5) == 0); +} + + +TEST_CASE("first_lager with vector", "[algorithm]"){ + std::vector vec = {0, 1, 2, 3, 4}; + REQUIRE(aare::first_larger(vec, 2.5) == 3); +} + +TEST_CASE("first_lager with all elements smaller returns last element", "[algorithm]"){ + std::vector vec = {0, 1, 2, 3, 4}; + REQUIRE(aare::first_larger(vec, 50.) == 4); +} + +TEST_CASE("first_lager with all elements bigger returns first element", "[algorithm]"){ + std::vector vec = {0, 1, 2, 3, 4}; + REQUIRE(aare::first_larger(vec, -50.) == 0); +} + +TEST_CASE("first_lager with all elements the same as the check returns last", "[algorithm]"){ + std::vector vec = {14, 14, 14, 14, 14}; + REQUIRE(aare::first_larger(vec, 14) == 4); +} + +TEST_CASE("first larger with the same element", "[algorithm]"){ + std::vector vec = {7,8,9,10,11}; + REQUIRE(aare::first_larger(vec, 9) == 3); +} + TEST_CASE("cumsum works", "[algorithm]"){ std::vector vec = {0, 1, 2, 3, 4}; auto result = aare::cumsum(vec); @@ -96,4 +155,5 @@ TEST_CASE("cumsum works with negative numbers", "[algorithm]"){ REQUIRE(result[2] == -3); REQUIRE(result[3] == -6); REQUIRE(result[4] == -10); -} \ No newline at end of file +} +