Skip to content

Commit 2a05506

Browse files
authored
Merge pull request #86 from libbooze/add_is_clamped_to_boost_algorithm
Add is_clamped.hpp, is_clamped_test.cpp
2 parents c5f6f52 + 9d5ddb6 commit 2a05506

File tree

3 files changed

+319
-0
lines changed

3 files changed

+319
-0
lines changed

Diff for: include/boost/algorithm/is_clamped.hpp

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
Copyright (c) Ivan Matek, Marshall Clow 2021.
3+
4+
Distributed under the Boost Software License, Version 1.0. (See accompanying
5+
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6+
7+
*/
8+
9+
/// \file is_clamped.hpp
10+
/// \brief IsClamped algorithm
11+
/// \authors Ivan Matek, Marshall Clow
12+
///
13+
14+
#ifndef BOOST_ALGORITHM_IS_CLAMPED_HPP
15+
#define BOOST_ALGORITHM_IS_CLAMPED_HPP
16+
17+
#include <functional> // for std::less
18+
#include <cassert>
19+
20+
#include <boost/type_traits/type_identity.hpp> // for boost::type_identity
21+
22+
namespace boost { namespace algorithm {
23+
24+
/// \fn is_clamped ( T const& val,
25+
/// typename boost::type_identity<T>::type const & lo,
26+
/// typename boost::type_identity<T>::type const & hi, Pred p )
27+
/// \returns true if value "val" is in the range [ lo, hi ]
28+
/// using the comparison predicate p.
29+
/// If p ( val, lo ) return false.
30+
/// If p ( hi, val ) return false.
31+
/// Otherwise, returns true.
32+
///
33+
/// \param val The value to be checked
34+
/// \param lo The lower bound of the range
35+
/// \param hi The upper bound of the range
36+
/// \param p A predicate to use to compare the values.
37+
/// p ( a, b ) returns a boolean.
38+
///
39+
template <typename T, typename Pred>
40+
BOOST_CXX14_CONSTEXPR bool is_clamped(
41+
T const& val, typename boost::type_identity<T>::type const& lo,
42+
typename boost::type_identity<T>::type const& hi, Pred p) {
43+
// assert ( !p ( hi, lo )); // Can't assert p ( lo, hi ) b/c they
44+
// might be equal
45+
return p(val, lo) ? false : p(hi, val) ? false : true;
46+
}
47+
48+
/// \fn is_clamped ( T const& val,
49+
/// typename boost::type_identity<T>::type const & lo,
50+
/// typename boost::type_identity<T>::type const & hi)
51+
/// \returns true if value "val" is in the range [ lo, hi ]
52+
/// using operator < for comparison.
53+
/// If the value is less than lo, return false.
54+
/// If the value is greater than hi, return false.
55+
/// Otherwise, returns true.
56+
///
57+
/// \param val The value to be checked
58+
/// \param lo The lower bound of the range
59+
/// \param hi The upper bound of the range
60+
///
61+
62+
template<typename T>
63+
BOOST_CXX14_CONSTEXPR bool is_clamped ( const T& val,
64+
typename boost::type_identity<T>::type const & lo,
65+
typename boost::type_identity<T>::type const & hi )
66+
{
67+
return boost::algorithm::is_clamped ( val, lo, hi, std::less<T>());
68+
}
69+
70+
}}
71+
72+
#endif // BOOST_ALGORITHM_CLAMP_HPP

Diff for: test/Jamfile.v2

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ alias unit_test_framework
2929

3030
# Misc tests
3131
[ run clamp_test.cpp unit_test_framework : : : : clamp_test ]
32+
[ run is_clamped_test.cpp unit_test_framework : : : : is_clamped_test ]
3233
[ run power_test.cpp unit_test_framework : : : : power_test ]
3334
[ compile-fail power_fail1.cpp : : : : ]
3435

Diff for: test/is_clamped_test.cpp

+246
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
// (C) Copyright Ivan Matek, Marshall Clow 2021.
2+
// Use, modification and distribution are subject to the
3+
// Boost Software License, Version 1.0. (See accompanying file
4+
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5+
6+
#include <boost/algorithm/is_clamped.hpp>
7+
#include <boost/algorithm/clamp.hpp>
8+
#include <cmath>
9+
#include <cstdint>
10+
#ifdef __cpp_impl_three_way_comparison
11+
#if __has_include(<compare>)
12+
#define BOOST_IS_CLAMPED_TEST_SPACESHIP
13+
#endif
14+
#ifdef BOOST_IS_CLAMPED_TEST_SPACESHIP
15+
#include <compare>
16+
#endif
17+
#endif
18+
#include <limits>
19+
#include <string>
20+
#include <tuple>
21+
22+
#define BOOST_TEST_MAIN
23+
#include <boost/test/unit_test.hpp>
24+
25+
namespace ba = boost::algorithm;
26+
27+
BOOST_CONSTEXPR bool intGreater(int lhs, int rhs) { return lhs > rhs; }
28+
29+
class custom {
30+
public:
31+
custom(int x) : v(x) {}
32+
custom(const custom &rhs) : v(rhs.v) {}
33+
34+
bool operator<(const custom &rhs) const { return v < rhs.v; }
35+
bool operator==(const custom &rhs) const {
36+
return v == rhs.v;
37+
} // need this for the test
38+
int v;
39+
};
40+
41+
static bool customLess(const custom &lhs, const custom &rhs) { return lhs.v < rhs.v; }
42+
43+
void test_ints() {
44+
// Inside the range, equal to the endpoints, and outside the endpoints.
45+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 3, 1, 6 ));
46+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 1, 1, 6 ));
47+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 6, 1, 6 ));
48+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 0, 1, 6 ));
49+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 7, 1, 6 ));
50+
51+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 3, 6, 1, intGreater ));
52+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 1, 6, 1, intGreater ));
53+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 6, 6, 1, intGreater ));
54+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 0, 6, 1, intGreater ));
55+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 7, 6, 1, intGreater ));
56+
57+
// Negative numbers
58+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( -3, -6, -1 ));
59+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( -1, -6, -1 ));
60+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( -6, -6, -1 ));
61+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 0, -6, -1 ));
62+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( -7, -6, -1 ));
63+
64+
// Mixed positive and negative numbers
65+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 1, -5, 5 ));
66+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( -5, -5, 5 ));
67+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 5, -5, 5 ));
68+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( -6, -5, 5 ));
69+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 6, -5, 5 ));
70+
71+
// Unsigned
72+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 5U, 1U, 6U ));
73+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 1U, 1U, 6U ));
74+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 6U, 1U, 6U ));
75+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 0U, 1U, 6U ));
76+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 8U, 1U, 6U ));
77+
78+
// Mixed (1)
79+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 5U, 1, 6 ));
80+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 1U, 1, 6 ));
81+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 6U, 1, 6 ));
82+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 0U, 1, 6 ));
83+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 8U, 1, 6 ));
84+
85+
// Mixed (2)
86+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 5U, 1, 6. ));
87+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 1U, 1, 6. ));
88+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 6U, 1, 6. ));
89+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 0U, 1, 6. ));
90+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 8U, 1, 6. ));
91+
92+
// float->short conversion does not round
93+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 50, 50.999, 100 ));
94+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 50, 51.001, 100 ));
95+
}
96+
97+
void test_floats() {
98+
// If floats are IEEE754 certain floats have exact representations.
99+
if (std::numeric_limits<float>::is_iec559) {
100+
const float lo = 0.125;
101+
const float hi = 0.625;
102+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( lo, lo, hi ));
103+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( hi, lo, hi ));
104+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( lo + 0.01, lo, hi ));
105+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( lo - 0.01, lo, hi ));
106+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( hi + 0.01, lo, hi ));
107+
// If we have nextafterf we can be more precise.
108+
#if __cplusplus >= 201103L
109+
assert(lo < hi);
110+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( hi, lo, hi ));
111+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( std::nextafterf( lo, hi ), lo, hi ));
112+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( std::nextafterf( hi, lo ), lo, hi ));
113+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( (lo + hi) / 2, lo, hi ));
114+
// 1.0 is just for direction of nextafterf, value of 1.0 is not significant.
115+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( std::nextafterf( lo, lo - 1.0f ), lo, hi ));
116+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( std::nextafterf( hi, hi + 1.0f ), lo, hi ));
117+
#endif
118+
}
119+
}
120+
121+
void test_std_string() {
122+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( std::string("a3"), std::string("a1"), std::string("a6") ));
123+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( std::string("a1"), std::string("a1"), std::string("a6") ));
124+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( std::string("a6"), std::string("a1"), std::string("a6") ));
125+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( std::string("a7"), std::string("a1"), std::string("a6") ));
126+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( std::string("a0"), std::string("a1"), std::string("a6") ));
127+
}
128+
129+
void test_custom() {
130+
// Inside the range, equal to the endpoints, and outside the endpoints.
131+
const custom c0(0);
132+
const custom c1(1);
133+
const custom c3(3);
134+
const custom c6(6);
135+
const custom c7(7);
136+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( c3, c1, c6 ));
137+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( c1, c1, c6 ));
138+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( c6, c1, c6 ));
139+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( c0, c1, c6 ));
140+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( c7, c1, c6 ));
141+
142+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( c3, c1, c6, customLess ));
143+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( c1, c1, c6, customLess ));
144+
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( c6, c1, c6, customLess ));
145+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( c0, c1, c6, customLess ));
146+
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( c7, c1, c6, customLess ));
147+
}
148+
149+
void test_point_interval() {
150+
BOOST_CHECK_EQUAL(true, ba::is_clamped(1, 1, 1));
151+
BOOST_CHECK_EQUAL(false, ba::is_clamped(0, 1, 1));
152+
BOOST_CHECK_EQUAL(false, ba::is_clamped(2, 1, 1));
153+
}
154+
155+
void test_first_argument_determines_types() {
156+
// all arguments are int
157+
BOOST_CHECK_EQUAL(true, ba::is_clamped(5, 5.9, 6.1));
158+
BOOST_CHECK_EQUAL(true, ba::is_clamped(6, 5.9, 6.1));
159+
// all arguments are double
160+
BOOST_CHECK_EQUAL(false, ba::is_clamped(5.0, 5.9, 6.1));
161+
BOOST_CHECK_EQUAL(false, ba::is_clamped(6.2, 5.9, 6.1));
162+
}
163+
164+
void test_constexpr() {
165+
#if __cplusplus >= 201402L
166+
{
167+
constexpr bool is_clamped = (ba::is_clamped ( 3, 1, 6 ));
168+
BOOST_CHECK_EQUAL (true, is_clamped );
169+
}
170+
{
171+
constexpr bool is_clamped = (ba::is_clamped ( 1, 1, 6 ));
172+
BOOST_CHECK_EQUAL (true, is_clamped );
173+
}
174+
{
175+
constexpr bool is_clamped = (ba::is_clamped ( 6, 1, 6 ));
176+
BOOST_CHECK_EQUAL (true, is_clamped );
177+
}
178+
{
179+
constexpr bool is_clamped = (ba::is_clamped ( 0, 1, 6 ));
180+
BOOST_CHECK_EQUAL(false, is_clamped );
181+
}
182+
{
183+
constexpr bool is_clamped = (ba::is_clamped ( 7, 1, 6 ));
184+
BOOST_CHECK_EQUAL(false, is_clamped );
185+
}
186+
#endif
187+
}
188+
189+
190+
#ifdef BOOST_IS_CLAMPED_TEST_SPACESHIP
191+
struct custom_with_spaceship {
192+
int v;
193+
auto operator<=>(const custom_with_spaceship&) const = default;
194+
};
195+
#endif
196+
197+
void test_spaceship() {
198+
#ifdef BOOST_IS_CLAMPED_TEST_SPACESHIP
199+
// Inside the range, equal to the endpoints, and outside the endpoints.
200+
const custom_with_spaceship c0(0);
201+
const custom_with_spaceship c1(1);
202+
const custom_with_spaceship c3(3);
203+
const custom_with_spaceship c6(6);
204+
const custom_with_spaceship c7(7);
205+
BOOST_CHECK_EQUAL ( true, ba::is_clamped (c3, c1, c6 ));
206+
BOOST_CHECK_EQUAL ( true, ba::is_clamped (c1, c1, c6 ));
207+
BOOST_CHECK_EQUAL ( true, ba::is_clamped (c6, c1, c6 ));
208+
BOOST_CHECK_EQUAL ( false, ba::is_clamped (c0, c1, c6 ));
209+
BOOST_CHECK_EQUAL ( false, ba::is_clamped (c7, c1, c6 ));
210+
{
211+
constexpr custom_with_spaceship c1(1);
212+
constexpr custom_with_spaceship c3(3);
213+
constexpr custom_with_spaceship c6(6);
214+
constexpr bool clamped = ba::is_clamped (c3, c1, c6 );
215+
BOOST_CHECK_EQUAL ( true, clamped );
216+
}
217+
#endif
218+
}
219+
220+
BOOST_AUTO_TEST_CASE(test_main) {
221+
test_ints();
222+
test_floats();
223+
test_std_string();
224+
test_custom();
225+
test_point_interval();
226+
test_first_argument_determines_types();
227+
test_constexpr();
228+
test_spaceship();
229+
}
230+
231+
#if __cplusplus >= 201103L
232+
typedef std::tuple<std::uint8_t, std::int8_t, std::uint16_t, std::int16_t,
233+
std::int32_t, std::uint32_t, std::int64_t, std::uint64_t> test_types_tuple;
234+
235+
BOOST_AUTO_TEST_CASE_TEMPLATE(test_extremes, T, test_types_tuple) {
236+
const T max = std::numeric_limits<T>::max();
237+
BOOST_CHECK_EQUAL(true, ba::is_clamped( max, max, max ));
238+
BOOST_CHECK_EQUAL(true, ba::is_clamped( max, max - 1, max ));
239+
BOOST_CHECK_EQUAL(false, ba::is_clamped( max - 1, max, max ));
240+
241+
const T min = std::numeric_limits<T>::min();
242+
BOOST_CHECK_EQUAL(true, ba::is_clamped( min, min, min ));
243+
BOOST_CHECK_EQUAL(true, ba::is_clamped( min, min, min + 1 ));
244+
BOOST_CHECK_EQUAL(false, ba::is_clamped( min, min + 1, min + 1 ));
245+
}
246+
#endif

0 commit comments

Comments
 (0)