| Home | Libraries | People | FAQ | More

## Other Algorithms

#### none_of_equal

`none_of_equal` Whether none of a range's elements matches a value

#### one_of_equal

`one_of_equal` Whether only one of a range's elements matches a value

#### is_decreasing

`is_decreasing` Whether an entire sequence is decreasing; i.e, each item is less than or equal to the previous one

#### is_increasing

`is_increasing` Whether an entire sequence is increasing; i.e, each item is greater than or equal to the previous one

#### is_strictly_decreasing

`is_strictly_decreasing` Whether an entire sequence is strictly decreasing; i.e, each item is less than the previous one

#### is_strictly_increasing

`is_strictly_increasing` Whether an entire sequence is strictly increasing; i.e, each item is greater than the previous one

#### clamp

The header file clamp.hpp contains two functions for "clamping" a value between a pair of boundary values.

###### clamp

The function `clamp (v, lo, hi)` returns:

- lo if v < lo
- hi if hi < v
- otherwise, v

Note: using `clamp` with floating point numbers may give unexpected results if one of the values is `NaN`.

There is also a version that allows the caller to specify a comparison predicate to use instead of `operator <`.

    template<typename T>
    const T& clamp ( const T& val, const T& lo, const T& hi );

    template<typename T, typename Pred>
    const T& clamp ( const T& val, const T& lo, const T& hi, Pred p );

The following code:

    int foo = 23;
    foo = clamp ( foo, 1, 10 );

will leave `foo` with a value of 10

Complexity: `clamp` will make either one or two calls to the comparison predicate before returning one of the three parameters.

###### clamp_range

There are also four range-based versions of clamp, that apply clamping to a series of values. You could write them yourself with std::transform and bind, like this: `std::transform ( first, last, out, bind ( clamp ( _1, lo, hi )))`, but they are provided here for your convenience.

    template<typename InputIterator, typename OutputIterator>
    OutputIterator clamp_range ( InputIterator first, InputIterator last, OutputIterator out,
        typename std::iterator_traits<InputIterator>::value_type lo,
        typename std::iterator_traits<InputIterator>::value_type hi );

    template<typename Range, typename OutputIterator>
    OutputIterator clamp_range ( const Range &r, OutputIterator out,
    	typename std::iterator_traits<typename boost::range_iterator<const Range>::type>::value_type lo,
    	typename std::iterator_traits<typename boost::range_iterator<const Range>::type>::value_type hi );

    template<typename InputIterator, typename OutputIterator, typename Pred>
    OutputIterator clamp_range ( InputIterator first, InputIterator last, OutputIterator out,
        typename std::iterator_traits<InputIterator>::value_type lo,
        typename std::iterator_traits<InputIterator>::value_type hi, Pred p );

    template<typename Range, typename OutputIterator, typename Pred>
    OutputIterator clamp_range ( const Range &r, OutputIterator out,
    	typename std::iterator_traits<typename boost::range_iterator<const Range>::type>::value_type lo,
    	typename std::iterator_traits<typename boost::range_iterator<const Range>::type>::value_type hi,
    	Pred p );

#### clamp_range

`clamp_range` Perform `clamp` on the elements of a range and write the results into an output iterator

#### find_not

The header file 'find_not.hpp' contains a variants of a the stl algorithm `find`. The algorithm finds the first value in the given sequence that is not equal to the given value.

Consider this use of `find()`:

    std::vector<int> vec = { 1, 1, 2 };
    auto it = std::find(vec.begin(), vec.end(), 1);

This gives us the first occurance of `1` in `vec`. What if we want to find the first occurrance of any number besides `1` in `vec`? We have to write an unfortunate amount of code:

    std::vector<int> vec = { 1, 1, 2 };
    auto it = std::find_if(vec.begin(), vec.end(), [](int i) { return i != 1; });

With `find_not()` the code gets much more terse:

    std::vector<int> vec = { 1, 1, 2 };
    auto it = find_not(vec.begin(), vec.end(), 1);

The existing `find` variants are: `find()`, `find_if()`, and `find_if_not()`. It seems natural to also have `find_not()`, for the very reason that we have `find_if_not()` -- to avoid having to write a lambda to wrap the negation of the find condition.

###### interface

    template<typename InputIter, typename Sentinel, typename T>
    InputIter find_not(InputIter first, Sentinel last, const T & x);

    template<typename Range, typename T>
    boost::range_iterator<Range> find_not(Range & r, const T & x);

These overloads of `find_not` return the first value that is not equal to `x` in the sequence `[first, last)` or `r`, respectively.

###### Examples

Given the container `c1` containing `{ 0, 1, 2 }`, then

    find_not ( c1.begin(),     c1.end(),    1 ) --> c1.begin()
    find_not ( c1.begin(),     c1.end(),    0 ) --> std::next(c1.begin())

###### Iterator Requirements

`find_not` works on all iterators except output iterators.

The template parameter `Sentinel` is allowed to be different from `InputIter`, or they may be the same. For an `InputIter` `it` and a `Sentinel` `end`, `it == end` and `it != end` must be well-formed expressions.

###### Complexity

Linear.

###### Exception Safety

`find_not` takes its parameters by value and do not depend upon any global state. Therefore, it provides the strong exception guarantee.

###### Notes

`constexpr` in C++14 or later.

#### find_backward

The header file 'find_backward.hpp' contains variants of the stl algorithm `find`. These variants are like `find`, except that the evaluate the elements of the given sequence in reverse order.

Consider how finding the last element that is equal to `x` in a range is typically done:

    // Assume a valid range if elements delimited by [first, last).
    while (last-- != first) {
        if (*last == x) {
            // Use last here...
        }
    }

Raw loops are icky though. Perhaps we should do a bit of extra work to allow the use of `std::find()`:

    auto rfirst = std::make_reverse_iterator(last);
    auto rlast = std::make_reverse_iterator(first);
    auto it = std::find(rfirst, rlast, x);
    // Use it here...

That seems nicer in that there is no raw loop, but it has two major drawbacks. First, it requires an unpleasant amount of typing. Second, it is less efficient than forward-iterator `find` , since `std::reverse_iterator` calls its base-iterator's `operator--()` in most of its member functions before doing the work that the member function requires.

###### interface

    template<typename BidiIter, typename T>
    BidiIter find_backward(BidiIter first, BidiIter last, const T & x);

    template<typename Range, typename T>
    boost::range_iterator<Range> find_backward(Range & range, const T & x);

These overloads of `find_backward` return an iterator to the last element that is equal to `x` in `[first, last)` or `r`, respectively.

    template<typename BidiIter, typename T>
    BidiIter find_not_backward(BidiIter first, BidiIter last, const T & x);

    template<typename Range, typename T>
    boost::range_iterator<Range> find_not_backward(Range & range, const T & x);

These overloads of `find_not_backward` return an iterator to the last element that is not equal to `x` in `[first, last)` or `r`, respectively.

    template<typename BidiIter, typename Pred>
    BidiIter find_if_backward(BidiIter first, BidiIter last, Pred p);

    template<typename Range, typename Pred>
    boost::range_iterator<Range> find_if_backward(Range & range, Pred p);

These overloads of `find_if_backward` return an iterator to the last element for which `pred` returns `true` in `[first, last)` or `r`, respectively.

    template<typename BidiIter, typename Pred>
    BidiIter find_if_not_backward(BidiIter first, BidiIter last, Pred p);

    template<typename Range, typename Pred>
    boost::range_iterator<Range> find_if_not_backward(Range & range, Pred p);

These overloads of `find_if_not_backward` return an iterator to the last element for which `pred` returns `false` in `[first, last)` or `r`, respectively.

###### Examples

Given the container `c1` containing `{ 2, 1, 2 }`, then

    find_backward        ( c1.begin(), c1.end(), 2                          ) --> --c1.end()
    find_backward        ( c1.begin(), c1.end(), 3                          ) --> c1.end()
    find_if_backward     ( c1.begin(), c1.end(), [](int i) {return i == 2;} ) --> --c1.end()
    find_if_backward     ( c1.begin(), c1.end(), [](int i) {return i == 3;} ) --> c1.end()
    find_not_backward    ( c1.begin(), c1.end(), 2                          ) --> std::prev(c1.end(), 2)
    find_not_backward    ( c1.begin(), c1.end(), 1                          ) --> c1.end()
    find_if_not_backward ( c1.begin(), c1.end(), [](int i) {return i == 2;} ) --> std::prev(c1.end(), 2)
    find_if_not_backward ( c1.begin(), c1.end(), [](int i) {return i == 1;} ) --> c1.end()

###### Iterator Requirements

All variants work on bidirectional iterators.

###### Complexity

Linear.

###### Exception Safety

All of the variants take their parameters by value and do not depend upon any global state. Therefore, all the routines in this file provide the strong exception guarantee.

###### Notes

All variants are `constexpr` in C++14 or later.

#### find_not_backward

`find_not_backward` Find the last element in a sequence that does not equal a value. See find_backward.

#### find_if_backward

`find_if_backward` Find the last element in a sequence that satisfies a predicate. See find_backward.

#### find_if_not

`find_if_not` Find the first element in a sequence that does not satisfy a predicate. See find_not.

#### find_if_not_backward

`find_if_not_backward` Find the last element in a sequence that does not satisfy a predicate. See find_backward.

#### gather

The header file 'boost/algorithm/gather.hpp' contains two variants of a single algorithm, `gather`.

`gather()` takes a collection of elements defined by a pair of iterators and moves the ones satisfying a predicate to them to a position (called the pivot) within the sequence. The algorithm is stable. The result is a pair of iterators that contains the items that satisfy the predicate.

###### Interface

The function `gather` returns a `std::pair` of iterators that denote the elements that satisfy the predicate.

There are two versions; one takes two iterators, and the other takes a range.

    namespace boost { namespace algorithm {

    template <typename BidirectionalIterator, typename Pred>
    std::pair<BidirectionalIterator,BidirectionalIterator>
    gather ( BidirectionalIterator first, BidirectionalIterator last, BidirectionalIterator pivot, Pred pred );

    template <typename BidirectionalRange, typename Pred>
    std::pair<typename boost::range_iterator<const BidirectionalRange>::type, typename boost::range_iterator<const BidirectionalRange>::type>
    gather ( const BidirectionalRange &range, typename boost::range_iterator<const BidirectionalRange>::type pivot, Pred pred );

    }}

###### Examples

Given an sequence containing:

    0 1 2 3 4 5 6 7 8 9

a call to gather ( arr, arr + 10, arr + 4, IsEven ) will result in:

    1 3 0 2 4 6 8 5 7 9
        |---|-----|
      first |  second
          pivot

where `first` and `second` are the fields of the pair that is returned by the call.

###### Iterator Requirements

`gather` work on bidirectional iterators or better. This requirement comes from the usage of `stable_partition`, which requires bidirectional iterators. Some standard libraries (libstdc++ and libc++, for example) have implementations of `stable_partition` that work with forward iterators. If that is the case, then `gather` will work with forward iterators as well.

###### Storage Requirements

`gather` uses `stable_partition`, which will attempt to allocate temporary memory, but will work in-situ if there is none available.

###### Complexity

If there is sufficient memory available, the run time is linear: `O(N)`

If there is not any memory available, then the run time is `O(N log N)`.

###### Exception Safety

###### Notes

#### hex

The header file `'boost/algorithm/hex.hpp'` contains three variants each of two algorithms, `hex` and `unhex`. They are inverse algorithms; that is, one undoes the effort of the other. `hex` takes a sequence of values, and turns them into hexadecimal characters. `unhex` takes a sequence of hexadecimal characters, and outputs a sequence of values.

`hex` and `unhex` come from MySQL, where they are used in database queries and stored procedures.

###### interface

The function `hex` takes a sequence of values and writes hexadecimal characters. There are three different interfaces, differing only in how the input sequence is specified.

The first one takes an iterator pair. The second one takes a pointer to the start of a zero-terminated sequence, such as a c string, and the third takes a range as defined by the Boost.Range library.

    template <typename InputIterator, typename OutputIterator>
    OutputIterator hex ( InputIterator first, InputIterator last, OutputIterator out );

    template <typename T, typename OutputIterator>
    OutputIterator hex ( const T *ptr, OutputIterator out );

    template <typename Range, typename OutputIterator>
    OutputIterator hex ( const Range &r, OutputIterator out );

`hex` writes only values in the range '0'..'9' and 'A'..'F', but is not limited to character output. The output iterator could refer to a wstring, or a vector of integers, or any other integral type.

The function `unhex` takes the output of `hex` and turns it back into a sequence of values.

The input parameters for the different variations of `unhex` are the same as `hex`.

    template <typename InputIterator, typename OutputIterator>
    OutputIterator unhex ( InputIterator first, InputIterator last, OutputIterator out );

    template <typename T, typename OutputIterator>
    OutputIterator unhex ( const T *ptr, OutputIterator out );

    template <typename Range, typename OutputIterator>
    OutputIterator unhex ( const Range &r, OutputIterator out );

###### Error Handling

The header 'hex.hpp' defines three exception classes:

    struct hex_decode_error: virtual boost::exception, virtual std::exception {};
    struct not_enough_input : public hex_decode_error;
    struct non_hex_input : public hex_decode_error;

If the input to `unhex` does not contain an "even number" of hex digits, then an exception of type `boost::algorithm::not_enough_input` is thrown.

If the input to `unhex` contains any non-hexadecimal characters, then an exception of type `boost::algorithm::non_hex_input` is thrown.

If you want to catch all the decoding errors, you can catch exceptions of type `boost::algorithm::hex_decode_error`.

###### Examples

Assuming that `out` is an iterator that accepts `char` values, and `wout` accepts `wchar_t` values (and that sizeof ( wchar_t ) == 2)

    hex ( "abcdef", out )  --> "616263646566"
    hex ( "32", out )     --> "3332"
    hex ( "abcdef", wout ) --> "006100620063006400650066"
    hex ( "32", wout )    --> "00330032"

    unhex ( "616263646566", out )  --> "abcdef"
    unhex ( "3332", out )          --> "32"
    unhex ( "616263646566", wout ) --> "\6162\6364\6566"	( i.e, a 3 character string )
    unhex ( "3332", wout )         --> "\3233"				( U+3332, SQUARE HUARADDO )

    unhex ( "3", out )            --> Error - not enough input
    unhex ( "32", wout )          --> Error - not enough input

    unhex ( "ACEG", out )         --> Error - non-hex input

###### Iterator Requirements

`hex` and `unhex` work on all iterator types.

###### Complexity

All of the variants of `hex` and `unhex` run in O(N) (linear) time; that is, that is, they process each element in the input sequence once.

###### Exception Safety

All of the variants of `hex` and `unhex` take their parameters by value or const reference, and do not depend upon any global state. Therefore, all the routines in this file provide the strong exception guarantee. However, when working on input iterators, if an exception is thrown, the input iterators will not be reset to their original values (i.e, the characters read from the iterator cannot be un-read)

###### Notes

- `hex` and `unhex` both do nothing when passed empty ranges.

#### unhex

`unhex` Convert a sequence of hexadecimal characters into a sequence of integers or characters

#### hex_lower

`hex_lower` Convert a sequence of integral types into a lower case hexadecimal sequence of characters

#### is_palindrome

The header file 'is_palindrome.hpp' contains six variants of a single algorithm, is_palindrome. The algorithm tests the sequence and returns true if the sequence is a palindrome; i.e, it is identical when traversed either backwards or frontwards.

The routine `is_palindrome` takes a sequence and, optionally, a predicate. It will return true if the predicate returns true for tested elements by algorithm in the sequence.

The routine come in 6 forms; the first one takes two iterators to define the range. The second form takes two iterators to define the range and a predicate. The third form takes a single range parameter, and uses Boost.Range to traverse it. The fourth form takes a single range parameter ( uses Boost.Range to traverse it) and a predicate. The fifth form takes a single C-string and a predicate. The sixth form takes a single C-string.

###### interface

The function `is_palindrome` returns true if the predicate returns true any tested by algorithm items in the sequence. There are six versions: 1) takes two iterators. 2) takes two iterators and a predicate. 3) takes a range. 4) takes a range and a predicate. 5) takes a C-string and a predicate. 6) takes a C-string.

    template<typename BidirectionalIterator>
    	bool is_palindrome ( BidirectionalIterator begin, BidirectionalIterator end );
    template<typename BidirectionalIterator, typename Predicate>
    	bool is_palindrome ( BidirectionalIterator begin, BidirectionalIterator end, Predicate p );
    template<typename Range>
    	bool is_palindrome ( const Range &r );
    template<typename Range, typename Predicate>
    	bool is_palindrome ( const Range &r, Predicate p );
    template<typename Predicate>
    	bool is_palindrome ( const char* str, Predicate p );
    bool is_palindrome(const char* str);

###### Examples

Given the containers: const std::list<int> empty, const std::vector<char> singleElement{'z'}, int oddNonPalindrome[] = {3,2,2}, const int oddPalindrome[] = {1,2,3,2,1}, const int evenPalindrome[] = {1,2,2,1}, int evenNonPalindrome[] = {1,4,8,8}, then

    is_palindrome(empty))  --> true //empty range
    is_palindrome(singleElement))  --> true
    is_palindrome(std::begin(oddNonPalindrome), std::end(oddNonPalindrome))) --> false
    is_palindrome(std::begin(evenPalindrome), std::end(evenPalindrome))) --> true
    is_palindrome(empty.begin(), empty.end(), functorComparator())) --> true //empty range
    is_palindrome(std::begin(oddNonPalindrome), std::end(oddNonPalindrome), funcComparator<int>)) --> false
    is_palindrome(std::begin(oddPalindrome), std::end(oddPalindrome)) --> true
    is_palindrome(evenPalindrome, std::equal_to<int>())) --> true
    is_palindrome(std::begin(evenNonPalindrome), std::end(evenNonPalindrome)) --> false
    is_palindrome("a") --> true
    is_palindrome("aba", std::equal_to<char>()) --> true

###### Iterator Requirements

`is_palindrome` work on Bidirectional and RandomAccess iterators.

###### Complexity

All of the variants of `is_palindrome` run in O(N) (linear) time; that is, they compare against each element in the list once. If any of the comparisons not succeed, the algorithm will terminate immediately, without examining the remaining members of the sequence.

###### Exception Safety

All of the variants of `is_palindrome` take their parameters by value, const pointer or const reference, and do not depend upon any global state. Therefore, all the routines in this file provide the strong exception guarantee.

###### Notes

- `is_palindrome` returns true for empty ranges, const char* null pointers and for single element ranges.
- If you use version of 'is_palindrome' without custom predicate, 'is_palindrome' uses default 'operator==()' for elements.
- Be careful with using not null pointer 'const char*' without '\0' - if you use it with 'is_palindrome', it's a undefined behaviour.

#### is_partitioned_until

The header file 'is_partitioned_until.hpp' contains two variants of a single algorithm, `is_partitioned_until`. The algorithm tests to see if a sequence is partitioned according to a predicate; in other words, all the items in the sequence that satisfy the predicate are at the beginning of the sequence.

The routine `is_partitioned_until` takes a sequence and a predicate. It returns the last iterator 'it' in the sequence [begin, end) for which the is_partitioned(begin, it) is true.

`is_partitioned_until` come in two forms; the first one takes two iterators to define the range. The second form takes a single range parameter, and uses Boost.Range to traverse it.

###### interface

The function `is_partitioned_until` returns the last iterator 'it' in the sequence [begin, end) for which the is_partitioned(begin, it) is true. There are two versions; one takes two iterators, and the other takes a range.

    template<typename InputIterator, typename Predicate>
    	InputIterator is_partitioned_until ( InputIterator first, InputIterator last, Predicate p );
    template<typename Range, typename Predicate>
    	typename boost::range_iterator<const Range>::type is_partitioned_until ( const Range &r, Predicate p );

###### Examples

Given the container `c` containing `{ 0, 1, 2, 3, 14, 15 }`, then

    bool isOdd ( int i ) { return i % 2 == 1; }
    bool lessThan10 ( int i ) { return i < 10; }

    is_partitioned_until ( c, isOdd ) --> iterator to '1'
    is_partitioned_until ( c, lessThan10 ) --> end
    is_partitioned_until ( c.begin (), c.end (), lessThan10 ) --> end
    is_partitioned_until ( c.begin (), c.begin () + 3, lessThan10 ) --> end
    is_partitioned_until ( c.end (), c.end (), isOdd ) --> end  // empty range

###### Iterator Requirements

`is_partitioned_until` works on all iterators except output iterators.

###### Complexity

Both of the variants of `is_partitioned_until` run in O(N) (linear) time; that is, they compare against each element in the list once. If the sequence is found to be not partitioned at any point, the routine will terminate immediately, without examining the rest of the elements.

###### Exception Safety

Both of the variants of `is_partitioned_until` take their parameters by value or const reference, and do not depend upon any global state. Therefore, all the routines in this file provide the strong exception guarantee.

###### Notes

- `is_partitioned_until` returns iterator to the end for empty and single-element ranges, no matter what predicate is passed to test against.

#### apply_reverse_permutation

See below

#### apply_permutation

The header file `apply_permutation.hpp` contains two algorithms, `apply_permutation` and `apply_reverse_permutation`. There are also range-based versions. The algorithms transform the item sequence according to index sequence order.

The routine `apply_permutation` takes a item sequence and a order sequence. It reshuffles item sequence according to order sequence. Every value in order sequence means where the item comes from. Order sequence needs to be exactly a permutation of the sequence [0, 1, ... , N], where N is the biggest index in the item sequence (zero-indexed). The routine `apply_reverse_permutation` takes a item sequence and a order sequence. It will reshuffle item sequence according to order sequence. Every value in order sequence means where the item goes to. Order sequence needs to be exactly a permutation of the sequence [0, 1, ... , N], where N is the biggest index in the item sequence (zero-indexed).

Implementations are based on these articles: https://devblogs.microsoft.com/oldnewthing/20170102-00/?p=95095 https://devblogs.microsoft.com/oldnewthing/20170103-00/?p=95105 https://devblogs.microsoft.com/oldnewthing/20170104-00/?p=95115 https://devblogs.microsoft.com/oldnewthing/20170109-00/?p=95145 https://devblogs.microsoft.com/oldnewthing/20170110-00/?p=95155 https://devblogs.microsoft.com/oldnewthing/20170111-00/?p=95165

The routines come in 2 forms; the first one takes two iterators to define the item range and one iterator to define the beginning of index range. The second form takes range to define the item sequence and range to define index sequence.

###### interface

There are two versions of algorithms: 1) takes four iterators. 2) takes two ranges.

    template<typename RandomAccessIterator1, typename RandomAccessIterator2>
    void apply_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end,
                      RandomAccessIterator2 ind_begin, RandomAccessIterator2 ind_end);
    template<typename Range1, typename Range2>
    void apply_permutation(Range1& item_range, Range2& ind_range);
    template<typename RandomAccessIterator1, typename RandomAccessIterator2>
    void apply_reverse_permutation(RandomAccessIterator1 item_begin, RandomAccessIterator1 item_end,
                      RandomAccessIterator2 ind_begin, RandomAccessIterator2 ind_end);
    template<typename Range1, typename Range2>
    void apply_reverse_permutation(Range1& item_range, Range2& ind_range);

###### Examples

Given the containers: std::vector<int> emp_vec, emp_order, std::vector<int> one{1}, one_order{0}, std::vector<int> two{1,2}, two_order{1,0}, std::vector<int> vec{1, 2, 3, 4, 5}, std::vector<int> order{4, 2, 3, 1, 0}, then

    apply_permutation(emp_vec, emp_order))  --> no changes
    apply_reverse_permutation(emp_vec, emp_order))  --> no changes
    apply_permutation(one, one_order)  --> no changes
    apply_reverse_permutation(one, one_order)  --> no changes
    apply_permutation(two, two_order)  --> two:{2,1}
    apply_reverse_permutation(two, two_order)  --> two:{2,1}
    apply_permutation(vec, order)  --> vec:{5, 3, 4, 2, 1}
    apply_reverse_permutation(vec, order)  --> vec:{5, 4, 2, 3, 1}

###### Iterator Requirements

`apply_permutation` and 'apply_reverse_permutation' work only on RandomAccess iterators. RandomAccess iterators required both for item and index sequences.

###### Complexity

All of the variants of `apply_permutation` and `apply_reverse_permutation` run in O(N) (linear) time. More

###### Exception Safety

All of the variants of `apply_permutation` and `apply_reverse_permutation` take their parameters by iterators or reference, and do not depend upon any global state. Therefore, all the routines in this file provide the strong exception guarantee.

###### Notes

- If ItemSequence and IndexSequence are not equal, behavior is undefined.
- `apply_permutation` and `apply_reverse_permutation` work also on empty sequences.
- Order sequence must be zero-indexed.
- Order sequence gets permuted.

#### iota_n

`iota_n` Write a sequence of n increasing values to an output iterator

#### power

`power` Raise a value to an integral power (`constexpr` since C++14)