diff --git a/docs/_index.md b/docs/_index.md index dc6fde75..c31c6a6d 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -118,9 +118,9 @@ CRC calculations (8, 16, 32 & 64-bit) Checksums and hash functions - Variants (type-safe unions) - Extensive template support utilities -- Robust Error Handling: Configurable error checking using asserts, exceptions, error handlers, or no checks; user’s choice. +- Robust Error Handling: Configurable error checking using asserts, exceptions, error handlers, or no checks; user's choice. - Thoroughly Tested: -Over 10,000 unit tests +Over **14,000** tests Tested with Visual Studio 2022, GCC 12, and Clang 14 Continuous integration via GitHub Actions. - Readable and Well-Documented: Clean, maintainable source code with clear documentation. @@ -128,6 +128,7 @@ Continuous integration via GitHub Actions. - Archived: A snapshot of the ETL is preserved in the Arctic Code Vault for long-term digital preservation. ## Support the ETL + Maintaining the ETL can take a lot of man-hours of work, but unfortunately it doesn't pay the bills. When I have to take on paying work, the ETL gets a lot less attention. So if you have found the library is an important component in your work and you would like to help out, then please consider by supporting the project. --- diff --git a/docs/utilities/cyclic-value.md b/docs/utilities/cyclic-value.md index 37a92b65..d29a7b42 100644 --- a/docs/utilities/cyclic-value.md +++ b/docs/utilities/cyclic-value.md @@ -15,6 +15,9 @@ Supports compile time and runtime variants. template class cyclic_value ``` +Static asserts if `Last` < `First`. + +**Example** ```cpp etl::cyclic_value value_ct; // Compile time. Fixed range of 2 to 7 inclusive. @@ -24,126 +27,183 @@ value_rt.set(3, 8); ``` ## Constructor + +### Run time limits + ```cpp -cyclic_value value; +cyclic_value() noexcept; ``` -Creates a runtime cyclic value of type int with default constructed initial defined limits. +Constructs a cyclic value of type `T` with an initial value of `first`. +`constexpr` from C++11. --- ```cpp -cyclic_value value; +cyclic_value(T first, T last); ``` -Creates a compile time cyclic value of type int with fixed limits of N and M. +Constructs a *run time* cyclic value of type `T` with limits of `first` and `last` and an initial value of `first`. +Asserts an `etl::cyclic_value_reversed_limits` if `last` < `first`. +`constexpr` from C++11. + +--- -**Example** ```cpp -cyclic_value value(N, M); +cyclic_value(T first, T last, T initial); ``` -Creates a runtime cyclic value of type int with initial defined limits of N and M. +Constructs a cyclic value of type `T` with limits of `first` and `last` and an initial value of `initial`. +`initial` will be clamped to be in the range `(first, last)`. +Asserts an `etl::cyclic_value_reversed_limits` if `last` < `first`. +`constexpr` from C++11. + +### Compile time limits + +```cpp +cyclic_value() noexcept; +``` +Constructs a cyclic value of type `T` default constructed with an initial value of `First`. +`constexpr` from C++11. + +--- + +```cpp +cyclic_value(T initial) noexcept; +``` +Constructs a cyclic value of type `T` constructed with an `initial`. +`initial` will be clamped to be in the range `(First, Last)`. +`constexpr` from C++11. ## Modifiers + ```cpp -cyclic_value& operator ++(); -cyclic_value& operator ++(int); +cyclic_value& operator ++() noexcept; +cyclic_value& operator ++(int) noexcept; ``` -Increments the value. If the value is at the last value then is set to the first. +Increments the value. If the value is at the last value then is set to the first. +`constexpr` from C++14. --- ```cpp -cyclic_value& operator --(); -cyclic_value& operator --(int); +cyclic_value& operator --() noexcept; +cyclic_value& operator --(int) noexcept; ``` -Decrements the value. If the value is at the first value then is set to the last. +Decrements the value. If the value is at the first value then is set to the last. +`constexpr` from C++14. --- ```cpp -void advance(int n); +template +void advance(TStep n) noexcept; ``` -Advances the value by the specified amount, wrapping if necessary. +Advances the value by the specified amount, wrapping if necessary. +`constexpr` from C++14. + +--- + +```cpp +template +cyclic_value& operator +=(TStep n) noexcept; +``` +Advances the value by the specified amount, wrapping if necessary. +`constexpr` from C++14. + +--- + +```cpp +template +cyclic_value& operator -=(TStep n) noexcept; +``` +Decrements the value by the specified amount, wrapping if necessary. +`constexpr` from C++14. ## Access + ```cpp T get() const; ``` -Gets the current value +Gets the current value. +`constexpr` from C++11. --- ```cpp -T first() const; +T first() const noexcept; ``` -Sets the current value to the first value +Gets the first value. +`constexpr` from C++11. --- ```cpp -T last() const; +T last() const noexcept; ``` -Sets the current value to the last value +Gets the last value. +`constexpr` from C++11. --- ```cpp -void set(T first, T last); +void set(T first, T last) noexcept; ``` -Sets the new first and last values. Sets the current value to first. +Sets the new *run time* `first` and `last` values. Sets the current value to `first`. +Asserts an `etl::cyclic_value_reversed_limits` if `last` < `first`. +`constexpr` from C++14. --- ```cpp -void set(T value); +void set(T value) noexcept; ``` -Sets the current value. +Sets the current value. +`constexpr` from C++14. --- ```cpp -void to_first(); +void to_first() noexcept; ``` -Sets the current value to the first value +Sets the current value to the first value. +`constexpr` from C++14. --- ```cpp -void to_last(); +void to_last() noexcept; ``` -Sets the current value to the last value +Sets the current value to the last value. +`constexpr` from C++14. ## Operations ```cpp -void swap(cyclic_value& other); +void swap(cyclic_value& other) noexcept; ``` -Swaps with another cyclic value. +Swaps with another cyclic value. +`constexpr` from C++14. ---- - -```cpp -void swap(cyclic_value& lhs, cyclic_value& rhs); -``` -Swaps with another cyclic value. ## Operators + ```cpp -operator T(); -operator const T() const; +operator T() noexcept; +operator const T() const noexcept; ``` -Conversion operators to `T`. +Conversion operators to `T`. +`constexpr` from C++11. --- ```cpp -cyclic_value& operator =(T t); +cyclic_value& operator =(T t) noexcept; ``` -Sets the current value to `t`. +Sets the current value to `t`. +`constexpr` from C++14. ## Non-member functions ```cpp template -void swap(cyclic_value& lhs, cyclic_value& rhs) +void swap(cyclic_value& lhs, cyclic_value& rhs) noexcept ``` Swaps two cyclic values. @@ -151,14 +211,63 @@ Swaps two cyclic values. ```cpp template -bool operator == (cyclic_value& lhs, cyclic_value& rhs) +bool operator == (cyclic_value& lhs, cyclic_value& rhs) noexcept ``` -Checks equality of two cyclic values. +Checks equality of two cyclic values. +`constexpr` from C++11. + +{{< callout type="warning">}} + After version `20.49.0` equality is judged only by the current value. + Previously the first and last limits were also considered. + + <= 20.49.0 + `etl::cyclic_value<2, 7>(3) != etl::cyclic_value<2, 6>(3)` + + > 20.49.0 + `etl::cyclic_value<2, 7>(3) == etl::cyclic_value<2, 6>(3)` +{{< /callout >}} --- ```cpp template -bool operator != (cyclic_value& lhs, cyclic_value& rhs) +bool operator != (cyclic_value& lhs, cyclic_value& rhs) noexcept ``` -Checks inequality of two cyclic values. +Checks inequality of two cyclic values. +`constexpr` from C++11. + +--- + +```cpp +template +bool operator < (cyclic_value& lhs, cyclic_value& rhs) noexcept +``` +Checks if the current value of `lhs` is less than the current value of `rhs`. +`constexpr` from C++11. + +--- + +```cpp +template +bool operator <= (cyclic_value& lhs, cyclic_value& rhs) noexcept +``` +Checks if the current value of `lhs` is less than or equal the current value of `rhs`. +`constexpr` from C++11. + +--- + +```cpp +template +bool operator > (cyclic_value& lhs, cyclic_value& rhs) noexcept +``` +Checks if the current value of `lhs` is greater than the current value of `rhs`. +`constexpr` from C++11. + +--- + +```cpp +template +bool operator >= (cyclic_value& lhs, cyclic_value& rhs) noexcept +``` +Checks if the current value of `lhs` is greater than or equal the current value of `rhs`. +`constexpr` from C++11. diff --git a/include/etl/cyclic_value.h b/include/etl/cyclic_value.h index 964ed664..6acdd1f1 100644 --- a/include/etl/cyclic_value.h +++ b/include/etl/cyclic_value.h @@ -38,10 +38,198 @@ SOFTWARE. #include "platform.h" #include "algorithm.h" #include "exception.h" +#include "integral_limits.h" +#include "largest.h" +#include "negative.h" +#include "static_assert.h" #include "type_traits.h" namespace etl { + namespace private_cyclic_value + { + template + struct traits + { + typedef typename etl::make_unsigned::type unsigned_type; + }; + + //************************************************************************* + /// Increments a value and wraps it at the supplied limits. + ///\param value The current value. + ///\param min_value The minimum value. + ///\param max_value The maximum value. + ///\return The new value. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T increment(T value, T min_value, T max_value) ETL_NOEXCEPT + { + return value == max_value ? min_value : static_cast(etl::to_unsigned(value) + 1U); + } + + //************************************************************************* + /// Decrements a value and wraps it at the supplied limits. + ///\param value The current value. + ///\param min_value The minimum value. + ///\param max_value The maximum value. + ///\return The new value. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T decrement(T value, T min_value, T max_value) ETL_NOEXCEPT + { + return value == min_value ? max_value : static_cast(etl::to_unsigned(value) - 1U); + } + + //************************************************************************* + /// Advances a value and wraps it at the supplied limits. + /// Called for the case where the range is not the full width of the type. + ///\param value The current value. + ///\param min_value The minimum value. + ///\param max_value The maximum value. + ///\param step The number of steps. + ///\param subtract True if the value is to be decremented, false if it is to be incremented. + ///\return The advanced value. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, typename traits::unsigned_type step, bool subtract) ETL_NOEXCEPT + { + typedef typename traits::unsigned_type unsigned_type; + + switch (step) + { + case 0: + { + return value; + } + + case 1: + { + return subtract ? decrement(value, min_value, max_value) : increment(value, min_value, max_value); + } + + default: + { + if (subtract) + { + unsigned_type distance_to_first = static_cast(etl::to_unsigned(value) - etl::to_unsigned(min_value)); + + if (step <= distance_to_first) + { + // Room to subtract the step. + value = static_cast(etl::to_unsigned(value) - step); + } + else + { + // Step would roll over. + step -= distance_to_first + 1U; + value = static_cast(etl::to_unsigned(max_value) - step); + } + } + else + { + unsigned_type distance_to_last = static_cast(etl::to_unsigned(max_value) - etl::to_unsigned(value)); + + if (step <= distance_to_last) + { + // Room to add the step. + value = static_cast(etl::to_unsigned(value) + step); + } + else + { + // Step would roll over. + step -= distance_to_last + 1U; + value = static_cast(etl::to_unsigned(min_value) + step); + } + } + + return value; + } + } + } + + //************************************************************************* + /// Advances a value and wraps it at the supplied limits. + /// Called for the case where the range is the full width of the type. + ///\param value The current value. + ///\param step The number of steps. + ///\param subtract True if the value is to be decremented, false if it is to be incremented. + ///\return The advanced value. + //************************************************************************* + template + ETL_NODISCARD ETL_CONSTEXPR14 T advance(T value, typename traits::unsigned_type step, bool subtract) ETL_NOEXCEPT + { + if (subtract) + { + value = static_cast(etl::to_unsigned(value) - step); + } + else + { + value = static_cast(etl::to_unsigned(value) + step); + } + + return value; + } + + //************************************************************************* + // Provides information about a range. + // Ensures that Last - First + 1 is only calculated for when the range is not the full width of the type, to avoid overflow errors. + //************************************************************************* + template ::min) && (Last == etl::integral_limits::max))> + struct range_traits; + + //************************************************************************* + // Specialisation for when the range is the full width of the type. + template + struct range_traits + { + typedef typename traits::unsigned_type unsigned_type; + + static const bool is_full_range = true; + static const unsigned_type value = 0; // Unused, but required for template specialisation. + }; + + //************************************************************************* + // Specialisation for when the range is not the full width of the type. + template + struct range_traits + { + typedef typename traits::unsigned_type unsigned_type; + + static const bool is_full_range = false; + static const unsigned_type value = static_cast(static_cast(Last) - static_cast(First)) + 1U; + }; + + // Out-of-class definitions (required pre-C++17 for ODR-use of static const members). + template + ETL_CONSTANT bool range_traits::is_full_range; + + template + ETL_CONSTANT typename range_traits::unsigned_type range_traits::value; + + template + ETL_CONSTANT bool range_traits::is_full_range; + + template + ETL_CONSTANT typename range_traits::unsigned_type range_traits::value; + + } // namespace private_cyclic_value + + struct cyclic_value_exception : etl::exception + { + cyclic_value_exception(string_type reason_, string_type file_, numeric_type line_) + : etl::exception(reason_, file_, line_) + { + } + }; + + struct cyclic_value_reversed_limits : cyclic_value_exception + { + cyclic_value_reversed_limits(string_type file_, numeric_type line_) + : cyclic_value_exception(ETL_ERROR_TEXT("cyclic_value_exception:reversed limits", ETL_CYCLIC_VALUE_FILE_ID"A"), file_, line_) + { + } + }; + //*************************************************************************** /// Provides a value that cycles between two limits. //*************************************************************************** @@ -59,13 +247,20 @@ namespace etl template class cyclic_value { + private: + + typedef typename private_cyclic_value::traits::unsigned_type unsigned_type; + public: + ETL_STATIC_ASSERT(etl::is_integral::value, "T must be an integral type"); + ETL_STATIC_ASSERT(First <= Last, "First is not <= Last"); + //************************************************************************* /// Default constructor. /// The initial value is set to the first value. //************************************************************************* - ETL_CONSTEXPR cyclic_value() + ETL_CONSTEXPR cyclic_value() ETL_NOEXCEPT : value(First) { } @@ -75,15 +270,15 @@ namespace etl /// Set to an initial value. /// Clamped to the range. //************************************************************************* - ETL_CONSTEXPR14 explicit cyclic_value(T initial) + ETL_CONSTEXPR explicit cyclic_value(T initial) ETL_NOEXCEPT + : value(etl::clamp(initial, First, Last)) { - set(initial); } //************************************************************************* /// Copy constructor. //************************************************************************* - ETL_CONSTEXPR cyclic_value(const cyclic_value& other) + ETL_CONSTEXPR cyclic_value(const cyclic_value& other) ETL_NOEXCEPT : value(other.value) { } @@ -91,19 +286,48 @@ namespace etl //************************************************************************* /// Assignment operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) + ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { value = other.value; return *this; } + //************************************************************************* + /// Adds a number of steps and clamps to the range. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator+=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + advance(n); + return *this; + } + + //************************************************************************* + /// Subtracts a number of steps and clamps to the range. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator-=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + do_advance(n, !etl::is_negative(n)); + + return *this; + } + //************************************************************************* /// Sets the value. /// Truncates to the First/Last range. ///\param value The value. //************************************************************************* - ETL_CONSTEXPR14 void set(T value_) + ETL_CONSTEXPR14 void set(T value_) ETL_NOEXCEPT { value = etl::clamp(value_, First, Last); } @@ -111,7 +335,7 @@ namespace etl //************************************************************************* /// Resets the value to the first in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_first() + ETL_CONSTEXPR14 void to_first() ETL_NOEXCEPT { value = First; } @@ -119,7 +343,7 @@ namespace etl //************************************************************************* /// Resets the value to the last in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_last() + ETL_CONSTEXPR14 void to_last() ETL_NOEXCEPT { value = Last; } @@ -128,30 +352,13 @@ namespace etl /// Advances to value by a number of steps. ///\param n The number of steps to advance. //************************************************************************* - ETL_CONSTEXPR14 void advance(int n) + template + ETL_CONSTEXPR14 T advance(TStep n) ETL_NOEXCEPT { - if (n > 0) - { - for (int i = 0; i < n; ++i) - { - operator++(); - } - } - else - { - for (int i = 0; i < -n; ++i) - { - operator--(); - } - } - } + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + do_advance(n, etl::is_negative(n)); - //************************************************************************* - /// Conversion operator. - /// \return The value of the underlying type. - //************************************************************************* - ETL_CONSTEXPR14 operator T() - { return value; } @@ -159,7 +366,7 @@ namespace etl /// Const conversion operator. /// \return The value of the underlying type. //************************************************************************* - ETL_CONSTEXPR operator const T() const + ETL_CONSTEXPR operator T() const ETL_NOEXCEPT { return value; } @@ -167,16 +374,9 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator++() + ETL_CONSTEXPR14 cyclic_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (value >= Last) ETL_UNLIKELY - { - value = First; - } - else - { - ++value; - } + value = private_cyclic_value::increment(value, First, Last); return *this; } @@ -184,7 +384,7 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator++(int) + ETL_CONSTEXPR14 cyclic_value operator++(int) ETL_NOEXCEPT { cyclic_value temp(*this); @@ -196,16 +396,9 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator--() + ETL_CONSTEXPR14 cyclic_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (value <= First) ETL_UNLIKELY - { - value = Last; - } - else - { - --value; - } + value = private_cyclic_value::decrement(value, First, Last); return *this; } @@ -213,7 +406,7 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator--(int) + ETL_CONSTEXPR14 cyclic_value operator--(int) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { cyclic_value temp(*this); @@ -225,7 +418,7 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(T t) + ETL_CONSTEXPR14 cyclic_value& operator=(T t) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { set(t); return *this; @@ -234,8 +427,8 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - template - ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) + template + ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { set(other.get()); return *this; @@ -244,7 +437,7 @@ namespace etl //************************************************************************* /// Gets the value. //************************************************************************* - ETL_CONSTEXPR T get() const + ETL_CONSTEXPR T get() const ETL_NOEXCEPT { return value; } @@ -252,7 +445,7 @@ namespace etl //************************************************************************* /// Gets the first value. //************************************************************************* - static ETL_CONSTEXPR T first() + static ETL_CONSTEXPR T first() ETL_NOEXCEPT { return First; } @@ -260,7 +453,7 @@ namespace etl //************************************************************************* /// Gets the last value. //************************************************************************* - static ETL_CONSTEXPR T last() + static ETL_CONSTEXPR T last() ETL_NOEXCEPT { return Last; } @@ -268,39 +461,39 @@ namespace etl //************************************************************************* /// Swaps the values. //************************************************************************* - void swap(cyclic_value& other) + ETL_CONSTEXPR14 void swap(cyclic_value& other) ETL_NOEXCEPT { using ETL_OR_STD::swap; // Allow ADL swap(value, other.value); } - //************************************************************************* - /// Swaps the values. - //************************************************************************* - friend void swap(cyclic_value& lhs, cyclic_value& rhs) - { - lhs.swap(rhs); - } - - //************************************************************************* - /// Operator ==. - //************************************************************************* - friend ETL_CONSTEXPR bool operator==(const cyclic_value& lhs, const cyclic_value& rhs) - { - return lhs.value == rhs.value; - } - - //************************************************************************* - /// Operator !=. - //************************************************************************* - friend ETL_CONSTEXPR bool operator!=(const cyclic_value& lhs, const cyclic_value& rhs) - { - return !(lhs == rhs); - } - private: + //************************************************************************* + template + ETL_CONSTEXPR14 void do_advance(TStep n, bool subtract) ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + typedef private_cyclic_value::range_traits range_info; + + // Are the limits the full width of the type? If so, we can do a simpler calculation. + if ETL_IF_CONSTEXPR (range_info::is_full_range) + { + unsigned_type step = static_cast(etl::absolute_unsigned(n)); + + value = private_cyclic_value::advance(value, step, subtract); + } + else + { + unsigned_type range = range_info::value; + unsigned_type step = static_cast(etl::absolute_unsigned(n) % range); + + value = private_cyclic_value::advance(value, First, Last, step, subtract); + } + } + T value; ///< The current value. }; @@ -315,14 +508,20 @@ namespace etl template class cyclic_value { + private: + + typedef typename private_cyclic_value::traits::unsigned_type unsigned_type; + public: + ETL_STATIC_ASSERT(etl::is_integral::value, "T must be an integral type"); + //************************************************************************* /// Constructor. - /// Sets 'first' and 'last' to the template parameter values. + /// Sets 'first' and 'last' to the template parameter values which will be zero. /// The initial value is set to the first value. //************************************************************************* - ETL_CONSTEXPR cyclic_value() + ETL_CONSTEXPR cyclic_value() ETL_NOEXCEPT : value(First) , first_value(First) , last_value(Last) @@ -335,11 +534,12 @@ namespace etl ///\param first The first value in the range. ///\param last The last value in the range. //************************************************************************* - ETL_CONSTEXPR cyclic_value(T first_, T last_) + ETL_CONSTEXPR14 cyclic_value(T first_, T last_) : value(first_) , first_value(first_) , last_value(last_) { + ETL_ASSERT(first_ <= last_, ETL_ERROR(cyclic_value_reversed_limits)); } //************************************************************************* @@ -350,16 +550,17 @@ namespace etl ///\param last The last value in the range. //************************************************************************* ETL_CONSTEXPR14 cyclic_value(T first_, T last_, T initial) - : first_value(first_) + : value(etl::clamp(initial, first_, last_)) + , first_value(first_) , last_value(last_) { - set(initial); + ETL_ASSERT(first_ <= last_, ETL_ERROR(cyclic_value_reversed_limits)); } //************************************************************************* /// Copy constructor. //************************************************************************* - ETL_CONSTEXPR cyclic_value(const cyclic_value& other) + ETL_CONSTEXPR cyclic_value(const cyclic_value& other) ETL_NOEXCEPT : value(other.value) , first_value(other.first_value) , last_value(other.last_value) @@ -374,6 +575,8 @@ namespace etl //************************************************************************* ETL_CONSTEXPR14 void set(T first_, T last_) { + ETL_ASSERT(first_ <= last_, ETL_ERROR(cyclic_value_reversed_limits)); + first_value = first_; last_value = last_; value = first_; @@ -383,7 +586,7 @@ namespace etl /// Sets the value. ///\param value The value. //************************************************************************* - ETL_CONSTEXPR14 void set(T value_) + ETL_CONSTEXPR14 void set(T value_) ETL_NOEXCEPT { value = etl::clamp(value_, first_value, last_value); } @@ -391,7 +594,7 @@ namespace etl //************************************************************************* /// Resets the value to the first in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_first() + ETL_CONSTEXPR14 void to_first() ETL_NOEXCEPT { value = first_value; } @@ -399,7 +602,7 @@ namespace etl //************************************************************************* /// Resets the value to the last in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_last() + ETL_CONSTEXPR14 void to_last() ETL_NOEXCEPT { value = last_value; } @@ -408,30 +611,13 @@ namespace etl /// Advances to value by a number of steps. ///\param n The number of steps to advance. //************************************************************************* - ETL_CONSTEXPR14 void advance(int n) + template + ETL_CONSTEXPR14 T advance(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (n > 0) - { - for (int i = 0; i < n; ++i) - { - operator++(); - } - } - else - { - for (int i = 0; i < -n; ++i) - { - operator--(); - } - } - } + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + do_advance(n, etl::is_negative(n)); - //************************************************************************* - /// Conversion operator. - /// \return The value of the underlying type. - //************************************************************************* - ETL_CONSTEXPR14 operator T() - { return value; } @@ -439,7 +625,7 @@ namespace etl /// Const conversion operator. /// \return The value of the underlying type. //************************************************************************* - ETL_CONSTEXPR operator const T() const + ETL_CONSTEXPR operator T() const ETL_NOEXCEPT { return value; } @@ -447,16 +633,9 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator++() + ETL_CONSTEXPR14 cyclic_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (value >= last_value) - { - value = first_value; - } - else - { - ++value; - } + value = private_cyclic_value::increment(value, first_value, last_value); return *this; } @@ -464,7 +643,7 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator++(int) + ETL_CONSTEXPR14 cyclic_value operator++(int) ETL_NOEXCEPT { cyclic_value temp(*this); @@ -476,16 +655,9 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator--() + ETL_CONSTEXPR14 cyclic_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (value <= first_value) - { - value = last_value; - } - else - { - --value; - } + value = private_cyclic_value::decrement(value, first_value, last_value); return *this; } @@ -493,7 +665,7 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator--(int) + ETL_CONSTEXPR14 cyclic_value operator--(int) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { cyclic_value temp(*this); @@ -505,7 +677,7 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(T t) + ETL_CONSTEXPR14 cyclic_value& operator=(T t) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { set(t); return *this; @@ -514,7 +686,7 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) + ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { value = other.value; first_value = other.first_value; @@ -522,10 +694,39 @@ namespace etl return *this; } + //************************************************************************* + /// Adds a number of steps and clamps to the range. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator+=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + advance(n); + return *this; + } + + //************************************************************************* + /// Subtracts a number of steps and clamps to the range. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator-=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + do_advance(n, !etl::is_negative(n)); + + return *this; + } + //************************************************************************* /// Gets the value. //************************************************************************* - ETL_CONSTEXPR T get() const + ETL_CONSTEXPR T get() const ETL_NOEXCEPT { return value; } @@ -533,7 +734,7 @@ namespace etl //************************************************************************* /// Gets the first value. //************************************************************************* - ETL_CONSTEXPR T first() const + ETL_CONSTEXPR T first() const ETL_NOEXCEPT { return first_value; } @@ -541,7 +742,7 @@ namespace etl //************************************************************************* /// Gets the last value. //************************************************************************* - ETL_CONSTEXPR T last() const + ETL_CONSTEXPR T last() const ETL_NOEXCEPT { return last_value; } @@ -549,7 +750,7 @@ namespace etl //************************************************************************* /// Swaps the values. //************************************************************************* - void swap(cyclic_value& other) + ETL_CONSTEXPR14 void swap(cyclic_value& other) ETL_NOEXCEPT { using ETL_OR_STD::swap; // Allow ADL @@ -558,36 +759,117 @@ namespace etl swap(value, other.value); } - //************************************************************************* - /// Swaps the values. - //************************************************************************* - friend void swap(cyclic_value& lhs, cyclic_value& rhs) - { - lhs.swap(rhs); - } - - //************************************************************************* - /// Operator ==. - //************************************************************************* - friend ETL_CONSTEXPR bool operator==(const cyclic_value& lhs, const cyclic_value& rhs) - { - return (lhs.value == rhs.value) && (lhs.first_value == rhs.first_value) && (lhs.last_value == rhs.last_value); - } - - //************************************************************************* - /// Operator !=. - //************************************************************************* - friend ETL_CONSTEXPR bool operator!=(const cyclic_value& lhs, const cyclic_value& rhs) - { - return !(lhs == rhs); - } - private: + //************************************************************************* + template + ETL_CONSTEXPR14 void do_advance(TStep n, bool subtract) ETL_NOEXCEPT + { + ETL_STATIC_ASSERT(etl::is_integral::value, "TStep must be an integral type"); + + const bool Is_Full_Range = (first_value == etl::integral_limits::min) && (last_value == etl::integral_limits::max); + + // Are the limits the full width of the type? If so, we can do a simpler calculation. + if (Is_Full_Range) + { + unsigned_type step = static_cast(etl::absolute_unsigned(n)); + + value = private_cyclic_value::advance(value, step, subtract); + } + else + { + unsigned_type range = static_cast(etl::to_unsigned(last_value) - etl::to_unsigned(first_value)) + 1U; + unsigned_type step = static_cast(etl::absolute_unsigned(n) % range); + + value = private_cyclic_value::advance(value, first_value, last_value, step, subtract); + } + } + T value; ///< The current value. T first_value; ///< The first value in the range. T last_value; ///< The last value in the range. }; + + //************************************************************************* + /// Swaps the values. + //************************************************************************* + template + ETL_CONSTEXPR14 void swap(etl::cyclic_value& lhs, etl::cyclic_value& rhs) ETL_NOEXCEPT + { + lhs.swap(rhs); + } } // namespace etl +//************************************************************************* +/// Operator == +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs == rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator==(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return (lhs.get() == rhs.get()); +} + +//************************************************************************* +/// Operator != +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs != rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator!=(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return !(lhs == rhs); +} + +//************************************************************************* +/// Operator < +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs is less than rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator<(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return lhs.get() < rhs.get(); +} + +//************************************************************************* +/// Operator <= +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs is less than or equal to rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator<=(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return !(rhs < lhs); +} + +//************************************************************************* +/// Operator > +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs is greater than rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator>(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return rhs < lhs; +} + +//************************************************************************* +/// Operator >= +///\param lhs The left-hand operand. +///\param rhs The right-hand operand. +///\return `true` if lhs is greater than or equal to rhs. +//************************************************************************* +template +ETL_CONSTEXPR bool operator>=(const etl::cyclic_value& lhs, const etl::cyclic_value& rhs) ETL_NOEXCEPT +{ + return !(lhs < rhs); +} + #endif diff --git a/include/etl/file_error_numbers.h b/include/etl/file_error_numbers.h index 945f1d87..1bcddc48 100644 --- a/include/etl/file_error_numbers.h +++ b/include/etl/file_error_numbers.h @@ -112,4 +112,5 @@ SOFTWARE. #define ETL_FORMAT_FILE_ID "79" #define ETL_INPLACE_FUNCTION_FILE_ID "80" #define ETL_INTRUSIVE_AVL_TREE_FILE_ID "81" +#define ETL_CYCLIC_VALUE_FILE_ID "82" #endif diff --git a/test/run-syntax-checks.sh b/test/run-syntax-checks.sh index fcac0287..2fd0c804 100755 --- a/test/run-syntax-checks.sh +++ b/test/run-syntax-checks.sh @@ -11,14 +11,14 @@ TitleColour=$'\033[38;2;107;210;255m' HelpColour=$'\033[38;2;250;180;250m' NoColour=$'\033[0m' -ParseGitBranch() +ParseGitBranch() { git rev-parse --abbrev-ref HEAD } SetConfigurationName() { - configuration_name=$1 + configuration_name=$1 } PrintHeader() @@ -147,10 +147,9 @@ RunCheck() local cc_name=$1 local msg=$2 local no_stl=$3 - local builtins=$4 - local user_defined=$5 - local force_03=$6 - local build_dir=$7 + local user_defined=$4 + local force_03=$5 + local build_dir=$6 if [ "$compiler_enabled" != "$cc_name" ] && [ "$compiler_enabled" != "All compilers" ]; then return @@ -171,7 +170,7 @@ RunCheck() rm -rdf bgcc rm -rdf bclang cmake -E make_directory bgcc bclang - CC=$cc CXX=$cxx cmake -E chdir $build_dir cmake -DNO_STL=$no_stl -DETL_USE_TYPE_TRAITS_BUILTINS=$builtins -DETL_USER_DEFINED_TYPE_TRAITS=$user_defined -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=$force_03 -DETL_CXX_STANDARD=$cxx_standard .. + CC=$cc CXX=$cxx cmake -E chdir $build_dir cmake -DNO_STL=$no_stl -DETL_USER_DEFINED_TYPE_TRAITS=$user_defined -DETL_FORCE_TEST_CPP03_IMPLEMENTATION=$force_03 -DETL_CXX_STANDARD=$cxx_standard .. cmake --build $build_dir if [ $? -eq 0 ]; then PassedCompilation @@ -193,25 +192,21 @@ RunStandard() cxx_standard=$std # GCC configurations - RunCheck gcc "STL" OFF OFF OFF OFF bgcc - RunCheck gcc "No STL" ON OFF OFF OFF bgcc - RunCheck gcc "STL - Built-in traits" OFF ON OFF OFF bgcc - RunCheck gcc "No STL - Built-in traits" ON ON OFF OFF bgcc + RunCheck gcc "STL" OFF OFF OFF bgcc + RunCheck gcc "No STL" ON OFF OFF bgcc if [ "$has_force_03" = "1" ]; then - RunCheck gcc "STL - Force C++03" OFF OFF OFF ON bgcc - RunCheck gcc "No STL - Force C++03" ON OFF OFF ON bgcc + RunCheck gcc "STL - Force C++03" OFF OFF ON bgcc + RunCheck gcc "No STL - Force C++03" ON OFF ON bgcc fi # Clang configurations - RunCheck clang "STL" OFF OFF OFF OFF bclang - RunCheck clang "No STL" ON OFF OFF OFF bclang - RunCheck clang "STL - Built-in traits" OFF ON OFF OFF bgcc - RunCheck clang "No STL - Built-in traits" ON ON OFF OFF bgcc + RunCheck clang "STL" OFF OFF OFF bclang + RunCheck clang "No STL" ON OFF OFF bclang if [ "$has_force_03" = "1" ]; then - RunCheck clang "STL - Force C++03" OFF OFF OFF ON bclang - RunCheck clang "No STL - Force C++03" ON OFF OFF ON bclang + RunCheck clang "STL - Force C++03" OFF OFF ON bclang + RunCheck clang "No STL - Force C++03" ON OFF ON bclang fi } diff --git a/test/test_cyclic_value.cpp b/test/test_cyclic_value.cpp index 7077628a..414bba44 100644 --- a/test/test_cyclic_value.cpp +++ b/test/test_cyclic_value.cpp @@ -56,6 +56,15 @@ namespace CHECK_EQUAL(7, value.last()); } + //************************************************************************* + TEST(test_reversed_run_time_initialisation) + { + etl::cyclic_value value(1, 2); + + CHECK_THROW(etl::cyclic_value(7, 2), etl::cyclic_value_reversed_limits); + CHECK_THROW(value.set(7, 2), etl::cyclic_value_reversed_limits); + } + //************************************************************************* TEST(test_copy_constructor_compile_time) { @@ -336,7 +345,7 @@ namespace { etl::cyclic_value value; - value.advance(14); + value.advance(20); CHECK_EQUAL(4, value); } @@ -347,7 +356,7 @@ namespace etl::cyclic_value value; value.set(2, 7); - value.advance(14); + value.advance(20); CHECK_EQUAL(4, value); } @@ -358,7 +367,7 @@ namespace etl::cyclic_value value; value.to_last(); - value.advance(-14); + value.advance(-20); CHECK_EQUAL(5, value); } @@ -370,7 +379,7 @@ namespace value.set(2, 7); value.to_last(); - value.advance(-14); + value.advance(-20); CHECK_EQUAL(5, value); } @@ -408,6 +417,168 @@ namespace CHECK_EQUAL(1U, value); } + //************************************************************************* + TEST(test_add_positive_n_compile_time) + { + etl::cyclic_value value; + + value += 2; + + CHECK_EQUAL(4, value); + } + + //************************************************************************* + TEST(test_add_positive_n_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value += 2; + + CHECK_EQUAL(4, value); + } + + //************************************************************************* + TEST(test_add_positive_n_large_compile_time) + { + etl::cyclic_value value; + + value += 20; + + CHECK_EQUAL(4, value); + } + + //************************************************************************* + TEST(test_add_positive_n_large_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value += 20; + + CHECK_EQUAL(4, value); + } + + //************************************************************************* + TEST(test_add_negative_n_large_compile_time) + { + etl::cyclic_value value; + + value.to_last(); + value += -20; + + CHECK_EQUAL(5, value); + } + + //************************************************************************* + TEST(test_add_negative_n_large_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value.to_last(); + value += -20; + + CHECK_EQUAL(5, value); + } + + //************************************************************************* + TEST(test_add_negative_n_unsigned_compile_time) + { + etl::cyclic_value value; + + value += -2; + + CHECK_EQUAL(1U, value); + } + + //************************************************************************* + TEST(test_add_negative_n_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value.to_last(); + value += -20; + + CHECK_EQUAL(5, value); + } + + //************************************************************************* + TEST(test_decrease_positive_compile_time) + { + etl::cyclic_value value; + + value -= 2; + + CHECK_EQUAL(6, value); + } + + //************************************************************************* + TEST(test_subtract_positive_n_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value -= 2; + + CHECK_EQUAL(6, value); + } + + //************************************************************************* + TEST(test_subtract_positive_n_large_compile_time) + { + etl::cyclic_value value; + + value -= 20; + + CHECK_EQUAL(6, value); + } + + //************************************************************************* + TEST(test_subtract_positive_n_large_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value -= 20; + + CHECK_EQUAL(6, value); + } + + //************************************************************************* + TEST(test_subtract_negative_n_large_compile_time) + { + etl::cyclic_value value; + + value.to_last(); + value -= -20; + + CHECK_EQUAL(3, value); + } + + //************************************************************************* + TEST(test_subtract_negative_n_large_run_time) + { + etl::cyclic_value value; + + value.set(2, 7); + value.to_last(); + value -= -20; + + CHECK_EQUAL(3, value); + } + + //************************************************************************* + TEST(test_subtract_negative_n_unsigned_compile_time) + { + etl::cyclic_value value; + + value -= -2; + + CHECK_EQUAL(2U, value); + } + //************************************************************************* TEST(test_assignment_compile_time) { @@ -493,7 +664,50 @@ namespace CHECK(data2 == compare1); } -#if ETL_USING_CPP14 + //************************************************************************* + TEST(test_comparison_operators) + { + etl::cyclic_value cv1(3); + etl::cyclic_value cv2(3); + etl::cyclic_value cv3(5); + etl::cyclic_value cv4(3); + + CHECK_TRUE(cv1 == cv2); + CHECK_FALSE(cv1 == cv3); + + CHECK_FALSE(cv1 != cv2); + CHECK_TRUE(cv1 != cv3); + + CHECK_FALSE(cv1 < cv2); + CHECK_FALSE(cv2 < cv1); + CHECK_TRUE(cv1 < cv3); + CHECK_FALSE(cv3 < cv1); + CHECK_FALSE(cv1 < cv4); + CHECK_FALSE(cv4 < cv1); + + CHECK_FALSE(cv1 > cv2); + CHECK_FALSE(cv2 > cv1); + CHECK_FALSE(cv1 > cv3); + CHECK_TRUE(cv3 > cv1); + CHECK_FALSE(cv1 > cv4); + CHECK_FALSE(cv4 > cv1); + + CHECK_TRUE(cv1 <= cv2); + CHECK_TRUE(cv2 <= cv1); + CHECK_TRUE(cv1 <= cv3); + CHECK_FALSE(cv3 <= cv1); + CHECK_TRUE(cv1 <= cv4); + CHECK_TRUE(cv4 <= cv1); + + CHECK_TRUE(cv1 >= cv2); + CHECK_TRUE(cv2 >= cv1); + CHECK_FALSE(cv1 >= cv3); + CHECK_TRUE(cv3 >= cv1); + CHECK_TRUE(cv1 >= cv4); + CHECK_TRUE(cv4 >= cv1); + } + +#if ETL_USING_CPP11 //************************************************************************* TEST(test_cyclic_value_constexpr_ctor) { @@ -502,6 +716,7 @@ namespace CHECK(true); } + #if ETL_USING_CPP14 //************************************************************************* TEST(test_cyclic_value_constexpr_ctor_with_range) { @@ -510,6 +725,7 @@ namespace static_assert(cv.last() == 9, "constexpr range ctor last"); CHECK(true); } + #endif //************************************************************************* TEST(test_cyclic_value_constexpr_copy_ctor) @@ -519,6 +735,299 @@ namespace static_assert(cv2.get() == 0, "constexpr copy ctor"); CHECK(true); } + + //************************************************************************* + TEST(test_constexpr_comparison_operators) + { + constexpr etl::cyclic_value cv1(3); + constexpr etl::cyclic_value cv2(3); + constexpr etl::cyclic_value cv3(5); + constexpr etl::cyclic_value cv4(3); + + static_assert((cv1 == cv2), "(cv1 == cv2) failed"); + static_assert(!(cv1 == cv3), "!(cv1 == cv3) failed"); + + static_assert(!(cv1 != cv2), "!(cv1 != cv2) failed"); + static_assert((cv1 != cv3), "(cv1 != cv3) failed"); + + static_assert(!(cv1 < cv2), "!(cv1 < cv2) failed"); + static_assert(!(cv2 < cv1), "!(cv2 < cv1) failed"); + static_assert((cv1 < cv3), "(cv1 < cv3) failed"); + static_assert(!(cv3 < cv1), "!(cv3 < cv1) failed"); + static_assert(!(cv1 < cv4), "!(cv1 < cv4) failed"); + static_assert(!(cv4 < cv1), "!(cv4 < cv1) failed"); + + static_assert(!(cv1 > cv2), "!(cv1 > cv2) failed"); + static_assert(!(cv2 > cv1), "!(cv2 > cv1) failed"); + static_assert(!(cv1 > cv3), "!(cv1 > cv3) failed"); + static_assert((cv3 > cv1), "(cv3 > cv1) failed"); + static_assert(!(cv1 > cv4), "!(cv1 > cv4) failed"); + static_assert(!(cv4 > cv1), "!(cv4 > cv1) failed"); + + static_assert((cv1 <= cv2), "(cv1 <= cv2) failed"); + static_assert((cv2 <= cv1), "(cv2 <= cv1) failed"); + static_assert((cv1 <= cv3), "(cv1 <= cv3) failed"); + static_assert(!(cv3 <= cv1), "!(cv3 <= cv1) failed"); + static_assert((cv1 <= cv4), "(cv1 <= cv4) failed"); + static_assert((cv4 <= cv1), "(cv4 <= cv1) failed"); + + static_assert((cv1 >= cv2), "(cv1 >= cv2) failed"); + static_assert((cv2 >= cv1), "(cv2 >= cv1) failed"); + static_assert(!(cv1 >= cv3), "!(cv1 >= cv3) failed"); + static_assert((cv3 >= cv1), "(cv3 >= cv1) failed"); + static_assert((cv1 >= cv4), "(cv1 >= cv4) failed"); + static_assert((cv4 >= cv1), "(cv4 >= cv1) failed"); + } #endif + + //************************************************************************* + TEST(test_maximum_unsigned_range_wrap_with_large_step_type) + { + etl::cyclic_value value(0); + + value.advance(static_cast(255)); + CHECK_EQUAL(255, value); + + value.advance(static_cast(1)); + CHECK_EQUAL(0, value); + + value.advance(static_cast(256)); + CHECK_EQUAL(0, value); + + value.advance(static_cast(-256)); + CHECK_EQUAL(0, value); + + value.advance(static_cast(-1)); + CHECK_EQUAL(255, value); + } + + //************************************************************************* + TEST(test_maximum_signed_range_wrap_with_large_step_type) + { + etl::cyclic_value value(0); + + value.advance(static_cast(127)); + CHECK_EQUAL(127, int(value)); + + value.advance(static_cast(1)); + CHECK_EQUAL(-128, int(value)); + + value.advance(static_cast(256)); + CHECK_EQUAL(-128, int(value)); + + value.advance(static_cast(-256)); + CHECK_EQUAL(-128, int(value)); + + value.advance(static_cast(-1)); + CHECK_EQUAL(127, int(value)); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_same_size_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_same_size_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_larger_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int16_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_larger_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(uint16_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_smaller_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_smaller_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_unsigned_limits_where_the_types_are_64bit) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int64_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_compile_time_range_issue_in_advance_for_full_range_signed_limits_where_the_types_are_64bit) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int64_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_same_size_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_same_size_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_larger_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int16_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_larger_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(uint16_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_unsigned_limits_where_step_is_smaller_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_signed_limits_where_step_is_smaller_type) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int8_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_unsigned_limits_where_the_types_are_64bit) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(0); + + value.advance(int64_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } + + //************************************************************************* + TEST(test_run_time_range_issue_in_advance_for_full_range_signed_limits_where_the_types_are_64bit) + { + const auto minimum = etl::integral_limits::min; + const auto maximum = etl::integral_limits::max; + + etl::cyclic_value value(minimum, maximum); + + value.advance(int64_t(2)); + + CHECK_EQUAL(minimum + 2, value.get()); + } } } // namespace