Skip to content
Snippets Groups Projects
Commit 7959d540 authored by Philip Scherer's avatar Philip Scherer
Browse files

Add prefix methods for maps

parent 60d248e5
No related branches found
No related tags found
1 merge request!244Improve armem prediction interface
......@@ -86,6 +86,7 @@ set(LIB_HEADERS
core/MemoryID.h
core/MemoryID_operators.h
core/operations.h
core/prefixes.h
core/SuccessHeader.h
core/Time.h
core/aron_conversions.h
......
/*
* 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
/*
* 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/>.
*
* @package RobotAPI::armem
* @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
*/
#define BOOST_TEST_MODULE RobotAPI::armem
#define ARMARX_BOOST_TEST
#include <iostream>
#include <RobotAPI/Test.h>
#include <RobotAPI/libraries/armem/core/error.h>
#include <RobotAPI/libraries/armem/core/prefixes.h>
namespace armem = armarx::armem;
BOOST_AUTO_TEST_CASE(test_MemoryID_prefixes)
{
std::map<armem::MemoryID, int> idMap;
std::map<armem::MemoryID, std::vector<int>> idListMap;
armem::MemoryID empty;
armem::MemoryID complete("mem", "coreSeg", "provSeg", "entity", armarx::DateTime::Now(), 0);
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(armem::accumulateFromPrefixes(idListMap, empty).empty());
BOOST_CHECK(armem::accumulateFromPrefixes(idListMap, complete).empty());
}
idMap[armem::MemoryID()] = 0;
idMap.emplace("mem", 1);
idMap.emplace("mem/coreSeg", 2);
idMap.emplace("mem/coreSeg/provSeg", 3);
idMap.emplace("mem/otherSeg/provSeg", 10);
BOOST_TEST_CONTEXT(VAROUT(idMap))
{
BOOST_CHECK(armem::getWithLongestPrefix(idMap, empty)
.value_or(std::make_pair(armem::MemoryID("inv"), -1))
.second == 0);
BOOST_CHECK(armem::getWithLongestPrefix(idMap, complete.getCoreSegmentID())
.value_or(std::make_pair(armem::MemoryID("inv"), -1))
.second == 2);
BOOST_CHECK(armem::getWithLongestPrefix(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()) ==
std::vector<int>{2, 1, 0}));
BOOST_CHECK(
(armem::accumulateFromPrefixes(idMap, complete) == std::vector<int>{3, 2, 1, 0}));
}
idListMap.emplace("mem", std::vector<int>{1, 2});
idListMap.emplace("mem/coreSeg", std::vector<int>{3, 4, 5});
idListMap.emplace("mem/coreSeg/provSeg", std::vector<int>{6, 7, 8});
idListMap.emplace("mem/otherSeg/provSeg", std::vector<int>{9, 10, 11});
BOOST_TEST_CONTEXT(VAROUT(idListMap))
{
BOOST_CHECK((armem::accumulateFromPrefixes(idListMap, empty).empty()));
BOOST_CHECK((armem::accumulateFromPrefixes(idListMap, complete.getCoreSegmentID()) ==
std::vector<int>{3, 4, 5, 1, 2}));
BOOST_CHECK((armem::accumulateFromPrefixes(idListMap, complete) ==
std::vector<int>{6, 7, 8, 3, 4, 5, 1, 2}));
}
}
......@@ -7,3 +7,4 @@ armarx_add_test(ArMemGetFindTest ArMemGetFindTest.cpp "${LIBS}")
armarx_add_test(ArMemIceConversionsTest ArMemIceConversionsTest.cpp "${LIBS}")
armarx_add_test(ArMemMemoryIDTest ArMemMemoryIDTest.cpp "${LIBS}")
armarx_add_test(ArMemQueryBuilderTest ArMemQueryBuilderTest.cpp "${LIBS}")
armarx_add_test(ArMemPrefixesTest ArMemPrefixesTest.cpp "${LIBS}")
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment