diff --git a/source/RobotAPI/libraries/armem/CMakeLists.txt b/source/RobotAPI/libraries/armem/CMakeLists.txt index 8183a5501bf0a72142d92258864f83b955325be8..cff3ea8a1220edb2eaa9169e74068bfd297ecc56 100644 --- a/source/RobotAPI/libraries/armem/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem/CMakeLists.txt @@ -86,7 +86,7 @@ set(LIB_HEADERS core/MemoryID.h core/MemoryID_operators.h core/operations.h - core/prefixes.h + core/container_maps.h core/SuccessHeader.h core/Time.h core/aron_conversions.h diff --git a/source/RobotAPI/libraries/armem/core/container_maps.h b/source/RobotAPI/libraries/armem/core/container_maps.h new file mode 100644 index 0000000000000000000000000000000000000000..9c5b26f524100af46f81c197c1fd567732f7e1b9 --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/container_maps.h @@ -0,0 +1,209 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author phesch ( ulila at student dot kit dot edu ) + * @date 2022 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <map> +#include <optional> +#include <tuple> + +#include <RobotAPI/libraries/armem/core/MemoryID.h> + +namespace armarx::armem +{ + + namespace detail + { + + /** + * @brief Get the key-value pair from the map for which the returned key + * is the longest prefix of the given key among the keys in the map. + * + * `prefixFunc` is used to successively calculate the prefixes of the given key. + * It must be pure and return an empty optional when there is no shorter + * prefix of the given key (for strings, this would be the case when passed the empty string). + * + * @param keyValMap the map that contains the key-value-pairs to search + * @param prefixFunc the function that returns the longest non-identical prefix of the key + * @param key the key to calculate the prefixes of + */ + template <typename KeyT, typename ValueT> + std::optional<std::pair<KeyT, ValueT>> + getWithLongestPrefix(const std::map<KeyT, ValueT>& keyValMap, + const std::function<std::optional<KeyT>(KeyT&)>& prefixFunc, + const KeyT& key) + { + std::optional<KeyT> curKey = key; + std::optional<ValueT> value; + do + { + auto iterator = keyValMap.find(curKey.value()); + if (iterator != keyValMap.end()) + { + value = iterator->second; + } + else + { + curKey = prefixFunc(curKey.value()); + } + } while (!value.has_value() && curKey.has_value()); + + if (value.has_value()) + { + return {{curKey.value(), value.value()}}; + } + return {}; + } + + /** + * @brief Accumulate all the values in a map for which the keys are prefixes of the given key. + * + * `AccumulateT` is a type that the values will be accumulated into using `accumulateFunc`. + * `accumulateFunc` is a function that modifies the given accumulator + * (by, e.g., adding the given value to it). + * + * The values are accumulated in order from the longest key to the shortest. + * + * @see `getWithLongestPrefix` for a description of `prefixFunc` + * @param keyValMap the map that contains the key-value-pairs to search + * @param prefixFunc the function that returns the longest non-identical prefix of the key + * @param accumulateFunc the function that accumulates the values in the accumulator + * @param key the key to calculate the prefixes of + */ + template <typename KeyT, typename ValueT, typename AccumulateT> + AccumulateT + accumulateFromPrefixes(const std::map<KeyT, ValueT>& keyValMap, + const std::function<std::optional<KeyT>(KeyT&)>& prefixFunc, + const std::function<void(AccumulateT&, ValueT&)> accumulateFunc, + const KeyT& key) + { + std::optional<KeyT> curKey = key; + AccumulateT values; + do + { + std::optional<std::pair<KeyT, ValueT>> nextPair = + getWithLongestPrefix<KeyT, ValueT>(keyValMap, prefixFunc, curKey.value()); + if (nextPair.has_value()) + { + curKey = prefixFunc(nextPair.value().first); + accumulateFunc(values, nextPair.value().second); + } + else + { + curKey.reset(); + } + } while (curKey.has_value()); + + return values; + } + + /** + * @brief Collect all the values in a map for which the keys are prefixes of the given key. + * + * This is a specialization of the general `accumulateFromPrefixes` + * for collecting single values into a vector. + * + * @see `accumulateFromPrefixes` for a detailed description + * @param keyValMap the map that contains the key-value-pairs to search + * @param prefixFunc the function that returns the longest non-identical prefix of the key + * @param key the key to calculate the prefixes of + */ + template <typename KeyT, typename ValueT> + std::vector<ValueT> + accumulateFromPrefixes(const std::map<KeyT, ValueT>& keyValMap, + const std::function<std::optional<KeyT>(KeyT&)>& prefixFunc, + const KeyT& key) + { + return accumulateFromPrefixes<KeyT, ValueT, std::vector<ValueT>>( + keyValMap, + prefixFunc, + [](std::vector<ValueT>& values, ValueT& val) { values.push_back(val); }, + key); + } + + /** + * @brief Collect all the values in a map for which the keys are prefixes of the given key. + * + * This is a specialization of the general `accumulateFromPrefixes` + * for appending vector values into a single vector. + * + * @see `accumulateFromPrefixes` for a detailed description + * @param keyValMap the map that contains the key-value-pairs to search + * @param prefixFunc the function that returns the longest non-identical prefix of the key + * @param key the key to calculate the prefixes of + */ + template <typename KeyT, typename ValueT> + std::vector<ValueT> + accumulateFromPrefixes(const std::map<KeyT, std::vector<ValueT>>& keyValMap, + const std::function<std::optional<KeyT>(KeyT&)>& prefixFunc, + const KeyT& key) + { + return accumulateFromPrefixes<KeyT, std::vector<ValueT>, std::vector<ValueT>>( + keyValMap, + prefixFunc, + [](std::vector<ValueT>& values, std::vector<ValueT>& val) + { values.insert(values.end(), val.begin(), val.end()); }, + key); + } + + } // namespace detail + + std::optional<MemoryID> inline getMemoryIDParent(MemoryID& memID) + { + if (!memID.hasMemoryName()) + { + return {}; + } + MemoryID parent = memID.removeLeafItem(); + return {parent}; + } + + /** + * @see `getWithLongestPrefix` + */ + template <typename ValueT> + std::optional<std::pair<MemoryID, ValueT>> + getWithMostSpecificContainer(const std::map<MemoryID, ValueT>& idMap, const MemoryID& key) + { + return detail::getWithLongestPrefix<MemoryID, ValueT>(idMap, &getMemoryIDParent, key); + } + + /** + * @see `accumulateFromPrefixes` + */ + template <typename ValueT> + std::vector<ValueT> + accumulateFromContainers(const std::map<MemoryID, ValueT>& idMap, const MemoryID& key) + { + return detail::accumulateFromPrefixes<MemoryID, ValueT>(idMap, &getMemoryIDParent, key); + } + + /** + * @see `accumulateFromPrefixes` + */ + template <typename ValueT> + std::vector<ValueT> + accumulateFromContainers(const std::map<MemoryID, std::vector<ValueT>>& idMap, + const MemoryID& key) + { + return detail::accumulateFromPrefixes<MemoryID, ValueT>(idMap, &getMemoryIDParent, key); + } +} // namespace armarx::armem diff --git a/source/RobotAPI/libraries/armem/core/prefixes.h b/source/RobotAPI/libraries/armem/core/prefixes.h deleted file mode 100644 index 0435c42de3d853837270ff494e5f405e803cd013..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/prefixes.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @author phesch ( ulila at student dot kit dot edu ) - * @date 2022 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#pragma once - -#include <map> -#include <optional> -#include <tuple> - -#include <RobotAPI/libraries/armem/core/MemoryID.h> - -namespace armarx::armem -{ - /** - * @brief Get the key-value pair from the map for which the returned key - * is the longest prefix of the given key among the keys in the map. - * - * `prefixFunc` is used to successively calculate the prefixes of the given key. - * It must be pure and return an empty optional when there is no shorter - * prefix of the given key (for strings, this would be the case when passed the empty string). - * - * @param keyValMap the map that contains the key-value-pairs to search - * @param prefixFunc the function that returns the longest non-identical prefix of the key - * @param key the key to calculate the prefixes of - */ - template <typename KeyT, typename ValueT> - std::optional<std::pair<KeyT, ValueT>> - getWithLongestPrefix(const std::map<KeyT, ValueT>& keyValMap, - const std::function<std::optional<KeyT>(KeyT&)>& prefixFunc, - const KeyT& key) - { - std::optional<KeyT> curKey = key; - std::optional<ValueT> value; - do - { - auto iterator = keyValMap.find(curKey.value()); - if (iterator != keyValMap.end()) - { - value = iterator->second; - } - else - { - curKey = prefixFunc(curKey.value()); - } - } while (!value.has_value() && curKey.has_value()); - - if (value.has_value()) - { - return {{curKey.value(), value.value()}}; - } - return {}; - } - - /** - * @brief Accumulate all the values in a map for which the keys are prefixes of the given key. - * - * `AccumulateT` is a type that the values will be accumulated into using `accumulateFunc`. - * `accumulateFunc` is a function that modifies the given accumulator - * (by, e.g., adding the given value to it). - * - * The values are accumulated in order from the longest key to the shortest. - * - * @see `getWithLongestPrefix` for a description of `prefixFunc` - * @param keyValMap the map that contains the key-value-pairs to search - * @param prefixFunc the function that returns the longest non-identical prefix of the key - * @param accumulateFunc the function that accumulates the values in the accumulator - * @param key the key to calculate the prefixes of - */ - template <typename KeyT, typename ValueT, typename AccumulateT> - AccumulateT - accumulateFromPrefixes(const std::map<KeyT, ValueT>& keyValMap, - const std::function<std::optional<KeyT>(KeyT&)>& prefixFunc, - const std::function<void(AccumulateT&, ValueT&)> accumulateFunc, - const KeyT& key) - { - std::optional<KeyT> curKey = key; - AccumulateT values; - do - { - std::optional<std::pair<KeyT, ValueT>> nextPair = - getWithLongestPrefix<KeyT, ValueT>(keyValMap, prefixFunc, curKey.value()); - if (nextPair.has_value()) - { - curKey = prefixFunc(nextPair.value().first); - accumulateFunc(values, nextPair.value().second); - } - else - { - curKey.reset(); - } - } while (curKey.has_value()); - - return values; - } - - /** - * @brief Collect all the values in a map for which the keys are prefixes of the given key. - * - * This is a specialization of the general `accumulateFromPrefixes` - * for collecting single values into a vector. - * - * @see `accumulateFromPrefixes` for a detailed description - * @param keyValMap the map that contains the key-value-pairs to search - * @param prefixFunc the function that returns the longest non-identical prefix of the key - * @param key the key to calculate the prefixes of - */ - template <typename KeyT, typename ValueT> - std::vector<ValueT> - accumulateFromPrefixes(const std::map<KeyT, ValueT>& keyValMap, - const std::function<std::optional<KeyT>(KeyT&)>& prefixFunc, - const KeyT& key) - { - return accumulateFromPrefixes<KeyT, ValueT, std::vector<ValueT>>( - keyValMap, - prefixFunc, - [](std::vector<ValueT>& values, ValueT& val) { values.push_back(val); }, - key); - } - - /** - * @brief Collect all the values in a map for which the keys are prefixes of the given key. - * - * This is a specialization of the general `accumulateFromPrefixes` - * for appending vector values into a single vector. - * - * @see `accumulateFromPrefixes` for a detailed description - * @param keyValMap the map that contains the key-value-pairs to search - * @param prefixFunc the function that returns the longest non-identical prefix of the key - * @param key the key to calculate the prefixes of - */ - template <typename KeyT, typename ValueT> - std::vector<ValueT> - accumulateFromPrefixes(const std::map<KeyT, std::vector<ValueT>>& keyValMap, - const std::function<std::optional<KeyT>(KeyT&)>& prefixFunc, - const KeyT& key) - { - return accumulateFromPrefixes<KeyT, std::vector<ValueT>, std::vector<ValueT>>( - keyValMap, - prefixFunc, - [](std::vector<ValueT>& values, std::vector<ValueT>& val) - { values.insert(values.end(), val.begin(), val.end()); }, - key); - } - - std::optional<MemoryID> inline getMemoryIDParent(MemoryID& memID) - { - if (!memID.hasMemoryName()) - { - return {}; - } - MemoryID parent = memID.removeLeafItem(); - return {parent}; - } - - /** - * @see `getWithLongestPrefix` - */ - template <typename ValueT> - std::optional<std::pair<MemoryID, ValueT>> - getWithLongestPrefix(const std::map<MemoryID, ValueT>& idMap, const MemoryID& key) - { - return getWithLongestPrefix<MemoryID, ValueT>(idMap, &getMemoryIDParent, key); - } - - /** - * @see `accumulateFromPrefixes` - */ - template <typename ValueT> - std::vector<ValueT> - accumulateFromPrefixes(const std::map<MemoryID, ValueT>& idMap, const MemoryID& key) - { - return accumulateFromPrefixes<MemoryID, ValueT>(idMap, &getMemoryIDParent, key); - } - - /** - * @see `accumulateFromPrefixes` - */ - template <typename ValueT> - std::vector<ValueT> - accumulateFromPrefixes(const std::map<MemoryID, std::vector<ValueT>>& idMap, - const MemoryID& key) - { - return accumulateFromPrefixes<MemoryID, ValueT>(idMap, &getMemoryIDParent, key); - } -} // namespace armarx::armem diff --git a/source/RobotAPI/libraries/armem/test/ArMemPrefixesTest.cpp b/source/RobotAPI/libraries/armem/test/ArMemPrefixesTest.cpp index 877b83daa17ea4c8dc47d98d6cdeb0f66965a50a..eb75fb8406ebf179e551978067f99c5832342e45 100644 --- a/source/RobotAPI/libraries/armem/test/ArMemPrefixesTest.cpp +++ b/source/RobotAPI/libraries/armem/test/ArMemPrefixesTest.cpp @@ -28,7 +28,7 @@ #include <RobotAPI/Test.h> #include <RobotAPI/libraries/armem/core/error.h> -#include <RobotAPI/libraries/armem/core/prefixes.h> +#include <RobotAPI/libraries/armem/core/container_maps.h> namespace armem = armarx::armem; @@ -42,14 +42,14 @@ BOOST_AUTO_TEST_CASE(test_MemoryID_prefixes) BOOST_TEST_CONTEXT(VAROUT(idMap)) { - BOOST_CHECK(not armem::getWithLongestPrefix(idMap, empty).has_value()); - BOOST_CHECK(not armem::getWithLongestPrefix(idMap, complete).has_value()); - BOOST_CHECK(armem::accumulateFromPrefixes(idMap, empty).empty()); - BOOST_CHECK(armem::accumulateFromPrefixes(idMap, complete).empty()); + BOOST_CHECK(not armem::getWithMostSpecificContainer(idMap, empty).has_value()); + BOOST_CHECK(not armem::getWithMostSpecificContainer(idMap, complete).has_value()); + BOOST_CHECK(armem::accumulateFromContainers(idMap, empty).empty()); + BOOST_CHECK(armem::accumulateFromContainers(idMap, complete).empty()); - BOOST_CHECK(armem::accumulateFromPrefixes(idListMap, empty).empty()); - BOOST_CHECK(armem::accumulateFromPrefixes(idListMap, complete).empty()); + BOOST_CHECK(armem::accumulateFromContainers(idListMap, empty).empty()); + BOOST_CHECK(armem::accumulateFromContainers(idListMap, complete).empty()); } idMap[armem::MemoryID()] = 0; @@ -60,21 +60,21 @@ BOOST_AUTO_TEST_CASE(test_MemoryID_prefixes) BOOST_TEST_CONTEXT(VAROUT(idMap)) { - BOOST_CHECK(armem::getWithLongestPrefix(idMap, empty) + BOOST_CHECK(armem::getWithMostSpecificContainer(idMap, empty) .value_or(std::make_pair(armem::MemoryID("inv"), -1)) .second == 0); - BOOST_CHECK(armem::getWithLongestPrefix(idMap, complete.getCoreSegmentID()) + BOOST_CHECK(armem::getWithMostSpecificContainer(idMap, complete.getCoreSegmentID()) .value_or(std::make_pair(armem::MemoryID("inv"), -1)) .second == 2); - BOOST_CHECK(armem::getWithLongestPrefix(idMap, complete) + BOOST_CHECK(armem::getWithMostSpecificContainer(idMap, complete) .value_or(std::make_pair(armem::MemoryID("inv"), -1)) .second == 3); - BOOST_CHECK((armem::accumulateFromPrefixes(idMap, empty) == std::vector<int>{0})); - BOOST_CHECK((armem::accumulateFromPrefixes(idMap, complete.getCoreSegmentID()) == + BOOST_CHECK((armem::accumulateFromContainers(idMap, empty) == std::vector<int>{0})); + BOOST_CHECK((armem::accumulateFromContainers(idMap, complete.getCoreSegmentID()) == std::vector<int>{2, 1, 0})); BOOST_CHECK( - (armem::accumulateFromPrefixes(idMap, complete) == std::vector<int>{3, 2, 1, 0})); + (armem::accumulateFromContainers(idMap, complete) == std::vector<int>{3, 2, 1, 0})); } idListMap.emplace("mem", std::vector<int>{1, 2}); @@ -84,10 +84,10 @@ BOOST_AUTO_TEST_CASE(test_MemoryID_prefixes) BOOST_TEST_CONTEXT(VAROUT(idListMap)) { - BOOST_CHECK((armem::accumulateFromPrefixes(idListMap, empty).empty())); - BOOST_CHECK((armem::accumulateFromPrefixes(idListMap, complete.getCoreSegmentID()) == + BOOST_CHECK((armem::accumulateFromContainers(idListMap, empty).empty())); + BOOST_CHECK((armem::accumulateFromContainers(idListMap, complete.getCoreSegmentID()) == std::vector<int>{3, 4, 5, 1, 2})); - BOOST_CHECK((armem::accumulateFromPrefixes(idListMap, complete) == + BOOST_CHECK((armem::accumulateFromContainers(idListMap, complete) == std::vector<int>{6, 7, 8, 3, 4, 5, 1, 2})); } }