11#include <ankerl/unordered_dense.h>
16template<
class H,
class E>
17concept Transparent =
requires {
18 typename H::is_transparent;
19 typename E::is_transparent;
22template<
class K,
class T>
23concept NotConvertibleTo = !std::convertible_to<const K&, T>;
33 class LHash = ankerl::unordered_dense::hash<L>,
34 class LEq = std::equal_to<L>,
35 class RHash = ankerl::unordered_dense::hash<R>,
36 class REq = std::equal_to<R>>
39 using Left = ankerl::unordered_dense::map<L, R, LHash, LEq>;
40 using Right = ankerl::unordered_dense::map<R, L, RHash, REq>;
44 [[nodiscard]]
const Left&
left() const noexcept {
return l2r_; }
45 [[nodiscard]]
const Right&
right() const noexcept {
return r2l_; }
46 [[nodiscard]]
size_t size() const noexcept {
return l2r_.size(); }
47 [[nodiscard]]
bool empty() const noexcept {
return l2r_.empty(); }
48 [[nodiscard]]
auto begin() const noexcept {
return l2r_.begin(); }
49 [[nodiscard]]
auto end() const noexcept {
return l2r_.end(); }
55 [[nodiscard]]
bool contains_left(
const auto& l)
const {
return l2r_.contains(l); }
56 [[nodiscard]]
bool contains_right(
const auto& r)
const {
return r2l_.contains(r); }
57 [[nodiscard]]
const R&
at_left(
const auto& l)
const {
return l2r_.at(l); }
58 [[nodiscard]]
const L&
at_right(
const auto& r)
const {
return r2l_.at(r); }
67 auto [i, ins] = l2r_.try_emplace(l, r);
68 return ins && link(i, std::move(l), std::move(r));
75 link(l2r_.emplace(l, r).first, std::move(l), std::move(r));
80 auto i = l2r_.find(l);
81 if (i == l2r_.end())
return false;
82 r2l_.erase(i->second);
89 auto i = r2l_.find(r);
90 if (i == r2l_.end())
return false;
91 l2r_.erase(i->second);
110 bool link(
typename Left::iterator i, L&& l, R&& r) {
112 if (r2l_.try_emplace(std::move(r), std::move(l)).second)
return true;
132 class LHash = ankerl::unordered_dense::hash<L>,
133 class LEq = std::equal_to<L>,
134 class RHash = ankerl::unordered_dense::hash<R>,
135 class REq = std::equal_to<R>>
138 using Left = ankerl::unordered_dense::segmented_map<L, R, LHash, LEq>;
145 using is_transparent = void;
146 using is_avalanching = std::bool_constant<ankerl::unordered_dense::hash_is_avalanching_v<RHash>>;
148 size_t operator()(Ptr p)
const {
return hash(p->second); }
149 template<detail::NotConvertibleTo<Ptr> K>
150 size_t operator()(
const K& r)
const {
158 using is_transparent = void;
160 bool operator()(Ptr a, Ptr b)
const {
return eq(a->second, b->second); }
161 template<detail::NotConvertibleTo<Ptr> K>
162 bool operator()(
const K& r, Ptr b)
const {
163 return eq(r, b->second);
173 r2l_.reserve(l2r_.size());
174 for (
const auto& p : l2r_)
187 [[nodiscard]] const
Left&
left() const noexcept {
return l2r_; }
188 [[nodiscard]]
size_t size() const noexcept {
return l2r_.size(); }
189 [[nodiscard]]
bool empty() const noexcept {
return l2r_.empty(); }
190 [[nodiscard]]
auto begin() const noexcept {
return l2r_.begin(); }
191 [[nodiscard]]
auto end() const noexcept {
return l2r_.end(); }
197 [[nodiscard]]
bool contains_left(
const auto& l)
const {
return l2r_.contains(l); }
199 [[nodiscard]]
const R&
at_left(
const auto& l)
const {
return l2r_.at(l); }
200 [[nodiscard]]
const L&
at_right(
const auto& r)
const {
202 throw std::out_of_range(
"fe::BiNodeMap::at_right");
208 if constexpr (std::same_as<K, R> || detail::Transparent<RHash, REq>) {
209 auto i = r2l_.find(r);
210 return i != r2l_.end() ? *i :
nullptr;
224 auto [i, ins] = l2r_.try_emplace(std::move(l), std::move(r));
225 return ins && index(i);
232 index(l2r_.try_emplace(std::move(l), std::move(r)).first);
237 auto i = l2r_.find(l);
238 if (i == l2r_.end())
return false;
246 if (!p)
return false;
265 bool index(
typename Left::iterator i) {
267 if (r2l_.emplace(&*i).second)
return true;
277 Ptr back = &l2r_.values().back();
279 auto j = p != back ? r2l_.find(back) : r2l_.end();
280 l2r_.erase(p->first);
282 if (j != r2l_.end())
const_cast<Ptr&
>(*j) = p;
286 ankerl::unordered_dense::set<Ptr, Hash, Eq> r2l_;
A bijection between L and R, kept as one ankerl::unordered_dense::map per direction.
ankerl::unordered_dense::map< R, L, RHash, REq > Right
auto end() const noexcept
bool contains_right(const auto &r) const
size_t size() const noexcept
bool empty() const noexcept
const L & at_right(const auto &r) const
bool contains_left(const auto &l) const
bool erase_right(const auto &r)
Unbinds r and its partner; yields whether r was bound.
const R & at_left(const auto &l) const
bool insert(L l, R r)
Binds l to r unless either is already bound; yields whether it did.
ankerl::unordered_dense::map< L, R, LHash, LEq > Left
void insert_or_assign(L l, R r)
Binds l to r after unbinding whatever either was bound to.
auto begin() const noexcept
const Left & left() const noexcept
friend bool operator==(const BiMap &a, const BiMap &b)
bool erase_left(const auto &l)
Unbinds l and its partner; yields whether l was bound.
const Right & right() const noexcept
auto end() const noexcept
bool erase_left(const auto &l)
Unbinds l and its partner; yields whether l was bound.
bool contains_left(const auto &l) const
const Left & left() const noexcept
bool contains_right(const auto &r) const
BiNodeMap & operator=(BiNodeMap &&) noexcept=default
bool empty() const noexcept
auto begin() const noexcept
void insert_or_assign(L l, R r)
Binds l to r after unbinding whatever either was bound to.
BiNodeMap(const BiNodeMap &other)
const L & at_right(const auto &r) const
bool insert(L l, R r)
Binds l to r unless either is already bound; yields whether it did.
typename Left::value_type value_type
friend bool operator==(const BiNodeMap &a, const BiNodeMap &b)
size_t size() const noexcept
ankerl::unordered_dense::segmented_map< L, R, LHash, LEq > Left
const R & at_left(const auto &l) const
Ptr find_right(const K &r) const
Yields the pair whose right side is r, or nullptr.
BiNodeMap(BiNodeMap &&) noexcept=default
bool erase_right(const auto &r)
Unbinds r and its partner; yields whether r was bound.