1 // Copyright (C) 2016-2020 Jonathan Müller <jonathanmueller.dev@gmail.com>
2 // This file is subject to the license terms in the LICENSE file
3 // found in the top-level directory of this distribution.
4 
5 #include <type_safe/bounded_type.hpp>
6 
7 #include <catch.hpp>
8 
9 #include <type_safe/integer.hpp>
10 
11 using namespace type_safe;
12 
13 TEST_CASE("constraints::less")
14 {
15     constraints::less<int> p(42);
16     REQUIRE(p.get_bound() == 42);
17     REQUIRE(p(0));
18     REQUIRE(p(40));
19     REQUIRE(!p(42));
20     REQUIRE(!p(50));
21     REQUIRE(!p(100));
22 }
23 
24 TEST_CASE("constraints::less_equal")
25 {
26     constraints::less_equal<int> p(42);
27     REQUIRE(p.get_bound() == 42);
28     REQUIRE(p(0));
29     REQUIRE(p(40));
30     REQUIRE(p(42));
31     REQUIRE(!p(50));
32     REQUIRE(!p(100));
33 }
34 
35 TEST_CASE("constraints::greater")
36 {
37     constraints::greater<int> p(42);
38     REQUIRE(p.get_bound() == 42);
39     REQUIRE(!p(0));
40     REQUIRE(!p(40));
41     REQUIRE(!p(42));
42     REQUIRE(p(50));
43     REQUIRE(p(100));
44 }
45 
46 TEST_CASE("constraints::greater_equal")
47 {
48     constraints::greater_equal<int> p(42);
49     REQUIRE(p.get_bound() == 42);
50     REQUIRE(!p(0));
51     REQUIRE(!p(40));
52     REQUIRE(p(42));
53     REQUIRE(p(50));
54     REQUIRE(p(100));
55 }
56 
57 TEST_CASE("constraints::bounded")
58 {
59     SECTION("closed, closed")
60     {
61         constraints::bounded<int, true, true> p(0, 42);
62         static_assert(std::is_same<decltype(p), constraints::closed_interval<int>>::value, "");
63         REQUIRE(p.get_lower_bound() == 0);
64         REQUIRE(p.get_upper_bound() == 42);
65 
66         REQUIRE(p(30));
67         REQUIRE(p(41));
68         REQUIRE(p(1));
69 
70         REQUIRE(p(0));
71         REQUIRE(p(42));
72 
73         REQUIRE(!p(-5));
74         REQUIRE(!p(100));
75     }
76     SECTION("open, closed")
77     {
78         constraints::bounded<int, false, true> p(0, 42);
79         REQUIRE(p.get_lower_bound() == 0);
80         REQUIRE(p.get_upper_bound() == 42);
81 
82         REQUIRE(p(30));
83         REQUIRE(p(41));
84         REQUIRE(p(1));
85 
86         REQUIRE(!p(0));
87         REQUIRE(p(42));
88 
89         REQUIRE(!p(-5));
90         REQUIRE(!p(100));
91     }
92     SECTION("closed, open")
93     {
94         constraints::bounded<int, true, false> p(0, 42);
95         REQUIRE(p.get_lower_bound() == 0);
96         REQUIRE(p.get_upper_bound() == 42);
97 
98         REQUIRE(p(30));
99         REQUIRE(p(41));
100         REQUIRE(p(1));
101 
102         REQUIRE(p(0));
103         REQUIRE(!p(42));
104 
105         REQUIRE(!p(-5));
106         REQUIRE(!p(100));
107     }
108     SECTION("open, open")
109     {
110         constraints::bounded<int, false, false> p(0, 42);
111         static_assert(std::is_same<decltype(p), constraints::open_interval<int>>::value, "");
112         REQUIRE(p.get_lower_bound() == 0);
113         REQUIRE(p.get_upper_bound() == 42);
114 
115         REQUIRE(p(30));
116         REQUIRE(p(41));
117         REQUIRE(p(1));
118 
119         REQUIRE(!p(0));
120         REQUIRE(!p(42));
121 
122         REQUIRE(!p(-5));
123         REQUIRE(!p(100));
124     }
125 }
126 
127 TEST_CASE("bounded literal")
128 {
129     SECTION("unsigned")
130     {
131         constraints::less<integer<unsigned>, lit_detail::integer_bound<unsigned long long, 42>> p(
132             42_boundu);
133         static_assert(std::is_same<decltype(p.get_bound()), const unsigned long long&>::value,
134                       "ups");
135         REQUIRE(p.get_bound() == 42);
136     }
137     SECTION("signed")
138     {
139         constraints::less<integer<int>, lit_detail::integer_bound<long long, 42>> p(42_bound);
140         static_assert(std::is_same<decltype(p.get_bound()), const long long&>::value, "ups");
141         REQUIRE(p.get_bound() == 42);
142     }
143     SECTION("signed negative")
144     {
145         constraints::less<integer<int>, lit_detail::integer_bound<long long, -42>> p(-42_bound);
146         static_assert(std::is_same<decltype(p.get_bound()), const long long&>::value, "ups");
147         REQUIRE(p.get_bound() == -42);
148     }
149 
150     bounded_type<integer<int>, true, true, lit_detail::integer_bound<long long, 0>,
151                  lit_detail::integer_bound<long long, 100>>
152         bounded = make_bounded(integer<int>(50), 0_bound, 100_bound);
153     REQUIRE(bounded.get_constraint().get_lower_bound() == 0);
154     REQUIRE(bounded.get_constraint().get_upper_bound() == 100);
155 }
156 
157 TEST_CASE("bounded_type")
158 {
159     constrained_type<int, constraints::closed_interval<int>> dynamic_closed
160         = make_bounded(10, 0, 42);
161     static_assert(std::is_same<decltype(dynamic_closed), bounded_type<int, true, true>>::value, "");
162     REQUIRE(dynamic_closed.get_value() == 10);
163     REQUIRE(dynamic_closed.get_constraint().get_lower_bound() == 0);
164     REQUIRE(dynamic_closed.get_constraint().get_upper_bound() == 42);
165 
166     constrained_type<int, constraints::open_interval<int>> dynamic_open
167         = make_bounded_exclusive(10, 0, 42);
168     static_assert(std::is_same<decltype(dynamic_open), bounded_type<int, false, false>>::value, "");
169     REQUIRE(dynamic_open.get_value() == 10);
170     REQUIRE(dynamic_open.get_constraint().get_lower_bound() == 0);
171     REQUIRE(dynamic_open.get_constraint().get_upper_bound() == 42);
172 
173     constrained_type<int, constraints::closed_interval<int, std::integral_constant<int, 0>,
174                                                        std::integral_constant<int, 42>>>
175         static_closed
176         = make_bounded(10, std::integral_constant<int, 0>{}, std::integral_constant<int, 42>{});
177     static_assert(std::is_same<decltype(static_closed),
178                                bounded_type<int, true, true, std::integral_constant<int, 0>,
179                                             std::integral_constant<int, 42>>>::value,
180                   "");
181     REQUIRE(static_closed.get_value() == 10);
182     REQUIRE(static_closed.get_constraint().get_lower_bound() == 0);
183     REQUIRE(static_closed.get_constraint().get_upper_bound() == 42);
184 
185     decltype(static_closed) static_closed_default(10);
186     REQUIRE(static_closed_default.get_value() == 10);
187     REQUIRE(static_closed_default.get_constraint().get_lower_bound() == 0);
188     REQUIRE(static_closed_default.get_constraint().get_upper_bound() == 42);
189 
190     constrained_type<int, constraints::open_interval<int, std::integral_constant<int, 0>,
191                                                      std::integral_constant<int, 42>>>
192         static_open = make_bounded_exclusive(10, std::integral_constant<int, 0>{},
193                                              std::integral_constant<int, 42>{});
194     static_assert(std::is_same<decltype(static_open),
195                                bounded_type<int, false, false, std::integral_constant<int, 0>,
196                                             std::integral_constant<int, 42>>>::value,
197                   "");
198     REQUIRE(static_open.get_value() == 10);
199     REQUIRE(static_open.get_constraint().get_lower_bound() == 0);
200     REQUIRE(static_open.get_constraint().get_upper_bound() == 42);
201 
202     decltype(static_open) static_open_default(10);
203     REQUIRE(static_open_default.get_value() == 10);
204     REQUIRE(static_open_default.get_constraint().get_lower_bound() == 0);
205     REQUIRE(static_open_default.get_constraint().get_upper_bound() == 42);
206 
207     constrained_type<int, constraints::closed_interval<int, constraints::dynamic_bound,
208                                                        std::integral_constant<int, 42>>>
209         mixed_closed = make_bounded(10, 0, std::integral_constant<int, 42>{});
210     static_assert(std::is_same<decltype(mixed_closed),
211                                bounded_type<int, true, true, constraints::dynamic_bound,
212                                             std::integral_constant<int, 42>>>::value,
213                   "");
214     REQUIRE(mixed_closed.get_value() == 10);
215     REQUIRE(mixed_closed.get_constraint().get_lower_bound() == 0);
216     REQUIRE(mixed_closed.get_constraint().get_upper_bound() == 42);
217 
218     constrained_type<int, constraints::open_interval<int, constraints::dynamic_bound,
219                                                      std::integral_constant<int, 42>>>
220         mixed_open = make_bounded_exclusive(10, 0, std::integral_constant<int, 42>{});
221     static_assert(std::is_same<decltype(mixed_open),
222                                bounded_type<int, false, false, constraints::dynamic_bound,
223                                             std::integral_constant<int, 42>>>::value,
224                   "");
225     REQUIRE(mixed_open.get_value() == 10);
226     REQUIRE(mixed_open.get_constraint().get_lower_bound() == 0);
227     REQUIRE(mixed_open.get_constraint().get_upper_bound() == 42);
228 }
229 
230 TEST_CASE("clamping_verifier")
231 {
232     SECTION("less_equal")
233     {
234         constraints::less_equal<int> p(42);
235 
236         int a = 0;
237         a     = clamping_verifier::verify(a, p);
238         REQUIRE(a == 0);
239 
240         int b = 30;
241         b     = clamping_verifier::verify(b, p);
242         REQUIRE(b == 30);
243 
244         int c = 42;
245         c     = clamping_verifier::verify(c, p);
246         REQUIRE(c == 42);
247 
248         int d = 50;
249         d     = clamping_verifier::verify(d, p);
250         REQUIRE(d == 42);
251     }
252     SECTION("greater_equal")
253     {
254         constraints::greater_equal<int> p(42);
255 
256         int a = 0;
257         a     = clamping_verifier::verify(a, p);
258         REQUIRE(a == 42);
259 
260         int b = 30;
261         b     = clamping_verifier::verify(b, p);
262         REQUIRE(b == 42);
263 
264         int c = 42;
265         c     = clamping_verifier::verify(c, p);
266         REQUIRE(c == 42);
267 
268         int d = 50;
269         d     = clamping_verifier::verify(d, p);
270         REQUIRE(d == 50);
271     }
272     SECTION("closed_interval")
273     {
274         constraints::closed_interval<int> p(0, 42);
275 
276         int a = 30;
277         a     = clamping_verifier::verify(a, p);
278         REQUIRE(a == 30);
279 
280         int b = 10;
281         b     = clamping_verifier::verify(b, p);
282         REQUIRE(b == 10);
283 
284         int c = 0;
285         c     = clamping_verifier::verify(c, p);
286         REQUIRE(c == 0);
287 
288         int d = 42;
289         d     = clamping_verifier::verify(d, p);
290         REQUIRE(d == 42);
291 
292         int e = 50;
293         e     = clamping_verifier::verify(e, p);
294         REQUIRE(e == 42);
295 
296         int f = -20;
297         f     = clamping_verifier::verify(f, p);
298         REQUIRE(f == 0);
299     }
300 }
301 
302 TEST_CASE("clamped_type")
303 {
304     int value;
305     SECTION("no_clamping")
306     {
307         value = 10;
308     }
309     SECTION("clamping_lower")
310     {
311         value = -42;
312     }
313     SECTION("clamping_upper")
314     {
315         value = 100;
316     }
317 
318     clamped_type<int> dynamic = make_clamped(value, 0, 42);
319 
320     auto clamped_val = value;
321     clamped_val      = clamp(dynamic.get_constraint(), clamped_val);
322     REQUIRE(dynamic.get_value() == clamped_val);
323 
324     REQUIRE(dynamic.get_constraint().get_lower_bound() == 0);
325     REQUIRE(dynamic.get_constraint().get_upper_bound() == 42);
326 
327     clamped_type<int, std::integral_constant<int, 0>, std::integral_constant<int, 42>> static_
328         = make_clamped(value, std::integral_constant<int, 0>{}, std::integral_constant<int, 42>{});
329 
330     clamped_val = value;
331     clamped_val = clamp(static_.get_constraint(), clamped_val);
332     REQUIRE(static_.get_value() == clamped_val);
333 
334     REQUIRE(static_.get_constraint().get_lower_bound() == 0);
335     REQUIRE(static_.get_constraint().get_upper_bound() == 42);
336 
337     clamped_type<int, std::integral_constant<int, 0>> mixed
338         = make_clamped(value, std::integral_constant<int, 0>{}, 42);
339 
340     clamped_val = value;
341     clamped_val = clamp(mixed.get_constraint(), clamped_val);
342     REQUIRE(mixed.get_value() == clamped_val);
343 
344     REQUIRE(mixed.get_constraint().get_lower_bound() == 0);
345     REQUIRE(mixed.get_constraint().get_upper_bound() == 42);
346 }
347