2018-12-20 01:45:32 +08:00
// -*- C++ -*-
//===-- utils.h -----------------------------------------------------------===//
//
2019-01-19 18:56:40 +08:00
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2018-12-20 01:45:32 +08:00
//
//===----------------------------------------------------------------------===//
// File contains common utilities that tests rely on
// Do not #include <algorithm>, because if we do we will not detect accidental dependencies.
2019-03-26 00:44:08 +08:00
# include <atomic>
# include <cstdint>
# include <cstdlib>
2018-12-20 01:45:32 +08:00
# include <cstring>
2019-03-26 00:44:08 +08:00
# include <iostream>
2018-12-20 01:45:32 +08:00
# include <iterator>
# include <memory>
2019-03-26 00:44:08 +08:00
# include <sstream>
# include <vector>
2018-12-20 01:45:32 +08:00
# include "pstl_test_config.h"
namespace TestUtils
{
typedef double float64_t ;
typedef float float32_t ;
template < class T , std : : size_t N >
constexpr size_t
2019-05-31 04:46:31 +08:00
const_size ( const T ( & ) [ N ] ) noexcept
2018-12-20 01:45:32 +08:00
{
return N ;
}
template < typename T >
class Sequence ;
// Handy macros for error reporting
2019-03-26 00:44:08 +08:00
# define EXPECT_TRUE(condition, message) ::TestUtils::expect(true, condition, __FILE__, __LINE__, message)
# define EXPECT_FALSE(condition, message) ::TestUtils::expect(false, condition, __FILE__, __LINE__, message)
2018-12-20 01:45:32 +08:00
// Check that expected and actual are equal and have the same type.
2019-03-26 00:44:08 +08:00
# define EXPECT_EQ(expected, actual, message) ::TestUtils::expect_equal(expected, actual, __FILE__, __LINE__, message)
2018-12-20 01:45:32 +08:00
// Check that sequences started with expected and actual and have had size n are equal and have the same type.
# define EXPECT_EQ_N(expected, actual, n, message) \
2019-03-26 00:44:08 +08:00
: : TestUtils : : expect_equal ( expected , actual , n , __FILE__ , __LINE__ , message )
2018-12-20 01:45:32 +08:00
// Issue error message from outstr, adding a newline.
// Real purpose of this routine is to have a place to hang a breakpoint.
2019-03-26 00:44:08 +08:00
inline void
2018-12-20 01:45:32 +08:00
issue_error_message ( std : : stringstream & outstr )
{
outstr < < std : : endl ;
std : : cerr < < outstr . str ( ) ;
2019-03-26 00:44:08 +08:00
std : : exit ( EXIT_FAILURE ) ;
2018-12-20 01:45:32 +08:00
}
2019-03-26 00:44:08 +08:00
inline void
expect ( bool expected , bool condition , const char * file , int32_t line , const char * message )
2018-12-20 01:45:32 +08:00
{
2019-03-26 00:44:08 +08:00
if ( condition ! = expected )
2018-12-20 01:45:32 +08:00
{
std : : stringstream outstr ;
outstr < < " error at " < < file < < " : " < < line < < " - " < < message ;
issue_error_message ( outstr ) ;
}
}
// Do not change signature to const T&.
// Function must be able to detect const differences between expected and actual.
template < typename T >
void
expect_equal ( T & expected , T & actual , const char * file , int32_t line , const char * message )
{
if ( ! ( expected = = actual ) )
{
std : : stringstream outstr ;
outstr < < " error at " < < file < < " : " < < line < < " - " < < message < < " , expected " < < expected < < " got "
< < actual ;
issue_error_message ( outstr ) ;
}
}
template < typename T >
void
expect_equal ( Sequence < T > & expected , Sequence < T > & actual , const char * file , int32_t line , const char * message )
{
size_t n = expected . size ( ) ;
size_t m = actual . size ( ) ;
if ( n ! = m )
{
std : : stringstream outstr ;
outstr < < " error at " < < file < < " : " < < line < < " - " < < message < < " , expected sequence of size " < < n
< < " got sequence of size " < < m ;
issue_error_message ( outstr ) ;
return ;
}
size_t error_count = 0 ;
for ( size_t k = 0 ; k < n & & error_count < 10 ; + + k )
{
if ( ! ( expected [ k ] = = actual [ k ] ) )
{
std : : stringstream outstr ;
outstr < < " error at " < < file < < " : " < < line < < " - " < < message < < " , at index " < < k < < " expected "
< < expected [ k ] < < " got " < < actual [ k ] ;
issue_error_message ( outstr ) ;
+ + error_count ;
}
}
}
template < typename Iterator1 , typename Iterator2 , typename Size >
void
expect_equal ( Iterator1 expected_first , Iterator2 actual_first , Size n , const char * file , int32_t line ,
const char * message )
{
size_t error_count = 0 ;
2019-05-31 04:46:31 +08:00
for ( Size k = 0 ; k < n & & error_count < 10 ; + + k , + + expected_first , + + actual_first )
2018-12-20 01:45:32 +08:00
{
if ( ! ( * expected_first = = * actual_first ) )
{
std : : stringstream outstr ;
outstr < < " error at " < < file < < " : " < < line < < " - " < < message < < " , at index " < < k ;
issue_error_message ( outstr ) ;
+ + error_count ;
}
}
}
// ForwardIterator is like type Iterator, but restricted to be a forward iterator.
// Only the forward iterator signatures that are necessary for tests are present.
// Post-increment in particular is deliberatly omitted since our templates should avoid using it
// because of efficiency considerations.
template < typename Iterator , typename IteratorTag >
class ForwardIterator
{
public :
typedef IteratorTag iterator_category ;
typedef typename std : : iterator_traits < Iterator > : : value_type value_type ;
typedef typename std : : iterator_traits < Iterator > : : difference_type difference_type ;
typedef typename std : : iterator_traits < Iterator > : : pointer pointer ;
typedef typename std : : iterator_traits < Iterator > : : reference reference ;
protected :
Iterator my_iterator ;
typedef value_type element_type ;
public :
ForwardIterator ( ) = default ;
explicit ForwardIterator ( Iterator i ) : my_iterator ( i ) { }
reference operator * ( ) const { return * my_iterator ; }
Iterator operator - > ( ) const { return my_iterator ; }
ForwardIterator
operator + + ( )
{
+ + my_iterator ;
return * this ;
}
ForwardIterator operator + + ( int32_t )
{
auto retval = * this ;
my_iterator + + ;
return retval ;
}
friend bool
operator = = ( const ForwardIterator & i , const ForwardIterator & j )
{
return i . my_iterator = = j . my_iterator ;
}
friend bool
operator ! = ( const ForwardIterator & i , const ForwardIterator & j )
{
return i . my_iterator ! = j . my_iterator ;
}
Iterator
iterator ( ) const
{
return my_iterator ;
}
} ;
template < typename Iterator , typename IteratorTag >
class BidirectionalIterator : public ForwardIterator < Iterator , IteratorTag >
{
typedef ForwardIterator < Iterator , IteratorTag > base_type ;
public :
BidirectionalIterator ( ) = default ;
explicit BidirectionalIterator ( Iterator i ) : base_type ( i ) { }
BidirectionalIterator ( const base_type & i ) : base_type ( i . iterator ( ) ) { }
BidirectionalIterator
operator + + ( )
{
+ + base_type : : my_iterator ;
return * this ;
}
BidirectionalIterator
operator - - ( )
{
- - base_type : : my_iterator ;
return * this ;
}
BidirectionalIterator operator + + ( int32_t )
{
auto retval = * this ;
base_type : : my_iterator + + ;
return retval ;
}
BidirectionalIterator operator - - ( int32_t )
{
auto retval = * this ;
base_type : : my_iterator - - ;
return retval ;
}
} ;
template < typename Iterator , typename F >
void
fill_data ( Iterator first , Iterator last , F f )
{
typedef typename std : : iterator_traits < Iterator > : : value_type T ;
for ( std : : size_t i = 0 ; first ! = last ; + + first , + + i )
{
* first = T ( f ( i ) ) ;
}
}
2020-03-11 00:09:57 +08:00
struct MemoryChecker {
// static counters and state tags
static std : : atomic < std : : int64_t > alive_object_counter ; // initialized outside
static constexpr std : : int64_t alive_state = 0xAAAAAAAAAAAAAAAA ;
static constexpr std : : int32_t dead_state = 0 ; // only used as a set value to cancel alive_state
std : : int32_t _value ; // object value used for algorithms
std : : int64_t _state ; // state tag used for checks
// ctors, dtors, assign ops
explicit MemoryChecker ( std : : int32_t value = 0 ) : _value ( value ) {
// check for EXPECT_TRUE(state() != alive_state, ...) has not been done since we cannot guarantee that
// raw memory for object being constructed does not have a bit sequence being equal to alive_state
// set constructed state and increment counter for living object
inc_alive_objects ( ) ;
_state = alive_state ;
}
MemoryChecker ( MemoryChecker & & other ) : _value ( other . value ( ) ) {
// check for EXPECT_TRUE(state() != alive_state, ...) has not been done since
// compiler can optimize out the move ctor call that results in false positive failure
EXPECT_TRUE ( other . state ( ) = = alive_state , " wrong effect from MemoryChecker(MemoryChecker&&): attemp to construct an object from non-existing object " ) ;
// set constructed state and increment counter for living object
inc_alive_objects ( ) ;
_state = alive_state ;
}
MemoryChecker ( const MemoryChecker & other ) : _value ( other . value ( ) ) {
// check for EXPECT_TRUE(state() != alive_state, ...) has not been done since
// compiler can optimize out the copy ctor call that results in false positive failure
EXPECT_TRUE ( other . state ( ) = = alive_state , " wrong effect from MemoryChecker(const MemoryChecker&): attemp to construct an object from non-existing object " ) ;
// set constructed state and increment counter for living object
inc_alive_objects ( ) ;
_state = alive_state ;
}
MemoryChecker & operator = ( MemoryChecker & & other ) {
// check if we do not assign over uninitialized memory
EXPECT_TRUE ( state ( ) = = alive_state , " wrong effect from MemoryChecker::operator=(MemoryChecker&& other): attemp to assign to non-existing object " ) ;
EXPECT_TRUE ( other . state ( ) = = alive_state , " wrong effect from MemoryChecker::operator=(MemoryChecker&& other): attemp to assign from non-existing object " ) ;
// just assign new value, counter is the same, state is the same
_value = other . value ( ) ;
return * this ;
}
MemoryChecker & operator = ( const MemoryChecker & other ) {
// check if we do not assign over uninitialized memory
EXPECT_TRUE ( state ( ) = = alive_state , " wrong effect from MemoryChecker::operator=(const MemoryChecker& other): attemp to assign to non-existing object " ) ;
EXPECT_TRUE ( other . state ( ) = = alive_state , " wrong effect from MemoryChecker::operator=(const MemoryChecker& other): attemp to assign from non-existing object " ) ;
// just assign new value, counter is the same, state is the same
_value = other . value ( ) ;
return * this ;
}
~ MemoryChecker ( ) {
// check if we do not double destruct the object
EXPECT_TRUE ( state ( ) = = alive_state , " wrong effect from ~MemoryChecker(): attemp to destroy non-existing object " ) ;
// set destructed state and decrement counter for living object
static_cast < volatile std : : int64_t & > ( _state ) = dead_state ;
dec_alive_objects ( ) ;
}
// getters
std : : int32_t value ( ) const { return _value ; }
std : : int64_t state ( ) const { return _state ; }
static std : : int32_t alive_objects ( ) { return alive_object_counter . load ( ) ; }
private :
// setters
void inc_alive_objects ( ) { alive_object_counter . fetch_add ( 1 ) ; }
void dec_alive_objects ( ) { alive_object_counter . fetch_sub ( 1 ) ; }
} ;
std : : atomic < std : : int64_t > MemoryChecker : : alive_object_counter { 0 } ;
std : : ostream & operator < < ( std : : ostream & os , const MemoryChecker & val ) { return ( os < < val . value ( ) ) ; }
bool operator = = ( const MemoryChecker & v1 , const MemoryChecker & v2 ) { return v1 . value ( ) = = v2 . value ( ) ; }
bool operator < ( const MemoryChecker & v1 , const MemoryChecker & v2 ) { return v1 . value ( ) < v2 . value ( ) ; }
2018-12-20 01:45:32 +08:00
// Sequence<T> is a container of a sequence of T with lots of kinds of iterators.
// Prefixes on begin/end mean:
// c = "const"
// f = "forward"
// No prefix indicates non-const random-access iterator.
template < typename T >
class Sequence
{
std : : vector < T > m_storage ;
public :
typedef typename std : : vector < T > : : iterator iterator ;
typedef typename std : : vector < T > : : const_iterator const_iterator ;
typedef ForwardIterator < iterator , std : : forward_iterator_tag > forward_iterator ;
typedef ForwardIterator < const_iterator , std : : forward_iterator_tag > const_forward_iterator ;
typedef BidirectionalIterator < iterator , std : : bidirectional_iterator_tag > bidirectional_iterator ;
typedef BidirectionalIterator < const_iterator , std : : bidirectional_iterator_tag > const_bidirectional_iterator ;
typedef T value_type ;
explicit Sequence ( size_t size ) : m_storage ( size ) { }
// Construct sequence [f(0), f(1), ... f(size-1)]
// f can rely on its invocations being sequential from 0 to size-1.
template < typename Func >
Sequence ( size_t size , Func f )
{
m_storage . reserve ( size ) ;
// Use push_back because T might not have a default constructor
for ( size_t k = 0 ; k < size ; + + k )
m_storage . push_back ( T ( f ( k ) ) ) ;
}
Sequence ( const std : : initializer_list < T > & data ) : m_storage ( data ) { }
const_iterator
begin ( ) const
{
return m_storage . begin ( ) ;
}
const_iterator
end ( ) const
{
return m_storage . end ( ) ;
}
iterator
begin ( )
{
return m_storage . begin ( ) ;
}
iterator
end ( )
{
return m_storage . end ( ) ;
}
const_iterator
cbegin ( ) const
{
return m_storage . cbegin ( ) ;
}
const_iterator
cend ( ) const
{
return m_storage . cend ( ) ;
}
forward_iterator
fbegin ( )
{
return forward_iterator ( m_storage . begin ( ) ) ;
}
forward_iterator
fend ( )
{
return forward_iterator ( m_storage . end ( ) ) ;
}
const_forward_iterator
cfbegin ( ) const
{
return const_forward_iterator ( m_storage . cbegin ( ) ) ;
}
const_forward_iterator
cfend ( ) const
{
return const_forward_iterator ( m_storage . cend ( ) ) ;
}
const_forward_iterator
fbegin ( ) const
{
return const_forward_iterator ( m_storage . cbegin ( ) ) ;
}
const_forward_iterator
fend ( ) const
{
return const_forward_iterator ( m_storage . cend ( ) ) ;
}
const_bidirectional_iterator
cbibegin ( ) const
{
return const_bidirectional_iterator ( m_storage . cbegin ( ) ) ;
}
const_bidirectional_iterator
cbiend ( ) const
{
return const_bidirectional_iterator ( m_storage . cend ( ) ) ;
}
bidirectional_iterator
bibegin ( )
{
return bidirectional_iterator ( m_storage . begin ( ) ) ;
}
bidirectional_iterator
biend ( )
{
return bidirectional_iterator ( m_storage . end ( ) ) ;
}
std : : size_t
size ( ) const
{
return m_storage . size ( ) ;
}
const T *
data ( ) const
{
return m_storage . data ( ) ;
}
typename std : : vector < T > : : reference operator [ ] ( size_t j ) { return m_storage [ j ] ; }
const T & operator [ ] ( size_t j ) const { return m_storage [ j ] ; }
// Fill with given value
void
fill ( const T & value )
{
for ( size_t i = 0 ; i < m_storage . size ( ) ; i + + )
m_storage [ i ] = value ;
}
void
print ( ) const ;
template < typename Func >
void
fill ( Func f )
{
fill_data ( m_storage . begin ( ) , m_storage . end ( ) , f ) ;
}
} ;
template < typename T >
void
Sequence < T > : : print ( ) const
{
std : : cout < < " size = " < < size ( ) < < " : { " ;
std : : copy ( begin ( ) , end ( ) , std : : ostream_iterator < T > ( std : : cout , " " ) ) ;
std : : cout < < " } " < < std : : endl ;
}
// Predicates for algorithms
template < typename DataType >
struct is_equal_to
{
is_equal_to ( const DataType & expected ) : m_expected ( expected ) { }
bool
operator ( ) ( const DataType & actual ) const
{
return actual = = m_expected ;
}
private :
DataType m_expected ;
} ;
// Low-quality hash function, returns value between 0 and (1<<bits)-1
// Warning: low-order bits are quite predictable.
inline size_t
HashBits ( size_t i , size_t bits )
{
size_t mask = bits > = 8 * sizeof ( size_t ) ? ~ size_t ( 0 ) : ( size_t ( 1 ) < < bits ) - 1 ;
return ( 424157 * i ^ 0x24aFa ) & mask ;
}
// Stateful unary op
template < typename T , typename U >
class Complement
{
int32_t val ;
public :
Complement ( T v ) : val ( v ) { }
U
operator ( ) ( const T & x ) const
{
return U ( val - x ) ;
}
} ;
// Tag used to prevent accidental use of converting constructor, even if use is explicit.
struct OddTag
{
} ;
class Sum ;
// Type with limited set of operations. Not default-constructible.
// Only available operator is "==".
// Typically used as value type in tests.
class Number
{
int32_t value ;
friend class Add ;
friend class Sum ;
friend class IsMultiple ;
friend class Congruent ;
friend Sum
operator + ( const Sum & x , const Sum & y ) ;
public :
Number ( int32_t val , OddTag ) : value ( val ) { }
friend bool
operator = = ( const Number & x , const Number & y )
{
return x . value = = y . value ;
}
friend std : : ostream &
operator < < ( std : : ostream & o , const Number & d )
{
return o < < d . value ;
}
} ;
// Stateful predicate for Number. Not default-constructible.
class IsMultiple
{
long modulus ;
public :
// True if x is multiple of modulus
bool
operator ( ) ( Number x ) const
{
return x . value % modulus = = 0 ;
}
IsMultiple ( long modulus_ , OddTag ) : modulus ( modulus_ ) { }
} ;
// Stateful equivalence-class predicate for Number. Not default-constructible.
class Congruent
{
long modulus ;
public :
// True if x and y have same remainder for the given modulus.
// Note: this is not quite the same as "equivalent modulo modulus" when x and y have different
// sign, but nonetheless AreCongruent is still an equivalence relationship, which is all
// we need for testing.
bool
operator ( ) ( Number x , Number y ) const
{
return x . value % modulus = = y . value % modulus ;
}
Congruent ( long modulus_ , OddTag ) : modulus ( modulus_ ) { }
} ;
// Stateful reduction operation for Number
class Add
{
long bias ;
public :
explicit Add ( OddTag ) : bias ( 1 ) { }
Number
operator ( ) ( Number x , const Number & y )
{
return Number ( x . value + y . value + ( bias - 1 ) , OddTag ( ) ) ;
}
} ;
// Class similar to Number, but has default constructor and +.
class Sum : public Number
{
public :
Sum ( ) : Number ( 0 , OddTag ( ) ) { }
Sum ( long x , OddTag ) : Number ( x , OddTag ( ) ) { }
friend Sum
operator + ( const Sum & x , const Sum & y )
{
return Sum ( x . value + y . value , OddTag ( ) ) ;
}
} ;
// Type with limited set of operations, which includes an associative but not commutative operation.
// Not default-constructible.
// Typically used as value type in tests involving "GENERALIZED_NONCOMMUTATIVE_SUM".
class MonoidElement
{
size_t a , b ;
public :
MonoidElement ( size_t a_ , size_t b_ , OddTag ) : a ( a_ ) , b ( b_ ) { }
friend bool
operator = = ( const MonoidElement & x , const MonoidElement & y )
{
return x . a = = y . a & & x . b = = y . b ;
}
friend std : : ostream &
operator < < ( std : : ostream & o , const MonoidElement & x )
{
return o < < " [ " < < x . a < < " .. " < < x . b < < " ) " ;
}
friend class AssocOp ;
} ;
// Stateful associative op for MonoidElement
// It's not really a monoid since the operation is not allowed for any two elements.
// But it's good enough for testing.
class AssocOp
{
unsigned c ;
public :
explicit AssocOp ( OddTag ) : c ( 5 ) { }
MonoidElement
operator ( ) ( const MonoidElement & x , const MonoidElement & y )
{
unsigned d = 5 ;
EXPECT_EQ ( d , c , " state lost " ) ;
EXPECT_EQ ( x . b , y . a , " commuted? " ) ;
return MonoidElement ( x . a , y . b , OddTag ( ) ) ;
}
} ;
// Multiplication of matrix is an associative but not commutative operation
// Typically used as value type in tests involving "GENERALIZED_NONCOMMUTATIVE_SUM".
template < typename T >
struct Matrix2x2
{
T a [ 2 ] [ 2 ] ;
Matrix2x2 ( ) : a { { 1 , 0 } , { 0 , 1 } } { }
Matrix2x2 ( T x , T y ) : a { { 0 , x } , { x , y } } { }
2021-05-27 03:24:31 +08:00
# if !defined(_PSTL_ICL_19_VC14_VC141_TEST_SCAN_RELEASE_BROKEN)
2018-12-20 01:45:32 +08:00
Matrix2x2 ( const Matrix2x2 & m ) : a { { m . a [ 0 ] [ 0 ] , m . a [ 0 ] [ 1 ] } , { m . a [ 1 ] [ 0 ] , m . a [ 1 ] [ 1 ] } } { }
Matrix2x2 &
operator = ( const Matrix2x2 & m )
{
a [ 0 ] [ 0 ] = m . a [ 0 ] [ 0 ] , a [ 0 ] [ 1 ] = m . a [ 0 ] [ 1 ] , a [ 1 ] [ 0 ] = m . a [ 1 ] [ 0 ] , a [ 1 ] [ 1 ] = m . a [ 1 ] [ 1 ] ;
return * this ;
}
# endif
} ;
template < typename T >
bool
operator = = ( const Matrix2x2 < T > & left , const Matrix2x2 < T > & right )
{
return left . a [ 0 ] [ 0 ] = = right . a [ 0 ] [ 0 ] & & left . a [ 0 ] [ 1 ] = = right . a [ 0 ] [ 1 ] & & left . a [ 1 ] [ 0 ] = = right . a [ 1 ] [ 0 ] & &
left . a [ 1 ] [ 1 ] = = right . a [ 1 ] [ 1 ] ;
}
template < typename T >
Matrix2x2 < T >
multiply_matrix ( const Matrix2x2 < T > & left , const Matrix2x2 < T > & right )
{
Matrix2x2 < T > result ;
for ( int32_t i = 0 ; i < 2 ; + + i )
{
for ( int32_t j = 0 ; j < 2 ; + + j )
{
result . a [ i ] [ j ] = left . a [ i ] [ 0 ] * right . a [ 0 ] [ j ] + left . a [ i ] [ 1 ] * right . a [ 1 ] [ j ] ;
}
}
return result ;
}
//============================================================================
// Adapters for creating different types of iterators.
//
// In this block we implemented some adapters for creating differnet types of iterators.
// It's needed for extending the unit testing of Parallel STL algorithms.
// We have adapters for iterators with different tags (forward_iterator_tag, bidirectional_iterator_tag), reverse iterators.
// The input iterator should be const or non-const, non-reverse random access iterator.
// Iterator creates in "MakeIterator":
// firstly, iterator is "packed" by "IteratorTypeAdapter" (creating forward or bidirectional iterator)
// then iterator is "packed" by "ReverseAdapter" (if it's possible)
// So, from input iterator we may create, for example, reverse bidirectional iterator.
// "Main" functor for testing iterators is named "invoke_on_all_iterator_types".
// Base adapter
template < typename Iterator >
struct BaseAdapter
{
typedef Iterator iterator_type ;
iterator_type
operator ( ) ( Iterator it )
{
return it ;
}
} ;
// Check if the iterator is reverse iterator
// Note: it works only for iterators that created by std::reverse_iterator
template < typename NotReverseIterator >
struct isReverse : std : : false_type
{
} ;
template < typename Iterator >
struct isReverse < std : : reverse_iterator < Iterator > > : std : : true_type
{
} ;
// Reverse adapter
template < typename Iterator , typename IsReverse >
struct ReverseAdapter
{
typedef std : : reverse_iterator < Iterator > iterator_type ;
iterator_type
operator ( ) ( Iterator it )
{
2021-05-27 03:24:31 +08:00
# if defined(_PSTL_CPP14_MAKE_REVERSE_ITERATOR_PRESENT)
2018-12-20 01:45:32 +08:00
return std : : make_reverse_iterator ( it ) ;
# else
return iterator_type ( it ) ;
# endif
}
} ;
// Non-reverse adapter
template < typename Iterator >
struct ReverseAdapter < Iterator , std : : false_type > : BaseAdapter < Iterator >
{
} ;
// Iterator adapter by type (by default std::random_access_iterator_tag)
template < typename Iterator , typename IteratorTag >
struct IteratorTypeAdapter : BaseAdapter < Iterator >
{
} ;
// Iterator adapter for forward iterator
template < typename Iterator >
struct IteratorTypeAdapter < Iterator , std : : forward_iterator_tag >
{
typedef ForwardIterator < Iterator , std : : forward_iterator_tag > iterator_type ;
iterator_type
operator ( ) ( Iterator it )
{
return iterator_type ( it ) ;
}
} ;
// Iterator adapter for bidirectional iterator
template < typename Iterator >
struct IteratorTypeAdapter < Iterator , std : : bidirectional_iterator_tag >
{
typedef BidirectionalIterator < Iterator , std : : bidirectional_iterator_tag > iterator_type ;
iterator_type
operator ( ) ( Iterator it )
{
return iterator_type ( it ) ;
}
} ;
//For creating iterator with new type
template < typename InputIterator , typename IteratorTag , typename IsReverse >
struct MakeIterator
{
typedef IteratorTypeAdapter < InputIterator , IteratorTag > IterByType ;
typedef ReverseAdapter < typename IterByType : : iterator_type , IsReverse > ReverseIter ;
typename ReverseIter : : iterator_type
operator ( ) ( InputIterator it )
{
return ReverseIter ( ) ( IterByType ( ) ( it ) ) ;
}
} ;
// Useful constant variables
constexpr std : : size_t GuardSize = 5 ;
2019-02-16 01:30:35 +08:00
constexpr std : : ptrdiff_t sizeLimit = 1000 ;
2018-12-20 01:45:32 +08:00
template < typename Iter , typename Void = void > // local iterator_traits for non-iterators
struct iterator_traits_
{
} ;
template < typename Iter > // For iterators
struct iterator_traits_ < Iter ,
typename std : : enable_if < ! std : : is_void < typename Iter : : iterator_category > : : value , void > : : type >
{
typedef typename Iter : : iterator_category iterator_category ;
} ;
template < typename T > // For pointers
struct iterator_traits_ < T * >
{
typedef std : : random_access_iterator_tag iterator_category ;
} ;
// is iterator Iter has tag Tag
template < typename Iter , typename Tag >
using is_same_iterator_category = std : : is_same < typename iterator_traits_ < Iter > : : iterator_category , Tag > ;
// if we run with reverse or const iterators we shouldn't test the large range
template < typename IsReverse , typename IsConst >
struct invoke_if_
{
template < typename Op , typename . . . Rest >
void
operator ( ) ( bool is_allow , Op op , Rest & & . . . rest )
{
if ( is_allow )
op ( std : : forward < Rest > ( rest ) . . . ) ;
}
} ;
template < >
struct invoke_if_ < std : : false_type , std : : false_type >
{
template < typename Op , typename . . . Rest >
void
2019-05-31 04:46:31 +08:00
operator ( ) ( bool , Op op , Rest & & . . . rest )
2018-12-20 01:45:32 +08:00
{
op ( std : : forward < Rest > ( rest ) . . . ) ;
}
} ;
// Base non_const_wrapper struct. It is used to distinguish non_const testcases
// from a regular one. For non_const testcases only compilation is checked.
struct non_const_wrapper
{
} ;
// Generic wrapper to specify iterator type to execute callable Op on.
// The condition can be either positive(Op is executed only with IteratorTag)
// or negative(Op is executed with every type of iterators except IteratorTag)
template < typename Op , typename IteratorTag , bool IsPositiveCondition = true >
struct non_const_wrapper_tagged : non_const_wrapper
{
template < typename Policy , typename Iterator >
typename std : : enable_if < IsPositiveCondition = = is_same_iterator_category < Iterator , IteratorTag > : : value , void > : : type
operator ( ) ( Policy & & exec , Iterator iter )
{
Op ( ) ( exec , iter ) ;
}
template < typename Policy , typename InputIterator , typename OutputIterator >
typename std : : enable_if < IsPositiveCondition = = is_same_iterator_category < OutputIterator , IteratorTag > : : value ,
void > : : type
operator ( ) ( Policy & & exec , InputIterator input_iter , OutputIterator out_iter )
{
Op ( ) ( exec , input_iter , out_iter ) ;
}
template < typename Policy , typename Iterator >
typename std : : enable_if < IsPositiveCondition ! = is_same_iterator_category < Iterator , IteratorTag > : : value , void > : : type
2019-05-31 04:46:31 +08:00
operator ( ) ( Policy & & , Iterator )
2018-12-20 01:45:32 +08:00
{
}
template < typename Policy , typename InputIterator , typename OutputIterator >
typename std : : enable_if < IsPositiveCondition ! = is_same_iterator_category < OutputIterator , IteratorTag > : : value ,
void > : : type
2019-05-31 04:46:31 +08:00
operator ( ) ( Policy & & , InputIterator , OutputIterator )
2018-12-20 01:45:32 +08:00
{
}
} ;
// These run_for_* structures specify with which types of iterators callable object Op
// should be executed.
template < typename Op >
struct run_for_rnd : non_const_wrapper_tagged < Op , std : : random_access_iterator_tag >
{
} ;
template < typename Op >
struct run_for_rnd_bi : non_const_wrapper_tagged < Op , std : : forward_iterator_tag , false >
{
} ;
template < typename Op >
struct run_for_rnd_fw : non_const_wrapper_tagged < Op , std : : bidirectional_iterator_tag , false >
{
} ;
// Invoker for different types of iterators.
template < typename IteratorTag , typename IsReverse >
struct iterator_invoker
{
template < typename Iterator >
using make_iterator = MakeIterator < Iterator , IteratorTag , IsReverse > ;
template < typename Iterator >
using IsConst = typename std : : is_const <
typename std : : remove_pointer < typename std : : iterator_traits < Iterator > : : pointer > : : type > : : type ;
template < typename Iterator >
using invoke_if = invoke_if_ < IsReverse , IsConst < Iterator > > ;
// A single iterator version which is used for non_const testcases
template < typename Policy , typename Op , typename Iterator >
typename std : : enable_if < is_same_iterator_category < Iterator , std : : random_access_iterator_tag > : : value & &
std : : is_base_of < non_const_wrapper , Op > : : value ,
void > : : type
operator ( ) ( Policy & & exec , Op op , Iterator iter )
{
op ( std : : forward < Policy > ( exec ) , make_iterator < Iterator > ( ) ( iter ) ) ;
}
// A version with 2 iterators which is used for non_const testcases
template < typename Policy , typename Op , typename InputIterator , typename OutputIterator >
typename std : : enable_if < is_same_iterator_category < OutputIterator , std : : random_access_iterator_tag > : : value & &
std : : is_base_of < non_const_wrapper , Op > : : value ,
void > : : type
operator ( ) ( Policy & & exec , Op op , InputIterator input_iter , OutputIterator out_iter )
{
op ( std : : forward < Policy > ( exec ) , make_iterator < InputIterator > ( ) ( input_iter ) ,
make_iterator < OutputIterator > ( ) ( out_iter ) ) ;
}
template < typename Policy , typename Op , typename Iterator , typename Size , typename . . . Rest >
typename std : : enable_if < is_same_iterator_category < Iterator , std : : random_access_iterator_tag > : : value , void > : : type
operator ( ) ( Policy & & exec , Op op , Iterator begin , Size n , Rest & & . . . rest )
{
invoke_if < Iterator > ( ) ( n < = sizeLimit , op , exec , make_iterator < Iterator > ( ) ( begin ) , n ,
std : : forward < Rest > ( rest ) . . . ) ;
}
template < typename Policy , typename Op , typename Iterator , typename . . . Rest >
typename std : : enable_if < is_same_iterator_category < Iterator , std : : random_access_iterator_tag > : : value & &
! std : : is_base_of < non_const_wrapper , Op > : : value ,
void > : : type
operator ( ) ( Policy & & exec , Op op , Iterator inputBegin , Iterator inputEnd , Rest & & . . . rest )
{
invoke_if < Iterator > ( ) ( std : : distance ( inputBegin , inputEnd ) < = sizeLimit , op , exec ,
make_iterator < Iterator > ( ) ( inputBegin ) , make_iterator < Iterator > ( ) ( inputEnd ) ,
std : : forward < Rest > ( rest ) . . . ) ;
}
template < typename Policy , typename Op , typename InputIterator , typename OutputIterator , typename . . . Rest >
typename std : : enable_if < is_same_iterator_category < OutputIterator , std : : random_access_iterator_tag > : : value ,
void > : : type
operator ( ) ( Policy & & exec , Op op , InputIterator inputBegin , InputIterator inputEnd , OutputIterator outputBegin ,
Rest & & . . . rest )
{
invoke_if < InputIterator > ( ) ( std : : distance ( inputBegin , inputEnd ) < = sizeLimit , op , exec ,
make_iterator < InputIterator > ( ) ( inputBegin ) , make_iterator < InputIterator > ( ) ( inputEnd ) ,
make_iterator < OutputIterator > ( ) ( outputBegin ) , std : : forward < Rest > ( rest ) . . . ) ;
}
template < typename Policy , typename Op , typename InputIterator , typename OutputIterator , typename . . . Rest >
typename std : : enable_if < is_same_iterator_category < OutputIterator , std : : random_access_iterator_tag > : : value ,
void > : : type
operator ( ) ( Policy & & exec , Op op , InputIterator inputBegin , InputIterator inputEnd , OutputIterator outputBegin ,
OutputIterator outputEnd , Rest & & . . . rest )
{
invoke_if < InputIterator > ( ) ( std : : distance ( inputBegin , inputEnd ) < = sizeLimit , op , exec ,
make_iterator < InputIterator > ( ) ( inputBegin ) , make_iterator < InputIterator > ( ) ( inputEnd ) ,
make_iterator < OutputIterator > ( ) ( outputBegin ) ,
make_iterator < OutputIterator > ( ) ( outputEnd ) , std : : forward < Rest > ( rest ) . . . ) ;
}
template < typename Policy , typename Op , typename InputIterator1 , typename InputIterator2 , typename OutputIterator ,
typename . . . Rest >
typename std : : enable_if < is_same_iterator_category < OutputIterator , std : : random_access_iterator_tag > : : value ,
void > : : type
operator ( ) ( Policy & & exec , Op op , InputIterator1 inputBegin1 , InputIterator1 inputEnd1 , InputIterator2 inputBegin2 ,
InputIterator2 inputEnd2 , OutputIterator outputBegin , OutputIterator outputEnd , Rest & & . . . rest )
{
invoke_if < InputIterator1 > ( ) (
std : : distance ( inputBegin1 , inputEnd1 ) < = sizeLimit , op , exec , make_iterator < InputIterator1 > ( ) ( inputBegin1 ) ,
make_iterator < InputIterator1 > ( ) ( inputEnd1 ) , make_iterator < InputIterator2 > ( ) ( inputBegin2 ) ,
make_iterator < InputIterator2 > ( ) ( inputEnd2 ) , make_iterator < OutputIterator > ( ) ( outputBegin ) ,
make_iterator < OutputIterator > ( ) ( outputEnd ) , std : : forward < Rest > ( rest ) . . . ) ;
}
} ;
// Invoker for reverse iterators only
// Note: if we run with reverse iterators we shouldn't test the large range
template < typename IteratorTag >
struct iterator_invoker < IteratorTag , /* IsReverse = */ std : : true_type >
{
template < typename Iterator >
using make_iterator = MakeIterator < Iterator , IteratorTag , std : : true_type > ;
// A single iterator version which is used for non_const testcases
template < typename Policy , typename Op , typename Iterator >
typename std : : enable_if < is_same_iterator_category < Iterator , std : : random_access_iterator_tag > : : value & &
std : : is_base_of < non_const_wrapper , Op > : : value ,
void > : : type
operator ( ) ( Policy & & exec , Op op , Iterator iter )
{
op ( std : : forward < Policy > ( exec ) , make_iterator < Iterator > ( ) ( iter ) ) ;
}
// A version with 2 iterators which is used for non_const testcases
template < typename Policy , typename Op , typename InputIterator , typename OutputIterator >
typename std : : enable_if < is_same_iterator_category < OutputIterator , std : : random_access_iterator_tag > : : value & &
std : : is_base_of < non_const_wrapper , Op > : : value ,
void > : : type
operator ( ) ( Policy & & exec , Op op , InputIterator input_iter , OutputIterator out_iter )
{
op ( std : : forward < Policy > ( exec ) , make_iterator < InputIterator > ( ) ( input_iter ) ,
make_iterator < OutputIterator > ( ) ( out_iter ) ) ;
}
template < typename Policy , typename Op , typename Iterator , typename Size , typename . . . Rest >
typename std : : enable_if < is_same_iterator_category < Iterator , std : : random_access_iterator_tag > : : value , void > : : type
operator ( ) ( Policy & & exec , Op op , Iterator begin , Size n , Rest & & . . . rest )
{
if ( n < = sizeLimit )
op ( exec , make_iterator < Iterator > ( ) ( begin + n ) , n , std : : forward < Rest > ( rest ) . . . ) ;
}
template < typename Policy , typename Op , typename Iterator , typename . . . Rest >
typename std : : enable_if < is_same_iterator_category < Iterator , std : : random_access_iterator_tag > : : value & &
! std : : is_base_of < non_const_wrapper , Op > : : value ,
void > : : type
operator ( ) ( Policy & & exec , Op op , Iterator inputBegin , Iterator inputEnd , Rest & & . . . rest )
{
if ( std : : distance ( inputBegin , inputEnd ) < = sizeLimit )
op ( exec , make_iterator < Iterator > ( ) ( inputEnd ) , make_iterator < Iterator > ( ) ( inputBegin ) ,
std : : forward < Rest > ( rest ) . . . ) ;
}
template < typename Policy , typename Op , typename InputIterator , typename OutputIterator , typename . . . Rest >
typename std : : enable_if < is_same_iterator_category < OutputIterator , std : : random_access_iterator_tag > : : value ,
void > : : type
operator ( ) ( Policy & & exec , Op op , InputIterator inputBegin , InputIterator inputEnd , OutputIterator outputBegin ,
Rest & & . . . rest )
{
if ( std : : distance ( inputBegin , inputEnd ) < = sizeLimit )
op ( exec , make_iterator < InputIterator > ( ) ( inputEnd ) , make_iterator < InputIterator > ( ) ( inputBegin ) ,
make_iterator < OutputIterator > ( ) ( outputBegin + ( inputEnd - inputBegin ) ) , std : : forward < Rest > ( rest ) . . . ) ;
}
template < typename Policy , typename Op , typename InputIterator , typename OutputIterator , typename . . . Rest >
typename std : : enable_if < is_same_iterator_category < OutputIterator , std : : random_access_iterator_tag > : : value ,
void > : : type
operator ( ) ( Policy & & exec , Op op , InputIterator inputBegin , InputIterator inputEnd , OutputIterator outputBegin ,
OutputIterator outputEnd , Rest & & . . . rest )
{
if ( std : : distance ( inputBegin , inputEnd ) < = sizeLimit )
op ( exec , make_iterator < InputIterator > ( ) ( inputEnd ) , make_iterator < InputIterator > ( ) ( inputBegin ) ,
make_iterator < OutputIterator > ( ) ( outputEnd ) , make_iterator < OutputIterator > ( ) ( outputBegin ) ,
std : : forward < Rest > ( rest ) . . . ) ;
}
template < typename Policy , typename Op , typename InputIterator1 , typename InputIterator2 , typename OutputIterator ,
typename . . . Rest >
typename std : : enable_if < is_same_iterator_category < OutputIterator , std : : random_access_iterator_tag > : : value ,
void > : : type
operator ( ) ( Policy & & exec , Op op , InputIterator1 inputBegin1 , InputIterator1 inputEnd1 , InputIterator2 inputBegin2 ,
InputIterator2 inputEnd2 , OutputIterator outputBegin , OutputIterator outputEnd , Rest & & . . . rest )
{
if ( std : : distance ( inputBegin1 , inputEnd1 ) < = sizeLimit )
op ( exec , make_iterator < InputIterator1 > ( ) ( inputEnd1 ) , make_iterator < InputIterator1 > ( ) ( inputBegin1 ) ,
make_iterator < InputIterator2 > ( ) ( inputEnd2 ) , make_iterator < InputIterator2 > ( ) ( inputBegin2 ) ,
make_iterator < OutputIterator > ( ) ( outputEnd ) , make_iterator < OutputIterator > ( ) ( outputBegin ) ,
std : : forward < Rest > ( rest ) . . . ) ;
}
} ;
// We can't create reverse iterator from forward iterator
template < >
struct iterator_invoker < std : : forward_iterator_tag , /*isReverse=*/ std : : true_type >
{
template < typename . . . Rest >
void
2019-05-31 04:46:31 +08:00
operator ( ) ( Rest & & . . . )
2018-12-20 01:45:32 +08:00
{
}
} ;
template < typename IsReverse >
struct reverse_invoker
{
template < typename . . . Rest >
void
operator ( ) ( Rest & & . . . rest )
{
// Random-access iterator
iterator_invoker < std : : random_access_iterator_tag , IsReverse > ( ) ( std : : forward < Rest > ( rest ) . . . ) ;
// Forward iterator
iterator_invoker < std : : forward_iterator_tag , IsReverse > ( ) ( std : : forward < Rest > ( rest ) . . . ) ;
// Bidirectional iterator
iterator_invoker < std : : bidirectional_iterator_tag , IsReverse > ( ) ( std : : forward < Rest > ( rest ) . . . ) ;
}
} ;
struct invoke_on_all_iterator_types
{
template < typename . . . Rest >
void
operator ( ) ( Rest & & . . . rest )
{
reverse_invoker < /* IsReverse = */ std : : false_type > ( ) ( std : : forward < Rest > ( rest ) . . . ) ;
reverse_invoker < /* IsReverse = */ std : : true_type > ( ) ( std : : forward < Rest > ( rest ) . . . ) ;
}
} ;
//============================================================================
// Invoke op(policy,rest...) for each possible policy.
template < typename Op , typename . . . T >
void
invoke_on_all_policies ( Op op , T & & . . . rest )
{
using namespace __pstl : : execution ;
// Try static execution policies
invoke_on_all_iterator_types ( ) ( seq , op , std : : forward < T > ( rest ) . . . ) ;
invoke_on_all_iterator_types ( ) ( unseq , op , std : : forward < T > ( rest ) . . . ) ;
invoke_on_all_iterator_types ( ) ( par , op , std : : forward < T > ( rest ) . . . ) ;
invoke_on_all_iterator_types ( ) ( par_unseq , op , std : : forward < T > ( rest ) . . . ) ;
}
template < typename F >
struct NonConstAdapter
{
F my_f ;
NonConstAdapter ( const F & f ) : my_f ( f ) { }
template < typename . . . Types >
auto
operator ( ) ( Types & & . . . args ) - > decltype ( std : : declval < F > ( ) .
operator ( ) ( std : : forward < Types > ( args ) . . . ) )
{
return my_f ( std : : forward < Types > ( args ) . . . ) ;
}
} ;
template < typename F >
NonConstAdapter < F >
non_const ( const F & f )
{
return NonConstAdapter < F > ( f ) ;
}
// Wrapper for types. It's need for counting of constructing and destructing objects
template < typename T >
class Wrapper
{
public :
Wrapper ( )
{
my_field = std : : shared_ptr < T > ( new T ( ) ) ;
+ + my_count ;
}
Wrapper ( const T & input )
{
my_field = std : : shared_ptr < T > ( new T ( input ) ) ;
+ + my_count ;
}
Wrapper ( const Wrapper & input )
{
my_field = input . my_field ;
+ + my_count ;
}
Wrapper ( Wrapper & & input )
{
my_field = input . my_field ;
input . my_field = nullptr ;
+ + move_count ;
}
Wrapper &
operator = ( const Wrapper & input )
{
my_field = input . my_field ;
return * this ;
}
Wrapper &
operator = ( Wrapper & & input )
{
my_field = input . my_field ;
input . my_field = nullptr ;
+ + move_count ;
return * this ;
}
bool
operator = = ( const Wrapper & input ) const
{
return my_field = = input . my_field ;
}
bool
operator < ( const Wrapper & input ) const
{
return * my_field < * input . my_field ;
}
bool
operator > ( const Wrapper & input ) const
{
return * my_field > * input . my_field ;
}
friend std : : ostream &
operator < < ( std : : ostream & stream , const Wrapper & input )
{
return stream < < * ( input . my_field ) ;
}
~ Wrapper ( )
{
- - my_count ;
if ( move_count > 0 )
{
- - move_count ;
}
}
T *
get_my_field ( ) const
{
return my_field . get ( ) ;
} ;
static size_t
Count ( )
{
return my_count ;
}
static size_t
MoveCount ( )
{
return move_count ;
}
static void
SetCount ( const size_t & n )
{
my_count = n ;
}
static void
SetMoveCount ( const size_t & n )
{
move_count = n ;
}
private :
static std : : atomic < size_t > my_count ;
static std : : atomic < size_t > move_count ;
std : : shared_ptr < T > my_field ;
} ;
template < typename T >
std : : atomic < size_t > Wrapper < T > : : my_count = { 0 } ;
template < typename T >
std : : atomic < size_t > Wrapper < T > : : move_count = { 0 } ;
template < typename InputIterator , typename T , typename BinaryOperation , typename UnaryOperation >
T
transform_reduce_serial ( InputIterator first , InputIterator last , T init , BinaryOperation binary_op ,
UnaryOperation unary_op ) noexcept
{
for ( ; first ! = last ; + + first )
{
init = binary_op ( init , unary_op ( * first ) ) ;
}
return init ;
}
static const char *
done ( )
{
2021-05-27 03:24:31 +08:00
# if defined(_PSTL_TEST_SUCCESSFUL_KEYWORD)
2018-12-20 01:45:32 +08:00
return " done " ;
# else
return " passed " ;
# endif
}
// test_algo_basic_* functions are used to execute
// f on a very basic sequence of elements of type T.
// Should be used with unary predicate
template < typename T , typename F >
static void
test_algo_basic_single ( F & & f )
{
size_t N = 10 ;
Sequence < T > in ( N , [ ] ( size_t v ) - > T { return T ( v ) ; } ) ;
invoke_on_all_policies ( f , in . begin ( ) ) ;
}
// Should be used with binary predicate
template < typename T , typename F >
static void
test_algo_basic_double ( F & & f )
{
size_t N = 10 ;
Sequence < T > in ( N , [ ] ( size_t v ) - > T { return T ( v ) ; } ) ;
Sequence < T > out ( N , [ ] ( size_t v ) - > T { return T ( v ) ; } ) ;
invoke_on_all_policies ( f , in . begin ( ) , out . begin ( ) ) ;
}
template < typename Policy , typename F >
static void
2019-05-31 04:46:31 +08:00
invoke_if ( Policy & & , F f )
2018-12-20 01:45:32 +08:00
{
2021-05-27 03:24:31 +08:00
# if defined(_PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN) || defined(_PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN)
2022-01-28 08:54:27 +08:00
using decay_policy = typename std : : decay < Policy > : : type ;
using allow_unsequenced =
std : : integral_constant < bool , ( std : : is_same < decay_policy , std : : execution : : unsequenced_policy > : : value | |
std : : is_same < decay_policy , std : : execution : : parallel_unsequenced_policy > : : value ) > ;
__pstl : : __internal : : __invoke_if_not ( allow_unsequenced { } , f ) ;
2018-12-20 01:45:32 +08:00
# else
f ( ) ;
# endif
}
} /* namespace TestUtils */