Commit f27a2019 authored by Christoph Schwering's avatar Christoph Schwering Committed by Chromium LUCI CQ

[Autofill] Added union, difference, subset operations to DenseSet.

This CL adds set operations to DenseSet for the union and difference
and for subset and disjointness of two DenseSets.

The set's size parameter (formerly kEnd, now kMaxValue) is now implicit
and defaults to the T::kMaxValue (which according to the chromium-style
is the highest value in the enum.

Bug: 1007974
Change-Id: Ia8fd96481bf28d361a7736032ea4d65b3dbbab83
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2630706
Commit-Queue: Christoph Schwering <schwering@google.com>
Reviewed-by: default avatarMatthias Körber <koerber@google.com>
Cr-Commit-Position: refs/heads/master@{#844523}
parent 484f3956
......@@ -23,18 +23,18 @@ namespace autofill {
// representation.
//
// The lower and upper bounds of elements storable in a container are
// [T(0), kEnd).
// [T(0), kMaxValue]. By default, kMaxValue is T::kMaxValue.
//
// Internally, the set is represented as a std::bitset.
//
// Time and space complexity depend on std::bitset:
// - insert(), erase(), contains() should run in time O(1)
// - empty(), size(), iteration should run in time O(kEnd)
// - sizeof(DenseSet) should be ceil(kEnd / 8) bytes.
// - empty(), size(), iteration should run in time O(kMaxValue)
// - sizeof(DenseSet) should be ceil(kMaxValue / 8) bytes.
//
// Iterators are invalidated when the owning container is destructed or moved,
// or when the element the iterator points to is erased from the container.
template <typename T, T kEnd>
template <typename T, T kMaxValue = T::kMaxValue>
class DenseSet {
private:
using Index = std::make_unsigned_t<T>;
......@@ -194,6 +194,9 @@ class DenseSet {
return {find(x), !contained};
}
// Inserts all values of |xs| into the present set.
void insert_all(const DenseSet& xs) { bitset_ |= xs.bitset_; }
// Erases the element whose index matches the index of |x| and returns the
// number of erased elements (0 or 1).
size_t erase(T x) {
......@@ -220,6 +223,9 @@ class DenseSet {
return last;
}
// Erases all values of |xs| into the present set.
void erase_all(const DenseSet& xs) { bitset_ &= ~xs.bitset_; }
// Lookup.
// Returns 1 if |x| is an element, otherwise 0.
......@@ -233,6 +239,21 @@ class DenseSet {
// Returns true if |x| is an element, else |false|.
bool contains(T x) const { return bitset_.test(value_to_index(x)); }
// Returns true if some element of |xs| is an element, else |false|.
bool contains_none(const DenseSet& xs) const {
return (bitset_ & xs.bitset_).none();
}
// Returns true if some element of |xs| is an element, else |false|.
bool contains_any(const DenseSet& xs) const {
return (bitset_ & xs.bitset_).any();
}
// Returns true if every elements of |xs| is an element, else |false|.
bool contains_all(const DenseSet& xs) const {
return (bitset_ & xs.bitset_) == xs.bitset_;
}
// Returns an iterator to the first element not less than the |x|, or end().
const_iterator lower_bound(T x) const {
const_iterator it(this, value_to_index(x));
......@@ -255,12 +276,12 @@ class DenseSet {
};
static constexpr Index value_to_index(T x) {
DCHECK(index_to_value(0) <= x && x < kEnd);
DCHECK(index_to_value(0) <= x && x <= kMaxValue);
return base::checked_cast<Index>(x);
}
static constexpr T index_to_value(Index i) {
DCHECK_LT(i, base::checked_cast<Index>(kEnd));
DCHECK_LE(i, base::checked_cast<Index>(kMaxValue));
using UnderlyingType =
typename std::conditional_t<std::is_enum<T>::value,
std::underlying_type<T>, Wrapper>::type;
......@@ -268,9 +289,9 @@ class DenseSet {
}
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "");
static_assert(index_to_value(0) <= kEnd, "");
static_assert(0 <= base::checked_cast<Index>(kMaxValue) + 1, "");
std::bitset<base::checked_cast<Index>(kEnd)> bitset_{};
std::bitset<base::checked_cast<Index>(kMaxValue) + 1> bitset_{};
};
} // namespace autofill
......
......@@ -19,9 +19,9 @@ TEST(DenseSet, initialization) {
Three = 3,
Four = 4,
Five = 5,
kEnd = 6
kMaxValue = Five,
};
using DS = DenseSet<T, T::kEnd>;
using DS = DenseSet<T>;
DS s;
EXPECT_TRUE(s.empty());
......@@ -45,9 +45,9 @@ TEST(DenseSet, iterators_begin_end) {
Three = 3,
Four = 4,
Five = 5,
kEnd = 6
kMaxValue = Five,
};
using DS = DenseSet<T, T::kEnd>;
using DS = DenseSet<T, T::kMaxValue>;
DS s;
s.insert(T::Two);
......@@ -88,9 +88,9 @@ TEST(DenseSet, iterators_begin_end_reverse) {
Three = 3,
Four = 4,
Five = 5,
kEnd = 6
kMaxValue = Five,
};
using DS = DenseSet<T, T::kEnd>;
using DS = DenseSet<T>;
DS s;
s.insert(T::Two);
......@@ -123,8 +123,15 @@ TEST(DenseSet, iterators_begin_end_reverse) {
}
TEST(DenseSet, iterators_rbegin_rend) {
enum class T { One = 1, Two = 2, Three = 3, Four = 4, Five = 5, kEnd = 6 };
using DS = DenseSet<T, T::kEnd>;
enum class T {
One = 1,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
kMaxValue = Five
};
using DS = DenseSet<T, T::kMaxValue>;
DS s;
s.insert(T::Two);
......@@ -160,8 +167,15 @@ TEST(DenseSet, iterators_rbegin_rend) {
}
TEST(DenseSet, lookup) {
enum class T { One = 1, Two = 2, Three = 3, Four = 4, Five = 5, kEnd = 6 };
using DS = DenseSet<T, T::kEnd>;
enum class T {
One = 1,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
kMaxValue = Five
};
using DS = DenseSet<T, T::kMaxValue>;
DS s;
s.insert(T::Two);
......@@ -199,11 +213,37 @@ TEST(DenseSet, lookup) {
EXPECT_NE(s.find(T::Three), s.lower_bound(T::Three));
EXPECT_EQ(s.find(T::Four), s.lower_bound(T::Four));
EXPECT_EQ(s.find(T::Five), s.lower_bound(T::Five));
DS t;
EXPECT_TRUE(t.empty());
EXPECT_TRUE(t.contains_none({}));
EXPECT_FALSE(t.contains_any({}));
EXPECT_TRUE(t.contains_all({}));
t.insert_all(s);
EXPECT_EQ(s, t);
EXPECT_FALSE(s.contains_none(t));
EXPECT_TRUE(s.contains_any(t));
EXPECT_TRUE(s.contains_all(t));
EXPECT_TRUE(s.contains_none({}));
EXPECT_FALSE(s.contains_any({}));
EXPECT_TRUE(s.contains_all({}));
t.erase(t.begin());
EXPECT_FALSE(s.contains_none(t));
EXPECT_TRUE(s.contains_any(t));
EXPECT_TRUE(s.contains_all(t));
EXPECT_FALSE(t.contains_none(s));
EXPECT_FALSE(t.contains_all(s));
EXPECT_TRUE(t.contains_any(s));
EXPECT_TRUE(s.contains_none({}));
EXPECT_FALSE(s.contains_any({}));
EXPECT_TRUE(s.contains_all({}));
}
TEST(DenseSet, iterators_lower_upper_bound) {
enum class T { One = 1, Two = 2, Three = 3, Four = 4, Five = 5, kEnd = 6 };
using DS = DenseSet<T, T::kEnd>;
enum class T { One = 1, Two = 2, Three = 3, Four = 4, Five = 5 };
using DS = DenseSet<T, T::Five>;
DS s;
s.insert(T::Two);
......@@ -276,8 +316,8 @@ TEST(DenseSet, max_size) {
// const int Three = 3;
const int Four = 4;
// const int Five = 5;
const int kEnd = 6;
using DS = DenseSet<int, kEnd>;
const int kMaxValue = 5;
using DS = DenseSet<int, kMaxValue>;
DS s;
EXPECT_TRUE(s.empty());
......@@ -301,8 +341,8 @@ TEST(DenseSet, modifiers) {
const size_t Three = 3;
const size_t Four = 4;
// const size_t Five = 5;
const size_t kEnd = 6;
using DS = DenseSet<size_t, kEnd>;
const size_t kMaxValue = 5;
using DS = DenseSet<size_t, kMaxValue>;
DS s;
s.insert(Two);
......@@ -375,7 +415,35 @@ TEST(DenseSet, modifiers) {
s.clear();
EXPECT_EQ(s, DS());
EXPECT_EQ(s.size(), 0u);
EXPECT_TRUE(s.empty());
s.insert(Three);
s.insert_all(t);
EXPECT_EQ(s.size(), 4u);
EXPECT_EQ(t.size(), 3u);
EXPECT_NE(s, t);
EXPECT_FALSE(s.contains_none(t));
EXPECT_TRUE(s.contains_any(t));
EXPECT_TRUE(s.contains_all(t));
EXPECT_TRUE(s.contains(Three));
EXPECT_FALSE(t.contains_none(s));
EXPECT_TRUE(t.contains_any(s));
EXPECT_FALSE(t.contains_all(s));
s.erase_all(t);
EXPECT_EQ(s.size(), 1u);
EXPECT_TRUE(s.contains(Three));
EXPECT_TRUE(s.contains_none(t));
EXPECT_FALSE(s.contains_any(t));
EXPECT_FALSE(s.contains_all(t));
s.insert_all(t);
s.erase(Three);
EXPECT_EQ(s.size(), 3u);
EXPECT_EQ(s, t);
s.erase_all(t);
EXPECT_TRUE(s.empty());
EXPECT_INSERTION(s, *t.begin(), true);
EXPECT_TRUE(s.contains(One));
......@@ -389,8 +457,8 @@ TEST(DenseSet, modifiers) {
}
TEST(DenseSet, std_set) {
constexpr size_t kEnd = 50;
DenseSet<size_t, kEnd> dense_set;
constexpr size_t kMaxValue = 50;
DenseSet<size_t, kMaxValue> dense_set;
std::set<size_t> std_set;
auto expect_equivalence = [&] {
......@@ -401,7 +469,7 @@ TEST(DenseSet, std_set) {
auto random_insert = [&] {
expect_equivalence();
size_t value = base::RandUint64() % kEnd;
size_t value = base::RandUint64() % kMaxValue;
auto p = dense_set.insert(value);
auto q = std_set.insert(value);
EXPECT_EQ(p.second, q.second);
......@@ -412,13 +480,13 @@ TEST(DenseSet, std_set) {
auto random_erase = [&] {
expect_equivalence();
size_t value = base::RandUint64() % kEnd;
size_t value = base::RandUint64() % kMaxValue;
EXPECT_EQ(dense_set.erase(value), std_set.erase(value));
};
auto random_erase_iterator = [&] {
expect_equivalence();
size_t value = base::RandUint64() % kEnd;
size_t value = base::RandUint64() % kMaxValue;
auto it = dense_set.find(value);
auto jt = std_set.find(value);
EXPECT_EQ(it == dense_set.end(), jt == std_set.end());
......@@ -434,8 +502,8 @@ TEST(DenseSet, std_set) {
auto random_erase_range = [&] {
expect_equivalence();
size_t min_value = base::RandUint64() % kEnd;
size_t max_value = base::RandUint64() % kEnd;
size_t min_value = base::RandUint64() % kMaxValue;
size_t max_value = base::RandUint64() % kMaxValue;
min_value = std::min(min_value, max_value);
max_value = std::max(min_value, max_value);
dense_set.erase(dense_set.lower_bound(min_value),
......@@ -444,11 +512,11 @@ TEST(DenseSet, std_set) {
std_set.upper_bound(max_value));
};
for (size_t i = 0; i < kEnd; ++i) {
for (size_t i = 0; i < kMaxValue; ++i) {
random_insert();
}
for (size_t i = 0; i < kEnd / 2; ++i) {
for (size_t i = 0; i < kMaxValue / 2; ++i) {
random_erase();
}
......@@ -457,11 +525,11 @@ TEST(DenseSet, std_set) {
std_set.clear();
expect_equivalence();
for (size_t i = 0; i < kEnd; ++i) {
for (size_t i = 0; i < kMaxValue; ++i) {
random_insert();
}
for (size_t i = 0; i < kEnd; ++i) {
for (size_t i = 0; i < kMaxValue; ++i) {
random_erase_iterator();
}
......@@ -470,11 +538,11 @@ TEST(DenseSet, std_set) {
std_set.clear();
expect_equivalence();
for (size_t i = 0; i < kEnd; ++i) {
for (size_t i = 0; i < kMaxValue; ++i) {
random_insert();
}
for (size_t i = 0; i < kEnd; ++i) {
for (size_t i = 0; i < kMaxValue; ++i) {
random_erase_range();
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment