diff --git a/llvm/include/llvm/ADT/SetOperations.h b/llvm/include/llvm/ADT/SetOperations.h --- a/llvm/include/llvm/ADT/SetOperations.h +++ b/llvm/include/llvm/ADT/SetOperations.h @@ -45,6 +45,25 @@ } } +template +S1Ty set_intersection_impl(const S1Ty &S1, const S2Ty &S2) { + S1Ty Result; + for (typename S1Ty::const_iterator SI = S1.begin(), SE = S1.end(); SI != SE; + ++SI) + if (S2.count(*SI)) + Result.insert(*SI); + return Result; +} + +/// set_intersection(A, B) - Return A ^ B +template +S1Ty set_intersection(const S1Ty &S1, const S2Ty &S2) { + if (S1.size() < S2.size()) + return set_intersection_impl(S1, S2); + else + return set_intersection_impl(S2, S1); +} + /// set_difference(A, B) - Return A - B /// template @@ -66,6 +85,20 @@ S1.erase(*SI); } +/// set_subtract(A, B, C, D) - Compute A := A - B, set C to the elements of B +/// removed from A (A ^ B), and D to the elements of B not found in and removed +/// from A (B - A). +/// +template +void set_subtract(S1Ty &S1, const S2Ty &S2, S1Ty &Removed, S1Ty &Remaining) { + for (typename S2Ty::const_iterator SI = S2.begin(), SE = S2.end(); SI != SE; + ++SI) + if (S1.erase(*SI)) + Removed.insert(*SI); + else + Remaining.insert(*SI); +} + /// set_is_subset(A, B) - Return true iff A in B /// template diff --git a/llvm/unittests/ADT/CMakeLists.txt b/llvm/unittests/ADT/CMakeLists.txt --- a/llvm/unittests/ADT/CMakeLists.txt +++ b/llvm/unittests/ADT/CMakeLists.txt @@ -62,6 +62,7 @@ STLForwardCompatTest.cpp ScopeExitTest.cpp SequenceTest.cpp + SetOperationsTest.cpp SetVectorTest.cpp SimpleIListTest.cpp SmallPtrSetTest.cpp diff --git a/llvm/unittests/ADT/SetOperationsTest.cpp b/llvm/unittests/ADT/SetOperationsTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/ADT/SetOperationsTest.cpp @@ -0,0 +1,272 @@ +//===- SetOperations.cpp - Unit tests for set operations ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SetOperations.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include + +using namespace llvm; + +namespace { + +TEST(SetOperationsTest, SetUnion) { + std::set Set1 = {1, 2, 3, 4}; + std::set Set2 = {5, 6, 7, 8}; + // Set1 should be the union of input sets Set1 and Set2. + std::set ExpectedSet1 = {1, 2, 3, 4, 5, 6, 7, 8}; + // Set2 should not be touched. + std::set ExpectedSet2 = Set2; + + set_union(Set1, Set2); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); + + Set1.clear(); + Set2 = {1, 2}; + // Set1 should be the union of input sets Set1 and Set2, which in this case + // will be Set2. + ExpectedSet1 = Set2; + // Set2 should not be touched. + ExpectedSet2 = Set2; + + set_union(Set1, Set2); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); +} + +TEST(SetOperationsTest, SetIntersect) { + std::set Set1 = {1, 2, 3, 4}; + std::set Set2 = {3, 4, 5, 6}; + // Set1 should be the intersection of sets Set1 and Set2. + std::set ExpectedSet1 = {3, 4}; + // Set2 should not be touched. + std::set ExpectedSet2 = Set2; + + set_intersect(Set1, Set2); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); + + Set1 = {1, 2, 3, 4}; + Set2 = {5, 6}; + // Set1 should be the intersection of sets Set1 and Set2, which + // is empty as they are non-overlapping. + ExpectedSet1.clear(); + // Set2 should not be touched. + ExpectedSet2 = Set2; + + set_intersect(Set1, Set2); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); +} + +TEST(SetOperationsTest, SetIntersection) { + std::set Set1 = {1, 2, 3, 4}; + std::set Set2 = {3, 4, 5, 6}; + std::set Result; + // Result should be the intersection of sets Set1 and Set2. + std::set ExpectedResult = {3, 4}; + // Set1 and Set2 should not be touched. + std::set ExpectedSet1 = Set1; + std::set ExpectedSet2 = Set2; + + Result = set_intersection(Set1, Set2); + EXPECT_EQ(ExpectedResult, Result); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); + + Set1 = {1, 2, 3, 4}; + Set2 = {5, 6}; + // Result should be the intersection of sets Set1 and Set2, which + // is empty as they are non-overlapping. + ExpectedResult.clear(); + // Set1 and Set2 should not be touched. + ExpectedSet1 = Set1; + ExpectedSet2 = Set2; + + Result = set_intersection(Set1, Set2); + EXPECT_EQ(ExpectedResult, Result); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); + + Set1 = {5, 6}; + Set2 = {1, 2, 3, 4}; + // Result should be the intersection of sets Set1 and Set2, which + // is empty as they are non-overlapping. Test this again with the input sets + // reversed, since the code takes a different path depending on which input + // set is smaller. + ExpectedResult.clear(); + // Set1 and Set2 should not be touched. + ExpectedSet1 = Set1; + ExpectedSet2 = Set2; + + Result = set_intersection(Set1, Set2); + EXPECT_EQ(ExpectedResult, Result); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); +} + +TEST(SetOperationsTest, SetDifference) { + std::set Set1 = {1, 2, 3, 4}; + std::set Set2 = {3, 4, 5, 6}; + std::set Result; + // Result should be Set1 - Set2, leaving only {1, 2}. + std::set ExpectedResult = {1, 2}; + // Set1 and Set2 should not be touched. + std::set ExpectedSet1 = Set1; + std::set ExpectedSet2 = Set2; + + Result = set_difference(Set1, Set2); + EXPECT_EQ(ExpectedResult, Result); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); + + Set1 = {1, 2, 3, 4}; + Set2 = {1, 2, 3, 4}; + // Result should be Set1 - Set2, which should be empty. + ExpectedResult.clear(); + // Set1 and Set2 should not be touched. + ExpectedSet1 = Set1; + ExpectedSet2 = Set2; + + Result = set_difference(Set1, Set2); + EXPECT_EQ(ExpectedResult, Result); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); + + Set1 = {1, 2, 3, 4}; + Set2 = {5, 6}; + // Result should be Set1 - Set2, which should be Set1 as they are + // non-overlapping. + ExpectedResult = Set1; + // Set1 and Set2 should not be touched. + ExpectedSet1 = Set1; + ExpectedSet2 = Set2; + + Result = set_difference(Set1, Set2); + EXPECT_EQ(ExpectedResult, Result); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); +} + +TEST(SetOperationsTest, SetSubtract) { + std::set Set1 = {1, 2, 3, 4}; + std::set Set2 = {3, 4, 5, 6}; + // Set1 should get Set1 - Set2, leaving only {1, 2}. + std::set ExpectedSet1 = {1, 2}; + // Set2 should not be touched. + std::set ExpectedSet2 = Set2; + + set_subtract(Set1, Set2); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); + + Set1 = {1, 2, 3, 4}; + Set2 = {1, 2, 3, 4}; + // Set1 should get Set1 - Set2, which should be empty. + ExpectedSet1.clear(); + // Set2 should not be touched. + ExpectedSet2 = Set2; + + set_subtract(Set1, Set2); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); + + Set1 = {1, 2, 3, 4}; + Set2 = {5, 6}; + // Set1 should get Set1 - Set2, which should be Set1 as they are + // non-overlapping. + ExpectedSet1 = Set1; + // Set2 should not be touched. + ExpectedSet2 = Set2; + + set_subtract(Set1, Set2); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); +} + +TEST(SetOperationsTest, SetSubtractRemovedRemaining) { + std::set Removed, Remaining; + + std::set Set1 = {1, 2, 3, 4}; + std::set Set2 = {3, 4, 5, 6}; + // Set1 should get Set1 - Set2, leaving only {1, 2}. + std::set ExpectedSet1 = {1, 2}; + // Set2 should not be touched. + std::set ExpectedSet2 = Set2; + // We should get back that {3, 4} from Set2 were removed from Set1, and {5, 6} + // were not removed from Set1. + std::set ExpectedRemoved = {3, 4}; + std::set ExpectedRemaining = {5, 6}; + + set_subtract(Set1, Set2, Removed, Remaining); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); + EXPECT_EQ(ExpectedRemoved, Removed); + EXPECT_EQ(ExpectedRemaining, Remaining); + + Set1 = {1, 2, 3, 4}; + Set2 = {1, 2, 3, 4}; + Removed.clear(); + Remaining.clear(); + // Set1 should get Set1 - Set2, which should be empty. + ExpectedSet1.clear(); + // Set2 should not be touched. + ExpectedSet2 = Set2; + // Set should get back that all of Set2 was removed from Set1, and nothing + // left in Set2 was not removed from Set1. + ExpectedRemoved = Set2; + ExpectedRemaining.clear(); + + set_subtract(Set1, Set2, Removed, Remaining); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); + EXPECT_EQ(ExpectedRemoved, Removed); + EXPECT_EQ(ExpectedRemaining, Remaining); + + Set1 = {1, 2, 3, 4}; + Set2 = {5, 6}; + Removed.clear(); + Remaining.clear(); + // Set1 should get Set1 - Set2, which should be Set1 as they are + // non-overlapping. + ExpectedSet1 = {1, 2, 3, 4}; + // Set2 should not be touched. + ExpectedSet2 = Set2; + // Set should get back that none of Set2 was removed from Set1, and all + // of Set2 was not removed from Set1. + ExpectedRemoved.clear(); + ExpectedRemaining = Set2; + + set_subtract(Set1, Set2, Removed, Remaining); + EXPECT_EQ(ExpectedSet1, Set1); + EXPECT_EQ(ExpectedSet2, Set2); + EXPECT_EQ(ExpectedRemoved, Removed); + EXPECT_EQ(ExpectedRemaining, Remaining); +} + +TEST(SetOperationsTest, SetIsSubset) { + std::set Set1 = {1, 2, 3, 4}; + std::set Set2 = {3, 4}; + EXPECT_FALSE(set_is_subset(Set1, Set2)); + + Set1 = {1, 2, 3, 4}; + Set2 = {1, 2, 3, 4}; + EXPECT_TRUE(set_is_subset(Set1, Set2)); + + Set1 = {1, 2}; + Set2 = {1, 2, 3, 4}; + EXPECT_TRUE(set_is_subset(Set1, Set2)); + + Set1 = {1, 2}; + Set2 = {3, 4}; + EXPECT_FALSE(set_is_subset(Set1, Set2)); +} + +} // namespace