1 | //===- SetTest.cpp - Tests for PresburgerSet ------------------------------===// |
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 | // This file contains tests for PresburgerSet. The tests for union, |
10 | // intersection, subtract, and complement work by computing the operation on |
11 | // two sets and checking, for a set of points, that the resulting set contains |
12 | // the point iff the result is supposed to contain it. The test for isEqual just |
13 | // checks if the result for two sets matches the expected result. |
14 | // |
15 | //===----------------------------------------------------------------------===// |
16 | |
17 | #include "Parser.h" |
18 | #include "Utils.h" |
19 | #include "mlir/Analysis/Presburger/PresburgerRelation.h" |
20 | #include "mlir/IR/MLIRContext.h" |
21 | |
22 | #include <gmock/gmock.h> |
23 | #include <gtest/gtest.h> |
24 | #include <optional> |
25 | |
26 | using namespace mlir; |
27 | using namespace presburger; |
28 | |
29 | /// Compute the union of s and t, and check that each of the given points |
30 | /// belongs to the union iff it belongs to at least one of s and t. |
31 | static void testUnionAtPoints(const PresburgerSet &s, const PresburgerSet &t, |
32 | ArrayRef<SmallVector<int64_t, 4>> points) { |
33 | PresburgerSet unionSet = s.unionSet(set: t); |
34 | for (const SmallVector<int64_t, 4> &point : points) { |
35 | bool inS = s.containsPoint(point); |
36 | bool inT = t.containsPoint(point); |
37 | bool inUnion = unionSet.containsPoint(point); |
38 | EXPECT_EQ(inUnion, inS || inT); |
39 | } |
40 | } |
41 | |
42 | /// Compute the intersection of s and t, and check that each of the given points |
43 | /// belongs to the intersection iff it belongs to both s and t. |
44 | static void testIntersectAtPoints(const PresburgerSet &s, |
45 | const PresburgerSet &t, |
46 | ArrayRef<SmallVector<int64_t, 4>> points) { |
47 | PresburgerSet intersection = s.intersect(set: t); |
48 | for (const SmallVector<int64_t, 4> &point : points) { |
49 | bool inS = s.containsPoint(point); |
50 | bool inT = t.containsPoint(point); |
51 | bool inIntersection = intersection.containsPoint(point); |
52 | EXPECT_EQ(inIntersection, inS && inT); |
53 | } |
54 | } |
55 | |
56 | /// Compute the set difference s \ t, and check that each of the given points |
57 | /// belongs to the difference iff it belongs to s and does not belong to t. |
58 | static void testSubtractAtPoints(const PresburgerSet &s, const PresburgerSet &t, |
59 | ArrayRef<SmallVector<int64_t, 4>> points) { |
60 | PresburgerSet diff = s.subtract(set: t); |
61 | for (const SmallVector<int64_t, 4> &point : points) { |
62 | bool inS = s.containsPoint(point); |
63 | bool inT = t.containsPoint(point); |
64 | bool inDiff = diff.containsPoint(point); |
65 | if (inT) |
66 | EXPECT_FALSE(inDiff); |
67 | else |
68 | EXPECT_EQ(inDiff, inS); |
69 | } |
70 | } |
71 | |
72 | /// Compute the complement of s, and check that each of the given points |
73 | /// belongs to the complement iff it does not belong to s. |
74 | static void testComplementAtPoints(const PresburgerSet &s, |
75 | ArrayRef<SmallVector<int64_t, 4>> points) { |
76 | PresburgerSet complement = s.complement(); |
77 | complement.complement(); |
78 | for (const SmallVector<int64_t, 4> &point : points) { |
79 | bool inS = s.containsPoint(point); |
80 | bool inComplement = complement.containsPoint(point); |
81 | if (inS) |
82 | EXPECT_FALSE(inComplement); |
83 | else |
84 | EXPECT_TRUE(inComplement); |
85 | } |
86 | } |
87 | |
88 | /// Construct a PresburgerSet having `numDims` dimensions and no symbols from |
89 | /// the given list of IntegerPolyhedron. Each Poly in `polys` should also have |
90 | /// `numDims` dimensions and no symbols, although it can have any number of |
91 | /// local ids. |
92 | static PresburgerSet makeSetFromPoly(unsigned numDims, |
93 | ArrayRef<IntegerPolyhedron> polys) { |
94 | PresburgerSet set = |
95 | PresburgerSet::getEmpty(space: PresburgerSpace::getSetSpace(numDims)); |
96 | for (const IntegerPolyhedron &poly : polys) |
97 | set.unionInPlace(disjunct: poly); |
98 | return set; |
99 | } |
100 | |
101 | TEST(SetTest, containsPoint) { |
102 | PresburgerSet setA = parsePresburgerSet( |
103 | strs: {"(x) : (x - 2 >= 0, -x + 8 >= 0)" , "(x) : (x - 10 >= 0, -x + 20 >= 0)" }); |
104 | for (unsigned x = 0; x <= 21; ++x) { |
105 | if ((2 <= x && x <= 8) || (10 <= x && x <= 20)) |
106 | EXPECT_TRUE(setA.containsPoint({x})); |
107 | else |
108 | EXPECT_FALSE(setA.containsPoint({x})); |
109 | } |
110 | |
111 | // A parallelogram with vertices {(3, 1), (10, -6), (24, 8), (17, 15)} union |
112 | // a square with opposite corners (2, 2) and (10, 10). |
113 | PresburgerSet setB = parsePresburgerSet( |
114 | strs: {"(x,y) : (x + y - 4 >= 0, -x - y + 32 >= 0, " |
115 | "x - y - 2 >= 0, -x + y + 16 >= 0)" , |
116 | "(x,y) : (x - 2 >= 0, y - 2 >= 0, -x + 10 >= 0, -y + 10 >= 0)" }); |
117 | |
118 | for (unsigned x = 1; x <= 25; ++x) { |
119 | for (unsigned y = -6; y <= 16; ++y) { |
120 | if (4 <= x + y && x + y <= 32 && 2 <= x - y && x - y <= 16) |
121 | EXPECT_TRUE(setB.containsPoint({x, y})); |
122 | else if (2 <= x && x <= 10 && 2 <= y && y <= 10) |
123 | EXPECT_TRUE(setB.containsPoint({x, y})); |
124 | else |
125 | EXPECT_FALSE(setB.containsPoint({x, y})); |
126 | } |
127 | } |
128 | |
129 | // The PresburgerSet has only one id, x, so we supply one value. |
130 | EXPECT_TRUE( |
131 | PresburgerSet(parseIntegerPolyhedron("(x) : (x - 2*(x floordiv 2) == 0)" )) |
132 | .containsPoint({0})); |
133 | } |
134 | |
135 | TEST(SetTest, Union) { |
136 | PresburgerSet set = parsePresburgerSet( |
137 | strs: {"(x) : (x - 2 >= 0, -x + 8 >= 0)" , "(x) : (x - 10 >= 0, -x + 20 >= 0)" }); |
138 | |
139 | // Universe union set. |
140 | testUnionAtPoints(s: PresburgerSet::getUniverse(space: PresburgerSpace::getSetSpace(numDims: 1)), |
141 | t: set, points: {{1}, {2}, {8}, {9}, {10}, {20}, {21}}); |
142 | |
143 | // empty set union set. |
144 | testUnionAtPoints(s: PresburgerSet::getEmpty(space: PresburgerSpace::getSetSpace(numDims: 1)), |
145 | t: set, points: {{1}, {2}, {8}, {9}, {10}, {20}, {21}}); |
146 | |
147 | // empty set union Universe. |
148 | testUnionAtPoints(s: PresburgerSet::getEmpty(space: PresburgerSpace::getSetSpace(numDims: 1)), |
149 | t: PresburgerSet::getUniverse(space: PresburgerSpace::getSetSpace(numDims: 1)), |
150 | points: {{1}, {2}, {0}, {-1}}); |
151 | |
152 | // Universe union empty set. |
153 | testUnionAtPoints(s: PresburgerSet::getUniverse(space: PresburgerSpace::getSetSpace(numDims: 1)), |
154 | t: PresburgerSet::getEmpty(space: PresburgerSpace::getSetSpace(numDims: 1)), |
155 | points: {{1}, {2}, {0}, {-1}}); |
156 | |
157 | // empty set union empty set. |
158 | testUnionAtPoints(s: PresburgerSet::getEmpty(space: PresburgerSpace::getSetSpace(numDims: (1))), |
159 | t: PresburgerSet::getEmpty(space: PresburgerSpace::getSetSpace(numDims: (1))), |
160 | points: {{1}, {2}, {0}, {-1}}); |
161 | } |
162 | |
163 | TEST(SetTest, Intersect) { |
164 | PresburgerSet set = parsePresburgerSet( |
165 | strs: {"(x) : (x - 2 >= 0, -x + 8 >= 0)" , "(x) : (x - 10 >= 0, -x + 20 >= 0)" }); |
166 | |
167 | // Universe intersection set. |
168 | testIntersectAtPoints( |
169 | s: PresburgerSet::getUniverse(space: PresburgerSpace::getSetSpace(numDims: (1))), t: set, |
170 | points: {{1}, {2}, {8}, {9}, {10}, {20}, {21}}); |
171 | |
172 | // empty set intersection set. |
173 | testIntersectAtPoints( |
174 | s: PresburgerSet::getEmpty(space: PresburgerSpace::getSetSpace(numDims: (1))), t: set, |
175 | points: {{1}, {2}, {8}, {9}, {10}, {20}, {21}}); |
176 | |
177 | // empty set intersection Universe. |
178 | testIntersectAtPoints( |
179 | s: PresburgerSet::getEmpty(space: PresburgerSpace::getSetSpace(numDims: (1))), |
180 | t: PresburgerSet::getUniverse(space: PresburgerSpace::getSetSpace(numDims: (1))), |
181 | points: {{1}, {2}, {0}, {-1}}); |
182 | |
183 | // Universe intersection empty set. |
184 | testIntersectAtPoints( |
185 | s: PresburgerSet::getUniverse(space: PresburgerSpace::getSetSpace(numDims: (1))), |
186 | t: PresburgerSet::getEmpty(space: PresburgerSpace::getSetSpace(numDims: (1))), |
187 | points: {{1}, {2}, {0}, {-1}}); |
188 | |
189 | // Universe intersection Universe. |
190 | testIntersectAtPoints( |
191 | s: PresburgerSet::getUniverse(space: PresburgerSpace::getSetSpace(numDims: (1))), |
192 | t: PresburgerSet::getUniverse(space: PresburgerSpace::getSetSpace(numDims: (1))), |
193 | points: {{1}, {2}, {0}, {-1}}); |
194 | } |
195 | |
196 | TEST(SetTest, Subtract) { |
197 | // The interval [2, 8] minus the interval [10, 20]. |
198 | testSubtractAtPoints( |
199 | s: parsePresburgerSet(strs: {"(x) : (x - 2 >= 0, -x + 8 >= 0)" }), |
200 | t: parsePresburgerSet(strs: {"(x) : (x - 10 >= 0, -x + 20 >= 0)" }), |
201 | points: {{1}, {2}, {8}, {9}, {10}, {20}, {21}}); |
202 | |
203 | // Universe minus [2, 8] U [10, 20] |
204 | testSubtractAtPoints( |
205 | s: parsePresburgerSet(strs: {"(x) : ()" }), |
206 | t: parsePresburgerSet(strs: {"(x) : (x - 2 >= 0, -x + 8 >= 0)" , |
207 | "(x) : (x - 10 >= 0, -x + 20 >= 0)" }), |
208 | points: {{1}, {2}, {8}, {9}, {10}, {20}, {21}}); |
209 | |
210 | // ((-infinity, 0] U [3, 4] U [6, 7]) - ([2, 3] U [5, 6]) |
211 | testSubtractAtPoints( |
212 | s: parsePresburgerSet(strs: {"(x) : (-x >= 0)" , "(x) : (x - 3 >= 0, -x + 4 >= 0)" , |
213 | "(x) : (x - 6 >= 0, -x + 7 >= 0)" }), |
214 | t: parsePresburgerSet(strs: {"(x) : (x - 2 >= 0, -x + 3 >= 0)" , |
215 | "(x) : (x - 5 >= 0, -x + 6 >= 0)" }), |
216 | points: {{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}}); |
217 | |
218 | // Expected result is {[x, y] : x > y}, i.e., {[x, y] : x >= y + 1}. |
219 | testSubtractAtPoints(s: parsePresburgerSet(strs: {"(x, y) : (x - y >= 0)" }), |
220 | t: parsePresburgerSet(strs: {"(x, y) : (x + y >= 0)" }), |
221 | points: {{0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}}); |
222 | |
223 | // A rectangle with corners at (2, 2) and (10, 10), minus |
224 | // a rectangle with corners at (5, -10) and (7, 100). |
225 | // This splits the former rectangle into two halves, (2, 2) to (5, 10) and |
226 | // (7, 2) to (10, 10). |
227 | testSubtractAtPoints( |
228 | s: parsePresburgerSet(strs: { |
229 | "(x, y) : (x - 2 >= 0, y - 2 >= 0, -x + 10 >= 0, -y + 10 >= 0)" , |
230 | }), |
231 | t: parsePresburgerSet(strs: { |
232 | "(x, y) : (x - 5 >= 0, y + 10 >= 0, -x + 7 >= 0, -y + 100 >= 0)" , |
233 | }), |
234 | points: {{1, 2}, {2, 2}, {4, 2}, {5, 2}, {7, 2}, {8, 2}, {11, 2}, |
235 | {1, 1}, {2, 1}, {4, 1}, {5, 1}, {7, 1}, {8, 1}, {11, 1}, |
236 | {1, 10}, {2, 10}, {4, 10}, {5, 10}, {7, 10}, {8, 10}, {11, 10}, |
237 | {1, 11}, {2, 11}, {4, 11}, {5, 11}, {7, 11}, {8, 11}, {11, 11}}); |
238 | |
239 | // A rectangle with corners at (2, 2) and (10, 10), minus |
240 | // a rectangle with corners at (5, 4) and (7, 8). |
241 | // This creates a hole in the middle of the former rectangle, and the |
242 | // resulting set can be represented as a union of four rectangles. |
243 | testSubtractAtPoints( |
244 | s: parsePresburgerSet( |
245 | strs: {"(x, y) : (x - 2 >= 0, y -2 >= 0, -x + 10 >= 0, -y + 10 >= 0)" }), |
246 | t: parsePresburgerSet(strs: { |
247 | "(x, y) : (x - 5 >= 0, y - 4 >= 0, -x + 7 >= 0, -y + 8 >= 0)" , |
248 | }), |
249 | points: {{1, 1}, |
250 | {2, 2}, |
251 | {10, 10}, |
252 | {11, 11}, |
253 | {5, 4}, |
254 | {7, 4}, |
255 | {5, 8}, |
256 | {7, 8}, |
257 | {4, 4}, |
258 | {8, 4}, |
259 | {4, 8}, |
260 | {8, 8}}); |
261 | |
262 | // The second set is a superset of the first one, since on the line x + y = 0, |
263 | // y <= 1 is equivalent to x >= -1. So the result is empty. |
264 | testSubtractAtPoints( |
265 | s: parsePresburgerSet(strs: {"(x, y) : (x >= 0, x + y == 0)" }), |
266 | t: parsePresburgerSet(strs: {"(x, y) : (-y + 1 >= 0, x + y == 0)" }), |
267 | points: {{0, 0}, |
268 | {1, -1}, |
269 | {2, -2}, |
270 | {-1, 1}, |
271 | {-2, 2}, |
272 | {1, 1}, |
273 | {-1, -1}, |
274 | {-1, 1}, |
275 | {1, -1}}); |
276 | |
277 | // The result should be {0} U {2}. |
278 | testSubtractAtPoints(s: parsePresburgerSet(strs: {"(x) : (x >= 0, -x + 2 >= 0)" }), |
279 | t: parsePresburgerSet(strs: {"(x) : (x - 1 == 0)" }), |
280 | points: {{-1}, {0}, {1}, {2}, {3}}); |
281 | |
282 | // Sets with lots of redundant inequalities to test the redundancy heuristic. |
283 | // (the heuristic is for the subtrahend, the second set which is the one being |
284 | // subtracted) |
285 | |
286 | // A parallelogram with vertices {(3, 1), (10, -6), (24, 8), (17, 15)} minus |
287 | // a triangle with vertices {(2, 2), (10, 2), (10, 10)}. |
288 | testSubtractAtPoints( |
289 | s: parsePresburgerSet(strs: { |
290 | "(x, y) : (x + y - 4 >= 0, -x - y + 32 >= 0, x - y - 2 >= 0, " |
291 | "-x + y + 16 >= 0)" , |
292 | }), |
293 | t: parsePresburgerSet( |
294 | strs: {"(x, y) : (x - 2 >= 0, y - 2 >= 0, -x + 10 >= 0, " |
295 | "-y + 10 >= 0, x + y - 2 >= 0, -x - y + 30 >= 0, x - y >= 0, " |
296 | "-x + y + 10 >= 0)" }), |
297 | points: {{1, 2}, {2, 2}, {3, 2}, {4, 2}, {1, 1}, {2, 1}, {3, 1}, |
298 | {4, 1}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {10, 2}, {11, 2}, |
299 | {10, 1}, {10, 10}, {10, 11}, {10, 9}, {11, 10}, {10, -6}, {11, -6}, |
300 | {24, 8}, {24, 7}, {17, 15}, {16, 15}}); |
301 | |
302 | // ((-infinity, -5] U [3, 3] U [4, 4] U [5, 5]) - ([-2, -10] U [3, 4] U [6, |
303 | // 7]) |
304 | testSubtractAtPoints( |
305 | s: parsePresburgerSet(strs: {"(x) : (-x - 5 >= 0)" , "(x) : (x - 3 == 0)" , |
306 | "(x) : (x - 4 == 0)" , "(x) : (x - 5 == 0)" }), |
307 | t: parsePresburgerSet( |
308 | strs: {"(x) : (-x - 2 >= 0, x - 10 >= 0, -x >= 0, -x + 10 >= 0, " |
309 | "x - 100 >= 0, x - 50 >= 0)" , |
310 | "(x) : (x - 3 >= 0, -x + 4 >= 0, x + 1 >= 0, " |
311 | "x + 7 >= 0, -x + 10 >= 0)" , |
312 | "(x) : (x - 6 >= 0, -x + 7 >= 0, x + 1 >= 0, x - 3 >= 0, " |
313 | "-x + 5 >= 0)" }), |
314 | points: {{-6}, |
315 | {-5}, |
316 | {-4}, |
317 | {-9}, |
318 | {-10}, |
319 | {-11}, |
320 | {0}, |
321 | {1}, |
322 | {2}, |
323 | {3}, |
324 | {4}, |
325 | {5}, |
326 | {6}, |
327 | {7}, |
328 | {8}}); |
329 | } |
330 | |
331 | TEST(SetTest, Complement) { |
332 | // Complement of universe. |
333 | testComplementAtPoints( |
334 | s: PresburgerSet::getUniverse(space: PresburgerSpace::getSetSpace(numDims: (1))), |
335 | points: {{-1}, {-2}, {-8}, {1}, {2}, {8}, {9}, {10}, {20}, {21}}); |
336 | |
337 | // Complement of empty set. |
338 | testComplementAtPoints( |
339 | s: PresburgerSet::getEmpty(space: PresburgerSpace::getSetSpace(numDims: (1))), |
340 | points: {{-1}, {-2}, {-8}, {1}, {2}, {8}, {9}, {10}, {20}, {21}}); |
341 | |
342 | testComplementAtPoints(s: parsePresburgerSet(strs: {"(x,y) : (x - 2 >= 0, y - 2 >= 0, " |
343 | "-x + 10 >= 0, -y + 10 >= 0)" }), |
344 | points: {{1, 1}, |
345 | {2, 1}, |
346 | {1, 2}, |
347 | {2, 2}, |
348 | {2, 3}, |
349 | {3, 2}, |
350 | {10, 10}, |
351 | {10, 11}, |
352 | {11, 10}, |
353 | {2, 10}, |
354 | {2, 11}, |
355 | {1, 10}}); |
356 | } |
357 | |
358 | TEST(SetTest, isEqual) { |
359 | // set = [2, 8] U [10, 20]. |
360 | PresburgerSet universe = |
361 | PresburgerSet::getUniverse(space: PresburgerSpace::getSetSpace(numDims: (1))); |
362 | PresburgerSet emptySet = |
363 | PresburgerSet::getEmpty(space: PresburgerSpace::getSetSpace(numDims: (1))); |
364 | PresburgerSet set = parsePresburgerSet( |
365 | strs: {"(x) : (x - 2 >= 0, -x + 8 >= 0)" , "(x) : (x - 10 >= 0, -x + 20 >= 0)" }); |
366 | |
367 | // universe != emptySet. |
368 | EXPECT_FALSE(universe.isEqual(emptySet)); |
369 | // emptySet != universe. |
370 | EXPECT_FALSE(emptySet.isEqual(universe)); |
371 | // emptySet == emptySet. |
372 | EXPECT_TRUE(emptySet.isEqual(emptySet)); |
373 | // universe == universe. |
374 | EXPECT_TRUE(universe.isEqual(universe)); |
375 | |
376 | // universe U emptySet == universe. |
377 | EXPECT_TRUE(universe.unionSet(emptySet).isEqual(universe)); |
378 | // universe U universe == universe. |
379 | EXPECT_TRUE(universe.unionSet(universe).isEqual(universe)); |
380 | // emptySet U emptySet == emptySet. |
381 | EXPECT_TRUE(emptySet.unionSet(emptySet).isEqual(emptySet)); |
382 | // universe U emptySet != emptySet. |
383 | EXPECT_FALSE(universe.unionSet(emptySet).isEqual(emptySet)); |
384 | // universe U universe != emptySet. |
385 | EXPECT_FALSE(universe.unionSet(universe).isEqual(emptySet)); |
386 | // emptySet U emptySet != universe. |
387 | EXPECT_FALSE(emptySet.unionSet(emptySet).isEqual(universe)); |
388 | |
389 | // set \ set == emptySet. |
390 | EXPECT_TRUE(set.subtract(set).isEqual(emptySet)); |
391 | // set == set. |
392 | EXPECT_TRUE(set.isEqual(set)); |
393 | // set U (universe \ set) == universe. |
394 | EXPECT_TRUE(set.unionSet(set.complement()).isEqual(universe)); |
395 | // set U (universe \ set) != set. |
396 | EXPECT_FALSE(set.unionSet(set.complement()).isEqual(set)); |
397 | // set != set U (universe \ set). |
398 | EXPECT_FALSE(set.isEqual(set.unionSet(set.complement()))); |
399 | |
400 | // square is one unit taller than rect. |
401 | PresburgerSet square = parsePresburgerSet( |
402 | strs: {"(x, y) : (x - 2 >= 0, y - 2 >= 0, -x + 9 >= 0, -y + 9 >= 0)" }); |
403 | PresburgerSet rect = parsePresburgerSet( |
404 | strs: {"(x, y) : (x - 2 >= 0, y - 2 >= 0, -x + 9 >= 0, -y + 8 >= 0)" }); |
405 | EXPECT_FALSE(square.isEqual(rect)); |
406 | PresburgerSet universeRect = square.unionSet(set: square.complement()); |
407 | PresburgerSet universeSquare = rect.unionSet(set: rect.complement()); |
408 | EXPECT_TRUE(universeRect.isEqual(universeSquare)); |
409 | EXPECT_FALSE(universeRect.isEqual(rect)); |
410 | EXPECT_FALSE(universeSquare.isEqual(square)); |
411 | EXPECT_FALSE(rect.complement().isEqual(square.complement())); |
412 | } |
413 | |
414 | void expectEqual(const PresburgerSet &s, const PresburgerSet &t) { |
415 | EXPECT_TRUE(s.isEqual(t)); |
416 | } |
417 | |
418 | void expectEqual(const IntegerPolyhedron &s, const IntegerPolyhedron &t) { |
419 | EXPECT_TRUE(s.isEqual(t)); |
420 | } |
421 | |
422 | void expectEmpty(const PresburgerSet &s) { EXPECT_TRUE(s.isIntegerEmpty()); } |
423 | |
424 | TEST(SetTest, divisions) { |
425 | // evens = {x : exists q, x = 2q}. |
426 | PresburgerSet evens{ |
427 | parseIntegerPolyhedron(str: "(x) : (x - 2 * (x floordiv 2) == 0)" )}; |
428 | |
429 | // odds = {x : exists q, x = 2q + 1}. |
430 | PresburgerSet odds{ |
431 | parseIntegerPolyhedron(str: "(x) : (x - 2 * (x floordiv 2) - 1 == 0)" )}; |
432 | |
433 | // multiples3 = {x : exists q, x = 3q}. |
434 | PresburgerSet multiples3{ |
435 | parseIntegerPolyhedron(str: "(x) : (x - 3 * (x floordiv 3) == 0)" )}; |
436 | |
437 | // multiples6 = {x : exists q, x = 6q}. |
438 | PresburgerSet multiples6{ |
439 | parseIntegerPolyhedron(str: "(x) : (x - 6 * (x floordiv 6) == 0)" )}; |
440 | |
441 | // evens /\ odds = empty. |
442 | expectEmpty(s: PresburgerSet(evens).intersect(set: PresburgerSet(odds))); |
443 | // evens U odds = universe. |
444 | expectEqual(s: evens.unionSet(set: odds), |
445 | t: PresburgerSet::getUniverse(space: PresburgerSpace::getSetSpace(numDims: (1)))); |
446 | expectEqual(s: evens.complement(), t: odds); |
447 | expectEqual(s: odds.complement(), t: evens); |
448 | // even multiples of 3 = multiples of 6. |
449 | expectEqual(s: multiples3.intersect(set: evens), t: multiples6); |
450 | |
451 | PresburgerSet setA{parseIntegerPolyhedron(str: "(x) : (-x >= 0)" )}; |
452 | PresburgerSet setB{parseIntegerPolyhedron(str: "(x) : (x floordiv 2 - 4 >= 0)" )}; |
453 | EXPECT_TRUE(setA.subtract(setB).isEqual(setA)); |
454 | } |
455 | |
456 | void convertSuffixDimsToLocals(IntegerPolyhedron &poly, unsigned numLocals) { |
457 | poly.convertVarKind(srcKind: VarKind::SetDim, varStart: poly.getNumDimVars() - numLocals, |
458 | varLimit: poly.getNumDimVars(), dstKind: VarKind::Local); |
459 | } |
460 | |
461 | inline IntegerPolyhedron |
462 | parseIntegerPolyhedronAndMakeLocals(StringRef str, unsigned numLocals) { |
463 | IntegerPolyhedron poly = parseIntegerPolyhedron(str); |
464 | convertSuffixDimsToLocals(poly, numLocals); |
465 | return poly; |
466 | } |
467 | |
468 | TEST(SetTest, divisionsDefByEq) { |
469 | // evens = {x : exists q, x = 2q}. |
470 | PresburgerSet evens{parseIntegerPolyhedronAndMakeLocals( |
471 | str: "(x, y) : (x - 2 * y == 0)" , /*numLocals=*/1)}; |
472 | |
473 | // odds = {x : exists q, x = 2q + 1}. |
474 | PresburgerSet odds{parseIntegerPolyhedronAndMakeLocals( |
475 | str: "(x, y) : (x - 2 * y - 1 == 0)" , /*numLocals=*/1)}; |
476 | |
477 | // multiples3 = {x : exists q, x = 3q}. |
478 | PresburgerSet multiples3{parseIntegerPolyhedronAndMakeLocals( |
479 | str: "(x, y) : (x - 3 * y == 0)" , /*numLocals=*/1)}; |
480 | |
481 | // multiples6 = {x : exists q, x = 6q}. |
482 | PresburgerSet multiples6{parseIntegerPolyhedronAndMakeLocals( |
483 | str: "(x, y) : (x - 6 * y == 0)" , /*numLocals=*/1)}; |
484 | |
485 | // evens /\ odds = empty. |
486 | expectEmpty(s: PresburgerSet(evens).intersect(set: PresburgerSet(odds))); |
487 | // evens U odds = universe. |
488 | expectEqual(s: evens.unionSet(set: odds), |
489 | t: PresburgerSet::getUniverse(space: PresburgerSpace::getSetSpace(numDims: (1)))); |
490 | expectEqual(s: evens.complement(), t: odds); |
491 | expectEqual(s: odds.complement(), t: evens); |
492 | // even multiples of 3 = multiples of 6. |
493 | expectEqual(s: multiples3.intersect(set: evens), t: multiples6); |
494 | |
495 | PresburgerSet evensDefByIneq{ |
496 | parseIntegerPolyhedron(str: "(x) : (x - 2 * (x floordiv 2) == 0)" )}; |
497 | expectEqual(s: evens, t: PresburgerSet(evensDefByIneq)); |
498 | } |
499 | |
500 | TEST(SetTest, divisionNonDivLocals) { |
501 | // This is a tetrahedron with vertices at |
502 | // (1/3, 0, 0), (2/3, 0, 0), (2/3, 0, 1000), and (1000, 1000, 1000). |
503 | // |
504 | // The only integer point in this is at (1000, 1000, 1000). |
505 | // We project this to the xy plane. |
506 | IntegerPolyhedron tetrahedron = parseIntegerPolyhedronAndMakeLocals( |
507 | str: "(x, y, z) : (y >= 0, z - y >= 0, 3000*x - 2998*y " |
508 | "- 1000 - z >= 0, -1500*x + 1499*y + 1000 >= 0)" , |
509 | /*numLocals=*/1); |
510 | |
511 | // This is a triangle with vertices at (1/3, 0), (2/3, 0) and (1000, 1000). |
512 | // The only integer point in this is at (1000, 1000). |
513 | // |
514 | // It also happens to be the projection of the above onto the xy plane. |
515 | IntegerPolyhedron triangle = |
516 | parseIntegerPolyhedron(str: "(x,y) : (y >= 0, 3000 * x - 2999 * y - 1000 >= " |
517 | "0, -3000 * x + 2998 * y + 2000 >= 0)" ); |
518 | |
519 | EXPECT_TRUE(triangle.containsPoint({1000, 1000})); |
520 | EXPECT_FALSE(triangle.containsPoint({1001, 1001})); |
521 | expectEqual(s: triangle, t: tetrahedron); |
522 | |
523 | convertSuffixDimsToLocals(poly&: triangle, numLocals: 1); |
524 | IntegerPolyhedron line = parseIntegerPolyhedron(str: "(x) : (x - 1000 == 0)" ); |
525 | expectEqual(s: line, t: triangle); |
526 | |
527 | // Triangle with vertices (0, 0), (5, 0), (15, 5). |
528 | // Projected on x, it becomes [0, 13] U {15} as it becomes too narrow towards |
529 | // the apex and so does not have any integer point at x = 14. |
530 | // At x = 15, the apex is an integer point. |
531 | PresburgerSet triangle2{ |
532 | parseIntegerPolyhedronAndMakeLocals(str: "(x,y) : (y >= 0, " |
533 | "x - 3*y >= 0, " |
534 | "2*y - x + 5 >= 0)" , |
535 | /*numLocals=*/1)}; |
536 | PresburgerSet zeroToThirteen{ |
537 | parseIntegerPolyhedron(str: "(x) : (13 - x >= 0, x >= 0)" )}; |
538 | PresburgerSet fifteen{parseIntegerPolyhedron(str: "(x) : (x - 15 == 0)" )}; |
539 | expectEqual(s: triangle2.subtract(set: zeroToThirteen), t: fifteen); |
540 | } |
541 | |
542 | TEST(SetTest, subtractDuplicateDivsRegression) { |
543 | // Previously, subtracting sets with duplicate divs might result in crashes |
544 | // due to existing divs being removed when merging local ids, due to being |
545 | // identified as being duplicates for the first time. |
546 | IntegerPolyhedron setA(PresburgerSpace::getSetSpace(numDims: 1)); |
547 | setA.addLocalFloorDiv(dividend: {1, 0}, divisor: 2); |
548 | setA.addLocalFloorDiv(dividend: {1, 0, 0}, divisor: 2); |
549 | EXPECT_TRUE(setA.isEqual(setA)); |
550 | } |
551 | |
552 | /// Coalesce `set` and check that the `newSet` is equal to `set` and that |
553 | /// `expectedNumPoly` matches the number of Poly in the coalesced set. |
554 | void expectCoalesce(size_t expectedNumPoly, const PresburgerSet &set) { |
555 | PresburgerSet newSet = set.coalesce(); |
556 | EXPECT_TRUE(set.isEqual(newSet)); |
557 | EXPECT_TRUE(expectedNumPoly == newSet.getNumDisjuncts()); |
558 | } |
559 | |
560 | TEST(SetTest, coalesceNoPoly) { |
561 | PresburgerSet set = makeSetFromPoly(numDims: 0, polys: {}); |
562 | expectCoalesce(expectedNumPoly: 0, set); |
563 | } |
564 | |
565 | TEST(SetTest, coalesceContainedOneDim) { |
566 | PresburgerSet set = parsePresburgerSet( |
567 | strs: {"(x) : (x >= 0, -x + 4 >= 0)" , "(x) : (x - 1 >= 0, -x + 2 >= 0)" }); |
568 | expectCoalesce(expectedNumPoly: 1, set); |
569 | } |
570 | |
571 | TEST(SetTest, coalesceFirstEmpty) { |
572 | PresburgerSet set = parsePresburgerSet( |
573 | strs: {"(x) : ( x >= 0, -x - 1 >= 0)" , "(x) : ( x - 1 >= 0, -x + 2 >= 0)" }); |
574 | expectCoalesce(expectedNumPoly: 1, set); |
575 | } |
576 | |
577 | TEST(SetTest, coalesceSecondEmpty) { |
578 | PresburgerSet set = parsePresburgerSet( |
579 | strs: {"(x) : (x - 1 >= 0, -x + 2 >= 0)" , "(x) : (x >= 0, -x - 1 >= 0)" }); |
580 | expectCoalesce(expectedNumPoly: 1, set); |
581 | } |
582 | |
583 | TEST(SetTest, coalesceBothEmpty) { |
584 | PresburgerSet set = parsePresburgerSet( |
585 | strs: {"(x) : (x - 3 >= 0, -x - 1 >= 0)" , "(x) : (x >= 0, -x - 1 >= 0)" }); |
586 | expectCoalesce(expectedNumPoly: 0, set); |
587 | } |
588 | |
589 | TEST(SetTest, coalesceFirstUniv) { |
590 | PresburgerSet set = |
591 | parsePresburgerSet(strs: {"(x) : ()" , "(x) : ( x >= 0, -x + 1 >= 0)" }); |
592 | expectCoalesce(expectedNumPoly: 1, set); |
593 | } |
594 | |
595 | TEST(SetTest, coalesceSecondUniv) { |
596 | PresburgerSet set = |
597 | parsePresburgerSet(strs: {"(x) : ( x >= 0, -x + 1 >= 0)" , "(x) : ()" }); |
598 | expectCoalesce(expectedNumPoly: 1, set); |
599 | } |
600 | |
601 | TEST(SetTest, coalesceBothUniv) { |
602 | PresburgerSet set = parsePresburgerSet(strs: {"(x) : ()" , "(x) : ()" }); |
603 | expectCoalesce(expectedNumPoly: 1, set); |
604 | } |
605 | |
606 | TEST(SetTest, coalesceFirstUnivSecondEmpty) { |
607 | PresburgerSet set = |
608 | parsePresburgerSet(strs: {"(x) : ()" , "(x) : ( x >= 0, -x - 1 >= 0)" }); |
609 | expectCoalesce(expectedNumPoly: 1, set); |
610 | } |
611 | |
612 | TEST(SetTest, coalesceFirstEmptySecondUniv) { |
613 | PresburgerSet set = |
614 | parsePresburgerSet(strs: {"(x) : ( x >= 0, -x - 1 >= 0)" , "(x) : ()" }); |
615 | expectCoalesce(expectedNumPoly: 1, set); |
616 | } |
617 | |
618 | TEST(SetTest, coalesceCutOneDim) { |
619 | PresburgerSet set = parsePresburgerSet(strs: { |
620 | "(x) : ( x >= 0, -x + 3 >= 0)" , |
621 | "(x) : ( x - 2 >= 0, -x + 4 >= 0)" , |
622 | }); |
623 | expectCoalesce(expectedNumPoly: 1, set); |
624 | } |
625 | |
626 | TEST(SetTest, coalesceSeparateOneDim) { |
627 | PresburgerSet set = parsePresburgerSet( |
628 | strs: {"(x) : ( x >= 0, -x + 2 >= 0)" , "(x) : ( x - 3 >= 0, -x + 4 >= 0)" }); |
629 | expectCoalesce(expectedNumPoly: 2, set); |
630 | } |
631 | |
632 | TEST(SetTest, coalesceAdjEq) { |
633 | PresburgerSet set = |
634 | parsePresburgerSet(strs: {"(x) : ( x == 0)" , "(x) : ( x - 1 == 0)" }); |
635 | expectCoalesce(expectedNumPoly: 2, set); |
636 | } |
637 | |
638 | TEST(SetTest, coalesceContainedTwoDim) { |
639 | PresburgerSet set = parsePresburgerSet(strs: { |
640 | "(x,y) : (x >= 0, -x + 3 >= 0, y >= 0, -y + 3 >= 0)" , |
641 | "(x,y) : (x >= 0, -x + 3 >= 0, y - 2 >= 0, -y + 3 >= 0)" , |
642 | }); |
643 | expectCoalesce(expectedNumPoly: 1, set); |
644 | } |
645 | |
646 | TEST(SetTest, coalesceCutTwoDim) { |
647 | PresburgerSet set = parsePresburgerSet(strs: { |
648 | "(x,y) : (x >= 0, -x + 3 >= 0, y >= 0, -y + 2 >= 0)" , |
649 | "(x,y) : (x >= 0, -x + 3 >= 0, y - 1 >= 0, -y + 3 >= 0)" , |
650 | }); |
651 | expectCoalesce(expectedNumPoly: 1, set); |
652 | } |
653 | |
654 | TEST(SetTest, coalesceEqStickingOut) { |
655 | PresburgerSet set = parsePresburgerSet(strs: { |
656 | "(x,y) : (x >= 0, -x + 2 >= 0, y >= 0, -y + 2 >= 0)" , |
657 | "(x,y) : (y - 1 == 0, x >= 0, -x + 3 >= 0)" , |
658 | }); |
659 | expectCoalesce(expectedNumPoly: 2, set); |
660 | } |
661 | |
662 | TEST(SetTest, coalesceSeparateTwoDim) { |
663 | PresburgerSet set = parsePresburgerSet(strs: { |
664 | "(x,y) : (x >= 0, -x + 3 >= 0, y >= 0, -y + 1 >= 0)" , |
665 | "(x,y) : (x >= 0, -x + 3 >= 0, y - 2 >= 0, -y + 3 >= 0)" , |
666 | }); |
667 | expectCoalesce(expectedNumPoly: 2, set); |
668 | } |
669 | |
670 | TEST(SetTest, coalesceContainedEq) { |
671 | PresburgerSet set = parsePresburgerSet(strs: { |
672 | "(x,y) : (x >= 0, -x + 3 >= 0, x - y == 0)" , |
673 | "(x,y) : (x - 1 >= 0, -x + 2 >= 0, x - y == 0)" , |
674 | }); |
675 | expectCoalesce(expectedNumPoly: 1, set); |
676 | } |
677 | |
678 | TEST(SetTest, coalesceCuttingEq) { |
679 | PresburgerSet set = parsePresburgerSet(strs: { |
680 | "(x,y) : (x + 1 >= 0, -x + 1 >= 0, x - y == 0)" , |
681 | "(x,y) : (x >= 0, -x + 2 >= 0, x - y == 0)" , |
682 | }); |
683 | expectCoalesce(expectedNumPoly: 1, set); |
684 | } |
685 | |
686 | TEST(SetTest, coalesceSeparateEq) { |
687 | PresburgerSet set = parsePresburgerSet(strs: { |
688 | "(x,y) : (x - 3 >= 0, -x + 4 >= 0, x - y == 0)" , |
689 | "(x,y) : (x >= 0, -x + 1 >= 0, x - y == 0)" , |
690 | }); |
691 | expectCoalesce(expectedNumPoly: 2, set); |
692 | } |
693 | |
694 | TEST(SetTest, coalesceContainedEqAsIneq) { |
695 | PresburgerSet set = parsePresburgerSet(strs: { |
696 | "(x,y) : (x >= 0, -x + 3 >= 0, x - y >= 0, -x + y >= 0)" , |
697 | "(x,y) : (x - 1 >= 0, -x + 2 >= 0, x - y == 0)" , |
698 | }); |
699 | expectCoalesce(expectedNumPoly: 1, set); |
700 | } |
701 | |
702 | TEST(SetTest, coalesceContainedEqComplex) { |
703 | PresburgerSet set = parsePresburgerSet(strs: { |
704 | "(x,y) : (x - 2 == 0, x - y == 0)" , |
705 | "(x,y) : (x - 1 >= 0, -x + 2 >= 0, x - y == 0)" , |
706 | }); |
707 | expectCoalesce(expectedNumPoly: 1, set); |
708 | } |
709 | |
710 | TEST(SetTest, coalesceThreeContained) { |
711 | PresburgerSet set = parsePresburgerSet(strs: { |
712 | "(x) : (x >= 0, -x + 1 >= 0)" , |
713 | "(x) : (x >= 0, -x + 2 >= 0)" , |
714 | "(x) : (x >= 0, -x + 3 >= 0)" , |
715 | }); |
716 | expectCoalesce(expectedNumPoly: 1, set); |
717 | } |
718 | |
719 | TEST(SetTest, coalesceDoubleIncrement) { |
720 | PresburgerSet set = parsePresburgerSet(strs: { |
721 | "(x) : (x == 0)" , |
722 | "(x) : (x - 2 == 0)" , |
723 | "(x) : (x + 2 == 0)" , |
724 | "(x) : (x - 2 >= 0, -x + 3 >= 0)" , |
725 | }); |
726 | expectCoalesce(expectedNumPoly: 3, set); |
727 | } |
728 | |
729 | TEST(SetTest, coalesceLastCoalesced) { |
730 | PresburgerSet set = parsePresburgerSet(strs: { |
731 | "(x) : (x == 0)" , |
732 | "(x) : (x - 1 >= 0, -x + 3 >= 0)" , |
733 | "(x) : (x + 2 == 0)" , |
734 | "(x) : (x - 2 >= 0, -x + 4 >= 0)" , |
735 | }); |
736 | expectCoalesce(expectedNumPoly: 3, set); |
737 | } |
738 | |
739 | TEST(SetTest, coalesceDiv) { |
740 | PresburgerSet set = parsePresburgerSet(strs: { |
741 | "(x) : (x floordiv 2 == 0)" , |
742 | "(x) : (x floordiv 2 - 1 == 0)" , |
743 | }); |
744 | expectCoalesce(expectedNumPoly: 2, set); |
745 | } |
746 | |
747 | TEST(SetTest, coalesceDivOtherContained) { |
748 | PresburgerSet set = parsePresburgerSet(strs: { |
749 | "(x) : (x floordiv 2 == 0)" , |
750 | "(x) : (x == 0)" , |
751 | "(x) : (x >= 0, -x + 1 >= 0)" , |
752 | }); |
753 | expectCoalesce(expectedNumPoly: 2, set); |
754 | } |
755 | |
756 | static void |
757 | expectComputedVolumeIsValidOverapprox(const PresburgerSet &set, |
758 | std::optional<int64_t> trueVolume, |
759 | std::optional<int64_t> resultBound) { |
760 | expectComputedVolumeIsValidOverapprox(computedVolume: set.computeVolume(), trueVolume, |
761 | resultBound); |
762 | } |
763 | |
764 | TEST(SetTest, computeVolume) { |
765 | // Diamond with vertices at (0, 0), (5, 5), (5, 5), (10, 0). |
766 | PresburgerSet diamond(parseIntegerPolyhedron( |
767 | str: "(x, y) : (x + y >= 0, -x - y + 10 >= 0, x - y >= 0, -x + y + " |
768 | "10 >= 0)" )); |
769 | expectComputedVolumeIsValidOverapprox(set: diamond, |
770 | /*trueVolume=*/61ull, |
771 | /*resultBound=*/121ull); |
772 | |
773 | // Diamond with vertices at (-5, 0), (0, -5), (0, 5), (5, 0). |
774 | PresburgerSet shiftedDiamond(parseIntegerPolyhedron( |
775 | str: "(x, y) : (x + y + 5 >= 0, -x - y + 5 >= 0, x - y + 5 >= 0, -x + y + " |
776 | "5 >= 0)" )); |
777 | expectComputedVolumeIsValidOverapprox(set: shiftedDiamond, |
778 | /*trueVolume=*/61ull, |
779 | /*resultBound=*/121ull); |
780 | |
781 | // Diamond with vertices at (-5, 0), (5, -10), (5, 10), (15, 0). |
782 | PresburgerSet biggerDiamond(parseIntegerPolyhedron( |
783 | str: "(x, y) : (x + y + 5 >= 0, -x - y + 15 >= 0, x - y + 5 >= 0, -x + y + " |
784 | "15 >= 0)" )); |
785 | expectComputedVolumeIsValidOverapprox(set: biggerDiamond, |
786 | /*trueVolume=*/221ull, |
787 | /*resultBound=*/441ull); |
788 | |
789 | // There is some overlap between diamond and shiftedDiamond. |
790 | expectComputedVolumeIsValidOverapprox(set: diamond.unionSet(set: shiftedDiamond), |
791 | /*trueVolume=*/104ull, |
792 | /*resultBound=*/242ull); |
793 | |
794 | // biggerDiamond subsumes both the small ones. |
795 | expectComputedVolumeIsValidOverapprox( |
796 | set: diamond.unionSet(set: shiftedDiamond).unionSet(set: biggerDiamond), |
797 | /*trueVolume=*/221ull, |
798 | /*resultBound=*/683ull); |
799 | |
800 | // Unbounded polytope. |
801 | PresburgerSet unbounded( |
802 | parseIntegerPolyhedron(str: "(x, y) : (2*x - y >= 0, y - 3*x >= 0)" )); |
803 | expectComputedVolumeIsValidOverapprox(set: unbounded, /*trueVolume=*/{}, |
804 | /*resultBound=*/{}); |
805 | |
806 | // Union of unbounded with bounded is unbounded. |
807 | expectComputedVolumeIsValidOverapprox(set: unbounded.unionSet(set: diamond), |
808 | /*trueVolume=*/{}, |
809 | /*resultBound=*/{}); |
810 | } |
811 | |
812 | // The last `numToProject` dims will be projected out, i.e., converted to |
813 | // locals. |
814 | void testComputeReprAtPoints(IntegerPolyhedron poly, |
815 | ArrayRef<SmallVector<int64_t, 4>> points, |
816 | unsigned numToProject) { |
817 | poly.convertVarKind(srcKind: VarKind::SetDim, varStart: poly.getNumDimVars() - numToProject, |
818 | varLimit: poly.getNumDimVars(), dstKind: VarKind::Local); |
819 | PresburgerRelation repr = poly.computeReprWithOnlyDivLocals(); |
820 | EXPECT_TRUE(repr.hasOnlyDivLocals()); |
821 | EXPECT_TRUE(repr.getSpace().isCompatible(poly.getSpace())); |
822 | for (const SmallVector<int64_t, 4> &point : points) { |
823 | EXPECT_EQ(poly.containsPointNoLocal(point).has_value(), |
824 | repr.containsPoint(point)); |
825 | } |
826 | } |
827 | |
828 | void testComputeRepr(IntegerPolyhedron poly, const PresburgerSet &expected, |
829 | unsigned numToProject) { |
830 | poly.convertVarKind(srcKind: VarKind::SetDim, varStart: poly.getNumDimVars() - numToProject, |
831 | varLimit: poly.getNumDimVars(), dstKind: VarKind::Local); |
832 | PresburgerRelation repr = poly.computeReprWithOnlyDivLocals(); |
833 | EXPECT_TRUE(repr.hasOnlyDivLocals()); |
834 | EXPECT_TRUE(repr.getSpace().isCompatible(poly.getSpace())); |
835 | EXPECT_TRUE(repr.isEqual(expected)); |
836 | } |
837 | |
838 | TEST(SetTest, computeReprWithOnlyDivLocals) { |
839 | testComputeReprAtPoints(poly: parseIntegerPolyhedron(str: "(x, y) : (x - 2*y == 0)" ), |
840 | points: {{1, 0}, {2, 1}, {3, 0}, {4, 2}, {5, 3}}, |
841 | /*numToProject=*/0); |
842 | testComputeReprAtPoints(poly: parseIntegerPolyhedron(str: "(x, e) : (x - 2*e == 0)" ), |
843 | points: {{1}, {2}, {3}, {4}, {5}}, /*numToProject=*/1); |
844 | |
845 | // Tests to check that the space is preserved. |
846 | testComputeReprAtPoints(poly: parseIntegerPolyhedron(str: "(x, y)[z, w] : ()" ), points: {}, |
847 | /*numToProject=*/1); |
848 | testComputeReprAtPoints( |
849 | poly: parseIntegerPolyhedron(str: "(x, y)[z, w] : (z - (w floordiv 2) == 0)" ), points: {}, |
850 | /*numToProject=*/1); |
851 | |
852 | // Bezout's lemma: if a, b are constants, |
853 | // the set of values that ax + by can take is all multiples of gcd(a, b). |
854 | testComputeRepr(poly: parseIntegerPolyhedron(str: "(x, e, f) : (x - 15*e - 21*f == 0)" ), |
855 | expected: PresburgerSet(parseIntegerPolyhedron( |
856 | str: {"(x) : (x - 3*(x floordiv 3) == 0)" })), |
857 | /*numToProject=*/2); |
858 | } |
859 | |
860 | TEST(SetTest, subtractOutputSizeRegression) { |
861 | PresburgerSet set1 = parsePresburgerSet(strs: {"(i) : (i >= 0, 10 - i >= 0)" }); |
862 | PresburgerSet set2 = parsePresburgerSet(strs: {"(i) : (i - 5 >= 0)" }); |
863 | |
864 | PresburgerSet set3 = parsePresburgerSet(strs: {"(i) : (i >= 0, 4 - i >= 0)" }); |
865 | |
866 | PresburgerSet result = set1.subtract(set: set2); |
867 | |
868 | EXPECT_TRUE(result.isEqual(set3)); |
869 | |
870 | // Previously, the subtraction result was producing an extra empty set, which |
871 | // is correct, but bad for output size. |
872 | EXPECT_EQ(result.getNumDisjuncts(), 1u); |
873 | |
874 | PresburgerSet subtractSelf = set1.subtract(set: set1); |
875 | EXPECT_TRUE(subtractSelf.isIntegerEmpty()); |
876 | // Previously, the subtraction result was producing several unnecessary empty |
877 | // sets, which is correct, but bad for output size. |
878 | EXPECT_EQ(subtractSelf.getNumDisjuncts(), 0u); |
879 | } |
880 | |