#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 RightMapT = Map> class BiMap { public: typedef LeftT Left; typedef RightT Right; typedef LeftMapT LeftMap; typedef RightMapT RightMap; typedef pair value_type; struct BiMapIterator { BiMapIterator& operator++(); BiMapIterator operator++(int); bool operator==(BiMapIterator const& rhs) const; bool operator!=(BiMapIterator const& rhs) const; pair operator*() const; typename LeftMap::const_iterator iterator; }; typedef BiMapIterator iterator; typedef iterator const_iterator; template static BiMap from(Collection const& c); BiMap(); BiMap(BiMap const& map); template BiMap(InputIterator beg, InputIterator end); BiMap(std::initializer_list list); List leftValues() const; List rightValues() const; List 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 maybeRight(Left const& left) const; Maybe maybeLeft(Right const& right) const; Right takeRight(Left const& left); Left takeLeft(Right const& right); Maybe maybeTakeRight(Left const& left); Maybe maybeTakeLeft(Right const& right); Right const* rightPtr(Left const& left) const; Left const* leftPtr(Right const& right) const; BiMap& operator=(BiMap const& map); pair 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 RightHash = Star::hash> using BiHashMap = BiMap, StableHashMap>; // Case insensitive Enum <-> String map template using EnumMap = BiMap, StableHashMap>; template auto BiMap::BiMapIterator::operator++() -> BiMapIterator & { ++iterator; return *this; } template auto BiMap::BiMapIterator::operator++(int) -> BiMapIterator { BiMapIterator last{iterator}; ++iterator; return last; } template bool BiMap::BiMapIterator::operator==(BiMapIterator const& rhs) const { return iterator == rhs.iterator; } template bool BiMap::BiMapIterator::operator!=(BiMapIterator const& rhs) const { return iterator != rhs.iterator; } template pair BiMap::BiMapIterator::operator*() const { return {iterator->first, *iterator->second}; } template template BiMap BiMap::from(Collection const& c) { return BiMap(c.begin(), c.end()); } template BiMap::BiMap() {} template BiMap::BiMap(BiMap const& map) : BiMap(map.begin(), map.end()) {} template template BiMap::BiMap(InputIterator beg, InputIterator end) { while (beg != end) { insert(*beg); ++beg; } } template BiMap::BiMap(std::initializer_list list) { for (value_type const& v : list) { if (!insert(v.first, v.second)) throw MapException::format("Repeat pair in BiMap initializer_list construction: (%s, %s)", outputAny(v.first), outputAny(v.second)); } } template List BiMap::leftValues() const { return m_leftMap.keys(); } template List BiMap::rightValues() const { return m_rightMap.keys(); } template auto BiMap::pairs() const -> List { List values; for (auto const& p : *this) values.append(p); return values; } template bool BiMap::hasLeftValue(Left const& left) const { return m_leftMap.contains(left); } template bool BiMap::hasRightValue(Right const& right) const { return m_rightMap.contains(right); } template RightT const& BiMap::getRight(Left const& left) const { return *m_leftMap.get(left); } template LeftT const& BiMap::getLeft(Right const& right) const { return *m_rightMap.get(right); } template RightT BiMap::valueRight(Left const& left, Right const& def) const { return maybeRight(left).value(def); } template LeftT BiMap::valueLeft(Right const& right, Left const& def) const { return maybeLeft(right).value(def); } template Maybe BiMap::maybeRight(Left const& left) const { auto i = m_leftMap.find(left); if (i != m_leftMap.end()) return *i->second; return {}; } template Maybe BiMap::maybeLeft(Right const& right) const { auto i = m_rightMap.find(right); if (i != m_rightMap.end()) return *i->second; return {}; } template RightT BiMap::takeRight(Left const& left) { if (auto right = maybeTakeRight(left)) return right.take(); throw MapException::format("No such key in BiMap::takeRight", outputAny(left)); } template LeftT BiMap::takeLeft(Right const& right) { if (auto left = maybeTakeLeft(right)) return left.take(); throw MapException::format("No such key in BiMap::takeLeft", outputAny(right)); } template Maybe BiMap::maybeTakeRight(Left const& left) { if (auto rightPtr = m_leftMap.maybeTake(left).value()) { Right right = *rightPtr; m_rightMap.remove(*rightPtr); return right; } else { return {}; } } template Maybe BiMap::maybeTakeLeft(Right const& right) { if (auto leftPtr = m_rightMap.maybeTake(right).value()) { Left left = *leftPtr; m_leftMap.remove(*leftPtr); return left; } else { return {}; } } template RightT const* BiMap::rightPtr(Left const& left) const { return m_leftMap.value(left); } template LeftT const* BiMap::leftPtr(Right const& right) const { return m_rightMap.value(right); } template BiMap& BiMap::operator=(BiMap const& map) { if (this != &map) { clear(); for (auto const& p : map) insert(p); } return *this; } template auto BiMap::insert(value_type const& val) -> pair { 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 bool BiMap::insert(Left const& left, Right const& right) { return insert(make_pair(left, right)).second; } template void BiMap::add(Left const& left, Right const& right) { if (m_leftMap.contains(left)) throw MapException(strf("BiMap already contains left side value '%s'", outputAny(left))); if (m_rightMap.contains(right)) throw MapException(strf("BiMap already contains right side value '%s'", outputAny(right))); insert(left, right); } template void BiMap::add(value_type const& value) { add(value.first, value.second); } template void BiMap::overwrite(Left const& left, Right const& right) { removeLeft(left); removeRight(right); insert(left, right); } template void BiMap::overwrite(value_type const& value) { return overwrite(value.first, value.second); } template bool BiMap::removeLeft(Left const& left) { if (auto right = m_leftMap.value(left)) { m_rightMap.remove(*right); m_leftMap.remove(left); return true; } return false; } template bool BiMap::removeRight(Right const& right) { if (auto left = m_rightMap.value(right)) { m_leftMap.remove(*left); m_rightMap.remove(right); return true; } return false; } template auto BiMap::begin() const -> const_iterator { return BiMapIterator{m_leftMap.begin()}; } template auto BiMap::end() const -> const_iterator { return BiMapIterator{m_leftMap.end()}; } template size_t BiMap::size() const { return m_leftMap.size(); } template void BiMap::clear() { m_leftMap.clear(); m_rightMap.clear(); } template bool BiMap::empty() const { return m_leftMap.empty(); } template bool BiMap::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