Skip to content

Commit bf0f94a

Browse files
committed
New SetOperations and unittesting for all SetOperations
New set operations split out of D140908 as suggested, and I have added unit testing for all set operations. This adds a set_intersection, which returns the intersection instead of updating the first set like set_intersect (using a different name analogous to set_difference vs set_subtract). Also adds a variant of set_subtract that updates two additional set arguments to note which members of the subtrahend were removed from the minuend and which were not. Differential Revision: https://reviews.llvm.org/D144220
1 parent 17a90f1 commit bf0f94a

File tree

3 files changed

+299
-0
lines changed

3 files changed

+299
-0
lines changed

llvm/include/llvm/ADT/SetOperations.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,25 @@ void set_intersect(S1Ty &S1, const S2Ty &S2) {
4545
}
4646
}
4747

48+
template <class S1Ty, class S2Ty>
49+
S1Ty set_intersection_impl(const S1Ty &S1, const S2Ty &S2) {
50+
S1Ty Result;
51+
for (typename S1Ty::const_iterator SI = S1.begin(), SE = S1.end(); SI != SE;
52+
++SI)
53+
if (S2.count(*SI))
54+
Result.insert(*SI);
55+
return Result;
56+
}
57+
58+
/// set_intersection(A, B) - Return A ^ B
59+
template <class S1Ty, class S2Ty>
60+
S1Ty set_intersection(const S1Ty &S1, const S2Ty &S2) {
61+
if (S1.size() < S2.size())
62+
return set_intersection_impl(S1, S2);
63+
else
64+
return set_intersection_impl(S2, S1);
65+
}
66+
4867
/// set_difference(A, B) - Return A - B
4968
///
5069
template <class S1Ty, class S2Ty>
@@ -66,6 +85,19 @@ void set_subtract(S1Ty &S1, const S2Ty &S2) {
6685
S1.erase(*SI);
6786
}
6887

88+
/// set_subtract(A, B, C, D) - Compute A := A - B, set C to the elements of B
89+
/// removed from A (A ^ B), and D to the elements of B not found in and removed
90+
/// from A (B - A).
91+
template <class S1Ty, class S2Ty>
92+
void set_subtract(S1Ty &S1, const S2Ty &S2, S1Ty &Removed, S1Ty &Remaining) {
93+
for (typename S2Ty::const_iterator SI = S2.begin(), SE = S2.end(); SI != SE;
94+
++SI)
95+
if (S1.erase(*SI))
96+
Removed.insert(*SI);
97+
else
98+
Remaining.insert(*SI);
99+
}
100+
69101
/// set_is_subset(A, B) - Return true iff A in B
70102
///
71103
template <class S1Ty, class S2Ty>

llvm/unittests/ADT/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ add_llvm_unittest(ADTTests
6262
STLForwardCompatTest.cpp
6363
ScopeExitTest.cpp
6464
SequenceTest.cpp
65+
SetOperationsTest.cpp
6566
SetVectorTest.cpp
6667
SimpleIListTest.cpp
6768
SmallPtrSetTest.cpp
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
//===- SetOperations.cpp - Unit tests for set operations ------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/ADT/SetOperations.h"
10+
#include "gmock/gmock.h"
11+
#include "gtest/gtest.h"
12+
13+
#include <set>
14+
15+
using namespace llvm;
16+
17+
using testing::IsEmpty;
18+
19+
namespace {
20+
21+
TEST(SetOperationsTest, SetUnion) {
22+
std::set<int> Set1 = {1, 2, 3, 4};
23+
std::set<int> Set2 = {5, 6, 7, 8};
24+
// Set1 should be the union of input sets Set1 and Set2.
25+
std::set<int> ExpectedSet1 = {1, 2, 3, 4, 5, 6, 7, 8};
26+
// Set2 should not be touched.
27+
std::set<int> ExpectedSet2 = Set2;
28+
29+
set_union(Set1, Set2);
30+
EXPECT_EQ(ExpectedSet1, Set1);
31+
EXPECT_EQ(ExpectedSet2, Set2);
32+
33+
Set1.clear();
34+
Set2 = {1, 2};
35+
// Set1 should be the union of input sets Set1 and Set2, which in this case
36+
// will be Set2.
37+
ExpectedSet1 = Set2;
38+
// Set2 should not be touched.
39+
ExpectedSet2 = Set2;
40+
41+
set_union(Set1, Set2);
42+
EXPECT_EQ(ExpectedSet1, Set1);
43+
EXPECT_EQ(ExpectedSet2, Set2);
44+
}
45+
46+
TEST(SetOperationsTest, SetIntersect) {
47+
std::set<int> Set1 = {1, 2, 3, 4};
48+
std::set<int> Set2 = {3, 4, 5, 6};
49+
// Set1 should be the intersection of sets Set1 and Set2.
50+
std::set<int> ExpectedSet1 = {3, 4};
51+
// Set2 should not be touched.
52+
std::set<int> ExpectedSet2 = Set2;
53+
54+
set_intersect(Set1, Set2);
55+
EXPECT_EQ(ExpectedSet1, Set1);
56+
EXPECT_EQ(ExpectedSet2, Set2);
57+
58+
Set1 = {1, 2, 3, 4};
59+
Set2 = {5, 6};
60+
// Set2 should not be touched.
61+
ExpectedSet2 = Set2;
62+
63+
set_intersect(Set1, Set2);
64+
// Set1 should be the intersection of sets Set1 and Set2, which
65+
// is empty as they are non-overlapping.
66+
EXPECT_THAT(Set1, IsEmpty());
67+
EXPECT_EQ(ExpectedSet2, Set2);
68+
}
69+
70+
TEST(SetOperationsTest, SetIntersection) {
71+
std::set<int> Set1 = {1, 2, 3, 4};
72+
std::set<int> Set2 = {3, 4, 5, 6};
73+
std::set<int> Result;
74+
// Result should be the intersection of sets Set1 and Set2.
75+
std::set<int> ExpectedResult = {3, 4};
76+
// Set1 and Set2 should not be touched.
77+
std::set<int> ExpectedSet1 = Set1;
78+
std::set<int> ExpectedSet2 = Set2;
79+
80+
Result = set_intersection(Set1, Set2);
81+
EXPECT_EQ(ExpectedResult, Result);
82+
EXPECT_EQ(ExpectedSet1, Set1);
83+
EXPECT_EQ(ExpectedSet2, Set2);
84+
85+
Set1 = {1, 2, 3, 4};
86+
Set2 = {5, 6};
87+
// Set1 and Set2 should not be touched.
88+
ExpectedSet1 = Set1;
89+
ExpectedSet2 = Set2;
90+
91+
Result = set_intersection(Set1, Set2);
92+
// Result should be the intersection of sets Set1 and Set2, which
93+
// is empty as they are non-overlapping.
94+
EXPECT_THAT(Result, IsEmpty());
95+
EXPECT_EQ(ExpectedSet1, Set1);
96+
EXPECT_EQ(ExpectedSet2, Set2);
97+
98+
Set1 = {5, 6};
99+
Set2 = {1, 2, 3, 4};
100+
// Set1 and Set2 should not be touched.
101+
ExpectedSet1 = Set1;
102+
ExpectedSet2 = Set2;
103+
104+
Result = set_intersection(Set1, Set2);
105+
// Result should be the intersection of sets Set1 and Set2, which
106+
// is empty as they are non-overlapping. Test this again with the input sets
107+
// reversed, since the code takes a different path depending on which input
108+
// set is smaller.
109+
EXPECT_THAT(Result, IsEmpty());
110+
EXPECT_EQ(ExpectedSet1, Set1);
111+
EXPECT_EQ(ExpectedSet2, Set2);
112+
}
113+
114+
TEST(SetOperationsTest, SetDifference) {
115+
std::set<int> Set1 = {1, 2, 3, 4};
116+
std::set<int> Set2 = {3, 4, 5, 6};
117+
std::set<int> Result;
118+
// Result should be Set1 - Set2, leaving only {1, 2}.
119+
std::set<int> ExpectedResult = {1, 2};
120+
// Set1 and Set2 should not be touched.
121+
std::set<int> ExpectedSet1 = Set1;
122+
std::set<int> ExpectedSet2 = Set2;
123+
124+
Result = set_difference(Set1, Set2);
125+
EXPECT_EQ(ExpectedResult, Result);
126+
EXPECT_EQ(ExpectedSet1, Set1);
127+
EXPECT_EQ(ExpectedSet2, Set2);
128+
129+
Set1 = {1, 2, 3, 4};
130+
Set2 = {1, 2, 3, 4};
131+
// Set1 and Set2 should not be touched.
132+
ExpectedSet1 = Set1;
133+
ExpectedSet2 = Set2;
134+
135+
Result = set_difference(Set1, Set2);
136+
// Result should be Set1 - Set2, which should be empty.
137+
EXPECT_THAT(Result, IsEmpty());
138+
EXPECT_EQ(ExpectedSet1, Set1);
139+
EXPECT_EQ(ExpectedSet2, Set2);
140+
141+
Set1 = {1, 2, 3, 4};
142+
Set2 = {5, 6};
143+
// Result should be Set1 - Set2, which should be Set1 as they are
144+
// non-overlapping.
145+
ExpectedResult = Set1;
146+
// Set1 and Set2 should not be touched.
147+
ExpectedSet1 = Set1;
148+
ExpectedSet2 = Set2;
149+
150+
Result = set_difference(Set1, Set2);
151+
EXPECT_EQ(ExpectedResult, Result);
152+
EXPECT_EQ(ExpectedSet1, Set1);
153+
EXPECT_EQ(ExpectedSet2, Set2);
154+
}
155+
156+
TEST(SetOperationsTest, SetSubtract) {
157+
std::set<int> Set1 = {1, 2, 3, 4};
158+
std::set<int> Set2 = {3, 4, 5, 6};
159+
// Set1 should get Set1 - Set2, leaving only {1, 2}.
160+
std::set<int> ExpectedSet1 = {1, 2};
161+
// Set2 should not be touched.
162+
std::set<int> ExpectedSet2 = Set2;
163+
164+
set_subtract(Set1, Set2);
165+
EXPECT_EQ(ExpectedSet1, Set1);
166+
EXPECT_EQ(ExpectedSet2, Set2);
167+
168+
Set1 = {1, 2, 3, 4};
169+
Set2 = {1, 2, 3, 4};
170+
// Set2 should not be touched.
171+
ExpectedSet2 = Set2;
172+
173+
set_subtract(Set1, Set2);
174+
// Set1 should get Set1 - Set2, which should be empty.
175+
EXPECT_THAT(Set1, IsEmpty());
176+
EXPECT_EQ(ExpectedSet2, Set2);
177+
178+
Set1 = {1, 2, 3, 4};
179+
Set2 = {5, 6};
180+
// Set1 should get Set1 - Set2, which should be Set1 as they are
181+
// non-overlapping.
182+
ExpectedSet1 = Set1;
183+
// Set2 should not be touched.
184+
ExpectedSet2 = Set2;
185+
186+
set_subtract(Set1, Set2);
187+
EXPECT_EQ(ExpectedSet1, Set1);
188+
EXPECT_EQ(ExpectedSet2, Set2);
189+
}
190+
191+
TEST(SetOperationsTest, SetSubtractRemovedRemaining) {
192+
std::set<int> Removed, Remaining;
193+
194+
std::set<int> Set1 = {1, 2, 3, 4};
195+
std::set<int> Set2 = {3, 4, 5, 6};
196+
// Set1 should get Set1 - Set2, leaving only {1, 2}.
197+
std::set<int> ExpectedSet1 = {1, 2};
198+
// Set2 should not be touched.
199+
std::set<int> ExpectedSet2 = Set2;
200+
// We should get back that {3, 4} from Set2 were removed from Set1, and {5, 6}
201+
// were not removed from Set1.
202+
std::set<int> ExpectedRemoved = {3, 4};
203+
std::set<int> ExpectedRemaining = {5, 6};
204+
205+
set_subtract(Set1, Set2, Removed, Remaining);
206+
EXPECT_EQ(ExpectedSet1, Set1);
207+
EXPECT_EQ(ExpectedSet2, Set2);
208+
EXPECT_EQ(ExpectedRemoved, Removed);
209+
EXPECT_EQ(ExpectedRemaining, Remaining);
210+
211+
Set1 = {1, 2, 3, 4};
212+
Set2 = {1, 2, 3, 4};
213+
Removed.clear();
214+
Remaining.clear();
215+
// Set2 should not be touched.
216+
ExpectedSet2 = Set2;
217+
// Set should get back that all of Set2 was removed from Set1, and nothing
218+
// left in Set2 was not removed from Set1.
219+
ExpectedRemoved = Set2;
220+
221+
set_subtract(Set1, Set2, Removed, Remaining);
222+
// Set1 should get Set1 - Set2, which should be empty.
223+
EXPECT_THAT(Set1, IsEmpty());
224+
EXPECT_EQ(ExpectedSet2, Set2);
225+
EXPECT_EQ(ExpectedRemoved, Removed);
226+
EXPECT_THAT(Remaining, IsEmpty());
227+
228+
Set1 = {1, 2, 3, 4};
229+
Set2 = {5, 6};
230+
Removed.clear();
231+
Remaining.clear();
232+
// Set1 should get Set1 - Set2, which should be Set1 as they are
233+
// non-overlapping.
234+
ExpectedSet1 = {1, 2, 3, 4};
235+
// Set2 should not be touched.
236+
ExpectedSet2 = Set2;
237+
// Set should get back that none of Set2 was removed from Set1, and all
238+
// of Set2 was not removed from Set1.
239+
ExpectedRemaining = Set2;
240+
241+
set_subtract(Set1, Set2, Removed, Remaining);
242+
EXPECT_EQ(ExpectedSet1, Set1);
243+
EXPECT_EQ(ExpectedSet2, Set2);
244+
EXPECT_THAT(Removed, IsEmpty());
245+
EXPECT_EQ(ExpectedRemaining, Remaining);
246+
}
247+
248+
TEST(SetOperationsTest, SetIsSubset) {
249+
std::set<int> Set1 = {1, 2, 3, 4};
250+
std::set<int> Set2 = {3, 4};
251+
EXPECT_FALSE(set_is_subset(Set1, Set2));
252+
253+
Set1 = {1, 2, 3, 4};
254+
Set2 = {1, 2, 3, 4};
255+
EXPECT_TRUE(set_is_subset(Set1, Set2));
256+
257+
Set1 = {1, 2};
258+
Set2 = {1, 2, 3, 4};
259+
EXPECT_TRUE(set_is_subset(Set1, Set2));
260+
261+
Set1 = {1, 2};
262+
Set2 = {3, 4};
263+
EXPECT_FALSE(set_is_subset(Set1, Set2));
264+
}
265+
266+
} // namespace

0 commit comments

Comments
 (0)