Add is_clamped.hpp, is_clamped_test.cpp

This commit is contained in:
Ivan Matek
2021-07-31 21:20:56 +02:00
parent c9077bd495
commit 4ad181f464
3 changed files with 298 additions and 0 deletions

View File

@ -0,0 +1,73 @@
/*
Copyright (c) Marshall Clow 2008-2012.
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
/// \file is_clamped.hpp
/// \brief IsClamped algorithm
/// \author TODO
///
#ifndef BOOST_ALGORITHM_IS_CLAMPED_HPP
#define BOOST_ALGORITHM_IS_CLAMPED_HPP
#include <functional> // For std::less
#include <iterator> // For std::iterator_traits
#include <cassert>
#include <boost/mpl/identity.hpp> // for identity
namespace boost { namespace algorithm {
/// \fn is_clamped ( T const& val,
/// typename boost::mpl::identity<T>::type const & lo,
/// typename boost::mpl::identity<T>::type const & hi, Pred p )
/// \returns true if value "val" is in the range [ lo, hi ]
/// using the comparison predicate p.
/// If p ( val, lo ) return false.
/// If p ( hi, val ) return false.
/// Otherwise, returns true.
///
/// \param val The value to be checked
/// \param lo The lower bound of the range
/// \param hi The upper bound of the range
/// \param p A predicate to use to compare the values.
/// p ( a, b ) returns a boolean.
///
template <typename T, typename Pred>
BOOST_CXX14_CONSTEXPR bool is_clamped(
T const& val, typename boost::mpl::identity<T>::type const& lo,
typename boost::mpl::identity<T>::type const& hi, Pred p) {
// assert ( !p ( hi, lo )); // Can't assert p ( lo, hi ) b/c they
// might be equal
return p(val, lo) ? false : p(hi, val) ? false : true;
}
/// \fn is_clamped ( T const& val,
/// typename boost::mpl::identity<T>::type const & lo,
/// typename boost::mpl::identity<T>::type const & hi)
/// \returns true if value "val" is in the range [ lo, hi ]
/// using operator < for comparison.
/// If the value is less than lo, return false.
/// If the value is greater than "hi", return false.
/// Otherwise, returns true.
///
/// \param val The value to be checked
/// \param lo The lower bound of the range
/// \param hi The upper bound of the range
///
template<typename T>
BOOST_CXX14_CONSTEXPR bool is_clamped ( const T& val,
typename boost::mpl::identity<T>::type const & lo,
typename boost::mpl::identity<T>::type const & hi )
{
return boost::algorithm::is_clamped ( val, lo, hi, std::less<T>());
}
}}
#endif // BOOST_ALGORITHM_CLAMP_HPP

View File

@ -29,6 +29,7 @@ alias unit_test_framework
# Misc tests # Misc tests
[ run clamp_test.cpp unit_test_framework : : : : clamp_test ] [ run clamp_test.cpp unit_test_framework : : : : clamp_test ]
[ run is_clamped_test.cpp unit_test_framework : : : : is_clamped_test ]
[ run power_test.cpp unit_test_framework : : : : power_test ] [ run power_test.cpp unit_test_framework : : : : power_test ]
[ compile-fail power_fail1.cpp : : : : ] [ compile-fail power_fail1.cpp : : : : ]

224
test/is_clamped_test.cpp Normal file
View File

@ -0,0 +1,224 @@
// (C) Copyright TODO
// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/algorithm/clamp.hpp>
#include <boost/algorithm/is_clamped.hpp>
#include <cmath>
#include <cstdint>
#ifdef __cpp_impl_three_way_comparison
#include <compare>
#endif
#include <limits>
#include <string>
#include <tuple>
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
namespace ba = boost::algorithm;
BOOST_CONSTEXPR bool intGreater(int lhs, int rhs) { return lhs > rhs; }
class custom {
public:
custom(int x) : v(x) {}
custom(const custom &rhs) : v(rhs.v) {}
bool operator<(const custom &rhs) const { return v < rhs.v; }
bool operator==(const custom &rhs) const {
return v == rhs.v;
} // need this for the test
int v;
};
static bool customLess(const custom &lhs, const custom &rhs) { return lhs.v < rhs.v; }
void test_ints() {
// Inside the range, equal to the endpoints, and outside the endpoints.
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 3, 1, 6 ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 1, 1, 6 ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 6, 1, 6 ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 0, 1, 6 ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 7, 1, 6 ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 3, 6, 1, intGreater ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 1, 6, 1, intGreater ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 6, 6, 1, intGreater ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 0, 6, 1, intGreater ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 7, 6, 1, intGreater ));
// Negative numbers
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( -3, -6, -1 ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( -1, -6, -1 ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( -6, -6, -1 ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 0, -6, -1 ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( -7, -6, -1 ));
// Mixed positive and negative numbers
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 1, -5, 5 ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( -5, -5, 5 ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 5, -5, 5 ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( -6, -5, 5 ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 6, -5, 5 ));
// Unsigned
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 5U, 1U, 6U ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 1U, 1U, 6U ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 6U, 1U, 6U ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 0U, 1U, 6U ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 8U, 1U, 6U ));
// Mixed (1)
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 5U, 1, 6 ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 1U, 1, 6 ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 6U, 1, 6 ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 0U, 1, 6 ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 8U, 1, 6 ));
// Mixed (2)
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 5U, 1, 6. ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 1U, 1, 6. ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( 6U, 1, 6. ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 0U, 1, 6. ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( 8U, 1, 6. ));
short foo = 50;
// float->short conversion does not round
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( foo, 50.999, 100 ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( foo, 51.001, 100 ));
}
void test_floats() {
// If floats are IEEE754 certain floats have exact representations.
if (std::numeric_limits<float>::is_iec559) {
const float lo = 0.125;
const float hi = 0.625;
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( lo, lo, hi ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( hi, lo, hi ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( lo + 0.01, lo, hi ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( lo - 0.01, lo, hi ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( hi + 0.01, lo, hi ));
// If we have nextafterf we can be more precise.
#if __cplusplus >= 201103L
assert(lo < hi);
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( hi, lo, hi ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( std::nextafterf( lo, hi ), lo, hi ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( std::nextafterf( hi, lo ), lo, hi ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( (lo + hi) / 2, lo, hi ));
// 1.0 is just for direction of nextafterf, value of 1.0 is not significant.
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( std::nextafterf( lo, lo - 1.0f ), lo, hi ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( std::nextafterf( hi, hi + 1.0f ), lo, hi ));
#endif
}
}
void test_std_string() {
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( std::string("a3"), std::string("a1"), std::string("a6") ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( std::string("a1"), std::string("a1"), std::string("a6") ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( std::string("a6"), std::string("a1"), std::string("a6") ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( std::string("a7"), std::string("a1"), std::string("a6") ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( std::string("a0"), std::string("a1"), std::string("a6") ));
}
void test_custom() {
// Inside the range, equal to the endpoints, and outside the endpoints.
const custom c0(0);
const custom c1(1);
const custom c3(3);
const custom c6(6);
const custom c7(7);
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( c3, c1, c6 ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( c1, c1, c6 ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( c6, c1, c6 ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( c0, c1, c6 ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( c7, c1, c6 ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( c3, c1, c6, customLess ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( c1, c1, c6, customLess ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped ( c6, c1, c6, customLess ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( c0, c1, c6, customLess ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped ( c7, c1, c6, customLess ));
}
void test_constexpr() {
#if __cplusplus >= 201402L
{
constexpr bool is_clamped = (ba::is_clamped ( 3, 1, 6 ));
BOOST_CHECK_EQUAL (true, is_clamped );
}
{
constexpr bool is_clamped = (ba::is_clamped ( 1, 1, 6 ));
BOOST_CHECK_EQUAL (true, is_clamped );
}
{
constexpr bool is_clamped = (ba::is_clamped ( 6, 1, 6 ));
BOOST_CHECK_EQUAL (true, is_clamped );
}
{
constexpr bool is_clamped = (ba::is_clamped ( 0, 1, 6 ));
BOOST_CHECK_EQUAL(false, is_clamped );
}
{
constexpr bool is_clamped = (ba::is_clamped ( 7, 1, 6 ));
BOOST_CHECK_EQUAL(false, is_clamped );
}
#endif
}
#ifdef __cpp_impl_three_way_comparison
struct custom_with_spaceship {
int v;
auto operator<=>(const custom_with_spaceship&) const = default;
};
#endif
void test_spaceship() {
#ifdef __cpp_impl_three_way_comparison
// Inside the range, equal to the endpoints, and outside the endpoints.
const custom_with_spaceship c0(0);
const custom_with_spaceship c1(1);
const custom_with_spaceship c3(3);
const custom_with_spaceship c6(6);
const custom_with_spaceship c7(7);
BOOST_CHECK_EQUAL ( true, ba::is_clamped (c3, c1, c6 ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped (c1, c1, c6 ));
BOOST_CHECK_EQUAL ( true, ba::is_clamped (c6, c1, c6 ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped (c0, c1, c6 ));
BOOST_CHECK_EQUAL ( false, ba::is_clamped (c7, c1, c6 ));
{
constexpr custom_with_spaceship c1(1);
constexpr custom_with_spaceship c3(3);
constexpr custom_with_spaceship c6(6);
constexpr bool clamped = ba::is_clamped (c3, c1, c6 );
BOOST_CHECK_EQUAL ( true, clamped );
}
#endif
}
BOOST_AUTO_TEST_CASE(test_main) {
test_ints();
test_floats();
test_std_string();
test_custom();
test_constexpr();
test_spaceship();
}
#if __cplusplus >= 201103L
typedef std::tuple<std::uint8_t, std::int8_t, std::uint16_t, std::int16_t,
std::int32_t, std::uint32_t, std::int64_t, std::uint64_t> test_types_tuple;
BOOST_AUTO_TEST_CASE_TEMPLATE(test_extremes, T, test_types_tuple) {
const T max = std::numeric_limits<T>::max();
BOOST_CHECK_EQUAL(true, ba::is_clamped( max, max, max ));
BOOST_CHECK_EQUAL(true, ba::is_clamped( max, max - 1, max ));
BOOST_CHECK_EQUAL(false, ba::is_clamped( max - 1, max, max ));
const T min = std::numeric_limits<T>::min();
BOOST_CHECK_EQUAL(true, ba::is_clamped( min, min, min ));
BOOST_CHECK_EQUAL(true, ba::is_clamped( min, min, min + 1 ));
BOOST_CHECK_EQUAL(false, ba::is_clamped( min, min + 1, min + 1 ));
}
#endif