osb/source/core/StarBiMap.hpp
2023-06-27 20:23:44 +10:00

420 lines
14 KiB
C++

#ifndef STAR_BI_MAP_HPP
#define STAR_BI_MAP_HPP
#include "StarString.hpp"
namespace Star {
// Bi-directional map of unique sets of elements with quick map access from
// either the left or right element to the other side. Every left side value
// must be unique from every other left side value and the same for the right
// side.
template <typename LeftT,
typename RightT,
typename LeftMapT = Map<LeftT, RightT const*>,
typename RightMapT = Map<RightT, LeftT const*>>
class BiMap {
public:
typedef LeftT Left;
typedef RightT Right;
typedef LeftMapT LeftMap;
typedef RightMapT RightMap;
typedef pair<Left, Right> value_type;
struct BiMapIterator {
BiMapIterator& operator++();
BiMapIterator operator++(int);
bool operator==(BiMapIterator const& rhs) const;
bool operator!=(BiMapIterator const& rhs) const;
pair<Left const&, Right const&> operator*() const;
typename LeftMap::const_iterator iterator;
};
typedef BiMapIterator iterator;
typedef iterator const_iterator;
template <typename Collection>
static BiMap from(Collection const& c);
BiMap();
BiMap(BiMap const& map);
template <typename InputIterator>
BiMap(InputIterator beg, InputIterator end);
BiMap(std::initializer_list<value_type> list);
List<Left> leftValues() const;
List<Right> rightValues() const;
List<value_type> pairs() const;
bool hasLeftValue(Left const& left) const;
bool hasRightValue(Right const& right) const;
Right const& getRight(Left const& left) const;
Left const& getLeft(Right const& right) const;
Right valueRight(Left const& left, Right const& def = Right()) const;
Left valueLeft(Right const& right, Left const& def = Left()) const;
Maybe<Right> maybeRight(Left const& left) const;
Maybe<Left> maybeLeft(Right const& right) const;
Right takeRight(Left const& left);
Left takeLeft(Right const& right);
Maybe<Right> maybeTakeRight(Left const& left);
Maybe<Left> maybeTakeLeft(Right const& right);
Right const* rightPtr(Left const& left) const;
Left const* leftPtr(Right const& right) const;
BiMap& operator=(BiMap const& map);
pair<iterator, bool> insert(value_type const& val);
// Returns true if value was inserted, false if either the left or right side
// already existed.
bool insert(Left const& left, Right const& right);
// Throws an exception if the pair cannot be inserted
void add(Left const& left, Right const& right);
void add(value_type const& value);
// Overwrites the left / right mapping regardless of whether each side
// already exists.
void overwrite(Left const& left, Right const& right);
void overwrite(value_type const& value);
// Removes the pair with the given left side, returns true if this pair was
// found, false otherwise.
bool removeLeft(Left const& left);
// Removes the pair with the given right side, returns true if this pair was
// found, false otherwise.
bool removeRight(Right const& right);
const_iterator begin() const;
const_iterator end() const;
size_t size() const;
void clear();
bool empty() const;
bool operator==(BiMap const& m) const;
private:
LeftMap m_leftMap;
RightMap m_rightMap;
};
template <typename Left, typename Right, typename LeftHash = Star::hash<Left>, typename RightHash = Star::hash<Right>>
using BiHashMap = BiMap<Left, Right, StableHashMap<Left, Right const*, LeftHash>, StableHashMap<Right, Left const*, RightHash>>;
// Case insensitive Enum <-> String map
template <typename EnumType>
using EnumMap = BiMap<EnumType,
String,
Map<EnumType, String const*>,
StableHashMap<String, EnumType const*, CaseInsensitiveStringHash, CaseInsensitiveStringCompare>>;
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
auto BiMap<LeftT, RightT, LeftMapT, RightMapT>::BiMapIterator::operator++() -> BiMapIterator & {
++iterator;
return *this;
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
auto BiMap<LeftT, RightT, LeftMapT, RightMapT>::BiMapIterator::operator++(int) -> BiMapIterator {
BiMapIterator last{iterator};
++iterator;
return last;
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
bool BiMap<LeftT, RightT, LeftMapT, RightMapT>::BiMapIterator::operator==(BiMapIterator const& rhs) const {
return iterator == rhs.iterator;
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
bool BiMap<LeftT, RightT, LeftMapT, RightMapT>::BiMapIterator::operator!=(BiMapIterator const& rhs) const {
return iterator != rhs.iterator;
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
pair<LeftT const&, RightT const&> BiMap<LeftT, RightT, LeftMapT, RightMapT>::BiMapIterator::operator*() const {
return {iterator->first, *iterator->second};
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
template <typename Collection>
BiMap<LeftT, RightT, LeftMapT, RightMapT> BiMap<LeftT, RightT, LeftMapT, RightMapT>::from(Collection const& c) {
return BiMap(c.begin(), c.end());
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
BiMap<LeftT, RightT, LeftMapT, RightMapT>::BiMap() {}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
BiMap<LeftT, RightT, LeftMapT, RightMapT>::BiMap(BiMap const& map)
: BiMap(map.begin(), map.end()) {}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
template <typename InputIterator>
BiMap<LeftT, RightT, LeftMapT, RightMapT>::BiMap(InputIterator beg, InputIterator end) {
while (beg != end) {
insert(*beg);
++beg;
}
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
BiMap<LeftT, RightT, LeftMapT, RightMapT>::BiMap(std::initializer_list<value_type> list) {
for (value_type const& v : list) {
if (!insert(v.first, v.second))
throw MapException::format("Repeat pair in BiMap initializer_list construction: ({}, {})", outputAny(v.first), outputAny(v.second));
}
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
List<LeftT> BiMap<LeftT, RightT, LeftMapT, RightMapT>::leftValues() const {
return m_leftMap.keys();
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
List<RightT> BiMap<LeftT, RightT, LeftMapT, RightMapT>::rightValues() const {
return m_rightMap.keys();
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
auto BiMap<LeftT, RightT, LeftMapT, RightMapT>::pairs() const -> List<value_type> {
List<value_type> values;
for (auto const& p : *this)
values.append(p);
return values;
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
bool BiMap<LeftT, RightT, LeftMapT, RightMapT>::hasLeftValue(Left const& left) const {
return m_leftMap.contains(left);
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
bool BiMap<LeftT, RightT, LeftMapT, RightMapT>::hasRightValue(Right const& right) const {
return m_rightMap.contains(right);
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
RightT const& BiMap<LeftT, RightT, LeftMapT, RightMapT>::getRight(Left const& left) const {
return *m_leftMap.get(left);
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
LeftT const& BiMap<LeftT, RightT, LeftMapT, RightMapT>::getLeft(Right const& right) const {
return *m_rightMap.get(right);
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
RightT BiMap<LeftT, RightT, LeftMapT, RightMapT>::valueRight(Left const& left, Right const& def) const {
return maybeRight(left).value(def);
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
LeftT BiMap<LeftT, RightT, LeftMapT, RightMapT>::valueLeft(Right const& right, Left const& def) const {
return maybeLeft(right).value(def);
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
Maybe<RightT> BiMap<LeftT, RightT, LeftMapT, RightMapT>::maybeRight(Left const& left) const {
auto i = m_leftMap.find(left);
if (i != m_leftMap.end())
return *i->second;
return {};
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
Maybe<LeftT> BiMap<LeftT, RightT, LeftMapT, RightMapT>::maybeLeft(Right const& right) const {
auto i = m_rightMap.find(right);
if (i != m_rightMap.end())
return *i->second;
return {};
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
RightT BiMap<LeftT, RightT, LeftMapT, RightMapT>::takeRight(Left const& left) {
if (auto right = maybeTakeRight(left))
return right.take();
throw MapException::format("No such key in BiMap::takeRight", outputAny(left));
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
LeftT BiMap<LeftT, RightT, LeftMapT, RightMapT>::takeLeft(Right const& right) {
if (auto left = maybeTakeLeft(right))
return left.take();
throw MapException::format("No such key in BiMap::takeLeft", outputAny(right));
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
Maybe<RightT> BiMap<LeftT, RightT, LeftMapT, RightMapT>::maybeTakeRight(Left const& left) {
if (auto rightPtr = m_leftMap.maybeTake(left).value()) {
Right right = *rightPtr;
m_rightMap.remove(*rightPtr);
return right;
} else {
return {};
}
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
Maybe<LeftT> BiMap<LeftT, RightT, LeftMapT, RightMapT>::maybeTakeLeft(Right const& right) {
if (auto leftPtr = m_rightMap.maybeTake(right).value()) {
Left left = *leftPtr;
m_leftMap.remove(*leftPtr);
return left;
} else {
return {};
}
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
RightT const* BiMap<LeftT, RightT, LeftMapT, RightMapT>::rightPtr(Left const& left) const {
return m_leftMap.value(left);
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
LeftT const* BiMap<LeftT, RightT, LeftMapT, RightMapT>::leftPtr(Right const& right) const {
return m_rightMap.value(right);
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
BiMap<LeftT, RightT, LeftMapT, RightMapT>& BiMap<LeftT, RightT, LeftMapT, RightMapT>::operator=(BiMap const& map) {
if (this != &map) {
clear();
for (auto const& p : map)
insert(p);
}
return *this;
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
auto BiMap<LeftT, RightT, LeftMapT, RightMapT>::insert(value_type const& val) -> pair<iterator, bool> {
auto leftRes = m_leftMap.insert(make_pair(val.first, nullptr));
if (!leftRes.second)
return {BiMapIterator{leftRes.first}, false};
auto rightRes = m_rightMap.insert(make_pair(val.second, nullptr));
starAssert(rightRes.second == true);
leftRes.first->second = &rightRes.first->first;
rightRes.first->second = &leftRes.first->first;
return {BiMapIterator{leftRes.first}, true};
};
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
bool BiMap<LeftT, RightT, LeftMapT, RightMapT>::insert(Left const& left, Right const& right) {
return insert(make_pair(left, right)).second;
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
void BiMap<LeftT, RightT, LeftMapT, RightMapT>::add(Left const& left, Right const& right) {
if (m_leftMap.contains(left))
throw MapException(strf("BiMap already contains left side value '{}'", outputAny(left)));
if (m_rightMap.contains(right))
throw MapException(strf("BiMap already contains right side value '{}'", outputAny(right)));
insert(left, right);
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
void BiMap<LeftT, RightT, LeftMapT, RightMapT>::add(value_type const& value) {
add(value.first, value.second);
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
void BiMap<LeftT, RightT, LeftMapT, RightMapT>::overwrite(Left const& left, Right const& right) {
removeLeft(left);
removeRight(right);
insert(left, right);
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
void BiMap<LeftT, RightT, LeftMapT, RightMapT>::overwrite(value_type const& value) {
return overwrite(value.first, value.second);
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
bool BiMap<LeftT, RightT, LeftMapT, RightMapT>::removeLeft(Left const& left) {
if (auto right = m_leftMap.value(left)) {
m_rightMap.remove(*right);
m_leftMap.remove(left);
return true;
}
return false;
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
bool BiMap<LeftT, RightT, LeftMapT, RightMapT>::removeRight(Right const& right) {
if (auto left = m_rightMap.value(right)) {
m_leftMap.remove(*left);
m_rightMap.remove(right);
return true;
}
return false;
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
auto BiMap<LeftT, RightT, LeftMapT, RightMapT>::begin() const -> const_iterator {
return BiMapIterator{m_leftMap.begin()};
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
auto BiMap<LeftT, RightT, LeftMapT, RightMapT>::end() const -> const_iterator {
return BiMapIterator{m_leftMap.end()};
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
size_t BiMap<LeftT, RightT, LeftMapT, RightMapT>::size() const {
return m_leftMap.size();
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
void BiMap<LeftT, RightT, LeftMapT, RightMapT>::clear() {
m_leftMap.clear();
m_rightMap.clear();
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
bool BiMap<LeftT, RightT, LeftMapT, RightMapT>::empty() const {
return m_leftMap.empty();
}
template <typename LeftT, typename RightT, typename LeftMapT, typename RightMapT>
bool BiMap<LeftT, RightT, LeftMapT, RightMapT>::operator==(BiMap const& m) const {
if (&m == this)
return true;
if (size() != m.size())
return false;
for (auto const& pair : *this) {
if (auto p = m.rightPtr(pair.first))
if (!p || *p != pair.second)
return false;
}
return true;
}
}
#endif