Skip to content

Commit

Permalink
Add set algebraic functions to HashSet
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=248881
rdar://103074259

Reviewed by Darin Adler.

Adds union, intersection and symmetric difference operations to
HashSet both as in-place mutating operations (the ones with the
`form` prefix) and new set creating operations (the ones with
the `With` suffix).

* Source/WTF/wtf/HashSet.h:
(WTF::W>::unionWith const):
(WTF::W>::intersectionWith const):
(WTF::W>::symmetricDifferenceWith const):
(WTF::W>::formUnion):
(WTF::W>::formIntersection):
(WTF::W>::formSymmetricDifference):
* Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp:
(TestWebKitAPI::TEST):

Canonical link: https://commits.webkit.org/257569@main
  • Loading branch information
weinig authored and Sam Weinig committed Dec 8, 2022
1 parent b666a12 commit 5e42510
Show file tree
Hide file tree
Showing 2 changed files with 373 additions and 0 deletions.
88 changes: 88 additions & 0 deletions Source/WTF/wtf/HashSet.h
Expand Up @@ -132,6 +132,40 @@ class HashSet final {
TakeType take(iterator);
TakeType takeAny();

// Returns a new set with the elements of both this and the given
// collection (a.k.a. OR).
template<typename OtherCollection>
HashSet unionWith(const OtherCollection&) const;

// Returns a new set with the elements that are common to both this
// set and the given collection (a.k.a. AND).
//
// NOTE: OtherCollection is required to implement `bool contains(Value)`.
template<typename OtherCollection>
HashSet intersectionWith(const OtherCollection&) const;

// Returns a new set with the elements that are either in this set or
// in the given collection, but not in both. (a.k.a. XOR).
template<typename OtherCollection>
HashSet symmetricDifferenceWith(const OtherCollection&) const;

// Adds the elements of the given collection to the set (a.k.a. OR).
template<typename OtherCollection>
void formUnion(const OtherCollection&);

// Removes the elements of this set that aren't also in the given
// collection (a.k.a. AND).
//
// NOTE: OtherCollection is required to implement `bool contains(Value)`.
template<typename OtherCollection>
void formIntersection(const OtherCollection&);

// Removes the elements of the set that are also in the given collection
// and adds the members of the given collection that are not already in
// the set (a.k.a. XOR).
template<typename OtherCollection>
void formSymmetricDifference(const OtherCollection&);

// Overloads for smart pointer values that take the raw pointer type as the parameter.
template<typename V = ValueType> typename std::enable_if<IsSmartPtr<V>::value, iterator>::type find(typename GetPtrHelper<V>::PtrType) const;
template<typename V = ValueType> typename std::enable_if<IsSmartPtr<V>::value, bool>::type contains(typename GetPtrHelper<V>::PtrType) const;
Expand Down Expand Up @@ -364,6 +398,60 @@ inline auto HashSet<T, U, V, W>::takeAny() -> TakeType
return take(begin());
}

template<typename T, typename U, typename V, typename W>
template<typename OtherCollection>
inline auto HashSet<T, U, V, W>::unionWith(const OtherCollection& other) const -> HashSet<T, U, V, W>
{
auto copy = *this;
copy.add(other.begin(), other.end());
return copy;
}

template<typename T, typename U, typename V, typename W>
template<typename OtherCollection>
inline auto HashSet<T, U, V, W>::intersectionWith(const OtherCollection& other) const -> HashSet<T, U, V, W>
{
HashSet result;
for (auto& value : *this) {
if (other.contains(value))
result.addVoid(value);
}
return result;
}

template<typename T, typename U, typename V, typename W>
template<typename OtherCollection>
inline auto HashSet<T, U, V, W>::symmetricDifferenceWith(const OtherCollection& other) const -> HashSet<T, U, V, W>
{
auto copy = *this;
copy.formSymmetricDifference(other);
return copy;
}

template<typename T, typename U, typename V, typename W>
template<typename OtherCollection>
inline void HashSet<T, U, V, W>::formUnion(const OtherCollection& other)
{
add(other.begin(), other.end());
}

template<typename T, typename U, typename V, typename W>
template<typename OtherCollection>
inline void HashSet<T, U, V, W>::formIntersection(const OtherCollection& other)
{
*this = intersectionWith(other);
}

template<typename T, typename U, typename V, typename W>
template<typename OtherCollection>
inline void HashSet<T, U, V, W>::formSymmetricDifference(const OtherCollection& other)
{
for (auto& value : other) {
if (!remove(value))
addVoid(value);
}
}

template<typename Value, typename HashFunctions, typename Traits, typename TableTraits>
template<typename V>
inline auto HashSet<Value, HashFunctions, Traits, TableTraits>::find(typename GetPtrHelper<V>::PtrType value) const -> typename std::enable_if<IsSmartPtr<V>::value, iterator>::type
Expand Down
285 changes: 285 additions & 0 deletions Tools/TestWebKitAPI/Tests/WTF/HashSet.cpp
Expand Up @@ -565,4 +565,289 @@ TEST(WTF_HashSet, ReserveInitialCapacity)
EXPECT_EQ(8u, set2.capacity());
}

TEST(WTF_HashSet, UnionWith)
{
HashSet<int> emptySet;
HashSet<int> set1 { 1, 2, 3 };
HashSet<int> set2 { 2, 3, 4 };
HashSet<int> set3 { 1, 2, 3, 4 };
Vector<int> sequence { 2, 3, 4 };

{
auto result = emptySet.unionWith(set1);
EXPECT_EQ(result, set1);
}

{
auto result = set1.unionWith(emptySet);
EXPECT_EQ(result, set1);
}

{
auto result = emptySet.unionWith(emptySet);
EXPECT_EQ(result, emptySet);
}

{
auto result = set1.unionWith(set1);
EXPECT_EQ(result, set1);
}

{
auto result = set1.unionWith(set2);
EXPECT_EQ(result, set3);
}

{
auto result = set2.unionWith(set1);
EXPECT_EQ(result, set3);
}

{
auto result = set1.unionWith(sequence);
EXPECT_EQ(result, set3);
}
}

TEST(WTF_HashSet, FormUnion)
{
HashSet<int> emptySet;
HashSet<int> set1 { 1, 2, 3 };
HashSet<int> set2 { 2, 3, 4 };
HashSet<int> set3 { 1, 2, 3, 4 };
Vector<int> sequence { 2, 3, 4 };

{
auto result = emptySet;
result.formUnion(set1);
EXPECT_EQ(result, set1);
}

{
auto result = set1;
result.formUnion(emptySet);
EXPECT_EQ(result, set1);
}

{
auto result = emptySet;
result.formUnion(emptySet);
EXPECT_EQ(result, emptySet);
}

{
auto result = set1;
result.formUnion(set1);
EXPECT_EQ(result, set1);
}

{
auto result = set1;
result.formUnion(set2);
EXPECT_EQ(result, set3);
}

{
auto result = set2;
result.formUnion(set1);
EXPECT_EQ(result, set3);
}

{
auto result = set1;
result.formUnion(sequence);
EXPECT_EQ(result, set3);
}
}

TEST(WTF_HashSet, IntersectionWith)
{
HashSet<int> emptySet;
HashSet<int> set1 { 1, 2, 3 };
HashSet<int> set2 { 2, 3, 4 };
HashSet<int> set3 { 2, 3 };
Vector<int> sequence { 2, 3, 4 };

{
auto result = emptySet.intersectionWith(set1);
EXPECT_EQ(result, emptySet);
}

{
auto result = set1.intersectionWith(emptySet);
EXPECT_EQ(result, emptySet);
}

{
auto result = emptySet.intersectionWith(emptySet);
EXPECT_EQ(result, emptySet);
}

{
auto result = set1.intersectionWith(set1);
EXPECT_EQ(result, set1);
}

{
auto result = set1.intersectionWith(set2);
EXPECT_EQ(result, set3);
}

{
auto result = set2.intersectionWith(set1);
EXPECT_EQ(result, set3);
}

{
auto result = set1.intersectionWith(sequence);
EXPECT_EQ(result, set3);
}
}

TEST(WTF_HashSet, FormIntersection)
{
HashSet<int> emptySet;
HashSet<int> set1 { 1, 2, 3 };
HashSet<int> set2 { 2, 3, 4 };
HashSet<int> set3 { 2, 3 };
Vector<int> sequence { 2, 3, 4 };

{
auto result = emptySet;
result.formIntersection(set1);
EXPECT_EQ(result, emptySet);
}

{
auto result = set1;
result.formIntersection(emptySet);
EXPECT_EQ(result, emptySet);
}

{
auto result = emptySet;
result.formIntersection(emptySet);
EXPECT_EQ(result, emptySet);
}

{
auto result = set1;
result.formIntersection(set1);
EXPECT_EQ(result, set1);
}

{
auto result = set1;
result.formIntersection(set2);
EXPECT_EQ(result, set3);
}

{
auto result = set2;
result.formIntersection(set1);
EXPECT_EQ(result, set3);
}

{
auto result = set1;
result.formIntersection(sequence);
EXPECT_EQ(result, set3);
}
}

TEST(WTF_HashSet, SymmetricDifferenceWith)
{
HashSet<int> emptySet;
HashSet<int> set1 { 1, 2, 3 };
HashSet<int> set2 { 2, 3, 4 };
HashSet<int> set3 { 1, 4 };
Vector<int> sequence { 2, 3, 4 };

{
auto result = emptySet.symmetricDifferenceWith(set1);
EXPECT_EQ(result, set1);
}

{
auto result = set1.symmetricDifferenceWith(emptySet);
EXPECT_EQ(result, set1);
}

{
auto result = emptySet.symmetricDifferenceWith(emptySet);
EXPECT_EQ(result, emptySet);
}

{
auto result = set1.symmetricDifferenceWith(set1);
EXPECT_EQ(result, emptySet);
}

{
auto result = set1.symmetricDifferenceWith(set2);
EXPECT_EQ(result, set3);
}

{
auto result = set2.symmetricDifferenceWith(set1);
EXPECT_EQ(result, set3);
}

{
auto result = set1.symmetricDifferenceWith(sequence);
EXPECT_EQ(result, set3);
}
}

TEST(WTF_HashSet, FormSymmetricDifference)
{
HashSet<int> emptySet;
HashSet<int> set1 { 1, 2, 3 };
HashSet<int> set2 { 2, 3, 4 };
HashSet<int> set3 { 1, 4 };
Vector<int> sequence { 2, 3, 4 };

{
auto result = emptySet;
result.formSymmetricDifference(set1);
EXPECT_EQ(result, set1);
}

{
auto result = set1;
result.formSymmetricDifference(emptySet);
EXPECT_EQ(result, set1);
}

{
auto result = emptySet;
result.formSymmetricDifference(emptySet);
EXPECT_EQ(result, emptySet);
}

{
auto result = set1;
result.formSymmetricDifference(set1);
EXPECT_EQ(result, emptySet);
}

{
auto result = set1;
result.formSymmetricDifference(set2);
EXPECT_EQ(result, set3);
}

{
auto result = set2;
result.formSymmetricDifference(set1);
EXPECT_EQ(result, set3);
}

{
auto result = set1;
result.formSymmetricDifference(sequence);
EXPECT_EQ(result, set3);
}
}

} // namespace TestWebKitAPI

0 comments on commit 5e42510

Please sign in to comment.