diff --git a/lib/utils/include/utils/bidict/algorithms/bidict_filter_keys.h b/lib/utils/include/utils/bidict/algorithms/bidict_filter_keys.h index 4c2ceb840b..e877ab3c67 100644 --- a/lib/utils/include/utils/bidict/algorithms/bidict_filter_keys.h +++ b/lib/utils/include/utils/bidict/algorithms/bidict_filter_keys.h @@ -10,7 +10,7 @@ bidict bidict_filter_keys(bidict const &m, F &&f) { bidict result; for (auto const &kv : m) { if (f(kv.first)) { - result.equate(kv); + result.equate_strict(kv); } } return result; diff --git a/lib/utils/include/utils/bidict/algorithms/bidict_filter_values.h b/lib/utils/include/utils/bidict/algorithms/bidict_filter_values.h index cb968f2d02..d992df96f8 100644 --- a/lib/utils/include/utils/bidict/algorithms/bidict_filter_values.h +++ b/lib/utils/include/utils/bidict/algorithms/bidict_filter_values.h @@ -10,7 +10,7 @@ bidict bidict_filter_values(bidict const &m, F &&f) { bidict result; for (auto const &kv : m) { if (f(kv.second)) { - result.equate(kv); + result.equate_strict(kv); } } return result; diff --git a/lib/utils/include/utils/bidict/algorithms/bidict_filtrans_keys.h b/lib/utils/include/utils/bidict/algorithms/bidict_filtrans_keys.h index bd9018bd38..7d068e3520 100644 --- a/lib/utils/include/utils/bidict/algorithms/bidict_filtrans_keys.h +++ b/lib/utils/include/utils/bidict/algorithms/bidict_filtrans_keys.h @@ -14,7 +14,7 @@ bidict bidict_filtrans_keys(bidict const &m, F &&f) { for (auto const &[k, v] : m) { std::optional new_k = f(k); if (new_k.has_value()) { - result.equate(new_k.value(), v); + result.equate_strict(new_k.value(), v); } } return result; diff --git a/lib/utils/include/utils/bidict/algorithms/bidict_filtrans_values.h b/lib/utils/include/utils/bidict/algorithms/bidict_filtrans_values.h index 592440f7f6..9f5ffb77e0 100644 --- a/lib/utils/include/utils/bidict/algorithms/bidict_filtrans_values.h +++ b/lib/utils/include/utils/bidict/algorithms/bidict_filtrans_values.h @@ -14,7 +14,7 @@ bidict bidict_filtrans_values(bidict const &m, F &&f) { for (auto const &[k, v] : m) { std::optional new_v = f(v); if (new_v.has_value()) { - result.equate(k, new_v.value()); + result.equate_strict(k, new_v.value()); } } return result; diff --git a/lib/utils/include/utils/bidict/algorithms/bidict_from_enumerating.h b/lib/utils/include/utils/bidict/algorithms/bidict_from_enumerating.h index 495cfcc667..4e1f768d0b 100644 --- a/lib/utils/include/utils/bidict/algorithms/bidict_from_enumerating.h +++ b/lib/utils/include/utils/bidict/algorithms/bidict_from_enumerating.h @@ -29,7 +29,7 @@ bidict bidict result; nonnegative_int idx = 0_n; for (T const &t : s) { - result.equate(idx, t); + result.equate_strict(idx, t); idx++; } diff --git a/lib/utils/include/utils/bidict/algorithms/bidict_from_map.h b/lib/utils/include/utils/bidict/algorithms/bidict_from_map.h index aca6bc4d5e..b25e93befd 100644 --- a/lib/utils/include/utils/bidict/algorithms/bidict_from_map.h +++ b/lib/utils/include/utils/bidict/algorithms/bidict_from_map.h @@ -7,22 +7,12 @@ namespace FlexFlow { template bidict bidict_from_map(std::map const &m) { - bidict result; - for (auto const &[k, v] : m) { - ASSERT(!result.contains_r(v)); - result.equate({k, v}); - } - return result; + return bidict{m.begin(), m.end()}; } template bidict bidict_from_map(std::unordered_map const &m) { - bidict result; - for (auto const &[k, v] : m) { - ASSERT(!result.contains_r(v)); - result.equate({k, v}); - } - return result; + return bidict{m.begin(), m.end()}; } } // namespace FlexFlow diff --git a/lib/utils/include/utils/bidict/algorithms/bidict_from_unstructured_relation.h b/lib/utils/include/utils/bidict/algorithms/bidict_from_unstructured_relation.h index 4794f6b393..ea29ce529a 100644 --- a/lib/utils/include/utils/bidict/algorithms/bidict_from_unstructured_relation.h +++ b/lib/utils/include/utils/bidict/algorithms/bidict_from_unstructured_relation.h @@ -44,11 +44,7 @@ bidict bidict_from_unstructured_relation( ASSERT(duplicated_element_counts.empty(), duplicated_element_counts); } - bidict result; - for (auto const &lr : relation) { - result.equate_strict(lr); - } - return result; + return bidict{relation.begin(), relation.end()}; } } // namespace FlexFlow diff --git a/lib/utils/include/utils/bidict/algorithms/exhaustive_relational_join.h b/lib/utils/include/utils/bidict/algorithms/exhaustive_relational_join.h index 65cd23bdb0..4502981528 100644 --- a/lib/utils/include/utils/bidict/algorithms/exhaustive_relational_join.h +++ b/lib/utils/include/utils/bidict/algorithms/exhaustive_relational_join.h @@ -16,7 +16,7 @@ bidict exhaustive_relational_join(bidict const &fst, bidict result; for (auto const &[v1, v2] : fst) { - result.equate({v1, snd.at_l(v2)}); + result.equate_strict({v1, snd.at_l(v2)}); } return result; diff --git a/lib/utils/include/utils/bidict/algorithms/transform.h b/lib/utils/include/utils/bidict/algorithms/transform.h index 7fbdd07db7..deb91d92d7 100644 --- a/lib/utils/include/utils/bidict/algorithms/transform.h +++ b/lib/utils/include/utils/bidict/algorithms/transform.h @@ -13,7 +13,7 @@ template transform(bidict const &m, F &&f) { bidict result; for (auto const &[k, v] : m) { - result.equate(f(k, v)); + result.equate_strict(f(k, v)); } return result; } diff --git a/lib/utils/include/utils/bidict/bidict.h b/lib/utils/include/utils/bidict/bidict.h index 4e1c430fa0..cde5297929 100644 --- a/lib/utils/include/utils/bidict/bidict.h +++ b/lib/utils/include/utils/bidict/bidict.h @@ -30,7 +30,7 @@ struct bidict { template bidict(InputIt first, InputIt last) { for (auto it = first; it != last; it++) { - this->equate(it->first, it->second); + this->equate_strict(it->first, it->second); } } @@ -47,37 +47,40 @@ struct bidict { } void erase_l(L const &l) { - this->fwd_map.erase(l); - for (auto const &kv : this->bwd_map) { - if (kv.second == l) { - bwd_map.erase(kv.first); - break; - } + if (this->contains_l(l)) { + R r = this->at_l(l); + this->fwd_map.erase(l); + this->bwd_map.erase(r); } } void erase_r(R const &r) { - this->bwd_map.erase(r); - for (auto const &kv : this->fwd_map) { - if (kv.second == r) { - fwd_map.erase(kv.first); - break; - } + if (this->contains_r(r)) { + L l = this->at_r(r); + this->fwd_map.erase(l); + this->bwd_map.erase(r); } } void equate(L const &l, R const &r) { - fwd_map.insert({l, r}); - bwd_map.insert({r, l}); + bool contains_l = this->contains_l(l); + bool contains_r = this->contains_r(r); - this->check_invariants(); + if (contains_l != contains_r || (contains_l && this->at_l(l) != r)) { + this->erase_l(l); + this->erase_r(r); + contains_l = contains_r = false; + } + + if (!contains_l) { + ASSERT(!contains_r); + fwd_map.insert({l, r}); + bwd_map.insert({r, l}); + } } void equate(std::pair const &lr) { - fwd_map.insert(lr); - bwd_map.insert({lr.second, lr.first}); - - this->check_invariants(); + this->equate(lr.first, lr.second); } void equate_strict(L const &l, R const &r) { @@ -86,7 +89,8 @@ struct bidict { if (this->contains_l(l)) { ASSERT(this->at_l(l) == r); } else { - this->equate(l, r); + fwd_map.insert({l, r}); + bwd_map.insert({r, l}); } } @@ -238,7 +242,9 @@ struct bidict { } bidict(std::map const &fwd_map, std::map const &bwd_map) - : fwd_map(fwd_map), bwd_map(bwd_map) {} + : fwd_map(fwd_map), bwd_map(bwd_map) { + this->check_invariants(); + } bool operator<(bidict const &other) const { return this->fwd_map < other.fwd_map; diff --git a/lib/utils/test/src/utils/bidict/bidict.cc b/lib/utils/test/src/utils/bidict/bidict.cc index 6db4bd1fbc..2db07a9741 100644 --- a/lib/utils/test/src/utils/bidict/bidict.cc +++ b/lib/utils/test/src/utils/bidict/bidict.cc @@ -1,6 +1,7 @@ #include "utils/bidict/bidict.h" #include "test/utils/doctest/check_without_stringify.h" #include "test/utils/doctest/fmt/map.h" +#include "test/utils/doctest/fmt/set.h" #include "test/utils/doctest/fmt/vector.h" #include "test/utils/rapidcheck.h" #include @@ -29,26 +30,92 @@ TEST_SUITE(FF_TEST_SUITE) { } SUBCASE("L type is not the same as R type") { - bidict dict; - dict.equate(1, "one"); - dict.equate(2, "two"); + bidict bd; + bd.equate(1, "one"); + bd.equate(2, "two"); SUBCASE("bidict::contains_l") { - CHECK(dict.contains_l(1)); - CHECK_FALSE(dict.contains_l(3)); + CHECK(bd.contains_l(1)); + CHECK_FALSE(bd.contains_l(3)); } SUBCASE("bidict::contains_r") { - CHECK(dict.contains_r("one")); - CHECK_FALSE(dict.contains_r("three")); + CHECK(bd.contains_r("one")); + CHECK_FALSE(bd.contains_r("three")); + } + } + + SUBCASE("bidict::bidict(std::initializer_list>)") { + bidict bd{{1, "one"}, {2, "two"}}; + CHECK(bd.contains_l(1)); + CHECK_FALSE(bd.contains_l(3)); + + SUBCASE("invalid mapping") { + CHECK_THROWS(bidict{{1, "one"}, {2, "one"}}); } } + SUBCASE("bidict::bidict(InputIt)") { + std::vector> pairs = {{1, "one"}, {2, "two"}}; + bidict bd{pairs.begin(), pairs.end()}; + CHECK(bd.contains_l(1)); + CHECK_FALSE(bd.contains_l(3)); + + SUBCASE("invalid mapping") { + std::vector> bad_pairs = {{1, "one"}, + {2, "one"}}; + CHECK_THROWS( + bidict{bad_pairs.begin(), bad_pairs.end()}); + } + } + + SUBCASE("bidict::bidict(std::map const &, std::map const &)") { + std::map fwd = {{1, "one"}, {2, "two"}}; + std::map bwd = {{"one", 1}, {"two", 2}}; + bidict bd{fwd, bwd}; + CHECK(bd.contains_l(1)); + CHECK_FALSE(bd.contains_l(3)); + + SUBCASE("invalid mapping") { + std::map bad_fwd = {{1, "one"}, {2, "one"}}; + std::map bad_bwd = {{"one", 1}}; + CHECK_THROWS(bidict{bad_fwd, bad_bwd}); + } + } + + SUBCASE("bidict::erase_l") { + dict.erase_l(1); + CHECK(dict.size() == 1); + CHECK_THROWS(dict.at_l(1)); + CHECK(dict.at_r("two") == 2); + } + + SUBCASE("bidict::erase_r") { + dict.erase_r("one"); + CHECK(dict.size() == 1); + CHECK_THROWS(dict.at_r("one")); + CHECK(dict.at_l(2) == "two"); + } + SUBCASE("bidict::equate") { CHECK(dict.at_l(1) == "one"); CHECK(dict.at_r("one") == 1); CHECK(dict.at_l(2) == "two"); CHECK(dict.at_r("two") == 2); + + dict.equate(1, "three"); + CHECK(dict.at_l(1) == "three"); + CHECK(dict.at_r("three") == 1); + CHECK_THROWS(dict.at_r("one")); + CHECK(dict.at_l(2) == "two"); + CHECK(dict.at_r("two") == 2); + + dict.equate(3, "three"); + CHECK(dict.at_l(3) == "three"); + CHECK(dict.at_r("three") == 3); + CHECK_THROWS(dict.at_l(1)); + CHECK(dict.at_l(2) == "two"); + CHECK(dict.at_r("two") == 2); } SUBCASE("bidict::equate_strict") { @@ -60,34 +127,46 @@ TEST_SUITE(FF_TEST_SUITE) { CHECK(dict.at_r("three") == 3); } - SUBCASE("bidict::erase_l") { - dict.erase_l(1); - CHECK(dict.size() == 1); - CHECK_THROWS(dict.at_l(1)); - CHECK(dict.at_r("two") == 2); + SUBCASE("bidict::operator==") { + bidict bd{{1, "one"}, {2, "two"}}; + bidict bd2{{1, "one"}, {3, "three"}}; + CHECK(dict == bd); + CHECK_FALSE(dict == bd2); } - SUBCASE("bidict::erase_r") { - dict.erase_r("one"); - CHECK(dict.size() == 1); - CHECK_THROWS(dict.at_r("one")); - CHECK(dict.at_l(2) == "two"); + SUBCASE("bidict::operator!=") { + bidict bd{{1, "one"}, {2, "two"}}; + bidict bd2{{1, "one"}, {3, "three"}}; + CHECK_FALSE(dict != bd); + CHECK(dict != bd2); } - SUBCASE("bidict::reversed") { - bidict reversed_dict = dict.reversed(); - CHECK(reversed_dict.at_l("one") == 1); - CHECK(reversed_dict.at_r(2) == "two"); + SUBCASE("bidict::at_l") { + CHECK(dict.at_l(1) == "one"); + CHECK_THROWS(dict.at_l(3)); + } + + SUBCASE("bidict::at_r") { + CHECK(dict.at_r("one") == 1); + CHECK_THROWS(dict.at_r("three")); + } + + SUBCASE("bidict::left_values") { + CHECK(dict.left_values() == std::set{1, 2}); + } + + SUBCASE("bidict::right_values") { + CHECK(dict.right_values() == std::set{"one", "two"}); } SUBCASE("bidict::size") { CHECK(dict.size() == 2); } - SUBCASE("implicitly convert to std::map") { - std::map res = dict; - std::map expected = {{1, "one"}, {2, "two"}}; - CHECK(res == expected); + SUBCASE("bidict::empty") { + CHECK_FALSE(dict.empty()); + bidict empty{}; + CHECK(empty.empty()); } SUBCASE("bidict::begin") { @@ -102,6 +181,18 @@ TEST_SUITE(FF_TEST_SUITE) { CHECK_WITHOUT_STRINGIFY(it == dict.end()); } + SUBCASE("bidict::reversed") { + bidict reversed_dict = dict.reversed(); + CHECK(reversed_dict.at_l("one") == 1); + CHECK(reversed_dict.at_r(2) == "two"); + } + + SUBCASE("implicitly convert to std::map") { + std::map res = dict; + std::map expected = {{1, "one"}, {2, "two"}}; + CHECK(res == expected); + } + SUBCASE("fmt::to_string(bidict)") { std::string result = fmt::to_string(dict); std::string correct = fmt::to_string(dict.as_map()); @@ -137,7 +228,7 @@ TEST_SUITE(FF_TEST_SUITE) { } } - TEST_CASE("rc::Arbitrary") { + TEST_CASE("rc::Arbitrary>") { RC_SUBCASE([](bidict) {}); } }